Browse Source

Merge remote-tracking branch 'origin/master' into more-windows-comm

jason 1 year ago
parent
commit
e6d84d18d3
62 changed files with 12637 additions and 685 deletions
  1. 26 0
      base/runtime/core.odin
  2. 2 1
      base/runtime/default_temp_allocator_arena.odin
  3. 120 0
      base/runtime/random_generator.odin
  4. 1 1
      core/container/avl/avl.odin
  5. 1 1
      core/container/rbtree/rbtree.odin
  6. 22 0
      core/crypto/crypto.odin
  7. 28 0
      core/flags/LICENSE
  8. 38 0
      core/flags/constants.odin
  9. 181 0
      core/flags/doc.odin
  10. 50 0
      core/flags/errors.odin
  11. 9 0
      core/flags/errors_bsd.odin
  12. 11 0
      core/flags/errors_nonbsd.odin
  13. 132 0
      core/flags/example/example.odin
  14. 262 0
      core/flags/internal_assignment.odin
  15. 170 0
      core/flags/internal_parsing.odin
  16. 536 0
      core/flags/internal_rtti.odin
  17. 31 0
      core/flags/internal_rtti_nonbsd.odin
  18. 244 0
      core/flags/internal_validation.odin
  19. 101 0
      core/flags/parsing.odin
  20. 43 0
      core/flags/rtti.odin
  21. 293 0
      core/flags/usage.odin
  22. 130 0
      core/flags/util.odin
  23. 37 0
      core/flags/validation.odin
  24. 10 4
      core/fmt/fmt.odin
  25. 5 5
      core/math/big/helpers.odin
  26. 12 7
      core/math/big/internal.odin
  27. 3 5
      core/math/big/prime.odin
  28. 60 60
      core/math/rand/distributions.odin
  29. 4 4
      core/math/rand/exp.odin
  30. 5 12
      core/math/rand/normal.odin
  31. 63 252
      core/math/rand/rand.odin
  32. 10 6
      core/slice/slice.odin
  33. 1 4
      core/sync/chan/chan.odin
  34. 59 109
      core/sys/windows/kernel32.odin
  35. 9 0
      core/sys/windows/types.odin
  36. 21 0
      core/sys/windows/user32.odin
  37. 1 1
      core/sys/windows/winerror.odin
  38. 16 7
      core/testing/logging.odin
  39. 39 13
      core/testing/runner.odin
  40. 21 6
      core/testing/testing.odin
  41. 46 44
      core/text/i18n/doc.odin
  42. 107 59
      core/text/i18n/i18n.odin
  43. 264 4
      core/unicode/letter.odin
  44. 2449 0
      core/unicode/tables.odin
  45. 387 0
      core/unicode/utf8/grapheme.odin
  46. 2 0
      examples/all/all_main.odin
  47. 1 1
      src/check_decl.cpp
  48. 3 1
      src/check_expr.cpp
  49. 8 0
      src/checker.cpp
  50. 6 2
      src/linker.cpp
  51. 2 1
      src/main.cpp
  52. 2 5
      tests/core/container/test_core_avl.odin
  53. 3 6
      tests/core/container/test_core_rbtree.odin
  54. 1393 0
      tests/core/flags/test_core_flags.odin
  55. 2 2
      tests/core/hash/test_core_hash.odin
  56. 35 0
      tests/core/math/rand/test_core_math_rand.odin
  57. 3 0
      tests/core/normal.odin
  58. 70 9
      tests/core/slice/test_core_slice.odin
  59. 21 19
      tests/core/text/i18n/test_core_text_i18n.odin
  60. 73 0
      tests/core/unicode/test_core_unicode.odin
  61. 4912 0
      tests/core/unicode/test_core_unicode_data.odin
  62. 41 34
      tests/internal/test_map.odin

+ 26 - 0
base/runtime/core.odin

@@ -397,11 +397,34 @@ Logger :: struct {
 	options:      Logger_Options,
 }
 
+
+Random_Generator_Mode :: enum {
+	Read,
+	Reset,
+	Query_Info,
+}
+
+Random_Generator_Query_Info_Flag :: enum u32 {
+	Cryptographic,
+	Uniform,
+	External_Entropy,
+	Resettable,
+}
+Random_Generator_Query_Info :: distinct bit_set[Random_Generator_Query_Info_Flag; u32]
+
+Random_Generator_Proc :: #type proc(data: rawptr, mode: Random_Generator_Mode, p: []byte)
+
+Random_Generator :: struct {
+	procedure: Random_Generator_Proc,
+	data:      rawptr,
+}
+
 Context :: struct {
 	allocator:              Allocator,
 	temp_allocator:         Allocator,
 	assertion_failure_proc: Assertion_Failure_Proc,
 	logger:                 Logger,
+	random_generator:       Random_Generator,
 
 	user_ptr:   rawptr,
 	user_index: int,
@@ -708,6 +731,9 @@ __init_context :: proc "contextless" (c: ^Context) {
 
 	c.logger.procedure = default_logger_proc
 	c.logger.data = nil
+
+	c.random_generator.procedure = default_random_generator_proc
+	c.random_generator.data = nil
 }
 
 default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {

+ 2 - 1
base/runtime/default_allocators_arena.odin → base/runtime/default_temp_allocator_arena.odin

@@ -12,7 +12,8 @@ Memory_Block :: struct {
 	capacity:  uint,
 }
 
-// NOTE: This is for internal use, prefer `Arena` from `core:mem/virtual` if necessary
+// NOTE: This is a growing arena that is only used for the default temp allocator.
+// For your own growing arena needs, prefer `Arena` from `core:mem/virtual`.
 Arena :: struct {
 	backing_allocator:  Allocator,
 	curr_block:         ^Memory_Block,

+ 120 - 0
base/runtime/random_generator.odin

@@ -0,0 +1,120 @@
+package runtime
+
+import "base:intrinsics"
+
+@(require_results)
+random_generator_read_bytes :: proc(rg: Random_Generator, p: []byte) -> bool {
+	if rg.procedure != nil {
+		rg.procedure(rg.data, .Read, p)
+		return true
+	}
+	return false
+}
+
+@(require_results)
+random_generator_read_ptr :: proc(rg: Random_Generator, p: rawptr, len: uint) -> bool {
+	if rg.procedure != nil {
+		rg.procedure(rg.data, .Read, ([^]byte)(p)[:len])
+		return true
+	}
+	return false
+}
+
+@(require_results)
+random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Generator_Query_Info) {
+	if rg.procedure != nil {
+		rg.procedure(rg.data, .Query_Info, ([^]byte)(&info)[:size_of(info)])
+	}
+	return
+}
+
+
+random_generator_reset_bytes :: proc(rg: Random_Generator, p: []byte) {
+	if rg.procedure != nil {
+		rg.procedure(rg.data, .Reset, p)
+	}
+}
+
+random_generator_reset_u64 :: proc(rg: Random_Generator, p: u64) {
+	if rg.procedure != nil {
+		p := p
+		rg.procedure(rg.data, .Reset, ([^]byte)(&p)[:size_of(p)])
+	}
+}
+
+
+Default_Random_State :: struct {
+	state: u64,
+	inc:   u64,
+}
+
+default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte) {
+	@(require_results)
+	read_u64 :: proc "contextless" (r: ^Default_Random_State) -> u64 {
+		old_state := r.state
+		r.state = old_state * 6364136223846793005 + (r.inc|1)
+		xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
+		rot := (old_state >> 59)
+		return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
+	}
+
+	@(thread_local)
+	global_rand_seed: Default_Random_State
+
+	init :: proc "contextless" (r: ^Default_Random_State, seed: u64) {
+		seed := seed
+		if seed == 0 {
+			seed = u64(intrinsics.read_cycle_counter())
+		}
+		r.state = 0
+		r.inc = (seed << 1) | 1
+		_ = read_u64(r)
+		r.state += seed
+		_ = read_u64(r)
+	}
+
+	r: ^Default_Random_State = ---
+	if data == nil {
+		r = &global_rand_seed
+	} else {
+		r = cast(^Default_Random_State)data
+	}
+
+	switch mode {
+	case .Read:
+		if r.state == 0 && r.inc == 0 {
+		   	init(r, 0)
+		}
+
+		pos := i8(0)
+		val := u64(0)
+		for &v in p {
+			if pos == 0 {
+				val = read_u64(r)
+				pos = 7
+			}
+			v = byte(val)
+			val >>= 8
+			pos -= 1
+		}
+
+	case .Reset:
+		seed: u64
+		mem_copy_non_overlapping(&seed, raw_data(p), min(size_of(seed), len(p)))
+	   	init(r, seed)
+
+	case .Query_Info:
+		if len(p) != size_of(Random_Generator_Query_Info) {
+			return
+		}
+		info := (^Random_Generator_Query_Info)(raw_data(p))
+		info^ += {.Uniform, .Resettable}
+	}
+}
+
+default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator {
+	return {
+		procedure = default_random_generator_proc,
+		data = state,
+	}
+}

+ 1 - 1
core/container/avl/avl.odin

@@ -87,7 +87,7 @@ init_cmp :: proc(
 init_ordered :: proc(
 	t: ^$T/Tree($Value),
 	node_allocator := context.allocator,
-) where intrinsics.type_is_ordered_numeric(Value) {
+) where intrinsics.type_is_ordered(Value) {
 	init_cmp(t, slice.cmp_proc(Value), node_allocator)
 }
 

+ 1 - 1
core/container/rbtree/rbtree.odin

@@ -79,7 +79,7 @@ init_cmp :: proc(t: ^$T/Tree($Key, $Value), cmp_fn: proc(a, b: Key) -> Ordering,
 
 // init_ordered initializes a tree containing ordered keys, with
 // a comparison function that results in an ascending order sort.
-init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered_numeric(Key) {
+init_ordered :: proc(t: ^$T/Tree($Key, $Value), node_allocator := context.allocator) where intrinsics.type_is_ordered(Key) {
 	init_cmp(t, slice.cmp_proc(Key), node_allocator)
 }
 

+ 22 - 0
core/crypto/crypto.odin

@@ -4,6 +4,7 @@ helper routines.
 */
 package crypto
 
+import "base:runtime"
 import "core:mem"
 
 // compare_constant_time returns 1 iff a and b are equal, 0 otherwise.
@@ -58,3 +59,24 @@ rand_bytes :: proc (dst: []byte) {
 
 	_rand_bytes(dst)
 }
+
+
+random_generator :: proc() -> runtime.Random_Generator {
+	return {
+		procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) {
+			switch mode {
+			case .Read:
+				rand_bytes(p)
+			case .Reset:
+				// do nothing
+			case .Query_Info:
+				if len(p) != size_of(runtime.Random_Generator_Query_Info) {
+					return
+				}
+				info := (^runtime.Random_Generator_Query_Info)(raw_data(p))
+				info^ += {.Uniform, .Cryptographic, .External_Entropy}
+			}
+		},
+		data = nil,
+	}
+}

+ 28 - 0
core/flags/LICENSE

@@ -0,0 +1,28 @@
+BSD 3-Clause License
+
+Copyright (c) 2024, Feoramund
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 38 - 0
core/flags/constants.odin

@@ -0,0 +1,38 @@
+package flags
+
+import "core:time"
+
+// Set to true to compile with support for core named types disabled, as a
+// fallback in the event your platform does not support one of the types, or
+// you have no need for them and want a smaller binary.
+NO_CORE_NAMED_TYPES :: #config(ODIN_CORE_FLAGS_NO_CORE_NAMED_TYPES, false)
+
+// Override support for parsing `time` types.
+IMPORTING_TIME      :: #config(ODIN_CORE_FLAGS_USE_TIME, time.IS_SUPPORTED)
+
+// Override support for parsing `net` types.
+// TODO: Update this when the BSDs are supported.
+IMPORTING_NET       :: #config(ODIN_CORE_FLAGS_USE_NET, ODIN_OS == .Windows || ODIN_OS == .Linux || ODIN_OS == .Darwin)
+
+TAG_ARGS          :: "args"
+SUBTAG_NAME       :: "name"
+SUBTAG_POS        :: "pos"
+SUBTAG_REQUIRED   :: "required"
+SUBTAG_HIDDEN     :: "hidden"
+SUBTAG_VARIADIC   :: "variadic"
+SUBTAG_FILE       :: "file"
+SUBTAG_PERMS      :: "perms"
+SUBTAG_INDISTINCT :: "indistinct"
+
+TAG_USAGE         :: "usage"
+
+UNDOCUMENTED_FLAG :: "<This flag has not been documented yet.>"
+
+INTERNAL_VARIADIC_FLAG   :: "varg"
+
+RESERVED_HELP_FLAG       :: "help"
+RESERVED_HELP_FLAG_SHORT :: "h"
+
+// If there are more than this number of flags in total, only the required and
+// positional flags will be shown in the one-line usage summary.
+ONE_LINE_FLAG_CUTOFF_COUNT :: 16

+ 181 - 0
core/flags/doc.odin

@@ -0,0 +1,181 @@
+/*
+package flags implements a command-line argument parser.
+
+It works by using Odin's run-time type information to determine where and how
+to store data on a struct provided by the program. Type conversion is handled
+automatically and errors are reported with useful messages.
+
+
+Command-Line Syntax:
+
+Arguments are treated differently depending on how they're formatted.
+The format is similar to the Odin binary's way of handling compiler flags.
+
+```
+type                  handling
+------------          ------------------------
+<positional>          depends on struct layout
+-<flag>               set a bool true
+-<flag:option>        set flag to option
+-<flag=option>        set flag to option, alternative syntax
+-<map>:<key>=<value>  set map[key] to value
+```
+
+
+Struct Tags:
+
+Users of the `core:encoding/json` package may be familiar with using tags to
+annotate struct metadata. The same technique is used here to annotate where
+arguments should go and which are required.
+
+Under the `args` tag, there are the following subtags:
+
+- `name=S`: set `S` as the flag's name.
+- `pos=N`: place positional argument `N` into this flag.
+- `hidden`: hide this flag from the usage documentation.
+- `required`: cause verification to fail if this argument is not set.
+- `variadic`: take all remaining arguments when set, UNIX-style only.
+- `file`: for `os.Handle` types, file open mode.
+- `perms`: for `os.Handle` types, file open permissions.
+- `indistinct`: allow the setting of distinct types by their base type.
+
+`required` may be given a range specifier in the following formats:
+```
+min
+<max
+min<max
+```
+
+`max` is not inclusive in this range, as noted by the less-than `<` sign, so if
+you want to require 3 and only 3 arguments in a dynamic array, you would
+specify `required=3<4`.
+
+
+`variadic` may be given a number (`variadic=N`) above 1 to limit how many extra
+arguments it consumes.
+
+
+`file` determines the file open mode for an `os.Handle`.
+It accepts a string of flags that can be mixed together:
+- r: read
+- w: write
+- c: create, create the file if it doesn't exist
+- a: append, add any new writes to the end of the file
+- t: truncate, erase the file on open
+
+
+`perms` determines the file open permissions for an `os.Handle`.
+
+The permissions are represented by three numbers in octal format. The first
+number is the owner, the second is the group, and the third is other. Read is
+represented by 4, write by 2, and execute by 1.
+
+These numbers are added together to get combined permissions. For example, 644
+represents read/write for the owner, read for the group, and read for other.
+
+Note that this may only have effect on UNIX-like platforms. By default, `perms`
+is set to 444 when only reading and 644 when writing.
+
+
+`indistinct` tells the parser that it's okay to treat distinct types as their
+underlying base type. Normally, the parser will hand those types off to the
+custom type setter (more about that later) if one is available, if it doesn't
+know how to handle the type.
+
+
+Usage Tag:
+
+There is also the `usage` tag, which is a plain string to be printed alongside
+the flag in the usage output. If `usage` contains a newline, it will be
+properly aligned when printed.
+
+All surrounding whitespace is trimmed when formatting with multiple lines.
+
+
+Supported Flag Data Types:
+
+- all booleans
+- all integers
+- all floats
+- all enums
+- all complex numbers
+- all quaternions
+- all bit_sets
+- `string` and `cstring`
+- `rune`
+- `os.Handle`
+- `time.Time`
+- `datetime.DateTime`
+- `net.Host_Or_Endpoint`,
+- additional custom types, see Custom Types below
+- `dynamic` arrays with element types of the above
+- `map[string]`s or `map[cstring]`s with value types of the above
+
+
+Validation:
+
+The parser will ensure `required` arguments are set, if no errors occurred
+during parsing. This is on by default.
+
+Additionally, you may call `register_flag_checker` to set your own argument
+validation procedure that will be called after the default checker.
+
+
+Strict:
+
+The parser will return on the first error and stop parsing. This is on by
+default. Otherwise, all arguments that can be parsed, will be, and only the
+last error is returned.
+
+
+Error Messages:
+
+All error message strings are allocated using the context's `temp_allocator`,
+so if you need them to persist, make sure to clone the underlying `message`.
+
+
+Help:
+
+By default, `-h` and `-help` are reserved flags which raise their own error
+type when set, allowing the program to handle the request differently from
+other errors.
+
+
+Custom Types:
+
+You may specify your own type setter for program-specific structs and other
+named types. Call `register_type_setter` with an appropriate proc before
+calling any of the parsing procs.
+
+A compliant `Custom_Type_Setter` must return three values:
+- an error message if one occurred,
+- a boolean indicating if the proc handles the type, and
+- an `Allocator_Error` if any occurred.
+
+If the setter does not handle the type, simply return without setting any of
+the values.
+
+
+UNIX-style:
+
+This package also supports parsing arguments in a limited flavor of UNIX.
+Odin and UNIX style are mutually exclusive, and which one to be used is chosen
+at parse time.
+
+```
+--flag
+--flag=argument
+--flag argument
+--flag argument repeating-argument
+```
+
+`-flag` may also be substituted for `--flag`.
+
+Do note that map flags are not currently supported in this parsing style.
+
+
+Example:
+
+A complete example is given in the `example` subdirectory.
+*/
+package flags

+ 50 - 0
core/flags/errors.odin

@@ -0,0 +1,50 @@
+package flags
+
+import "core:os"
+
+Parse_Error_Reason :: enum {
+	None,
+	// An extra positional argument was given, and there is no `varg` field.
+	Extra_Positional,
+	// The underlying type does not support the string value it is being set to.
+	Bad_Value,
+	// No flag was given by the user.
+	No_Flag,
+	// No value was given by the user.
+	No_Value,
+	// The flag on the struct is missing.
+	Missing_Flag,
+	// The type itself isn't supported.
+	Unsupported_Type,
+}
+
+// Raised during parsing, naturally.
+Parse_Error :: struct {
+	reason: Unified_Parse_Error_Reason,
+	message: string,
+}
+
+// Raised during parsing.
+// Provides more granular information than what just a string could hold.
+Open_File_Error :: struct {
+	filename: string,
+	errno: os.Errno,
+	mode: int,
+	perms: int,
+}
+
+// Raised during parsing.
+Help_Request :: distinct bool
+
+
+// Raised after parsing, during validation.
+Validation_Error :: struct {
+	message: string,
+}
+
+Error :: union {
+	Parse_Error,
+	Open_File_Error,
+	Help_Request,
+	Validation_Error,
+}

+ 9 - 0
core/flags/errors_bsd.odin

@@ -0,0 +1,9 @@
+//+build freebsd, netbsd, openbsd
+package flags
+
+import "base:runtime"
+
+Unified_Parse_Error_Reason :: union #shared_nil {
+	Parse_Error_Reason,
+	runtime.Allocator_Error,
+}

+ 11 - 0
core/flags/errors_nonbsd.odin

@@ -0,0 +1,11 @@
+//+build !freebsd !netbsd !openbsd
+package flags
+
+import "base:runtime"
+import "core:net"
+
+Unified_Parse_Error_Reason :: union #shared_nil {
+	Parse_Error_Reason,
+	runtime.Allocator_Error,
+	net.Parse_Endpoint_Error,
+}

+ 132 - 0
core/flags/example/example.odin

@@ -0,0 +1,132 @@
+package core_flags_example
+
+import "base:runtime"
+import "core:flags"
+import "core:fmt"
+import "core:net"
+import "core:os"
+import "core:time/datetime"
+
+
+Fixed_Point1_1 :: struct {
+	integer: u8,
+	fractional: u8,
+}
+
+Optimization_Level :: enum {
+	Slow,
+	Fast,
+	Warp_Speed,
+	Ludicrous_Speed,
+}
+
+// It's simple but powerful.
+my_custom_type_setter :: proc(
+	data: rawptr,
+	data_type: typeid,
+	unparsed_value: string,
+	args_tag: string,
+) -> (
+	error: string,
+	handled: bool,
+	alloc_error: runtime.Allocator_Error,
+) {
+	if data_type == Fixed_Point1_1 {
+		handled = true
+		ptr := cast(^Fixed_Point1_1)data
+
+		// precision := flags.get_subtag(args_tag, "precision")
+
+		if len(unparsed_value) == 3 {
+			ptr.integer = unparsed_value[0] - '0'
+			ptr.fractional = unparsed_value[2] - '0'
+		} else {
+			error = "Incorrect format. Must be in the form of `i.f`."
+		}
+
+		// Perform sanity checking here in the type parsing phase.
+		//
+		// The validation phase is flag-specific.
+		if !(0 <= ptr.integer && ptr.integer < 10) || !(0 <= ptr.fractional && ptr.fractional < 10) {
+			error = "Incorrect format. Must be between `0.0` and `9.9`."
+		}
+	}
+
+	return
+}
+
+my_custom_flag_checker :: proc(
+	model: rawptr,
+	name: string,
+	value: any,
+	args_tag: string,
+) -> (error: string) {
+	if name == "iterations" {
+		v := value.(int)
+		if !(1 <= v && v < 5) {
+			error = "Iterations only supports 1 ..< 5."
+		}
+	}
+
+	return
+}
+
+Distinct_Int :: distinct int
+
+main :: proc() {
+	Options :: struct {
+
+		file: os.Handle `args:"pos=0,required,file=r" usage:"Input file."`,
+		output: os.Handle `args:"pos=1,file=cw" usage:"Output file."`,
+
+		hub: net.Host_Or_Endpoint `usage:"Internet address to contact for updates."`,
+		schedule: datetime.DateTime `usage:"Launch tasks at this time."`,
+
+		opt: Optimization_Level `usage:"Optimization level."`,
+		todo: [dynamic]string `usage:"Todo items."`,
+
+		accuracy: Fixed_Point1_1 `args:"required" usage:"Lenience in FLOP calculations."`,
+		iterations: int `usage:"Run this many times."`,
+
+		// Note how the parser will transform this flag's name into `special-int`.
+		special_int: Distinct_Int `args:"indistinct" usage:"Able to set distinct types."`,
+
+		quat: quaternion256,
+
+		bits: bit_set[0..<8],
+
+		// Many different requirement styles:
+
+		// gadgets: [dynamic]string `args:"required=1" usage:"gadgets"`,
+		// widgets: [dynamic]string `args:"required=<3" usage:"widgets"`,
+		// foos: [dynamic]string `args:"required=2<4"`,
+		// bars: [dynamic]string `args:"required=3<4"`,
+		// bots: [dynamic]string `args:"required"`,
+
+		// (Maps) Only available in Odin style:
+
+		// assignments: map[string]u8 `args:"name=assign" usage:"Number of jobs per worker."`,
+
+		// (Variadic) Only available in UNIX style:
+
+		// bots: [dynamic]string `args:"variadic=2,required"`,
+
+		verbose: bool `usage:"Show verbose output."`,
+		debug: bool `args:"hidden" usage:"print debug info"`,
+
+		varg: [dynamic]string `usage:"Any extra arguments go here."`,
+	}
+
+	opt: Options
+	style : flags.Parsing_Style = .Odin
+
+	flags.register_type_setter(my_custom_type_setter)
+	flags.register_flag_checker(my_custom_flag_checker)
+	flags.parse_or_exit(&opt, os.args, style)
+
+	fmt.printfln("%#v", opt)
+
+	if opt.output != 0 {
+		os.write_string(opt.output, "Hellope!\n")
+	}
+}

+ 262 - 0
core/flags/internal_assignment.odin

@@ -0,0 +1,262 @@
+//+private
+package flags
+
+import "base:intrinsics"
+@require import "base:runtime"
+import "core:container/bit_array"
+@require import "core:fmt"
+@require import "core:mem"
+import "core:reflect"
+@require import "core:strconv"
+@require import "core:strings"
+
+// Push a positional argument onto a data struct, checking for specified
+// positionals first before adding it to a fallback field.
+@(optimization_mode="size")
+push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
+	if bit_array.get(&parser.filled_pos, parser.filled_pos.max_index) {
+		// The max index is set, which means we're out of space.
+		// Add one free bit by setting the index above to false.
+		bit_array.set(&parser.filled_pos, 1 + parser.filled_pos.max_index, false)
+	}
+
+	pos: int = ---
+	{
+		iter := bit_array.make_iterator(&parser.filled_pos)
+		ok: bool
+		pos, ok = bit_array.iterate_by_unset(&iter)
+
+		// This may be an allocator error.
+		assert(ok, "Unable to find a free spot in the positional bit_array.")
+	}
+
+	field, index, has_pos_assigned := get_field_by_pos(model, pos)
+
+	if !has_pos_assigned {
+		when intrinsics.type_has_field(T, INTERNAL_VARIADIC_FLAG) {
+			// Add it to the fallback array.
+			field = reflect.struct_field_by_name(T, INTERNAL_VARIADIC_FLAG)
+		} else {
+			return Parse_Error {
+				.Extra_Positional,
+				fmt.tprintf("Got extra positional argument `%s` with nowhere to store it.", arg),
+			}
+		}
+	}
+
+	ptr := cast(rawptr)(cast(uintptr)model + field.offset)
+	args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
+	field_name := get_field_name(field)
+	error = parse_and_set_pointer_by_type(ptr, arg, field.type, args_tag)
+	#partial switch &specific_error in error {
+	case Parse_Error:
+		specific_error.message = fmt.tprintf("Unable to set positional #%i (%s) of type %v to `%s`.%s%s",
+			pos,
+			field_name,
+			field.type,
+			arg,
+			" " if len(specific_error.message) > 0 else "",
+			specific_error.message)
+	case nil:
+		bit_array.set(&parser.filled_pos, pos)
+		bit_array.set(&parser.fields_set, index)
+	}
+
+	return
+}
+
+register_field :: proc(parser: ^Parser, field: reflect.Struct_Field, index: int) {
+	if pos, ok := get_field_pos(field); ok {
+		bit_array.set(&parser.filled_pos, pos)
+	}
+
+	bit_array.set(&parser.fields_set, index)
+}
+
+// Set a `-flag` argument, Odin-style.
+@(optimization_mode="size")
+set_odin_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (error: Error) {
+	// We make a special case for help requests.
+	switch name {
+	case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
+		return Help_Request{}
+	}
+
+	field, index := get_field_by_name(model, name) or_return
+
+	#partial switch specific_type_info in field.type.variant {
+	case runtime.Type_Info_Boolean:
+		ptr := cast(^bool)(cast(uintptr)model + field.offset)
+		ptr^ = true
+	case:
+		return Parse_Error {
+			.Bad_Value,
+			fmt.tprintf("Unable to set `%s` of type %v to true.", name, field.type),
+		}
+	}
+
+	register_field(parser, field, index)
+	return
+}
+
+// Set a `-flag` argument, UNIX-style.
+@(optimization_mode="size")
+set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args: int, error: Error) {
+	// We make a special case for help requests.
+	switch name {
+	case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
+		return 0, Help_Request{}
+	}
+
+	field, index := get_field_by_name(model, name) or_return
+
+	#partial switch specific_type_info in field.type.variant {
+	case runtime.Type_Info_Boolean:
+		ptr := cast(^bool)(cast(uintptr)model + field.offset)
+		ptr^ = true
+	case runtime.Type_Info_Dynamic_Array:
+		future_args = 1
+		if tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
+			if length, is_variadic := get_struct_subtag(tag, SUBTAG_VARIADIC); is_variadic {
+				// Variadic arrays may specify how many arguments they consume at once.
+				// Otherwise, they take everything that's left.
+				if value, value_ok := strconv.parse_u64_of_base(length, 10); value_ok {
+					future_args = cast(int)value
+				} else {
+					future_args = max(int)
+				}
+			}
+		}
+	case:
+		// `--flag`, waiting on its value.
+		future_args = 1
+	}
+
+	register_field(parser, field, index)
+	return
+}
+
+// Set a `-flag:option` argument.
+@(optimization_mode="size")
+set_option :: proc(model: ^$T, parser: ^Parser, name, option: string) -> (error: Error) {
+	field, index := get_field_by_name(model, name) or_return
+
+	if len(option) == 0 {
+		return Parse_Error {
+			.No_Value,
+			fmt.tprintf("Setting `%s` to an empty value is meaningless.", name),
+		}
+	}
+
+	// Guard against incorrect syntax.
+	#partial switch specific_type_info in field.type.variant {
+	case runtime.Type_Info_Map:
+		return Parse_Error {
+			.No_Value,
+			fmt.tprintf("Unable to set `%s` of type %v to `%s`. Are you missing an `=`? The correct format is `map:key=value`.", name, field.type, option),
+		}
+	}
+
+	ptr := cast(rawptr)(cast(uintptr)model + field.offset)
+	args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS)
+	error = parse_and_set_pointer_by_type(ptr, option, field.type, args_tag)
+	#partial switch &specific_error in error {
+	case Parse_Error:
+		specific_error.message = fmt.tprintf("Unable to set `%s` of type %v to `%s`.%s%s",
+			name,
+			field.type,
+			option,
+			" " if len(specific_error.message) > 0 else "",
+			specific_error.message)
+	case nil:
+		register_field(parser, field, index)
+	}
+
+	return
+}
+
+// Set a `-map:key=value` argument.
+@(optimization_mode="size")
+set_key_value :: proc(model: ^$T, parser: ^Parser, name, key, value: string) -> (error: Error) {
+	field, index := get_field_by_name(model, name) or_return
+
+	#partial switch specific_type_info in field.type.variant {
+	case runtime.Type_Info_Map:
+		key := key
+		key_ptr := cast(rawptr)&key
+		key_cstr: cstring
+		if reflect.is_cstring(specific_type_info.key) {
+			// We clone the key here, because it's liable to be a slice of an
+			// Odin string, and we need to put a NUL terminator in it.
+			key_cstr = strings.clone_to_cstring(key)
+			key_ptr = &key_cstr
+		}
+		defer if key_cstr != nil {
+			delete(key_cstr)
+		}
+
+		raw_map := (^runtime.Raw_Map)(cast(uintptr)model + field.offset)
+
+		hash := specific_type_info.map_info.key_hasher(key_ptr, runtime.map_seed(raw_map^))
+
+		backing_alloc := false
+		elem_backing: []byte
+		value_ptr: rawptr
+
+		if raw_map.allocator.procedure == nil {
+			raw_map.allocator = context.allocator
+		} else {
+			value_ptr = runtime.__dynamic_map_get(raw_map,
+				specific_type_info.map_info,
+				hash,
+				key_ptr,
+			)
+		}
+
+		if value_ptr == nil {
+			alloc_error: runtime.Allocator_Error = ---
+			elem_backing, alloc_error = mem.alloc_bytes(specific_type_info.value.size, specific_type_info.value.align)
+			if elem_backing == nil {
+				return Parse_Error {
+					alloc_error,
+					"Failed to allocate element backing for map value.",
+				}
+			}
+
+			backing_alloc = true
+			value_ptr = raw_data(elem_backing)
+		}
+
+		args_tag, _ := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
+		error = parse_and_set_pointer_by_type(value_ptr, value, specific_type_info.value, args_tag)
+		#partial switch &specific_error in error {
+		case Parse_Error:
+			specific_error.message = fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.%s%s",
+				name,
+				field.type,
+				key,
+				value,
+				" " if len(specific_error.message) > 0 else "",
+				specific_error.message)
+		}
+
+		if backing_alloc {
+			runtime.__dynamic_map_set(raw_map,
+				specific_type_info.map_info,
+				hash,
+				key_ptr,
+				value_ptr,
+			)
+
+			delete(elem_backing)
+		}
+
+		register_field(parser, field, index)
+		return
+	}
+
+	return Parse_Error {
+		.Bad_Value,
+		fmt.tprintf("Unable to set `%s` of type %v with key=value: `%s`=`%s`.", name, field.type, key, value),
+	}
+}

+ 170 - 0
core/flags/internal_parsing.odin

@@ -0,0 +1,170 @@
+//+private
+package flags
+
+import "core:container/bit_array"
+import "core:strconv"
+import "core:strings"
+
+// Used to group state together.
+Parser :: struct {
+	// `fields_set` tracks which arguments have been set.
+	// It uses their struct field index.
+	fields_set: bit_array.Bit_Array,
+
+	// `filled_pos` tracks which arguments have been filled into positional
+	// spots, much like how `fmt` treats them.
+	filled_pos: bit_array.Bit_Array,
+}
+
+parse_one_odin_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (error: Error) {
+	arg := arg
+
+	if strings.has_prefix(arg, "-") {
+		arg = arg[1:]
+
+		flag: string
+		assignment_rune: rune
+		find_assignment: for r, i in arg {
+			switch r {
+			case ':', '=':
+				assignment_rune = r
+				flag = arg[:i]
+				arg = arg[1 + i:]
+				break find_assignment
+			case:
+				continue find_assignment
+			}
+		}
+
+		if assignment_rune == 0 {
+			if len(arg) == 0 {
+				return Parse_Error {
+					.No_Flag,
+					"No flag was given.",
+				}
+			}
+
+			// -flag
+			set_odin_flag(model, parser, arg) or_return
+
+		} else if assignment_rune == ':' {
+			// -flag:option <OR> -map:key=value
+			error = set_option(model, parser, flag, arg)
+
+			if error != nil {
+				// -flag:option did not work, so this may be a -map:key=value set.
+				find_equals: for r, i in arg {
+					if r == '=' {
+						key := arg[:i]
+						arg = arg[1 + i:]
+						error = set_key_value(model, parser, flag, key, arg)
+						break find_equals
+					}
+				}
+			}
+
+		} else {
+			// -flag=option, alternative syntax
+			set_option(model, parser, flag, arg) or_return
+		}
+
+	} else {
+		// positional
+		error = push_positional(model, parser, arg)
+	}
+
+	return
+}
+
+parse_one_unix_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (
+	future_args: int,
+	current_flag: string,
+	error: Error,
+) {
+	arg := arg
+
+	if strings.has_prefix(arg, "-") {
+		// -flag
+		arg = arg[1:]
+
+		if strings.has_prefix(arg, "-") {
+			// Allow `--` to function as `-`.
+			arg = arg[1:]
+
+			if len(arg) == 0 {
+				// `--`, and only `--`.
+				// Everything from now on will be treated as an argument.
+				future_args = max(int)
+				current_flag = INTERNAL_VARIADIC_FLAG
+				return
+			}
+		}
+
+		flag: string
+		find_assignment: for r, i in arg {
+			if r == '=' {
+				// --flag=option
+				flag = arg[:i]
+				arg = arg[1 + i:]
+				error = set_option(model, parser, flag, arg)
+				return
+			}
+		}
+
+		// --flag option, potentially
+		future_args = set_unix_flag(model, parser, arg) or_return
+		current_flag = arg
+
+	} else {
+		// positional
+		error = push_positional(model, parser, arg)
+	}
+
+	return
+}
+
+// Parse a number of requirements specifier.
+//
+// Examples:
+//
+//    `min`
+//    `<max`
+//    `min<max`
+parse_requirements :: proc(str: string) -> (minimum, maximum: int, ok: bool) {
+	if len(str) == 0 {
+		return 1, max(int), true
+	}
+
+	if less_than := strings.index_byte(str, '<'); less_than != -1 {
+		if len(str) == 1 {
+			return 0, 0, false
+		}
+
+		#no_bounds_check left  := str[:less_than]
+		#no_bounds_check right := str[1 + less_than:]
+
+		if left_value, parse_ok := strconv.parse_u64_of_base(left, 10); parse_ok {
+			minimum = cast(int)left_value
+		} else if len(left) > 0 {
+			return 0, 0, false
+		}
+
+		if right_value, parse_ok := strconv.parse_u64_of_base(right, 10); parse_ok {
+			maximum = cast(int)right_value
+		} else if len(right) > 0 {
+			return 0, 0, false
+		} else {
+			maximum = max(int)
+		}
+	} else {
+		if value, parse_ok := strconv.parse_u64_of_base(str, 10); parse_ok {
+			minimum = cast(int)value
+			maximum = max(int)
+		} else {
+			return 0, 0, false
+		}
+	}
+
+	ok = true
+	return
+}

+ 536 - 0
core/flags/internal_rtti.odin

@@ -0,0 +1,536 @@
+//+private
+package flags
+
+import "base:intrinsics"
+import "base:runtime"
+import "core:fmt"
+import "core:mem"
+import "core:os"
+import "core:reflect"
+import "core:strconv"
+import "core:strings"
+@require import "core:time"
+@require import "core:time/datetime"
+import "core:unicode/utf8"
+
+@(optimization_mode="size")
+parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool {
+	bounded_int :: proc(value, min, max: i128) -> (result: i128, ok: bool) {
+		return value, min <= value && value <= max
+	}
+
+	bounded_uint :: proc(value, max: u128) -> (result: u128, ok: bool) {
+		return value, value <= max
+	}
+
+	// NOTE(Feoramund): This procedure has been written with the goal in mind
+	// of generating the least amount of assembly, given that this library is
+	// likely to be called once and forgotten.
+	//
+	// I've rewritten the switch tables below in 3 different ways, and the
+	// current one generates the least amount of code for me on Linux AMD64.
+	//
+	// The other two ways were:
+	//
+	// - the original implementation: use of parametric polymorphism which led
+	//   to dozens of functions generated, one for each type.
+	//
+	// - a `value, ok` assignment statement with the `or_return` done at the
+	//   end of the switch, instead of inline.
+	//
+	// This seems to be the smallest way for now.
+
+	#partial switch specific_type_info in type_info.variant {
+	case runtime.Type_Info_Integer:
+		if specific_type_info.signed {
+			value := strconv.parse_i128(str) or_return
+			switch type_info.id {
+				case i8:     (cast(^i8)    ptr)^ = cast(i8)     bounded_int(value, cast(i128)min(i8),     cast(i128)max(i8)    ) or_return
+				case i16:    (cast(^i16)   ptr)^ = cast(i16)    bounded_int(value, cast(i128)min(i16),    cast(i128)max(i16)   ) or_return
+				case i32:    (cast(^i32)   ptr)^ = cast(i32)    bounded_int(value, cast(i128)min(i32),    cast(i128)max(i32)   ) or_return
+				case i64:    (cast(^i64)   ptr)^ = cast(i64)    bounded_int(value, cast(i128)min(i64),    cast(i128)max(i64)   ) or_return
+				case i128:   (cast(^i128)  ptr)^ = value
+
+				case int:    (cast(^int)   ptr)^ = cast(int)    bounded_int(value, cast(i128)min(int),    cast(i128)max(int)   ) or_return
+
+				case i16le:  (cast(^i16le) ptr)^ = cast(i16le)  bounded_int(value, cast(i128)min(i16le),  cast(i128)max(i16le) ) or_return
+				case i32le:  (cast(^i32le) ptr)^ = cast(i32le)  bounded_int(value, cast(i128)min(i32le),  cast(i128)max(i32le) ) or_return
+				case i64le:  (cast(^i64le) ptr)^ = cast(i64le)  bounded_int(value, cast(i128)min(i64le),  cast(i128)max(i64le) ) or_return
+				case i128le: (cast(^i128le)ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return
+
+				case i16be:  (cast(^i16be) ptr)^ = cast(i16be)  bounded_int(value, cast(i128)min(i16be),  cast(i128)max(i16be) ) or_return
+				case i32be:  (cast(^i32be) ptr)^ = cast(i32be)  bounded_int(value, cast(i128)min(i32be),  cast(i128)max(i32be) ) or_return
+				case i64be:  (cast(^i64be) ptr)^ = cast(i64be)  bounded_int(value, cast(i128)min(i64be),  cast(i128)max(i64be) ) or_return
+				case i128be: (cast(^i128be)ptr)^ = cast(i128be) bounded_int(value, cast(i128)min(i128be), cast(i128)max(i128be)) or_return
+			}
+		} else {
+			value := strconv.parse_u128(str) or_return
+			switch type_info.id {
+				case u8:      (cast(^u8)     ptr)^ = cast(u8)      bounded_uint(value, cast(u128)max(u8)     ) or_return
+				case u16:     (cast(^u16)    ptr)^ = cast(u16)     bounded_uint(value, cast(u128)max(u16)    ) or_return
+				case u32:     (cast(^u32)    ptr)^ = cast(u32)     bounded_uint(value, cast(u128)max(u32)    ) or_return
+				case u64:     (cast(^u64)    ptr)^ = cast(u64)     bounded_uint(value, cast(u128)max(u64)    ) or_return
+				case u128:    (cast(^u128)   ptr)^ = value
+
+				case uint:    (cast(^uint)   ptr)^ = cast(uint)    bounded_uint(value, cast(u128)max(uint)   ) or_return
+				case uintptr: (cast(^uintptr)ptr)^ = cast(uintptr) bounded_uint(value, cast(u128)max(uintptr)) or_return
+
+				case u16le:   (cast(^u16le)  ptr)^ = cast(u16le)   bounded_uint(value, cast(u128)max(u16le)  ) or_return
+				case u32le:   (cast(^u32le)  ptr)^ = cast(u32le)   bounded_uint(value, cast(u128)max(u32le)  ) or_return
+				case u64le:   (cast(^u64le)  ptr)^ = cast(u64le)   bounded_uint(value, cast(u128)max(u64le)  ) or_return
+				case u128le:  (cast(^u128le) ptr)^ = cast(u128le)  bounded_uint(value, cast(u128)max(u128le) ) or_return
+
+				case u16be:   (cast(^u16be)  ptr)^ = cast(u16be)   bounded_uint(value, cast(u128)max(u16be)  ) or_return
+				case u32be:   (cast(^u32be)  ptr)^ = cast(u32be)   bounded_uint(value, cast(u128)max(u32be)  ) or_return
+				case u64be:   (cast(^u64be)  ptr)^ = cast(u64be)   bounded_uint(value, cast(u128)max(u64be)  ) or_return
+				case u128be:  (cast(^u128be) ptr)^ = cast(u128be)  bounded_uint(value, cast(u128)max(u128be) ) or_return
+			}
+		}
+
+	case runtime.Type_Info_Rune:
+		if utf8.rune_count_in_string(str) != 1 {
+			return false
+		}
+
+		(cast(^rune)ptr)^ = utf8.rune_at_pos(str, 0)
+
+	case runtime.Type_Info_Float:
+		value := strconv.parse_f64(str) or_return
+		switch type_info.id {
+			case f16:   (cast(^f16)  ptr)^ = cast(f16)   value
+			case f32:   (cast(^f32)  ptr)^ = cast(f32)   value
+			case f64:   (cast(^f64)  ptr)^ =             value
+
+			case f16le: (cast(^f16le)ptr)^ = cast(f16le) value
+			case f32le: (cast(^f32le)ptr)^ = cast(f32le) value
+			case f64le: (cast(^f64le)ptr)^ = cast(f64le) value
+
+			case f16be: (cast(^f16be)ptr)^ = cast(f16be) value
+			case f32be: (cast(^f32be)ptr)^ = cast(f32be) value
+			case f64be: (cast(^f64be)ptr)^ = cast(f64be) value
+		}
+	
+	case runtime.Type_Info_Complex:
+		value := strconv.parse_complex128(str) or_return
+		switch type_info.id {
+			case complex128: (cast(^complex128)ptr)^ = value
+			case complex64:  (cast(^complex64) ptr)^ = cast(complex64)value
+			case complex32:  (cast(^complex32) ptr)^ = cast(complex32)value
+		}
+	
+	case runtime.Type_Info_Quaternion:
+		value := strconv.parse_quaternion256(str) or_return
+		switch type_info.id {
+			case quaternion256: (cast(^quaternion256)ptr)^ = value
+			case quaternion128: (cast(^quaternion128)ptr)^ = cast(quaternion128)value
+			case quaternion64:  (cast(^quaternion64) ptr)^ = cast(quaternion64)value
+		}
+
+	case runtime.Type_Info_String:
+		if specific_type_info.is_cstring {
+			cstr_ptr := cast(^cstring)ptr
+			if cstr_ptr != nil {
+				// Prevent memory leaks from us setting this value multiple times.
+				delete(cstr_ptr^)
+			}
+			cstr_ptr^ = strings.clone_to_cstring(str)
+		} else {
+			(cast(^string)ptr)^ = str
+		}
+
+	case runtime.Type_Info_Boolean:
+		value := strconv.parse_bool(str) or_return
+		switch type_info.id {
+			case bool: (cast(^bool) ptr)^ =           value
+			case b8:   (cast(^b8)   ptr)^ = cast(b8)  value
+			case b16:  (cast(^b16)  ptr)^ = cast(b16) value
+			case b32:  (cast(^b32)  ptr)^ = cast(b32) value
+			case b64:  (cast(^b64)  ptr)^ = cast(b64) value
+		}
+
+	case runtime.Type_Info_Bit_Set:
+		// Parse a string of 1's and 0's, from left to right,
+		// least significant bit to most significant bit.
+		value: u128
+
+		// NOTE: `upper` is inclusive, i.e: `0..=31`
+		max_bit_index := cast(u128)(1 + specific_type_info.upper - specific_type_info.lower)
+		bit_index : u128 = 0
+		#no_bounds_check for string_index : uint = 0; string_index < len(str); string_index += 1 {
+			if bit_index == max_bit_index {
+				// The string's too long for this bit_set.
+				return false
+			}
+
+			switch str[string_index] {
+			case '1':
+				value |= 1 << bit_index
+				bit_index += 1
+			case '0':
+				bit_index += 1
+				continue
+			case '_':
+				continue
+			case:
+				return false
+			}
+		}
+
+		if specific_type_info.underlying != nil {
+			set_unbounded_integer_by_type(ptr, value, specific_type_info.underlying.id)
+		} else {
+			switch 8*type_info.size {
+			case 8:   (cast(^u8)   ptr)^ = cast(u8)   value
+			case 16:  (cast(^u16)  ptr)^ = cast(u16)  value
+			case 32:  (cast(^u32)  ptr)^ = cast(u32)  value
+			case 64:  (cast(^u64)  ptr)^ = cast(u64)  value
+			case 128: (cast(^u128) ptr)^ = cast(u128) value
+			}
+		}
+
+	case:
+		fmt.panicf("Unsupported base data type: %v", specific_type_info)
+	}
+
+	return true
+}
+
+// This proc exists to make error handling easier, since everything in the base
+// type one above works on booleans. It's a simple parsing error if it's false.
+//
+// However, here we have to be more careful about how we handle errors,
+// especially with files.
+//
+// We want to provide as informative as an error as we can.
+@(optimization_mode="size", disabled=NO_CORE_NAMED_TYPES)
+parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: typeid, arg_tag: string, out_error: ^Error) {
+	// Core types currently supported:
+	//
+	// - os.Handle
+	// - time.Time
+	// - datetime.DateTime
+	// - net.Host_Or_Endpoint
+
+	GENERIC_RFC_3339_ERROR :: "Invalid RFC 3339 string. Try this format: `yyyy-mm-ddThh:mm:ssZ`, for example `2024-02-29T16:30:00Z`."
+
+	out_error^ = nil
+
+	if data_type == os.Handle {
+		// NOTE: `os` is hopefully available everywhere, even if it might panic on some calls.
+		wants_read := false
+		wants_write := false
+		mode: int
+
+		if file, ok := get_struct_subtag(arg_tag, SUBTAG_FILE); ok {
+			for i := 0; i < len(file); i += 1 {
+				#no_bounds_check switch file[i] {
+				case 'r': wants_read = true
+				case 'w': wants_write = true
+				case 'c': mode |= os.O_CREATE
+				case 'a': mode |= os.O_APPEND
+				case 't': mode |= os.O_TRUNC
+				}
+			}
+		}
+
+		// Sane default.
+		// owner/group/other: r--r--r--
+		perms: int = 0o444
+
+		if wants_read && wants_write {
+			mode |= os.O_RDWR
+			perms |= 0o200
+		} else if wants_write {
+			mode |= os.O_WRONLY
+			perms |= 0o200
+		} else {
+			mode |= os.O_RDONLY
+		}
+
+		if permstr, ok := get_struct_subtag(arg_tag, SUBTAG_PERMS); ok {
+			if value, parse_ok := strconv.parse_u64_of_base(permstr, 8); parse_ok {
+				perms = cast(int)value
+			}
+		}
+
+		handle, errno := os.open(str, mode, perms)
+		if errno != 0 {
+			// NOTE(Feoramund): os.Errno is system-dependent, and there's
+			// currently no good way to translate them all into strings.
+			//
+			// The upcoming `os2` package will hopefully solve this.
+			//
+			// We can at least provide the number for now, so the user can look
+			// it up.
+			out_error^ = Open_File_Error {
+				str,
+				errno,
+				mode,
+				perms,
+			}
+			return
+		}
+
+		(cast(^os.Handle)ptr)^ = handle
+		return
+	}
+
+	when IMPORTING_TIME {
+		if data_type == time.Time {
+			// NOTE: The leap second data is discarded.
+			res, consumed := time.rfc3339_to_time_utc(str)
+			if consumed == 0 {
+				// The RFC 3339 parsing facilities provide no indication as to what
+				// went wrong, so just treat it as a regular parsing error.
+				out_error^ = Parse_Error {
+					.Bad_Value,
+					GENERIC_RFC_3339_ERROR,
+				}
+				return
+			}
+
+			(cast(^time.Time)ptr)^ = res
+			return
+		} else if data_type == datetime.DateTime {
+			// NOTE: The UTC offset and leap second data are discarded.
+			res, _, _, consumed := time.rfc3339_to_components(str)
+			if consumed == 0 {
+				out_error^ = Parse_Error {
+					.Bad_Value,
+					GENERIC_RFC_3339_ERROR,
+				}
+				return
+			}
+
+			(cast(^datetime.DateTime)ptr)^ = res
+			return
+		}
+	}
+
+	when IMPORTING_NET {
+		if try_net_parse_workaround(data_type, str, ptr, out_error) {
+			return
+		}
+	}
+
+	out_error ^= Parse_Error {
+		// The caller will add more details.
+		.Unsupported_Type,
+		"",
+	}
+}
+
+@(optimization_mode="size")
+set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
+	switch data_type {
+	case i8:      (cast(^i8)     ptr)^ = cast(i8)      value
+	case i16:     (cast(^i16)    ptr)^ = cast(i16)     value
+	case i32:     (cast(^i32)    ptr)^ = cast(i32)     value
+	case i64:     (cast(^i64)    ptr)^ = cast(i64)     value
+	case i128:    (cast(^i128)   ptr)^ = cast(i128)    value
+
+	case int:     (cast(^int)    ptr)^ = cast(int)     value
+
+	case i16le:   (cast(^i16le)  ptr)^ = cast(i16le)   value
+	case i32le:   (cast(^i32le)  ptr)^ = cast(i32le)   value
+	case i64le:   (cast(^i64le)  ptr)^ = cast(i64le)   value
+	case i128le:  (cast(^i128le) ptr)^ = cast(i128le)  value
+
+	case i16be:   (cast(^i16be)  ptr)^ = cast(i16be)   value
+	case i32be:   (cast(^i32be)  ptr)^ = cast(i32be)   value
+	case i64be:   (cast(^i64be)  ptr)^ = cast(i64be)   value
+	case i128be:  (cast(^i128be) ptr)^ = cast(i128be)  value
+
+	case u8:      (cast(^u8)     ptr)^ = cast(u8)      value
+	case u16:     (cast(^u16)    ptr)^ = cast(u16)     value
+	case u32:     (cast(^u32)    ptr)^ = cast(u32)     value
+	case u64:     (cast(^u64)    ptr)^ = cast(u64)     value
+	case u128:    (cast(^u128)   ptr)^ = cast(u128)    value
+
+	case uint:    (cast(^uint)   ptr)^ = cast(uint)    value
+	case uintptr: (cast(^uintptr)ptr)^ = cast(uintptr) value
+
+	case u16le:   (cast(^u16le)  ptr)^ = cast(u16le)   value
+	case u32le:   (cast(^u32le)  ptr)^ = cast(u32le)   value
+	case u64le:   (cast(^u64le)  ptr)^ = cast(u64le)   value
+	case u128le:  (cast(^u128le) ptr)^ = cast(u128le)  value
+
+	case u16be:   (cast(^u16be)  ptr)^ = cast(u16be)   value
+	case u32be:   (cast(^u32be)  ptr)^ = cast(u32be)   value
+	case u64be:   (cast(^u64be)  ptr)^ = cast(u64be)   value
+	case u128be:  (cast(^u128be) ptr)^ = cast(u128be)  value
+
+	case rune:    (cast(^rune)   ptr)^ = cast(rune)    value
+
+	case:
+		fmt.panicf("Unsupported integer backing type: %v", data_type)
+	}
+}
+
+@(optimization_mode="size")
+parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info, arg_tag: string) -> (error: Error) {
+	#partial switch specific_type_info in type_info.variant {
+	case runtime.Type_Info_Named:
+		if global_custom_type_setter != nil {
+			// The program gets to go first.
+			error_message, handled, alloc_error := global_custom_type_setter(ptr, type_info.id, str, arg_tag)
+
+			if alloc_error != nil {
+				// There was an allocation error. Bail out.
+				return Parse_Error {
+					alloc_error,
+					"Custom type setter encountered allocation error.",
+				}
+			}
+
+			if handled {
+				// The program handled the type.
+
+				if len(error_message) != 0 {
+					// However, there was an error. Pass it along.
+					error = Parse_Error {
+						.Bad_Value,
+						error_message,
+					}
+				}
+
+				return
+			}
+		}
+
+		// Might be a named enum. Need to check here first, since we handle all enums.
+		if enum_type_info, is_enum := specific_type_info.base.variant.(runtime.Type_Info_Enum); is_enum {
+			if value, ok := reflect.enum_from_name_any(type_info.id, str); ok {
+				set_unbounded_integer_by_type(ptr, value, enum_type_info.base.id)
+			} else {
+				return Parse_Error {
+					.Bad_Value,
+					fmt.tprintf("Invalid value name. Valid names are: %s", enum_type_info.names),
+				}
+			}
+		} else {
+			parse_and_set_pointer_by_named_type(ptr, str, type_info.id, arg_tag, &error)
+			
+			if error != nil {
+				// So far, it's none of the types that we recognize.
+				// Check to see if we can set it by base type, if allowed.
+				if _, is_indistinct := get_struct_subtag(arg_tag, SUBTAG_INDISTINCT); is_indistinct {
+					return parse_and_set_pointer_by_type(ptr, str, specific_type_info.base, arg_tag)
+				}
+			}
+		}
+
+	case runtime.Type_Info_Dynamic_Array:
+		ptr := cast(^runtime.Raw_Dynamic_Array)ptr
+
+		// Try to convert the value first.
+		elem_backing, alloc_error := mem.alloc_bytes(specific_type_info.elem.size, specific_type_info.elem.align)
+		if alloc_error != nil {
+			return Parse_Error {
+				alloc_error,
+				"Failed to allocate element backing for dynamic array.",
+			}
+		}
+		defer delete(elem_backing)
+		parse_and_set_pointer_by_type(raw_data(elem_backing), str, specific_type_info.elem, arg_tag) or_return
+
+		if !runtime.__dynamic_array_resize(ptr, specific_type_info.elem.size, specific_type_info.elem.align, ptr.len + 1) {
+			// NOTE: This is purely an assumption that it's OOM.
+			// Regardless, the resize failed.
+			return Parse_Error {
+				runtime.Allocator_Error.Out_Of_Memory,
+				"Failed to resize dynamic array.",
+			}
+		}
+
+		subptr := cast(rawptr)(
+			cast(uintptr)ptr.data +
+			cast(uintptr)((ptr.len - 1) * specific_type_info.elem.size))
+		mem.copy(subptr, raw_data(elem_backing), len(elem_backing))
+
+	case runtime.Type_Info_Enum:
+		// This is a nameless enum.
+		// The code here is virtually the same as above for named enums.
+		if value, ok := reflect.enum_from_name_any(type_info.id, str); ok {
+			set_unbounded_integer_by_type(ptr, value, specific_type_info.base.id)
+		} else {
+			return Parse_Error {
+				.Bad_Value,
+				fmt.tprintf("Invalid value name. Valid names are: %s", specific_type_info.names),
+			}
+		}
+
+	case:
+		if !parse_and_set_pointer_by_base_type(ptr, str, type_info) {
+			return Parse_Error {
+				// The caller will add more details.
+				.Bad_Value,
+				"",
+			}
+		}
+	}
+
+	return
+}
+
+get_struct_subtag :: get_subtag
+
+get_field_name :: proc(field: reflect.Struct_Field) -> string {
+	if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
+		if name_subtag, name_ok := get_struct_subtag(args_tag, SUBTAG_NAME); name_ok {
+			return name_subtag
+		}
+	}
+
+	name, _ := strings.replace_all(field.name, "_", "-", context.temp_allocator)
+	return name
+}
+
+get_field_pos :: proc(field: reflect.Struct_Field) -> (int, bool) {
+	if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
+		if pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS); pos_ok {
+			if value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10); parse_ok {
+				return cast(int)value, true
+			}
+		}
+	}
+
+	return 0, false
+}
+
+// Get a struct field by its field name or `name` subtag.
+get_field_by_name :: proc(model: ^$T, name: string) -> (result: reflect.Struct_Field, index: int, error: Error) {
+	for field, i in reflect.struct_fields_zipped(T) {
+		if get_field_name(field) == name {
+			return field, i, nil
+		}
+	}
+
+	error = Parse_Error {
+		.Missing_Flag,
+		fmt.tprintf("Unable to find any flag named `%s`.", name),
+	}
+	return
+}
+
+// Get a struct field by its `pos` subtag.
+get_field_by_pos :: proc(model: ^$T, pos: int) -> (result: reflect.Struct_Field, index: int, ok: bool) {
+	for field, i in reflect.struct_fields_zipped(T) {
+		args_tag, tag_ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
+		if !tag_ok {
+			continue
+		}
+
+		pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS)
+		if !pos_ok {
+			continue
+		}
+
+		value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10)
+		if parse_ok && cast(int)value == pos {
+			return field, i, true
+		}
+	}
+
+	return
+}

+ 31 - 0
core/flags/internal_rtti_nonbsd.odin

@@ -0,0 +1,31 @@
+//+private
+//+build !freebsd !netbsd !openbsd
+package flags
+
+import "core:net"
+
+// This proc exists purely as a workaround for import restrictions.
+// Returns true if caller should return early.
+try_net_parse_workaround :: #force_inline proc (
+	data_type: typeid,
+	str: string,
+	ptr: rawptr,
+	out_error: ^Error,
+) -> bool {
+	if data_type == net.Host_Or_Endpoint {
+		addr, net_error := net.parse_hostname_or_endpoint(str)
+		if net_error != nil {
+			// We pass along `net.Error` here.
+			out_error^ = Parse_Error {
+				net_error,
+				"Invalid Host/Endpoint.",
+			}
+			return true
+		}
+
+		(cast(^net.Host_Or_Endpoint)ptr)^ = addr
+		return true
+	}
+
+	return false
+}

+ 244 - 0
core/flags/internal_validation.odin

@@ -0,0 +1,244 @@
+//+private
+package flags
+
+@require import "base:runtime"
+@require import "core:container/bit_array"
+@require import "core:fmt"
+@require import "core:mem"
+@require import "core:os"
+@require import "core:reflect"
+@require import "core:strconv"
+@require import "core:strings"
+
+// This proc is used to assert that `T` meets the expectations of the library.
+@(optimization_mode="size", disabled=ODIN_DISABLE_ASSERT)
+validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_location) {
+	positionals_assigned_so_far: bit_array.Bit_Array
+	defer bit_array.destroy(&positionals_assigned_so_far)
+
+	check_fields: for field in reflect.struct_fields_zipped(T) {
+		if style == .Unix {
+			#partial switch specific_type_info in field.type.variant {
+			case runtime.Type_Info_Map:
+				fmt.panicf("%T.%s is a map type, and these are not supported in UNIX-style parsing mode.",
+					model_type, field.name, loc = loc)
+			}
+		}
+
+		name_is_safe := true
+		defer {
+			fmt.assertf(name_is_safe, "%T.%s is using a reserved name.",
+				model_type, field.name, loc = loc)
+		}
+
+		switch field.name {
+		case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
+			name_is_safe = false
+		}
+
+		args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
+		if !ok {
+			// If it has no args tag, then we've checked all we need to.
+			// Most of this proc is validating that the subtags are sane.
+			continue
+		}
+
+		if name, has_name := get_struct_subtag(args_tag, SUBTAG_NAME); has_name {
+			fmt.assertf(len(name) > 0, "%T.%s has a zero-length `%s`.",
+				model_type, field.name, SUBTAG_NAME, loc = loc)
+
+			fmt.assertf(strings.index(name, " ") == -1, "%T.%s has a `%s` with spaces in it.",
+				model_type, field.name, SUBTAG_NAME, loc = loc)
+
+			switch name {
+			case RESERVED_HELP_FLAG, RESERVED_HELP_FLAG_SHORT:
+				name_is_safe = false
+				continue check_fields
+			case:
+				name_is_safe = true
+			}
+		}
+
+		if pos_str, has_pos := get_struct_subtag(args_tag, SUBTAG_POS); has_pos {
+			#partial switch specific_type_info in field.type.variant {
+			case runtime.Type_Info_Map:
+				fmt.panicf("%T.%s has `%s` defined, and this does not make sense on a map type.",
+					model_type, field.name, SUBTAG_POS, loc = loc)
+			}
+
+			pos_value, pos_ok := strconv.parse_u64_of_base(pos_str, 10)
+			fmt.assertf(pos_ok, "%T.%s has `%s` defined as %q but cannot be parsed a base-10 integer >= 0.",
+				model_type, field.name, SUBTAG_POS, pos_str, loc = loc)
+			fmt.assertf(!bit_array.get(&positionals_assigned_so_far, pos_value), "%T.%s has `%s` set to #%i, but that position has already been assigned to another flag.",
+				model_type, field.name, SUBTAG_POS, pos_value, loc = loc)
+			bit_array.set(&positionals_assigned_so_far, pos_value)
+		}
+
+		required_min, required_max: int
+		if requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED); is_required {
+			fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.",
+				model_type, field.name, loc = loc)
+
+			fmt.assertf(field.name != INTERNAL_VARIADIC_FLAG, "%T.%s is defined as required. This is disallowed.",
+				model_type, field.name, loc = loc)
+
+			if len(requirement) > 0 {
+				if required_min, required_max, ok = parse_requirements(requirement); ok {
+					#partial switch specific_type_info in field.type.variant {
+					case runtime.Type_Info_Dynamic_Array:
+						fmt.assertf(required_min != required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are the same. Increase the maximum by 1 for an exact number of arguments: (%i<%i)",
+							model_type,
+							field.name,
+							SUBTAG_REQUIRED,
+							requirement,
+							required_min,
+							1 + required_max,
+							loc = loc)
+
+						fmt.assertf(required_min < required_max, "%T.%s has `%s` defined as %q, but the minimum and maximum are swapped.",
+							model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
+
+					case:
+						fmt.panicf("%T.%s has `%s` defined as %q, but ranges are only supported on dynamic arrays.",
+							model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
+					}
+				} else {
+					fmt.panicf("%T.%s has `%s` defined as %q, but it cannot be parsed as a valid range.",
+						model_type, field.name, SUBTAG_REQUIRED, requirement, loc = loc)
+				}
+			}
+		}
+
+		if length, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
+			if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok {
+				fmt.assertf(value > 0,
+					"%T.%s has `%s` set to %i. It must be greater than zero.",
+					model_type, field.name, value, SUBTAG_VARIADIC, loc = loc)
+				fmt.assertf(value != 1,
+					"%T.%s has `%s` set to 1. This has no effect.",
+					model_type, field.name, SUBTAG_VARIADIC, loc = loc)
+			}
+
+			#partial switch specific_type_info in field.type.variant {
+			case runtime.Type_Info_Dynamic_Array:
+				fmt.assertf(style != .Odin,
+					"%T.%s has `%s` defined, but this only makes sense in UNIX-style parsing mode.",
+					model_type, field.name, SUBTAG_VARIADIC, loc = loc)
+			case:
+				fmt.panicf("%T.%s has `%s` defined, but this only makes sense on dynamic arrays.",
+					model_type, field.name, SUBTAG_VARIADIC, loc = loc)
+			}
+		}
+
+		allowed_to_define_file_perms: bool = ---
+		#partial switch specific_type_info in field.type.variant {
+		case runtime.Type_Info_Map:
+			allowed_to_define_file_perms = specific_type_info.value.id == os.Handle
+		case runtime.Type_Info_Dynamic_Array:
+			allowed_to_define_file_perms = specific_type_info.elem.id == os.Handle
+		case:
+			allowed_to_define_file_perms = field.type.id == os.Handle
+		}
+
+		if _, has_file := get_struct_subtag(args_tag, SUBTAG_FILE); has_file {
+			fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.",
+				model_type, field.name, SUBTAG_FILE, loc = loc)
+		}
+
+		if _, has_perms := get_struct_subtag(args_tag, SUBTAG_PERMS); has_perms {
+			fmt.assertf(allowed_to_define_file_perms, "%T.%s has `%s` defined, but it is not nor does it contain an `os.Handle` type.",
+				model_type, field.name, SUBTAG_PERMS, loc = loc)
+		}
+
+		#partial switch specific_type_info in field.type.variant {
+		case runtime.Type_Info_Map:
+			fmt.assertf(reflect.is_string(specific_type_info.key), "%T.%s is defined as a map[%T]. Only string types are currently supported as map keys.",
+				model_type,
+				field.name,
+				specific_type_info.key)
+		}
+	}
+}
+
+// Validate that all the required arguments are set and that the set arguments
+// are up to the program's expectations.
+@(optimization_mode="size")
+validate_arguments :: proc(model: ^$T, parser: ^Parser) -> Error {
+	check_fields: for field, index in reflect.struct_fields_zipped(T) {
+		was_set := bit_array.get(&parser.fields_set, index)
+
+		field_name := get_field_name(field)
+		args_tag := reflect.struct_tag_get(field.tag, TAG_ARGS)
+		requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED)
+
+		required_min, required_max: int
+		has_requirements: bool
+		if is_required {
+			required_min, required_max, has_requirements = parse_requirements(requirement)
+		}
+
+		if has_requirements && required_min == 0 {
+			// Allow `0<n` or `<n` to bypass the required condition.
+			is_required = false
+		}
+
+		if _, is_array := field.type.variant.(runtime.Type_Info_Dynamic_Array); is_array && has_requirements {
+			// If it's an array, make sure it meets the required number of arguments.
+			ptr := cast(^runtime.Raw_Dynamic_Array)(cast(uintptr)model + field.offset)
+			if required_min == required_max - 1 && ptr.len != required_min {
+				return Validation_Error {
+					fmt.tprintf("The flag `%s` had %i option%s set, but it requires exactly %i.",
+						field_name,
+						ptr.len,
+						"" if ptr.len == 1 else "s",
+						required_min),
+				}
+			} else if required_min > ptr.len || ptr.len >= required_max {
+				if required_max == max(int) {
+					return Validation_Error {
+						fmt.tprintf("The flag `%s` had %i option%s set, but it requires at least %i.",
+							field_name,
+							ptr.len,
+							"" if ptr.len == 1 else "s",
+							required_min),
+					}
+				} else {
+					return Validation_Error {
+						fmt.tprintf("The flag `%s` had %i option%s set, but it requires at least %i and at most %i.",
+							field_name,
+							ptr.len,
+							"" if ptr.len == 1 else "s",
+							required_min,
+							required_max - 1),
+					}
+				}
+			}
+		} else if !was_set {
+			if is_required {
+				return Validation_Error {
+					fmt.tprintf("The required flag `%s` was not set.", field_name),
+				}
+			}
+
+			// Not set, not required; moving on.
+			continue
+		}
+
+		// All default checks have passed. The program gets a look at it now.
+
+		if global_custom_flag_checker != nil {
+			ptr := cast(rawptr)(cast(uintptr)model + field.offset)
+			error := global_custom_flag_checker(model,
+				field.name,
+				mem.make_any(ptr, field.type.id),
+				args_tag)
+
+			if len(error) > 0 {
+				// The program reported an error message.
+				return Validation_Error { error }
+			}
+		}
+	}
+
+	return nil
+}

+ 101 - 0
core/flags/parsing.odin

@@ -0,0 +1,101 @@
+package flags
+
+@require import "core:container/bit_array"
+@require import "core:fmt"
+
+Parsing_Style :: enum {
+	// Odin-style: `-flag`, `-flag:option`, `-map:key=value`
+	Odin,
+	// UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument repeating-argument`
+	Unix,
+}
+
+/*
+Parse a slice of command-line arguments into an annotated struct.
+
+*Allocates Using Provided Allocator*
+
+By default, this proc will only allocate memory outside of its lifetime if it
+has to append to a dynamic array, set a map value, or set a cstring.
+
+The program is expected to free any allocations on `model` as a result of parsing.
+
+Inputs:
+- model: A pointer to an annotated struct with flag definitions.
+- args: A slice of strings, usually `os.args[1:]`.
+- style: The argument parsing style.
+- validate_args: If `true`, will ensure that all required arguments are set if no errors occurred.
+- strict: If `true`, will return on first error. Otherwise, parsing continues.
+- allocator: (default: context.allocator)
+- loc: The caller location for debugging purposes (default: #caller_location)
+
+Returns:
+- error: A union of errors; parsing, file open, a help request, or validation.
+*/
+@(optimization_mode="size")
+parse :: proc(
+	model: ^$T,
+	args: []string,
+	style: Parsing_Style = .Odin,
+	validate_args: bool = true,
+	strict: bool = true,
+	allocator := context.allocator,
+	loc := #caller_location,
+) -> (error: Error) {
+	context.allocator = allocator
+	validate_structure(model^, style, loc)
+
+	parser: Parser
+	defer {
+		bit_array.destroy(&parser.filled_pos)
+		bit_array.destroy(&parser.fields_set)
+	}
+
+	switch style {
+	case .Odin:
+		for arg in args {
+			error = parse_one_odin_arg(model, &parser, arg)
+			if strict && error != nil {
+				return
+			}
+		}
+
+	case .Unix:
+		// Support for `-flag argument (repeating-argument ...)`
+		future_args: int
+		current_flag: string
+
+		for i := 0; i < len(args); i += 1 {
+			#no_bounds_check arg := args[i]
+			future_args, current_flag, error = parse_one_unix_arg(model, &parser, arg)
+			if strict && error != nil {
+				return
+			}
+
+			for starting_future_args := future_args; future_args > 0; future_args -= 1 {
+				i += 1
+				if i == len(args) {
+					if future_args == starting_future_args {
+						return Parse_Error {
+							.No_Value,
+							fmt.tprintf("Expected a value for `%s` but none was given.", current_flag),
+						}
+					}
+					break
+				}
+				#no_bounds_check arg = args[i]
+
+				error = set_option(model, &parser, current_flag, arg)
+				if strict && error != nil {
+					return
+				}
+			}
+		}
+	}
+
+	if error == nil && validate_args {
+		return validate_arguments(model, &parser)
+	}
+
+	return
+}

+ 43 - 0
core/flags/rtti.odin

@@ -0,0 +1,43 @@
+package flags
+
+import "base:runtime"
+
+/*
+Handle setting custom data types.
+
+Inputs:
+- data: A raw pointer to the field where the data will go.
+- data_type: Type information on the underlying field.
+- unparsed_value: The unparsed string that the flag is being set to.
+- args_tag: The `args` tag from the struct's field.
+
+Returns:
+- error: An error message, or an empty string if no error occurred.
+- handled: A boolean indicating if the setter handles this type.
+- alloc_error: If an allocation error occurred, return it here.
+*/
+Custom_Type_Setter :: #type proc(
+	data:           rawptr,
+	data_type:      typeid,
+	unparsed_value: string,
+	args_tag:       string,
+) -> (
+	error:       string,
+	handled:     bool,
+	alloc_error: runtime.Allocator_Error,
+)
+
+@(private)
+global_custom_type_setter: Custom_Type_Setter
+
+/*
+Set the global custom type setter.
+
+Note that only one can be active at a time.
+
+Inputs:
+- setter: The type setter. Pass `nil` to disable any previously set setter.
+*/
+register_type_setter :: proc(setter: Custom_Type_Setter) {
+	global_custom_type_setter = setter
+}

+ 293 - 0
core/flags/usage.odin

@@ -0,0 +1,293 @@
+package flags
+
+import "base:runtime"
+import "core:fmt"
+import "core:io"
+import "core:reflect"
+import "core:slice"
+import "core:strconv"
+import "core:strings"
+
+/*
+Write out the documentation for the command-line arguments to a stream.
+
+Inputs:
+- out: The stream to write to.
+- data_type: The typeid of the data structure to describe.
+- program: The name of the program, usually the first argument to `os.args`.
+- style: The argument parsing style, required to show flags in the proper style.
+*/
+@(optimization_mode="size")
+write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", style: Parsing_Style = .Odin) {
+	// All flags get their tags parsed so they can be reasoned about later.
+	Flag :: struct {
+		name: string,
+		usage: string,
+		type_description: string,
+		full_length: int,
+		pos: int,
+		required_min, required_max: int,
+		is_positional: bool,
+		is_required: bool,
+		is_boolean: bool,
+		is_variadic: bool,
+		variadic_length: int,
+	}
+
+	//
+	// POSITIONAL+REQUIRED, POSITIONAL, REQUIRED, NON_REQUIRED+NON_POSITIONAL, ...
+	//
+	sort_flags :: proc(i, j: Flag) -> slice.Ordering {
+		// `varg` goes to the end.
+		if i.name == INTERNAL_VARIADIC_FLAG {
+			return .Greater
+		} else if j.name == INTERNAL_VARIADIC_FLAG {
+			return .Less
+		}
+
+		// Handle positionals.
+		if i.is_positional {
+			if j.is_positional {
+				return slice.cmp(i.pos, j.pos)
+			} else {
+				return .Less
+			}
+		} else {
+			if j.is_positional {
+				return .Greater
+			}
+		}
+
+		// Then required flags.
+		if i.is_required {
+			if !j.is_required {
+				return .Less
+			}
+		} else if j.is_required {
+			return .Greater
+		}
+
+		// Finally, sort by name.
+		return slice.cmp(i.name, j.name)
+	}
+
+	describe_array_requirements :: proc(flag: Flag) -> (spec: string) {
+		if flag.is_required {
+			if flag.required_min == flag.required_max - 1 {
+				spec = fmt.tprintf(", exactly %i", flag.required_min)
+			} else if flag.required_min > 0 && flag.required_max == max(int) {
+				spec = fmt.tprintf(", at least %i", flag.required_min)
+			} else if flag.required_min == 0 && flag.required_max > 1 {
+				spec = fmt.tprintf(", at most %i", flag.required_max - 1)
+			} else if flag.required_min > 0 && flag.required_max > 1 {
+				spec = fmt.tprintf(", between %i and %i", flag.required_min, flag.required_max - 1)
+			} else {
+				spec = ", required"
+			}
+		}
+		return
+	}
+
+	builder := strings.builder_make()
+	defer strings.builder_destroy(&builder)
+
+	flag_prefix, flag_assignment: string = ---, ---
+	switch style {
+	case .Odin: flag_prefix = "-";  flag_assignment = ":"
+	case .Unix: flag_prefix = "--"; flag_assignment = " "
+	}
+
+	visible_flags: [dynamic]Flag
+	defer delete(visible_flags)
+
+	longest_flag_length: int
+
+	for field in reflect.struct_fields_zipped(data_type) {
+		flag: Flag
+
+		if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
+			if _, is_hidden := get_struct_subtag(args_tag, SUBTAG_HIDDEN); is_hidden {
+				// Hidden flags stay hidden.
+				continue
+			}
+			if pos_str, is_pos := get_struct_subtag(args_tag, SUBTAG_POS); is_pos {
+				flag.is_positional = true
+				if pos, parse_ok := strconv.parse_u64_of_base(pos_str, 10); parse_ok {
+					flag.pos = cast(int)pos
+				}
+			}
+			if requirement, is_required := get_struct_subtag(args_tag, SUBTAG_REQUIRED); is_required {
+				flag.is_required = true
+				flag.required_min, flag.required_max, _ = parse_requirements(requirement)
+			}
+			if length_str, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
+				flag.is_variadic = true
+				if length, parse_ok := strconv.parse_u64_of_base(length_str, 10); parse_ok {
+					flag.variadic_length = cast(int)length
+				}
+			}
+		}
+
+		flag.name = get_field_name(field)
+		flag.is_boolean = reflect.is_boolean(field.type)
+
+		if usage, ok := reflect.struct_tag_lookup(field.tag, TAG_USAGE); ok {
+			flag.usage = usage
+		} else {
+			flag.usage = UNDOCUMENTED_FLAG
+		}
+
+		#partial switch specific_type_info in field.type.variant {
+		case runtime.Type_Info_Map:
+			flag.type_description = fmt.tprintf("<%v>=<%v>%s",
+				specific_type_info.key.id,
+				specific_type_info.value.id,
+				", required" if flag.is_required else "")
+
+		case runtime.Type_Info_Dynamic_Array:
+			requirement_spec := describe_array_requirements(flag)
+
+			if flag.is_variadic || flag.name == INTERNAL_VARIADIC_FLAG {
+				if flag.variadic_length == 0 {
+					flag.type_description = fmt.tprintf("<%v, ...>%s",
+						specific_type_info.elem.id,
+						requirement_spec)
+				} else {
+					flag.type_description = fmt.tprintf("<%v, %i at once>%s",
+						specific_type_info.elem.id,
+						flag.variadic_length,
+						requirement_spec)
+				}
+			} else {
+				flag.type_description = fmt.tprintf("<%v>%s", specific_type_info.elem.id,
+					requirement_spec if len(requirement_spec) > 0 else ", multiple")
+			}
+
+		case:
+			if flag.is_boolean {
+				/*
+				if flag.is_required {
+					flag.type_description = ", required"
+				}
+				*/
+			} else {
+				flag.type_description = fmt.tprintf("<%v>%s",
+					field.type.id,
+					", required" if flag.is_required else "")
+			}
+		}
+
+		if flag.name == INTERNAL_VARIADIC_FLAG {
+			flag.full_length = len(flag.type_description)
+		} else if flag.is_boolean {
+			flag.full_length = len(flag_prefix) + len(flag.name) + len(flag.type_description)
+		} else {
+			flag.full_length = len(flag_prefix) + len(flag.name) + len(flag_assignment) + len(flag.type_description)
+		}
+
+		longest_flag_length = max(longest_flag_length, flag.full_length)
+
+		append(&visible_flags, flag)
+	}
+
+	slice.sort_by_cmp(visible_flags[:], sort_flags)
+
+	// All the flags have been figured out now.
+
+	if len(program) > 0 {
+		keep_it_short := len(visible_flags) >= ONE_LINE_FLAG_CUTOFF_COUNT
+
+		strings.write_string(&builder, "Usage:\n\t")
+		strings.write_string(&builder, program)
+
+		for flag in visible_flags {
+			if keep_it_short && !(flag.is_required || flag.is_positional || flag.name == INTERNAL_VARIADIC_FLAG) {
+				continue
+			}
+
+			strings.write_byte(&builder, ' ')
+
+			if flag.name == INTERNAL_VARIADIC_FLAG {
+				strings.write_string(&builder, "...")
+				continue
+			}
+
+			if !flag.is_required { strings.write_byte(&builder, '[') }
+			if !flag.is_positional { strings.write_string(&builder, flag_prefix) }
+			strings.write_string(&builder, flag.name)
+			if !flag.is_required { strings.write_byte(&builder, ']') }
+		}
+
+		strings.write_byte(&builder, '\n')
+	}
+
+	if len(visible_flags) == 0 {
+		// No visible flags. An unusual situation, but prevent any extra work.
+		fmt.wprint(out, strings.to_string(builder))
+		return
+	}
+
+	strings.write_string(&builder, "Flags:\n")
+	
+	// Divide the positional/required arguments and the non-required arguments.
+	divider_index := -1
+	for flag, i in visible_flags {
+		if !flag.is_positional && !flag.is_required {
+			divider_index = i
+			break
+		}
+	}
+	if divider_index == 0 {
+		divider_index = -1
+	}
+
+	for flag, i in visible_flags {
+		if i == divider_index {
+			SPACING :: 2 // Number of spaces before the '|' from below.
+			strings.write_byte(&builder, '\t')
+			spacing := strings.repeat(" ", SPACING + longest_flag_length, context.temp_allocator)
+			strings.write_string(&builder, spacing)
+			strings.write_string(&builder, "|\n")
+		}
+
+		strings.write_byte(&builder, '\t')
+
+		if flag.name == INTERNAL_VARIADIC_FLAG {
+			strings.write_string(&builder, flag.type_description)
+		} else {
+			strings.write_string(&builder, flag_prefix)
+			strings.write_string(&builder, flag.name)
+			if !flag.is_boolean {
+				strings.write_string(&builder, flag_assignment)
+			}
+			strings.write_string(&builder, flag.type_description)
+		}
+
+		if strings.contains_rune(flag.usage, '\n') {
+			// Multi-line usage documentation. Let's make it look nice.
+			usage_builder := strings.builder_make(context.temp_allocator)
+
+			strings.write_byte(&usage_builder, '\n')
+			iter := strings.trim_space(flag.usage)
+			for line in strings.split_lines_iterator(&iter) {
+				strings.write_string(&usage_builder, "\t\t")
+				strings.write_string(&usage_builder, strings.trim_left_space(line))
+				strings.write_byte(&usage_builder, '\n')
+			}
+
+			strings.write_string(&builder, strings.to_string(usage_builder))
+		} else {
+			// Single-line usage documentation.
+			spacing := strings.repeat(" ",
+				(longest_flag_length) - flag.full_length,
+				context.temp_allocator)
+
+			strings.write_string(&builder, spacing)
+			strings.write_string(&builder, "  | ")
+			strings.write_string(&builder, flag.usage)
+			strings.write_byte(&builder, '\n')
+		}
+	}
+
+	fmt.wprint(out, strings.to_string(builder))
+}

+ 130 - 0
core/flags/util.odin

@@ -0,0 +1,130 @@
+package flags
+
+import "core:fmt"
+@require import "core:os"
+@require import "core:path/filepath"
+import "core:strings"
+
+/*
+Parse any arguments into an annotated struct or exit if there was an error.
+
+*Allocates Using Provided Allocator*
+
+This is a convenience wrapper over `parse` and `print_errors`.
+
+Inputs:
+- model: A pointer to an annotated struct.
+- program_args: A slice of strings, usually `os.args`.
+- style: The argument parsing style.
+- allocator: (default: context.allocator)
+- loc: The caller location for debugging purposes (default: #caller_location)
+*/
+@(optimization_mode="size")
+parse_or_exit :: proc(
+	model: ^$T,
+	program_args: []string,
+	style: Parsing_Style = .Odin,
+	allocator := context.allocator,
+	loc := #caller_location,
+) {
+	assert(len(program_args) > 0, "Program arguments slice is empty.", loc)
+
+	program := filepath.base(program_args[0])
+	args: []string
+
+	if len(program_args) > 1 {
+		args = program_args[1:]
+	}
+
+	error := parse(model, args, style)
+	if error != nil {
+		stderr := os.stream_from_handle(os.stderr)
+
+		if len(args) == 0 {
+			// No arguments entered, and there was an error; show the usage,
+			// specifically on STDERR.
+			write_usage(stderr, T, program, style)
+			fmt.wprintln(stderr)
+		}
+
+		print_errors(T, error, program, style)
+
+		_, was_help_request := error.(Help_Request)
+		os.exit(0 if was_help_request else 1)
+	}
+}
+/*
+Print out any errors that may have resulted from parsing.
+
+All error messages print to STDERR, while usage goes to STDOUT, if requested.
+
+Inputs:
+- data_type: The typeid of the data structure to describe, if usage is requested.
+- error: The error returned from `parse`.
+- style: The argument parsing style, required to show flags in the proper style, when usage is shown.
+*/
+@(optimization_mode="size")
+print_errors :: proc(data_type: typeid, error: Error, program: string, style: Parsing_Style = .Odin) {
+	stderr := os.stream_from_handle(os.stderr)
+	stdout := os.stream_from_handle(os.stdout)
+
+	switch specific_error in error {
+	case Parse_Error:
+		fmt.wprintfln(stderr, "[%T.%v] %s", specific_error, specific_error.reason, specific_error.message)
+	case Open_File_Error:
+		fmt.wprintfln(stderr, "[%T#%i] Unable to open file with perms 0o%o in mode 0x%x: %s",
+			specific_error,
+			specific_error.errno,
+			specific_error.perms,
+			specific_error.mode,
+			specific_error.filename)
+	case Validation_Error:
+		fmt.wprintfln(stderr, "[%T] %s", specific_error, specific_error.message)
+	case Help_Request:
+		write_usage(stdout, data_type, program, style)
+	}
+}
+/*
+Get the value for a subtag.
+
+This is useful if you need to parse through the `args` tag for a struct field
+on a custom type setter or custom flag checker.
+
+Example:
+
+	import "core:flags"
+	import "core:fmt"
+
+	subtag_example :: proc() {
+		args_tag := "precision=3,signed"
+
+		precision, has_precision := flags.get_subtag(args_tag, "precision")
+		signed, is_signed := flags.get_subtag(args_tag, "signed")
+
+		fmt.printfln("precision = %q, %t", precision, has_precision)
+		fmt.printfln("signed = %q, %t", signed, is_signed)
+	}
+
+Output:
+
+	precision = "3", true
+	signed = "", true
+
+*/
+get_subtag :: proc(tag, id: string) -> (value: string, ok: bool) {
+	// This proc was initially private in `internal_rtti.odin`, but given how
+	// useful it would be to custom type setters and flag checkers, it lives
+	// here now.
+
+	tag := tag
+
+	for subtag in strings.split_iterator(&tag, ",") {
+		if equals := strings.index_byte(subtag, '='); equals != -1 && id == subtag[:equals] {
+			return subtag[1 + equals:], true
+		} else if id == subtag {
+			return "", true
+		}
+	}
+
+	return
+}

+ 37 - 0
core/flags/validation.odin

@@ -0,0 +1,37 @@
+package flags
+
+/*
+Check a flag after parsing, during the validation stage.
+
+Inputs:
+- model: A raw pointer to the data structure provided to `parse`.
+- name: The name of the flag being checked.
+- value: An `any` type that contains the value to be checked.
+- args_tag: The `args` tag from within the struct.
+
+Returns:
+- error: An error message, or an empty string if no error occurred.
+*/
+Custom_Flag_Checker :: #type proc(
+	model:    rawptr,
+	name:     string,
+	value:    any,
+	args_tag: string,
+) -> (
+	error: string,
+)
+
+@(private)
+global_custom_flag_checker: Custom_Flag_Checker
+
+/*
+Set the global custom flag checker.
+
+Note that only one can be active at a time.
+
+Inputs:
+- checker: The flag checker. Pass `nil` to disable any previously set checker.
+*/
+register_flag_checker :: proc(checker: Custom_Flag_Checker) {
+	global_custom_flag_checker = checker
+}

+ 10 - 4
core/fmt/fmt.odin

@@ -1973,11 +1973,13 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
 	// fi.hash = false;
 	fi.indent += 1
 
-	if !is_soa && hash {
+	is_empty := len(info.names) == 0
+
+	if !is_soa && hash && !is_empty {
 		io.write_byte(fi.writer, '\n', &fi.n)
 	}
 	defer {
-		if hash {
+		if !is_soa && hash && !is_empty {
 			for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
 		}
 		io.write_byte(fi.writer, ']' if is_soa && the_verb == 'v' else '}', &fi.n)
@@ -2025,9 +2027,9 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
 			}
 			io.write_string(fi.writer, base_type_name, &fi.n)
 			io.write_byte(fi.writer, '{', &fi.n)
-			if hash { io.write_byte(fi.writer, '\n', &fi.n) }
+			if hash && !is_empty { io.write_byte(fi.writer, '\n', &fi.n) }
 			defer {
-				if hash {
+				if hash && !is_empty {
 					fi.indent -= 1
 					fmt_write_indent(fi)
 					fi.indent += 1
@@ -2075,6 +2077,10 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
 				if hash { io.write_string(fi.writer, ",\n", &fi.n) }
 			}
 		}
+
+		if hash && n > 0 {
+			for _ in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
+		}
 	} else {
 		field_count := -1
 		for name, i in info.names {

+ 5 - 5
core/math/big/helpers.odin

@@ -362,11 +362,11 @@ platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
 
 count_lsb :: proc { int_count_lsb, platform_count_lsb, }
 
-int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
+int_random_digit :: proc() -> (res: DIGIT) {
 	when _DIGIT_BITS == 60 { // DIGIT = u64
-		return DIGIT(rnd.uint64(r)) & _MASK
+		return DIGIT(rnd.uint64()) & _MASK
 	} else when _DIGIT_BITS == 28 { // DIGIT = u32
-		return DIGIT(rnd.uint32(r)) & _MASK
+		return DIGIT(rnd.uint32()) & _MASK
 	} else {
 		panic("Unsupported DIGIT size.")
 	}
@@ -374,12 +374,12 @@ int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
 	return 0 // We shouldn't get here.
 }
 
-int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
+int_random :: proc(dest: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
 	/*
 		Check that `a` is usable.
 	*/
 	assert_if_nil(dest)
-	return #force_inline internal_int_random(dest, bits, r, allocator)
+	return #force_inline internal_int_random(dest, bits, allocator)
 
 }
 random :: proc { int_random, }

+ 12 - 7
core/math/big/internal.odin

@@ -2178,15 +2178,20 @@ internal_int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator
 	}
 
 	/*
-		If not yet iniialized, initialize the `digit` backing with the allocator we were passed.
+		If not yet initialized, initialize the `digit` backing with the allocator we were passed.
 	*/
 	if cap == 0 {
 		a.digit = make([dynamic]DIGIT, needed, allocator)
-	} else if cap != needed {
+	} else if cap < needed {
 		/*
 			`[dynamic]DIGIT` already knows what allocator was used for it, so resize will do the right thing.
 		*/
 		resize(&a.digit, needed)
+	} else if cap > needed {
+		/*
+			Same applies to builtin.shrink here as resize above
+		*/
+		builtin.shrink(&a.digit, needed)
 	}
 	/*
 		Let's see if the allocation/resize worked as expected.
@@ -2812,11 +2817,11 @@ internal_platform_count_lsb :: #force_inline proc(a: $T) -> (count: int)
 
 internal_count_lsb :: proc { internal_int_count_lsb, internal_platform_count_lsb, }
 
-internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
+internal_int_random_digit :: proc() -> (res: DIGIT) {
 	when _DIGIT_BITS == 60 { // DIGIT = u64
-		return DIGIT(rnd.uint64(r)) & _MASK
+		return DIGIT(rnd.uint64()) & _MASK
 	} else when _DIGIT_BITS == 28 { // DIGIT = u32
-		return DIGIT(rnd.uint32(r)) & _MASK
+		return DIGIT(rnd.uint32()) & _MASK
 	} else {
 		panic("Unsupported DIGIT size.")
 	}
@@ -2824,7 +2829,7 @@ internal_int_random_digit :: proc(r: ^rnd.Rand = nil) -> (res: DIGIT) {
 	return 0 // We shouldn't get here.
 }
 
-internal_int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
+internal_int_random :: proc(dest: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
 	context.allocator = allocator
 
 	bits := bits
@@ -2841,7 +2846,7 @@ internal_int_random :: proc(dest: ^Int, bits: int, r: ^rnd.Rand = nil, allocator
 	#force_inline internal_grow(dest, digits) or_return
 
 	for i := 0; i < digits; i += 1 {
-		dest.digit[i] = int_random_digit(r) & _MASK
+		dest.digit[i] = int_random_digit() & _MASK
 	}
 	if bits > 0 {
 		dest.digit[digits - 1] &= ((1 << uint(bits)) - 1)

+ 3 - 5
core/math/big/prime.odin

@@ -12,8 +12,6 @@
 
 package math_big
 
-import rnd "core:math/rand"
-
 /*
 	Determines if an Integer is divisible by one of the _PRIME_TABLE primes.
 	Returns true if it is, false if not. 
@@ -315,7 +313,7 @@ internal_int_prime_miller_rabin :: proc(a, b: ^Int, allocator := context.allocat
 
 	Assumes `a` not to be `nil` and to have been initialized.
 */
-internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_rabin_only := USE_MILLER_RABIN_ONLY, r: ^rnd.Rand = nil, allocator := context.allocator) -> (is_prime: bool, err: Error) {
+internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_rabin_only := USE_MILLER_RABIN_ONLY, allocator := context.allocator) -> (is_prime: bool, err: Error) {
 	context.allocator = allocator
 	miller_rabin_trials := miller_rabin_trials
 
@@ -461,7 +459,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
 		for ix := 0; ix < miller_rabin_trials; ix += 1 {
 
 			// rand() guarantees the first digit to be non-zero
-			internal_random(b, _DIGIT_TYPE_BITS, r) or_return
+			internal_random(b, _DIGIT_TYPE_BITS) or_return
 
 			// Reduce digit before casting because DIGIT might be bigger than
 			// an unsigned int and "mask" on the other side is most probably not.
@@ -1183,7 +1181,7 @@ internal_int_prime_next_prime :: proc(a: ^Int, trials: int, bbs_style: bool, all
 
 	This is possibly the mother of all prime generation functions, muahahahahaha!
 */
-internal_random_prime :: proc(a: ^Int, size_in_bits: int, trials: int, flags := Primality_Flags{}, r: ^rnd.Rand = nil, allocator := context.allocator) -> (err: Error) {
+internal_random_prime :: proc(a: ^Int, size_in_bits: int, trials: int, flags := Primality_Flags{}, allocator := context.allocator) -> (err: Error) {
 	context.allocator = allocator
 	flags  := flags
 	trials := trials

+ 60 - 60
core/math/rand/distributions.odin

@@ -8,12 +8,12 @@ float32_uniform :: float32_range
 // Triangular Distribution
 // See: http://wikipedia.org/wiki/Triangular_distribution
 @(require_results)
-float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 {
+float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64)) -> f64 {
 	if hi-lo == 0 {
 		return lo
 	}
 	lo, hi := lo, hi
-	u := float64(r)
+	u := float64()
 	c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
 	if u > c {
 		u = 1-u
@@ -26,12 +26,12 @@ float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64
 // Triangular Distribution
 // See: http://wikipedia.org/wiki/Triangular_distribution
 @(require_results)
-float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 {
+float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32)) -> f32 {
 	if hi-lo == 0 {
 		return lo
 	}
 	lo, hi := lo, hi
-	u := float32(r)
+	u := float32()
 	c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
 	if u > c {
 		u = 1-u
@@ -44,25 +44,25 @@ float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32
 
 // Normal/Gaussian Distribution
 @(require_results)
-float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
-	return norm_float64(r) * stddev + mean
+float64_normal :: proc(mean, stddev: f64) -> f64 {
+	return norm_float64() * stddev + mean
 }
 // Normal/Gaussian Distribution
 @(require_results)
-float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_normal(f64(mean), f64(stddev), r))
+float32_normal :: proc(mean, stddev: f32) -> f32 {
+	return f32(float64_normal(f64(mean), f64(stddev)))
 }
 
 
 // Log Normal Distribution
 @(require_results)
-float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
-	return math.exp(float64_normal(mean, stddev, r))
+float64_log_normal :: proc(mean, stddev: f64) -> f64 {
+	return math.exp(float64_normal(mean, stddev))
 }
 // Log Normal Distribution
 @(require_results)
-float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_log_normal(f64(mean), f64(stddev), r))
+float32_log_normal :: proc(mean, stddev: f32) -> f32 {
+	return f32(float64_log_normal(f64(mean), f64(stddev)))
 }
 
 
@@ -72,8 +72,8 @@ float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
 //     0 to positive infinity if lambda >  0
 //     negative infinity to 0 if lambda <= 0
 @(require_results)
-float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
-	return - math.ln(1 - float64(r)) / lambda
+float64_exponential :: proc(lambda: f64) -> f64 {
+	return - math.ln(1 - float64()) / lambda
 }
 // Exponential Distribution
 // `lambda` is 1.0/(desired mean). It should be non-zero.
@@ -81,8 +81,8 @@ float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
 //     0 to positive infinity if lambda >  0
 //     negative infinity to 0 if lambda <= 0
 @(require_results)
-float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_exponential(f64(lambda), r))
+float32_exponential :: proc(lambda: f32) -> f32 {
+	return f32(float64_exponential(f64(lambda)))
 }
 
 
@@ -96,7 +96,7 @@ float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
 //
 // mean is alpha*beta, variance is math.pow(alpha*beta, 2)
 @(require_results)
-float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
+float64_gamma :: proc(alpha, beta: f64) -> f64 {
 	if alpha <= 0 || beta <= 0 {
 		panic(#procedure + ": alpha and beta must be > 0.0")
 	}
@@ -112,11 +112,11 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
 		bbb := alpha - LOG4
 		ccc := alpha + ainv
 		for {
-			u1 := float64(r)
+			u1 := float64()
 			if !(1e-7 < u1 && u1 < 0.9999999) {
 				continue
 			}
-			u2 := 1 - float64(r)
+			u2 := 1 - float64()
 			v := math.ln(u1 / (1 - u1)) / ainv
 			x := alpha * math.exp(v)
 			z := u1 * u1 * u2
@@ -127,12 +127,12 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
 		}
 	case alpha == 1:
 		// float64_exponential(1/beta)
-		return -math.ln(1 - float64(r)) * beta
+		return -math.ln(1 - float64()) * beta
 	case:
 		// ALGORITHM GS of Statistical Computing - Kennedy & Gentle
 		x: f64
 		for {
-			u := float64(r)
+			u := float64()
 			b := (math.e + alpha) / math.e
 			p := b * u
 			if p <= 1 {
@@ -140,7 +140,7 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
 			} else {
 				x = -math.ln((b - p) / alpha)
 			}
-			u1 := float64(r)
+			u1 := float64()
 			if p > 1 {
 				if u1 <= math.pow(x, alpha-1) {
 					break
@@ -162,8 +162,8 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
 //
 // mean is alpha*beta, variance is math.pow(alpha*beta, 2)
 @(require_results)
-float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_gamma(f64(alpha), f64(beta), r))
+float32_gamma :: proc(alpha, beta: f32) -> f32 {
+	return f32(float64_gamma(f64(alpha), f64(beta)))
 }
 
 
@@ -173,14 +173,14 @@ float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
 //
 // Return values range between 0 and 1
 @(require_results)
-float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
+float64_beta :: proc(alpha, beta: f64) -> f64 {
 	if alpha <= 0 || beta <= 0 {
 		panic(#procedure + ": alpha and beta must be > 0.0")
 	}
 	// Knuth Vol 2 Ed 3 pg 134 "the beta distribution"
-	y := float64_gamma(alpha, 1.0, r)
+	y := float64_gamma(alpha, 1.0)
 	if y != 0 {
-		return y / (y + float64_gamma(beta, 1.0, r))
+		return y / (y + float64_gamma(beta, 1.0))
 	}
 	return 0
 }
@@ -190,35 +190,35 @@ float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
 //
 // Return values range between 0 and 1
 @(require_results)
-float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_beta(f64(alpha), f64(beta), r))
+float32_beta :: proc(alpha, beta: f32) -> f32 {
+	return f32(float64_beta(f64(alpha), f64(beta)))
 }
 
 
 // Pareto distribution, `alpha` is the shape parameter.
 // https://wikipedia.org/wiki/Pareto_distribution
 @(require_results)
-float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 {
-	return math.pow(1 - float64(r), -1.0 / alpha)
+float64_pareto :: proc(alpha: f64) -> f64 {
+	return math.pow(1 - float64(), -1.0 / alpha)
 }
 // Pareto distribution, `alpha` is the shape parameter.
 // https://wikipedia.org/wiki/Pareto_distribution
 @(require_results)
-float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_pareto(f64(alpha), r))
+float32_pareto :: proc(alpha, beta: f32) -> f32 {
+	return f32(float64_pareto(f64(alpha)))
 }
 
 
 // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
 @(require_results)
-float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
-	u := 1 - float64(r)
+float64_weibull :: proc(alpha, beta: f64) -> f64 {
+	u := 1 - float64()
 	return alpha * math.pow(-math.ln(u), 1.0/beta)
 }
 // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
 @(require_results)
-float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_weibull(f64(alpha), f64(beta), r))
+float32_weibull :: proc(alpha, beta: f32) -> f32 {
+	return f32(float64_weibull(f64(alpha), f64(beta)))
 }
 
 
@@ -227,23 +227,23 @@ float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
 // `kappa` is the concentration parameter which must be >= 0
 // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
 @(require_results)
-float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
+float64_von_mises :: proc(mean_angle, kappa: f64) -> f64 {
 	// Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
 
 	mu := mean_angle
 	if kappa <= 1e-6 {
-		return math.TAU * float64(r)
+		return math.TAU * float64()
 	}
 
 	s := 0.5 / kappa
 	t := s + math.sqrt(1 + s*s)
 	z: f64
 	for {
-		u1 := float64(r)
+		u1 := float64()
 		z = math.cos(math.TAU * 0.5 * u1)
 
 		d := z / (t + z)
-		u2 := float64(r)
+		u2 := float64()
 		if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
 			break
 		}
@@ -251,7 +251,7 @@ float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
 
 	q := 1.0 / t
 	f := (q + z) / (1 + q*z)
-	u3 := float64(r)
+	u3 := float64()
 	if u3 > 0.5 {
 		return math.mod(mu + math.acos(f), math.TAU)
 	} else {
@@ -263,57 +263,57 @@ float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
 // `kappa` is the concentration parameter which must be >= 0
 // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
 @(require_results)
-float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_von_mises(f64(mean_angle), f64(kappa), r))
+float32_von_mises :: proc(mean_angle, kappa: f32) -> f32 {
+	return f32(float64_von_mises(f64(mean_angle), f64(kappa)))
 }
 
 
 // Cauchy-Lorentz Distribution
 // `x_0` is the location, `gamma` is the scale where `gamma` > 0
 @(require_results)
-float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
+float64_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 {
 	assert(gamma > 0)
 
 	// Calculated from the inverse CDF
 
-	return math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0
+	return math.tan(math.PI * (float64() - 0.5))*gamma + x_0
 }
 // Cauchy-Lorentz Distribution
 // `x_0` is the location, `gamma` is the scale where `gamma` > 0
 @(require_results)
-float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r))
+float32_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 {
+	return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma)))
 }
 
 
 // Log Cauchy-Lorentz Distribution
 // `x_0` is the location, `gamma` is the scale where `gamma` > 0
 @(require_results)
-float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
+float64_log_cauchy_lorentz :: proc(x_0, gamma: f64) -> f64 {
 	assert(gamma > 0)
-	return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0)
+	return math.exp(math.tan(math.PI * (float64() - 0.5))*gamma + x_0)
 }
 // Log Cauchy-Lorentz Distribution
 // `x_0` is the location, `gamma` is the scale where `gamma` > 0
 @(require_results)
-float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r))
+float32_log_cauchy_lorentz :: proc(x_0, gamma: f32) -> f32 {
+	return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma)))
 }
 
 
 // Laplace Distribution
 // `b` is the scale where `b` > 0
 @(require_results)
-float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 {
+float64_laplace :: proc(mean, b: f64) -> f64 {
 	assert(b > 0)
-	p := float64(r)-0.5
+	p := float64()-0.5
 	return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
 }
 // Laplace Distribution
 // `b` is the scale where `b` > 0
 @(require_results)
-float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_laplace(f64(mean), f64(b), r))
+float32_laplace :: proc(mean, b: f32) -> f32 {
+	return f32(float64_laplace(f64(mean), f64(b)))
 }
 
 
@@ -321,18 +321,18 @@ float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
 // `eta` is the shape, `b` is the scale
 // Both `eta` and `b` must be > 0
 @(require_results)
-float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 {
+float64_gompertz :: proc(eta, b: f64) -> f64 {
 	if eta <= 0 || b <= 0 {
 		panic(#procedure + ": eta and b must be > 0.0")
 	}
 
-	p := float64(r)
+	p := float64()
 	return math.ln(1 - math.ln(1 - p)/eta)/b
 }
 // Gompertz Distribution
 // `eta` is the shape, `b` is the scale
 // Both `eta` and `b` must be > 0
 @(require_results)
-float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 {
-	return f32(float64_gompertz(f64(eta), f64(b), r))
+float32_gompertz :: proc(eta, b: f32) -> f32 {
+	return f32(float64_gompertz(f64(eta), f64(b)))
 }

+ 4 - 4
core/math/rand/exp.odin

@@ -16,7 +16,7 @@ import "core:math"
 //    https://www.jstatsoft.org/article/view/v005i08 [web page]
 //
 @(require_results)
-exp_float64 :: proc(r: ^Rand = nil) -> f64 {
+exp_float64 :: proc() -> f64 {
 	re :: 7.69711747013104972
 
 	@(static, rodata)
@@ -199,16 +199,16 @@ exp_float64 :: proc(r: ^Rand = nil) -> f64 {
 	}
 
 	for {
-		j := uint32(r)
+		j := uint32()
 		i := j & 0xFF
 		x := f64(j) * f64(we[i])
 		if j < ke[i] {
 			return x
 		}
 		if i == 0 {
-			return re - math.ln(float64(r))
+			return re - math.ln(float64())
 		}
-		if fe[i]+f32(float64(r))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
+		if fe[i]+f32(float64())*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
 			return x
 		}
 	}

+ 5 - 12
core/math/rand/normal.odin

@@ -18,7 +18,7 @@ import "core:math"
 //    https://www.jstatsoft.org/article/view/v005i08 [web page]
 //
 @(require_results)
-norm_float64 :: proc(r: ^Rand = nil) -> f64 {
+norm_float64 :: proc() -> f64 {
 	rn :: 3.442619855899
 
 	@(static, rodata)
@@ -115,15 +115,8 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
 		0.008624485, 0.005548995, 0.0026696292,
 	}
 
-	r := r
-	if r == nil {
-		// NOTE(bill, 2020-09-07): Do this so that people can
-		// enforce the global random state if necessary with `nil`
-		r = &global_rand
-	}
-
 	for {
-		j := i32(uint32(r))
+		j := i32(uint32())
 		i := j & 0x7f
 		x := f64(j) * f64(wn[i])
 		if u32(abs(j)) < kn[i] {
@@ -133,15 +126,15 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
 
 		if i == 0 {
 			for {
-				x = -math.ln(float64(r)) * (1.0/ rn)
-				y := -math.ln(float64(r))
+				x = -math.ln(float64()) * (1.0/ rn)
+				y := -math.ln(float64())
 				if y+y >= x*x {
 					break
 				}
 			}
 			return j > 0 ? rn + x : -rn - x
 		}
-		if fn[i]+f32(float64(r))*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
+		if fn[i]+f32(float64())*(fn[i-1]-fn[i]) < f32(math.exp(-0.5*x*x)) {
 			return x
 		}
 	}

+ 63 - 252
core/math/rand/rand.odin

@@ -5,22 +5,22 @@ Package core:math/rand implements various random number generators
 package rand
 
 import "base:intrinsics"
-import "core:crypto"
+import "base:runtime"
 import "core:math"
 import "core:mem"
 
-Rand :: struct {
-	state: u64,
-	inc:   u64,
-	is_system: bool,
-}
-
+Default_Random_State :: runtime.Default_Random_State
+default_random_generator :: runtime.default_random_generator
 
-@(private)
-global_rand := create(u64(intrinsics.read_cycle_counter()))
+create :: proc(seed: u64) -> (state: Default_Random_State) {
+	seed := seed
+	runtime.default_random_generator(&state)
+	runtime.default_random_generator_proc(&state, .Reset, ([^]byte)(&seed)[:size_of(seed)])
+	return
+}
 
 /*
-Sets the seed used by the global random number generator.
+Reset the seed used by the context.random_generator.
 
 Inputs:
 - seed: The seed value
@@ -37,139 +37,46 @@ Example:
 Possible Output:
 
 	10
-
 */
+@(deprecated="Prefer `rand.reset`")
 set_global_seed :: proc(seed: u64) {
-	init(&global_rand, seed)
+	runtime.random_generator_reset_u64(context.random_generator, seed)
 }
 
 /*
-Creates a new random number generator.
+Reset the seed used by the context.random_generator.
 
 Inputs:
-- seed: The seed value to create the random number generator with
-
-Returns:
-- res: The created random number generator
-
-Example:
-	import "core:math/rand"
-	import "core:fmt"
-
-	create_example :: proc() {
-		my_rand := rand.create(1)
-		fmt.println(rand.uint64(&my_rand))
-	}
-
-Possible Output:
-
-	10
-
-*/
-@(require_results)
-create :: proc(seed: u64) -> (res: Rand) {
-	r: Rand
-	init(&r, seed)
-	return r
-}
-
-
-/*
-Initialises a random number generator.
-
-Inputs:
-- r: The random number generator to initialise
-- seed: The seed value to initialise this random number generator
+- seed: The seed value
 
 Example:
 	import "core:math/rand"
 	import "core:fmt"
 
-	init_example :: proc() {
-		my_rand: rand.Rand
-		rand.init(&my_rand, 1)
-		fmt.println(rand.uint64(&my_rand))
+	set_global_seed_example :: proc() {
+		rand.set_global_seed(1)
+		fmt.println(rand.uint64())
 	}
 
 Possible Output:
 
 	10
-
 */
-init :: proc(r: ^Rand, seed: u64) {
-	r.state = 0
-	r.inc = (seed << 1) | 1
-	_random_u64(r)
-	r.state += seed
-	_random_u64(r)
+reset :: proc(seed: u64) {
+	runtime.random_generator_reset_u64(context.random_generator, seed)
 }
 
-/*
-Initialises a random number generator to use the system random number generator.
-The system random number generator is platform specific, and not supported
-on all targets.
-
-Inputs:
-- r: The random number generator to use the system random number generator
-
-WARNING: Panics if the system random number generator is not supported.
-Support can be determined via the `core:crypto.HAS_RAND_BYTES` constant.
-
-Example:
-	import "core:crypto"
-	import "core:math/rand"
-	import "core:fmt"
-
-	init_as_system_example :: proc() {
-		my_rand: rand.Rand
-		switch crypto.HAS_RAND_BYTES {
-		case true:
-			rand.init_as_system(&my_rand)
-			fmt.println(rand.uint64(&my_rand))
-		case false:
-			fmt.println("system random not supported!")
-		}
-	}
-
-Possible Output:
-
-	10
-
-*/
-init_as_system :: proc(r: ^Rand) {
-	if !crypto.HAS_RAND_BYTES {
-		panic(#procedure + " is not supported on this platform yet")
-	}
-	r.state = 0
-	r.inc   = 0
-	r.is_system = true
-}
 
 @(private)
-_random_u64 :: proc(r: ^Rand) -> u64 {
-	r := r
-	switch {
-	case r == nil:
-		r = &global_rand
-	case r.is_system:
-		value: u64
-		crypto.rand_bytes((cast([^]u8)&value)[:size_of(u64)])
-		return value
-	}
-
-	old_state := r.state
-	r.state = old_state * 6364136223846793005 + (r.inc|1)
-	xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
-	rot := (old_state >> 59)
-	return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
+_random_u64 :: proc() -> (res: u64) {
+	ok := runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res))
+	assert(ok, "uninitialized context.random_generator")
+	return
 }
 
 /*
 Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
 
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
 Returns:
 - val: A random unsigned 32 bit value
 
@@ -178,11 +85,7 @@ Example:
 	import "core:fmt"
 
 	uint32_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.uint32())
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.uint32(&my_rand))
 	}
 
 Possible Output:
@@ -192,14 +95,11 @@ Possible Output:
 
 */
 @(require_results)
-uint32 :: proc(r: ^Rand = nil) -> (val: u32) { return u32(_random_u64(r)) }
+uint32 :: proc() -> (val: u32) { return u32(_random_u64()) }
 
 /*
 Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
 
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
 Returns:
 - val: A random unsigned 64 bit value
 
@@ -208,11 +108,7 @@ Example:
 	import "core:fmt"
 
 	uint64_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.uint64())
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.uint64(&my_rand))
 	}
 
 Possible Output:
@@ -222,14 +118,11 @@ Possible Output:
 
 */
 @(require_results)
-uint64 :: proc(r: ^Rand = nil) -> (val: u64) { return _random_u64(r) }
+uint64 :: proc() -> (val: u64) { return _random_u64() }
 
 /*
 Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
 
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
 Returns:
 - val: A random unsigned 128 bit value
 
@@ -238,11 +131,7 @@ Example:
 	import "core:fmt"
 
 	uint128_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.uint128())
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.uint128(&my_rand))
 	}
 
 Possible Output:
@@ -252,9 +141,9 @@ Possible Output:
 
 */
 @(require_results)
-uint128 :: proc(r: ^Rand = nil) -> (val: u128) {
-	a := u128(_random_u64(r))
-	b := u128(_random_u64(r))
+uint128 :: proc() -> (val: u128) {
+	a := u128(_random_u64())
+	b := u128(_random_u64())
 	return (a<<64) | b
 }
 
@@ -262,9 +151,6 @@ uint128 :: proc(r: ^Rand = nil) -> (val: u128) {
 Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.  
 The sign bit will always be set to 0, thus all generated numbers will be positive.
 
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
 Returns:
 - val: A random 31 bit value
 
@@ -273,11 +159,7 @@ Example:
 	import "core:fmt"
 
 	int31_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.int31())
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.int31(&my_rand))
 	}
 
 Possible Output:
@@ -286,15 +168,12 @@ Possible Output:
 	389
 
 */
-@(require_results) int31  :: proc(r: ^Rand = nil) -> (val: i32)  { return i32(uint32(r) << 1 >> 1) }
+@(require_results) int31  :: proc() -> (val: i32)  { return i32(uint32() << 1 >> 1) }
 
 /*
 Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.  
 The sign bit will always be set to 0, thus all generated numbers will be positive.
 
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
 Returns:
 - val: A random 63 bit value
 
@@ -303,11 +182,7 @@ Example:
 	import "core:fmt"
 
 	int63_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.int63())
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.int63(&my_rand))
 	}
 
 Possible Output:
@@ -316,15 +191,12 @@ Possible Output:
 	389
 
 */
-@(require_results) int63  :: proc(r: ^Rand = nil) -> (val: i64)  { return i64(uint64(r) << 1 >> 1) }
+@(require_results) int63  :: proc() -> (val: i64)  { return i64(uint64() << 1 >> 1) }
 
 /*
 Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.  
 The sign bit will always be set to 0, thus all generated numbers will be positive.
 
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
 Returns:
 - val: A random 127 bit value
 
@@ -333,11 +205,7 @@ Example:
 	import "core:fmt"
 
 	int127_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.int127())
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.int127(&my_rand))
 	}
 
 Possible Output:
@@ -346,14 +214,13 @@ Possible Output:
 	389
 
 */
-@(require_results) int127 :: proc(r: ^Rand = nil) -> (val: i128) { return i128(uint128(r) << 1 >> 1) }
+@(require_results) int127 :: proc() -> (val: i128) { return i128(uint128() << 1 >> 1) }
 
 /*
 Generates a random 31 bit value in the range `[0, n)` using the provided random number generator. If no generator is provided the global random number generator will be used.
 
 Inputs:
 - n: The upper bound of the generated number, this value is exclusive
-- r: The random number generator to use, or nil for the global generator
 
 Returns:
 - val: A random 31 bit value in the range `[0, n)`
@@ -365,11 +232,7 @@ Example:
 	import "core:fmt"
 
 	int31_max_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.int31_max(16))
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.int31_max(1024, &my_rand))
 	}
 
 Possible Output:
@@ -379,17 +242,17 @@ Possible Output:
 
 */
 @(require_results)
-int31_max :: proc(n: i32, r: ^Rand = nil) -> (val: i32) {
+int31_max :: proc(n: i32) -> (val: i32) {
 	if n <= 0 {
 		panic("Invalid argument to int31_max")
 	}
 	if n&(n-1) == 0 {
-		return int31(r) & (n-1)
+		return int31() & (n-1)
 	}
 	max := i32((1<<31) - 1 - (1<<31)%u32(n))
-	v := int31(r)
+	v := int31()
 	for v > max {
-		v = int31(r)
+		v = int31()
 	}
 	return v % n
 }
@@ -399,7 +262,6 @@ Generates a random 63 bit value in the range `[0, n)` using the provided random
 
 Inputs:
 - n: The upper bound of the generated number, this value is exclusive
-- r: The random number generator to use, or nil for the global generator
 
 Returns:
 - val: A random 63 bit value in the range `[0, n)`
@@ -411,11 +273,7 @@ Example:
 	import "core:fmt"
 
 	int63_max_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.int63_max(16))
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.int63_max(1024, &my_rand))
 	}
 
 Possible Output:
@@ -425,17 +283,17 @@ Possible Output:
 
 */
 @(require_results)
-int63_max :: proc(n: i64, r: ^Rand = nil) -> (val: i64) {
+int63_max :: proc(n: i64) -> (val: i64) {
 	if n <= 0 {
 		panic("Invalid argument to int63_max")
 	}
 	if n&(n-1) == 0 {
-		return int63(r) & (n-1)
+		return int63() & (n-1)
 	}
 	max := i64((1<<63) - 1 - (1<<63)%u64(n))
-	v := int63(r)
+	v := int63()
 	for v > max {
-		v = int63(r)
+		v = int63()
 	}
 	return v % n
 }
@@ -445,7 +303,6 @@ Generates a random 127 bit value in the range `[0, n)` using the provided random
 
 Inputs:
 - n: The upper bound of the generated number, this value is exclusive
-- r: The random number generator to use, or nil for the global generator
 
 Returns:
 - val: A random 127 bit value in the range `[0, n)`
@@ -457,11 +314,7 @@ Example:
 	import "core:fmt"
 
 	int127_max_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.int127_max(16))
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.int127_max(1024, &my_rand))
 	}
 
 Possible Output:
@@ -471,17 +324,17 @@ Possible Output:
 
 */
 @(require_results)
-int127_max :: proc(n: i128, r: ^Rand = nil) -> (val: i128) {
+int127_max :: proc(n: i128) -> (val: i128) {
 	if n <= 0 {
 		panic("Invalid argument to int127_max")
 	}
 	if n&(n-1) == 0 {
-		return int127(r) & (n-1)
+		return int127() & (n-1)
 	}
 	max := i128((1<<127) - 1 - (1<<127)%u128(n))
-	v := int127(r)
+	v := int127()
 	for v > max {
-		v = int127(r)
+		v = int127()
 	}
 	return v % n
 }
@@ -491,7 +344,6 @@ Generates a random integer value in the range `[0, n)` using the provided random
 
 Inputs:
 - n: The upper bound of the generated number, this value is exclusive
-- r: The random number generator to use, or nil for the global generator
 
 Returns:
 - val: A random integer value in the range `[0, n)`
@@ -503,11 +355,7 @@ Example:
 	import "core:fmt"
 
 	int_max_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.int_max(16))
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.int_max(1024, &my_rand))
 	}
 
 Possible Output:
@@ -517,23 +365,20 @@ Possible Output:
 
 */
 @(require_results)
-int_max :: proc(n: int, r: ^Rand = nil) -> (val: int) {
+int_max :: proc(n: int) -> (val: int) {
 	if n <= 0 {
 		panic("Invalid argument to int_max")
 	}
 	when size_of(int) == 4 {
-		return int(int31_max(i32(n), r))
+		return int(int31_max(i32(n)))
 	} else {
-		return int(int63_max(i64(n), r))
+		return int(int63_max(i64(n)))
 	}
 }
 
 /*
 Generates a random double floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
 
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
 Returns:
 - val: A random double floating point value in the range `[0, 1)`
 
@@ -542,11 +387,7 @@ Example:
 	import "core:fmt"
 
 	float64_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.float64())
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.float64(&my_rand))
 	}
 
 Possible Output:
@@ -555,14 +396,11 @@ Possible Output:
 	0.511
 
 */
-@(require_results) float64 :: proc(r: ^Rand = nil) -> (val: f64) { return f64(int63_max(1<<53, r)) / (1 << 53) }
+@(require_results) float64 :: proc() -> (val: f64) { return f64(int63_max(1<<53)) / (1 << 53) }
 
 /*
 Generates a random single floating point value in the range `[0, 1)` using the provided random number generator. If no generator is provided the global random number generator will be used.
 
-Inputs:
-- r: The random number generator to use, or nil for the global generator
-
 Returns:
 - val: A random single floating point value in the range `[0, 1)`
 
@@ -571,11 +409,7 @@ Example:
 	import "core:fmt"
 
 	float32_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.float32())
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.float32(&my_rand))
 	}
 
 Possible Output:
@@ -584,7 +418,7 @@ Possible Output:
 	0.511
 
 */
-@(require_results) float32 :: proc(r: ^Rand = nil) -> (val: f32) { return f32(int31_max(1<<24, r)) / (1 << 24) }
+@(require_results) float32 :: proc() -> (val: f32) { return f32(int31_max(1<<24)) / (1 << 24) }
 
 /*
 Generates a random double floating point value in the range `[low, high)` using the provided random number generator. If no generator is provided the global random number generator will be used.
@@ -594,7 +428,6 @@ WARNING: Panics if `high < low`
 Inputs:
 - low: The lower bounds of the value, this value is inclusive
 - high: The upper bounds of the value, this value is exclusive
-- r: The random number generator to use, or nil for the global generator
 
 Returns:
 - val: A random double floating point value in the range [low, high)
@@ -604,11 +437,7 @@ Example:
 	import "core:fmt"
 
 	float64_range_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.float64_range(-10, 300))
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.float64_range(600, 900, &my_rand))
 	}
 
 Possible Output:
@@ -617,9 +446,9 @@ Possible Output:
 	673.130
 
 */
-@(require_results) float64_range :: proc(low, high: f64, r: ^Rand = nil) -> (val: f64) {
+@(require_results) float64_range :: proc(low, high: f64) -> (val: f64) {
 	assert(low <= high, "low must be lower than or equal to high")
-	val = (high-low)*float64(r) + low
+	val = (high-low)*float64() + low
 	if val >= high {
 		val = max(low, high * (1 - math.F64_EPSILON))
 	}
@@ -632,7 +461,6 @@ Generates a random single floating point value in the range `[low, high)` using
 Inputs:
 - low: The lower bounds of the value, this value is inclusive
 - high: The upper bounds of the value, this value is exclusive
-- r: The random number generator to use, or nil for the global generator
 
 Returns:
 - val: A random single floating point value in the range [low, high)
@@ -644,11 +472,7 @@ Example:
 	import "core:fmt"
 
 	float32_range_example :: proc() {
-		// Using the global random number generator
 		fmt.println(rand.float32_range(-10, 300))
-		// Using local random number generator
-		my_rand := rand.create(1)
-		fmt.println(rand.float32_range(600, 900, &my_rand))
 	}
 
 Possible Output:
@@ -657,9 +481,9 @@ Possible Output:
 	673.130
 
 */
-@(require_results) float32_range :: proc(low, high: f32, r: ^Rand = nil) -> (val: f32) {
+@(require_results) float32_range :: proc(low, high: f32) -> (val: f32) {
 	assert(low <= high, "low must be lower than or equal to high")
-	val = (high-low)*float32(r) + low
+	val = (high-low)*float32() + low
 	if val >= high {
 		val = max(low, high * (1 - math.F32_EPSILON))
 	}
@@ -672,7 +496,6 @@ Due to floating point precision there is no guarantee if the upper and lower bou
 
 Inputs:
 - p: The byte slice to fill
-- r: The random number generator to use, or nil for the global generator
 
 Returns:
 - n: The number of bytes generated
@@ -682,7 +505,6 @@ Example:
 	import "core:fmt"
 
 	read_example :: proc() {
-		// Using the global random number generator
 		data: [8]byte
 		n := rand.read(data[:])
 		fmt.println(n)
@@ -696,12 +518,12 @@ Possible Output:
 
 */
 @(require_results)
-read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
+read :: proc(p: []byte) -> (n: int) {
 	pos := i8(0)
 	val := i64(0)
 	for n = 0; n < len(p); n += 1 {
 		if pos == 0 {
-			val = int63(r)
+			val = int63()
 			pos = 7
 		}
 		p[n] = byte(val)
@@ -718,7 +540,6 @@ Creates a slice of `int` filled with random values using the provided random num
 
 Inputs:
 - n: The size of the created slice
-- r: The random number generator to use, or nil for the global generator
 - allocator: (default: context.allocator)
 
 Returns:
@@ -731,16 +552,10 @@ Example:
 	import "core:fmt"
 
 	perm_example :: proc() -> (err: mem.Allocator_Error) {
-		// Using the global random number generator and using the context allocator
 		data := rand.perm(4) or_return
 		fmt.println(data)
 		defer delete(data, context.allocator)
 
-		// Using local random number generator and temp allocator
-		my_rand := rand.create(1)
-		data_tmp := rand.perm(4, &my_rand, context.temp_allocator) or_return
-		fmt.println(data_tmp)
-
 		return
 	}
 
@@ -751,10 +566,10 @@ Possible Output:
 
 */
 @(require_results)
-perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error {
+perm :: proc(n: int, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error {
 	m := make([]int, n, allocator) or_return
 	for i := 0; i < n; i += 1 {
-		j := int_max(i+1, r)
+		j := int_max(i+1)
 		m[i] = m[j]
 		m[j] = i
 	}
@@ -766,14 +581,12 @@ Randomizes the ordering of elements for the provided slice. If no generator is p
 
 Inputs:
 - array: The slice to randomize
-- r: The random number generator to use, or nil for the global generator
 
 Example:
 	import "core:math/rand"
 	import "core:fmt"
 
 	shuffle_example :: proc() {
-		// Using the global random number generator
 		data: [4]int = { 1, 2, 3, 4 }
 		fmt.println(data) // the contents are in order
 		rand.shuffle(data[:])
@@ -786,14 +599,14 @@ Possible Output:
 	[2, 4, 3, 1]
 
 */
-shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
+shuffle :: proc(array: $T/[]$E) {
 	n := i64(len(array))
 	if n < 2 {
 		return
 	}
 
 	for i := i64(n - 1); i > 0; i -= 1 {
-		j := int63_max(i + 1, r)
+		j := int63_max(i + 1)
 		array[i], array[j] = array[j], array[i]
 	}
 }
@@ -803,7 +616,6 @@ Returns a random element from the provided slice. If no generator is provided th
 
 Inputs:
 - array: The slice to choose an element from
-- r: The random number generator to use, or nil for the global generator
 
 Returns:
 - res: A random element from `array`
@@ -813,7 +625,6 @@ Example:
 	import "core:fmt"
 
 	choice_example :: proc() {
-		// Using the global random number generator
 		data: [4]int = { 1, 2, 3, 4 }
 		fmt.println(rand.choice(data[:]))
 		fmt.println(rand.choice(data[:]))
@@ -830,17 +641,17 @@ Possible Output:
 
 */
 @(require_results)
-choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
+choice :: proc(array: $T/[]$E) -> (res: E) {
 	n := i64(len(array))
 	if n < 1 {
 		return E{}
 	}
-	return array[int63_max(n, r)]
+	return array[int63_max(n)]
 }
 
 
 @(require_results)
-choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T
+choice_enum :: proc($T: typeid) -> T
 	where
 		intrinsics.type_is_enum(T),
 		size_of(T) <= 8,
@@ -848,11 +659,11 @@ choice_enum :: proc($T: typeid, r: ^Rand = nil) -> T
 {
 	when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
 	     u64(max(T)) > u64(max(i64)) {
-		i := uint64(r) % u64(len(T))
+		i := uint64() % u64(len(T))
 		i += u64(min(T))
 		return T(i)
 	} else {
-		i := int63_max(i64(len(T)), r)
+		i := int63_max(i64(len(T)))
 		i += i64(min(T))
 		return T(i)
 	}

+ 10 - 6
core/slice/slice.odin

@@ -180,7 +180,7 @@ binary_search_by :: proc(array: $A/[]$T, key: T, f: proc(T, T) -> Ordering) -> (
 }
 
 @(require_results)
-equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) {
+equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) #no_bounds_check {
 	if len(a) != len(b) {
 		return false
 	}
@@ -495,8 +495,10 @@ unique :: proc(s: $S/[]$T) -> S where intrinsics.type_is_comparable(T) #no_bound
 	}
 	i := 1
 	for j in 1..<len(s) {
-		if s[j] != s[j-1] && i != j {
-			s[i] = s[j]
+		if s[j] != s[j-1] {
+			if i != j {
+				s[i] = s[j]
+			}
 			i += 1
 		}
 	}
@@ -513,8 +515,10 @@ unique_proc :: proc(s: $S/[]$T, eq: proc(T, T) -> bool) -> S #no_bounds_check {
 	}
 	i := 1
 	for j in 1..<len(s) {
-		if !eq(s[j], s[j-1]) && i != j {
-			s[i] = s[j]
+		if !eq(s[j], s[j-1]) {
+			if i != j {
+				s[i] = s[j]
+			}
 			i += 1
 		}
 	}
@@ -736,4 +740,4 @@ bitset_to_enum_slice_with_make :: proc(bs: $T, $E: typeid, allocator := context.
 	return bitset_to_enum_slice(buf, bs)
 }
 
-bitset_to_enum_slice :: proc{bitset_to_enum_slice_with_make, bitset_to_enum_slice_with_buffer}
+bitset_to_enum_slice :: proc{bitset_to_enum_slice_with_make, bitset_to_enum_slice_with_buffer}

+ 1 - 4
core/sync/chan/chan.odin

@@ -476,10 +476,7 @@ select_raw :: proc "odin" (recvs: []^Raw_Chan, sends: []^Raw_Chan, send_msgs: []
 		return
 	}
 
-	r: ^rand.Rand = nil
-
-
-	select_idx = rand.int_max(count, r) if count > 0 else 0
+	select_idx = rand.int_max(count) if count > 0 else 0
 
 	sel := candidates[select_idx]
 	if sel.is_recv {

+ 59 - 109
core/sys/windows/kernel32.odin

@@ -78,6 +78,7 @@ foreign kernel32 {
 	RemoveVectoredContinueHandler  :: proc(Handle: LPVOID) -> DWORD ---
 	RaiseException :: proc(dwExceptionCode, dwExceptionFlags, nNumberOfArguments: DWORD, lpArguments: ^ULONG_PTR) -> ! ---
 
+	SetUnhandledExceptionFilter :: proc(lpTopLevelExceptionFilter: LPTOP_LEVEL_EXCEPTION_FILTER) -> LPTOP_LEVEL_EXCEPTION_FILTER ---
 
 	CreateHardLinkW :: proc(lpSymlinkFileName: LPCWSTR,
 	                        lpTargetFileName: LPCWSTR,
@@ -479,6 +480,8 @@ foreign kernel32 {
 	GetHandleInformation :: proc(hObject: HANDLE, lpdwFlags: ^DWORD) -> BOOL ---
 
 	RtlCaptureStackBackTrace :: proc(FramesToSkip: ULONG, FramesToCapture: ULONG, BackTrace: [^]PVOID, BackTraceHash: PULONG) -> USHORT ---
+
+	GetSystemPowerStatus :: proc(lpSystemPowerStatus: ^SYSTEM_POWER_STATUS) -> BOOL ---
 }
 
 DEBUG_PROCESS                    :: 0x00000001
@@ -1024,31 +1027,9 @@ foreign kernel32 {
 HandlerRoutine :: proc "system" (dwCtrlType: DWORD) -> BOOL
 PHANDLER_ROUTINE :: HandlerRoutine
 
+// NOTE(Jeroen, 2024-06-13): As Odin now supports bit_fields, we no longer need
+// a helper procedure. `init_dcb_with_config` and `get_dcb_config` have been removed.
 
-
-
-DCB_Config :: struct {
-	fParity: bool,
-	fOutxCtsFlow: bool,
-	fOutxDsrFlow: bool,
-	fDtrControl: DTR_Control,
-	fDsrSensitivity: bool,
-	fTXContinueOnXoff: bool,
-	fOutX: bool,
-	fInX: bool,
-	fErrorChar: bool,
-	fNull: bool,
-	fRtsControl: RTS_Control,
-	fAbortOnError: bool,
-	BaudRate: DWORD,
-	ByteSize: BYTE,
-	Parity: Parity,
-	StopBits: Stop_Bits,
-	XonChar: byte,
-	XoffChar: byte,
-	ErrorChar: byte,
-	EvtChar: byte,
-}
 DTR_Control :: enum byte {
 	Disable = 0,
 	Enable = 1,
@@ -1073,92 +1054,35 @@ Stop_Bits :: enum byte {
 	Two = 2,
 }
 
-// A helper procedure to set the values of a DCB structure.
-init_dcb_with_config :: proc "contextless" (dcb: ^DCB, config: DCB_Config) {
-	out: u32
-
-	// NOTE(tetra, 2022-09-21): On both Clang 14 on Windows, and MSVC, the bits in the bitfield
-	// appear to be defined from LSB to MSB order.
-	// i.e: `fBinary` (the first bitfield in the C source) is the LSB in the `settings` u32.
-
-	out |= u32(1) << 0 // fBinary must always be true on Windows.
-
-	out |= u32(config.fParity) << 1
-	out |= u32(config.fOutxCtsFlow) << 2
-	out |= u32(config.fOutxDsrFlow) << 3
-
-	out |= u32(config.fDtrControl) << 4
-
-	out |= u32(config.fDsrSensitivity) << 6
-	out |= u32(config.fTXContinueOnXoff) << 7
-	out |= u32(config.fOutX) << 8
-	out |= u32(config.fInX) << 9
-	out |= u32(config.fErrorChar) << 10
-	out |= u32(config.fNull) << 11
-
-	out |= u32(config.fRtsControl) << 12
-
-	out |= u32(config.fAbortOnError) << 14
-
-	dcb.settings = out
-
-	dcb.BaudRate = config.BaudRate
-	dcb.ByteSize = config.ByteSize
-	dcb.Parity = config.Parity
-	dcb.StopBits = config.StopBits
-	dcb.XonChar = config.XonChar
-	dcb.XoffChar = config.XoffChar
-	dcb.ErrorChar = config.ErrorChar
-	dcb.EvtChar = config.EvtChar
-
-	dcb.DCBlength = size_of(DCB)
-}
-get_dcb_config :: proc "contextless" (dcb: DCB) -> (config: DCB_Config) {
-	config.fParity = bool((dcb.settings >> 1) & 0x01)
-	config.fOutxCtsFlow = bool((dcb.settings >> 2) & 0x01)
-	config.fOutxDsrFlow = bool((dcb.settings >> 3) & 0x01)
-
-	config.fDtrControl = DTR_Control((dcb.settings >> 4) & 0x02)
-
-	config.fDsrSensitivity = bool((dcb.settings >> 6) & 0x01)
-	config.fTXContinueOnXoff = bool((dcb.settings >> 7) & 0x01)
-	config.fOutX = bool((dcb.settings >> 8) & 0x01)
-	config.fInX = bool((dcb.settings >> 9) & 0x01)
-	config.fErrorChar = bool((dcb.settings >> 10) & 0x01)
-	config.fNull = bool((dcb.settings >> 11) & 0x01)
-
-	config.fRtsControl = RTS_Control((dcb.settings >> 12) & 0x02)
-
-	config.fAbortOnError = bool((dcb.settings >> 14) & 0x01)
-
-	config.BaudRate = dcb.BaudRate
-	config.ByteSize = dcb.ByteSize
-	config.Parity = dcb.Parity
-	config.StopBits = dcb.StopBits
-	config.XonChar = dcb.XonChar
-	config.XoffChar = dcb.XoffChar
-	config.ErrorChar = dcb.ErrorChar
-	config.EvtChar = dcb.EvtChar
-
-	return
-}
-
-// NOTE(tetra): See get_dcb_config() and init_dcb_with_config() for help with initializing this.
 DCB :: struct {
-	DCBlength: DWORD, // NOTE(tetra): Must be set to size_of(DCB).
-	BaudRate: DWORD,
-	settings: u32, // NOTE(tetra): These are bitfields in the C struct.
-	wReserved: WORD,
-	XOnLim: WORD,
-	XOffLim: WORD,
-	ByteSize: BYTE,
-	Parity: Parity,
-	StopBits: Stop_Bits,
-	XonChar: byte,
-	XoffChar: byte,
-	ErrorChar: byte,
-	EofChar: byte,
-	EvtChar: byte,
+	DCBlength:  DWORD,
+	BaudRate:   DWORD,
+	using _: bit_field DWORD {
+		fBinary:           bool        | 1,
+		fParity:           bool        | 1,
+		fOutxCtsFlow:      bool        | 1,
+		fOutxDsrFlow:      bool        | 1,
+		fDtrControl:       DTR_Control | 2,
+		fDsrSensitivity:   bool        | 1,
+		fTXContinueOnXoff: bool        | 1,
+		fOutX:             bool        | 1,
+		fInX:              bool        | 1,
+		fErrorChar:        bool        | 1,
+		fNull:             bool        | 1,
+		fRtsControl:       RTS_Control | 2,
+		fAbortOnError:     bool        | 1,
+	},
+	wReserved:  WORD,
+	XOnLim:     WORD,
+	XOffLim:    WORD,
+	ByteSize:   BYTE,
+	Parity:     Parity,
+	StopBits:   Stop_Bits,
+	XonChar:    byte,
+	XoffChar:   byte,
+	ErrorChar:  byte,
+	EofChar:    byte,
+	EvtChar:    byte,
 	wReserved1: WORD,
 }
 
@@ -1238,6 +1162,30 @@ SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct {
 	DummyUnion: DUMMYUNIONNAME_u,
 }
 
+SYSTEM_POWER_STATUS :: struct {
+	ACLineStatus:        AC_Line_Status,
+	BatteryFlag:         Battery_Flags,
+	BatteryLifePercent:  BYTE,
+	SystemStatusFlag:    BYTE,
+	BatteryLifeTime:     DWORD,
+	BatteryFullLifeTime: DWORD,
+}
+
+AC_Line_Status :: enum BYTE {
+   Offline = 0,
+   Online  = 1,
+   Unknown = 255,
+}
+
+Battery_Flag :: enum BYTE {
+    High     = 0,
+    Low      = 1,
+    Critical = 2,
+    Charging = 3,
+    No_Battery = 7, 
+}
+Battery_Flags :: bit_set[Battery_Flag; BYTE] 
+
 /* Global Memory Flags */
 GMEM_FIXED          :: 0x0000
 GMEM_MOVEABLE       :: 0x0002
@@ -1256,3 +1204,5 @@ GMEM_INVALID_HANDLE :: 0x8000
 
 GHND                :: (GMEM_MOVEABLE | GMEM_ZEROINIT)
 GPTR                :: (GMEM_FIXED | GMEM_ZEROINIT)
+
+LPTOP_LEVEL_EXCEPTION_FILTER :: PVECTORED_EXCEPTION_HANDLER

+ 9 - 0
core/sys/windows/types.odin

@@ -34,6 +34,7 @@ HGDIOBJ :: distinct HANDLE
 HBITMAP :: distinct HANDLE
 HGLOBAL :: distinct HANDLE
 HHOOK :: distinct HANDLE
+HWINEVENTHOOK :: distinct HANDLE
 HKEY :: distinct HANDLE
 HDESK :: distinct HANDLE
 HFONT :: distinct HANDLE
@@ -703,6 +704,14 @@ WNDPROC :: #type proc "system" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
 
 HOOKPROC :: #type proc "system" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
 
+WINEVENTPROC :: #type proc "system" (
+	hWinEventHook: HWINEVENTHOOK,
+	event: DWORD,
+	hwnd: HWND,
+	idObject, idChild: LONG,
+	idEventThread, dwmsEventTime: DWORD,
+)
+
 CWPRETSTRUCT :: struct {
 	lResult: LRESULT,
 	lParam: LPARAM,

+ 21 - 0
core/sys/windows/user32.odin

@@ -17,6 +17,18 @@ foreign user32 {
 
 	GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int ---
 
+	GetParent :: proc(hWnd: HWND) -> HWND ---
+	IsWindowVisible :: proc(hWnd: HWND) -> BOOL ---
+	SetWinEventHook :: proc(
+		eventMin, eventMax: DWORD,
+		hmodWinEventProc: HMODULE,
+		pfnWinEvenProc: WINEVENTPROC,
+		idProcess, idThread: DWORD,
+		dwFlags: WinEventFlags,
+	) -> HWINEVENTHOOK ---
+
+	IsChild :: proc(hWndParent, hWnd: HWND) -> BOOL ---
+
 	RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM ---
 	RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
 	UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL ---
@@ -568,3 +580,12 @@ RedrawWindowFlags :: enum UINT {
 	RDW_FRAME           = 0x0400,
 	RDW_NOFRAME         = 0x0800,
 }
+
+// OUTOFCONTEXT is the zero value, use {}
+WinEventFlags :: bit_set[WinEventFlag; DWORD]
+
+WinEventFlag :: enum DWORD {
+    SKIPOWNTHREAD  = 0,
+    SKIPOWNPROCESS = 1,
+    INCONTEXT      = 2,
+}

+ 1 - 1
core/sys/windows/winerror.odin

@@ -47,7 +47,7 @@ ERROR_PIPE_BUSY              : DWORD : 231
 
 E_NOTIMPL :: HRESULT(-0x7fff_bfff) // 0x8000_4001
 
-SUCCEEDED :: #force_inline proc(#any_int result: int) -> bool { return result >= 0 }
+SUCCEEDED :: #force_inline proc "contextless" (#any_int result: int) -> bool { return result >= 0 }
 
 
 System_Error :: enum DWORD {

+ 16 - 7
core/testing/logging.odin

@@ -8,13 +8,22 @@ import "core:strings"
 import "core:sync/chan"
 import "core:time"
 
-Default_Test_Logger_Opts :: runtime.Logger_Options {
-	.Level,
-	.Terminal_Color,
-	.Short_File_Path,
-	.Line,
-	.Procedure,
-	.Date, .Time,
+when USING_SHORT_LOGS {
+	Default_Test_Logger_Opts :: runtime.Logger_Options {
+		.Level,
+		.Terminal_Color,
+		.Short_File_Path,
+		.Line,
+	}
+} else {
+	Default_Test_Logger_Opts :: runtime.Logger_Options {
+		.Level,
+		.Terminal_Color,
+		.Short_File_Path,
+		.Line,
+		.Procedure,
+		.Date, .Time,
+	}
 }
 
 Log_Message :: struct {

+ 39 - 13
core/testing/runner.odin

@@ -9,6 +9,7 @@ import "core:encoding/ansi"
 import "core:fmt"
 import "core:io"
 @require import pkg_log "core:log"
+import "core:math/rand"
 import "core:mem"
 import "core:os"
 import "core:slice"
@@ -41,6 +42,8 @@ PROGRESS_WIDTH        : int    : #config(ODIN_TEST_PROGRESS_WIDTH, 24)
 SHARED_RANDOM_SEED    : u64    : #config(ODIN_TEST_RANDOM_SEED, 0)
 // Set the lowest log level for this test run.
 LOG_LEVEL             : string : #config(ODIN_TEST_LOG_LEVEL, "info")
+// Show only the most necessary logging information.
+USING_SHORT_LOGS      : bool   : #config(ODIN_TEST_SHORT_LOGS, false)
 
 
 get_log_level :: #force_inline proc() -> runtime.Logger_Level {
@@ -104,6 +107,13 @@ run_test_task :: proc(task: thread.Task) {
 		options = Default_Test_Logger_Opts,
 	}
 
+	random_generator_state: runtime.Default_Random_State
+	context.random_generator = {
+		procedure = runtime.default_random_generator_proc,
+		data = &random_generator_state,
+	}
+	rand.reset(data.t.seed)
+
 	free_all(context.temp_allocator)
 
 	data.it.p(&data.t)
@@ -497,6 +507,7 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
 				data.it = it
 				data.t.seed = shared_random_seed
 				data.t.error_count = 0
+				data.t._fail_now_called = false
 
 				thread.pool_add_task(&pool, task.allocator, run_test_task, data, run_index)
 			}
@@ -604,10 +615,10 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
 			})
 			fmt.assertf(alloc_error == nil, "Error appending to log messages: %v", alloc_error)
 
-			find_task_data: for &data in task_data_slots {
+			find_task_data_for_timeout: for &data in task_data_slots {
 				if data.it.pkg == it.pkg && data.it.name == it.name {
 					end_t(&data.t)
-					break find_task_data
+					break find_task_data_for_timeout
 				}
 			}
 		}
@@ -645,21 +656,36 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
 				"A signal (%v) was raised to stop test #%i %s.%s, but it was unable to be found.",
 				reason, test_index, it.pkg, it.name)
 
-			if test_index not_in failed_test_reason_map {
-				// We only write a new error message here if there wasn't one
-				// already, because the message we can provide based only on
-				// the signal won't be very useful, whereas asserts and panics
-				// will provide a user-written error message.
-				failed_test_reason_map[test_index] = fmt.aprintf("Signal caught: %v", reason, allocator = shared_log_allocator)
-				pkg_log.fatalf("Caught signal to stop test #%i %s.%s for: %v.", test_index, it.pkg, it.name, reason)
-
+			// The order this is handled in is a little particular.
+			task_data: ^Task_Data
+			find_task_data_for_stop_signal: for &data in task_data_slots {
+				if data.it.pkg == it.pkg && data.it.name == it.name {
+					task_data = &data
+					break find_task_data_for_stop_signal
+				}
 			}
 
-			when FANCY_OUTPUT {
-				bypass_progress_overwrite = true
-				signals_were_raised = true
+			fmt.assertf(task_data != nil, "A signal (%v) was raised to stop test #%i %s.%s, but its task data is missing.",
+				reason, test_index, it.pkg, it.name)
+
+			if !task_data.t._fail_now_called {
+				if test_index not_in failed_test_reason_map {
+					// We only write a new error message here if there wasn't one
+					// already, because the message we can provide based only on
+					// the signal won't be very useful, whereas asserts and panics
+					// will provide a user-written error message.
+					failed_test_reason_map[test_index] = fmt.aprintf("Signal caught: %v", reason, allocator = shared_log_allocator)
+					pkg_log.fatalf("Caught signal to stop test #%i %s.%s for: %v.", test_index, it.pkg, it.name, reason)
+				}
+
+				when FANCY_OUTPUT {
+					bypass_progress_overwrite = true
+					signals_were_raised = true
+				}
 			}
 
+			end_t(&task_data.t)
+
 			total_failure_count += 1
 			total_done_count += 1
 		}

+ 21 - 6
core/testing/testing.odin

@@ -48,7 +48,7 @@ T :: struct {
 	// tests during channel transmission.
 	_log_allocator: runtime.Allocator,
 
-	_fail_now: proc() -> !,
+	_fail_now_called: bool,
 }
 
 
@@ -66,15 +66,20 @@ fail :: proc(t: ^T, loc := #caller_location) {
 	pkg_log.error("FAIL", location=loc)
 }
 
-fail_now :: proc(t: ^T, msg := "", loc := #caller_location) {
+// fail_now will cause a test to immediately fail and abort, much in the same
+// way a failed assertion or panic call will stop a thread.
+//
+// It is for when you absolutely need a test to fail without calling any of its
+// deferred statements. It will be cleaner than a regular assert or panic,
+// as the test runner will know to expect the signal this procedure will raise.
+fail_now :: proc(t: ^T, msg := "", loc := #caller_location) -> ! {
+	t._fail_now_called = true
 	if msg != "" {
 		pkg_log.error("FAIL:", msg, location=loc)
 	} else {
 		pkg_log.error("FAIL", location=loc)
 	}
-	if t._fail_now != nil {
-		t._fail_now()
-	}
+	runtime.trap()
 }
 
 failed :: proc(t: ^T) -> bool {
@@ -94,7 +99,17 @@ logf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
 
 // cleanup registers a procedure and user_data, which will be called when the test, and all its subtests, complete.
 // Cleanup procedures will be called in LIFO (last added, first called) order.
-// Each procedure will use a copy of the context at the time of registering.
+//
+// Each procedure will use a copy of the context at the time of registering,
+// and if the test failed due to a timeout, failed assertion, panic, bounds-checking error,
+// memory access violation, or any other signal-based fault, this procedure will
+// run with greater privilege in the test runner's main thread.
+//
+// That means that any cleanup procedure absolutely must not fail in the same way,
+// or it will take down the entire test runner with it. This is for when you
+// need something to run no matter what, if a test failed.
+//
+// For almost every usual case, `defer` should be preferable and sufficient.
 cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) {
 	append(&t.cleanups, Internal_Cleanup{procedure, user_data, context})
 }

+ 46 - 44
core/text/i18n/doc.odin

@@ -1,31 +1,44 @@
 
 /*
-The `i18n` package is flexible and easy to use.
-
-It has one call to get a translation: `get`, which the user can alias into something like `T`.
-
-`get`, referred to as `T` here, has a few different signatures.
-All of them will return the key if the entry can't be found in the active translation catalog.
-
-- `T(key)`              returns the translation of `key`.
-- `T(key, n)`           returns a pluralized translation of `key` according to value `n`.
-
-- `T(section, key)`     returns the translation of `key` in `section`.
-- `T(section, key, n)`  returns a pluralized translation of `key` in `section` according to value `n`.
-
-By default lookup take place in the global `i18n.ACTIVE` catalog for ease of use.
-If you want to override which translation to use, for example in a language preview dialog, you can use the following:
-
-- `T(key, n, catalog)`           returns the pluralized version of `key` from explictly supplied catalog.
-- `T(section, key, n, catalog)`  returns the pluralized version of `key` in `section` from explictly supplied catalog.
+The `i18n` package is a flexible and easy to use way to localise applications.
+
+It has two calls to get a translation: `get()` and `get_n()`, which the user can alias into something like `T` and `Tn`
+with statements like:
+	T  :: i18n.get
+	Tn :: i18n.get_n.
+
+`get()` is used for retrieving the translation of sentences which **never** change in form,
+like for instance "Connection established" or "All temporary files have been deleted".
+Note that the number (singular, dual, plural, whatever else) is not relevant: the sentence is fixed and it will have only one possible translation in any other language.
+
+`get_n()` is used for retrieving the translations of sentences which change according to the number of items referenced.
+The various signatures of `get_n()` have one more parameter, `n`, which will receive that number and be used
+to select the correct form according to the pluralizer attached to the message catalogue when initially loaded;
+for instance, to summarise a rather complex matter, some languages use the singular form when referring to 0 items and some use the (only in their case) plural forms;
+also, languages may have more or less quantifier forms than a single singular form and a universal plural form:
+for instance, Chinese has just one form for any quantity, while Welsh may have up to 6 different forms for specific different quantities.
+
+Both `get()` and `get_n()`, referred to as `T` and `Tn` here, have several different signatures.
+All of them will return the key if the entry can't be found in the active translation catalogue.
+By default lookup take place in the global `i18n.ACTIVE` catalogue for ease of use, unless a specific catalogue is supplied.
+
+- `T(key)`                   returns the translation of `key`.
+- `T(key, catalog)`          returns the translation of `key` from explictly supplied catalogue.
+- `T(section, key)`          returns the translation of `key` in `section`.
+- `T(section, key, catalog)` returns the translation of `key` in `section` from explictly supplied catalogue.
+
+- `Tn(key, n)`                   returns the translation of `key` according to number of items `n`.
+- `Tn(key, n, catalog)`          returns the translation of `key` from explictly supplied catalogue.
+- `Tn(section, key, n)`          returns the translation of `key` in `section` according to number of items `n`.
+- `Tn(section, key, n, catalog)` returns the translation of `key` in `section` according to number of items `n` from explictly supplied catalogue.
 
 If a catalog has translation contexts or sections, then omitting it in the above calls looks up in section "".
 
-The default pluralization rule is n != 1, which is to say that passing n == 1 (or not passing n) returns the singular form.
-Passing n != 1 returns plural form 1.
+The default pluralization rule is `n != 1`, which is to say that passing `n == 1` returns the singular form (in slot 0).
+Passing `n != 1` returns the plural form in slot 1 (if any).
 
 Should a language not conform to this rule, you can pass a pluralizer procedure to the catalog parser.
-This is a procedure that maps an integer to an integer, taking a value and returning which plural slot should be used.
+This is a procedure that maps an integer to an integer, taking a quantity and returning which plural slot should be used.
 
 You can also assign it to a loaded catalog after parsing, of course.
 
@@ -34,24 +47,21 @@ Example:
 	import "core:fmt"
 	import "core:text/i18n"
 
-	T :: i18n.get
+	T  :: i18n.get
+	Tn :: i18n.get_n
 
 	mo :: proc() {
 		using fmt
 
 		err: i18n.Error
 
-		/*
-			Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter.
-		*/
+		// Parse MO file and set it as the active translation so we can omit `get`'s "catalog" parameter.
 		i18n.ACTIVE, err = i18n.parse_mo(#load("translations/nl_NL.mo"))
 		defer i18n.destroy()
 
 		if err != .None { return }
 
-		/*
-			These are in the .MO catalog.
-		*/
+		// These are in the .MO catalog.
 		println("-----")
 		println(T(""))
 		println("-----")
@@ -60,13 +70,11 @@ Example:
 		println(T("Hellope, World!"))
 		println("-----")
 		// We pass 1 into `T` to get the singular format string, then 1 again into printf.
-		printf(T("There is %d leaf.\n", 1), 1)
+		printf(Tn("There is %d leaf.\n", 1), 1)
 		// We pass 42 into `T` to get the plural format string, then 42 again into printf.
-		printf(T("There is %d leaf.\n", 42), 42)
+		printf(Tn("There is %d leaf.\n", 42), 42)
 
-		/*
-			This isn't in the translation catalog, so the key is passed back untranslated.
-		*/
+		// This isn't in the translation catalog, so the key is passed back untranslated.
 		println("-----")
 		println(T("Come visit us on Discord!"))
 	}
@@ -76,19 +84,13 @@ Example:
 
 		err: i18n.Error
 
-		/*
-			Parse QT file and set it as the active translation so we can omit `get`'s "catalog" parameter.
-		*/
+		// Parse QT file and set it as the active translation so we can omit `get`'s "catalog" parameter.
 		i18n.ACTIVE, err = i18n.parse_qt(#load("translations/nl_NL-qt-ts.ts"))
 		defer i18n.destroy()
 
-		if err != .None {
-			return
-		}
+		if err != .None { return }
 
-		/*
-			These are in the .TS catalog. As you can see they have sections.
-		*/
+		// These are in the .TS catalog. As you can see they have sections.
 		println("--- Page section ---")
 		println("Page:Text for translation =", T("Page", "Text for translation"))
 		println("-----")
@@ -99,8 +101,8 @@ Example:
 		println("-----")
 		println("--- apple_count section ---")
 		println("apple_count:%d apple(s) =")
-		println("\t 1  =", T("apple_count", "%d apple(s)", 1))
-		println("\t 42 =", T("apple_count", "%d apple(s)", 42))
+		println("\t 1  =", Tn("apple_count", "%d apple(s)", 1))
+		println("\t 42 =", Tn("apple_count", "%d apple(s)", 42))
 	}
 */
 package i18n

+ 107 - 59
core/text/i18n/i18n.odin

@@ -10,23 +10,13 @@ package i18n
 */
 import "core:strings"
 
-/*
-	TODO:
-	- Support for more translation catalog file formats.
-*/
-
-/*
-	Currently active catalog.
-*/
+// Currently active catalog.
 ACTIVE: ^Translation
 
 // Allow between 1 and 255 plural forms. Default: 10.
 MAX_PLURALS :: min(max(#config(ODIN_i18N_MAX_PLURAL_FORMS, 10), 1), 255)
 
-/*
-	The main data structure. This can be generated from various different file formats, as long as we have a parser for them.
-*/
-
+// The main data structure. This can be generated from various different file formats, as long as we have a parser for them.
 Section :: map[string][]string
 
 Translation :: struct {
@@ -37,34 +27,24 @@ Translation :: struct {
 }
 
 Error :: enum {
-	/*
-		General return values.
-	*/
+	// General return values.
 	None = 0,
 	Empty_Translation_Catalog,
 	Duplicate_Key,
 
-	/*
-		Couldn't find, open or read file.
-	*/
+	// Couldn't find, open or read file.
 	File_Error,
 
-	/*
-		File too short.
-	*/
+	// File too short.
 	Premature_EOF,
 
-	/*
-		GNU Gettext *.MO file errors.
-	*/
+	// GNU Gettext *.MO file errors.
 	MO_File_Invalid_Signature,
 	MO_File_Unsupported_Version,
 	MO_File_Invalid,
 	MO_File_Incorrect_Plural_Count,
 
-	/*
-		Qt Linguist *.TS file errors.
-	*/
+	// Qt Linguist *.TS file errors.
 	TS_File_Parse_Error,
 	TS_File_Expected_Context,
 	TS_File_Expected_Context_Name,
@@ -85,73 +65,142 @@ DEFAULT_PARSE_OPTIONS :: Parse_Options{
 }
 
 /*
-	Several ways to use:
-	- get(key), which defaults to the singular form and i18n.ACTIVE catalog, or
-	- get(key, number), which returns the appropriate plural from the active catalog, or
-	- get(key, number, catalog) to grab text from a specific one.
+	Returns the first translation string for the passed `key`.
+	It is also aliased with `get()`.
+
+	Two ways to use it:
+	- get(key), which defaults to the `i18n.ACTIVE` catalogue, or
+	- get(key, catalog) to grab text from a specific loaded catalogue
+
+	Inputs:
+	- key:     the string to translate
+	- catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE)
+
+	Returns:   the translated string, or the original `key` if no translation was found.
 */
-get_single_section :: proc(key: string, number := 1, catalog: ^Translation = ACTIVE) -> (value: string) {
+get_single_section :: proc(key: string, catalog: ^Translation = ACTIVE) -> (value: string) {
+	return get_by_slot(key, 0, catalog)
+}
+
+/*
+	Returns the first translation string for the passed `key` in a specific section or context.
+	It is also aliases with `get()`.
+
+	Two ways to use it:
+	- get(section, key), which defaults to the `i18n.ACTIVE` catalogue, or
+	- get(section, key, catalog) to grab text from a specific loaded catalogue
+
+	Inputs:
+	- section: the catalogue section (sometimes also called 'context') in which to look up the translation
+	- key:     the string to translate
+	- catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE)
+
+	Returns:   the translated string, or the original `key` if no translation was found.
+*/
+get_by_section :: proc(section, key: string, catalog: ^Translation = ACTIVE) -> (value: string) {
+	return get_by_slot(section, key, 0, catalog)
+}
+
+get :: proc{get_single_section, get_by_section}
+
+/*
+	Returns the translation string for the passed `key` in a specific plural form (if present in the catalogue).
+	It is also aliased with `get_n()`.
+
+	Two ways to use it:
+	- get_n(key, quantity), which returns the appropriate plural from the active catalogue, or
+	- get_n(key, quantity, catalog) to grab text from a specific loaded catalogue
+
+	Inputs:
+	- key:      the string to translate
+	- quantity: the quantity of item to be used to select the correct plural form
+	- catalog:  the catalogue to use for the translation (defaults to i18n.ACTIVE)
+
+	Returns:    the translated string, or the original `key` if no translation was found.
+*/
+get_single_section_with_quantity :: proc(key: string, quantity: int, catalog: ^Translation = ACTIVE) -> (value: string) {
 	/*
 		A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule.
 	*/
-	plural := 1 if number != 1 else 0
+	slot := 1 if quantity != 1 else 0
 
 	if catalog.pluralize != nil {
-		plural = catalog.pluralize(number)
+		slot = catalog.pluralize(quantity)
 	}
-	return get_by_slot(key, plural, catalog)
+	return get_by_slot(key, slot, catalog)
 }
 
 /*
-	Several ways to use:
-	- get(section, key), which defaults to the singular form and i18n.ACTIVE catalog, or
-	- get(section, key, number), which returns the appropriate plural from the active catalog, or
-	- get(section, key, number, catalog) to grab text from a specific one.
+	Returns the translation string for the passed `key` in a specific plural form (if present in the catalogue)
+	in a specific section or context.
+	It is also aliases with `get_n()`.
+
+	Two ways to use it:
+	- get(section, key, quantity), which returns the appropriate plural from the active catalogue, or
+	- get(section, key, quantity, catalog) to grab text from a specific loaded catalogue
+
+	Inputs:
+	- section: the catalogue section (sometime also called 'context') from which to lookup the translation
+	- key:     the string to translate
+	- qantity: the quantity of item to be used to select the correct plural form
+	- catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE)
+
+	Returns:   the translated string, or the original `key` if no translation was found
 */
-get_by_section :: proc(section, key: string, number := 1, catalog: ^Translation = ACTIVE) -> (value: string) {
+get_by_section_with_quantity :: proc(section, key: string, quantity: int, catalog: ^Translation = ACTIVE) -> (value: string) {
 	/*
 		A lot of languages use singular for 1 item and plural for 0 or more than 1 items. This is our default pluralize rule.
 	*/
-	plural := 1 if number != 1 else 0
+	slot := 1 if quantity != 1 else 0
 
 	if catalog.pluralize != nil {
-		plural = catalog.pluralize(number)
+		slot = catalog.pluralize(quantity)
 	}
-	return get_by_slot(section, key, plural, catalog)
+	return get_by_slot(section, key, slot, catalog)
 }
-get :: proc{get_single_section, get_by_section}
+get_n :: proc{get_single_section_with_quantity, get_by_section_with_quantity}
 
 /*
-	Several ways to use:
-	- get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or
-	- get_by_slot(key, slot), which returns the requested plural from the active catalog, or
-	- get_by_slot(key, slot, catalog) to grab text from a specific one.
+	Two ways to use:
+	- get_by_slot(key, slot), which returns the requested plural from the active catalogue, or
+	- get_by_slot(key, slot, catalog) to grab text from a specific loaded catalogue.
 
 	If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string.
+	- section: the catalogue section (sometime also called 'context') from which to lookup the translation
+
+	Inputs:
+	- key:     the string to translate.
+	- slot:    the translation slot to choose (slots refer to plural forms specific for each language and their meaning changes from catalogue to catalogue).
+	- catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE)
+
+	Returns:   the translated string, or the original `key` if no translation was found.
 */
-get_by_slot_single_section :: proc(key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+get_by_slot_single_section :: proc(key: string, slot: int, catalog: ^Translation = ACTIVE) -> (value: string) {
 	return get_by_slot_by_section("", key, slot, catalog)
 }
 
 /*
-	Several ways to use:
-	- get_by_slot(key), which defaults to the singular form and i18n.ACTIVE catalog, or
+	Two ways to use:
 	- get_by_slot(key, slot), which returns the requested plural from the active catalog, or
 	- get_by_slot(key, slot, catalog) to grab text from a specific one.
 
 	If a file format parser doesn't (yet) support plural slots, each of the slots will point at the same string.
+
+	Inputs:
+	- section: the catalogue section (sometime also called 'context') from which to lookup the translation
+	- key:     the string to translate.
+	- slot:    the translation slot to choose (slots refer to plural forms specific for each language and their meaning changes from catalogue to catalogue).
+	- catalog: the catalogue to use for the translation (defaults to i18n.ACTIVE)
+
+	Returns:   the translated string or the original `key` if no translation was found.
 */
-get_by_slot_by_section :: proc(section, key: string, slot := 0, catalog: ^Translation = ACTIVE) -> (value: string) {
+get_by_slot_by_section :: proc(section, key: string, slot: int, catalog: ^Translation = ACTIVE) -> (value: string) {
 	if catalog == nil || section not_in catalog.k_v {
-		/*
-			Return the key if the catalog catalog hasn't been initialized yet, or the section is not present.
-		*/
+		// Return the key if the catalog catalog hasn't been initialized yet, or the section is not present.
 		return key
 	}
 
-	/*
-		Return the translation from the requested slot if this key is known, else return the key.
-	*/
+	// Return the translation from the requested slot if this key is known, else return the key.
 	if translations, ok := catalog.k_v[section][key]; ok {
 		plural := min(max(0, slot), len(catalog.k_v[section][key]) - 1)
 		return translations[plural]
@@ -161,7 +210,6 @@ get_by_slot_by_section :: proc(section, key: string, slot := 0, catalog: ^Transl
 get_by_slot :: proc{get_by_slot_single_section, get_by_slot_by_section}
 
 /*
-	Same for destroy:
 	- destroy(), to clean up the currently active catalog catalog i18n.ACTIVE
 	- destroy(catalog), to clean up a specific catalog.
 */

+ 264 - 4
core/unicode/letter.odin

@@ -5,6 +5,10 @@ REPLACEMENT_CHAR :: '\ufffd'     // Represented an invalid code point
 MAX_ASCII        :: '\u007f'     // Maximum ASCII value
 MAX_LATIN1       :: '\u00ff'     // Maximum Latin-1 value
 
+ZERO_WIDTH_NON_JOINER :: '\u200C'
+ZERO_WIDTH_JOINER     :: '\u200D'
+
+@(require_results)
 binary_search :: proc(c: i32, table: []i32, length, stride: int) -> int {
 	n := length
 	t := 0
@@ -24,6 +28,7 @@ binary_search :: proc(c: i32, table: []i32, length, stride: int) -> int {
 	return -1
 }
 
+@(require_results)
 to_lower :: proc(r: rune) -> rune {
 	c := i32(r)
 	p := binary_search(c, to_lower_ranges[:], len(to_lower_ranges)/3, 3)
@@ -36,6 +41,7 @@ to_lower :: proc(r: rune) -> rune {
 	}
 	return rune(c)
 }
+@(require_results)
 to_upper :: proc(r: rune) -> rune {
 	c := i32(r)
 	p := binary_search(c, to_upper_ranges[:], len(to_upper_ranges)/3, 3)
@@ -48,6 +54,7 @@ to_upper :: proc(r: rune) -> rune {
 	}
 	return rune(c)
 }
+@(require_results)
 to_title :: proc(r: rune) -> rune {
 	c := i32(r)
 	p := binary_search(c, to_upper_singlets[:], len(to_title_singlets)/2, 2)
@@ -58,6 +65,7 @@ to_title :: proc(r: rune) -> rune {
 }
 
 
+@(require_results)
 is_lower :: proc(r: rune) -> bool {
 	if r <= MAX_ASCII {
 		return u32(r)-'a' < 26
@@ -74,6 +82,7 @@ is_lower :: proc(r: rune) -> bool {
 	return false
 }
 
+@(require_results)
 is_upper :: proc(r: rune) -> bool {
 	if r <= MAX_ASCII {
 		return u32(r)-'A' < 26
@@ -91,6 +100,7 @@ is_upper :: proc(r: rune) -> bool {
 }
 
 is_alpha :: is_letter
+@(require_results)
 is_letter :: proc(r: rune) -> bool {
 	if u32(r) <= MAX_LATIN1 {
 		return char_properties[u8(r)]&pLmask != 0
@@ -111,10 +121,12 @@ is_letter :: proc(r: rune) -> bool {
 	return false
 }
 
+@(require_results)
 is_title :: proc(r: rune) -> bool {
 	return is_upper(r) && is_lower(r)
 }
 
+@(require_results)
 is_digit :: proc(r: rune) -> bool {
 	if r <= MAX_LATIN1 {
 		return '0' <= r && r <= '9'
@@ -124,6 +136,7 @@ is_digit :: proc(r: rune) -> bool {
 
 
 is_white_space :: is_space
+@(require_results)
 is_space :: proc(r: rune) -> bool {
 	if u32(r) <= MAX_LATIN1 {
 		switch r {
@@ -140,18 +153,20 @@ is_space :: proc(r: rune) -> bool {
 	return false
 }
 
+@(require_results)
 is_combining :: proc(r: rune) -> bool {
 	c := i32(r)
 
 	return c >= 0x0300 && (c <= 0x036f ||
-          (c >= 0x1ab0 && c <= 0x1aff) ||
-          (c >= 0x1dc0 && c <= 0x1dff) ||
-          (c >= 0x20d0 && c <= 0x20ff) ||
-          (c >= 0xfe20 && c <= 0xfe2f))
+	      (c >= 0x1ab0 && c <= 0x1aff) ||
+	      (c >= 0x1dc0 && c <= 0x1dff) ||
+	      (c >= 0x20d0 && c <= 0x20ff) ||
+	      (c >= 0xfe20 && c <= 0xfe2f))
 }
 
 
 
+@(require_results)
 is_graphic :: proc(r: rune) -> bool {
 	if u32(r) <= MAX_LATIN1 {
 		return char_properties[u8(r)]&pg != 0
@@ -159,6 +174,7 @@ is_graphic :: proc(r: rune) -> bool {
 	return false
 }
 
+@(require_results)
 is_print :: proc(r: rune) -> bool {
 	if u32(r) <= MAX_LATIN1 {
 		return char_properties[u8(r)]&pp != 0
@@ -166,6 +182,7 @@ is_print :: proc(r: rune) -> bool {
 	return false
 }
 
+@(require_results)
 is_control :: proc(r: rune) -> bool {
 	if u32(r) <= MAX_LATIN1 {
 		return char_properties[u8(r)]&pC != 0
@@ -173,6 +190,7 @@ is_control :: proc(r: rune) -> bool {
 	return false
 }
 
+@(require_results)
 is_number :: proc(r: rune) -> bool {
 	if u32(r) <= MAX_LATIN1 {
 		return char_properties[u8(r)]&pN != 0
@@ -180,6 +198,7 @@ is_number :: proc(r: rune) -> bool {
 	return false
 }
 
+@(require_results)
 is_punct :: proc(r: rune) -> bool {
 	if u32(r) <= MAX_LATIN1 {
 		return char_properties[u8(r)]&pP != 0
@@ -187,9 +206,250 @@ is_punct :: proc(r: rune) -> bool {
 	return false
 }
 
+@(require_results)
 is_symbol :: proc(r: rune) -> bool {
 	if u32(r) <= MAX_LATIN1 {
 		return char_properties[u8(r)]&pS != 0
 	}
 	return false
 }
+
+//
+// The procedures below are accurate as of Unicode 15.1.0.
+//
+
+// Emoji_Modifier
+@(require_results)
+is_emoji_modifier :: proc(r: rune) -> bool {
+	return 0x1F3FB <= r && r <= 0x1F3FF
+}
+
+// Regional_Indicator
+@(require_results)
+is_regional_indicator :: proc(r: rune) -> bool {
+	return 0x1F1E6 <= r && r <= 0x1F1FF
+}
+
+// General_Category=Enclosing_Mark
+@(require_results)
+is_enclosing_mark :: proc(r: rune) -> bool {
+	switch r {
+	case 0x0488,
+	     0x0489,
+	     0x1ABE,
+	     0x20DD ..= 0x20E0,
+	     0x20E2 ..= 0x20E4,
+	     0xA670 ..= 0xA672:
+	     return true
+	}
+
+	return false
+}
+
+// Prepended_Concatenation_Mark
+@(require_results)
+is_prepended_concatenation_mark :: proc(r: rune) -> bool {
+	switch r {
+	case 0x00600 ..= 0x00605,
+	     0x006DD,
+	     0x0070F,
+	     0x00890 ..= 0x00891,
+	     0x008E2,
+	     0x110BD,
+	     0x110CD:
+		return true
+	case:
+		return false
+	}
+}
+
+// General_Category=Spacing_Mark
+@(require_results)
+is_spacing_mark :: proc(r: rune) -> bool {
+	c := i32(r)
+	p := binary_search(c, spacing_mark_ranges[:], len(spacing_mark_ranges)/2, 2)
+	if p >= 0 && spacing_mark_ranges[p] <= c && c <= spacing_mark_ranges[p+1] {
+		return true
+	}
+	return false
+}
+
+// General_Category=Nonspacing_Mark
+@(require_results)
+is_nonspacing_mark :: proc(r: rune) -> bool {
+	c := i32(r)
+	p := binary_search(c, nonspacing_mark_ranges[:], len(nonspacing_mark_ranges)/2, 2)
+	if p >= 0 && nonspacing_mark_ranges[p] <= c && c <= nonspacing_mark_ranges[p+1] {
+		return true
+	}
+	return false
+}
+
+// Extended_Pictographic
+@(require_results)
+is_emoji_extended_pictographic :: proc(r: rune) -> bool {
+	c := i32(r)
+	p := binary_search(c, emoji_extended_pictographic_ranges[:], len(emoji_extended_pictographic_ranges)/2, 2)
+	if p >= 0 && emoji_extended_pictographic_ranges[p] <= c && c <= emoji_extended_pictographic_ranges[p+1] {
+		return true
+	}
+	return false
+}
+
+// Grapheme_Extend
+@(require_results)
+is_grapheme_extend :: proc(r: rune) -> bool {
+	c := i32(r)
+	p := binary_search(c, grapheme_extend_ranges[:], len(grapheme_extend_ranges)/2, 2)
+	if p >= 0 && grapheme_extend_ranges[p] <= c && c <= grapheme_extend_ranges[p+1] {
+		return true
+	}
+	return false
+}
+
+
+// Hangul_Syllable_Type=Leading_Jamo
+@(require_results)
+is_hangul_syllable_leading :: proc(r: rune) -> bool {
+	return 0x1100 <= r && r <= 0x115F || 0xA960 <= r && r <= 0xA97C
+}
+
+// Hangul_Syllable_Type=Vowel_Jamo
+@(require_results)
+is_hangul_syllable_vowel :: proc(r: rune) -> bool {
+	return 0x1160 <= r && r <= 0x11A7 || 0xD7B0 <= r && r <= 0xD7C6
+}
+
+// Hangul_Syllable_Type=Trailing_Jamo
+@(require_results)
+is_hangul_syllable_trailing :: proc(r: rune) -> bool {
+	return 0x11A8 <= r && r <= 0x11FF || 0xD7CB <= r && r <= 0xD7FB
+}
+
+// Hangul_Syllable_Type=LV_Syllable
+@(require_results)
+is_hangul_syllable_lv :: proc(r: rune) -> bool {
+	c := i32(r)
+	p := binary_search(c, hangul_syllable_lv_singlets[:], len(hangul_syllable_lv_singlets), 1)
+	if p >= 0 && c == hangul_syllable_lv_singlets[p] {
+		return true
+	}
+	return false
+}
+
+// Hangul_Syllable_Type=LVT_Syllable
+@(require_results)
+is_hangul_syllable_lvt :: proc(r: rune) -> bool {
+	c := i32(r)
+	p := binary_search(c, hangul_syllable_lvt_ranges[:], len(hangul_syllable_lvt_ranges)/2, 2)
+	if p >= 0 && hangul_syllable_lvt_ranges[p] <= c && c <= hangul_syllable_lvt_ranges[p+1] {
+		return true
+	}
+	return false
+}
+
+
+// Indic_Syllabic_Category=Consonant_Preceding_Repha
+@(require_results)
+is_indic_consonant_preceding_repha :: proc(r: rune) -> bool {
+	switch r {
+	case 0x00D4E,
+	     0x11941,
+	     0x11D46,
+	     0x11F02:
+		return true
+	case:
+		return false
+	}
+}
+
+// Indic_Syllabic_Category=Consonant_Prefixed
+@(require_results)
+is_indic_consonant_prefixed :: proc(r: rune) -> bool {
+	switch r {
+	case 0x111C2 ..= 0x111C3,
+	     0x1193F,
+	     0x11A3A,
+	     0x11A84 ..= 0x11A89:
+		return true
+	case:
+		return false
+	}
+}
+
+// Indic_Conjunct_Break=Linker
+@(require_results)
+is_indic_conjunct_break_linker :: proc(r: rune) -> bool {
+	switch r {
+	case 0x094D,
+	     0x09CD,
+	     0x0ACD,
+	     0x0B4D,
+	     0x0C4D,
+	     0x0D4D:
+		return true
+	case:
+		return false
+	}
+}
+
+// Indic_Conjunct_Break=Consonant
+@(require_results)
+is_indic_conjunct_break_consonant :: proc(r: rune) -> bool {
+	c := i32(r)
+	p := binary_search(c, indic_conjunct_break_consonant_ranges[:], len(indic_conjunct_break_consonant_ranges)/2, 2)
+	if p >= 0 && indic_conjunct_break_consonant_ranges[p] <= c && c <= indic_conjunct_break_consonant_ranges[p+1] {
+		return true
+	}
+	return false
+}
+
+// Indic_Conjunct_Break=Extend
+@(require_results)
+is_indic_conjunct_break_extend :: proc(r: rune) -> bool {
+	c := i32(r)
+	p := binary_search(c, indic_conjunct_break_extend_ranges[:], len(indic_conjunct_break_extend_ranges)/2, 2)
+	if p >= 0 && indic_conjunct_break_extend_ranges[p] <= c && c <= indic_conjunct_break_extend_ranges[p+1] {
+		return true
+	}
+	return false
+}
+
+
+/*
+For grapheme text segmentation, from Unicode TR 29 Rev 43:
+
+```
+Indic_Syllabic_Category = Consonant_Preceding_Repha, or
+Indic_Syllabic_Category = Consonant_Prefixed, or
+Prepended_Concatenation_Mark = Yes
+```
+*/
+@(require_results)
+is_gcb_prepend_class :: proc(r: rune) -> bool {
+	return is_indic_consonant_preceding_repha(r) || is_indic_consonant_prefixed(r) || is_prepended_concatenation_mark(r)
+}
+
+/*
+For grapheme text segmentation, from Unicode TR 29 Rev 43:
+
+```
+Grapheme_Extend = Yes, or
+Emoji_Modifier = Yes
+
+This includes:
+General_Category = Nonspacing_Mark
+General_Category = Enclosing_Mark
+U+200C ZERO WIDTH NON-JOINER
+
+plus a few General_Category = Spacing_Mark needed for canonical equivalence.
+```
+*/
+@(require_results)
+is_gcb_extend_class :: proc(r: rune) -> bool {
+	return is_grapheme_extend(r) || is_emoji_modifier(r)
+}
+
+//
+// End of Unicode 15.1.0 block.
+//

+ 2449 - 0
core/unicode/tables.odin

@@ -1270,3 +1270,2452 @@ to_title_singlets := [?]i32{
 	0x01f1, 501,
 	0x01f3, 499,
 }
+
+//
+// The tables below are accurate as of Unicode 15.1.0.
+//
+
+@(rodata)
+spacing_mark_ranges := [?]i32 {
+	0x0903, 0x0903,
+	0x093B, 0x093B,
+	0x093E, 0x0940,
+	0x0949, 0x094C,
+	0x094E, 0x094F,
+	0x0982, 0x0983,
+	0x09BE, 0x09C0,
+	0x09C7, 0x09C8,
+	0x09CB, 0x09CC,
+	0x09D7, 0x09D7,
+	0x0A03, 0x0A03,
+	0x0A3E, 0x0A40,
+	0x0A83, 0x0A83,
+	0x0ABE, 0x0AC0,
+	0x0AC9, 0x0AC9,
+	0x0ACB, 0x0ACC,
+	0x0B02, 0x0B03,
+	0x0B3E, 0x0B3E,
+	0x0B40, 0x0B40,
+	0x0B47, 0x0B48,
+	0x0B4B, 0x0B4C,
+	0x0B57, 0x0B57,
+	0x0BBE, 0x0BBF,
+	0x0BC1, 0x0BC2,
+	0x0BC6, 0x0BC8,
+	0x0BCA, 0x0BCC,
+	0x0BD7, 0x0BD7,
+	0x0C01, 0x0C03,
+	0x0C41, 0x0C44,
+	0x0C82, 0x0C83,
+	0x0CBE, 0x0CBE,
+	0x0CC0, 0x0CC4,
+	0x0CC7, 0x0CC8,
+	0x0CCA, 0x0CCB,
+	0x0CD5, 0x0CD6,
+	0x0CF3, 0x0CF3,
+	0x0D02, 0x0D03,
+	0x0D3E, 0x0D40,
+	0x0D46, 0x0D48,
+	0x0D4A, 0x0D4C,
+	0x0D57, 0x0D57,
+	0x0D82, 0x0D83,
+	0x0DCF, 0x0DD1,
+	0x0DD8, 0x0DDF,
+	0x0DF2, 0x0DF3,
+	0x0F3E, 0x0F3F,
+	0x0F7F, 0x0F7F,
+	0x102B, 0x102C,
+	0x1031, 0x1031,
+	0x1038, 0x1038,
+	0x103B, 0x103C,
+	0x1056, 0x1057,
+	0x1062, 0x1064,
+	0x1067, 0x106D,
+	0x1083, 0x1084,
+	0x1087, 0x108C,
+	0x108F, 0x108F,
+	0x109A, 0x109C,
+	0x1715, 0x1715,
+	0x1734, 0x1734,
+	0x17B6, 0x17B6,
+	0x17BE, 0x17C5,
+	0x17C7, 0x17C8,
+	0x1923, 0x1926,
+	0x1929, 0x192B,
+	0x1930, 0x1931,
+	0x1933, 0x1938,
+	0x1A19, 0x1A1A,
+	0x1A55, 0x1A55,
+	0x1A57, 0x1A57,
+	0x1A61, 0x1A61,
+	0x1A63, 0x1A64,
+	0x1A6D, 0x1A72,
+	0x1B04, 0x1B04,
+	0x1B35, 0x1B35,
+	0x1B3B, 0x1B3B,
+	0x1B3D, 0x1B41,
+	0x1B43, 0x1B44,
+	0x1B82, 0x1B82,
+	0x1BA1, 0x1BA1,
+	0x1BA6, 0x1BA7,
+	0x1BAA, 0x1BAA,
+	0x1BE7, 0x1BE7,
+	0x1BEA, 0x1BEC,
+	0x1BEE, 0x1BEE,
+	0x1BF2, 0x1BF3,
+	0x1C24, 0x1C2B,
+	0x1C34, 0x1C35,
+	0x1CE1, 0x1CE1,
+	0x1CF7, 0x1CF7,
+	0x302E, 0x302F,
+	0xA823, 0xA824,
+	0xA827, 0xA827,
+	0xA880, 0xA881,
+	0xA8B4, 0xA8C3,
+	0xA952, 0xA953,
+	0xA983, 0xA983,
+	0xA9B4, 0xA9B5,
+	0xA9BA, 0xA9BB,
+	0xA9BE, 0xA9C0,
+	0xAA2F, 0xAA30,
+	0xAA33, 0xAA34,
+	0xAA4D, 0xAA4D,
+	0xAA7B, 0xAA7B,
+	0xAA7D, 0xAA7D,
+	0xAAEB, 0xAAEB,
+	0xAAEE, 0xAAEF,
+	0xAAF5, 0xAAF5,
+	0xABE3, 0xABE4,
+	0xABE6, 0xABE7,
+	0xABE9, 0xABEA,
+	0xABEC, 0xABEC,
+	0x11000, 0x11000,
+	0x11002, 0x11002,
+	0x11082, 0x11082,
+	0x110B0, 0x110B2,
+	0x110B7, 0x110B8,
+	0x1112C, 0x1112C,
+	0x11145, 0x11146,
+	0x11182, 0x11182,
+	0x111B3, 0x111B5,
+	0x111BF, 0x111C0,
+	0x111CE, 0x111CE,
+	0x1122C, 0x1122E,
+	0x11232, 0x11233,
+	0x11235, 0x11235,
+	0x112E0, 0x112E2,
+	0x11302, 0x11303,
+	0x1133E, 0x1133F,
+	0x11341, 0x11344,
+	0x11347, 0x11348,
+	0x1134B, 0x1134D,
+	0x11357, 0x11357,
+	0x11362, 0x11363,
+	0x11435, 0x11437,
+	0x11440, 0x11441,
+	0x11445, 0x11445,
+	0x114B0, 0x114B2,
+	0x114B9, 0x114B9,
+	0x114BB, 0x114BE,
+	0x114C1, 0x114C1,
+	0x115AF, 0x115B1,
+	0x115B8, 0x115BB,
+	0x115BE, 0x115BE,
+	0x11630, 0x11632,
+	0x1163B, 0x1163C,
+	0x1163E, 0x1163E,
+	0x116AC, 0x116AC,
+	0x116AE, 0x116AF,
+	0x116B6, 0x116B6,
+	0x11720, 0x11721,
+	0x11726, 0x11726,
+	0x1182C, 0x1182E,
+	0x11838, 0x11838,
+	0x11930, 0x11935,
+	0x11937, 0x11938,
+	0x1193D, 0x1193D,
+	0x11940, 0x11940,
+	0x11942, 0x11942,
+	0x119D1, 0x119D3,
+	0x119DC, 0x119DF,
+	0x119E4, 0x119E4,
+	0x11A39, 0x11A39,
+	0x11A57, 0x11A58,
+	0x11A97, 0x11A97,
+	0x11C2F, 0x11C2F,
+	0x11C3E, 0x11C3E,
+	0x11CA9, 0x11CA9,
+	0x11CB1, 0x11CB1,
+	0x11CB4, 0x11CB4,
+	0x11D8A, 0x11D8E,
+	0x11D93, 0x11D94,
+	0x11D96, 0x11D96,
+	0x11EF5, 0x11EF6,
+	0x11F03, 0x11F03,
+	0x11F34, 0x11F35,
+	0x11F3E, 0x11F3F,
+	0x11F41, 0x11F41,
+	0x16F51, 0x16F87,
+	0x16FF0, 0x16FF1,
+	0x1D165, 0x1D166,
+	0x1D16D, 0x1D172,
+}
+
+@(rodata)
+nonspacing_mark_ranges := [?]i32 {
+	0x0300, 0x036F,
+	0x0483, 0x0487,
+	0x0591, 0x05BD,
+	0x05BF, 0x05BF,
+	0x05C1, 0x05C2,
+	0x05C4, 0x05C5,
+	0x05C7, 0x05C7,
+	0x0610, 0x061A,
+	0x064B, 0x065F,
+	0x0670, 0x0670,
+	0x06D6, 0x06DC,
+	0x06DF, 0x06E4,
+	0x06E7, 0x06E8,
+	0x06EA, 0x06ED,
+	0x0711, 0x0711,
+	0x0730, 0x074A,
+	0x07A6, 0x07B0,
+	0x07EB, 0x07F3,
+	0x07FD, 0x07FD,
+	0x0816, 0x0819,
+	0x081B, 0x0823,
+	0x0825, 0x0827,
+	0x0829, 0x082D,
+	0x0859, 0x085B,
+	0x0898, 0x089F,
+	0x08CA, 0x08E1,
+	0x08E3, 0x0902,
+	0x093A, 0x093A,
+	0x093C, 0x093C,
+	0x0941, 0x0948,
+	0x094D, 0x094D,
+	0x0951, 0x0957,
+	0x0962, 0x0963,
+	0x0981, 0x0981,
+	0x09BC, 0x09BC,
+	0x09C1, 0x09C4,
+	0x09CD, 0x09CD,
+	0x09E2, 0x09E3,
+	0x09FE, 0x09FE,
+	0x0A01, 0x0A02,
+	0x0A3C, 0x0A3C,
+	0x0A41, 0x0A42,
+	0x0A47, 0x0A48,
+	0x0A4B, 0x0A4D,
+	0x0A51, 0x0A51,
+	0x0A70, 0x0A71,
+	0x0A75, 0x0A75,
+	0x0A81, 0x0A82,
+	0x0ABC, 0x0ABC,
+	0x0AC1, 0x0AC5,
+	0x0AC7, 0x0AC8,
+	0x0ACD, 0x0ACD,
+	0x0AE2, 0x0AE3,
+	0x0AFA, 0x0AFF,
+	0x0B01, 0x0B01,
+	0x0B3C, 0x0B3C,
+	0x0B3F, 0x0B3F,
+	0x0B41, 0x0B44,
+	0x0B4D, 0x0B4D,
+	0x0B55, 0x0B56,
+	0x0B62, 0x0B63,
+	0x0B82, 0x0B82,
+	0x0BC0, 0x0BC0,
+	0x0BCD, 0x0BCD,
+	0x0C00, 0x0C00,
+	0x0C04, 0x0C04,
+	0x0C3C, 0x0C3C,
+	0x0C3E, 0x0C40,
+	0x0C46, 0x0C48,
+	0x0C4A, 0x0C4D,
+	0x0C55, 0x0C56,
+	0x0C62, 0x0C63,
+	0x0C81, 0x0C81,
+	0x0CBC, 0x0CBC,
+	0x0CBF, 0x0CBF,
+	0x0CC6, 0x0CC6,
+	0x0CCC, 0x0CCD,
+	0x0CE2, 0x0CE3,
+	0x0D00, 0x0D01,
+	0x0D3B, 0x0D3C,
+	0x0D41, 0x0D44,
+	0x0D4D, 0x0D4D,
+	0x0D62, 0x0D63,
+	0x0D81, 0x0D81,
+	0x0DCA, 0x0DCA,
+	0x0DD2, 0x0DD4,
+	0x0DD6, 0x0DD6,
+	0x0E31, 0x0E31,
+	0x0E34, 0x0E3A,
+	0x0E47, 0x0E4E,
+	0x0EB1, 0x0EB1,
+	0x0EB4, 0x0EBC,
+	0x0EC8, 0x0ECE,
+	0x0F18, 0x0F19,
+	0x0F35, 0x0F35,
+	0x0F37, 0x0F37,
+	0x0F39, 0x0F39,
+	0x0F71, 0x0F7E,
+	0x0F80, 0x0F84,
+	0x0F86, 0x0F87,
+	0x0F8D, 0x0F97,
+	0x0F99, 0x0FBC,
+	0x0FC6, 0x0FC6,
+	0x102D, 0x1030,
+	0x1032, 0x1037,
+	0x1039, 0x103A,
+	0x103D, 0x103E,
+	0x1058, 0x1059,
+	0x105E, 0x1060,
+	0x1071, 0x1074,
+	0x1082, 0x1082,
+	0x1085, 0x1086,
+	0x108D, 0x108D,
+	0x109D, 0x109D,
+	0x135D, 0x135F,
+	0x1712, 0x1714,
+	0x1732, 0x1733,
+	0x1752, 0x1753,
+	0x1772, 0x1773,
+	0x17B4, 0x17B5,
+	0x17B7, 0x17BD,
+	0x17C6, 0x17C6,
+	0x17C9, 0x17D3,
+	0x17DD, 0x17DD,
+	0x180B, 0x180D,
+	0x180F, 0x180F,
+	0x1885, 0x1886,
+	0x18A9, 0x18A9,
+	0x1920, 0x1922,
+	0x1927, 0x1928,
+	0x1932, 0x1932,
+	0x1939, 0x193B,
+	0x1A17, 0x1A18,
+	0x1A1B, 0x1A1B,
+	0x1A56, 0x1A56,
+	0x1A58, 0x1A5E,
+	0x1A60, 0x1A60,
+	0x1A62, 0x1A62,
+	0x1A65, 0x1A6C,
+	0x1A73, 0x1A7C,
+	0x1A7F, 0x1A7F,
+	0x1AB0, 0x1ABD,
+	0x1ABF, 0x1ACE,
+	0x1B00, 0x1B03,
+	0x1B34, 0x1B34,
+	0x1B36, 0x1B3A,
+	0x1B3C, 0x1B3C,
+	0x1B42, 0x1B42,
+	0x1B6B, 0x1B73,
+	0x1B80, 0x1B81,
+	0x1BA2, 0x1BA5,
+	0x1BA8, 0x1BA9,
+	0x1BAB, 0x1BAD,
+	0x1BE6, 0x1BE6,
+	0x1BE8, 0x1BE9,
+	0x1BED, 0x1BED,
+	0x1BEF, 0x1BF1,
+	0x1C2C, 0x1C33,
+	0x1C36, 0x1C37,
+	0x1CD0, 0x1CD2,
+	0x1CD4, 0x1CE0,
+	0x1CE2, 0x1CE8,
+	0x1CED, 0x1CED,
+	0x1CF4, 0x1CF4,
+	0x1CF8, 0x1CF9,
+	0x1DC0, 0x1DFF,
+	0x20D0, 0x20DC,
+	0x20E1, 0x20E1,
+	0x20E5, 0x20F0,
+	0x2CEF, 0x2CF1,
+	0x2D7F, 0x2D7F,
+	0x2DE0, 0x2DFF,
+	0x302A, 0x302D,
+	0x3099, 0x309A,
+	0xA66F, 0xA66F,
+	0xA674, 0xA67D,
+	0xA69E, 0xA69F,
+	0xA6F0, 0xA6F1,
+	0xA802, 0xA802,
+	0xA806, 0xA806,
+	0xA80B, 0xA80B,
+	0xA825, 0xA826,
+	0xA82C, 0xA82C,
+	0xA8C4, 0xA8C5,
+	0xA8E0, 0xA8F1,
+	0xA8FF, 0xA8FF,
+	0xA926, 0xA92D,
+	0xA947, 0xA951,
+	0xA980, 0xA982,
+	0xA9B3, 0xA9B3,
+	0xA9B6, 0xA9B9,
+	0xA9BC, 0xA9BD,
+	0xA9E5, 0xA9E5,
+	0xAA29, 0xAA2E,
+	0xAA31, 0xAA32,
+	0xAA35, 0xAA36,
+	0xAA43, 0xAA43,
+	0xAA4C, 0xAA4C,
+	0xAA7C, 0xAA7C,
+	0xAAB0, 0xAAB0,
+	0xAAB2, 0xAAB4,
+	0xAAB7, 0xAAB8,
+	0xAABE, 0xAABF,
+	0xAAC1, 0xAAC1,
+	0xAAEC, 0xAAED,
+	0xAAF6, 0xAAF6,
+	0xABE5, 0xABE5,
+	0xABE8, 0xABE8,
+	0xABED, 0xABED,
+	0xFB1E, 0xFB1E,
+	0xFE00, 0xFE0F,
+	0xFE20, 0xFE2F,
+	0x101FD, 0x101FD,
+	0x102E0, 0x102E0,
+	0x10376, 0x1037A,
+	0x10A01, 0x10A03,
+	0x10A05, 0x10A06,
+	0x10A0C, 0x10A0F,
+	0x10A38, 0x10A3A,
+	0x10A3F, 0x10A3F,
+	0x10AE5, 0x10AE6,
+	0x10D24, 0x10D27,
+	0x10EAB, 0x10EAC,
+	0x10EFD, 0x10EFF,
+	0x10F46, 0x10F50,
+	0x10F82, 0x10F85,
+	0x11001, 0x11001,
+	0x11038, 0x11046,
+	0x11070, 0x11070,
+	0x11073, 0x11074,
+	0x1107F, 0x11081,
+	0x110B3, 0x110B6,
+	0x110B9, 0x110BA,
+	0x110C2, 0x110C2,
+	0x11100, 0x11102,
+	0x11127, 0x1112B,
+	0x1112D, 0x11134,
+	0x11173, 0x11173,
+	0x11180, 0x11181,
+	0x111B6, 0x111BE,
+	0x111C9, 0x111CC,
+	0x111CF, 0x111CF,
+	0x1122F, 0x11231,
+	0x11234, 0x11234,
+	0x11236, 0x11237,
+	0x1123E, 0x1123E,
+	0x11241, 0x11241,
+	0x112DF, 0x112DF,
+	0x112E3, 0x112EA,
+	0x11300, 0x11301,
+	0x1133B, 0x1133C,
+	0x11340, 0x11340,
+	0x11366, 0x1136C,
+	0x11370, 0x11374,
+	0x11438, 0x1143F,
+	0x11442, 0x11444,
+	0x11446, 0x11446,
+	0x1145E, 0x1145E,
+	0x114B3, 0x114B8,
+	0x114BA, 0x114BA,
+	0x114BF, 0x114C0,
+	0x114C2, 0x114C3,
+	0x115B2, 0x115B5,
+	0x115BC, 0x115BD,
+	0x115BF, 0x115C0,
+	0x115DC, 0x115DD,
+	0x11633, 0x1163A,
+	0x1163D, 0x1163D,
+	0x1163F, 0x11640,
+	0x116AB, 0x116AB,
+	0x116AD, 0x116AD,
+	0x116B0, 0x116B5,
+	0x116B7, 0x116B7,
+	0x1171D, 0x1171F,
+	0x11722, 0x11725,
+	0x11727, 0x1172B,
+	0x1182F, 0x11837,
+	0x11839, 0x1183A,
+	0x1193B, 0x1193C,
+	0x1193E, 0x1193E,
+	0x11943, 0x11943,
+	0x119D4, 0x119D7,
+	0x119DA, 0x119DB,
+	0x119E0, 0x119E0,
+	0x11A01, 0x11A0A,
+	0x11A33, 0x11A38,
+	0x11A3B, 0x11A3E,
+	0x11A47, 0x11A47,
+	0x11A51, 0x11A56,
+	0x11A59, 0x11A5B,
+	0x11A8A, 0x11A96,
+	0x11A98, 0x11A99,
+	0x11C30, 0x11C36,
+	0x11C38, 0x11C3D,
+	0x11C3F, 0x11C3F,
+	0x11C92, 0x11CA7,
+	0x11CAA, 0x11CB0,
+	0x11CB2, 0x11CB3,
+	0x11CB5, 0x11CB6,
+	0x11D31, 0x11D36,
+	0x11D3A, 0x11D3A,
+	0x11D3C, 0x11D3D,
+	0x11D3F, 0x11D45,
+	0x11D47, 0x11D47,
+	0x11D90, 0x11D91,
+	0x11D95, 0x11D95,
+	0x11D97, 0x11D97,
+	0x11EF3, 0x11EF4,
+	0x11F00, 0x11F01,
+	0x11F36, 0x11F3A,
+	0x11F40, 0x11F40,
+	0x11F42, 0x11F42,
+	0x13440, 0x13440,
+	0x13447, 0x13455,
+	0x16AF0, 0x16AF4,
+	0x16B30, 0x16B36,
+	0x16F4F, 0x16F4F,
+	0x16F8F, 0x16F92,
+	0x16FE4, 0x16FE4,
+	0x1BC9D, 0x1BC9E,
+	0x1CF00, 0x1CF2D,
+	0x1CF30, 0x1CF46,
+	0x1D167, 0x1D169,
+	0x1D17B, 0x1D182,
+	0x1D185, 0x1D18B,
+	0x1D1AA, 0x1D1AD,
+	0x1D242, 0x1D244,
+	0x1DA00, 0x1DA36,
+	0x1DA3B, 0x1DA6C,
+	0x1DA75, 0x1DA75,
+	0x1DA84, 0x1DA84,
+	0x1DA9B, 0x1DA9F,
+	0x1DAA1, 0x1DAAF,
+	0x1E000, 0x1E006,
+	0x1E008, 0x1E018,
+	0x1E01B, 0x1E021,
+	0x1E023, 0x1E024,
+	0x1E026, 0x1E02A,
+	0x1E08F, 0x1E08F,
+	0x1E130, 0x1E136,
+	0x1E2AE, 0x1E2AE,
+	0x1E2EC, 0x1E2EF,
+	0x1E4EC, 0x1E4EF,
+	0x1E8D0, 0x1E8D6,
+	0x1E944, 0x1E94A,
+	0xE0100, 0xE01EF,
+}
+
+@(rodata)
+emoji_extended_pictographic_ranges := [?]i32 {
+	0x00A9, 0x00A9,
+	0x00AE, 0x00AE,
+	0x203C, 0x203C,
+	0x2049, 0x2049,
+	0x2122, 0x2122,
+	0x2139, 0x2139,
+	0x2194, 0x2199,
+	0x21A9, 0x21AA,
+	0x231A, 0x231B,
+	0x2328, 0x2328,
+	0x2388, 0x2388,
+	0x23CF, 0x23CF,
+	0x23E9, 0x23EC,
+	0x23ED, 0x23EE,
+	0x23EF, 0x23EF,
+	0x23F0, 0x23F0,
+	0x23F1, 0x23F2,
+	0x23F3, 0x23F3,
+	0x23F8, 0x23FA,
+	0x24C2, 0x24C2,
+	0x25AA, 0x25AB,
+	0x25B6, 0x25B6,
+	0x25C0, 0x25C0,
+	0x25FB, 0x25FE,
+	0x2600, 0x2601,
+	0x2602, 0x2603,
+	0x2604, 0x2604,
+	0x2605, 0x2605,
+	0x2607, 0x260D,
+	0x260E, 0x260E,
+	0x260F, 0x2610,
+	0x2611, 0x2611,
+	0x2612, 0x2612,
+	0x2614, 0x2615,
+	0x2616, 0x2617,
+	0x2618, 0x2618,
+	0x2619, 0x261C,
+	0x261D, 0x261D,
+	0x261E, 0x261F,
+	0x2620, 0x2620,
+	0x2621, 0x2621,
+	0x2622, 0x2623,
+	0x2624, 0x2625,
+	0x2626, 0x2626,
+	0x2627, 0x2629,
+	0x262A, 0x262A,
+	0x262B, 0x262D,
+	0x262E, 0x262E,
+	0x262F, 0x262F,
+	0x2630, 0x2637,
+	0x2638, 0x2639,
+	0x263A, 0x263A,
+	0x263B, 0x263F,
+	0x2640, 0x2640,
+	0x2641, 0x2641,
+	0x2642, 0x2642,
+	0x2643, 0x2647,
+	0x2648, 0x2653,
+	0x2654, 0x265E,
+	0x265F, 0x265F,
+	0x2660, 0x2660,
+	0x2661, 0x2662,
+	0x2663, 0x2663,
+	0x2664, 0x2664,
+	0x2665, 0x2666,
+	0x2667, 0x2667,
+	0x2668, 0x2668,
+	0x2669, 0x267A,
+	0x267B, 0x267B,
+	0x267C, 0x267D,
+	0x267E, 0x267E,
+	0x267F, 0x267F,
+	0x2680, 0x2685,
+	0x2690, 0x2691,
+	0x2692, 0x2692,
+	0x2693, 0x2693,
+	0x2694, 0x2694,
+	0x2695, 0x2695,
+	0x2696, 0x2697,
+	0x2698, 0x2698,
+	0x2699, 0x2699,
+	0x269A, 0x269A,
+	0x269B, 0x269C,
+	0x269D, 0x269F,
+	0x26A0, 0x26A1,
+	0x26A2, 0x26A6,
+	0x26A7, 0x26A7,
+	0x26A8, 0x26A9,
+	0x26AA, 0x26AB,
+	0x26AC, 0x26AF,
+	0x26B0, 0x26B1,
+	0x26B2, 0x26BC,
+	0x26BD, 0x26BE,
+	0x26BF, 0x26C3,
+	0x26C4, 0x26C5,
+	0x26C6, 0x26C7,
+	0x26C8, 0x26C8,
+	0x26C9, 0x26CD,
+	0x26CE, 0x26CE,
+	0x26CF, 0x26CF,
+	0x26D0, 0x26D0,
+	0x26D1, 0x26D1,
+	0x26D2, 0x26D2,
+	0x26D3, 0x26D3,
+	0x26D4, 0x26D4,
+	0x26D5, 0x26E8,
+	0x26E9, 0x26E9,
+	0x26EA, 0x26EA,
+	0x26EB, 0x26EF,
+	0x26F0, 0x26F1,
+	0x26F2, 0x26F3,
+	0x26F4, 0x26F4,
+	0x26F5, 0x26F5,
+	0x26F6, 0x26F6,
+	0x26F7, 0x26F9,
+	0x26FA, 0x26FA,
+	0x26FB, 0x26FC,
+	0x26FD, 0x26FD,
+	0x26FE, 0x2701,
+	0x2702, 0x2702,
+	0x2703, 0x2704,
+	0x2705, 0x2705,
+	0x2708, 0x270C,
+	0x270D, 0x270D,
+	0x270E, 0x270E,
+	0x270F, 0x270F,
+	0x2710, 0x2711,
+	0x2712, 0x2712,
+	0x2714, 0x2714,
+	0x2716, 0x2716,
+	0x271D, 0x271D,
+	0x2721, 0x2721,
+	0x2728, 0x2728,
+	0x2733, 0x2734,
+	0x2744, 0x2744,
+	0x2747, 0x2747,
+	0x274C, 0x274C,
+	0x274E, 0x274E,
+	0x2753, 0x2755,
+	0x2757, 0x2757,
+	0x2763, 0x2763,
+	0x2764, 0x2764,
+	0x2765, 0x2767,
+	0x2795, 0x2797,
+	0x27A1, 0x27A1,
+	0x27B0, 0x27B0,
+	0x27BF, 0x27BF,
+	0x2934, 0x2935,
+	0x2B05, 0x2B07,
+	0x2B1B, 0x2B1C,
+	0x2B50, 0x2B50,
+	0x2B55, 0x2B55,
+	0x3030, 0x3030,
+	0x303D, 0x303D,
+	0x3297, 0x3297,
+	0x3299, 0x3299,
+	0x1F000, 0x1F003,
+	0x1F004, 0x1F004,
+	0x1F005, 0x1F0CE,
+	0x1F0CF, 0x1F0CF,
+	0x1F0D0, 0x1F0FF,
+	0x1F10D, 0x1F10F,
+	0x1F12F, 0x1F12F,
+	0x1F16C, 0x1F16F,
+	0x1F170, 0x1F171,
+	0x1F17E, 0x1F17F,
+	0x1F18E, 0x1F18E,
+	0x1F191, 0x1F19A,
+	0x1F1AD, 0x1F1E5,
+	0x1F201, 0x1F202,
+	0x1F203, 0x1F20F,
+	0x1F21A, 0x1F21A,
+	0x1F22F, 0x1F22F,
+	0x1F232, 0x1F23A,
+	0x1F23C, 0x1F23F,
+	0x1F249, 0x1F24F,
+	0x1F250, 0x1F251,
+	0x1F252, 0x1F2FF,
+	0x1F300, 0x1F30C,
+	0x1F30D, 0x1F30E,
+	0x1F30F, 0x1F30F,
+	0x1F310, 0x1F310,
+	0x1F311, 0x1F311,
+	0x1F312, 0x1F312,
+	0x1F313, 0x1F315,
+	0x1F316, 0x1F318,
+	0x1F319, 0x1F319,
+	0x1F31A, 0x1F31A,
+	0x1F31B, 0x1F31B,
+	0x1F31C, 0x1F31C,
+	0x1F31D, 0x1F31E,
+	0x1F31F, 0x1F320,
+	0x1F321, 0x1F321,
+	0x1F322, 0x1F323,
+	0x1F324, 0x1F32C,
+	0x1F32D, 0x1F32F,
+	0x1F330, 0x1F331,
+	0x1F332, 0x1F333,
+	0x1F334, 0x1F335,
+	0x1F336, 0x1F336,
+	0x1F337, 0x1F34A,
+	0x1F34B, 0x1F34B,
+	0x1F34C, 0x1F34F,
+	0x1F350, 0x1F350,
+	0x1F351, 0x1F37B,
+	0x1F37C, 0x1F37C,
+	0x1F37D, 0x1F37D,
+	0x1F37E, 0x1F37F,
+	0x1F380, 0x1F393,
+	0x1F394, 0x1F395,
+	0x1F396, 0x1F397,
+	0x1F398, 0x1F398,
+	0x1F399, 0x1F39B,
+	0x1F39C, 0x1F39D,
+	0x1F39E, 0x1F39F,
+	0x1F3A0, 0x1F3C4,
+	0x1F3C5, 0x1F3C5,
+	0x1F3C6, 0x1F3C6,
+	0x1F3C7, 0x1F3C7,
+	0x1F3C8, 0x1F3C8,
+	0x1F3C9, 0x1F3C9,
+	0x1F3CA, 0x1F3CA,
+	0x1F3CB, 0x1F3CE,
+	0x1F3CF, 0x1F3D3,
+	0x1F3D4, 0x1F3DF,
+	0x1F3E0, 0x1F3E3,
+	0x1F3E4, 0x1F3E4,
+	0x1F3E5, 0x1F3F0,
+	0x1F3F1, 0x1F3F2,
+	0x1F3F3, 0x1F3F3,
+	0x1F3F4, 0x1F3F4,
+	0x1F3F5, 0x1F3F5,
+	0x1F3F6, 0x1F3F6,
+	0x1F3F7, 0x1F3F7,
+	0x1F3F8, 0x1F3FA,
+	0x1F400, 0x1F407,
+	0x1F408, 0x1F408,
+	0x1F409, 0x1F40B,
+	0x1F40C, 0x1F40E,
+	0x1F40F, 0x1F410,
+	0x1F411, 0x1F412,
+	0x1F413, 0x1F413,
+	0x1F414, 0x1F414,
+	0x1F415, 0x1F415,
+	0x1F416, 0x1F416,
+	0x1F417, 0x1F429,
+	0x1F42A, 0x1F42A,
+	0x1F42B, 0x1F43E,
+	0x1F43F, 0x1F43F,
+	0x1F440, 0x1F440,
+	0x1F441, 0x1F441,
+	0x1F442, 0x1F464,
+	0x1F465, 0x1F465,
+	0x1F466, 0x1F46B,
+	0x1F46C, 0x1F46D,
+	0x1F46E, 0x1F4AC,
+	0x1F4AD, 0x1F4AD,
+	0x1F4AE, 0x1F4B5,
+	0x1F4B6, 0x1F4B7,
+	0x1F4B8, 0x1F4EB,
+	0x1F4EC, 0x1F4ED,
+	0x1F4EE, 0x1F4EE,
+	0x1F4EF, 0x1F4EF,
+	0x1F4F0, 0x1F4F4,
+	0x1F4F5, 0x1F4F5,
+	0x1F4F6, 0x1F4F7,
+	0x1F4F8, 0x1F4F8,
+	0x1F4F9, 0x1F4FC,
+	0x1F4FD, 0x1F4FD,
+	0x1F4FE, 0x1F4FE,
+	0x1F4FF, 0x1F502,
+	0x1F503, 0x1F503,
+	0x1F504, 0x1F507,
+	0x1F508, 0x1F508,
+	0x1F509, 0x1F509,
+	0x1F50A, 0x1F514,
+	0x1F515, 0x1F515,
+	0x1F516, 0x1F52B,
+	0x1F52C, 0x1F52D,
+	0x1F52E, 0x1F53D,
+	0x1F546, 0x1F548,
+	0x1F549, 0x1F54A,
+	0x1F54B, 0x1F54E,
+	0x1F54F, 0x1F54F,
+	0x1F550, 0x1F55B,
+	0x1F55C, 0x1F567,
+	0x1F568, 0x1F56E,
+	0x1F56F, 0x1F570,
+	0x1F571, 0x1F572,
+	0x1F573, 0x1F579,
+	0x1F57A, 0x1F57A,
+	0x1F57B, 0x1F586,
+	0x1F587, 0x1F587,
+	0x1F588, 0x1F589,
+	0x1F58A, 0x1F58D,
+	0x1F58E, 0x1F58F,
+	0x1F590, 0x1F590,
+	0x1F591, 0x1F594,
+	0x1F595, 0x1F596,
+	0x1F597, 0x1F5A3,
+	0x1F5A4, 0x1F5A4,
+	0x1F5A5, 0x1F5A5,
+	0x1F5A6, 0x1F5A7,
+	0x1F5A8, 0x1F5A8,
+	0x1F5A9, 0x1F5B0,
+	0x1F5B1, 0x1F5B2,
+	0x1F5B3, 0x1F5BB,
+	0x1F5BC, 0x1F5BC,
+	0x1F5BD, 0x1F5C1,
+	0x1F5C2, 0x1F5C4,
+	0x1F5C5, 0x1F5D0,
+	0x1F5D1, 0x1F5D3,
+	0x1F5D4, 0x1F5DB,
+	0x1F5DC, 0x1F5DE,
+	0x1F5DF, 0x1F5E0,
+	0x1F5E1, 0x1F5E1,
+	0x1F5E2, 0x1F5E2,
+	0x1F5E3, 0x1F5E3,
+	0x1F5E4, 0x1F5E7,
+	0x1F5E8, 0x1F5E8,
+	0x1F5E9, 0x1F5EE,
+	0x1F5EF, 0x1F5EF,
+	0x1F5F0, 0x1F5F2,
+	0x1F5F3, 0x1F5F3,
+	0x1F5F4, 0x1F5F9,
+	0x1F5FA, 0x1F5FA,
+	0x1F5FB, 0x1F5FF,
+	0x1F600, 0x1F600,
+	0x1F601, 0x1F606,
+	0x1F607, 0x1F608,
+	0x1F609, 0x1F60D,
+	0x1F60E, 0x1F60E,
+	0x1F60F, 0x1F60F,
+	0x1F610, 0x1F610,
+	0x1F611, 0x1F611,
+	0x1F612, 0x1F614,
+	0x1F615, 0x1F615,
+	0x1F616, 0x1F616,
+	0x1F617, 0x1F617,
+	0x1F618, 0x1F618,
+	0x1F619, 0x1F619,
+	0x1F61A, 0x1F61A,
+	0x1F61B, 0x1F61B,
+	0x1F61C, 0x1F61E,
+	0x1F61F, 0x1F61F,
+	0x1F620, 0x1F625,
+	0x1F626, 0x1F627,
+	0x1F628, 0x1F62B,
+	0x1F62C, 0x1F62C,
+	0x1F62D, 0x1F62D,
+	0x1F62E, 0x1F62F,
+	0x1F630, 0x1F633,
+	0x1F634, 0x1F634,
+	0x1F635, 0x1F635,
+	0x1F636, 0x1F636,
+	0x1F637, 0x1F640,
+	0x1F641, 0x1F644,
+	0x1F645, 0x1F64F,
+	0x1F680, 0x1F680,
+	0x1F681, 0x1F682,
+	0x1F683, 0x1F685,
+	0x1F686, 0x1F686,
+	0x1F687, 0x1F687,
+	0x1F688, 0x1F688,
+	0x1F689, 0x1F689,
+	0x1F68A, 0x1F68B,
+	0x1F68C, 0x1F68C,
+	0x1F68D, 0x1F68D,
+	0x1F68E, 0x1F68E,
+	0x1F68F, 0x1F68F,
+	0x1F690, 0x1F690,
+	0x1F691, 0x1F693,
+	0x1F694, 0x1F694,
+	0x1F695, 0x1F695,
+	0x1F696, 0x1F696,
+	0x1F697, 0x1F697,
+	0x1F698, 0x1F698,
+	0x1F699, 0x1F69A,
+	0x1F69B, 0x1F6A1,
+	0x1F6A2, 0x1F6A2,
+	0x1F6A3, 0x1F6A3,
+	0x1F6A4, 0x1F6A5,
+	0x1F6A6, 0x1F6A6,
+	0x1F6A7, 0x1F6AD,
+	0x1F6AE, 0x1F6B1,
+	0x1F6B2, 0x1F6B2,
+	0x1F6B3, 0x1F6B5,
+	0x1F6B6, 0x1F6B6,
+	0x1F6B7, 0x1F6B8,
+	0x1F6B9, 0x1F6BE,
+	0x1F6BF, 0x1F6BF,
+	0x1F6C0, 0x1F6C0,
+	0x1F6C1, 0x1F6C5,
+	0x1F6C6, 0x1F6CA,
+	0x1F6CB, 0x1F6CB,
+	0x1F6CC, 0x1F6CC,
+	0x1F6CD, 0x1F6CF,
+	0x1F6D0, 0x1F6D0,
+	0x1F6D1, 0x1F6D2,
+	0x1F6D3, 0x1F6D4,
+	0x1F6D5, 0x1F6D5,
+	0x1F6D6, 0x1F6D7,
+	0x1F6D8, 0x1F6DB,
+	0x1F6DC, 0x1F6DC,
+	0x1F6DD, 0x1F6DF,
+	0x1F6E0, 0x1F6E5,
+	0x1F6E6, 0x1F6E8,
+	0x1F6E9, 0x1F6E9,
+	0x1F6EA, 0x1F6EA,
+	0x1F6EB, 0x1F6EC,
+	0x1F6ED, 0x1F6EF,
+	0x1F6F0, 0x1F6F0,
+	0x1F6F1, 0x1F6F2,
+	0x1F6F3, 0x1F6F3,
+	0x1F6F4, 0x1F6F6,
+	0x1F6F7, 0x1F6F8,
+	0x1F6F9, 0x1F6F9,
+	0x1F6FA, 0x1F6FA,
+	0x1F6FB, 0x1F6FC,
+	0x1F6FD, 0x1F6FF,
+	0x1F774, 0x1F77F,
+	0x1F7D5, 0x1F7DF,
+	0x1F7E0, 0x1F7EB,
+	0x1F7EC, 0x1F7EF,
+	0x1F7F0, 0x1F7F0,
+	0x1F7F1, 0x1F7FF,
+	0x1F80C, 0x1F80F,
+	0x1F848, 0x1F84F,
+	0x1F85A, 0x1F85F,
+	0x1F888, 0x1F88F,
+	0x1F8AE, 0x1F8FF,
+	0x1F90C, 0x1F90C,
+	0x1F90D, 0x1F90F,
+	0x1F910, 0x1F918,
+	0x1F919, 0x1F91E,
+	0x1F91F, 0x1F91F,
+	0x1F920, 0x1F927,
+	0x1F928, 0x1F92F,
+	0x1F930, 0x1F930,
+	0x1F931, 0x1F932,
+	0x1F933, 0x1F93A,
+	0x1F93C, 0x1F93E,
+	0x1F93F, 0x1F93F,
+	0x1F940, 0x1F945,
+	0x1F947, 0x1F94B,
+	0x1F94C, 0x1F94C,
+	0x1F94D, 0x1F94F,
+	0x1F950, 0x1F95E,
+	0x1F95F, 0x1F96B,
+	0x1F96C, 0x1F970,
+	0x1F971, 0x1F971,
+	0x1F972, 0x1F972,
+	0x1F973, 0x1F976,
+	0x1F977, 0x1F978,
+	0x1F979, 0x1F979,
+	0x1F97A, 0x1F97A,
+	0x1F97B, 0x1F97B,
+	0x1F97C, 0x1F97F,
+	0x1F980, 0x1F984,
+	0x1F985, 0x1F991,
+	0x1F992, 0x1F997,
+	0x1F998, 0x1F9A2,
+	0x1F9A3, 0x1F9A4,
+	0x1F9A5, 0x1F9AA,
+	0x1F9AB, 0x1F9AD,
+	0x1F9AE, 0x1F9AF,
+	0x1F9B0, 0x1F9B9,
+	0x1F9BA, 0x1F9BF,
+	0x1F9C0, 0x1F9C0,
+	0x1F9C1, 0x1F9C2,
+	0x1F9C3, 0x1F9CA,
+	0x1F9CB, 0x1F9CB,
+	0x1F9CC, 0x1F9CC,
+	0x1F9CD, 0x1F9CF,
+	0x1F9D0, 0x1F9E6,
+	0x1F9E7, 0x1F9FF,
+	0x1FA00, 0x1FA6F,
+	0x1FA70, 0x1FA73,
+	0x1FA74, 0x1FA74,
+	0x1FA75, 0x1FA77,
+	0x1FA78, 0x1FA7A,
+	0x1FA7B, 0x1FA7C,
+	0x1FA7D, 0x1FA7F,
+	0x1FA80, 0x1FA82,
+	0x1FA83, 0x1FA86,
+	0x1FA87, 0x1FA88,
+	0x1FA89, 0x1FA8F,
+	0x1FA90, 0x1FA95,
+	0x1FA96, 0x1FAA8,
+	0x1FAA9, 0x1FAAC,
+	0x1FAAD, 0x1FAAF,
+	0x1FAB0, 0x1FAB6,
+	0x1FAB7, 0x1FABA,
+	0x1FABB, 0x1FABD,
+	0x1FABE, 0x1FABE,
+	0x1FABF, 0x1FABF,
+	0x1FAC0, 0x1FAC2,
+	0x1FAC3, 0x1FAC5,
+	0x1FAC6, 0x1FACD,
+	0x1FACE, 0x1FACF,
+	0x1FAD0, 0x1FAD6,
+	0x1FAD7, 0x1FAD9,
+	0x1FADA, 0x1FADB,
+	0x1FADC, 0x1FADF,
+	0x1FAE0, 0x1FAE7,
+	0x1FAE8, 0x1FAE8,
+	0x1FAE9, 0x1FAEF,
+	0x1FAF0, 0x1FAF6,
+	0x1FAF7, 0x1FAF8,
+	0x1FAF9, 0x1FAFF,
+	0x1FC00, 0x1FFFD,
+}
+
+@(rodata)
+grapheme_extend_ranges := [?]i32 {
+	0x0300, 0x036F,
+	0x0483, 0x0487,
+	0x0488, 0x0489,
+	0x0591, 0x05BD,
+	0x05BF, 0x05BF,
+	0x05C1, 0x05C2,
+	0x05C4, 0x05C5,
+	0x05C7, 0x05C7,
+	0x0610, 0x061A,
+	0x064B, 0x065F,
+	0x0670, 0x0670,
+	0x06D6, 0x06DC,
+	0x06DF, 0x06E4,
+	0x06E7, 0x06E8,
+	0x06EA, 0x06ED,
+	0x0711, 0x0711,
+	0x0730, 0x074A,
+	0x07A6, 0x07B0,
+	0x07EB, 0x07F3,
+	0x07FD, 0x07FD,
+	0x0816, 0x0819,
+	0x081B, 0x0823,
+	0x0825, 0x0827,
+	0x0829, 0x082D,
+	0x0859, 0x085B,
+	0x0898, 0x089F,
+	0x08CA, 0x08E1,
+	0x08E3, 0x0902,
+	0x093A, 0x093A,
+	0x093C, 0x093C,
+	0x0941, 0x0948,
+	0x094D, 0x094D,
+	0x0951, 0x0957,
+	0x0962, 0x0963,
+	0x0981, 0x0981,
+	0x09BC, 0x09BC,
+	0x09BE, 0x09BE,
+	0x09C1, 0x09C4,
+	0x09CD, 0x09CD,
+	0x09D7, 0x09D7,
+	0x09E2, 0x09E3,
+	0x09FE, 0x09FE,
+	0x0A01, 0x0A02,
+	0x0A3C, 0x0A3C,
+	0x0A41, 0x0A42,
+	0x0A47, 0x0A48,
+	0x0A4B, 0x0A4D,
+	0x0A51, 0x0A51,
+	0x0A70, 0x0A71,
+	0x0A75, 0x0A75,
+	0x0A81, 0x0A82,
+	0x0ABC, 0x0ABC,
+	0x0AC1, 0x0AC5,
+	0x0AC7, 0x0AC8,
+	0x0ACD, 0x0ACD,
+	0x0AE2, 0x0AE3,
+	0x0AFA, 0x0AFF,
+	0x0B01, 0x0B01,
+	0x0B3C, 0x0B3C,
+	0x0B3E, 0x0B3E,
+	0x0B3F, 0x0B3F,
+	0x0B41, 0x0B44,
+	0x0B4D, 0x0B4D,
+	0x0B55, 0x0B56,
+	0x0B57, 0x0B57,
+	0x0B62, 0x0B63,
+	0x0B82, 0x0B82,
+	0x0BBE, 0x0BBE,
+	0x0BC0, 0x0BC0,
+	0x0BCD, 0x0BCD,
+	0x0BD7, 0x0BD7,
+	0x0C00, 0x0C00,
+	0x0C04, 0x0C04,
+	0x0C3C, 0x0C3C,
+	0x0C3E, 0x0C40,
+	0x0C46, 0x0C48,
+	0x0C4A, 0x0C4D,
+	0x0C55, 0x0C56,
+	0x0C62, 0x0C63,
+	0x0C81, 0x0C81,
+	0x0CBC, 0x0CBC,
+	0x0CBF, 0x0CBF,
+	0x0CC2, 0x0CC2,
+	0x0CC6, 0x0CC6,
+	0x0CCC, 0x0CCD,
+	0x0CD5, 0x0CD6,
+	0x0CE2, 0x0CE3,
+	0x0D00, 0x0D01,
+	0x0D3B, 0x0D3C,
+	0x0D3E, 0x0D3E,
+	0x0D41, 0x0D44,
+	0x0D4D, 0x0D4D,
+	0x0D57, 0x0D57,
+	0x0D62, 0x0D63,
+	0x0D81, 0x0D81,
+	0x0DCA, 0x0DCA,
+	0x0DCF, 0x0DCF,
+	0x0DD2, 0x0DD4,
+	0x0DD6, 0x0DD6,
+	0x0DDF, 0x0DDF,
+	0x0E31, 0x0E31,
+	0x0E34, 0x0E3A,
+	0x0E47, 0x0E4E,
+	0x0EB1, 0x0EB1,
+	0x0EB4, 0x0EBC,
+	0x0EC8, 0x0ECE,
+	0x0F18, 0x0F19,
+	0x0F35, 0x0F35,
+	0x0F37, 0x0F37,
+	0x0F39, 0x0F39,
+	0x0F71, 0x0F7E,
+	0x0F80, 0x0F84,
+	0x0F86, 0x0F87,
+	0x0F8D, 0x0F97,
+	0x0F99, 0x0FBC,
+	0x0FC6, 0x0FC6,
+	0x102D, 0x1030,
+	0x1032, 0x1037,
+	0x1039, 0x103A,
+	0x103D, 0x103E,
+	0x1058, 0x1059,
+	0x105E, 0x1060,
+	0x1071, 0x1074,
+	0x1082, 0x1082,
+	0x1085, 0x1086,
+	0x108D, 0x108D,
+	0x109D, 0x109D,
+	0x135D, 0x135F,
+	0x1712, 0x1714,
+	0x1732, 0x1733,
+	0x1752, 0x1753,
+	0x1772, 0x1773,
+	0x17B4, 0x17B5,
+	0x17B7, 0x17BD,
+	0x17C6, 0x17C6,
+	0x17C9, 0x17D3,
+	0x17DD, 0x17DD,
+	0x180B, 0x180D,
+	0x180F, 0x180F,
+	0x1885, 0x1886,
+	0x18A9, 0x18A9,
+	0x1920, 0x1922,
+	0x1927, 0x1928,
+	0x1932, 0x1932,
+	0x1939, 0x193B,
+	0x1A17, 0x1A18,
+	0x1A1B, 0x1A1B,
+	0x1A56, 0x1A56,
+	0x1A58, 0x1A5E,
+	0x1A60, 0x1A60,
+	0x1A62, 0x1A62,
+	0x1A65, 0x1A6C,
+	0x1A73, 0x1A7C,
+	0x1A7F, 0x1A7F,
+	0x1AB0, 0x1ABD,
+	0x1ABE, 0x1ABE,
+	0x1ABF, 0x1ACE,
+	0x1B00, 0x1B03,
+	0x1B34, 0x1B34,
+	0x1B35, 0x1B35,
+	0x1B36, 0x1B3A,
+	0x1B3C, 0x1B3C,
+	0x1B42, 0x1B42,
+	0x1B6B, 0x1B73,
+	0x1B80, 0x1B81,
+	0x1BA2, 0x1BA5,
+	0x1BA8, 0x1BA9,
+	0x1BAB, 0x1BAD,
+	0x1BE6, 0x1BE6,
+	0x1BE8, 0x1BE9,
+	0x1BED, 0x1BED,
+	0x1BEF, 0x1BF1,
+	0x1C2C, 0x1C33,
+	0x1C36, 0x1C37,
+	0x1CD0, 0x1CD2,
+	0x1CD4, 0x1CE0,
+	0x1CE2, 0x1CE8,
+	0x1CED, 0x1CED,
+	0x1CF4, 0x1CF4,
+	0x1CF8, 0x1CF9,
+	0x1DC0, 0x1DFF,
+	0x200C, 0x200C,
+	0x20D0, 0x20DC,
+	0x20DD, 0x20E0,
+	0x20E1, 0x20E1,
+	0x20E2, 0x20E4,
+	0x20E5, 0x20F0,
+	0x2CEF, 0x2CF1,
+	0x2D7F, 0x2D7F,
+	0x2DE0, 0x2DFF,
+	0x302A, 0x302D,
+	0x302E, 0x302F,
+	0x3099, 0x309A,
+	0xA66F, 0xA66F,
+	0xA670, 0xA672,
+	0xA674, 0xA67D,
+	0xA69E, 0xA69F,
+	0xA6F0, 0xA6F1,
+	0xA802, 0xA802,
+	0xA806, 0xA806,
+	0xA80B, 0xA80B,
+	0xA825, 0xA826,
+	0xA82C, 0xA82C,
+	0xA8C4, 0xA8C5,
+	0xA8E0, 0xA8F1,
+	0xA8FF, 0xA8FF,
+	0xA926, 0xA92D,
+	0xA947, 0xA951,
+	0xA980, 0xA982,
+	0xA9B3, 0xA9B3,
+	0xA9B6, 0xA9B9,
+	0xA9BC, 0xA9BD,
+	0xA9E5, 0xA9E5,
+	0xAA29, 0xAA2E,
+	0xAA31, 0xAA32,
+	0xAA35, 0xAA36,
+	0xAA43, 0xAA43,
+	0xAA4C, 0xAA4C,
+	0xAA7C, 0xAA7C,
+	0xAAB0, 0xAAB0,
+	0xAAB2, 0xAAB4,
+	0xAAB7, 0xAAB8,
+	0xAABE, 0xAABF,
+	0xAAC1, 0xAAC1,
+	0xAAEC, 0xAAED,
+	0xAAF6, 0xAAF6,
+	0xABE5, 0xABE5,
+	0xABE8, 0xABE8,
+	0xABED, 0xABED,
+	0xFB1E, 0xFB1E,
+	0xFE00, 0xFE0F,
+	0xFE20, 0xFE2F,
+	0xFF9E, 0xFF9F,
+	0x101FD, 0x101FD,
+	0x102E0, 0x102E0,
+	0x10376, 0x1037A,
+	0x10A01, 0x10A03,
+	0x10A05, 0x10A06,
+	0x10A0C, 0x10A0F,
+	0x10A38, 0x10A3A,
+	0x10A3F, 0x10A3F,
+	0x10AE5, 0x10AE6,
+	0x10D24, 0x10D27,
+	0x10EAB, 0x10EAC,
+	0x10EFD, 0x10EFF,
+	0x10F46, 0x10F50,
+	0x10F82, 0x10F85,
+	0x11001, 0x11001,
+	0x11038, 0x11046,
+	0x11070, 0x11070,
+	0x11073, 0x11074,
+	0x1107F, 0x11081,
+	0x110B3, 0x110B6,
+	0x110B9, 0x110BA,
+	0x110C2, 0x110C2,
+	0x11100, 0x11102,
+	0x11127, 0x1112B,
+	0x1112D, 0x11134,
+	0x11173, 0x11173,
+	0x11180, 0x11181,
+	0x111B6, 0x111BE,
+	0x111C9, 0x111CC,
+	0x111CF, 0x111CF,
+	0x1122F, 0x11231,
+	0x11234, 0x11234,
+	0x11236, 0x11237,
+	0x1123E, 0x1123E,
+	0x11241, 0x11241,
+	0x112DF, 0x112DF,
+	0x112E3, 0x112EA,
+	0x11300, 0x11301,
+	0x1133B, 0x1133C,
+	0x1133E, 0x1133E,
+	0x11340, 0x11340,
+	0x11357, 0x11357,
+	0x11366, 0x1136C,
+	0x11370, 0x11374,
+	0x11438, 0x1143F,
+	0x11442, 0x11444,
+	0x11446, 0x11446,
+	0x1145E, 0x1145E,
+	0x114B0, 0x114B0,
+	0x114B3, 0x114B8,
+	0x114BA, 0x114BA,
+	0x114BD, 0x114BD,
+	0x114BF, 0x114C0,
+	0x114C2, 0x114C3,
+	0x115AF, 0x115AF,
+	0x115B2, 0x115B5,
+	0x115BC, 0x115BD,
+	0x115BF, 0x115C0,
+	0x115DC, 0x115DD,
+	0x11633, 0x1163A,
+	0x1163D, 0x1163D,
+	0x1163F, 0x11640,
+	0x116AB, 0x116AB,
+	0x116AD, 0x116AD,
+	0x116B0, 0x116B5,
+	0x116B7, 0x116B7,
+	0x1171D, 0x1171F,
+	0x11722, 0x11725,
+	0x11727, 0x1172B,
+	0x1182F, 0x11837,
+	0x11839, 0x1183A,
+	0x11930, 0x11930,
+	0x1193B, 0x1193C,
+	0x1193E, 0x1193E,
+	0x11943, 0x11943,
+	0x119D4, 0x119D7,
+	0x119DA, 0x119DB,
+	0x119E0, 0x119E0,
+	0x11A01, 0x11A0A,
+	0x11A33, 0x11A38,
+	0x11A3B, 0x11A3E,
+	0x11A47, 0x11A47,
+	0x11A51, 0x11A56,
+	0x11A59, 0x11A5B,
+	0x11A8A, 0x11A96,
+	0x11A98, 0x11A99,
+	0x11C30, 0x11C36,
+	0x11C38, 0x11C3D,
+	0x11C3F, 0x11C3F,
+	0x11C92, 0x11CA7,
+	0x11CAA, 0x11CB0,
+	0x11CB2, 0x11CB3,
+	0x11CB5, 0x11CB6,
+	0x11D31, 0x11D36,
+	0x11D3A, 0x11D3A,
+	0x11D3C, 0x11D3D,
+	0x11D3F, 0x11D45,
+	0x11D47, 0x11D47,
+	0x11D90, 0x11D91,
+	0x11D95, 0x11D95,
+	0x11D97, 0x11D97,
+	0x11EF3, 0x11EF4,
+	0x11F00, 0x11F01,
+	0x11F36, 0x11F3A,
+	0x11F40, 0x11F40,
+	0x11F42, 0x11F42,
+	0x13440, 0x13440,
+	0x13447, 0x13455,
+	0x16AF0, 0x16AF4,
+	0x16B30, 0x16B36,
+	0x16F4F, 0x16F4F,
+	0x16F8F, 0x16F92,
+	0x16FE4, 0x16FE4,
+	0x1BC9D, 0x1BC9E,
+	0x1CF00, 0x1CF2D,
+	0x1CF30, 0x1CF46,
+	0x1D165, 0x1D165,
+	0x1D167, 0x1D169,
+	0x1D16E, 0x1D172,
+	0x1D17B, 0x1D182,
+	0x1D185, 0x1D18B,
+	0x1D1AA, 0x1D1AD,
+	0x1D242, 0x1D244,
+	0x1DA00, 0x1DA36,
+	0x1DA3B, 0x1DA6C,
+	0x1DA75, 0x1DA75,
+	0x1DA84, 0x1DA84,
+	0x1DA9B, 0x1DA9F,
+	0x1DAA1, 0x1DAAF,
+	0x1E000, 0x1E006,
+	0x1E008, 0x1E018,
+	0x1E01B, 0x1E021,
+	0x1E023, 0x1E024,
+	0x1E026, 0x1E02A,
+	0x1E08F, 0x1E08F,
+	0x1E130, 0x1E136,
+	0x1E2AE, 0x1E2AE,
+	0x1E2EC, 0x1E2EF,
+	0x1E4EC, 0x1E4EF,
+	0x1E8D0, 0x1E8D6,
+	0x1E944, 0x1E94A,
+	0xE0020, 0xE007F,
+	0xE0100, 0xE01EF,
+}
+
+@(rodata)
+hangul_syllable_lv_singlets := [?]i32 {
+	0xAC00,
+	0xAC1C,
+	0xAC38,
+	0xAC54,
+	0xAC70,
+	0xAC8C,
+	0xACA8,
+	0xACC4,
+	0xACE0,
+	0xACFC,
+	0xAD18,
+	0xAD34,
+	0xAD50,
+	0xAD6C,
+	0xAD88,
+	0xADA4,
+	0xADC0,
+	0xADDC,
+	0xADF8,
+	0xAE14,
+	0xAE30,
+	0xAE4C,
+	0xAE68,
+	0xAE84,
+	0xAEA0,
+	0xAEBC,
+	0xAED8,
+	0xAEF4,
+	0xAF10,
+	0xAF2C,
+	0xAF48,
+	0xAF64,
+	0xAF80,
+	0xAF9C,
+	0xAFB8,
+	0xAFD4,
+	0xAFF0,
+	0xB00C,
+	0xB028,
+	0xB044,
+	0xB060,
+	0xB07C,
+	0xB098,
+	0xB0B4,
+	0xB0D0,
+	0xB0EC,
+	0xB108,
+	0xB124,
+	0xB140,
+	0xB15C,
+	0xB178,
+	0xB194,
+	0xB1B0,
+	0xB1CC,
+	0xB1E8,
+	0xB204,
+	0xB220,
+	0xB23C,
+	0xB258,
+	0xB274,
+	0xB290,
+	0xB2AC,
+	0xB2C8,
+	0xB2E4,
+	0xB300,
+	0xB31C,
+	0xB338,
+	0xB354,
+	0xB370,
+	0xB38C,
+	0xB3A8,
+	0xB3C4,
+	0xB3E0,
+	0xB3FC,
+	0xB418,
+	0xB434,
+	0xB450,
+	0xB46C,
+	0xB488,
+	0xB4A4,
+	0xB4C0,
+	0xB4DC,
+	0xB4F8,
+	0xB514,
+	0xB530,
+	0xB54C,
+	0xB568,
+	0xB584,
+	0xB5A0,
+	0xB5BC,
+	0xB5D8,
+	0xB5F4,
+	0xB610,
+	0xB62C,
+	0xB648,
+	0xB664,
+	0xB680,
+	0xB69C,
+	0xB6B8,
+	0xB6D4,
+	0xB6F0,
+	0xB70C,
+	0xB728,
+	0xB744,
+	0xB760,
+	0xB77C,
+	0xB798,
+	0xB7B4,
+	0xB7D0,
+	0xB7EC,
+	0xB808,
+	0xB824,
+	0xB840,
+	0xB85C,
+	0xB878,
+	0xB894,
+	0xB8B0,
+	0xB8CC,
+	0xB8E8,
+	0xB904,
+	0xB920,
+	0xB93C,
+	0xB958,
+	0xB974,
+	0xB990,
+	0xB9AC,
+	0xB9C8,
+	0xB9E4,
+	0xBA00,
+	0xBA1C,
+	0xBA38,
+	0xBA54,
+	0xBA70,
+	0xBA8C,
+	0xBAA8,
+	0xBAC4,
+	0xBAE0,
+	0xBAFC,
+	0xBB18,
+	0xBB34,
+	0xBB50,
+	0xBB6C,
+	0xBB88,
+	0xBBA4,
+	0xBBC0,
+	0xBBDC,
+	0xBBF8,
+	0xBC14,
+	0xBC30,
+	0xBC4C,
+	0xBC68,
+	0xBC84,
+	0xBCA0,
+	0xBCBC,
+	0xBCD8,
+	0xBCF4,
+	0xBD10,
+	0xBD2C,
+	0xBD48,
+	0xBD64,
+	0xBD80,
+	0xBD9C,
+	0xBDB8,
+	0xBDD4,
+	0xBDF0,
+	0xBE0C,
+	0xBE28,
+	0xBE44,
+	0xBE60,
+	0xBE7C,
+	0xBE98,
+	0xBEB4,
+	0xBED0,
+	0xBEEC,
+	0xBF08,
+	0xBF24,
+	0xBF40,
+	0xBF5C,
+	0xBF78,
+	0xBF94,
+	0xBFB0,
+	0xBFCC,
+	0xBFE8,
+	0xC004,
+	0xC020,
+	0xC03C,
+	0xC058,
+	0xC074,
+	0xC090,
+	0xC0AC,
+	0xC0C8,
+	0xC0E4,
+	0xC100,
+	0xC11C,
+	0xC138,
+	0xC154,
+	0xC170,
+	0xC18C,
+	0xC1A8,
+	0xC1C4,
+	0xC1E0,
+	0xC1FC,
+	0xC218,
+	0xC234,
+	0xC250,
+	0xC26C,
+	0xC288,
+	0xC2A4,
+	0xC2C0,
+	0xC2DC,
+	0xC2F8,
+	0xC314,
+	0xC330,
+	0xC34C,
+	0xC368,
+	0xC384,
+	0xC3A0,
+	0xC3BC,
+	0xC3D8,
+	0xC3F4,
+	0xC410,
+	0xC42C,
+	0xC448,
+	0xC464,
+	0xC480,
+	0xC49C,
+	0xC4B8,
+	0xC4D4,
+	0xC4F0,
+	0xC50C,
+	0xC528,
+	0xC544,
+	0xC560,
+	0xC57C,
+	0xC598,
+	0xC5B4,
+	0xC5D0,
+	0xC5EC,
+	0xC608,
+	0xC624,
+	0xC640,
+	0xC65C,
+	0xC678,
+	0xC694,
+	0xC6B0,
+	0xC6CC,
+	0xC6E8,
+	0xC704,
+	0xC720,
+	0xC73C,
+	0xC758,
+	0xC774,
+	0xC790,
+	0xC7AC,
+	0xC7C8,
+	0xC7E4,
+	0xC800,
+	0xC81C,
+	0xC838,
+	0xC854,
+	0xC870,
+	0xC88C,
+	0xC8A8,
+	0xC8C4,
+	0xC8E0,
+	0xC8FC,
+	0xC918,
+	0xC934,
+	0xC950,
+	0xC96C,
+	0xC988,
+	0xC9A4,
+	0xC9C0,
+	0xC9DC,
+	0xC9F8,
+	0xCA14,
+	0xCA30,
+	0xCA4C,
+	0xCA68,
+	0xCA84,
+	0xCAA0,
+	0xCABC,
+	0xCAD8,
+	0xCAF4,
+	0xCB10,
+	0xCB2C,
+	0xCB48,
+	0xCB64,
+	0xCB80,
+	0xCB9C,
+	0xCBB8,
+	0xCBD4,
+	0xCBF0,
+	0xCC0C,
+	0xCC28,
+	0xCC44,
+	0xCC60,
+	0xCC7C,
+	0xCC98,
+	0xCCB4,
+	0xCCD0,
+	0xCCEC,
+	0xCD08,
+	0xCD24,
+	0xCD40,
+	0xCD5C,
+	0xCD78,
+	0xCD94,
+	0xCDB0,
+	0xCDCC,
+	0xCDE8,
+	0xCE04,
+	0xCE20,
+	0xCE3C,
+	0xCE58,
+	0xCE74,
+	0xCE90,
+	0xCEAC,
+	0xCEC8,
+	0xCEE4,
+	0xCF00,
+	0xCF1C,
+	0xCF38,
+	0xCF54,
+	0xCF70,
+	0xCF8C,
+	0xCFA8,
+	0xCFC4,
+	0xCFE0,
+	0xCFFC,
+	0xD018,
+	0xD034,
+	0xD050,
+	0xD06C,
+	0xD088,
+	0xD0A4,
+	0xD0C0,
+	0xD0DC,
+	0xD0F8,
+	0xD114,
+	0xD130,
+	0xD14C,
+	0xD168,
+	0xD184,
+	0xD1A0,
+	0xD1BC,
+	0xD1D8,
+	0xD1F4,
+	0xD210,
+	0xD22C,
+	0xD248,
+	0xD264,
+	0xD280,
+	0xD29C,
+	0xD2B8,
+	0xD2D4,
+	0xD2F0,
+	0xD30C,
+	0xD328,
+	0xD344,
+	0xD360,
+	0xD37C,
+	0xD398,
+	0xD3B4,
+	0xD3D0,
+	0xD3EC,
+	0xD408,
+	0xD424,
+	0xD440,
+	0xD45C,
+	0xD478,
+	0xD494,
+	0xD4B0,
+	0xD4CC,
+	0xD4E8,
+	0xD504,
+	0xD520,
+	0xD53C,
+	0xD558,
+	0xD574,
+	0xD590,
+	0xD5AC,
+	0xD5C8,
+	0xD5E4,
+	0xD600,
+	0xD61C,
+	0xD638,
+	0xD654,
+	0xD670,
+	0xD68C,
+	0xD6A8,
+	0xD6C4,
+	0xD6E0,
+	0xD6FC,
+	0xD718,
+	0xD734,
+	0xD750,
+	0xD76C,
+	0xD788,
+}
+
+@(rodata)
+hangul_syllable_lvt_ranges := [?]i32 {
+	0xAC01, 0xAC1B,
+	0xAC1D, 0xAC37,
+	0xAC39, 0xAC53,
+	0xAC55, 0xAC6F,
+	0xAC71, 0xAC8B,
+	0xAC8D, 0xACA7,
+	0xACA9, 0xACC3,
+	0xACC5, 0xACDF,
+	0xACE1, 0xACFB,
+	0xACFD, 0xAD17,
+	0xAD19, 0xAD33,
+	0xAD35, 0xAD4F,
+	0xAD51, 0xAD6B,
+	0xAD6D, 0xAD87,
+	0xAD89, 0xADA3,
+	0xADA5, 0xADBF,
+	0xADC1, 0xADDB,
+	0xADDD, 0xADF7,
+	0xADF9, 0xAE13,
+	0xAE15, 0xAE2F,
+	0xAE31, 0xAE4B,
+	0xAE4D, 0xAE67,
+	0xAE69, 0xAE83,
+	0xAE85, 0xAE9F,
+	0xAEA1, 0xAEBB,
+	0xAEBD, 0xAED7,
+	0xAED9, 0xAEF3,
+	0xAEF5, 0xAF0F,
+	0xAF11, 0xAF2B,
+	0xAF2D, 0xAF47,
+	0xAF49, 0xAF63,
+	0xAF65, 0xAF7F,
+	0xAF81, 0xAF9B,
+	0xAF9D, 0xAFB7,
+	0xAFB9, 0xAFD3,
+	0xAFD5, 0xAFEF,
+	0xAFF1, 0xB00B,
+	0xB00D, 0xB027,
+	0xB029, 0xB043,
+	0xB045, 0xB05F,
+	0xB061, 0xB07B,
+	0xB07D, 0xB097,
+	0xB099, 0xB0B3,
+	0xB0B5, 0xB0CF,
+	0xB0D1, 0xB0EB,
+	0xB0ED, 0xB107,
+	0xB109, 0xB123,
+	0xB125, 0xB13F,
+	0xB141, 0xB15B,
+	0xB15D, 0xB177,
+	0xB179, 0xB193,
+	0xB195, 0xB1AF,
+	0xB1B1, 0xB1CB,
+	0xB1CD, 0xB1E7,
+	0xB1E9, 0xB203,
+	0xB205, 0xB21F,
+	0xB221, 0xB23B,
+	0xB23D, 0xB257,
+	0xB259, 0xB273,
+	0xB275, 0xB28F,
+	0xB291, 0xB2AB,
+	0xB2AD, 0xB2C7,
+	0xB2C9, 0xB2E3,
+	0xB2E5, 0xB2FF,
+	0xB301, 0xB31B,
+	0xB31D, 0xB337,
+	0xB339, 0xB353,
+	0xB355, 0xB36F,
+	0xB371, 0xB38B,
+	0xB38D, 0xB3A7,
+	0xB3A9, 0xB3C3,
+	0xB3C5, 0xB3DF,
+	0xB3E1, 0xB3FB,
+	0xB3FD, 0xB417,
+	0xB419, 0xB433,
+	0xB435, 0xB44F,
+	0xB451, 0xB46B,
+	0xB46D, 0xB487,
+	0xB489, 0xB4A3,
+	0xB4A5, 0xB4BF,
+	0xB4C1, 0xB4DB,
+	0xB4DD, 0xB4F7,
+	0xB4F9, 0xB513,
+	0xB515, 0xB52F,
+	0xB531, 0xB54B,
+	0xB54D, 0xB567,
+	0xB569, 0xB583,
+	0xB585, 0xB59F,
+	0xB5A1, 0xB5BB,
+	0xB5BD, 0xB5D7,
+	0xB5D9, 0xB5F3,
+	0xB5F5, 0xB60F,
+	0xB611, 0xB62B,
+	0xB62D, 0xB647,
+	0xB649, 0xB663,
+	0xB665, 0xB67F,
+	0xB681, 0xB69B,
+	0xB69D, 0xB6B7,
+	0xB6B9, 0xB6D3,
+	0xB6D5, 0xB6EF,
+	0xB6F1, 0xB70B,
+	0xB70D, 0xB727,
+	0xB729, 0xB743,
+	0xB745, 0xB75F,
+	0xB761, 0xB77B,
+	0xB77D, 0xB797,
+	0xB799, 0xB7B3,
+	0xB7B5, 0xB7CF,
+	0xB7D1, 0xB7EB,
+	0xB7ED, 0xB807,
+	0xB809, 0xB823,
+	0xB825, 0xB83F,
+	0xB841, 0xB85B,
+	0xB85D, 0xB877,
+	0xB879, 0xB893,
+	0xB895, 0xB8AF,
+	0xB8B1, 0xB8CB,
+	0xB8CD, 0xB8E7,
+	0xB8E9, 0xB903,
+	0xB905, 0xB91F,
+	0xB921, 0xB93B,
+	0xB93D, 0xB957,
+	0xB959, 0xB973,
+	0xB975, 0xB98F,
+	0xB991, 0xB9AB,
+	0xB9AD, 0xB9C7,
+	0xB9C9, 0xB9E3,
+	0xB9E5, 0xB9FF,
+	0xBA01, 0xBA1B,
+	0xBA1D, 0xBA37,
+	0xBA39, 0xBA53,
+	0xBA55, 0xBA6F,
+	0xBA71, 0xBA8B,
+	0xBA8D, 0xBAA7,
+	0xBAA9, 0xBAC3,
+	0xBAC5, 0xBADF,
+	0xBAE1, 0xBAFB,
+	0xBAFD, 0xBB17,
+	0xBB19, 0xBB33,
+	0xBB35, 0xBB4F,
+	0xBB51, 0xBB6B,
+	0xBB6D, 0xBB87,
+	0xBB89, 0xBBA3,
+	0xBBA5, 0xBBBF,
+	0xBBC1, 0xBBDB,
+	0xBBDD, 0xBBF7,
+	0xBBF9, 0xBC13,
+	0xBC15, 0xBC2F,
+	0xBC31, 0xBC4B,
+	0xBC4D, 0xBC67,
+	0xBC69, 0xBC83,
+	0xBC85, 0xBC9F,
+	0xBCA1, 0xBCBB,
+	0xBCBD, 0xBCD7,
+	0xBCD9, 0xBCF3,
+	0xBCF5, 0xBD0F,
+	0xBD11, 0xBD2B,
+	0xBD2D, 0xBD47,
+	0xBD49, 0xBD63,
+	0xBD65, 0xBD7F,
+	0xBD81, 0xBD9B,
+	0xBD9D, 0xBDB7,
+	0xBDB9, 0xBDD3,
+	0xBDD5, 0xBDEF,
+	0xBDF1, 0xBE0B,
+	0xBE0D, 0xBE27,
+	0xBE29, 0xBE43,
+	0xBE45, 0xBE5F,
+	0xBE61, 0xBE7B,
+	0xBE7D, 0xBE97,
+	0xBE99, 0xBEB3,
+	0xBEB5, 0xBECF,
+	0xBED1, 0xBEEB,
+	0xBEED, 0xBF07,
+	0xBF09, 0xBF23,
+	0xBF25, 0xBF3F,
+	0xBF41, 0xBF5B,
+	0xBF5D, 0xBF77,
+	0xBF79, 0xBF93,
+	0xBF95, 0xBFAF,
+	0xBFB1, 0xBFCB,
+	0xBFCD, 0xBFE7,
+	0xBFE9, 0xC003,
+	0xC005, 0xC01F,
+	0xC021, 0xC03B,
+	0xC03D, 0xC057,
+	0xC059, 0xC073,
+	0xC075, 0xC08F,
+	0xC091, 0xC0AB,
+	0xC0AD, 0xC0C7,
+	0xC0C9, 0xC0E3,
+	0xC0E5, 0xC0FF,
+	0xC101, 0xC11B,
+	0xC11D, 0xC137,
+	0xC139, 0xC153,
+	0xC155, 0xC16F,
+	0xC171, 0xC18B,
+	0xC18D, 0xC1A7,
+	0xC1A9, 0xC1C3,
+	0xC1C5, 0xC1DF,
+	0xC1E1, 0xC1FB,
+	0xC1FD, 0xC217,
+	0xC219, 0xC233,
+	0xC235, 0xC24F,
+	0xC251, 0xC26B,
+	0xC26D, 0xC287,
+	0xC289, 0xC2A3,
+	0xC2A5, 0xC2BF,
+	0xC2C1, 0xC2DB,
+	0xC2DD, 0xC2F7,
+	0xC2F9, 0xC313,
+	0xC315, 0xC32F,
+	0xC331, 0xC34B,
+	0xC34D, 0xC367,
+	0xC369, 0xC383,
+	0xC385, 0xC39F,
+	0xC3A1, 0xC3BB,
+	0xC3BD, 0xC3D7,
+	0xC3D9, 0xC3F3,
+	0xC3F5, 0xC40F,
+	0xC411, 0xC42B,
+	0xC42D, 0xC447,
+	0xC449, 0xC463,
+	0xC465, 0xC47F,
+	0xC481, 0xC49B,
+	0xC49D, 0xC4B7,
+	0xC4B9, 0xC4D3,
+	0xC4D5, 0xC4EF,
+	0xC4F1, 0xC50B,
+	0xC50D, 0xC527,
+	0xC529, 0xC543,
+	0xC545, 0xC55F,
+	0xC561, 0xC57B,
+	0xC57D, 0xC597,
+	0xC599, 0xC5B3,
+	0xC5B5, 0xC5CF,
+	0xC5D1, 0xC5EB,
+	0xC5ED, 0xC607,
+	0xC609, 0xC623,
+	0xC625, 0xC63F,
+	0xC641, 0xC65B,
+	0xC65D, 0xC677,
+	0xC679, 0xC693,
+	0xC695, 0xC6AF,
+	0xC6B1, 0xC6CB,
+	0xC6CD, 0xC6E7,
+	0xC6E9, 0xC703,
+	0xC705, 0xC71F,
+	0xC721, 0xC73B,
+	0xC73D, 0xC757,
+	0xC759, 0xC773,
+	0xC775, 0xC78F,
+	0xC791, 0xC7AB,
+	0xC7AD, 0xC7C7,
+	0xC7C9, 0xC7E3,
+	0xC7E5, 0xC7FF,
+	0xC801, 0xC81B,
+	0xC81D, 0xC837,
+	0xC839, 0xC853,
+	0xC855, 0xC86F,
+	0xC871, 0xC88B,
+	0xC88D, 0xC8A7,
+	0xC8A9, 0xC8C3,
+	0xC8C5, 0xC8DF,
+	0xC8E1, 0xC8FB,
+	0xC8FD, 0xC917,
+	0xC919, 0xC933,
+	0xC935, 0xC94F,
+	0xC951, 0xC96B,
+	0xC96D, 0xC987,
+	0xC989, 0xC9A3,
+	0xC9A5, 0xC9BF,
+	0xC9C1, 0xC9DB,
+	0xC9DD, 0xC9F7,
+	0xC9F9, 0xCA13,
+	0xCA15, 0xCA2F,
+	0xCA31, 0xCA4B,
+	0xCA4D, 0xCA67,
+	0xCA69, 0xCA83,
+	0xCA85, 0xCA9F,
+	0xCAA1, 0xCABB,
+	0xCABD, 0xCAD7,
+	0xCAD9, 0xCAF3,
+	0xCAF5, 0xCB0F,
+	0xCB11, 0xCB2B,
+	0xCB2D, 0xCB47,
+	0xCB49, 0xCB63,
+	0xCB65, 0xCB7F,
+	0xCB81, 0xCB9B,
+	0xCB9D, 0xCBB7,
+	0xCBB9, 0xCBD3,
+	0xCBD5, 0xCBEF,
+	0xCBF1, 0xCC0B,
+	0xCC0D, 0xCC27,
+	0xCC29, 0xCC43,
+	0xCC45, 0xCC5F,
+	0xCC61, 0xCC7B,
+	0xCC7D, 0xCC97,
+	0xCC99, 0xCCB3,
+	0xCCB5, 0xCCCF,
+	0xCCD1, 0xCCEB,
+	0xCCED, 0xCD07,
+	0xCD09, 0xCD23,
+	0xCD25, 0xCD3F,
+	0xCD41, 0xCD5B,
+	0xCD5D, 0xCD77,
+	0xCD79, 0xCD93,
+	0xCD95, 0xCDAF,
+	0xCDB1, 0xCDCB,
+	0xCDCD, 0xCDE7,
+	0xCDE9, 0xCE03,
+	0xCE05, 0xCE1F,
+	0xCE21, 0xCE3B,
+	0xCE3D, 0xCE57,
+	0xCE59, 0xCE73,
+	0xCE75, 0xCE8F,
+	0xCE91, 0xCEAB,
+	0xCEAD, 0xCEC7,
+	0xCEC9, 0xCEE3,
+	0xCEE5, 0xCEFF,
+	0xCF01, 0xCF1B,
+	0xCF1D, 0xCF37,
+	0xCF39, 0xCF53,
+	0xCF55, 0xCF6F,
+	0xCF71, 0xCF8B,
+	0xCF8D, 0xCFA7,
+	0xCFA9, 0xCFC3,
+	0xCFC5, 0xCFDF,
+	0xCFE1, 0xCFFB,
+	0xCFFD, 0xD017,
+	0xD019, 0xD033,
+	0xD035, 0xD04F,
+	0xD051, 0xD06B,
+	0xD06D, 0xD087,
+	0xD089, 0xD0A3,
+	0xD0A5, 0xD0BF,
+	0xD0C1, 0xD0DB,
+	0xD0DD, 0xD0F7,
+	0xD0F9, 0xD113,
+	0xD115, 0xD12F,
+	0xD131, 0xD14B,
+	0xD14D, 0xD167,
+	0xD169, 0xD183,
+	0xD185, 0xD19F,
+	0xD1A1, 0xD1BB,
+	0xD1BD, 0xD1D7,
+	0xD1D9, 0xD1F3,
+	0xD1F5, 0xD20F,
+	0xD211, 0xD22B,
+	0xD22D, 0xD247,
+	0xD249, 0xD263,
+	0xD265, 0xD27F,
+	0xD281, 0xD29B,
+	0xD29D, 0xD2B7,
+	0xD2B9, 0xD2D3,
+	0xD2D5, 0xD2EF,
+	0xD2F1, 0xD30B,
+	0xD30D, 0xD327,
+	0xD329, 0xD343,
+	0xD345, 0xD35F,
+	0xD361, 0xD37B,
+	0xD37D, 0xD397,
+	0xD399, 0xD3B3,
+	0xD3B5, 0xD3CF,
+	0xD3D1, 0xD3EB,
+	0xD3ED, 0xD407,
+	0xD409, 0xD423,
+	0xD425, 0xD43F,
+	0xD441, 0xD45B,
+	0xD45D, 0xD477,
+	0xD479, 0xD493,
+	0xD495, 0xD4AF,
+	0xD4B1, 0xD4CB,
+	0xD4CD, 0xD4E7,
+	0xD4E9, 0xD503,
+	0xD505, 0xD51F,
+	0xD521, 0xD53B,
+	0xD53D, 0xD557,
+	0xD559, 0xD573,
+	0xD575, 0xD58F,
+	0xD591, 0xD5AB,
+	0xD5AD, 0xD5C7,
+	0xD5C9, 0xD5E3,
+	0xD5E5, 0xD5FF,
+	0xD601, 0xD61B,
+	0xD61D, 0xD637,
+	0xD639, 0xD653,
+	0xD655, 0xD66F,
+	0xD671, 0xD68B,
+	0xD68D, 0xD6A7,
+	0xD6A9, 0xD6C3,
+	0xD6C5, 0xD6DF,
+	0xD6E1, 0xD6FB,
+	0xD6FD, 0xD717,
+	0xD719, 0xD733,
+	0xD735, 0xD74F,
+	0xD751, 0xD76B,
+	0xD76D, 0xD787,
+	0xD789, 0xD7A3,
+}
+
+@(rodata)
+indic_conjunct_break_consonant_ranges := [?]i32 {
+	0x0915, 0x0939,
+	0x0958, 0x095F,
+	0x0978, 0x097F,
+	0x0995, 0x09A8,
+	0x09AA, 0x09B0,
+	0x09B2, 0x09B2,
+	0x09B6, 0x09B9,
+	0x09DC, 0x09DD,
+	0x09DF, 0x09DF,
+	0x09F0, 0x09F1,
+	0x0A95, 0x0AA8,
+	0x0AAA, 0x0AB0,
+	0x0AB2, 0x0AB3,
+	0x0AB5, 0x0AB9,
+	0x0AF9, 0x0AF9,
+	0x0B15, 0x0B28,
+	0x0B2A, 0x0B30,
+	0x0B32, 0x0B33,
+	0x0B35, 0x0B39,
+	0x0B5C, 0x0B5D,
+	0x0B5F, 0x0B5F,
+	0x0B71, 0x0B71,
+	0x0C15, 0x0C28,
+	0x0C2A, 0x0C39,
+	0x0C58, 0x0C5A,
+	0x0D15, 0x0D3A,
+}
+
+@(rodata)
+indic_conjunct_break_extend_ranges := [?]i32 {
+	0x0300, 0x034E,
+	0x0350, 0x036F,
+	0x0483, 0x0487,
+	0x0591, 0x05BD,
+	0x05BF, 0x05BF,
+	0x05C1, 0x05C2,
+	0x05C4, 0x05C5,
+	0x05C7, 0x05C7,
+	0x0610, 0x061A,
+	0x064B, 0x065F,
+	0x0670, 0x0670,
+	0x06D6, 0x06DC,
+	0x06DF, 0x06E4,
+	0x06E7, 0x06E8,
+	0x06EA, 0x06ED,
+	0x0711, 0x0711,
+	0x0730, 0x074A,
+	0x07EB, 0x07F3,
+	0x07FD, 0x07FD,
+	0x0816, 0x0819,
+	0x081B, 0x0823,
+	0x0825, 0x0827,
+	0x0829, 0x082D,
+	0x0859, 0x085B,
+	0x0898, 0x089F,
+	0x08CA, 0x08E1,
+	0x08E3, 0x08FF,
+	0x093C, 0x093C,
+	0x0951, 0x0954,
+	0x09BC, 0x09BC,
+	0x09FE, 0x09FE,
+	0x0A3C, 0x0A3C,
+	0x0ABC, 0x0ABC,
+	0x0B3C, 0x0B3C,
+	0x0C3C, 0x0C3C,
+	0x0C55, 0x0C56,
+	0x0CBC, 0x0CBC,
+	0x0D3B, 0x0D3C,
+	0x0E38, 0x0E3A,
+	0x0E48, 0x0E4B,
+	0x0EB8, 0x0EBA,
+	0x0EC8, 0x0ECB,
+	0x0F18, 0x0F19,
+	0x0F35, 0x0F35,
+	0x0F37, 0x0F37,
+	0x0F39, 0x0F39,
+	0x0F71, 0x0F72,
+	0x0F74, 0x0F74,
+	0x0F7A, 0x0F7D,
+	0x0F80, 0x0F80,
+	0x0F82, 0x0F84,
+	0x0F86, 0x0F87,
+	0x0FC6, 0x0FC6,
+	0x1037, 0x1037,
+	0x1039, 0x103A,
+	0x108D, 0x108D,
+	0x135D, 0x135F,
+	0x1714, 0x1714,
+	0x17D2, 0x17D2,
+	0x17DD, 0x17DD,
+	0x18A9, 0x18A9,
+	0x1939, 0x193B,
+	0x1A17, 0x1A18,
+	0x1A60, 0x1A60,
+	0x1A75, 0x1A7C,
+	0x1A7F, 0x1A7F,
+	0x1AB0, 0x1ABD,
+	0x1ABF, 0x1ACE,
+	0x1B34, 0x1B34,
+	0x1B6B, 0x1B73,
+	0x1BAB, 0x1BAB,
+	0x1BE6, 0x1BE6,
+	0x1C37, 0x1C37,
+	0x1CD0, 0x1CD2,
+	0x1CD4, 0x1CE0,
+	0x1CE2, 0x1CE8,
+	0x1CED, 0x1CED,
+	0x1CF4, 0x1CF4,
+	0x1CF8, 0x1CF9,
+	0x1DC0, 0x1DFF,
+	0x200D, 0x200D,
+	0x20D0, 0x20DC,
+	0x20E1, 0x20E1,
+	0x20E5, 0x20F0,
+	0x2CEF, 0x2CF1,
+	0x2D7F, 0x2D7F,
+	0x2DE0, 0x2DFF,
+	0x302A, 0x302D,
+	0x302E, 0x302F,
+	0x3099, 0x309A,
+	0xA66F, 0xA66F,
+	0xA674, 0xA67D,
+	0xA69E, 0xA69F,
+	0xA6F0, 0xA6F1,
+	0xA82C, 0xA82C,
+	0xA8E0, 0xA8F1,
+	0xA92B, 0xA92D,
+	0xA9B3, 0xA9B3,
+	0xAAB0, 0xAAB0,
+	0xAAB2, 0xAAB4,
+	0xAAB7, 0xAAB8,
+	0xAABE, 0xAABF,
+	0xAAC1, 0xAAC1,
+	0xAAF6, 0xAAF6,
+	0xABED, 0xABED,
+	0xFB1E, 0xFB1E,
+	0xFE20, 0xFE2F,
+	0x101FD, 0x101FD,
+	0x102E0, 0x102E0,
+	0x10376, 0x1037A,
+	0x10A0D, 0x10A0D,
+	0x10A0F, 0x10A0F,
+	0x10A38, 0x10A3A,
+	0x10A3F, 0x10A3F,
+	0x10AE5, 0x10AE6,
+	0x10D24, 0x10D27,
+	0x10EAB, 0x10EAC,
+	0x10EFD, 0x10EFF,
+	0x10F46, 0x10F50,
+	0x10F82, 0x10F85,
+	0x11070, 0x11070,
+	0x1107F, 0x1107F,
+	0x110BA, 0x110BA,
+	0x11100, 0x11102,
+	0x11133, 0x11134,
+	0x11173, 0x11173,
+	0x111CA, 0x111CA,
+	0x11236, 0x11236,
+	0x112E9, 0x112EA,
+	0x1133B, 0x1133C,
+	0x11366, 0x1136C,
+	0x11370, 0x11374,
+	0x11446, 0x11446,
+	0x1145E, 0x1145E,
+	0x114C3, 0x114C3,
+	0x115C0, 0x115C0,
+	0x116B7, 0x116B7,
+	0x1172B, 0x1172B,
+	0x1183A, 0x1183A,
+	0x1193E, 0x1193E,
+	0x11943, 0x11943,
+	0x11A34, 0x11A34,
+	0x11A47, 0x11A47,
+	0x11A99, 0x11A99,
+	0x11D42, 0x11D42,
+	0x11D44, 0x11D45,
+	0x11D97, 0x11D97,
+	0x11F42, 0x11F42,
+	0x16AF0, 0x16AF4,
+	0x16B30, 0x16B36,
+	0x1BC9E, 0x1BC9E,
+	0x1D165, 0x1D165,
+	0x1D167, 0x1D169,
+	0x1D16E, 0x1D172,
+	0x1D17B, 0x1D182,
+	0x1D185, 0x1D18B,
+	0x1D1AA, 0x1D1AD,
+	0x1D242, 0x1D244,
+	0x1E000, 0x1E006,
+	0x1E008, 0x1E018,
+	0x1E01B, 0x1E021,
+	0x1E023, 0x1E024,
+	0x1E026, 0x1E02A,
+	0x1E08F, 0x1E08F,
+	0x1E130, 0x1E136,
+	0x1E2AE, 0x1E2AE,
+	0x1E2EC, 0x1E2EF,
+	0x1E4EC, 0x1E4EF,
+	0x1E8D0, 0x1E8D6,
+	0x1E944, 0x1E94A,
+}
+
+//
+// End of Unicode 15.1.0 block.
+//

+ 387 - 0
core/unicode/utf8/grapheme.odin

@@ -0,0 +1,387 @@
+package utf8
+
+import "core:unicode"
+
+ZERO_WIDTH_JOINER                 :: unicode.ZERO_WIDTH_JOINER
+is_control                        :: unicode.is_control
+is_hangul_syllable_leading        :: unicode.is_hangul_syllable_leading
+is_hangul_syllable_vowel          :: unicode.is_hangul_syllable_vowel
+is_hangul_syllable_trailing       :: unicode.is_hangul_syllable_trailing
+is_hangul_syllable_lv             :: unicode.is_hangul_syllable_lv
+is_hangul_syllable_lvt            :: unicode.is_hangul_syllable_lvt
+is_indic_conjunct_break_extend    :: unicode.is_indic_conjunct_break_extend
+is_indic_conjunct_break_linker    :: unicode.is_indic_conjunct_break_linker
+is_indic_conjunct_break_consonant :: unicode.is_indic_conjunct_break_consonant
+is_gcb_extend_class               :: unicode.is_gcb_extend_class
+is_spacing_mark                   :: unicode.is_spacing_mark
+is_gcb_prepend_class              :: unicode.is_gcb_prepend_class
+is_emoji_extended_pictographic    :: unicode.is_emoji_extended_pictographic
+is_regional_indicator             :: unicode.is_regional_indicator
+
+
+Grapheme :: struct {
+	byte_index: int,
+	rune_index: int,
+}
+
+/*
+Count the individual graphemes in a UTF-8 string.
+
+Inputs:
+- str: The input string.
+
+Returns:
+- graphemes: The number of graphemes in the string.
+- runes: The number of runes in the string.
+*/
+@(require_results)
+grapheme_count :: proc(str: string) -> (graphemes, runes: int) {
+	_, graphemes, runes = decode_grapheme_clusters(str, false)
+	return
+}
+
+/*
+Decode the individual graphemes in a UTF-8 string.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- str: The input string.
+- track_graphemes: Whether or not to allocate and return `graphemes` with extra data about each grapheme.
+- allocator: (default: context.allocator)
+
+Returns:
+- graphemes: Extra data about each grapheme.
+- grapheme_count: The number of graphemes in the string.
+- rune_count: The number of runes in the string.
+*/
+@(require_results)
+decode_grapheme_clusters :: proc(
+	str: string,
+	track_graphemes := true,
+	allocator       := context.allocator,
+) -> (
+	graphemes:      [dynamic]Grapheme,
+	grapheme_count: int,
+	rune_count:     int,
+) {
+	// The following procedure implements text segmentation by breaking on
+	// Grapheme Cluster Boundaries[1], using the values[2] and rules[3] from
+	// the Unicode® Standard Annex #29, entitled:
+	//
+	// UNICODE TEXT SEGMENTATION
+	//
+	// Version:  Unicode 15.1.0
+	// Date:     2023-08-16
+	// Revision: 43
+	//
+	// This procedure is conformant[4] to UAX29-C1-1, otherwise known as the
+	// extended, non-legacy ruleset.
+	//
+	// Please see the references below for more information.
+	//
+	//
+	// NOTE(Feoramund): This procedure has not been highly optimized.
+	// A couple opportunities were taken to bypass repeated checking when a
+	// rune is outside of certain codepoint ranges, but little else has been
+	// done. Standard switches, conditionals, and binary search are used to
+	// see if a rune fits into a certain category.
+	//
+	// I did find that only one prior rune of state was necessary to build an
+	// algorithm that successfully passes all 4,835 test cases provided with
+	// this implementation from the Unicode organization's website.
+	//
+	// My initial implementation tracked explicit breaks and counted them once
+	// the string iteration had terminated. I've found this current
+	// implementation to be far simpler and need no allocations (unless the
+	// caller wants position data).
+	//
+	// Most rules work backwards instead of forwards which has helped keep this
+	// simple, despite its length and verbosity.
+	//
+	//
+	// The implementation has been left verbose and in the order described by
+	// the specification, to enable better readability and future upkeep.
+	//
+	// Some possible optimizations might include:
+	//
+	// - saving the type of `last_rune` instead of the exact rune.
+	// - reordering rules.
+	// - combining tables.
+	//
+	//
+	// [1]: https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
+	// [2]: https://www.unicode.org/reports/tr29/#Default_Grapheme_Cluster_Table
+	// [3]: https://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundary_Rules
+	// [4]: https://www.unicode.org/reports/tr29/#Conformance
+
+	Grapheme_Cluster_Sequence :: enum {
+		None,
+		Indic,
+		Emoji,
+		Regional,
+	}
+
+	context.allocator = allocator
+
+	last_rune: rune
+	last_rune_breaks_forward: bool
+
+	last_grapheme_count: int
+
+	bypass_next_rune: bool
+
+	regional_indicator_counter: int
+
+	current_sequence: Grapheme_Cluster_Sequence
+	continue_sequence: bool
+
+	for this_rune, byte_index in str {
+		defer {
+			// "Break at the start and end of text, unless the text is empty."
+			//
+			// GB1: sot  ÷  Any
+			// GB2: Any  ÷  eot
+			if rune_count == 0 && grapheme_count == 0 {
+				grapheme_count += 1
+			}
+			if track_graphemes && grapheme_count > last_grapheme_count {
+				append(&graphemes, Grapheme{ byte_index, rune_count })
+			}
+			last_grapheme_count = grapheme_count
+
+			last_rune = this_rune
+			rune_count += 1
+
+			if !continue_sequence {
+				current_sequence = .None
+				regional_indicator_counter = 0
+			}
+			continue_sequence = false
+		}
+
+		// "Do not break between a CR and LF. Otherwise, break before and after controls."
+		//
+		// GB3:                 CR   ×   LF
+		// GB4: (Control | CR | LF)  ÷
+		// GB5:                      ÷  (Control | CR | LF)
+		if this_rune == '\n' && last_rune == '\r' {
+			last_rune_breaks_forward = false
+			bypass_next_rune = false
+			continue
+		}
+
+		if is_control(this_rune) {
+			grapheme_count += 1
+			last_rune_breaks_forward = true
+			bypass_next_rune = true
+			continue
+		}
+
+		// (This check is for rules that work forwards, instead of backwards.)
+		if bypass_next_rune {
+			if last_rune_breaks_forward {
+				grapheme_count += 1
+				last_rune_breaks_forward = false
+			}
+
+			bypass_next_rune = false
+			continue
+		}
+
+		// (Optimization 1: Prevent low runes from proceeding further.)
+		//
+		//  * 0xA9 and 0xAE are in the Extended_Pictographic range,
+		//    which is checked later in GB11.
+		if this_rune != 0xA9 && this_rune != 0xAE && this_rune <= 0x2FF {
+			grapheme_count += 1
+			continue
+		}
+
+		// (Optimization 2: Check if the rune is in the Hangul space before getting specific.)
+		if 0x1100 <= this_rune && this_rune <= 0xD7FB {
+			// "Do not break Hangul syllable sequences."
+			//
+			// GB6:        L   ×  (L | V | LV | LVT)
+			// GB7:  (LV | V)  ×  (V | T)
+			// GB8: (LVT | T)  ×   T
+			if is_hangul_syllable_leading(this_rune) ||
+			   is_hangul_syllable_lv(this_rune)      ||
+			   is_hangul_syllable_lvt(this_rune)
+			{
+				if !is_hangul_syllable_leading(last_rune) {
+					grapheme_count += 1
+				}
+				continue
+			}
+
+			if is_hangul_syllable_vowel(this_rune) {
+				if is_hangul_syllable_leading(last_rune) ||
+				   is_hangul_syllable_vowel(last_rune)   ||
+				   is_hangul_syllable_lv(last_rune)
+				{
+					continue
+				}
+				grapheme_count += 1
+				continue
+			}
+
+			if is_hangul_syllable_trailing(this_rune) {
+				if is_hangul_syllable_trailing(last_rune) ||
+				   is_hangul_syllable_lvt(last_rune)      ||
+				   is_hangul_syllable_lv(last_rune)       ||
+				   is_hangul_syllable_vowel(last_rune)
+				{
+					continue
+				}
+				grapheme_count += 1
+				continue
+			}
+		}
+
+		// "Do not break before extending characters or ZWJ."
+		//
+		// GB9:         × (Extend | ZWJ)
+		if this_rune == ZERO_WIDTH_JOINER {
+			continue_sequence = true
+			continue
+		}
+
+		if is_gcb_extend_class(this_rune) {
+			// (Support for GB9c.)
+			if current_sequence == .Indic {
+				if is_indic_conjunct_break_extend(this_rune)    && (
+				   is_indic_conjunct_break_linker(last_rune)    ||
+				   is_indic_conjunct_break_consonant(last_rune)    )
+				{
+					continue_sequence = true
+					continue
+				}
+
+				if is_indic_conjunct_break_linker(this_rune)    && (
+				   is_indic_conjunct_break_linker(last_rune)    ||
+				   is_indic_conjunct_break_extend(last_rune)    ||
+				   is_indic_conjunct_break_consonant(last_rune)    )
+				{
+					continue_sequence = true
+					continue
+				}
+
+				continue
+			}
+
+			// (Support for GB11.)
+			if current_sequence == .Emoji                && (
+			   is_gcb_extend_class(last_rune)            ||
+			   is_emoji_extended_pictographic(last_rune)    )
+			{
+				continue_sequence = true
+			}
+
+			continue
+		}
+
+		// _The GB9a and GB9b rules only apply to extended grapheme clusters:_
+		// "Do not break before SpacingMarks, or after Prepend characters."
+		//
+		// GB9a:          ×  SpacingMark
+		// GB9b: Prepend  ×
+		if is_spacing_mark(this_rune) {
+			continue
+		}
+
+		if is_gcb_prepend_class(this_rune) {
+			grapheme_count += 1
+			bypass_next_rune = true
+			continue
+		}
+
+		// _The GB9c rule only applies to extended grapheme clusters:_
+		// "Do not break within certain combinations with Indic_Conjunct_Break (InCB)=Linker."
+		//
+		// GB9c: \p{InCB=Consonant} [ \p{InCB=Extend} \p{InCB=Linker} ]* \p{InCB=Linker} [ \p{InCB=Extend} \p{InCB=Linker} ]*  ×  \p{InCB=Consonant}
+		if is_indic_conjunct_break_consonant(this_rune) {
+			if current_sequence == .Indic {
+				if last_rune == ZERO_WIDTH_JOINER            ||
+				   is_indic_conjunct_break_linker(last_rune)
+				{
+					continue_sequence = true
+				} else {
+					grapheme_count += 1
+				}
+			} else {
+				grapheme_count += 1
+				current_sequence = .Indic
+				continue_sequence = true
+			}
+			continue
+		}
+
+		if is_indic_conjunct_break_extend(this_rune) {
+			if current_sequence == .Indic {
+				if is_indic_conjunct_break_consonant(last_rune) ||
+				   is_indic_conjunct_break_linker(last_rune)
+				{
+					continue_sequence = true
+				} else {
+					grapheme_count += 1
+				}
+			}
+			continue
+		}
+
+		if is_indic_conjunct_break_linker(this_rune) {
+			if current_sequence == .Indic {
+				if is_indic_conjunct_break_extend(last_rune) ||
+				   is_indic_conjunct_break_linker(last_rune)
+				{
+					continue_sequence = true
+				} else {
+					grapheme_count += 1
+				}
+			}
+			continue
+		}
+
+		//
+		// (Curiously, there is no GB10.)
+		//
+
+		// "Do not break within emoji modifier sequences or emoji zwj sequences."
+		//
+		// GB11: \p{Extended_Pictographic} Extend* ZWJ  ×  \p{Extended_Pictographic}
+		if is_emoji_extended_pictographic(this_rune) {
+			if current_sequence != .Emoji || last_rune != ZERO_WIDTH_JOINER {
+				grapheme_count += 1
+			}
+			current_sequence = .Emoji
+			continue_sequence = true
+			continue
+		}
+
+		// "Do not break within emoji flag sequences.
+		//  That is, do not break between regional indicator (RI) symbols
+		//  if there is an odd number of RI characters before the break point."
+		//
+		// GB12:   sot (RI RI)* RI  ×  RI
+		// GB13: [^RI] (RI RI)* RI  ×  RI
+		if is_regional_indicator(this_rune) {
+			if regional_indicator_counter & 1 == 0 {
+				grapheme_count += 1
+			}
+
+			current_sequence = .Regional
+			continue_sequence = true
+			regional_indicator_counter += 1
+
+			continue
+		}
+
+		// "Otherwise, break everywhere."
+		//
+		// GB999: Any ÷ Any
+		grapheme_count += 1
+	}
+
+	return
+}

+ 2 - 0
examples/all/all_main.odin

@@ -121,6 +121,7 @@ import edit             "core:text/edit"
 import thread           "core:thread"
 import time             "core:time"
 import datetime         "core:time/datetime"
+import flags            "core:flags"
 
 import sysinfo          "core:sys/info"
 
@@ -233,6 +234,7 @@ _ :: edit
 _ :: thread
 _ :: time
 _ :: datetime
+_ :: flags
 _ :: sysinfo
 _ :: unicode
 _ :: utf8

+ 1 - 1
src/check_decl.cpp

@@ -1077,7 +1077,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	}
 
 
-	if (e->pkg != nullptr && e->token.string == "main") {
+	if (e->pkg != nullptr && e->token.string == "main" && !build_context.no_entry_point) {
 		if (e->pkg->kind != Package_Runtime) {
 			if (pt->param_count != 0 ||
 			    pt->result_count != 0) {

+ 3 - 1
src/check_expr.cpp

@@ -9819,7 +9819,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
 				if (tav.mode != Addressing_Constant) {
 					continue;
 				}
-				GB_ASSERT(tav.value.kind == ExactValue_Integer);
+				if (tav.value.kind != ExactValue_Integer) {
+					continue;
+				}
 				i64 v = big_int_to_i64(&tav.value.value_integer);
 				i64 lower = bt->BitSet.lower;
 				u64 index = cast(u64)(v-lower);

+ 8 - 0
src/checker.cpp

@@ -4110,6 +4110,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 	bool is_test = false;
 	bool is_init = false;
 	bool is_fini = false;
+	bool is_priv = false;
 
 	for_array(i, vd->attributes) {
 		Ast *attr = vd->attributes[i];
@@ -4154,6 +4155,8 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 				}
 				if (!success) {
 					error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+				} else {
+					is_priv = true;
 				}
 
 
@@ -4175,6 +4178,11 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
 		}
 	}
 
+	if (is_priv && is_test) {
+		error(decl, "Attribute 'private' is not allowed on a test case");
+		return;
+	}
+
 	if (entity_visibility_kind == EntityVisiblity_Public &&
 	    (c->scope->flags&ScopeFlag_File) &&
 	    c->scope->file) {

+ 6 - 2
src/linker.cpp

@@ -265,16 +265,20 @@ gb_internal i32 linker_stage(LinkerData *gen) {
 			if (!build_context.use_lld) { // msvc
 				String res_path = {};
 				defer (gb_free(heap_allocator(), res_path.text));
+
+				// TODO(Jeroen): Add ability to reuse .res file instead of recompiling, if `-resource:file.res` is given.
 				if (build_context.has_resource) {
 					String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
 					res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\""));
 					gb_free(heap_allocator(), temp_res_path.text);
 
-					String rc_path  = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
+					String temp_rc_path  = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
+					String rc_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_rc_path, str_lit("\""));
+					gb_free(heap_allocator(), temp_rc_path.text);
 					defer (gb_free(heap_allocator(), rc_path.text));
 
 					result = system_exec_command_line_app("msvc-link",
-						"\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+						"\"%.*src.exe\" /nologo /fo %.*s %.*s",
 						LIT(windows_sdk_bin_path),
 						LIT(res_path),
 						LIT(rc_path)

+ 2 - 1
src/main.cpp

@@ -2934,7 +2934,8 @@ int main(int arg_count, char const **arg_ptr) {
 	// TODO(jeroen): Remove the `init_filename` param.
 	// Let's put that on `build_context.build_paths[0]` instead.
 	if (parse_packages(parser, init_filename) != ParseFile_None) {
-		return 1;
+		GB_ASSERT_MSG(any_errors(), "parse_packages failed but no error was reported.");
+		// We depend on the next conditional block to return 1, after printing errors.
 	}
 
 	if (any_errors()) {

+ 2 - 5
tests/core/container/test_core_avl.odin

@@ -20,15 +20,12 @@ test_avl :: proc(t: ^testing.T) {
 	iter := avl.iterator(&tree, avl.Direction.Forward)
 	testing.expect(t, avl.iterator_get(&iter) == nil, "empty/iterator: first node should be nil")
 
-	r: rand.Rand
-	rand.init(&r, t.seed)
-
 	// Test insertion.
 	NR_INSERTS :: 32 + 1 // Ensure at least 1 collision.
 	inserted_map := make(map[int]^avl.Node(int))
 	defer delete(inserted_map)
 	for i := 0; i < NR_INSERTS; i += 1 {
-		v := int(rand.uint32(&r) & 0x1f)
+		v := int(rand.uint32() & 0x1f)
 		existing_node, in_map := inserted_map[v]
 
 		n, ok, _ := avl.find_or_insert(&tree, v)
@@ -78,7 +75,7 @@ test_avl :: proc(t: ^testing.T) {
 	testing.expect(t, visited == nrEntries, "iterator/backward: visited")
 
 	// Test removal.
-	rand.shuffle(inserted_values[:], &r)
+	rand.shuffle(inserted_values[:])
 	for v, i in inserted_values {
 		node := avl.find(&tree, v)
 		testing.expect(t, node != nil, "remove: find (pre)")

+ 3 - 6
tests/core/container/test_core_rbtree.odin

@@ -14,9 +14,6 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
 	defer mem.tracking_allocator_destroy(&track)
 	context.allocator = mem.tracking_allocator(&track)
 
-	r: rand.Rand
-	rand.init(&r, t.seed)
-
 	log.infof("Testing Red-Black Tree($Key=%v,$Value=%v) using random seed %v.", type_info_of(Key), type_info_of(Value), t.seed)
 	tree: rb.Tree(Key, Value)
 	rb.init(&tree)
@@ -35,9 +32,9 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
 	max_key := min(Key)
 
 	for i := 0; i < NR_INSERTS; i += 1 {
-		k := Key(rand.uint32(&r)) & 0x1f
+		k := Key(rand.uint32()) & 0x1f
 		min_key = min(min_key, k); max_key = max(max_key, k)
-		v := Value(rand.uint32(&r))
+		v := Value(rand.uint32())
 
 		existing_node, in_map := inserted_map[k]
 		n, inserted, _ := rb.find_or_insert(&tree, k, v)
@@ -92,7 +89,7 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) {
 	testing.expect(t, visited == entry_count, "iterator/backward: visited")
 
 	// Test removal (and on_remove callback)
-	rand.shuffle(inserted_keys[:], &r)
+	rand.shuffle(inserted_keys[:])
 	callback_count := entry_count
 	tree.user_data = &callback_count
 	tree.on_remove = proc(key: Key, value: Value, user_data: rawptr) {

+ 1393 - 0
tests/core/flags/test_core_flags.odin

@@ -0,0 +1,1393 @@
+package test_core_flags
+
+import "base:runtime"
+import "core:bytes"
+import "core:flags"
+import "core:fmt"
+@require import "core:log"
+import "core:math"
+@require import "core:net"
+import "core:os"
+import "core:strings"
+import "core:testing"
+import "core:time/datetime"
+
+@(test)
+test_no_args :: proc(t: ^testing.T) {
+	S :: struct {
+		a: string,
+	}
+	s: S
+	args: []string
+	result := flags.parse(&s, args)
+	testing.expect_value(t, result, nil)
+}
+
+@(test)
+test_two_flags :: proc(t: ^testing.T) {
+	S :: struct {
+		i: string,
+		o: string,
+	}
+	s: S
+	args := [?]string { "-i:hellope", "-o:world" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.i, "hellope")
+	testing.expect_value(t, s.o, "world")
+}
+
+@(test)
+test_extra_arg :: proc(t: ^testing.T) {
+	S :: struct {
+		a: string,
+	}
+	s: S
+	args := [?]string { "-a:hellope", "world" }
+	result := flags.parse(&s, args[:])
+	err, ok := result.(flags.Parse_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Extra_Positional)
+	}
+}
+
+@(test)
+test_assignment_oddities :: proc(t: ^testing.T) {
+	S :: struct {
+		s: string,
+	}
+	s: S
+
+	{
+		args := [?]string { "-s:=" }
+		result := flags.parse(&s, args[:])
+		testing.expect_value(t, result, nil)
+		testing.expect_value(t, s.s, "=")
+	}
+
+	{
+		args := [?]string { "-s=:" }
+		result := flags.parse(&s, args[:])
+		testing.expect_value(t, result, nil)
+		testing.expect_value(t, s.s, ":")
+	}
+
+	{
+		args := [?]string { "-" }
+		result := flags.parse(&s, args[:])
+		err, ok := result.(flags.Parse_Error)
+		testing.expectf(t, ok, "unexpected result: %v", result)
+		if ok {
+			testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Flag)
+		}
+	}
+}
+
+@(test)
+test_string_into_int :: proc(t: ^testing.T) {
+	S :: struct {
+		n: int,
+	}
+	s: S
+	args := [?]string { "-n:hellope" }
+	result := flags.parse(&s, args[:])
+	err, ok := result.(flags.Parse_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
+	}
+}
+
+@(test)
+test_string_into_bool :: proc(t: ^testing.T) {
+	S :: struct {
+		b: bool,
+	}
+	s: S
+	args := [?]string { "-b:hellope" }
+	result := flags.parse(&s, args[:])
+	err, ok := result.(flags.Parse_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
+	}
+}
+
+@(test)
+test_all_bools :: proc(t: ^testing.T) {
+	S :: struct {
+		a: bool,
+		b: b8,
+		c: b16,
+		d: b32,
+		e: b64,
+	}
+	s: S
+	s.a = true
+	s.c = true
+	args := [?]string { "-a:false", "-b:true", "-c:0", "-d", "-e:1" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, false)
+	testing.expect_value(t, s.b, true)
+	testing.expect_value(t, s.c, false)
+	testing.expect_value(t, s.d, true)
+	testing.expect_value(t, s.e, true)
+}
+
+@(test)
+test_all_ints :: proc(t: ^testing.T) {
+	S :: struct {
+		a: u8,
+		b: i8,
+		c: u16,
+		d: i16,
+		e: u32,
+		f: i32,
+		g: u64,
+		i: i64,
+		j: u128,
+		k: i128,
+	}
+
+	s: S
+	args := [?]string {
+		fmt.tprintf("-a:%i", max(u8)),
+		fmt.tprintf("-b:%i", min(i8)),
+		fmt.tprintf("-c:%i", max(u16)),
+		fmt.tprintf("-d:%i", min(i16)),
+		fmt.tprintf("-e:%i", max(u32)),
+		fmt.tprintf("-f:%i", min(i32)),
+		fmt.tprintf("-g:%i", max(u64)),
+		fmt.tprintf("-i:%i", min(i64)),
+		fmt.tprintf("-j:%i", max(u128)),
+		fmt.tprintf("-k:%i", min(i128)),
+	}
+
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, max(u8))
+	testing.expect_value(t, s.b, min(i8))
+	testing.expect_value(t, s.c, max(u16))
+	testing.expect_value(t, s.d, min(i16))
+	testing.expect_value(t, s.e, max(u32))
+	testing.expect_value(t, s.f, min(i32))
+	testing.expect_value(t, s.g, max(u64))
+	testing.expect_value(t, s.i, min(i64))
+	testing.expect_value(t, s.j, max(u128))
+	testing.expect_value(t, s.k, min(i128))
+}
+
+@(test)
+test_all_floats :: proc(t: ^testing.T) {
+	S :: struct {
+		a: f16,
+		b: f32,
+		c: f64,
+		d: f64,
+		e: f64,
+	}
+	s: S
+	args := [?]string { "-a:100", "-b:3.14", "-c:-123.456", "-d:nan", "-e:inf" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, 100)
+	testing.expect_value(t, s.b, 3.14)
+	testing.expect_value(t, s.c, -123.456)
+	testing.expectf(t, math.is_nan(s.d), "expected NaN, got %v", s.d)
+	testing.expectf(t, math.is_inf(s.e, +1), "expected +Inf, got %v", s.e)
+}
+
+@(test)
+test_all_enums :: proc(t: ^testing.T) {
+	E :: enum { A, B }
+	S :: struct {
+		nameless: enum { C, D },
+		named: E,
+	}
+	s: S
+	args := [?]string { "-nameless:D", "-named:B" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, cast(int)s.nameless, 1)
+	testing.expect_value(t, s.named, E.B)
+}
+
+@(test)
+test_all_complex :: proc(t: ^testing.T) {
+	S :: struct {
+		a: complex32,
+		b: complex64,
+		c: complex128,
+	}
+	s: S
+	args := [?]string { "-a:1+0i", "-b:3+7i", "-c:NaNNaNi" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, real(s.a), 1)
+	testing.expect_value(t, imag(s.a), 0)
+	testing.expect_value(t, real(s.b), 3)
+	testing.expect_value(t, imag(s.b), 7)
+	testing.expectf(t, math.is_nan(real(s.c)), "expected NaN, got %v", real(s.c))
+	testing.expectf(t, math.is_nan(imag(s.c)), "expected NaN, got %v", imag(s.c))
+}
+
+@(test)
+test_all_quaternion :: proc(t: ^testing.T) {
+	S :: struct {
+		a: quaternion64,
+		b: quaternion128,
+		c: quaternion256,
+	}
+	s: S
+	args := [?]string { "-a:1+0i+1j+0k", "-b:3+7i+5j-3k", "-c:NaNNaNi+Infj-Infk" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+
+	raw_a := (cast(^runtime.Raw_Quaternion64)&s.a)
+	raw_b := (cast(^runtime.Raw_Quaternion128)&s.b)
+	raw_c := (cast(^runtime.Raw_Quaternion256)&s.c)
+
+	testing.expect_value(t, raw_a.real, 1)
+	testing.expect_value(t, raw_a.imag, 0)
+	testing.expect_value(t, raw_a.jmag, 1)
+	testing.expect_value(t, raw_a.kmag, 0)
+
+	testing.expect_value(t, raw_b.real, 3)
+	testing.expect_value(t, raw_b.imag, 7)
+	testing.expect_value(t, raw_b.jmag, 5)
+	testing.expect_value(t, raw_b.kmag, -3)
+
+	testing.expectf(t, math.is_nan(raw_c.real), "expected NaN, got %v", raw_c.real)
+	testing.expectf(t, math.is_nan(raw_c.imag), "expected NaN, got %v", raw_c.imag)
+	testing.expectf(t, math.is_inf(raw_c.jmag, +1), "expected +Inf, got %v", raw_c.jmag)
+	testing.expectf(t, math.is_inf(raw_c.kmag, -1), "expected -Inf, got %v", raw_c.kmag)
+}
+
+@(test)
+test_all_bit_sets :: proc(t: ^testing.T) {
+	E :: enum {
+		Option_A,
+		Option_B,
+	}
+	S :: struct {
+		a: bit_set[0..<8],
+		b: bit_set[0..<16; u16],
+		c: bit_set[16..<18; rune],
+		d: bit_set[0..<1; i8],
+		e: bit_set[0..<128],
+		f: bit_set[-32..<32],
+		g: bit_set[E],
+		i: bit_set[E; u8],
+	}
+	s: S
+	{
+		args := [?]string {
+			"-a:10101",
+			"-b:0000_0000_0000_0001",
+			"-c:11",
+			"-d:___1",
+			"-e:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
+			"-f:1",
+			"-g:01",
+			"-i:1",
+		}
+		result := flags.parse(&s, args[:])
+		testing.expect_value(t, result, nil)
+		testing.expect_value(t, s.a, bit_set[0..<8]{0, 2, 4})
+		testing.expect_value(t, s.b, bit_set[0..<16; u16]{15})
+		testing.expect_value(t, s.c, bit_set[16..<18; rune]{16, 17})
+		testing.expect_value(t, s.d, bit_set[0..<1; i8]{0})
+		testing.expect_value(t, s.e, bit_set[0..<128]{127})
+		testing.expect_value(t, s.f, bit_set[-32..<32]{-32})
+		testing.expect_value(t, s.g, bit_set[E]{E.Option_B})
+		testing.expect_value(t, s.i, bit_set[E; u8]{E.Option_A})
+	}
+	{
+		args := [?]string { "-d:11" }
+		result := flags.parse(&s, args[:])
+		err, ok := result.(flags.Parse_Error)
+		testing.expectf(t, ok, "unexpected result: %v", result)
+		if ok {
+			testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
+		}
+	}
+}
+
+@(test)
+test_all_strings :: proc(t: ^testing.T) {
+	S :: struct {
+		a, b, c: string,
+		d: cstring,
+	}
+	s: S
+	args := [?]string { "-a:hi", "-b:hellope", "-c:spaced out", "-d:cstr", "-d:cstr-overwrite" }
+	result := flags.parse(&s, args[:])
+	defer delete(s.d)
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, "hi")
+	testing.expect_value(t, s.b, "hellope")
+	testing.expect_value(t, s.c, "spaced out")
+	testing.expect_value(t, s.d, "cstr-overwrite")
+}
+
+@(test)
+test_runes :: proc(t: ^testing.T) {
+	S :: struct {
+		a, b, c: rune,
+	}
+	s: S
+	args := [?]string { "-a:a", "-b:ツ", "-c:\U0010FFFF" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, 'a')
+	testing.expect_value(t, s.b, 'ツ')
+	testing.expect_value(t, s.c, '\U0010FFFF')
+}
+
+@(test)
+test_no_value :: proc(t: ^testing.T) {
+	S :: struct {
+		a: rune,
+	}
+	s: S
+
+	{
+		args := [?]string { "-a:" }
+		result := flags.parse(&s, args[:])
+		err, ok := result.(flags.Parse_Error)
+		testing.expectf(t, ok, "unexpected result: %v", result)
+		if ok {
+			testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value)
+		}
+	}
+
+	{
+		args := [?]string { "-a=" }
+		result := flags.parse(&s, args[:])
+		err, ok := result.(flags.Parse_Error)
+		testing.expectf(t, ok, "unexpected result: %v", result)
+		if ok {
+			testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value)
+		}
+	}
+}
+
+@(test)
+test_overflow :: proc(t: ^testing.T) {
+	S :: struct {
+		a: u8,
+	}
+	s: S
+	args := [?]string { "-a:256" }
+	result := flags.parse(&s, args[:])
+	err, ok := result.(flags.Parse_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
+	}
+}
+
+@(test)
+test_underflow :: proc(t: ^testing.T) {
+	S :: struct {
+		a: i8,
+	}
+	s: S
+	args := [?]string { "-a:-129" }
+	result := flags.parse(&s, args[:])
+	err, ok := result.(flags.Parse_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
+	}
+}
+
+@(test)
+test_arrays :: proc(t: ^testing.T) {
+	S :: struct {
+		a: [dynamic]string,
+		b: [dynamic]int,
+	}
+	s: S
+	args := [?]string { "-a:abc", "-b:1", "-a:foo", "-b:3" }
+	result := flags.parse(&s, args[:])
+	defer {
+		delete(s.a)
+		delete(s.b)
+	}
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.a), 2)
+	testing.expect_value(t, len(s.b), 2)
+
+	if len(s.a) < 2 || len(s.b) < 2 {
+		return
+	}
+
+	testing.expect_value(t, s.a[0], "abc")
+	testing.expect_value(t, s.a[1], "foo")
+	testing.expect_value(t, s.b[0], 1)
+	testing.expect_value(t, s.b[1], 3)
+}
+
+@(test)
+test_varargs :: proc(t: ^testing.T) {
+	S :: struct {
+		varg: [dynamic]string,
+	}
+	s: S
+	args := [?]string { "abc", "foo", "bar" }
+	result := flags.parse(&s, args[:])
+	defer delete(s.varg)
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.varg), 3)
+
+	if len(s.varg) < 3 {
+		return
+	}
+
+	testing.expect_value(t, s.varg[0], "abc")
+	testing.expect_value(t, s.varg[1], "foo")
+	testing.expect_value(t, s.varg[2], "bar")
+}
+
+@(test)
+test_mixed_varargs :: proc(t: ^testing.T) {
+	S :: struct {
+		input: string `args:"pos=0"`,
+		varg: [dynamic]string,
+	}
+	s: S
+	args := [?]string { "abc", "foo", "bar" }
+	result := flags.parse(&s, args[:])
+	defer delete(s.varg)
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.varg), 2)
+
+	if len(s.varg) < 2 {
+		return
+	}
+
+	testing.expect_value(t, s.input, "abc")
+	testing.expect_value(t, s.varg[0], "foo")
+	testing.expect_value(t, s.varg[1], "bar")
+}
+
+@(test)
+test_maps :: proc(t: ^testing.T) {
+	S :: struct {
+		a: map[string]string,
+		b: map[string]int,
+	}
+	s: S
+	args := [?]string { "-a:abc=foo", "-b:bar=42" }
+	result := flags.parse(&s, args[:])
+	defer {
+		delete(s.a)
+		delete(s.b)
+	}
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.a), 1)
+	testing.expect_value(t, len(s.b), 1)
+
+	if len(s.a) < 1 || len(s.b) < 1 {
+		return
+	}
+
+	abc, has_abc := s.a["abc"]
+	bar, has_bar := s.b["bar"]
+
+	testing.expect(t, has_abc, "expected map to have `abc` key set")
+	testing.expect(t, has_bar, "expected map to have `bar` key set")
+	testing.expect_value(t, abc, "foo")
+	testing.expect_value(t, bar, 42)
+}
+
+@(test)
+test_invalid_map_syntax :: proc(t: ^testing.T) {
+	S :: struct {
+		a: map[string]string,
+	}
+	s: S
+	args := [?]string { "-a:foo:42" }
+	result := flags.parse(&s, args[:])
+	err, ok := result.(flags.Parse_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value)
+	}
+}
+
+@(test)
+test_underline_name_to_dash :: proc(t: ^testing.T) {
+	S :: struct {
+		a_b: int,
+	}
+	s: S
+	args := [?]string { "-a-b:3" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a_b, 3)
+}
+
+@(test)
+test_tags_pos :: proc(t: ^testing.T) {
+	S :: struct {
+		b: int `args:"pos=1"`,
+		a: int `args:"pos=0"`,
+	}
+	s: S
+	args := [?]string { "42", "99" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, 42)
+	testing.expect_value(t, s.b, 99)
+}
+
+@(test)
+test_tags_name :: proc(t: ^testing.T) {
+	S :: struct {
+		a: int `args:"name=alice"`,
+		b: int `args:"name=bill"`,
+	}
+	s: S
+	args := [?]string { "-alice:1", "-bill:2" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, 1)
+	testing.expect_value(t, s.b, 2)
+}
+
+@(test)
+test_tags_required :: proc(t: ^testing.T) {
+	S :: struct {
+		a: int,
+		b: int `args:"required"`,
+	}
+	s: S
+	args := [?]string { "-a:1" }
+	result := flags.parse(&s, args[:])
+	_, ok := result.(flags.Validation_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+}
+
+@(test)
+test_tags_required_pos :: proc(t: ^testing.T) {
+	S :: struct {
+		a: int `args:"pos=0,required"`,
+		b: int `args:"pos=1"`,
+	}
+	s: S
+	args := [?]string { "-b:5" }
+	result := flags.parse(&s, args[:])
+	_, ok := result.(flags.Validation_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+}
+
+@(test)
+test_tags_required_limit_min :: proc(t: ^testing.T) {
+	S :: struct {
+		n: [dynamic]int `args:"required=3"`,
+	}
+
+	{
+		s: S
+		args := [?]string { "-n:1" }
+		result := flags.parse(&s, args[:])
+		defer delete(s.n)
+		_, ok := result.(flags.Validation_Error)
+		testing.expectf(t, ok, "unexpected result: %v", result)
+	}
+
+	{
+		s: S
+		args := [?]string { "-n:3", "-n:5", "-n:7" }
+		result := flags.parse(&s, args[:])
+		defer delete(s.n)
+		testing.expect_value(t, result, nil)
+		testing.expect_value(t, len(s.n), 3)
+
+		if len(s.n) == 3 {
+			testing.expect_value(t, s.n[0], 3)
+			testing.expect_value(t, s.n[1], 5)
+			testing.expect_value(t, s.n[2], 7)
+		}
+	}
+}
+
+@(test)
+test_tags_required_limit_min_max :: proc(t: ^testing.T) {
+	S :: struct {
+		n: [dynamic]int `args:"required=2<4"`,
+	}
+
+	{
+		s: S
+		args := [?]string { "-n:1" }
+		result := flags.parse(&s, args[:])
+		defer delete(s.n)
+		_, ok := result.(flags.Validation_Error)
+		testing.expectf(t, ok, "unexpected result: %v", result)
+	}
+
+	{
+		s: S
+		args := [?]string { "-n:1", "-n:2", "-n:3", "-n:4" }
+		result := flags.parse(&s, args[:])
+		defer delete(s.n)
+		_, ok := result.(flags.Validation_Error)
+		testing.expectf(t, ok, "unexpected result: %v", result)
+	}
+
+	{
+		s: S
+		args := [?]string { "-n:3", "-n:5", "-n:7" }
+		result := flags.parse(&s, args[:])
+		defer delete(s.n)
+		testing.expect_value(t, result, nil)
+		testing.expect_value(t, len(s.n), 3)
+
+		if len(s.n) == 3 {
+			testing.expect_value(t, s.n[0], 3)
+			testing.expect_value(t, s.n[1], 5)
+			testing.expect_value(t, s.n[2], 7)
+		}
+	}
+}
+
+@(test)
+test_tags_required_limit_max :: proc(t: ^testing.T) {
+	S :: struct {
+		n: [dynamic]int `args:"required=<4"`,
+	}
+
+	{
+		s: S
+		args: []string
+		result := flags.parse(&s, args)
+		testing.expect_value(t, result, nil)
+	}
+
+	{
+		s: S
+		args := [?]string { "-n:1", "-n:2", "-n:3", "-n:4" }
+		result := flags.parse(&s, args[:])
+		defer delete(s.n)
+		_, ok := result.(flags.Validation_Error)
+		testing.expectf(t, ok, "unexpected result: %v", result)
+	}
+
+	{
+		s: S
+		args := [?]string { "-n:3", "-n:5", "-n:7" }
+		result := flags.parse(&s, args[:])
+		defer delete(s.n)
+		testing.expect_value(t, result, nil)
+		testing.expect_value(t, len(s.n), 3)
+
+		if len(s.n) == 3 {
+			testing.expect_value(t, s.n[0], 3)
+			testing.expect_value(t, s.n[1], 5)
+			testing.expect_value(t, s.n[2], 7)
+		}
+	}
+}
+
+@(test)
+test_tags_pos_out_of_order :: proc(t: ^testing.T) {
+	S :: struct {
+		a: int `args:"pos=2"`,
+		varg: [dynamic]int,
+	}
+	s: S
+	args := [?]string { "1", "2", "3", "4" }
+	result := flags.parse(&s, args[:])
+	defer delete(s.varg)
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.varg), 3)
+
+	if len(s.varg) < 3 {
+		return
+	}
+
+	testing.expect_value(t, s.a, 3)
+	testing.expect_value(t, s.varg[0], 1)
+	testing.expect_value(t, s.varg[1], 2)
+	testing.expect_value(t, s.varg[2], 4)
+}
+
+@(test)
+test_missing_flag :: proc(t: ^testing.T) {
+	S :: struct {
+		a: int,
+	}
+	s: S
+	args := [?]string { "-b" }
+	result := flags.parse(&s, args[:])
+	err, ok := result.(flags.Parse_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Missing_Flag)
+	}
+}
+
+@(test)
+test_alt_syntax :: proc(t: ^testing.T) {
+	S :: struct {
+		a: int,
+	}
+	s: S
+	args := [?]string { "-a=3" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, 3)
+}
+
+@(test)
+test_strict_returns_first_error :: proc(t: ^testing.T) {
+	S :: struct {
+		b: int,
+		c: int,
+	}
+	s: S
+	args := [?]string { "-a=3", "-b=3" }
+	result := flags.parse(&s, args[:], strict=true)
+	err, ok := result.(flags.Parse_Error)
+	testing.expect_value(t, s.b, 0)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Missing_Flag)
+	}
+}
+
+@(test)
+test_non_strict_returns_last_error :: proc(t: ^testing.T) {
+	S :: struct {
+		a: int,
+		b: int,
+	}
+	s: S
+	args := [?]string { "-a=foo", "-b=2", "-c=3" }
+	result := flags.parse(&s, args[:], strict=false)
+	err, ok := result.(flags.Parse_Error)
+	testing.expect_value(t, s.b, 2)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Missing_Flag)
+	}
+}
+
+@(test)
+test_map_overwrite :: proc(t: ^testing.T) {
+	S :: struct {
+		m: map[string]int,
+	}
+	s: S
+	args := [?]string { "-m:foo=3", "-m:foo=5" }
+	result := flags.parse(&s, args[:])
+	defer delete(s.m)
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.m), 1)
+	foo, has_foo := s.m["foo"]
+	testing.expect(t, has_foo, "expected map to have `foo` key set")
+	testing.expect_value(t, foo, 5)
+}
+
+@(test)
+test_maps_of_arrays :: proc(t: ^testing.T) {
+	// Why you would ever want to do this, I don't know, but it's possible!
+	S :: struct {
+		m: map[string][dynamic]int,
+	}
+	s: S
+	args := [?]string { "-m:foo=1", "-m:foo=2", "-m:bar=3" }
+	result := flags.parse(&s, args[:])
+	defer {
+		for _, v in s.m {
+			delete(v)
+		}
+		delete(s.m)
+	}
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.m), 2)
+
+	if len(s.m) != 2 {
+		return
+	}
+
+	foo, has_foo := s.m["foo"]
+	bar, has_bar := s.m["bar"]
+
+	testing.expect_value(t, has_foo, true)
+	testing.expect_value(t, has_bar, true)
+
+	if has_foo {
+		testing.expect_value(t, len(foo), 2)
+		if len(foo) == 2 {
+			testing.expect_value(t, foo[0], 1)
+			testing.expect_value(t, foo[1], 2)
+		}
+	}
+
+	if has_bar {
+		testing.expect_value(t, len(bar), 1)
+		if len(bar) == 1 {
+			testing.expect_value(t, bar[0], 3)
+		}
+	}
+}
+
+@(test)
+test_builtin_help_flag :: proc(t: ^testing.T) {
+	S :: struct {}
+	s: S
+
+	args_short  := [?]string { "-h" }
+	args_normal := [?]string { "-help" }
+
+	result := flags.parse(&s, args_short[:])
+	_, ok := result.(flags.Help_Request)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+
+	result = flags.parse(&s, args_normal[:])
+	_, ok = result.(flags.Help_Request)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+}
+
+// This test makes sure that if a positional argument is specified, it won't be
+// overwritten by an unspecified positional, which should follow the principle
+// of least surprise for the user.
+@(test)
+test_pos_nonoverlap :: proc(t: ^testing.T) {
+	S :: struct {
+		a: int `args:"pos=0"`,
+		b: int `args:"pos=1"`,
+	}
+	s: S
+
+	args := [?]string { "-a:3", "5" }
+
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, 3)
+	testing.expect_value(t, s.b, 5)
+}
+
+// This test ensures the underlying `bit_array` container handles many
+// arguments in a sane manner.
+@(test)
+test_pos_many_args :: proc(t: ^testing.T) {
+	S :: struct {
+		varg: [dynamic]int,
+		a: int `args:"pos=0,required"`,
+		b: int `args:"pos=64,required"`,
+		c: int `args:"pos=66,required"`,
+		d: int `args:"pos=129,required"`,
+	}
+	s: S
+
+	args: [dynamic]string
+	defer delete(s.varg)
+
+	for i in 0 ..< 130 { append(&args, fmt.aprintf("%i", 1 + i)) }
+	defer {
+		for a in args {
+			delete(a)
+		}
+		delete(args)
+	}
+
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+
+	testing.expect_value(t, s.a, 1)
+	for i in 1 ..< 63 { testing.expect_value(t, s.varg[i], 2 + i) }
+	testing.expect_value(t, s.b, 65)
+	testing.expect_value(t, s.varg[63], 66)
+	testing.expect_value(t, s.c, 67)
+	testing.expect_value(t, s.varg[64], 68)
+	testing.expect_value(t, s.varg[65], 69)
+	testing.expect_value(t, s.varg[66], 70)
+	for i in 67 ..< 126 { testing.expect_value(t, s.varg[i], 4 + i) }
+	testing.expect_value(t, s.d, 130)
+}
+
+@(test)
+test_unix :: proc(t: ^testing.T) {
+	S :: struct {
+		a: string,
+	}
+	s: S
+
+	{
+		args := [?]string { "--a", "hellope" }
+
+		result := flags.parse(&s, args[:], .Unix)
+		testing.expect_value(t, result, nil)
+		testing.expect_value(t, s.a, "hellope")
+	}
+
+	{
+		args := [?]string { "-a", "hellope", "--a", "world" }
+
+		result := flags.parse(&s, args[:], .Unix)
+		testing.expect_value(t, result, nil)
+		testing.expect_value(t, s.a, "world")
+	}
+
+	{
+		args := [?]string { "-a=hellope" }
+
+		result := flags.parse(&s, args[:], .Unix)
+		testing.expect_value(t, result, nil)
+		testing.expect_value(t, s.a, "hellope")
+	}
+}
+
+@(test)
+test_unix_variadic :: proc(t: ^testing.T) {
+	S :: struct {
+		a: [dynamic]int `args:"variadic"`,
+	}
+	s: S
+
+	args := [?]string { "--a", "7", "32", "11" }
+
+	result := flags.parse(&s, args[:], .Unix)
+	defer delete(s.a)
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.a), 3)
+
+	if len(s.a) < 3 {
+		return
+	}
+
+	testing.expect_value(t, s.a[0], 7)
+	testing.expect_value(t, s.a[1], 32)
+	testing.expect_value(t, s.a[2], 11)
+}
+
+@(test)
+test_unix_variadic_limited :: proc(t: ^testing.T) {
+	S :: struct {
+		a: [dynamic]int `args:"variadic=2"`,
+		b: int,
+	}
+	s: S
+
+	args := [?]string { "-a", "11", "101", "-b", "3" }
+
+	result := flags.parse(&s, args[:], .Unix)
+	defer delete(s.a)
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.a), 2)
+
+	if len(s.a) < 2 {
+		return
+	}
+
+	testing.expect_value(t, s.a[0], 11)
+	testing.expect_value(t, s.a[1], 101)
+	testing.expect_value(t, s.b, 3)
+}
+
+@(test)
+test_unix_positional :: proc(t: ^testing.T) {
+	S :: struct {
+		a: int `args:"pos=1"`,
+		b: int `args:"pos=0"`,
+	}
+	s: S
+
+	args := [?]string { "-b", "17", "11" }
+
+	result := flags.parse(&s, args[:], .Unix)
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a, 11)
+	testing.expect_value(t, s.b, 17)
+}
+
+@(test)
+test_unix_positional_with_variadic :: proc(t: ^testing.T) {
+	S :: struct {
+		varg: [dynamic]int,
+		v: [dynamic]int `args:"variadic"`,
+	}
+	s: S
+
+	args := [?]string { "35", "-v", "17", "11" }
+
+	result := flags.parse(&s, args[:], .Unix)
+	defer {
+		delete(s.varg)
+		delete(s.v)
+	}
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.varg), 1)
+	testing.expect_value(t, len(s.v), 2)
+}
+
+@(test)
+test_unix_double_dash_variadic :: proc(t: ^testing.T) {
+	S :: struct {
+		varg: [dynamic]string,
+		i: int,
+	}
+	s: S
+
+	args := [?]string { "-i", "3", "--", "hellope", "-i", "5" }
+
+	result := flags.parse(&s, args[:], .Unix)
+	defer {
+		delete(s.varg)
+	}
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, len(s.varg), 3)
+	testing.expect_value(t, s.i, 3)
+
+	if len(s.varg) != 3 {
+		return
+	}
+
+	testing.expect_value(t, s.varg[0], "hellope")
+	testing.expect_value(t, s.varg[1], "-i")
+	testing.expect_value(t, s.varg[2], "5")
+}
+
+@(test)
+test_unix_no_value :: proc(t: ^testing.T) {
+	S :: struct {
+		i: int,
+	}
+	s: S
+
+	args := [?]string { "--i" }
+
+	result := flags.parse(&s, args[:], .Unix)
+	err, ok := result.(flags.Parse_Error)
+	testing.expectf(t, ok, "unexpected result: %v", result)
+	if ok {
+		testing.expect_value(t, err.reason, flags.Parse_Error_Reason.No_Value)
+	}
+}
+
+// This test ensures there are no bad frees with cstrings.
+@(test)
+test_if_dynamic_cstrings_get_freed :: proc(t: ^testing.T) {
+	S :: struct {
+		varg: [dynamic]cstring,
+	}
+	s: S
+
+	args := [?]string { "Hellope", "world!" }
+	result := flags.parse(&s, args[:])
+	defer {
+		for v in s.varg {
+			delete(v)
+		}
+		delete(s.varg)
+	}
+	testing.expect_value(t, result, nil)
+}
+
+// This test ensures there are no double allocations with cstrings.
+@(test)
+test_if_map_cstrings_get_freed :: proc(t: ^testing.T) {
+	S :: struct {
+		m: map[cstring]cstring,
+	}
+	s: S
+
+	args := [?]string { "-m:hellope=world", "-m:hellope=bar", "-m:hellope=foo" }
+	result := flags.parse(&s, args[:])
+	defer {
+		for _, v in s.m {
+			delete(v)
+		}
+		delete(s.m)
+	}
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.m["hellope"], "foo")
+}
+
+@(test)
+test_os_handle :: proc(t: ^testing.T) {
+	defer if !testing.failed(t) {
+		// Delete the file now that we're done.
+		//
+		// This is not done all the time, just in case the file is useful to debugging.
+		testing.expect_value(t, os.remove(TEMPORARY_FILENAME), os.ERROR_NONE)
+	}
+
+	TEMPORARY_FILENAME :: "test_core_flags_write_test_output_data"
+
+	test_data := "Hellope!"
+
+	W :: struct {
+		outf: os.Handle `args:"file=cw"`,
+	}
+	w: W
+
+	args := [?]string { fmt.tprintf("-outf:%s", TEMPORARY_FILENAME) }
+	result := flags.parse(&w, args[:])
+	testing.expect_value(t, result, nil)
+	if result != nil {
+		return
+	}
+	defer os.close(w.outf)
+	os.write_string(w.outf, test_data)
+
+	R :: struct {
+		inf: os.Handle `args:"file=r"`,
+	}
+	r: R
+
+	args = [?]string { fmt.tprintf("-inf:%s", TEMPORARY_FILENAME) }
+	result = flags.parse(&r, args[:])
+	testing.expect_value(t, result, nil)
+	if result != nil {
+		return
+	}
+	defer os.close(r.inf)
+	data, read_ok := os.read_entire_file_from_handle(r.inf, context.temp_allocator)
+	testing.expect_value(t, read_ok, true)
+	file_contents_equal := 0 == bytes.compare(transmute([]u8)test_data, data)
+	testing.expectf(t, file_contents_equal, "expected file contents to be the same, got %v", data)
+}
+
+@(test)
+test_distinct_types :: proc(t: ^testing.T) {
+	I :: distinct int
+	S :: struct {
+		base_i: I `args:"indistinct"`,
+		unmodified_i: I,
+	}
+	s: S
+
+	{
+		args := [?]string {"-base-i:1"}
+		result := flags.parse(&s, args[:])
+		testing.expect_value(t, result, nil)
+	}
+
+	{
+		args := [?]string {"-unmodified-i:1"}
+		result := flags.parse(&s, args[:])
+		err, ok := result.(flags.Parse_Error)
+		testing.expectf(t, ok, "unexpected result: %v", result)
+		if ok {
+			testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Unsupported_Type)
+		}
+	}
+}
+
+@(test)
+test_datetime :: proc(t: ^testing.T) {
+	when flags.IMPORTING_TIME {
+		W :: struct {
+			t: datetime.DateTime,
+		}
+		w: W
+
+		args := [?]string { "-t:2024-06-04T12:34:56Z" }
+		result := flags.parse(&w, args[:])
+		testing.expect_value(t, result, nil)
+		if result != nil {
+			return
+		}
+		testing.expect_value(t, w.t.date.year, 2024)
+		testing.expect_value(t, w.t.date.month, 6)
+		testing.expect_value(t, w.t.date.day, 4)
+	} else {
+		log.info("Skipping test due to lack of platform support.")
+	}
+}
+
+@(test)
+test_net :: proc(t: ^testing.T) {
+	when flags.IMPORTING_NET {
+		W :: struct {
+			addr: net.Host_Or_Endpoint,
+		}
+		w: W
+
+		args := [?]string { "-addr:odin-lang.org:80" }
+		result := flags.parse(&w, args[:])
+		testing.expect_value(t, result, nil)
+		if result != nil {
+			return
+		}
+		host, is_host := w.addr.(net.Host)
+		testing.expectf(t, is_host, "expected type of `addr` to be `net.Host`, was %v", w.addr)
+		testing.expect_value(t, host.hostname, "odin-lang.org")
+		testing.expect_value(t, host.port, 80)
+	} else {
+		log.info("Skipping test due to lack of platform support.")
+	}
+}
+
+@(test)
+test_custom_type_setter :: proc(t: ^testing.T) {
+	Custom_Bool :: distinct bool
+	Custom_Data :: struct {
+		a: int,
+	}
+
+	S :: struct {
+		a: Custom_Data,
+		b: Custom_Bool `args:"indistinct"`,
+	}
+	s: S
+
+	// NOTE: Mind that this setter is global state, and the test runner is multi-threaded.
+	// It should be fine so long as all type setter tests are in this one test proc.
+	flags.register_type_setter(proc (data: rawptr, data_type: typeid, _, _: string) -> (string, bool, runtime.Allocator_Error) {
+		if data_type == Custom_Data {
+			(cast(^Custom_Data)data).a = 32
+			return "", true, nil
+		}
+		return "", false, nil
+	})
+	defer flags.register_type_setter(nil)
+	args := [?]string { "-a:hellope", "-b:true" }
+	result := flags.parse(&s, args[:])
+	testing.expect_value(t, result, nil)
+	testing.expect_value(t, s.a.a, 32)
+	testing.expect_value(t, s.b, true)
+}
+
+// This test is sensitive to many of the underlying mechanisms of the library,
+// so if something isn't working, it'll probably show up here first, but it may
+// not be immediately obvious as to what's wrong.
+//
+// It makes for a good early warning system.
+@(test)
+test_usage_write_odin :: proc(t: ^testing.T) {
+	Expected_Output :: `Usage:
+	varg required-number [number] [name] -bars -bots -foos -gadgets -widgets [-array] [-count] [-greek] [-map-type] [-verbose] ...
+Flags:
+	-required-number:<int>, required  | some number
+	-number:<int>                     | some other number
+	-name:<string>
+		Multi-line documentation
+		gets formatted
+		very nicely.
+	-bars:<string>, exactly 3         | <This flag has not been documented yet.>
+	-bots:<string>, at least 1        | <This flag has not been documented yet.>
+	-foos:<string>, between 2 and 3   | <This flag has not been documented yet.>
+	-gadgets:<string>, at least 1     | <This flag has not been documented yet.>
+	-widgets:<string>, at most 2      | <This flag has not been documented yet.>
+	                                  |
+	-array:<rune>, multiple           | <This flag has not been documented yet.>
+	-count:<u8>                       | <This flag has not been documented yet.>
+	-greek:<Custom_Enum>              | <This flag has not been documented yet.>
+	-map-type:<cstring>=<u8>          | <This flag has not been documented yet.>
+	-verbose                          | <This flag has not been documented yet.>
+	<string, ...>                     | <This flag has not been documented yet.>
+`
+
+	Custom_Enum :: enum {
+		Alpha,
+		Omega,
+	}
+
+	S :: struct {
+		required_number: int `args:"pos=0,required" usage:"some number"`,
+		number: int `args:"pos=1" usage:"some other number"`,
+		name: string `args:"pos=2" usage:"
+	Multi-line documentation
+		gets formatted
+very nicely.
+
+"`,
+
+		c: u8 `args:"name=count"`,
+		greek: Custom_Enum,
+
+		array: [dynamic]rune,
+		map_type: map[cstring]byte,
+
+		gadgets: [dynamic]string `args:"required=1"`,
+		widgets: [dynamic]string `args:"required=<3"`,
+		foos: [dynamic]string `args:"required=2<4"`,
+		bars: [dynamic]string `args:"required=3<4"`,
+		bots: [dynamic]string `args:"required"`,
+
+		debug: bool `args:"hidden" usage:"print debug info"`,
+		verbose: bool,
+
+		varg: [dynamic]string,
+	}
+
+	builder := strings.builder_make()
+	defer strings.builder_destroy(&builder)
+	writer := strings.to_stream(&builder)
+	flags.write_usage(writer, S, "varg", .Odin)
+	testing.expect_value(t, strings.to_string(builder), Expected_Output)
+}
+
+@(test)
+test_usage_write_unix :: proc(t: ^testing.T) {
+	Expected_Output :: `Usage:
+	varg required-number [number] [name] --bars --bots --foos --gadgets --variadic-flag --widgets [--array] [--count] [--greek] [--verbose] ...
+Flags:
+	--required-number <int>, required       | some number
+	--number <int>                          | some other number
+	--name <string>
+		Multi-line documentation
+		gets formatted
+		very nicely.
+	--bars <string>, exactly 3              | <This flag has not been documented yet.>
+	--bots <string>, at least 1             | <This flag has not been documented yet.>
+	--foos <string>, between 2 and 3        | <This flag has not been documented yet.>
+	--gadgets <string>, at least 1          | <This flag has not been documented yet.>
+	--variadic-flag <int, ...>, at least 2  | <This flag has not been documented yet.>
+	--widgets <string>, at most 2           | <This flag has not been documented yet.>
+	                                        |
+	--array <rune>, multiple                | <This flag has not been documented yet.>
+	--count <u8>                            | <This flag has not been documented yet.>
+	--greek <Custom_Enum>                   | <This flag has not been documented yet.>
+	--verbose                               | <This flag has not been documented yet.>
+	<string, ...>                           | <This flag has not been documented yet.>
+`
+
+	Custom_Enum :: enum {
+		Alpha,
+		Omega,
+	}
+
+	S :: struct {
+		required_number: int `args:"pos=0,required" usage:"some number"`,
+		number: int `args:"pos=1" usage:"some other number"`,
+		name: string `args:"pos=2" usage:"
+	Multi-line documentation
+		gets formatted
+very nicely.
+
+"`,
+
+		c: u8 `args:"name=count"`,
+		greek: Custom_Enum,
+
+		array: [dynamic]rune,
+		variadic_flag: [dynamic]int `args:"variadic,required=2"`,
+
+		gadgets: [dynamic]string `args:"required=1"`,
+		widgets: [dynamic]string `args:"required=<3"`,
+		foos: [dynamic]string `args:"required=2<4"`,
+		bars: [dynamic]string `args:"required=3<4"`,
+		bots: [dynamic]string `args:"required"`,
+
+		debug: bool `args:"hidden" usage:"print debug info"`,
+		verbose: bool,
+
+		varg: [dynamic]string,
+	}
+
+	builder := strings.builder_make()
+	defer strings.builder_destroy(&builder)
+	writer := strings.to_stream(&builder)
+	flags.write_usage(writer, S, "varg", .Unix)
+	testing.expect_value(t, strings.to_string(builder), Expected_Output)
+}

+ 2 - 2
tests/core/hash/test_core_hash.odin

@@ -53,9 +53,9 @@ test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) {
 		testing.expect(t, xxh3_128_err == nil, "Problem initializing XXH3_128 state")
 
 		// XXH3_128_update
-		random_seed := rand.create(t.seed)
+		rand.reset(t.seed)
 		for len(b) > 0 {
-			update_size := min(len(b), rand.int_max(8192, &random_seed))
+			update_size := min(len(b), rand.int_max(8192))
 			if update_size > 4096 {
 				update_size %= 73
 			}

+ 35 - 0
tests/core/math/rand/test_core_math_rand.odin

@@ -0,0 +1,35 @@
+package test_core_math_rand
+
+import "core:math/rand"
+import "core:testing"
+
+@test
+test_default_rand_determinism :: proc(t: ^testing.T) {
+	rand.reset(13)
+	first_value := rand.int127()
+	rand.reset(13)
+	second_value := rand.int127()
+
+	testing.expect(t, first_value == second_value, "Context default random number generator is non-deterministic.")
+}
+
+@test
+test_default_rand_determinism_user_set :: proc(t: ^testing.T) {
+	rng_state_1 := rand.create(13)
+	rng_state_2 := rand.create(13)
+
+	rng_1 := rand.default_random_generator(&rng_state_1)
+	rng_2 := rand.default_random_generator(&rng_state_2)
+
+	first_value, second_value: i128
+	{
+		context.random_generator = rng_1
+		first_value = rand.int127()
+	}
+	{
+		context.random_generator = rng_2
+		second_value = rand.int127()
+	}
+
+	testing.expect(t, first_value == second_value, "User-set default random number generator is non-deterministic.")
+}

+ 3 - 0
tests/core/normal.odin

@@ -19,11 +19,13 @@ download_assets :: proc() {
 @(require) import "encoding/json"
 @(require) import "encoding/varint"
 @(require) import "encoding/xml"
+@(require) import "flags"
 @(require) import "fmt"
 @(require) import "math"
 @(require) import "math/big"
 @(require) import "math/linalg/glsl"
 @(require) import "math/noise"
+@(require) import "math/rand"
 @(require) import "mem"
 @(require) import "net"
 @(require) import "odin"
@@ -37,3 +39,4 @@ download_assets :: proc() {
 @(require) import "text/match"
 @(require) import "thread"
 @(require) import "time"
+@(require) import "unicode"

+ 70 - 9
tests/core/slice/test_core_slice.odin

@@ -3,6 +3,7 @@ package test_core_slice
 import "core:slice"
 import "core:testing"
 import "core:math/rand"
+import "core:log"
 
 @test
 test_sort_with_indices :: proc(t: ^testing.T) {
@@ -10,7 +11,7 @@ test_sort_with_indices :: proc(t: ^testing.T) {
 	test_sizes :: []int{7, 13, 347, 1031, 10111, 100003}
 
 	for test_size in test_sizes {
-		r := rand.create(t.seed)
+		rand.reset(t.seed)
 
 		vals  := make([]u64, test_size)
 		r_idx := make([]int, test_size) // Reverse index
@@ -21,7 +22,7 @@ test_sort_with_indices :: proc(t: ^testing.T) {
 
 		// Set up test values
 		for _, i in vals {
-			vals[i] = rand.uint64(&r)
+			vals[i] = rand.uint64()
 		}
 
 		// Sort
@@ -29,7 +30,7 @@ test_sort_with_indices :: proc(t: ^testing.T) {
 		defer delete(f_idx)
 
 		// Verify sorted test values
-		rand.init(&r, t.seed)
+		rand.reset(t.seed)
 
 		for v, i in f_idx {
 			r_idx[v] = i
@@ -45,7 +46,7 @@ test_sort_with_indices :: proc(t: ^testing.T) {
 				}
 			}
 
-			idx_pass := vals[r_idx[i]] == rand.uint64(&r)
+			idx_pass := vals[r_idx[i]] == rand.uint64()
 			testing.expect(t, idx_pass, "Expected index to have been sorted")
 			if !idx_pass {
 				break
@@ -61,7 +62,7 @@ test_sort_by_indices :: proc(t: ^testing.T) {
 	test_sizes :: []int{7, 13, 347, 1031, 10111, 100003}
 
 	for test_size in test_sizes {
-		r := rand.create(t.seed)
+		rand.reset(t.seed)
 
 		vals  := make([]u64, test_size)
 		r_idx := make([]int, test_size) // Reverse index
@@ -72,7 +73,7 @@ test_sort_by_indices :: proc(t: ^testing.T) {
 
 		// Set up test values
 		for _, i in vals {
-			vals[i] = rand.uint64(&r)
+			vals[i] = rand.uint64()
 		}
 
 		// Sort
@@ -80,7 +81,7 @@ test_sort_by_indices :: proc(t: ^testing.T) {
 		defer delete(f_idx)
 
 		// Verify sorted test values
-		rand.init(&r, t.seed)
+		rand.reset(t.seed)
 
 		{
 			indices := make([]int, test_size)
@@ -200,12 +201,12 @@ test_permutation_iterator :: proc(t: ^testing.T) {
 	permutations_counted: int
 	for slice.permute(&iter) {
 		n := 0
-		for item, index in s {
+		for item in s {
 			n *= 10
 			n += item
 		}
 		if n in seen {
-			testing.fail_now(t, "Permutation iterator made a duplicate permutation.")
+			log.error("Permutation iterator made a duplicate permutation.")
 			return
 		}
 		seen[n] = true
@@ -215,3 +216,63 @@ test_permutation_iterator :: proc(t: ^testing.T) {
 	testing.expect_value(t, len(seen), FAC_5)
 	testing.expect_value(t, permutations_counted, FAC_5)
 }
+
+// Test inputs from #3276 and #3769
+UNIQUE_TEST_VECTORS :: [][2][]int{
+	{{2,2,2},             {2}},
+	{{1,1,1,2,2,3,3,3,3}, {1,2,3}},
+	{{1,2,4,4,5},         {1,2,4,5}},
+}
+
+@test
+test_unique :: proc(t: ^testing.T) {
+	for v in UNIQUE_TEST_VECTORS {
+		assorted := v[0]
+		expected := v[1]
+
+		uniq := slice.unique(assorted)
+		testing.expectf(t, slice.equal(uniq, expected), "Expected slice.uniq(%v) == %v, got %v", v[0], v[1], uniq)
+	}
+
+	for v in UNIQUE_TEST_VECTORS {
+		assorted := v[0]
+		expected := v[1]
+
+		uniq := slice.unique_proc(assorted, proc(a, b: int) -> bool {
+			return a == b
+		})
+		testing.expectf(t, slice.equal(uniq, expected), "Expected slice.unique_proc(%v, ...) == %v, got %v", v[0], v[1], uniq)
+	}
+
+	r := rand.create(t.seed)
+	context.random_generator = rand.default_random_generator(&r)
+
+	// 10_000 random tests
+	for _ in 0..<10_000 {
+		assorted: [dynamic]i64
+		expected: [dynamic]i64
+
+		// Prime with 1 value
+		old := rand.int63()
+		append(&assorted, old)
+		append(&expected, old)
+
+		// Add 99 additional random values
+		for _ in 1..<100 {
+			new := rand.int63()
+			append(&assorted, new)
+			if old != new {
+				append(&expected, new)
+			}
+			old = new
+		}
+
+		original := slice.clone(assorted[:])
+		uniq := slice.unique(assorted[:])
+		testing.expectf(t, slice.equal(uniq, expected[:]), "Expected slice.uniq(%v) == %v, got %v", original, expected, uniq)
+
+		delete(assorted)
+		delete(original)
+		delete(expected)
+	}
+}

+ 21 - 19
tests/core/text/i18n/test_core_text_i18n.odin

@@ -5,6 +5,7 @@ import "core:testing"
 import "core:text/i18n"
 
 T :: i18n.get
+Tn :: i18n.get_n
 
 Test :: struct {
 	section: string,
@@ -47,7 +48,8 @@ test_custom_pluralizer :: proc(t: ^testing.T) {
 			{"", "Message1/plural",               "This is message 1",             1},
 			{"", "Message1/plural",               "This is message 1 - plural A",  1_000_000},
 			{"", "Message1/plural",               "This is message 1 - plural B",  42},
-			// This isn't in the catalog, so should ruturn the key.
+
+			// This isn't in the catalog, so should return the key.
 			{"", "Come visit us on Discord!",     "Come visit us on Discord!",      1},
 		},
 	})
@@ -61,11 +63,11 @@ test_mixed_context :: proc(t: ^testing.T) {
 		plural = nil,
 		tests  = {
 			// These are in the catalog.
-			{"",        "Message1",               "This is message 1 without Context", 1},
-			{"Context", "Message1",               "This is message 1 with Context",    1},
+			{"",        "Message1",               "This is message 1 without Context",-1},
+			{"Context", "Message1",               "This is message 1 with Context",   -1},
 
 			// This isn't in the catalog, so should ruturn the key.
-			{"", "Come visit us on Discord!",     "Come visit us on Discord!",         1},
+			{"", "Come visit us on Discord!",     "Come visit us on Discord!",        -1},
 		},
 	})
 }
@@ -90,15 +92,15 @@ test_nl_mo :: proc(t: ^testing.T) {
 		plural = nil, // Default pluralizer
 		tests  = {
 			// These are in the catalog.
-			{"", "There are 69,105 leaves here.", "Er zijn hier 69.105 bladeren.",  1},
-			{"", "Hellope, World!",               "Hallo, Wereld!",                 1},
+			{"", "There are 69,105 leaves here.", "Er zijn hier 69.105 bladeren.", -1},
+			{"", "Hellope, World!",               "Hallo, Wereld!",                -1},
 			{"", "There is %d leaf.\n",           "Er is %d blad.\n",               1},
 			{"", "There are %d leaves.\n",        "Er is %d blad.\n",               1},
 			{"", "There is %d leaf.\n",           "Er zijn %d bladeren.\n",        42},
 			{"", "There are %d leaves.\n",        "Er zijn %d bladeren.\n",        42},
 
 			// This isn't in the catalog, so should ruturn the key.
-			{"", "Come visit us on Discord!",     "Come visit us on Discord!",      1},
+			{"", "Come visit us on Discord!",     "Come visit us on Discord!",     -1},
 		},
 	})
 }
@@ -111,15 +113,15 @@ test_qt_linguist :: proc(t: ^testing.T) {
 		plural = nil, // Default pluralizer
 		tests  = {
 			// These are in the catalog.
-			{"Page",          "Text for translation",           "Tekst om te vertalen",        1},
-			{"Page",          "Also text to translate",         "Ook tekst om te vertalen",    1},
-			{"installscript", "99 bottles of beer on the wall", "99 flessen bier op de muur",  1},
+			{"Page",          "Text for translation",           "Tekst om te vertalen",       -1},
+			{"Page",          "Also text to translate",         "Ook tekst om te vertalen",   -1},
+			{"installscript", "99 bottles of beer on the wall", "99 flessen bier op de muur", -1},
 			{"apple_count",   "%d apple(s)",                    "%d appel",                    1},
 			{"apple_count",   "%d apple(s)",                    "%d appels",                  42},
 
 			// These aren't in the catalog, so should ruturn the key.
-			{"",              "Come visit us on Discord!",      "Come visit us on Discord!",   1},
-			{"Fake_Section",  "Come visit us on Discord!",      "Come visit us on Discord!",   1},
+			{"",              "Come visit us on Discord!",      "Come visit us on Discord!",  -1},
+			{"Fake_Section",  "Come visit us on Discord!",      "Come visit us on Discord!",  -1},
 		},
 	})
 }
@@ -133,16 +135,16 @@ test_qt_linguist_merge_sections :: proc(t: ^testing.T) {
 		options = {merge_sections = true},
 		tests   = {
 			// All of them are now in section "", lookup with original section should return the key.
-			{"",              "Text for translation",           "Tekst om te vertalen",            1},
-			{"",              "Also text to translate",         "Ook tekst om te vertalen",        1},
-			{"",              "99 bottles of beer on the wall", "99 flessen bier op de muur",      1},
+			{"",              "Text for translation",           "Tekst om te vertalen",           -1},
+			{"",              "Also text to translate",         "Ook tekst om te vertalen",       -1},
+			{"",              "99 bottles of beer on the wall", "99 flessen bier op de muur",     -1},
 			{"",              "%d apple(s)",                    "%d appel",                        1},
 			{"",              "%d apple(s)",                    "%d appels",                      42},
 
 			// All of them are now in section "", lookup with original section should return the key.
-			{"Page",          "Text for translation",           "Text for translation",            1},
-			{"Page",          "Also text to translate",         "Also text to translate",          1},
-			{"installscript", "99 bottles of beer on the wall", "99 bottles of beer on the wall",  1},
+			{"Page",          "Text for translation",           "Text for translation",           -1},
+			{"Page",          "Also text to translate",         "Also text to translate",         -1},
+			{"installscript", "99 bottles of beer on the wall", "99 bottles of beer on the wall", -1},
 			{"apple_count",   "%d apple(s)",                    "%d apple(s)",                     1},
 			{"apple_count",   "%d apple(s)",                    "%d apple(s)",                    42},
 		},
@@ -175,7 +177,7 @@ test :: proc(t: ^testing.T, suite: Test_Suite, loc := #caller_location) {
 
 	if err == .None {
 		for test in suite.tests {
-			val := T(test.section, test.key, test.n, cat)
+			val := test.n > -1 ? Tn(test.section, test.key, test.n, cat): T(test.section, test.key, cat)
 			testing.expectf(t, val == test.val, "Expected key `%v` from section `%v`'s form for value `%v` to equal `%v`, got `%v`", test.key, test.section, test.n, test.val, val, loc=loc)
 		}
 	}

+ 73 - 0
tests/core/unicode/test_core_unicode.odin

@@ -0,0 +1,73 @@
+package test_core_unicode
+
+import "core:log"
+import "core:testing"
+import "core:unicode/utf8"
+
+Test_Case :: struct {
+	str: string,
+	expected_clusters: int,
+}
+
+run_test_cases :: proc(t: ^testing.T, test_cases: []Test_Case, loc := #caller_location) {
+	failed := 0
+	for c, i in test_cases {
+		log.debugf("(#% 4i) %q ...", i, c.str)
+		result, _ := utf8.grapheme_count(c.str)
+		if !testing.expectf(t, result == c.expected_clusters,
+			"(#% 4i) graphemes: %i != %i, %q %s", i, result, c.expected_clusters, c.str, c.str,
+			loc = loc)
+		{
+			failed += 1
+		}
+	}
+
+	log.logf(.Error if failed > 0 else .Info, "% 4i/% 4i test cases failed.", failed, len(test_cases), location = loc)
+}
+
+@test
+test_official_gcb_cases :: proc(t: ^testing.T) {
+	run_test_cases(t, official_grapheme_break_test_cases)
+}
+
+@test
+test_official_emoji_cases :: proc(t: ^testing.T) {
+	run_test_cases(t, official_emoji_test_cases)
+}
+
+@test
+test_grapheme_byte_index_segmentation :: proc(t: ^testing.T) {
+	SAMPLE_1 :: "\U0001F600"
+	SAMPLE_2 :: "\U0001F3F4\U000E0067\U000E0062\U000E0065\U000E006E\U000E0067\U000E007F"
+	SAMPLE_3 :: "\U0001F468\U0001F3FB\u200D\U0001F9B0"
+
+	str := SAMPLE_1 + SAMPLE_2 + SAMPLE_3 + SAMPLE_2 + SAMPLE_1
+
+	graphemes, _, _ := utf8.decode_grapheme_clusters(str)
+	defer delete(graphemes)
+
+	defer if testing.failed(t) {
+		log.infof("%#v\n%q\n%v", graphemes, str, transmute([]u8)str)
+	}
+	if !testing.expect_value(t, len(graphemes), 5) {
+		return
+	}
+
+	testing.expect_value(t, graphemes[0].rune_index, 0)
+	testing.expect_value(t, graphemes[1].rune_index, 1)
+	testing.expect_value(t, graphemes[2].rune_index, 8)
+	testing.expect_value(t, graphemes[3].rune_index, 12)
+	testing.expect_value(t, graphemes[4].rune_index, 19)
+
+	grapheme_1 := str[graphemes[0].byte_index:graphemes[1].byte_index]
+	grapheme_2 := str[graphemes[1].byte_index:graphemes[2].byte_index]
+	grapheme_3 := str[graphemes[2].byte_index:graphemes[3].byte_index]
+	grapheme_4 := str[graphemes[3].byte_index:graphemes[4].byte_index]
+	grapheme_5 := str[graphemes[4].byte_index:]
+
+	testing.expectf(t, grapheme_1 == SAMPLE_1, "expected %q, got %q", SAMPLE_1, grapheme_1)
+	testing.expectf(t, grapheme_2 == SAMPLE_2, "expected %q, got %q", SAMPLE_2, grapheme_2)
+	testing.expectf(t, grapheme_3 == SAMPLE_3, "expected %q, got %q", SAMPLE_3, grapheme_3)
+	testing.expectf(t, grapheme_4 == SAMPLE_2, "expected %q, got %q", SAMPLE_2, grapheme_2)
+	testing.expectf(t, grapheme_5 == SAMPLE_1, "expected %q, got %q", SAMPLE_1, grapheme_1)
+}

+ 4912 - 0
tests/core/unicode/test_core_unicode_data.odin

@@ -0,0 +1,4912 @@
+package test_core_unicode
+
+// This file contains test data licensed under the Unicode Consortium that has
+// been converted to a format that will work within Odin.
+//
+// The Unicode license to which said test data is under is included verbatim
+// within this file, along with the names of the files from which the test data
+// has been converted.
+
+/*
+UNICODE LICENSE V3
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright © 1991-2024 Unicode, Inc.
+
+NOTICE TO USER: Carefully read the following legal agreement. BY
+DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR
+SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE
+TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT
+DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of data files and any associated documentation (the "Data Files") or
+software and any associated documentation (the "Software") to deal in the
+Data Files or Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, and/or sell
+copies of the Data Files or Software, and to permit persons to whom the
+Data Files or Software are furnished to do so, provided that either (a)
+this copyright and permission notice appear with all copies of the Data
+Files or Software, or (b) this copyright and permission notice appear in
+associated Documentation.
+
+THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+THIRD PARTY RIGHTS.
+
+IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE
+BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES,
+OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA
+FILES OR SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or other
+dealings in these Data Files or Software without prior written
+authorization of the copyright holder.
+*/
+
+
+// https://unicode.org/Public/15.1.0/ucd/auxiliary/GraphemeBreakTest.txt
+//
+// GraphemeBreakTest-15.1.0.txt
+// Date: 2023-08-07, 15:52:55 GMT
+// © 2023 Unicode®, Inc.
+// Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+// For terms of use, see https://www.unicode.org/terms_of_use.html
+@(rodata)
+official_grapheme_break_test_cases := []Test_Case {
+{"\u0020\u0020", 2},
+{"\u0020\u0308\u0020", 2},
+{"\u0020\u000D", 2},
+{"\u0020\u0308\u000D", 2},
+{"\u0020\u000A", 2},
+{"\u0020\u0308\u000A", 2},
+{"\u0020\u0001", 2},
+{"\u0020\u0308\u0001", 2},
+{"\u0020\u034F", 1},
+{"\u0020\u0308\u034F", 1},
+{"\u0020\U0001F1E6", 2},
+{"\u0020\u0308\U0001F1E6", 2},
+{"\u0020\u0600", 2},
+{"\u0020\u0308\u0600", 2},
+{"\u0020\u0A03", 1},
+{"\u0020\u0308\u0A03", 1},
+{"\u0020\u1100", 2},
+{"\u0020\u0308\u1100", 2},
+{"\u0020\u1160", 2},
+{"\u0020\u0308\u1160", 2},
+{"\u0020\u11A8", 2},
+{"\u0020\u0308\u11A8", 2},
+{"\u0020\uAC00", 2},
+{"\u0020\u0308\uAC00", 2},
+{"\u0020\uAC01", 2},
+{"\u0020\u0308\uAC01", 2},
+{"\u0020\u0900", 1},
+{"\u0020\u0308\u0900", 1},
+{"\u0020\u0903", 1},
+{"\u0020\u0308\u0903", 1},
+{"\u0020\u0904", 2},
+{"\u0020\u0308\u0904", 2},
+{"\u0020\u0D4E", 2},
+{"\u0020\u0308\u0D4E", 2},
+{"\u0020\u0915", 2},
+{"\u0020\u0308\u0915", 2},
+{"\u0020\u231A", 2},
+{"\u0020\u0308\u231A", 2},
+{"\u0020\u0300", 1},
+{"\u0020\u0308\u0300", 1},
+{"\u0020\u093C", 1},
+{"\u0020\u0308\u093C", 1},
+{"\u0020\u094D", 1},
+{"\u0020\u0308\u094D", 1},
+{"\u0020\u200D", 1},
+{"\u0020\u0308\u200D", 1},
+{"\u0020\u0378", 2},
+{"\u0020\u0308\u0378", 2},
+{"\u000D\u0020", 2},
+{"\u000D\u0308\u0020", 3},
+{"\u000D\u000D", 2},
+{"\u000D\u0308\u000D", 3},
+{"\u000D\u000A", 1},
+{"\u000D\u0308\u000A", 3},
+{"\u000D\u0001", 2},
+{"\u000D\u0308\u0001", 3},
+{"\u000D\u034F", 2},
+{"\u000D\u0308\u034F", 2},
+{"\u000D\U0001F1E6", 2},
+{"\u000D\u0308\U0001F1E6", 3},
+{"\u000D\u0600", 2},
+{"\u000D\u0308\u0600", 3},
+{"\u000D\u0A03", 2},
+{"\u000D\u0308\u0A03", 2},
+{"\u000D\u1100", 2},
+{"\u000D\u0308\u1100", 3},
+{"\u000D\u1160", 2},
+{"\u000D\u0308\u1160", 3},
+{"\u000D\u11A8", 2},
+{"\u000D\u0308\u11A8", 3},
+{"\u000D\uAC00", 2},
+{"\u000D\u0308\uAC00", 3},
+{"\u000D\uAC01", 2},
+{"\u000D\u0308\uAC01", 3},
+{"\u000D\u0900", 2},
+{"\u000D\u0308\u0900", 2},
+{"\u000D\u0903", 2},
+{"\u000D\u0308\u0903", 2},
+{"\u000D\u0904", 2},
+{"\u000D\u0308\u0904", 3},
+{"\u000D\u0D4E", 2},
+{"\u000D\u0308\u0D4E", 3},
+{"\u000D\u0915", 2},
+{"\u000D\u0308\u0915", 3},
+{"\u000D\u231A", 2},
+{"\u000D\u0308\u231A", 3},
+{"\u000D\u0300", 2},
+{"\u000D\u0308\u0300", 2},
+{"\u000D\u093C", 2},
+{"\u000D\u0308\u093C", 2},
+{"\u000D\u094D", 2},
+{"\u000D\u0308\u094D", 2},
+{"\u000D\u200D", 2},
+{"\u000D\u0308\u200D", 2},
+{"\u000D\u0378", 2},
+{"\u000D\u0308\u0378", 3},
+{"\u000A\u0020", 2},
+{"\u000A\u0308\u0020", 3},
+{"\u000A\u000D", 2},
+{"\u000A\u0308\u000D", 3},
+{"\u000A\u000A", 2},
+{"\u000A\u0308\u000A", 3},
+{"\u000A\u0001", 2},
+{"\u000A\u0308\u0001", 3},
+{"\u000A\u034F", 2},
+{"\u000A\u0308\u034F", 2},
+{"\u000A\U0001F1E6", 2},
+{"\u000A\u0308\U0001F1E6", 3},
+{"\u000A\u0600", 2},
+{"\u000A\u0308\u0600", 3},
+{"\u000A\u0A03", 2},
+{"\u000A\u0308\u0A03", 2},
+{"\u000A\u1100", 2},
+{"\u000A\u0308\u1100", 3},
+{"\u000A\u1160", 2},
+{"\u000A\u0308\u1160", 3},
+{"\u000A\u11A8", 2},
+{"\u000A\u0308\u11A8", 3},
+{"\u000A\uAC00", 2},
+{"\u000A\u0308\uAC00", 3},
+{"\u000A\uAC01", 2},
+{"\u000A\u0308\uAC01", 3},
+{"\u000A\u0900", 2},
+{"\u000A\u0308\u0900", 2},
+{"\u000A\u0903", 2},
+{"\u000A\u0308\u0903", 2},
+{"\u000A\u0904", 2},
+{"\u000A\u0308\u0904", 3},
+{"\u000A\u0D4E", 2},
+{"\u000A\u0308\u0D4E", 3},
+{"\u000A\u0915", 2},
+{"\u000A\u0308\u0915", 3},
+{"\u000A\u231A", 2},
+{"\u000A\u0308\u231A", 3},
+{"\u000A\u0300", 2},
+{"\u000A\u0308\u0300", 2},
+{"\u000A\u093C", 2},
+{"\u000A\u0308\u093C", 2},
+{"\u000A\u094D", 2},
+{"\u000A\u0308\u094D", 2},
+{"\u000A\u200D", 2},
+{"\u000A\u0308\u200D", 2},
+{"\u000A\u0378", 2},
+{"\u000A\u0308\u0378", 3},
+{"\u0001\u0020", 2},
+{"\u0001\u0308\u0020", 3},
+{"\u0001\u000D", 2},
+{"\u0001\u0308\u000D", 3},
+{"\u0001\u000A", 2},
+{"\u0001\u0308\u000A", 3},
+{"\u0001\u0001", 2},
+{"\u0001\u0308\u0001", 3},
+{"\u0001\u034F", 2},
+{"\u0001\u0308\u034F", 2},
+{"\u0001\U0001F1E6", 2},
+{"\u0001\u0308\U0001F1E6", 3},
+{"\u0001\u0600", 2},
+{"\u0001\u0308\u0600", 3},
+{"\u0001\u0A03", 2},
+{"\u0001\u0308\u0A03", 2},
+{"\u0001\u1100", 2},
+{"\u0001\u0308\u1100", 3},
+{"\u0001\u1160", 2},
+{"\u0001\u0308\u1160", 3},
+{"\u0001\u11A8", 2},
+{"\u0001\u0308\u11A8", 3},
+{"\u0001\uAC00", 2},
+{"\u0001\u0308\uAC00", 3},
+{"\u0001\uAC01", 2},
+{"\u0001\u0308\uAC01", 3},
+{"\u0001\u0900", 2},
+{"\u0001\u0308\u0900", 2},
+{"\u0001\u0903", 2},
+{"\u0001\u0308\u0903", 2},
+{"\u0001\u0904", 2},
+{"\u0001\u0308\u0904", 3},
+{"\u0001\u0D4E", 2},
+{"\u0001\u0308\u0D4E", 3},
+{"\u0001\u0915", 2},
+{"\u0001\u0308\u0915", 3},
+{"\u0001\u231A", 2},
+{"\u0001\u0308\u231A", 3},
+{"\u0001\u0300", 2},
+{"\u0001\u0308\u0300", 2},
+{"\u0001\u093C", 2},
+{"\u0001\u0308\u093C", 2},
+{"\u0001\u094D", 2},
+{"\u0001\u0308\u094D", 2},
+{"\u0001\u200D", 2},
+{"\u0001\u0308\u200D", 2},
+{"\u0001\u0378", 2},
+{"\u0001\u0308\u0378", 3},
+{"\u034F\u0020", 2},
+{"\u034F\u0308\u0020", 2},
+{"\u034F\u000D", 2},
+{"\u034F\u0308\u000D", 2},
+{"\u034F\u000A", 2},
+{"\u034F\u0308\u000A", 2},
+{"\u034F\u0001", 2},
+{"\u034F\u0308\u0001", 2},
+{"\u034F\u034F", 1},
+{"\u034F\u0308\u034F", 1},
+{"\u034F\U0001F1E6", 2},
+{"\u034F\u0308\U0001F1E6", 2},
+{"\u034F\u0600", 2},
+{"\u034F\u0308\u0600", 2},
+{"\u034F\u0A03", 1},
+{"\u034F\u0308\u0A03", 1},
+{"\u034F\u1100", 2},
+{"\u034F\u0308\u1100", 2},
+{"\u034F\u1160", 2},
+{"\u034F\u0308\u1160", 2},
+{"\u034F\u11A8", 2},
+{"\u034F\u0308\u11A8", 2},
+{"\u034F\uAC00", 2},
+{"\u034F\u0308\uAC00", 2},
+{"\u034F\uAC01", 2},
+{"\u034F\u0308\uAC01", 2},
+{"\u034F\u0900", 1},
+{"\u034F\u0308\u0900", 1},
+{"\u034F\u0903", 1},
+{"\u034F\u0308\u0903", 1},
+{"\u034F\u0904", 2},
+{"\u034F\u0308\u0904", 2},
+{"\u034F\u0D4E", 2},
+{"\u034F\u0308\u0D4E", 2},
+{"\u034F\u0915", 2},
+{"\u034F\u0308\u0915", 2},
+{"\u034F\u231A", 2},
+{"\u034F\u0308\u231A", 2},
+{"\u034F\u0300", 1},
+{"\u034F\u0308\u0300", 1},
+{"\u034F\u093C", 1},
+{"\u034F\u0308\u093C", 1},
+{"\u034F\u094D", 1},
+{"\u034F\u0308\u094D", 1},
+{"\u034F\u200D", 1},
+{"\u034F\u0308\u200D", 1},
+{"\u034F\u0378", 2},
+{"\u034F\u0308\u0378", 2},
+{"\U0001F1E6\u0020", 2},
+{"\U0001F1E6\u0308\u0020", 2},
+{"\U0001F1E6\u000D", 2},
+{"\U0001F1E6\u0308\u000D", 2},
+{"\U0001F1E6\u000A", 2},
+{"\U0001F1E6\u0308\u000A", 2},
+{"\U0001F1E6\u0001", 2},
+{"\U0001F1E6\u0308\u0001", 2},
+{"\U0001F1E6\u034F", 1},
+{"\U0001F1E6\u0308\u034F", 1},
+{"\U0001F1E6\U0001F1E6", 1},
+{"\U0001F1E6\u0308\U0001F1E6", 2},
+{"\U0001F1E6\u0600", 2},
+{"\U0001F1E6\u0308\u0600", 2},
+{"\U0001F1E6\u0A03", 1},
+{"\U0001F1E6\u0308\u0A03", 1},
+{"\U0001F1E6\u1100", 2},
+{"\U0001F1E6\u0308\u1100", 2},
+{"\U0001F1E6\u1160", 2},
+{"\U0001F1E6\u0308\u1160", 2},
+{"\U0001F1E6\u11A8", 2},
+{"\U0001F1E6\u0308\u11A8", 2},
+{"\U0001F1E6\uAC00", 2},
+{"\U0001F1E6\u0308\uAC00", 2},
+{"\U0001F1E6\uAC01", 2},
+{"\U0001F1E6\u0308\uAC01", 2},
+{"\U0001F1E6\u0900", 1},
+{"\U0001F1E6\u0308\u0900", 1},
+{"\U0001F1E6\u0903", 1},
+{"\U0001F1E6\u0308\u0903", 1},
+{"\U0001F1E6\u0904", 2},
+{"\U0001F1E6\u0308\u0904", 2},
+{"\U0001F1E6\u0D4E", 2},
+{"\U0001F1E6\u0308\u0D4E", 2},
+{"\U0001F1E6\u0915", 2},
+{"\U0001F1E6\u0308\u0915", 2},
+{"\U0001F1E6\u231A", 2},
+{"\U0001F1E6\u0308\u231A", 2},
+{"\U0001F1E6\u0300", 1},
+{"\U0001F1E6\u0308\u0300", 1},
+{"\U0001F1E6\u093C", 1},
+{"\U0001F1E6\u0308\u093C", 1},
+{"\U0001F1E6\u094D", 1},
+{"\U0001F1E6\u0308\u094D", 1},
+{"\U0001F1E6\u200D", 1},
+{"\U0001F1E6\u0308\u200D", 1},
+{"\U0001F1E6\u0378", 2},
+{"\U0001F1E6\u0308\u0378", 2},
+{"\u0600\u0020", 1},
+{"\u0600\u0308\u0020", 2},
+{"\u0600\u000D", 2},
+{"\u0600\u0308\u000D", 2},
+{"\u0600\u000A", 2},
+{"\u0600\u0308\u000A", 2},
+{"\u0600\u0001", 2},
+{"\u0600\u0308\u0001", 2},
+{"\u0600\u034F", 1},
+{"\u0600\u0308\u034F", 1},
+{"\u0600\U0001F1E6", 1},
+{"\u0600\u0308\U0001F1E6", 2},
+{"\u0600\u0600", 1},
+{"\u0600\u0308\u0600", 2},
+{"\u0600\u0A03", 1},
+{"\u0600\u0308\u0A03", 1},
+{"\u0600\u1100", 1},
+{"\u0600\u0308\u1100", 2},
+{"\u0600\u1160", 1},
+{"\u0600\u0308\u1160", 2},
+{"\u0600\u11A8", 1},
+{"\u0600\u0308\u11A8", 2},
+{"\u0600\uAC00", 1},
+{"\u0600\u0308\uAC00", 2},
+{"\u0600\uAC01", 1},
+{"\u0600\u0308\uAC01", 2},
+{"\u0600\u0900", 1},
+{"\u0600\u0308\u0900", 1},
+{"\u0600\u0903", 1},
+{"\u0600\u0308\u0903", 1},
+{"\u0600\u0904", 1},
+{"\u0600\u0308\u0904", 2},
+{"\u0600\u0D4E", 1},
+{"\u0600\u0308\u0D4E", 2},
+{"\u0600\u0915", 1},
+{"\u0600\u0308\u0915", 2},
+{"\u0600\u231A", 1},
+{"\u0600\u0308\u231A", 2},
+{"\u0600\u0300", 1},
+{"\u0600\u0308\u0300", 1},
+{"\u0600\u093C", 1},
+{"\u0600\u0308\u093C", 1},
+{"\u0600\u094D", 1},
+{"\u0600\u0308\u094D", 1},
+{"\u0600\u200D", 1},
+{"\u0600\u0308\u200D", 1},
+{"\u0600\u0378", 1},
+{"\u0600\u0308\u0378", 2},
+{"\u0A03\u0020", 2},
+{"\u0A03\u0308\u0020", 2},
+{"\u0A03\u000D", 2},
+{"\u0A03\u0308\u000D", 2},
+{"\u0A03\u000A", 2},
+{"\u0A03\u0308\u000A", 2},
+{"\u0A03\u0001", 2},
+{"\u0A03\u0308\u0001", 2},
+{"\u0A03\u034F", 1},
+{"\u0A03\u0308\u034F", 1},
+{"\u0A03\U0001F1E6", 2},
+{"\u0A03\u0308\U0001F1E6", 2},
+{"\u0A03\u0600", 2},
+{"\u0A03\u0308\u0600", 2},
+{"\u0A03\u0A03", 1},
+{"\u0A03\u0308\u0A03", 1},
+{"\u0A03\u1100", 2},
+{"\u0A03\u0308\u1100", 2},
+{"\u0A03\u1160", 2},
+{"\u0A03\u0308\u1160", 2},
+{"\u0A03\u11A8", 2},
+{"\u0A03\u0308\u11A8", 2},
+{"\u0A03\uAC00", 2},
+{"\u0A03\u0308\uAC00", 2},
+{"\u0A03\uAC01", 2},
+{"\u0A03\u0308\uAC01", 2},
+{"\u0A03\u0900", 1},
+{"\u0A03\u0308\u0900", 1},
+{"\u0A03\u0903", 1},
+{"\u0A03\u0308\u0903", 1},
+{"\u0A03\u0904", 2},
+{"\u0A03\u0308\u0904", 2},
+{"\u0A03\u0D4E", 2},
+{"\u0A03\u0308\u0D4E", 2},
+{"\u0A03\u0915", 2},
+{"\u0A03\u0308\u0915", 2},
+{"\u0A03\u231A", 2},
+{"\u0A03\u0308\u231A", 2},
+{"\u0A03\u0300", 1},
+{"\u0A03\u0308\u0300", 1},
+{"\u0A03\u093C", 1},
+{"\u0A03\u0308\u093C", 1},
+{"\u0A03\u094D", 1},
+{"\u0A03\u0308\u094D", 1},
+{"\u0A03\u200D", 1},
+{"\u0A03\u0308\u200D", 1},
+{"\u0A03\u0378", 2},
+{"\u0A03\u0308\u0378", 2},
+{"\u1100\u0020", 2},
+{"\u1100\u0308\u0020", 2},
+{"\u1100\u000D", 2},
+{"\u1100\u0308\u000D", 2},
+{"\u1100\u000A", 2},
+{"\u1100\u0308\u000A", 2},
+{"\u1100\u0001", 2},
+{"\u1100\u0308\u0001", 2},
+{"\u1100\u034F", 1},
+{"\u1100\u0308\u034F", 1},
+{"\u1100\U0001F1E6", 2},
+{"\u1100\u0308\U0001F1E6", 2},
+{"\u1100\u0600", 2},
+{"\u1100\u0308\u0600", 2},
+{"\u1100\u0A03", 1},
+{"\u1100\u0308\u0A03", 1},
+{"\u1100\u1100", 1},
+{"\u1100\u0308\u1100", 2},
+{"\u1100\u1160", 1},
+{"\u1100\u0308\u1160", 2},
+{"\u1100\u11A8", 2},
+{"\u1100\u0308\u11A8", 2},
+{"\u1100\uAC00", 1},
+{"\u1100\u0308\uAC00", 2},
+{"\u1100\uAC01", 1},
+{"\u1100\u0308\uAC01", 2},
+{"\u1100\u0900", 1},
+{"\u1100\u0308\u0900", 1},
+{"\u1100\u0903", 1},
+{"\u1100\u0308\u0903", 1},
+{"\u1100\u0904", 2},
+{"\u1100\u0308\u0904", 2},
+{"\u1100\u0D4E", 2},
+{"\u1100\u0308\u0D4E", 2},
+{"\u1100\u0915", 2},
+{"\u1100\u0308\u0915", 2},
+{"\u1100\u231A", 2},
+{"\u1100\u0308\u231A", 2},
+{"\u1100\u0300", 1},
+{"\u1100\u0308\u0300", 1},
+{"\u1100\u093C", 1},
+{"\u1100\u0308\u093C", 1},
+{"\u1100\u094D", 1},
+{"\u1100\u0308\u094D", 1},
+{"\u1100\u200D", 1},
+{"\u1100\u0308\u200D", 1},
+{"\u1100\u0378", 2},
+{"\u1100\u0308\u0378", 2},
+{"\u1160\u0020", 2},
+{"\u1160\u0308\u0020", 2},
+{"\u1160\u000D", 2},
+{"\u1160\u0308\u000D", 2},
+{"\u1160\u000A", 2},
+{"\u1160\u0308\u000A", 2},
+{"\u1160\u0001", 2},
+{"\u1160\u0308\u0001", 2},
+{"\u1160\u034F", 1},
+{"\u1160\u0308\u034F", 1},
+{"\u1160\U0001F1E6", 2},
+{"\u1160\u0308\U0001F1E6", 2},
+{"\u1160\u0600", 2},
+{"\u1160\u0308\u0600", 2},
+{"\u1160\u0A03", 1},
+{"\u1160\u0308\u0A03", 1},
+{"\u1160\u1100", 2},
+{"\u1160\u0308\u1100", 2},
+{"\u1160\u1160", 1},
+{"\u1160\u0308\u1160", 2},
+{"\u1160\u11A8", 1},
+{"\u1160\u0308\u11A8", 2},
+{"\u1160\uAC00", 2},
+{"\u1160\u0308\uAC00", 2},
+{"\u1160\uAC01", 2},
+{"\u1160\u0308\uAC01", 2},
+{"\u1160\u0900", 1},
+{"\u1160\u0308\u0900", 1},
+{"\u1160\u0903", 1},
+{"\u1160\u0308\u0903", 1},
+{"\u1160\u0904", 2},
+{"\u1160\u0308\u0904", 2},
+{"\u1160\u0D4E", 2},
+{"\u1160\u0308\u0D4E", 2},
+{"\u1160\u0915", 2},
+{"\u1160\u0308\u0915", 2},
+{"\u1160\u231A", 2},
+{"\u1160\u0308\u231A", 2},
+{"\u1160\u0300", 1},
+{"\u1160\u0308\u0300", 1},
+{"\u1160\u093C", 1},
+{"\u1160\u0308\u093C", 1},
+{"\u1160\u094D", 1},
+{"\u1160\u0308\u094D", 1},
+{"\u1160\u200D", 1},
+{"\u1160\u0308\u200D", 1},
+{"\u1160\u0378", 2},
+{"\u1160\u0308\u0378", 2},
+{"\u11A8\u0020", 2},
+{"\u11A8\u0308\u0020", 2},
+{"\u11A8\u000D", 2},
+{"\u11A8\u0308\u000D", 2},
+{"\u11A8\u000A", 2},
+{"\u11A8\u0308\u000A", 2},
+{"\u11A8\u0001", 2},
+{"\u11A8\u0308\u0001", 2},
+{"\u11A8\u034F", 1},
+{"\u11A8\u0308\u034F", 1},
+{"\u11A8\U0001F1E6", 2},
+{"\u11A8\u0308\U0001F1E6", 2},
+{"\u11A8\u0600", 2},
+{"\u11A8\u0308\u0600", 2},
+{"\u11A8\u0A03", 1},
+{"\u11A8\u0308\u0A03", 1},
+{"\u11A8\u1100", 2},
+{"\u11A8\u0308\u1100", 2},
+{"\u11A8\u1160", 2},
+{"\u11A8\u0308\u1160", 2},
+{"\u11A8\u11A8", 1},
+{"\u11A8\u0308\u11A8", 2},
+{"\u11A8\uAC00", 2},
+{"\u11A8\u0308\uAC00", 2},
+{"\u11A8\uAC01", 2},
+{"\u11A8\u0308\uAC01", 2},
+{"\u11A8\u0900", 1},
+{"\u11A8\u0308\u0900", 1},
+{"\u11A8\u0903", 1},
+{"\u11A8\u0308\u0903", 1},
+{"\u11A8\u0904", 2},
+{"\u11A8\u0308\u0904", 2},
+{"\u11A8\u0D4E", 2},
+{"\u11A8\u0308\u0D4E", 2},
+{"\u11A8\u0915", 2},
+{"\u11A8\u0308\u0915", 2},
+{"\u11A8\u231A", 2},
+{"\u11A8\u0308\u231A", 2},
+{"\u11A8\u0300", 1},
+{"\u11A8\u0308\u0300", 1},
+{"\u11A8\u093C", 1},
+{"\u11A8\u0308\u093C", 1},
+{"\u11A8\u094D", 1},
+{"\u11A8\u0308\u094D", 1},
+{"\u11A8\u200D", 1},
+{"\u11A8\u0308\u200D", 1},
+{"\u11A8\u0378", 2},
+{"\u11A8\u0308\u0378", 2},
+{"\uAC00\u0020", 2},
+{"\uAC00\u0308\u0020", 2},
+{"\uAC00\u000D", 2},
+{"\uAC00\u0308\u000D", 2},
+{"\uAC00\u000A", 2},
+{"\uAC00\u0308\u000A", 2},
+{"\uAC00\u0001", 2},
+{"\uAC00\u0308\u0001", 2},
+{"\uAC00\u034F", 1},
+{"\uAC00\u0308\u034F", 1},
+{"\uAC00\U0001F1E6", 2},
+{"\uAC00\u0308\U0001F1E6", 2},
+{"\uAC00\u0600", 2},
+{"\uAC00\u0308\u0600", 2},
+{"\uAC00\u0A03", 1},
+{"\uAC00\u0308\u0A03", 1},
+{"\uAC00\u1100", 2},
+{"\uAC00\u0308\u1100", 2},
+{"\uAC00\u1160", 1},
+{"\uAC00\u0308\u1160", 2},
+{"\uAC00\u11A8", 1},
+{"\uAC00\u0308\u11A8", 2},
+{"\uAC00\uAC00", 2},
+{"\uAC00\u0308\uAC00", 2},
+{"\uAC00\uAC01", 2},
+{"\uAC00\u0308\uAC01", 2},
+{"\uAC00\u0900", 1},
+{"\uAC00\u0308\u0900", 1},
+{"\uAC00\u0903", 1},
+{"\uAC00\u0308\u0903", 1},
+{"\uAC00\u0904", 2},
+{"\uAC00\u0308\u0904", 2},
+{"\uAC00\u0D4E", 2},
+{"\uAC00\u0308\u0D4E", 2},
+{"\uAC00\u0915", 2},
+{"\uAC00\u0308\u0915", 2},
+{"\uAC00\u231A", 2},
+{"\uAC00\u0308\u231A", 2},
+{"\uAC00\u0300", 1},
+{"\uAC00\u0308\u0300", 1},
+{"\uAC00\u093C", 1},
+{"\uAC00\u0308\u093C", 1},
+{"\uAC00\u094D", 1},
+{"\uAC00\u0308\u094D", 1},
+{"\uAC00\u200D", 1},
+{"\uAC00\u0308\u200D", 1},
+{"\uAC00\u0378", 2},
+{"\uAC00\u0308\u0378", 2},
+{"\uAC01\u0020", 2},
+{"\uAC01\u0308\u0020", 2},
+{"\uAC01\u000D", 2},
+{"\uAC01\u0308\u000D", 2},
+{"\uAC01\u000A", 2},
+{"\uAC01\u0308\u000A", 2},
+{"\uAC01\u0001", 2},
+{"\uAC01\u0308\u0001", 2},
+{"\uAC01\u034F", 1},
+{"\uAC01\u0308\u034F", 1},
+{"\uAC01\U0001F1E6", 2},
+{"\uAC01\u0308\U0001F1E6", 2},
+{"\uAC01\u0600", 2},
+{"\uAC01\u0308\u0600", 2},
+{"\uAC01\u0A03", 1},
+{"\uAC01\u0308\u0A03", 1},
+{"\uAC01\u1100", 2},
+{"\uAC01\u0308\u1100", 2},
+{"\uAC01\u1160", 2},
+{"\uAC01\u0308\u1160", 2},
+{"\uAC01\u11A8", 1},
+{"\uAC01\u0308\u11A8", 2},
+{"\uAC01\uAC00", 2},
+{"\uAC01\u0308\uAC00", 2},
+{"\uAC01\uAC01", 2},
+{"\uAC01\u0308\uAC01", 2},
+{"\uAC01\u0900", 1},
+{"\uAC01\u0308\u0900", 1},
+{"\uAC01\u0903", 1},
+{"\uAC01\u0308\u0903", 1},
+{"\uAC01\u0904", 2},
+{"\uAC01\u0308\u0904", 2},
+{"\uAC01\u0D4E", 2},
+{"\uAC01\u0308\u0D4E", 2},
+{"\uAC01\u0915", 2},
+{"\uAC01\u0308\u0915", 2},
+{"\uAC01\u231A", 2},
+{"\uAC01\u0308\u231A", 2},
+{"\uAC01\u0300", 1},
+{"\uAC01\u0308\u0300", 1},
+{"\uAC01\u093C", 1},
+{"\uAC01\u0308\u093C", 1},
+{"\uAC01\u094D", 1},
+{"\uAC01\u0308\u094D", 1},
+{"\uAC01\u200D", 1},
+{"\uAC01\u0308\u200D", 1},
+{"\uAC01\u0378", 2},
+{"\uAC01\u0308\u0378", 2},
+{"\u0900\u0020", 2},
+{"\u0900\u0308\u0020", 2},
+{"\u0900\u000D", 2},
+{"\u0900\u0308\u000D", 2},
+{"\u0900\u000A", 2},
+{"\u0900\u0308\u000A", 2},
+{"\u0900\u0001", 2},
+{"\u0900\u0308\u0001", 2},
+{"\u0900\u034F", 1},
+{"\u0900\u0308\u034F", 1},
+{"\u0900\U0001F1E6", 2},
+{"\u0900\u0308\U0001F1E6", 2},
+{"\u0900\u0600", 2},
+{"\u0900\u0308\u0600", 2},
+{"\u0900\u0A03", 1},
+{"\u0900\u0308\u0A03", 1},
+{"\u0900\u1100", 2},
+{"\u0900\u0308\u1100", 2},
+{"\u0900\u1160", 2},
+{"\u0900\u0308\u1160", 2},
+{"\u0900\u11A8", 2},
+{"\u0900\u0308\u11A8", 2},
+{"\u0900\uAC00", 2},
+{"\u0900\u0308\uAC00", 2},
+{"\u0900\uAC01", 2},
+{"\u0900\u0308\uAC01", 2},
+{"\u0900\u0900", 1},
+{"\u0900\u0308\u0900", 1},
+{"\u0900\u0903", 1},
+{"\u0900\u0308\u0903", 1},
+{"\u0900\u0904", 2},
+{"\u0900\u0308\u0904", 2},
+{"\u0900\u0D4E", 2},
+{"\u0900\u0308\u0D4E", 2},
+{"\u0900\u0915", 2},
+{"\u0900\u0308\u0915", 2},
+{"\u0900\u231A", 2},
+{"\u0900\u0308\u231A", 2},
+{"\u0900\u0300", 1},
+{"\u0900\u0308\u0300", 1},
+{"\u0900\u093C", 1},
+{"\u0900\u0308\u093C", 1},
+{"\u0900\u094D", 1},
+{"\u0900\u0308\u094D", 1},
+{"\u0900\u200D", 1},
+{"\u0900\u0308\u200D", 1},
+{"\u0900\u0378", 2},
+{"\u0900\u0308\u0378", 2},
+{"\u0903\u0020", 2},
+{"\u0903\u0308\u0020", 2},
+{"\u0903\u000D", 2},
+{"\u0903\u0308\u000D", 2},
+{"\u0903\u000A", 2},
+{"\u0903\u0308\u000A", 2},
+{"\u0903\u0001", 2},
+{"\u0903\u0308\u0001", 2},
+{"\u0903\u034F", 1},
+{"\u0903\u0308\u034F", 1},
+{"\u0903\U0001F1E6", 2},
+{"\u0903\u0308\U0001F1E6", 2},
+{"\u0903\u0600", 2},
+{"\u0903\u0308\u0600", 2},
+{"\u0903\u0A03", 1},
+{"\u0903\u0308\u0A03", 1},
+{"\u0903\u1100", 2},
+{"\u0903\u0308\u1100", 2},
+{"\u0903\u1160", 2},
+{"\u0903\u0308\u1160", 2},
+{"\u0903\u11A8", 2},
+{"\u0903\u0308\u11A8", 2},
+{"\u0903\uAC00", 2},
+{"\u0903\u0308\uAC00", 2},
+{"\u0903\uAC01", 2},
+{"\u0903\u0308\uAC01", 2},
+{"\u0903\u0900", 1},
+{"\u0903\u0308\u0900", 1},
+{"\u0903\u0903", 1},
+{"\u0903\u0308\u0903", 1},
+{"\u0903\u0904", 2},
+{"\u0903\u0308\u0904", 2},
+{"\u0903\u0D4E", 2},
+{"\u0903\u0308\u0D4E", 2},
+{"\u0903\u0915", 2},
+{"\u0903\u0308\u0915", 2},
+{"\u0903\u231A", 2},
+{"\u0903\u0308\u231A", 2},
+{"\u0903\u0300", 1},
+{"\u0903\u0308\u0300", 1},
+{"\u0903\u093C", 1},
+{"\u0903\u0308\u093C", 1},
+{"\u0903\u094D", 1},
+{"\u0903\u0308\u094D", 1},
+{"\u0903\u200D", 1},
+{"\u0903\u0308\u200D", 1},
+{"\u0903\u0378", 2},
+{"\u0903\u0308\u0378", 2},
+{"\u0904\u0020", 2},
+{"\u0904\u0308\u0020", 2},
+{"\u0904\u000D", 2},
+{"\u0904\u0308\u000D", 2},
+{"\u0904\u000A", 2},
+{"\u0904\u0308\u000A", 2},
+{"\u0904\u0001", 2},
+{"\u0904\u0308\u0001", 2},
+{"\u0904\u034F", 1},
+{"\u0904\u0308\u034F", 1},
+{"\u0904\U0001F1E6", 2},
+{"\u0904\u0308\U0001F1E6", 2},
+{"\u0904\u0600", 2},
+{"\u0904\u0308\u0600", 2},
+{"\u0904\u0A03", 1},
+{"\u0904\u0308\u0A03", 1},
+{"\u0904\u1100", 2},
+{"\u0904\u0308\u1100", 2},
+{"\u0904\u1160", 2},
+{"\u0904\u0308\u1160", 2},
+{"\u0904\u11A8", 2},
+{"\u0904\u0308\u11A8", 2},
+{"\u0904\uAC00", 2},
+{"\u0904\u0308\uAC00", 2},
+{"\u0904\uAC01", 2},
+{"\u0904\u0308\uAC01", 2},
+{"\u0904\u0900", 1},
+{"\u0904\u0308\u0900", 1},
+{"\u0904\u0903", 1},
+{"\u0904\u0308\u0903", 1},
+{"\u0904\u0904", 2},
+{"\u0904\u0308\u0904", 2},
+{"\u0904\u0D4E", 2},
+{"\u0904\u0308\u0D4E", 2},
+{"\u0904\u0915", 2},
+{"\u0904\u0308\u0915", 2},
+{"\u0904\u231A", 2},
+{"\u0904\u0308\u231A", 2},
+{"\u0904\u0300", 1},
+{"\u0904\u0308\u0300", 1},
+{"\u0904\u093C", 1},
+{"\u0904\u0308\u093C", 1},
+{"\u0904\u094D", 1},
+{"\u0904\u0308\u094D", 1},
+{"\u0904\u200D", 1},
+{"\u0904\u0308\u200D", 1},
+{"\u0904\u0378", 2},
+{"\u0904\u0308\u0378", 2},
+{"\u0D4E\u0020", 1},
+{"\u0D4E\u0308\u0020", 2},
+{"\u0D4E\u000D", 2},
+{"\u0D4E\u0308\u000D", 2},
+{"\u0D4E\u000A", 2},
+{"\u0D4E\u0308\u000A", 2},
+{"\u0D4E\u0001", 2},
+{"\u0D4E\u0308\u0001", 2},
+{"\u0D4E\u034F", 1},
+{"\u0D4E\u0308\u034F", 1},
+{"\u0D4E\U0001F1E6", 1},
+{"\u0D4E\u0308\U0001F1E6", 2},
+{"\u0D4E\u0600", 1},
+{"\u0D4E\u0308\u0600", 2},
+{"\u0D4E\u0A03", 1},
+{"\u0D4E\u0308\u0A03", 1},
+{"\u0D4E\u1100", 1},
+{"\u0D4E\u0308\u1100", 2},
+{"\u0D4E\u1160", 1},
+{"\u0D4E\u0308\u1160", 2},
+{"\u0D4E\u11A8", 1},
+{"\u0D4E\u0308\u11A8", 2},
+{"\u0D4E\uAC00", 1},
+{"\u0D4E\u0308\uAC00", 2},
+{"\u0D4E\uAC01", 1},
+{"\u0D4E\u0308\uAC01", 2},
+{"\u0D4E\u0900", 1},
+{"\u0D4E\u0308\u0900", 1},
+{"\u0D4E\u0903", 1},
+{"\u0D4E\u0308\u0903", 1},
+{"\u0D4E\u0904", 1},
+{"\u0D4E\u0308\u0904", 2},
+{"\u0D4E\u0D4E", 1},
+{"\u0D4E\u0308\u0D4E", 2},
+{"\u0D4E\u0915", 1},
+{"\u0D4E\u0308\u0915", 2},
+{"\u0D4E\u231A", 1},
+{"\u0D4E\u0308\u231A", 2},
+{"\u0D4E\u0300", 1},
+{"\u0D4E\u0308\u0300", 1},
+{"\u0D4E\u093C", 1},
+{"\u0D4E\u0308\u093C", 1},
+{"\u0D4E\u094D", 1},
+{"\u0D4E\u0308\u094D", 1},
+{"\u0D4E\u200D", 1},
+{"\u0D4E\u0308\u200D", 1},
+{"\u0D4E\u0378", 1},
+{"\u0D4E\u0308\u0378", 2},
+{"\u0915\u0020", 2},
+{"\u0915\u0308\u0020", 2},
+{"\u0915\u000D", 2},
+{"\u0915\u0308\u000D", 2},
+{"\u0915\u000A", 2},
+{"\u0915\u0308\u000A", 2},
+{"\u0915\u0001", 2},
+{"\u0915\u0308\u0001", 2},
+{"\u0915\u034F", 1},
+{"\u0915\u0308\u034F", 1},
+{"\u0915\U0001F1E6", 2},
+{"\u0915\u0308\U0001F1E6", 2},
+{"\u0915\u0600", 2},
+{"\u0915\u0308\u0600", 2},
+{"\u0915\u0A03", 1},
+{"\u0915\u0308\u0A03", 1},
+{"\u0915\u1100", 2},
+{"\u0915\u0308\u1100", 2},
+{"\u0915\u1160", 2},
+{"\u0915\u0308\u1160", 2},
+{"\u0915\u11A8", 2},
+{"\u0915\u0308\u11A8", 2},
+{"\u0915\uAC00", 2},
+{"\u0915\u0308\uAC00", 2},
+{"\u0915\uAC01", 2},
+{"\u0915\u0308\uAC01", 2},
+{"\u0915\u0900", 1},
+{"\u0915\u0308\u0900", 1},
+{"\u0915\u0903", 1},
+{"\u0915\u0308\u0903", 1},
+{"\u0915\u0904", 2},
+{"\u0915\u0308\u0904", 2},
+{"\u0915\u0D4E", 2},
+{"\u0915\u0308\u0D4E", 2},
+{"\u0915\u0915", 2},
+{"\u0915\u0308\u0915", 2},
+{"\u0915\u231A", 2},
+{"\u0915\u0308\u231A", 2},
+{"\u0915\u0300", 1},
+{"\u0915\u0308\u0300", 1},
+{"\u0915\u093C", 1},
+{"\u0915\u0308\u093C", 1},
+{"\u0915\u094D", 1},
+{"\u0915\u0308\u094D", 1},
+{"\u0915\u200D", 1},
+{"\u0915\u0308\u200D", 1},
+{"\u0915\u0378", 2},
+{"\u0915\u0308\u0378", 2},
+{"\u231A\u0020", 2},
+{"\u231A\u0308\u0020", 2},
+{"\u231A\u000D", 2},
+{"\u231A\u0308\u000D", 2},
+{"\u231A\u000A", 2},
+{"\u231A\u0308\u000A", 2},
+{"\u231A\u0001", 2},
+{"\u231A\u0308\u0001", 2},
+{"\u231A\u034F", 1},
+{"\u231A\u0308\u034F", 1},
+{"\u231A\U0001F1E6", 2},
+{"\u231A\u0308\U0001F1E6", 2},
+{"\u231A\u0600", 2},
+{"\u231A\u0308\u0600", 2},
+{"\u231A\u0A03", 1},
+{"\u231A\u0308\u0A03", 1},
+{"\u231A\u1100", 2},
+{"\u231A\u0308\u1100", 2},
+{"\u231A\u1160", 2},
+{"\u231A\u0308\u1160", 2},
+{"\u231A\u11A8", 2},
+{"\u231A\u0308\u11A8", 2},
+{"\u231A\uAC00", 2},
+{"\u231A\u0308\uAC00", 2},
+{"\u231A\uAC01", 2},
+{"\u231A\u0308\uAC01", 2},
+{"\u231A\u0900", 1},
+{"\u231A\u0308\u0900", 1},
+{"\u231A\u0903", 1},
+{"\u231A\u0308\u0903", 1},
+{"\u231A\u0904", 2},
+{"\u231A\u0308\u0904", 2},
+{"\u231A\u0D4E", 2},
+{"\u231A\u0308\u0D4E", 2},
+{"\u231A\u0915", 2},
+{"\u231A\u0308\u0915", 2},
+{"\u231A\u231A", 2},
+{"\u231A\u0308\u231A", 2},
+{"\u231A\u0300", 1},
+{"\u231A\u0308\u0300", 1},
+{"\u231A\u093C", 1},
+{"\u231A\u0308\u093C", 1},
+{"\u231A\u094D", 1},
+{"\u231A\u0308\u094D", 1},
+{"\u231A\u200D", 1},
+{"\u231A\u0308\u200D", 1},
+{"\u231A\u0378", 2},
+{"\u231A\u0308\u0378", 2},
+{"\u0300\u0020", 2},
+{"\u0300\u0308\u0020", 2},
+{"\u0300\u000D", 2},
+{"\u0300\u0308\u000D", 2},
+{"\u0300\u000A", 2},
+{"\u0300\u0308\u000A", 2},
+{"\u0300\u0001", 2},
+{"\u0300\u0308\u0001", 2},
+{"\u0300\u034F", 1},
+{"\u0300\u0308\u034F", 1},
+{"\u0300\U0001F1E6", 2},
+{"\u0300\u0308\U0001F1E6", 2},
+{"\u0300\u0600", 2},
+{"\u0300\u0308\u0600", 2},
+{"\u0300\u0A03", 1},
+{"\u0300\u0308\u0A03", 1},
+{"\u0300\u1100", 2},
+{"\u0300\u0308\u1100", 2},
+{"\u0300\u1160", 2},
+{"\u0300\u0308\u1160", 2},
+{"\u0300\u11A8", 2},
+{"\u0300\u0308\u11A8", 2},
+{"\u0300\uAC00", 2},
+{"\u0300\u0308\uAC00", 2},
+{"\u0300\uAC01", 2},
+{"\u0300\u0308\uAC01", 2},
+{"\u0300\u0900", 1},
+{"\u0300\u0308\u0900", 1},
+{"\u0300\u0903", 1},
+{"\u0300\u0308\u0903", 1},
+{"\u0300\u0904", 2},
+{"\u0300\u0308\u0904", 2},
+{"\u0300\u0D4E", 2},
+{"\u0300\u0308\u0D4E", 2},
+{"\u0300\u0915", 2},
+{"\u0300\u0308\u0915", 2},
+{"\u0300\u231A", 2},
+{"\u0300\u0308\u231A", 2},
+{"\u0300\u0300", 1},
+{"\u0300\u0308\u0300", 1},
+{"\u0300\u093C", 1},
+{"\u0300\u0308\u093C", 1},
+{"\u0300\u094D", 1},
+{"\u0300\u0308\u094D", 1},
+{"\u0300\u200D", 1},
+{"\u0300\u0308\u200D", 1},
+{"\u0300\u0378", 2},
+{"\u0300\u0308\u0378", 2},
+{"\u093C\u0020", 2},
+{"\u093C\u0308\u0020", 2},
+{"\u093C\u000D", 2},
+{"\u093C\u0308\u000D", 2},
+{"\u093C\u000A", 2},
+{"\u093C\u0308\u000A", 2},
+{"\u093C\u0001", 2},
+{"\u093C\u0308\u0001", 2},
+{"\u093C\u034F", 1},
+{"\u093C\u0308\u034F", 1},
+{"\u093C\U0001F1E6", 2},
+{"\u093C\u0308\U0001F1E6", 2},
+{"\u093C\u0600", 2},
+{"\u093C\u0308\u0600", 2},
+{"\u093C\u0A03", 1},
+{"\u093C\u0308\u0A03", 1},
+{"\u093C\u1100", 2},
+{"\u093C\u0308\u1100", 2},
+{"\u093C\u1160", 2},
+{"\u093C\u0308\u1160", 2},
+{"\u093C\u11A8", 2},
+{"\u093C\u0308\u11A8", 2},
+{"\u093C\uAC00", 2},
+{"\u093C\u0308\uAC00", 2},
+{"\u093C\uAC01", 2},
+{"\u093C\u0308\uAC01", 2},
+{"\u093C\u0900", 1},
+{"\u093C\u0308\u0900", 1},
+{"\u093C\u0903", 1},
+{"\u093C\u0308\u0903", 1},
+{"\u093C\u0904", 2},
+{"\u093C\u0308\u0904", 2},
+{"\u093C\u0D4E", 2},
+{"\u093C\u0308\u0D4E", 2},
+{"\u093C\u0915", 2},
+{"\u093C\u0308\u0915", 2},
+{"\u093C\u231A", 2},
+{"\u093C\u0308\u231A", 2},
+{"\u093C\u0300", 1},
+{"\u093C\u0308\u0300", 1},
+{"\u093C\u093C", 1},
+{"\u093C\u0308\u093C", 1},
+{"\u093C\u094D", 1},
+{"\u093C\u0308\u094D", 1},
+{"\u093C\u200D", 1},
+{"\u093C\u0308\u200D", 1},
+{"\u093C\u0378", 2},
+{"\u093C\u0308\u0378", 2},
+{"\u094D\u0020", 2},
+{"\u094D\u0308\u0020", 2},
+{"\u094D\u000D", 2},
+{"\u094D\u0308\u000D", 2},
+{"\u094D\u000A", 2},
+{"\u094D\u0308\u000A", 2},
+{"\u094D\u0001", 2},
+{"\u094D\u0308\u0001", 2},
+{"\u094D\u034F", 1},
+{"\u094D\u0308\u034F", 1},
+{"\u094D\U0001F1E6", 2},
+{"\u094D\u0308\U0001F1E6", 2},
+{"\u094D\u0600", 2},
+{"\u094D\u0308\u0600", 2},
+{"\u094D\u0A03", 1},
+{"\u094D\u0308\u0A03", 1},
+{"\u094D\u1100", 2},
+{"\u094D\u0308\u1100", 2},
+{"\u094D\u1160", 2},
+{"\u094D\u0308\u1160", 2},
+{"\u094D\u11A8", 2},
+{"\u094D\u0308\u11A8", 2},
+{"\u094D\uAC00", 2},
+{"\u094D\u0308\uAC00", 2},
+{"\u094D\uAC01", 2},
+{"\u094D\u0308\uAC01", 2},
+{"\u094D\u0900", 1},
+{"\u094D\u0308\u0900", 1},
+{"\u094D\u0903", 1},
+{"\u094D\u0308\u0903", 1},
+{"\u094D\u0904", 2},
+{"\u094D\u0308\u0904", 2},
+{"\u094D\u0D4E", 2},
+{"\u094D\u0308\u0D4E", 2},
+{"\u094D\u0915", 2},
+{"\u094D\u0308\u0915", 2},
+{"\u094D\u231A", 2},
+{"\u094D\u0308\u231A", 2},
+{"\u094D\u0300", 1},
+{"\u094D\u0308\u0300", 1},
+{"\u094D\u093C", 1},
+{"\u094D\u0308\u093C", 1},
+{"\u094D\u094D", 1},
+{"\u094D\u0308\u094D", 1},
+{"\u094D\u200D", 1},
+{"\u094D\u0308\u200D", 1},
+{"\u094D\u0378", 2},
+{"\u094D\u0308\u0378", 2},
+{"\u200D\u0020", 2},
+{"\u200D\u0308\u0020", 2},
+{"\u200D\u000D", 2},
+{"\u200D\u0308\u000D", 2},
+{"\u200D\u000A", 2},
+{"\u200D\u0308\u000A", 2},
+{"\u200D\u0001", 2},
+{"\u200D\u0308\u0001", 2},
+{"\u200D\u034F", 1},
+{"\u200D\u0308\u034F", 1},
+{"\u200D\U0001F1E6", 2},
+{"\u200D\u0308\U0001F1E6", 2},
+{"\u200D\u0600", 2},
+{"\u200D\u0308\u0600", 2},
+{"\u200D\u0A03", 1},
+{"\u200D\u0308\u0A03", 1},
+{"\u200D\u1100", 2},
+{"\u200D\u0308\u1100", 2},
+{"\u200D\u1160", 2},
+{"\u200D\u0308\u1160", 2},
+{"\u200D\u11A8", 2},
+{"\u200D\u0308\u11A8", 2},
+{"\u200D\uAC00", 2},
+{"\u200D\u0308\uAC00", 2},
+{"\u200D\uAC01", 2},
+{"\u200D\u0308\uAC01", 2},
+{"\u200D\u0900", 1},
+{"\u200D\u0308\u0900", 1},
+{"\u200D\u0903", 1},
+{"\u200D\u0308\u0903", 1},
+{"\u200D\u0904", 2},
+{"\u200D\u0308\u0904", 2},
+{"\u200D\u0D4E", 2},
+{"\u200D\u0308\u0D4E", 2},
+{"\u200D\u0915", 2},
+{"\u200D\u0308\u0915", 2},
+{"\u200D\u231A", 2},
+{"\u200D\u0308\u231A", 2},
+{"\u200D\u0300", 1},
+{"\u200D\u0308\u0300", 1},
+{"\u200D\u093C", 1},
+{"\u200D\u0308\u093C", 1},
+{"\u200D\u094D", 1},
+{"\u200D\u0308\u094D", 1},
+{"\u200D\u200D", 1},
+{"\u200D\u0308\u200D", 1},
+{"\u200D\u0378", 2},
+{"\u200D\u0308\u0378", 2},
+{"\u0378\u0020", 2},
+{"\u0378\u0308\u0020", 2},
+{"\u0378\u000D", 2},
+{"\u0378\u0308\u000D", 2},
+{"\u0378\u000A", 2},
+{"\u0378\u0308\u000A", 2},
+{"\u0378\u0001", 2},
+{"\u0378\u0308\u0001", 2},
+{"\u0378\u034F", 1},
+{"\u0378\u0308\u034F", 1},
+{"\u0378\U0001F1E6", 2},
+{"\u0378\u0308\U0001F1E6", 2},
+{"\u0378\u0600", 2},
+{"\u0378\u0308\u0600", 2},
+{"\u0378\u0A03", 1},
+{"\u0378\u0308\u0A03", 1},
+{"\u0378\u1100", 2},
+{"\u0378\u0308\u1100", 2},
+{"\u0378\u1160", 2},
+{"\u0378\u0308\u1160", 2},
+{"\u0378\u11A8", 2},
+{"\u0378\u0308\u11A8", 2},
+{"\u0378\uAC00", 2},
+{"\u0378\u0308\uAC00", 2},
+{"\u0378\uAC01", 2},
+{"\u0378\u0308\uAC01", 2},
+{"\u0378\u0900", 1},
+{"\u0378\u0308\u0900", 1},
+{"\u0378\u0903", 1},
+{"\u0378\u0308\u0903", 1},
+{"\u0378\u0904", 2},
+{"\u0378\u0308\u0904", 2},
+{"\u0378\u0D4E", 2},
+{"\u0378\u0308\u0D4E", 2},
+{"\u0378\u0915", 2},
+{"\u0378\u0308\u0915", 2},
+{"\u0378\u231A", 2},
+{"\u0378\u0308\u231A", 2},
+{"\u0378\u0300", 1},
+{"\u0378\u0308\u0300", 1},
+{"\u0378\u093C", 1},
+{"\u0378\u0308\u093C", 1},
+{"\u0378\u094D", 1},
+{"\u0378\u0308\u094D", 1},
+{"\u0378\u200D", 1},
+{"\u0378\u0308\u200D", 1},
+{"\u0378\u0378", 2},
+{"\u0378\u0308\u0378", 2},
+{"\u000D\u000A\u0061\u000A\u0308", 4},
+{"\u0061\u0308", 1},
+{"\u0020\u200D\u0646", 2},
+{"\u0646\u200D\u0020", 2},
+{"\u1100\u1100", 1},
+{"\uAC00\u11A8\u1100", 2},
+{"\uAC01\u11A8\u1100", 2},
+{"\U0001F1E6\U0001F1E7\U0001F1E8\u0062", 3},
+{"\u0061\U0001F1E6\U0001F1E7\U0001F1E8\u0062", 4},
+{"\u0061\U0001F1E6\U0001F1E7\u200D\U0001F1E8\u0062", 4},
+{"\u0061\U0001F1E6\u200D\U0001F1E7\U0001F1E8\u0062", 4},
+{"\u0061\U0001F1E6\U0001F1E7\U0001F1E8\U0001F1E9\u0062", 4},
+{"\u0061\u200D", 1},
+{"\u0061\u0308\u0062", 2},
+{"\u0061\u0903\u0062", 2},
+{"\u0061\u0600\u0062", 2},
+{"\U0001F476\U0001F3FF\U0001F476", 2},
+{"\u0061\U0001F3FF\U0001F476", 2},
+{"\u0061\U0001F3FF\U0001F476\u200D\U0001F6D1", 2},
+{"\U0001F476\U0001F3FF\u0308\u200D\U0001F476\U0001F3FF", 1},
+{"\U0001F6D1\u200D\U0001F6D1", 1},
+{"\u0061\u200D\U0001F6D1", 2},
+{"\u2701\u200D\u2701", 1},
+{"\u0061\u200D\u2701", 2},
+{"\u0915\u0924", 2},
+{"\u0915\u094D\u0924", 1},
+{"\u0915\u094D\u094D\u0924", 1},
+{"\u0915\u094D\u200D\u0924", 1},
+{"\u0915\u093C\u200D\u094D\u0924", 1},
+{"\u0915\u093C\u094D\u200D\u0924", 1},
+{"\u0915\u094D\u0924\u094D\u092F", 1},
+{"\u0915\u094D\u0061", 2},
+{"\u0061\u094D\u0924", 2},
+{"\u003F\u094D\u0924", 2},
+{"\u0915\u094D\u094D\u0924", 1},
+}
+
+// (Note that the test cases below only include the multi-codepoint cases
+//  listed in the file, as these were the only relevant ones for counting
+//  grapheme cluster boundaries.)
+//
+// https://unicode.org/Public/emoji/15.1/emoji-test.txt
+//
+// emoji-test.txt
+// Date: 2023-06-05, 21:39:54 GMT
+// © 2023 Unicode®, Inc.
+// Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the U.S. and other countries.
+// For terms of use, see https://www.unicode.org/terms_of_use.html
+@(rodata)
+official_emoji_test_cases := []Test_Case {
+{"\u263A\uFE0F", 1},
+{"\U0001F636\u200D\U0001F32B\uFE0F", 1},
+{"\U0001F636\u200D\U0001F32B", 1},
+{"\U0001F62E\u200D\U0001F4A8", 1},
+{"\U0001F642\u200D\u2194\uFE0F", 1},
+{"\U0001F642\u200D\u2194", 1},
+{"\U0001F642\u200D\u2195\uFE0F", 1},
+{"\U0001F642\u200D\u2195", 1},
+{"\U0001F635\u200D\U0001F4AB", 1},
+{"\u2639\uFE0F", 1},
+{"\u2620\uFE0F", 1},
+{"\u2763\uFE0F", 1},
+{"\u2764\uFE0F\u200D\U0001F525", 1},
+{"\u2764\u200D\U0001F525", 1},
+{"\u2764\uFE0F\u200D\U0001FA79", 1},
+{"\u2764\u200D\U0001FA79", 1},
+{"\u2764\uFE0F", 1},
+{"\U0001F573\uFE0F", 1},
+{"\U0001F441\uFE0F\u200D\U0001F5E8\uFE0F", 1},
+{"\U0001F441\u200D\U0001F5E8\uFE0F", 1},
+{"\U0001F441\uFE0F\u200D\U0001F5E8", 1},
+{"\U0001F441\u200D\U0001F5E8", 1},
+{"\U0001F5E8\uFE0F", 1},
+{"\U0001F5EF\uFE0F", 1},
+{"\U0001F44B\U0001F3FB", 1},
+{"\U0001F44B\U0001F3FC", 1},
+{"\U0001F44B\U0001F3FD", 1},
+{"\U0001F44B\U0001F3FE", 1},
+{"\U0001F44B\U0001F3FF", 1},
+{"\U0001F91A\U0001F3FB", 1},
+{"\U0001F91A\U0001F3FC", 1},
+{"\U0001F91A\U0001F3FD", 1},
+{"\U0001F91A\U0001F3FE", 1},
+{"\U0001F91A\U0001F3FF", 1},
+{"\U0001F590\uFE0F", 1},
+{"\U0001F590\U0001F3FB", 1},
+{"\U0001F590\U0001F3FC", 1},
+{"\U0001F590\U0001F3FD", 1},
+{"\U0001F590\U0001F3FE", 1},
+{"\U0001F590\U0001F3FF", 1},
+{"\u270B\U0001F3FB", 1},
+{"\u270B\U0001F3FC", 1},
+{"\u270B\U0001F3FD", 1},
+{"\u270B\U0001F3FE", 1},
+{"\u270B\U0001F3FF", 1},
+{"\U0001F596\U0001F3FB", 1},
+{"\U0001F596\U0001F3FC", 1},
+{"\U0001F596\U0001F3FD", 1},
+{"\U0001F596\U0001F3FE", 1},
+{"\U0001F596\U0001F3FF", 1},
+{"\U0001FAF1\U0001F3FB", 1},
+{"\U0001FAF1\U0001F3FC", 1},
+{"\U0001FAF1\U0001F3FD", 1},
+{"\U0001FAF1\U0001F3FE", 1},
+{"\U0001FAF1\U0001F3FF", 1},
+{"\U0001FAF2\U0001F3FB", 1},
+{"\U0001FAF2\U0001F3FC", 1},
+{"\U0001FAF2\U0001F3FD", 1},
+{"\U0001FAF2\U0001F3FE", 1},
+{"\U0001FAF2\U0001F3FF", 1},
+{"\U0001FAF3\U0001F3FB", 1},
+{"\U0001FAF3\U0001F3FC", 1},
+{"\U0001FAF3\U0001F3FD", 1},
+{"\U0001FAF3\U0001F3FE", 1},
+{"\U0001FAF3\U0001F3FF", 1},
+{"\U0001FAF4\U0001F3FB", 1},
+{"\U0001FAF4\U0001F3FC", 1},
+{"\U0001FAF4\U0001F3FD", 1},
+{"\U0001FAF4\U0001F3FE", 1},
+{"\U0001FAF4\U0001F3FF", 1},
+{"\U0001FAF7\U0001F3FB", 1},
+{"\U0001FAF7\U0001F3FC", 1},
+{"\U0001FAF7\U0001F3FD", 1},
+{"\U0001FAF7\U0001F3FE", 1},
+{"\U0001FAF7\U0001F3FF", 1},
+{"\U0001FAF8\U0001F3FB", 1},
+{"\U0001FAF8\U0001F3FC", 1},
+{"\U0001FAF8\U0001F3FD", 1},
+{"\U0001FAF8\U0001F3FE", 1},
+{"\U0001FAF8\U0001F3FF", 1},
+{"\U0001F44C\U0001F3FB", 1},
+{"\U0001F44C\U0001F3FC", 1},
+{"\U0001F44C\U0001F3FD", 1},
+{"\U0001F44C\U0001F3FE", 1},
+{"\U0001F44C\U0001F3FF", 1},
+{"\U0001F90C\U0001F3FB", 1},
+{"\U0001F90C\U0001F3FC", 1},
+{"\U0001F90C\U0001F3FD", 1},
+{"\U0001F90C\U0001F3FE", 1},
+{"\U0001F90C\U0001F3FF", 1},
+{"\U0001F90F\U0001F3FB", 1},
+{"\U0001F90F\U0001F3FC", 1},
+{"\U0001F90F\U0001F3FD", 1},
+{"\U0001F90F\U0001F3FE", 1},
+{"\U0001F90F\U0001F3FF", 1},
+{"\u270C\uFE0F", 1},
+{"\u270C\U0001F3FB", 1},
+{"\u270C\U0001F3FC", 1},
+{"\u270C\U0001F3FD", 1},
+{"\u270C\U0001F3FE", 1},
+{"\u270C\U0001F3FF", 1},
+{"\U0001F91E\U0001F3FB", 1},
+{"\U0001F91E\U0001F3FC", 1},
+{"\U0001F91E\U0001F3FD", 1},
+{"\U0001F91E\U0001F3FE", 1},
+{"\U0001F91E\U0001F3FF", 1},
+{"\U0001FAF0\U0001F3FB", 1},
+{"\U0001FAF0\U0001F3FC", 1},
+{"\U0001FAF0\U0001F3FD", 1},
+{"\U0001FAF0\U0001F3FE", 1},
+{"\U0001FAF0\U0001F3FF", 1},
+{"\U0001F91F\U0001F3FB", 1},
+{"\U0001F91F\U0001F3FC", 1},
+{"\U0001F91F\U0001F3FD", 1},
+{"\U0001F91F\U0001F3FE", 1},
+{"\U0001F91F\U0001F3FF", 1},
+{"\U0001F918\U0001F3FB", 1},
+{"\U0001F918\U0001F3FC", 1},
+{"\U0001F918\U0001F3FD", 1},
+{"\U0001F918\U0001F3FE", 1},
+{"\U0001F918\U0001F3FF", 1},
+{"\U0001F919\U0001F3FB", 1},
+{"\U0001F919\U0001F3FC", 1},
+{"\U0001F919\U0001F3FD", 1},
+{"\U0001F919\U0001F3FE", 1},
+{"\U0001F919\U0001F3FF", 1},
+{"\U0001F448\U0001F3FB", 1},
+{"\U0001F448\U0001F3FC", 1},
+{"\U0001F448\U0001F3FD", 1},
+{"\U0001F448\U0001F3FE", 1},
+{"\U0001F448\U0001F3FF", 1},
+{"\U0001F449\U0001F3FB", 1},
+{"\U0001F449\U0001F3FC", 1},
+{"\U0001F449\U0001F3FD", 1},
+{"\U0001F449\U0001F3FE", 1},
+{"\U0001F449\U0001F3FF", 1},
+{"\U0001F446\U0001F3FB", 1},
+{"\U0001F446\U0001F3FC", 1},
+{"\U0001F446\U0001F3FD", 1},
+{"\U0001F446\U0001F3FE", 1},
+{"\U0001F446\U0001F3FF", 1},
+{"\U0001F595\U0001F3FB", 1},
+{"\U0001F595\U0001F3FC", 1},
+{"\U0001F595\U0001F3FD", 1},
+{"\U0001F595\U0001F3FE", 1},
+{"\U0001F595\U0001F3FF", 1},
+{"\U0001F447\U0001F3FB", 1},
+{"\U0001F447\U0001F3FC", 1},
+{"\U0001F447\U0001F3FD", 1},
+{"\U0001F447\U0001F3FE", 1},
+{"\U0001F447\U0001F3FF", 1},
+{"\u261D\uFE0F", 1},
+{"\u261D\U0001F3FB", 1},
+{"\u261D\U0001F3FC", 1},
+{"\u261D\U0001F3FD", 1},
+{"\u261D\U0001F3FE", 1},
+{"\u261D\U0001F3FF", 1},
+{"\U0001FAF5\U0001F3FB", 1},
+{"\U0001FAF5\U0001F3FC", 1},
+{"\U0001FAF5\U0001F3FD", 1},
+{"\U0001FAF5\U0001F3FE", 1},
+{"\U0001FAF5\U0001F3FF", 1},
+{"\U0001F44D\U0001F3FB", 1},
+{"\U0001F44D\U0001F3FC", 1},
+{"\U0001F44D\U0001F3FD", 1},
+{"\U0001F44D\U0001F3FE", 1},
+{"\U0001F44D\U0001F3FF", 1},
+{"\U0001F44E\U0001F3FB", 1},
+{"\U0001F44E\U0001F3FC", 1},
+{"\U0001F44E\U0001F3FD", 1},
+{"\U0001F44E\U0001F3FE", 1},
+{"\U0001F44E\U0001F3FF", 1},
+{"\u270A\U0001F3FB", 1},
+{"\u270A\U0001F3FC", 1},
+{"\u270A\U0001F3FD", 1},
+{"\u270A\U0001F3FE", 1},
+{"\u270A\U0001F3FF", 1},
+{"\U0001F44A\U0001F3FB", 1},
+{"\U0001F44A\U0001F3FC", 1},
+{"\U0001F44A\U0001F3FD", 1},
+{"\U0001F44A\U0001F3FE", 1},
+{"\U0001F44A\U0001F3FF", 1},
+{"\U0001F91B\U0001F3FB", 1},
+{"\U0001F91B\U0001F3FC", 1},
+{"\U0001F91B\U0001F3FD", 1},
+{"\U0001F91B\U0001F3FE", 1},
+{"\U0001F91B\U0001F3FF", 1},
+{"\U0001F91C\U0001F3FB", 1},
+{"\U0001F91C\U0001F3FC", 1},
+{"\U0001F91C\U0001F3FD", 1},
+{"\U0001F91C\U0001F3FE", 1},
+{"\U0001F91C\U0001F3FF", 1},
+{"\U0001F44F\U0001F3FB", 1},
+{"\U0001F44F\U0001F3FC", 1},
+{"\U0001F44F\U0001F3FD", 1},
+{"\U0001F44F\U0001F3FE", 1},
+{"\U0001F44F\U0001F3FF", 1},
+{"\U0001F64C\U0001F3FB", 1},
+{"\U0001F64C\U0001F3FC", 1},
+{"\U0001F64C\U0001F3FD", 1},
+{"\U0001F64C\U0001F3FE", 1},
+{"\U0001F64C\U0001F3FF", 1},
+{"\U0001FAF6\U0001F3FB", 1},
+{"\U0001FAF6\U0001F3FC", 1},
+{"\U0001FAF6\U0001F3FD", 1},
+{"\U0001FAF6\U0001F3FE", 1},
+{"\U0001FAF6\U0001F3FF", 1},
+{"\U0001F450\U0001F3FB", 1},
+{"\U0001F450\U0001F3FC", 1},
+{"\U0001F450\U0001F3FD", 1},
+{"\U0001F450\U0001F3FE", 1},
+{"\U0001F450\U0001F3FF", 1},
+{"\U0001F932\U0001F3FB", 1},
+{"\U0001F932\U0001F3FC", 1},
+{"\U0001F932\U0001F3FD", 1},
+{"\U0001F932\U0001F3FE", 1},
+{"\U0001F932\U0001F3FF", 1},
+{"\U0001F91D\U0001F3FB", 1},
+{"\U0001F91D\U0001F3FC", 1},
+{"\U0001F91D\U0001F3FD", 1},
+{"\U0001F91D\U0001F3FE", 1},
+{"\U0001F91D\U0001F3FF", 1},
+{"\U0001FAF1\U0001F3FB\u200D\U0001FAF2\U0001F3FC", 1},
+{"\U0001FAF1\U0001F3FB\u200D\U0001FAF2\U0001F3FD", 1},
+{"\U0001FAF1\U0001F3FB\u200D\U0001FAF2\U0001F3FE", 1},
+{"\U0001FAF1\U0001F3FB\u200D\U0001FAF2\U0001F3FF", 1},
+{"\U0001FAF1\U0001F3FC\u200D\U0001FAF2\U0001F3FB", 1},
+{"\U0001FAF1\U0001F3FC\u200D\U0001FAF2\U0001F3FD", 1},
+{"\U0001FAF1\U0001F3FC\u200D\U0001FAF2\U0001F3FE", 1},
+{"\U0001FAF1\U0001F3FC\u200D\U0001FAF2\U0001F3FF", 1},
+{"\U0001FAF1\U0001F3FD\u200D\U0001FAF2\U0001F3FB", 1},
+{"\U0001FAF1\U0001F3FD\u200D\U0001FAF2\U0001F3FC", 1},
+{"\U0001FAF1\U0001F3FD\u200D\U0001FAF2\U0001F3FE", 1},
+{"\U0001FAF1\U0001F3FD\u200D\U0001FAF2\U0001F3FF", 1},
+{"\U0001FAF1\U0001F3FE\u200D\U0001FAF2\U0001F3FB", 1},
+{"\U0001FAF1\U0001F3FE\u200D\U0001FAF2\U0001F3FC", 1},
+{"\U0001FAF1\U0001F3FE\u200D\U0001FAF2\U0001F3FD", 1},
+{"\U0001FAF1\U0001F3FE\u200D\U0001FAF2\U0001F3FF", 1},
+{"\U0001FAF1\U0001F3FF\u200D\U0001FAF2\U0001F3FB", 1},
+{"\U0001FAF1\U0001F3FF\u200D\U0001FAF2\U0001F3FC", 1},
+{"\U0001FAF1\U0001F3FF\u200D\U0001FAF2\U0001F3FD", 1},
+{"\U0001FAF1\U0001F3FF\u200D\U0001FAF2\U0001F3FE", 1},
+{"\U0001F64F\U0001F3FB", 1},
+{"\U0001F64F\U0001F3FC", 1},
+{"\U0001F64F\U0001F3FD", 1},
+{"\U0001F64F\U0001F3FE", 1},
+{"\U0001F64F\U0001F3FF", 1},
+{"\u270D\uFE0F", 1},
+{"\u270D\U0001F3FB", 1},
+{"\u270D\U0001F3FC", 1},
+{"\u270D\U0001F3FD", 1},
+{"\u270D\U0001F3FE", 1},
+{"\u270D\U0001F3FF", 1},
+{"\U0001F485\U0001F3FB", 1},
+{"\U0001F485\U0001F3FC", 1},
+{"\U0001F485\U0001F3FD", 1},
+{"\U0001F485\U0001F3FE", 1},
+{"\U0001F485\U0001F3FF", 1},
+{"\U0001F933\U0001F3FB", 1},
+{"\U0001F933\U0001F3FC", 1},
+{"\U0001F933\U0001F3FD", 1},
+{"\U0001F933\U0001F3FE", 1},
+{"\U0001F933\U0001F3FF", 1},
+{"\U0001F4AA\U0001F3FB", 1},
+{"\U0001F4AA\U0001F3FC", 1},
+{"\U0001F4AA\U0001F3FD", 1},
+{"\U0001F4AA\U0001F3FE", 1},
+{"\U0001F4AA\U0001F3FF", 1},
+{"\U0001F9B5\U0001F3FB", 1},
+{"\U0001F9B5\U0001F3FC", 1},
+{"\U0001F9B5\U0001F3FD", 1},
+{"\U0001F9B5\U0001F3FE", 1},
+{"\U0001F9B5\U0001F3FF", 1},
+{"\U0001F9B6\U0001F3FB", 1},
+{"\U0001F9B6\U0001F3FC", 1},
+{"\U0001F9B6\U0001F3FD", 1},
+{"\U0001F9B6\U0001F3FE", 1},
+{"\U0001F9B6\U0001F3FF", 1},
+{"\U0001F442\U0001F3FB", 1},
+{"\U0001F442\U0001F3FC", 1},
+{"\U0001F442\U0001F3FD", 1},
+{"\U0001F442\U0001F3FE", 1},
+{"\U0001F442\U0001F3FF", 1},
+{"\U0001F9BB\U0001F3FB", 1},
+{"\U0001F9BB\U0001F3FC", 1},
+{"\U0001F9BB\U0001F3FD", 1},
+{"\U0001F9BB\U0001F3FE", 1},
+{"\U0001F9BB\U0001F3FF", 1},
+{"\U0001F443\U0001F3FB", 1},
+{"\U0001F443\U0001F3FC", 1},
+{"\U0001F443\U0001F3FD", 1},
+{"\U0001F443\U0001F3FE", 1},
+{"\U0001F443\U0001F3FF", 1},
+{"\U0001F441\uFE0F", 1},
+{"\U0001F476\U0001F3FB", 1},
+{"\U0001F476\U0001F3FC", 1},
+{"\U0001F476\U0001F3FD", 1},
+{"\U0001F476\U0001F3FE", 1},
+{"\U0001F476\U0001F3FF", 1},
+{"\U0001F9D2\U0001F3FB", 1},
+{"\U0001F9D2\U0001F3FC", 1},
+{"\U0001F9D2\U0001F3FD", 1},
+{"\U0001F9D2\U0001F3FE", 1},
+{"\U0001F9D2\U0001F3FF", 1},
+{"\U0001F466\U0001F3FB", 1},
+{"\U0001F466\U0001F3FC", 1},
+{"\U0001F466\U0001F3FD", 1},
+{"\U0001F466\U0001F3FE", 1},
+{"\U0001F466\U0001F3FF", 1},
+{"\U0001F467\U0001F3FB", 1},
+{"\U0001F467\U0001F3FC", 1},
+{"\U0001F467\U0001F3FD", 1},
+{"\U0001F467\U0001F3FE", 1},
+{"\U0001F467\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FF", 1},
+{"\U0001F471\U0001F3FB", 1},
+{"\U0001F471\U0001F3FC", 1},
+{"\U0001F471\U0001F3FD", 1},
+{"\U0001F471\U0001F3FE", 1},
+{"\U0001F471\U0001F3FF", 1},
+{"\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FF", 1},
+{"\U0001F9D4\U0001F3FB", 1},
+{"\U0001F9D4\U0001F3FC", 1},
+{"\U0001F9D4\U0001F3FD", 1},
+{"\U0001F9D4\U0001F3FE", 1},
+{"\U0001F9D4\U0001F3FF", 1},
+{"\U0001F9D4\u200D\u2642\uFE0F", 1},
+{"\U0001F9D4\u200D\u2642", 1},
+{"\U0001F9D4\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9D4\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9D4\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9D4\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9D4\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9D4\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9D4\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9D4\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9D4\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9D4\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9D4\u200D\u2640\uFE0F", 1},
+{"\U0001F9D4\u200D\u2640", 1},
+{"\U0001F9D4\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9D4\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9D4\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9D4\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9D4\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9D4\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9D4\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9D4\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9D4\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9D4\U0001F3FF\u200D\u2640", 1},
+{"\U0001F468\u200D\U0001F9B0", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9B0", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9B0", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9B0", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9B0", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9B0", 1},
+{"\U0001F468\u200D\U0001F9B1", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9B1", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9B1", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9B1", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9B1", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9B1", 1},
+{"\U0001F468\u200D\U0001F9B3", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9B3", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9B3", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9B3", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9B3", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9B3", 1},
+{"\U0001F468\u200D\U0001F9B2", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9B2", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9B2", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9B2", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9B2", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9B2", 1},
+{"\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FF", 1},
+{"\U0001F469\u200D\U0001F9B0", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9B0", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9B0", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9B0", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9B0", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9B0", 1},
+{"\U0001F9D1\u200D\U0001F9B0", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9B0", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9B0", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9B0", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9B0", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9B0", 1},
+{"\U0001F469\u200D\U0001F9B1", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9B1", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9B1", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9B1", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9B1", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9B1", 1},
+{"\U0001F9D1\u200D\U0001F9B1", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9B1", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9B1", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9B1", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9B1", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9B1", 1},
+{"\U0001F469\u200D\U0001F9B3", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9B3", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9B3", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9B3", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9B3", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9B3", 1},
+{"\U0001F9D1\u200D\U0001F9B3", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9B3", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9B3", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9B3", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9B3", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9B3", 1},
+{"\U0001F469\u200D\U0001F9B2", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9B2", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9B2", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9B2", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9B2", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9B2", 1},
+{"\U0001F9D1\u200D\U0001F9B2", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9B2", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9B2", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9B2", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9B2", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9B2", 1},
+{"\U0001F471\u200D\u2640\uFE0F", 1},
+{"\U0001F471\u200D\u2640", 1},
+{"\U0001F471\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F471\U0001F3FB\u200D\u2640", 1},
+{"\U0001F471\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F471\U0001F3FC\u200D\u2640", 1},
+{"\U0001F471\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F471\U0001F3FD\u200D\u2640", 1},
+{"\U0001F471\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F471\U0001F3FE\u200D\u2640", 1},
+{"\U0001F471\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F471\U0001F3FF\u200D\u2640", 1},
+{"\U0001F471\u200D\u2642\uFE0F", 1},
+{"\U0001F471\u200D\u2642", 1},
+{"\U0001F471\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F471\U0001F3FB\u200D\u2642", 1},
+{"\U0001F471\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F471\U0001F3FC\u200D\u2642", 1},
+{"\U0001F471\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F471\U0001F3FD\u200D\u2642", 1},
+{"\U0001F471\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F471\U0001F3FE\u200D\u2642", 1},
+{"\U0001F471\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F471\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9D3\U0001F3FB", 1},
+{"\U0001F9D3\U0001F3FC", 1},
+{"\U0001F9D3\U0001F3FD", 1},
+{"\U0001F9D3\U0001F3FE", 1},
+{"\U0001F9D3\U0001F3FF", 1},
+{"\U0001F474\U0001F3FB", 1},
+{"\U0001F474\U0001F3FC", 1},
+{"\U0001F474\U0001F3FD", 1},
+{"\U0001F474\U0001F3FE", 1},
+{"\U0001F474\U0001F3FF", 1},
+{"\U0001F475\U0001F3FB", 1},
+{"\U0001F475\U0001F3FC", 1},
+{"\U0001F475\U0001F3FD", 1},
+{"\U0001F475\U0001F3FE", 1},
+{"\U0001F475\U0001F3FF", 1},
+{"\U0001F64D\U0001F3FB", 1},
+{"\U0001F64D\U0001F3FC", 1},
+{"\U0001F64D\U0001F3FD", 1},
+{"\U0001F64D\U0001F3FE", 1},
+{"\U0001F64D\U0001F3FF", 1},
+{"\U0001F64D\u200D\u2642\uFE0F", 1},
+{"\U0001F64D\u200D\u2642", 1},
+{"\U0001F64D\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F64D\U0001F3FB\u200D\u2642", 1},
+{"\U0001F64D\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F64D\U0001F3FC\u200D\u2642", 1},
+{"\U0001F64D\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F64D\U0001F3FD\u200D\u2642", 1},
+{"\U0001F64D\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F64D\U0001F3FE\u200D\u2642", 1},
+{"\U0001F64D\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F64D\U0001F3FF\u200D\u2642", 1},
+{"\U0001F64D\u200D\u2640\uFE0F", 1},
+{"\U0001F64D\u200D\u2640", 1},
+{"\U0001F64D\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F64D\U0001F3FB\u200D\u2640", 1},
+{"\U0001F64D\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F64D\U0001F3FC\u200D\u2640", 1},
+{"\U0001F64D\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F64D\U0001F3FD\u200D\u2640", 1},
+{"\U0001F64D\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F64D\U0001F3FE\u200D\u2640", 1},
+{"\U0001F64D\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F64D\U0001F3FF\u200D\u2640", 1},
+{"\U0001F64E\U0001F3FB", 1},
+{"\U0001F64E\U0001F3FC", 1},
+{"\U0001F64E\U0001F3FD", 1},
+{"\U0001F64E\U0001F3FE", 1},
+{"\U0001F64E\U0001F3FF", 1},
+{"\U0001F64E\u200D\u2642\uFE0F", 1},
+{"\U0001F64E\u200D\u2642", 1},
+{"\U0001F64E\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F64E\U0001F3FB\u200D\u2642", 1},
+{"\U0001F64E\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F64E\U0001F3FC\u200D\u2642", 1},
+{"\U0001F64E\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F64E\U0001F3FD\u200D\u2642", 1},
+{"\U0001F64E\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F64E\U0001F3FE\u200D\u2642", 1},
+{"\U0001F64E\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F64E\U0001F3FF\u200D\u2642", 1},
+{"\U0001F64E\u200D\u2640\uFE0F", 1},
+{"\U0001F64E\u200D\u2640", 1},
+{"\U0001F64E\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F64E\U0001F3FB\u200D\u2640", 1},
+{"\U0001F64E\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F64E\U0001F3FC\u200D\u2640", 1},
+{"\U0001F64E\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F64E\U0001F3FD\u200D\u2640", 1},
+{"\U0001F64E\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F64E\U0001F3FE\u200D\u2640", 1},
+{"\U0001F64E\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F64E\U0001F3FF\u200D\u2640", 1},
+{"\U0001F645\U0001F3FB", 1},
+{"\U0001F645\U0001F3FC", 1},
+{"\U0001F645\U0001F3FD", 1},
+{"\U0001F645\U0001F3FE", 1},
+{"\U0001F645\U0001F3FF", 1},
+{"\U0001F645\u200D\u2642\uFE0F", 1},
+{"\U0001F645\u200D\u2642", 1},
+{"\U0001F645\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F645\U0001F3FB\u200D\u2642", 1},
+{"\U0001F645\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F645\U0001F3FC\u200D\u2642", 1},
+{"\U0001F645\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F645\U0001F3FD\u200D\u2642", 1},
+{"\U0001F645\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F645\U0001F3FE\u200D\u2642", 1},
+{"\U0001F645\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F645\U0001F3FF\u200D\u2642", 1},
+{"\U0001F645\u200D\u2640\uFE0F", 1},
+{"\U0001F645\u200D\u2640", 1},
+{"\U0001F645\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F645\U0001F3FB\u200D\u2640", 1},
+{"\U0001F645\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F645\U0001F3FC\u200D\u2640", 1},
+{"\U0001F645\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F645\U0001F3FD\u200D\u2640", 1},
+{"\U0001F645\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F645\U0001F3FE\u200D\u2640", 1},
+{"\U0001F645\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F645\U0001F3FF\u200D\u2640", 1},
+{"\U0001F646\U0001F3FB", 1},
+{"\U0001F646\U0001F3FC", 1},
+{"\U0001F646\U0001F3FD", 1},
+{"\U0001F646\U0001F3FE", 1},
+{"\U0001F646\U0001F3FF", 1},
+{"\U0001F646\u200D\u2642\uFE0F", 1},
+{"\U0001F646\u200D\u2642", 1},
+{"\U0001F646\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F646\U0001F3FB\u200D\u2642", 1},
+{"\U0001F646\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F646\U0001F3FC\u200D\u2642", 1},
+{"\U0001F646\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F646\U0001F3FD\u200D\u2642", 1},
+{"\U0001F646\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F646\U0001F3FE\u200D\u2642", 1},
+{"\U0001F646\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F646\U0001F3FF\u200D\u2642", 1},
+{"\U0001F646\u200D\u2640\uFE0F", 1},
+{"\U0001F646\u200D\u2640", 1},
+{"\U0001F646\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F646\U0001F3FB\u200D\u2640", 1},
+{"\U0001F646\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F646\U0001F3FC\u200D\u2640", 1},
+{"\U0001F646\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F646\U0001F3FD\u200D\u2640", 1},
+{"\U0001F646\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F646\U0001F3FE\u200D\u2640", 1},
+{"\U0001F646\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F646\U0001F3FF\u200D\u2640", 1},
+{"\U0001F481\U0001F3FB", 1},
+{"\U0001F481\U0001F3FC", 1},
+{"\U0001F481\U0001F3FD", 1},
+{"\U0001F481\U0001F3FE", 1},
+{"\U0001F481\U0001F3FF", 1},
+{"\U0001F481\u200D\u2642\uFE0F", 1},
+{"\U0001F481\u200D\u2642", 1},
+{"\U0001F481\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F481\U0001F3FB\u200D\u2642", 1},
+{"\U0001F481\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F481\U0001F3FC\u200D\u2642", 1},
+{"\U0001F481\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F481\U0001F3FD\u200D\u2642", 1},
+{"\U0001F481\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F481\U0001F3FE\u200D\u2642", 1},
+{"\U0001F481\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F481\U0001F3FF\u200D\u2642", 1},
+{"\U0001F481\u200D\u2640\uFE0F", 1},
+{"\U0001F481\u200D\u2640", 1},
+{"\U0001F481\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F481\U0001F3FB\u200D\u2640", 1},
+{"\U0001F481\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F481\U0001F3FC\u200D\u2640", 1},
+{"\U0001F481\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F481\U0001F3FD\u200D\u2640", 1},
+{"\U0001F481\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F481\U0001F3FE\u200D\u2640", 1},
+{"\U0001F481\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F481\U0001F3FF\u200D\u2640", 1},
+{"\U0001F64B\U0001F3FB", 1},
+{"\U0001F64B\U0001F3FC", 1},
+{"\U0001F64B\U0001F3FD", 1},
+{"\U0001F64B\U0001F3FE", 1},
+{"\U0001F64B\U0001F3FF", 1},
+{"\U0001F64B\u200D\u2642\uFE0F", 1},
+{"\U0001F64B\u200D\u2642", 1},
+{"\U0001F64B\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F64B\U0001F3FB\u200D\u2642", 1},
+{"\U0001F64B\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F64B\U0001F3FC\u200D\u2642", 1},
+{"\U0001F64B\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F64B\U0001F3FD\u200D\u2642", 1},
+{"\U0001F64B\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F64B\U0001F3FE\u200D\u2642", 1},
+{"\U0001F64B\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F64B\U0001F3FF\u200D\u2642", 1},
+{"\U0001F64B\u200D\u2640\uFE0F", 1},
+{"\U0001F64B\u200D\u2640", 1},
+{"\U0001F64B\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F64B\U0001F3FB\u200D\u2640", 1},
+{"\U0001F64B\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F64B\U0001F3FC\u200D\u2640", 1},
+{"\U0001F64B\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F64B\U0001F3FD\u200D\u2640", 1},
+{"\U0001F64B\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F64B\U0001F3FE\u200D\u2640", 1},
+{"\U0001F64B\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F64B\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9CF\U0001F3FB", 1},
+{"\U0001F9CF\U0001F3FC", 1},
+{"\U0001F9CF\U0001F3FD", 1},
+{"\U0001F9CF\U0001F3FE", 1},
+{"\U0001F9CF\U0001F3FF", 1},
+{"\U0001F9CF\u200D\u2642\uFE0F", 1},
+{"\U0001F9CF\u200D\u2642", 1},
+{"\U0001F9CF\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9CF\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9CF\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9CF\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9CF\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9CF\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9CF\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9CF\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9CF\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9CF\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9CF\u200D\u2640\uFE0F", 1},
+{"\U0001F9CF\u200D\u2640", 1},
+{"\U0001F9CF\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9CF\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9CF\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9CF\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9CF\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9CF\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9CF\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9CF\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9CF\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9CF\U0001F3FF\u200D\u2640", 1},
+{"\U0001F647\U0001F3FB", 1},
+{"\U0001F647\U0001F3FC", 1},
+{"\U0001F647\U0001F3FD", 1},
+{"\U0001F647\U0001F3FE", 1},
+{"\U0001F647\U0001F3FF", 1},
+{"\U0001F647\u200D\u2642\uFE0F", 1},
+{"\U0001F647\u200D\u2642", 1},
+{"\U0001F647\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F647\U0001F3FB\u200D\u2642", 1},
+{"\U0001F647\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F647\U0001F3FC\u200D\u2642", 1},
+{"\U0001F647\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F647\U0001F3FD\u200D\u2642", 1},
+{"\U0001F647\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F647\U0001F3FE\u200D\u2642", 1},
+{"\U0001F647\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F647\U0001F3FF\u200D\u2642", 1},
+{"\U0001F647\u200D\u2640\uFE0F", 1},
+{"\U0001F647\u200D\u2640", 1},
+{"\U0001F647\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F647\U0001F3FB\u200D\u2640", 1},
+{"\U0001F647\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F647\U0001F3FC\u200D\u2640", 1},
+{"\U0001F647\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F647\U0001F3FD\u200D\u2640", 1},
+{"\U0001F647\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F647\U0001F3FE\u200D\u2640", 1},
+{"\U0001F647\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F647\U0001F3FF\u200D\u2640", 1},
+{"\U0001F926\U0001F3FB", 1},
+{"\U0001F926\U0001F3FC", 1},
+{"\U0001F926\U0001F3FD", 1},
+{"\U0001F926\U0001F3FE", 1},
+{"\U0001F926\U0001F3FF", 1},
+{"\U0001F926\u200D\u2642\uFE0F", 1},
+{"\U0001F926\u200D\u2642", 1},
+{"\U0001F926\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F926\U0001F3FB\u200D\u2642", 1},
+{"\U0001F926\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F926\U0001F3FC\u200D\u2642", 1},
+{"\U0001F926\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F926\U0001F3FD\u200D\u2642", 1},
+{"\U0001F926\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F926\U0001F3FE\u200D\u2642", 1},
+{"\U0001F926\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F926\U0001F3FF\u200D\u2642", 1},
+{"\U0001F926\u200D\u2640\uFE0F", 1},
+{"\U0001F926\u200D\u2640", 1},
+{"\U0001F926\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F926\U0001F3FB\u200D\u2640", 1},
+{"\U0001F926\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F926\U0001F3FC\u200D\u2640", 1},
+{"\U0001F926\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F926\U0001F3FD\u200D\u2640", 1},
+{"\U0001F926\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F926\U0001F3FE\u200D\u2640", 1},
+{"\U0001F926\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F926\U0001F3FF\u200D\u2640", 1},
+{"\U0001F937\U0001F3FB", 1},
+{"\U0001F937\U0001F3FC", 1},
+{"\U0001F937\U0001F3FD", 1},
+{"\U0001F937\U0001F3FE", 1},
+{"\U0001F937\U0001F3FF", 1},
+{"\U0001F937\u200D\u2642\uFE0F", 1},
+{"\U0001F937\u200D\u2642", 1},
+{"\U0001F937\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F937\U0001F3FB\u200D\u2642", 1},
+{"\U0001F937\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F937\U0001F3FC\u200D\u2642", 1},
+{"\U0001F937\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F937\U0001F3FD\u200D\u2642", 1},
+{"\U0001F937\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F937\U0001F3FE\u200D\u2642", 1},
+{"\U0001F937\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F937\U0001F3FF\u200D\u2642", 1},
+{"\U0001F937\u200D\u2640\uFE0F", 1},
+{"\U0001F937\u200D\u2640", 1},
+{"\U0001F937\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F937\U0001F3FB\u200D\u2640", 1},
+{"\U0001F937\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F937\U0001F3FC\u200D\u2640", 1},
+{"\U0001F937\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F937\U0001F3FD\u200D\u2640", 1},
+{"\U0001F937\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F937\U0001F3FE\u200D\u2640", 1},
+{"\U0001F937\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F937\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9D1\u200D\u2695\uFE0F", 1},
+{"\U0001F9D1\u200D\u2695", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2695\uFE0F", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2695", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2695\uFE0F", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2695", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2695\uFE0F", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2695", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2695\uFE0F", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2695", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2695\uFE0F", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2695", 1},
+{"\U0001F468\u200D\u2695\uFE0F", 1},
+{"\U0001F468\u200D\u2695", 1},
+{"\U0001F468\U0001F3FB\u200D\u2695\uFE0F", 1},
+{"\U0001F468\U0001F3FB\u200D\u2695", 1},
+{"\U0001F468\U0001F3FC\u200D\u2695\uFE0F", 1},
+{"\U0001F468\U0001F3FC\u200D\u2695", 1},
+{"\U0001F468\U0001F3FD\u200D\u2695\uFE0F", 1},
+{"\U0001F468\U0001F3FD\u200D\u2695", 1},
+{"\U0001F468\U0001F3FE\u200D\u2695\uFE0F", 1},
+{"\U0001F468\U0001F3FE\u200D\u2695", 1},
+{"\U0001F468\U0001F3FF\u200D\u2695\uFE0F", 1},
+{"\U0001F468\U0001F3FF\u200D\u2695", 1},
+{"\U0001F469\u200D\u2695\uFE0F", 1},
+{"\U0001F469\u200D\u2695", 1},
+{"\U0001F469\U0001F3FB\u200D\u2695\uFE0F", 1},
+{"\U0001F469\U0001F3FB\u200D\u2695", 1},
+{"\U0001F469\U0001F3FC\u200D\u2695\uFE0F", 1},
+{"\U0001F469\U0001F3FC\u200D\u2695", 1},
+{"\U0001F469\U0001F3FD\u200D\u2695\uFE0F", 1},
+{"\U0001F469\U0001F3FD\u200D\u2695", 1},
+{"\U0001F469\U0001F3FE\u200D\u2695\uFE0F", 1},
+{"\U0001F469\U0001F3FE\u200D\u2695", 1},
+{"\U0001F469\U0001F3FF\u200D\u2695\uFE0F", 1},
+{"\U0001F469\U0001F3FF\u200D\u2695", 1},
+{"\U0001F9D1\u200D\U0001F393", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F393", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F393", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F393", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F393", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F393", 1},
+{"\U0001F468\u200D\U0001F393", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F393", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F393", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F393", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F393", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F393", 1},
+{"\U0001F469\u200D\U0001F393", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F393", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F393", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F393", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F393", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F393", 1},
+{"\U0001F9D1\u200D\U0001F3EB", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F3EB", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F3EB", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F3EB", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F3EB", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F3EB", 1},
+{"\U0001F468\u200D\U0001F3EB", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F3EB", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F3EB", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F3EB", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F3EB", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F3EB", 1},
+{"\U0001F469\u200D\U0001F3EB", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F3EB", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F3EB", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F3EB", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F3EB", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F3EB", 1},
+{"\U0001F9D1\u200D\u2696\uFE0F", 1},
+{"\U0001F9D1\u200D\u2696", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2696\uFE0F", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2696", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2696\uFE0F", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2696", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2696\uFE0F", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2696", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2696\uFE0F", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2696", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2696\uFE0F", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2696", 1},
+{"\U0001F468\u200D\u2696\uFE0F", 1},
+{"\U0001F468\u200D\u2696", 1},
+{"\U0001F468\U0001F3FB\u200D\u2696\uFE0F", 1},
+{"\U0001F468\U0001F3FB\u200D\u2696", 1},
+{"\U0001F468\U0001F3FC\u200D\u2696\uFE0F", 1},
+{"\U0001F468\U0001F3FC\u200D\u2696", 1},
+{"\U0001F468\U0001F3FD\u200D\u2696\uFE0F", 1},
+{"\U0001F468\U0001F3FD\u200D\u2696", 1},
+{"\U0001F468\U0001F3FE\u200D\u2696\uFE0F", 1},
+{"\U0001F468\U0001F3FE\u200D\u2696", 1},
+{"\U0001F468\U0001F3FF\u200D\u2696\uFE0F", 1},
+{"\U0001F468\U0001F3FF\u200D\u2696", 1},
+{"\U0001F469\u200D\u2696\uFE0F", 1},
+{"\U0001F469\u200D\u2696", 1},
+{"\U0001F469\U0001F3FB\u200D\u2696\uFE0F", 1},
+{"\U0001F469\U0001F3FB\u200D\u2696", 1},
+{"\U0001F469\U0001F3FC\u200D\u2696\uFE0F", 1},
+{"\U0001F469\U0001F3FC\u200D\u2696", 1},
+{"\U0001F469\U0001F3FD\u200D\u2696\uFE0F", 1},
+{"\U0001F469\U0001F3FD\u200D\u2696", 1},
+{"\U0001F469\U0001F3FE\u200D\u2696\uFE0F", 1},
+{"\U0001F469\U0001F3FE\u200D\u2696", 1},
+{"\U0001F469\U0001F3FF\u200D\u2696\uFE0F", 1},
+{"\U0001F469\U0001F3FF\u200D\u2696", 1},
+{"\U0001F9D1\u200D\U0001F33E", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F33E", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F33E", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F33E", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F33E", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F33E", 1},
+{"\U0001F468\u200D\U0001F33E", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F33E", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F33E", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F33E", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F33E", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F33E", 1},
+{"\U0001F469\u200D\U0001F33E", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F33E", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F33E", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F33E", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F33E", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F33E", 1},
+{"\U0001F9D1\u200D\U0001F373", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F373", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F373", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F373", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F373", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F373", 1},
+{"\U0001F468\u200D\U0001F373", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F373", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F373", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F373", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F373", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F373", 1},
+{"\U0001F469\u200D\U0001F373", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F373", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F373", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F373", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F373", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F373", 1},
+{"\U0001F9D1\u200D\U0001F527", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F527", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F527", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F527", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F527", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F527", 1},
+{"\U0001F468\u200D\U0001F527", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F527", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F527", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F527", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F527", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F527", 1},
+{"\U0001F469\u200D\U0001F527", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F527", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F527", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F527", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F527", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F527", 1},
+{"\U0001F9D1\u200D\U0001F3ED", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F3ED", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F3ED", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F3ED", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F3ED", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F3ED", 1},
+{"\U0001F468\u200D\U0001F3ED", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F3ED", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F3ED", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F3ED", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F3ED", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F3ED", 1},
+{"\U0001F469\u200D\U0001F3ED", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F3ED", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F3ED", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F3ED", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F3ED", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F3ED", 1},
+{"\U0001F9D1\u200D\U0001F4BC", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F4BC", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F4BC", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F4BC", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F4BC", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F4BC", 1},
+{"\U0001F468\u200D\U0001F4BC", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F4BC", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F4BC", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F4BC", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F4BC", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F4BC", 1},
+{"\U0001F469\u200D\U0001F4BC", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F4BC", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F4BC", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F4BC", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F4BC", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F4BC", 1},
+{"\U0001F9D1\u200D\U0001F52C", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F52C", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F52C", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F52C", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F52C", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F52C", 1},
+{"\U0001F468\u200D\U0001F52C", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F52C", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F52C", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F52C", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F52C", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F52C", 1},
+{"\U0001F469\u200D\U0001F52C", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F52C", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F52C", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F52C", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F52C", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F52C", 1},
+{"\U0001F9D1\u200D\U0001F4BB", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F4BB", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F4BB", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F4BB", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F4BB", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F4BB", 1},
+{"\U0001F468\u200D\U0001F4BB", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F4BB", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F4BB", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F4BB", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F4BB", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F4BB", 1},
+{"\U0001F469\u200D\U0001F4BB", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F4BB", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F4BB", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F4BB", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F4BB", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F4BB", 1},
+{"\U0001F9D1\u200D\U0001F3A4", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F3A4", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F3A4", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F3A4", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F3A4", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F3A4", 1},
+{"\U0001F468\u200D\U0001F3A4", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F3A4", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F3A4", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F3A4", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F3A4", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F3A4", 1},
+{"\U0001F469\u200D\U0001F3A4", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F3A4", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F3A4", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F3A4", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F3A4", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F3A4", 1},
+{"\U0001F9D1\u200D\U0001F3A8", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F3A8", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F3A8", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F3A8", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F3A8", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F3A8", 1},
+{"\U0001F468\u200D\U0001F3A8", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F3A8", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F3A8", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F3A8", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F3A8", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F3A8", 1},
+{"\U0001F469\u200D\U0001F3A8", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F3A8", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F3A8", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F3A8", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F3A8", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F3A8", 1},
+{"\U0001F9D1\u200D\u2708\uFE0F", 1},
+{"\U0001F9D1\u200D\u2708", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2708\uFE0F", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2708", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2708\uFE0F", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2708", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2708\uFE0F", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2708", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2708\uFE0F", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2708", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2708\uFE0F", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2708", 1},
+{"\U0001F468\u200D\u2708\uFE0F", 1},
+{"\U0001F468\u200D\u2708", 1},
+{"\U0001F468\U0001F3FB\u200D\u2708\uFE0F", 1},
+{"\U0001F468\U0001F3FB\u200D\u2708", 1},
+{"\U0001F468\U0001F3FC\u200D\u2708\uFE0F", 1},
+{"\U0001F468\U0001F3FC\u200D\u2708", 1},
+{"\U0001F468\U0001F3FD\u200D\u2708\uFE0F", 1},
+{"\U0001F468\U0001F3FD\u200D\u2708", 1},
+{"\U0001F468\U0001F3FE\u200D\u2708\uFE0F", 1},
+{"\U0001F468\U0001F3FE\u200D\u2708", 1},
+{"\U0001F468\U0001F3FF\u200D\u2708\uFE0F", 1},
+{"\U0001F468\U0001F3FF\u200D\u2708", 1},
+{"\U0001F469\u200D\u2708\uFE0F", 1},
+{"\U0001F469\u200D\u2708", 1},
+{"\U0001F469\U0001F3FB\u200D\u2708\uFE0F", 1},
+{"\U0001F469\U0001F3FB\u200D\u2708", 1},
+{"\U0001F469\U0001F3FC\u200D\u2708\uFE0F", 1},
+{"\U0001F469\U0001F3FC\u200D\u2708", 1},
+{"\U0001F469\U0001F3FD\u200D\u2708\uFE0F", 1},
+{"\U0001F469\U0001F3FD\u200D\u2708", 1},
+{"\U0001F469\U0001F3FE\u200D\u2708\uFE0F", 1},
+{"\U0001F469\U0001F3FE\u200D\u2708", 1},
+{"\U0001F469\U0001F3FF\u200D\u2708\uFE0F", 1},
+{"\U0001F469\U0001F3FF\u200D\u2708", 1},
+{"\U0001F9D1\u200D\U0001F680", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F680", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F680", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F680", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F680", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F680", 1},
+{"\U0001F468\u200D\U0001F680", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F680", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F680", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F680", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F680", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F680", 1},
+{"\U0001F469\u200D\U0001F680", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F680", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F680", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F680", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F680", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F680", 1},
+{"\U0001F9D1\u200D\U0001F692", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F692", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F692", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F692", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F692", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F692", 1},
+{"\U0001F468\u200D\U0001F692", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F692", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F692", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F692", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F692", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F692", 1},
+{"\U0001F469\u200D\U0001F692", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F692", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F692", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F692", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F692", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F692", 1},
+{"\U0001F46E\U0001F3FB", 1},
+{"\U0001F46E\U0001F3FC", 1},
+{"\U0001F46E\U0001F3FD", 1},
+{"\U0001F46E\U0001F3FE", 1},
+{"\U0001F46E\U0001F3FF", 1},
+{"\U0001F46E\u200D\u2642\uFE0F", 1},
+{"\U0001F46E\u200D\u2642", 1},
+{"\U0001F46E\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F46E\U0001F3FB\u200D\u2642", 1},
+{"\U0001F46E\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F46E\U0001F3FC\u200D\u2642", 1},
+{"\U0001F46E\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F46E\U0001F3FD\u200D\u2642", 1},
+{"\U0001F46E\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F46E\U0001F3FE\u200D\u2642", 1},
+{"\U0001F46E\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F46E\U0001F3FF\u200D\u2642", 1},
+{"\U0001F46E\u200D\u2640\uFE0F", 1},
+{"\U0001F46E\u200D\u2640", 1},
+{"\U0001F46E\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F46E\U0001F3FB\u200D\u2640", 1},
+{"\U0001F46E\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F46E\U0001F3FC\u200D\u2640", 1},
+{"\U0001F46E\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F46E\U0001F3FD\u200D\u2640", 1},
+{"\U0001F46E\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F46E\U0001F3FE\u200D\u2640", 1},
+{"\U0001F46E\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F46E\U0001F3FF\u200D\u2640", 1},
+{"\U0001F575\uFE0F", 1},
+{"\U0001F575\U0001F3FB", 1},
+{"\U0001F575\U0001F3FC", 1},
+{"\U0001F575\U0001F3FD", 1},
+{"\U0001F575\U0001F3FE", 1},
+{"\U0001F575\U0001F3FF", 1},
+{"\U0001F575\uFE0F\u200D\u2642\uFE0F", 1},
+{"\U0001F575\u200D\u2642\uFE0F", 1},
+{"\U0001F575\uFE0F\u200D\u2642", 1},
+{"\U0001F575\u200D\u2642", 1},
+{"\U0001F575\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F575\U0001F3FB\u200D\u2642", 1},
+{"\U0001F575\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F575\U0001F3FC\u200D\u2642", 1},
+{"\U0001F575\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F575\U0001F3FD\u200D\u2642", 1},
+{"\U0001F575\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F575\U0001F3FE\u200D\u2642", 1},
+{"\U0001F575\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F575\U0001F3FF\u200D\u2642", 1},
+{"\U0001F575\uFE0F\u200D\u2640\uFE0F", 1},
+{"\U0001F575\u200D\u2640\uFE0F", 1},
+{"\U0001F575\uFE0F\u200D\u2640", 1},
+{"\U0001F575\u200D\u2640", 1},
+{"\U0001F575\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F575\U0001F3FB\u200D\u2640", 1},
+{"\U0001F575\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F575\U0001F3FC\u200D\u2640", 1},
+{"\U0001F575\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F575\U0001F3FD\u200D\u2640", 1},
+{"\U0001F575\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F575\U0001F3FE\u200D\u2640", 1},
+{"\U0001F575\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F575\U0001F3FF\u200D\u2640", 1},
+{"\U0001F482\U0001F3FB", 1},
+{"\U0001F482\U0001F3FC", 1},
+{"\U0001F482\U0001F3FD", 1},
+{"\U0001F482\U0001F3FE", 1},
+{"\U0001F482\U0001F3FF", 1},
+{"\U0001F482\u200D\u2642\uFE0F", 1},
+{"\U0001F482\u200D\u2642", 1},
+{"\U0001F482\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F482\U0001F3FB\u200D\u2642", 1},
+{"\U0001F482\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F482\U0001F3FC\u200D\u2642", 1},
+{"\U0001F482\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F482\U0001F3FD\u200D\u2642", 1},
+{"\U0001F482\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F482\U0001F3FE\u200D\u2642", 1},
+{"\U0001F482\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F482\U0001F3FF\u200D\u2642", 1},
+{"\U0001F482\u200D\u2640\uFE0F", 1},
+{"\U0001F482\u200D\u2640", 1},
+{"\U0001F482\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F482\U0001F3FB\u200D\u2640", 1},
+{"\U0001F482\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F482\U0001F3FC\u200D\u2640", 1},
+{"\U0001F482\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F482\U0001F3FD\u200D\u2640", 1},
+{"\U0001F482\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F482\U0001F3FE\u200D\u2640", 1},
+{"\U0001F482\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F482\U0001F3FF\u200D\u2640", 1},
+{"\U0001F977\U0001F3FB", 1},
+{"\U0001F977\U0001F3FC", 1},
+{"\U0001F977\U0001F3FD", 1},
+{"\U0001F977\U0001F3FE", 1},
+{"\U0001F977\U0001F3FF", 1},
+{"\U0001F477\U0001F3FB", 1},
+{"\U0001F477\U0001F3FC", 1},
+{"\U0001F477\U0001F3FD", 1},
+{"\U0001F477\U0001F3FE", 1},
+{"\U0001F477\U0001F3FF", 1},
+{"\U0001F477\u200D\u2642\uFE0F", 1},
+{"\U0001F477\u200D\u2642", 1},
+{"\U0001F477\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F477\U0001F3FB\u200D\u2642", 1},
+{"\U0001F477\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F477\U0001F3FC\u200D\u2642", 1},
+{"\U0001F477\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F477\U0001F3FD\u200D\u2642", 1},
+{"\U0001F477\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F477\U0001F3FE\u200D\u2642", 1},
+{"\U0001F477\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F477\U0001F3FF\u200D\u2642", 1},
+{"\U0001F477\u200D\u2640\uFE0F", 1},
+{"\U0001F477\u200D\u2640", 1},
+{"\U0001F477\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F477\U0001F3FB\u200D\u2640", 1},
+{"\U0001F477\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F477\U0001F3FC\u200D\u2640", 1},
+{"\U0001F477\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F477\U0001F3FD\u200D\u2640", 1},
+{"\U0001F477\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F477\U0001F3FE\u200D\u2640", 1},
+{"\U0001F477\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F477\U0001F3FF\u200D\u2640", 1},
+{"\U0001FAC5\U0001F3FB", 1},
+{"\U0001FAC5\U0001F3FC", 1},
+{"\U0001FAC5\U0001F3FD", 1},
+{"\U0001FAC5\U0001F3FE", 1},
+{"\U0001FAC5\U0001F3FF", 1},
+{"\U0001F934\U0001F3FB", 1},
+{"\U0001F934\U0001F3FC", 1},
+{"\U0001F934\U0001F3FD", 1},
+{"\U0001F934\U0001F3FE", 1},
+{"\U0001F934\U0001F3FF", 1},
+{"\U0001F478\U0001F3FB", 1},
+{"\U0001F478\U0001F3FC", 1},
+{"\U0001F478\U0001F3FD", 1},
+{"\U0001F478\U0001F3FE", 1},
+{"\U0001F478\U0001F3FF", 1},
+{"\U0001F473\U0001F3FB", 1},
+{"\U0001F473\U0001F3FC", 1},
+{"\U0001F473\U0001F3FD", 1},
+{"\U0001F473\U0001F3FE", 1},
+{"\U0001F473\U0001F3FF", 1},
+{"\U0001F473\u200D\u2642\uFE0F", 1},
+{"\U0001F473\u200D\u2642", 1},
+{"\U0001F473\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F473\U0001F3FB\u200D\u2642", 1},
+{"\U0001F473\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F473\U0001F3FC\u200D\u2642", 1},
+{"\U0001F473\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F473\U0001F3FD\u200D\u2642", 1},
+{"\U0001F473\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F473\U0001F3FE\u200D\u2642", 1},
+{"\U0001F473\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F473\U0001F3FF\u200D\u2642", 1},
+{"\U0001F473\u200D\u2640\uFE0F", 1},
+{"\U0001F473\u200D\u2640", 1},
+{"\U0001F473\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F473\U0001F3FB\u200D\u2640", 1},
+{"\U0001F473\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F473\U0001F3FC\u200D\u2640", 1},
+{"\U0001F473\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F473\U0001F3FD\u200D\u2640", 1},
+{"\U0001F473\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F473\U0001F3FE\u200D\u2640", 1},
+{"\U0001F473\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F473\U0001F3FF\u200D\u2640", 1},
+{"\U0001F472\U0001F3FB", 1},
+{"\U0001F472\U0001F3FC", 1},
+{"\U0001F472\U0001F3FD", 1},
+{"\U0001F472\U0001F3FE", 1},
+{"\U0001F472\U0001F3FF", 1},
+{"\U0001F9D5\U0001F3FB", 1},
+{"\U0001F9D5\U0001F3FC", 1},
+{"\U0001F9D5\U0001F3FD", 1},
+{"\U0001F9D5\U0001F3FE", 1},
+{"\U0001F9D5\U0001F3FF", 1},
+{"\U0001F935\U0001F3FB", 1},
+{"\U0001F935\U0001F3FC", 1},
+{"\U0001F935\U0001F3FD", 1},
+{"\U0001F935\U0001F3FE", 1},
+{"\U0001F935\U0001F3FF", 1},
+{"\U0001F935\u200D\u2642\uFE0F", 1},
+{"\U0001F935\u200D\u2642", 1},
+{"\U0001F935\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F935\U0001F3FB\u200D\u2642", 1},
+{"\U0001F935\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F935\U0001F3FC\u200D\u2642", 1},
+{"\U0001F935\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F935\U0001F3FD\u200D\u2642", 1},
+{"\U0001F935\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F935\U0001F3FE\u200D\u2642", 1},
+{"\U0001F935\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F935\U0001F3FF\u200D\u2642", 1},
+{"\U0001F935\u200D\u2640\uFE0F", 1},
+{"\U0001F935\u200D\u2640", 1},
+{"\U0001F935\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F935\U0001F3FB\u200D\u2640", 1},
+{"\U0001F935\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F935\U0001F3FC\u200D\u2640", 1},
+{"\U0001F935\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F935\U0001F3FD\u200D\u2640", 1},
+{"\U0001F935\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F935\U0001F3FE\u200D\u2640", 1},
+{"\U0001F935\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F935\U0001F3FF\u200D\u2640", 1},
+{"\U0001F470\U0001F3FB", 1},
+{"\U0001F470\U0001F3FC", 1},
+{"\U0001F470\U0001F3FD", 1},
+{"\U0001F470\U0001F3FE", 1},
+{"\U0001F470\U0001F3FF", 1},
+{"\U0001F470\u200D\u2642\uFE0F", 1},
+{"\U0001F470\u200D\u2642", 1},
+{"\U0001F470\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F470\U0001F3FB\u200D\u2642", 1},
+{"\U0001F470\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F470\U0001F3FC\u200D\u2642", 1},
+{"\U0001F470\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F470\U0001F3FD\u200D\u2642", 1},
+{"\U0001F470\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F470\U0001F3FE\u200D\u2642", 1},
+{"\U0001F470\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F470\U0001F3FF\u200D\u2642", 1},
+{"\U0001F470\u200D\u2640\uFE0F", 1},
+{"\U0001F470\u200D\u2640", 1},
+{"\U0001F470\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F470\U0001F3FB\u200D\u2640", 1},
+{"\U0001F470\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F470\U0001F3FC\u200D\u2640", 1},
+{"\U0001F470\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F470\U0001F3FD\u200D\u2640", 1},
+{"\U0001F470\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F470\U0001F3FE\u200D\u2640", 1},
+{"\U0001F470\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F470\U0001F3FF\u200D\u2640", 1},
+{"\U0001F930\U0001F3FB", 1},
+{"\U0001F930\U0001F3FC", 1},
+{"\U0001F930\U0001F3FD", 1},
+{"\U0001F930\U0001F3FE", 1},
+{"\U0001F930\U0001F3FF", 1},
+{"\U0001FAC3\U0001F3FB", 1},
+{"\U0001FAC3\U0001F3FC", 1},
+{"\U0001FAC3\U0001F3FD", 1},
+{"\U0001FAC3\U0001F3FE", 1},
+{"\U0001FAC3\U0001F3FF", 1},
+{"\U0001FAC4\U0001F3FB", 1},
+{"\U0001FAC4\U0001F3FC", 1},
+{"\U0001FAC4\U0001F3FD", 1},
+{"\U0001FAC4\U0001F3FE", 1},
+{"\U0001FAC4\U0001F3FF", 1},
+{"\U0001F931\U0001F3FB", 1},
+{"\U0001F931\U0001F3FC", 1},
+{"\U0001F931\U0001F3FD", 1},
+{"\U0001F931\U0001F3FE", 1},
+{"\U0001F931\U0001F3FF", 1},
+{"\U0001F469\u200D\U0001F37C", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F37C", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F37C", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F37C", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F37C", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F37C", 1},
+{"\U0001F468\u200D\U0001F37C", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F37C", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F37C", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F37C", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F37C", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F37C", 1},
+{"\U0001F9D1\u200D\U0001F37C", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F37C", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F37C", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F37C", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F37C", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F37C", 1},
+{"\U0001F47C\U0001F3FB", 1},
+{"\U0001F47C\U0001F3FC", 1},
+{"\U0001F47C\U0001F3FD", 1},
+{"\U0001F47C\U0001F3FE", 1},
+{"\U0001F47C\U0001F3FF", 1},
+{"\U0001F385\U0001F3FB", 1},
+{"\U0001F385\U0001F3FC", 1},
+{"\U0001F385\U0001F3FD", 1},
+{"\U0001F385\U0001F3FE", 1},
+{"\U0001F385\U0001F3FF", 1},
+{"\U0001F936\U0001F3FB", 1},
+{"\U0001F936\U0001F3FC", 1},
+{"\U0001F936\U0001F3FD", 1},
+{"\U0001F936\U0001F3FE", 1},
+{"\U0001F936\U0001F3FF", 1},
+{"\U0001F9D1\u200D\U0001F384", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F384", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F384", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F384", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F384", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F384", 1},
+{"\U0001F9B8\U0001F3FB", 1},
+{"\U0001F9B8\U0001F3FC", 1},
+{"\U0001F9B8\U0001F3FD", 1},
+{"\U0001F9B8\U0001F3FE", 1},
+{"\U0001F9B8\U0001F3FF", 1},
+{"\U0001F9B8\u200D\u2642\uFE0F", 1},
+{"\U0001F9B8\u200D\u2642", 1},
+{"\U0001F9B8\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9B8\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9B8\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9B8\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9B8\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9B8\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9B8\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9B8\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9B8\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9B8\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9B8\u200D\u2640\uFE0F", 1},
+{"\U0001F9B8\u200D\u2640", 1},
+{"\U0001F9B8\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9B8\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9B8\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9B8\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9B8\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9B8\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9B8\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9B8\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9B8\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9B8\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9B9\U0001F3FB", 1},
+{"\U0001F9B9\U0001F3FC", 1},
+{"\U0001F9B9\U0001F3FD", 1},
+{"\U0001F9B9\U0001F3FE", 1},
+{"\U0001F9B9\U0001F3FF", 1},
+{"\U0001F9B9\u200D\u2642\uFE0F", 1},
+{"\U0001F9B9\u200D\u2642", 1},
+{"\U0001F9B9\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9B9\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9B9\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9B9\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9B9\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9B9\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9B9\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9B9\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9B9\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9B9\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9B9\u200D\u2640\uFE0F", 1},
+{"\U0001F9B9\u200D\u2640", 1},
+{"\U0001F9B9\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9B9\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9B9\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9B9\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9B9\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9B9\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9B9\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9B9\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9B9\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9B9\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9D9\U0001F3FB", 1},
+{"\U0001F9D9\U0001F3FC", 1},
+{"\U0001F9D9\U0001F3FD", 1},
+{"\U0001F9D9\U0001F3FE", 1},
+{"\U0001F9D9\U0001F3FF", 1},
+{"\U0001F9D9\u200D\u2642\uFE0F", 1},
+{"\U0001F9D9\u200D\u2642", 1},
+{"\U0001F9D9\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9D9\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9D9\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9D9\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9D9\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9D9\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9D9\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9D9\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9D9\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9D9\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9D9\u200D\u2640\uFE0F", 1},
+{"\U0001F9D9\u200D\u2640", 1},
+{"\U0001F9D9\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9D9\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9D9\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9D9\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9D9\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9D9\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9D9\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9D9\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9D9\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9D9\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9DA\U0001F3FB", 1},
+{"\U0001F9DA\U0001F3FC", 1},
+{"\U0001F9DA\U0001F3FD", 1},
+{"\U0001F9DA\U0001F3FE", 1},
+{"\U0001F9DA\U0001F3FF", 1},
+{"\U0001F9DA\u200D\u2642\uFE0F", 1},
+{"\U0001F9DA\u200D\u2642", 1},
+{"\U0001F9DA\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9DA\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9DA\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9DA\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9DA\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9DA\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9DA\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9DA\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9DA\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9DA\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9DA\u200D\u2640\uFE0F", 1},
+{"\U0001F9DA\u200D\u2640", 1},
+{"\U0001F9DA\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9DA\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9DA\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9DA\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9DA\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9DA\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9DA\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9DA\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9DA\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9DA\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9DB\U0001F3FB", 1},
+{"\U0001F9DB\U0001F3FC", 1},
+{"\U0001F9DB\U0001F3FD", 1},
+{"\U0001F9DB\U0001F3FE", 1},
+{"\U0001F9DB\U0001F3FF", 1},
+{"\U0001F9DB\u200D\u2642\uFE0F", 1},
+{"\U0001F9DB\u200D\u2642", 1},
+{"\U0001F9DB\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9DB\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9DB\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9DB\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9DB\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9DB\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9DB\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9DB\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9DB\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9DB\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9DB\u200D\u2640\uFE0F", 1},
+{"\U0001F9DB\u200D\u2640", 1},
+{"\U0001F9DB\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9DB\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9DB\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9DB\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9DB\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9DB\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9DB\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9DB\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9DB\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9DB\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9DC\U0001F3FB", 1},
+{"\U0001F9DC\U0001F3FC", 1},
+{"\U0001F9DC\U0001F3FD", 1},
+{"\U0001F9DC\U0001F3FE", 1},
+{"\U0001F9DC\U0001F3FF", 1},
+{"\U0001F9DC\u200D\u2642\uFE0F", 1},
+{"\U0001F9DC\u200D\u2642", 1},
+{"\U0001F9DC\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9DC\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9DC\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9DC\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9DC\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9DC\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9DC\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9DC\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9DC\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9DC\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9DC\u200D\u2640\uFE0F", 1},
+{"\U0001F9DC\u200D\u2640", 1},
+{"\U0001F9DC\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9DC\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9DC\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9DC\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9DC\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9DC\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9DC\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9DC\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9DC\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9DC\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9DD\U0001F3FB", 1},
+{"\U0001F9DD\U0001F3FC", 1},
+{"\U0001F9DD\U0001F3FD", 1},
+{"\U0001F9DD\U0001F3FE", 1},
+{"\U0001F9DD\U0001F3FF", 1},
+{"\U0001F9DD\u200D\u2642\uFE0F", 1},
+{"\U0001F9DD\u200D\u2642", 1},
+{"\U0001F9DD\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9DD\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9DD\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9DD\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9DD\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9DD\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9DD\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9DD\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9DD\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9DD\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9DD\u200D\u2640\uFE0F", 1},
+{"\U0001F9DD\u200D\u2640", 1},
+{"\U0001F9DD\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9DD\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9DD\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9DD\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9DD\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9DD\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9DD\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9DD\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9DD\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9DD\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9DE\u200D\u2642\uFE0F", 1},
+{"\U0001F9DE\u200D\u2642", 1},
+{"\U0001F9DE\u200D\u2640\uFE0F", 1},
+{"\U0001F9DE\u200D\u2640", 1},
+{"\U0001F9DF\u200D\u2642\uFE0F", 1},
+{"\U0001F9DF\u200D\u2642", 1},
+{"\U0001F9DF\u200D\u2640\uFE0F", 1},
+{"\U0001F9DF\u200D\u2640", 1},
+{"\U0001F486\U0001F3FB", 1},
+{"\U0001F486\U0001F3FC", 1},
+{"\U0001F486\U0001F3FD", 1},
+{"\U0001F486\U0001F3FE", 1},
+{"\U0001F486\U0001F3FF", 1},
+{"\U0001F486\u200D\u2642\uFE0F", 1},
+{"\U0001F486\u200D\u2642", 1},
+{"\U0001F486\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F486\U0001F3FB\u200D\u2642", 1},
+{"\U0001F486\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F486\U0001F3FC\u200D\u2642", 1},
+{"\U0001F486\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F486\U0001F3FD\u200D\u2642", 1},
+{"\U0001F486\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F486\U0001F3FE\u200D\u2642", 1},
+{"\U0001F486\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F486\U0001F3FF\u200D\u2642", 1},
+{"\U0001F486\u200D\u2640\uFE0F", 1},
+{"\U0001F486\u200D\u2640", 1},
+{"\U0001F486\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F486\U0001F3FB\u200D\u2640", 1},
+{"\U0001F486\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F486\U0001F3FC\u200D\u2640", 1},
+{"\U0001F486\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F486\U0001F3FD\u200D\u2640", 1},
+{"\U0001F486\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F486\U0001F3FE\u200D\u2640", 1},
+{"\U0001F486\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F486\U0001F3FF\u200D\u2640", 1},
+{"\U0001F487\U0001F3FB", 1},
+{"\U0001F487\U0001F3FC", 1},
+{"\U0001F487\U0001F3FD", 1},
+{"\U0001F487\U0001F3FE", 1},
+{"\U0001F487\U0001F3FF", 1},
+{"\U0001F487\u200D\u2642\uFE0F", 1},
+{"\U0001F487\u200D\u2642", 1},
+{"\U0001F487\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F487\U0001F3FB\u200D\u2642", 1},
+{"\U0001F487\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F487\U0001F3FC\u200D\u2642", 1},
+{"\U0001F487\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F487\U0001F3FD\u200D\u2642", 1},
+{"\U0001F487\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F487\U0001F3FE\u200D\u2642", 1},
+{"\U0001F487\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F487\U0001F3FF\u200D\u2642", 1},
+{"\U0001F487\u200D\u2640\uFE0F", 1},
+{"\U0001F487\u200D\u2640", 1},
+{"\U0001F487\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F487\U0001F3FB\u200D\u2640", 1},
+{"\U0001F487\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F487\U0001F3FC\u200D\u2640", 1},
+{"\U0001F487\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F487\U0001F3FD\u200D\u2640", 1},
+{"\U0001F487\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F487\U0001F3FE\u200D\u2640", 1},
+{"\U0001F487\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F487\U0001F3FF\u200D\u2640", 1},
+{"\U0001F6B6\U0001F3FB", 1},
+{"\U0001F6B6\U0001F3FC", 1},
+{"\U0001F6B6\U0001F3FD", 1},
+{"\U0001F6B6\U0001F3FE", 1},
+{"\U0001F6B6\U0001F3FF", 1},
+{"\U0001F6B6\u200D\u2642\uFE0F", 1},
+{"\U0001F6B6\u200D\u2642", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2642", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2642", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2642", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2642", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2642", 1},
+{"\U0001F6B6\u200D\u2640\uFE0F", 1},
+{"\U0001F6B6\u200D\u2640", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2640", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2640", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2640", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2640", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2640", 1},
+{"\U0001F6B6\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u27A1", 1},
+{"\U0001F6B6\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F6B6\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FB\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FC\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FD\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FE\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F6B6\U0001F3FF\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F9CD\U0001F3FB", 1},
+{"\U0001F9CD\U0001F3FC", 1},
+{"\U0001F9CD\U0001F3FD", 1},
+{"\U0001F9CD\U0001F3FE", 1},
+{"\U0001F9CD\U0001F3FF", 1},
+{"\U0001F9CD\u200D\u2642\uFE0F", 1},
+{"\U0001F9CD\u200D\u2642", 1},
+{"\U0001F9CD\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9CD\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9CD\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9CD\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9CD\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9CD\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9CD\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9CD\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9CD\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9CD\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9CD\u200D\u2640\uFE0F", 1},
+{"\U0001F9CD\u200D\u2640", 1},
+{"\U0001F9CD\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9CD\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9CD\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9CD\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9CD\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9CD\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9CD\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9CD\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9CD\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9CD\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9CE\U0001F3FB", 1},
+{"\U0001F9CE\U0001F3FC", 1},
+{"\U0001F9CE\U0001F3FD", 1},
+{"\U0001F9CE\U0001F3FE", 1},
+{"\U0001F9CE\U0001F3FF", 1},
+{"\U0001F9CE\u200D\u2642\uFE0F", 1},
+{"\U0001F9CE\u200D\u2642", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9CE\u200D\u2640\uFE0F", 1},
+{"\U0001F9CE\u200D\u2640", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9CE\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u27A1", 1},
+{"\U0001F9CE\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F9CE\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FB\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FC\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FD\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FE\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F9CE\U0001F3FF\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F9D1\u200D\U0001F9AF", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9AF", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9AF", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9AF", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9AF", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9AF", 1},
+{"\U0001F9D1\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F468\u200D\U0001F9AF", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9AF", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9AF", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9AF", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9AF", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9AF", 1},
+{"\U0001F468\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F469\u200D\U0001F9AF", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9AF", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9AF", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9AF", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9AF", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9AF", 1},
+{"\U0001F469\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9AF\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9AF\u200D\u27A1", 1},
+{"\U0001F9D1\u200D\U0001F9BC", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9BC", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9BC", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9BC", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9BC", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9BC", 1},
+{"\U0001F9D1\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F468\u200D\U0001F9BC", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9BC", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9BC", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9BC", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9BC", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9BC", 1},
+{"\U0001F468\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F469\u200D\U0001F9BC", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9BC", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9BC", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9BC", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9BC", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9BC", 1},
+{"\U0001F469\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9BC\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9BC\u200D\u27A1", 1},
+{"\U0001F9D1\u200D\U0001F9BD", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9BD", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9BD", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9BD", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9BD", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9BD", 1},
+{"\U0001F9D1\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F468\u200D\U0001F9BD", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9BD", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9BD", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9BD", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9BD", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9BD", 1},
+{"\U0001F468\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F469\u200D\U0001F9BD", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9BD", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9BD", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9BD", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9BD", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9BD", 1},
+{"\U0001F469\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9BD\u200D\u27A1\uFE0F", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F9BD\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FB", 1},
+{"\U0001F3C3\U0001F3FC", 1},
+{"\U0001F3C3\U0001F3FD", 1},
+{"\U0001F3C3\U0001F3FE", 1},
+{"\U0001F3C3\U0001F3FF", 1},
+{"\U0001F3C3\u200D\u2642\uFE0F", 1},
+{"\U0001F3C3\u200D\u2642", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2642", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2642", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2642", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2642", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2642", 1},
+{"\U0001F3C3\u200D\u2640\uFE0F", 1},
+{"\U0001F3C3\u200D\u2640", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2640", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2640", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2640", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2640", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2640", 1},
+{"\U0001F3C3\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u27A1", 1},
+{"\U0001F3C3\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2640\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2640\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2640\u200D\u27A1", 1},
+{"\U0001F3C3\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FB\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FC\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FD\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FE\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2642\u200D\u27A1\uFE0F", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2642\uFE0F\u200D\u27A1", 1},
+{"\U0001F3C3\U0001F3FF\u200D\u2642\u200D\u27A1", 1},
+{"\U0001F483\U0001F3FB", 1},
+{"\U0001F483\U0001F3FC", 1},
+{"\U0001F483\U0001F3FD", 1},
+{"\U0001F483\U0001F3FE", 1},
+{"\U0001F483\U0001F3FF", 1},
+{"\U0001F57A\U0001F3FB", 1},
+{"\U0001F57A\U0001F3FC", 1},
+{"\U0001F57A\U0001F3FD", 1},
+{"\U0001F57A\U0001F3FE", 1},
+{"\U0001F57A\U0001F3FF", 1},
+{"\U0001F574\uFE0F", 1},
+{"\U0001F574\U0001F3FB", 1},
+{"\U0001F574\U0001F3FC", 1},
+{"\U0001F574\U0001F3FD", 1},
+{"\U0001F574\U0001F3FE", 1},
+{"\U0001F574\U0001F3FF", 1},
+{"\U0001F46F\u200D\u2642\uFE0F", 1},
+{"\U0001F46F\u200D\u2642", 1},
+{"\U0001F46F\u200D\u2640\uFE0F", 1},
+{"\U0001F46F\u200D\u2640", 1},
+{"\U0001F9D6\U0001F3FB", 1},
+{"\U0001F9D6\U0001F3FC", 1},
+{"\U0001F9D6\U0001F3FD", 1},
+{"\U0001F9D6\U0001F3FE", 1},
+{"\U0001F9D6\U0001F3FF", 1},
+{"\U0001F9D6\u200D\u2642\uFE0F", 1},
+{"\U0001F9D6\u200D\u2642", 1},
+{"\U0001F9D6\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9D6\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9D6\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9D6\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9D6\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9D6\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9D6\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9D6\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9D6\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9D6\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9D6\u200D\u2640\uFE0F", 1},
+{"\U0001F9D6\u200D\u2640", 1},
+{"\U0001F9D6\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9D6\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9D6\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9D6\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9D6\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9D6\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9D6\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9D6\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9D6\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9D6\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9D7\U0001F3FB", 1},
+{"\U0001F9D7\U0001F3FC", 1},
+{"\U0001F9D7\U0001F3FD", 1},
+{"\U0001F9D7\U0001F3FE", 1},
+{"\U0001F9D7\U0001F3FF", 1},
+{"\U0001F9D7\u200D\u2642\uFE0F", 1},
+{"\U0001F9D7\u200D\u2642", 1},
+{"\U0001F9D7\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9D7\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9D7\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9D7\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9D7\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9D7\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9D7\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9D7\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9D7\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9D7\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9D7\u200D\u2640\uFE0F", 1},
+{"\U0001F9D7\u200D\u2640", 1},
+{"\U0001F9D7\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9D7\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9D7\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9D7\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9D7\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9D7\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9D7\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9D7\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9D7\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9D7\U0001F3FF\u200D\u2640", 1},
+{"\U0001F3C7\U0001F3FB", 1},
+{"\U0001F3C7\U0001F3FC", 1},
+{"\U0001F3C7\U0001F3FD", 1},
+{"\U0001F3C7\U0001F3FE", 1},
+{"\U0001F3C7\U0001F3FF", 1},
+{"\u26F7\uFE0F", 1},
+{"\U0001F3C2\U0001F3FB", 1},
+{"\U0001F3C2\U0001F3FC", 1},
+{"\U0001F3C2\U0001F3FD", 1},
+{"\U0001F3C2\U0001F3FE", 1},
+{"\U0001F3C2\U0001F3FF", 1},
+{"\U0001F3CC\uFE0F", 1},
+{"\U0001F3CC\U0001F3FB", 1},
+{"\U0001F3CC\U0001F3FC", 1},
+{"\U0001F3CC\U0001F3FD", 1},
+{"\U0001F3CC\U0001F3FE", 1},
+{"\U0001F3CC\U0001F3FF", 1},
+{"\U0001F3CC\uFE0F\u200D\u2642\uFE0F", 1},
+{"\U0001F3CC\u200D\u2642\uFE0F", 1},
+{"\U0001F3CC\uFE0F\u200D\u2642", 1},
+{"\U0001F3CC\u200D\u2642", 1},
+{"\U0001F3CC\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F3CC\U0001F3FB\u200D\u2642", 1},
+{"\U0001F3CC\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F3CC\U0001F3FC\u200D\u2642", 1},
+{"\U0001F3CC\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F3CC\U0001F3FD\u200D\u2642", 1},
+{"\U0001F3CC\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F3CC\U0001F3FE\u200D\u2642", 1},
+{"\U0001F3CC\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F3CC\U0001F3FF\u200D\u2642", 1},
+{"\U0001F3CC\uFE0F\u200D\u2640\uFE0F", 1},
+{"\U0001F3CC\u200D\u2640\uFE0F", 1},
+{"\U0001F3CC\uFE0F\u200D\u2640", 1},
+{"\U0001F3CC\u200D\u2640", 1},
+{"\U0001F3CC\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F3CC\U0001F3FB\u200D\u2640", 1},
+{"\U0001F3CC\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F3CC\U0001F3FC\u200D\u2640", 1},
+{"\U0001F3CC\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F3CC\U0001F3FD\u200D\u2640", 1},
+{"\U0001F3CC\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F3CC\U0001F3FE\u200D\u2640", 1},
+{"\U0001F3CC\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F3CC\U0001F3FF\u200D\u2640", 1},
+{"\U0001F3C4\U0001F3FB", 1},
+{"\U0001F3C4\U0001F3FC", 1},
+{"\U0001F3C4\U0001F3FD", 1},
+{"\U0001F3C4\U0001F3FE", 1},
+{"\U0001F3C4\U0001F3FF", 1},
+{"\U0001F3C4\u200D\u2642\uFE0F", 1},
+{"\U0001F3C4\u200D\u2642", 1},
+{"\U0001F3C4\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F3C4\U0001F3FB\u200D\u2642", 1},
+{"\U0001F3C4\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F3C4\U0001F3FC\u200D\u2642", 1},
+{"\U0001F3C4\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F3C4\U0001F3FD\u200D\u2642", 1},
+{"\U0001F3C4\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F3C4\U0001F3FE\u200D\u2642", 1},
+{"\U0001F3C4\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F3C4\U0001F3FF\u200D\u2642", 1},
+{"\U0001F3C4\u200D\u2640\uFE0F", 1},
+{"\U0001F3C4\u200D\u2640", 1},
+{"\U0001F3C4\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F3C4\U0001F3FB\u200D\u2640", 1},
+{"\U0001F3C4\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F3C4\U0001F3FC\u200D\u2640", 1},
+{"\U0001F3C4\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F3C4\U0001F3FD\u200D\u2640", 1},
+{"\U0001F3C4\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F3C4\U0001F3FE\u200D\u2640", 1},
+{"\U0001F3C4\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F3C4\U0001F3FF\u200D\u2640", 1},
+{"\U0001F6A3\U0001F3FB", 1},
+{"\U0001F6A3\U0001F3FC", 1},
+{"\U0001F6A3\U0001F3FD", 1},
+{"\U0001F6A3\U0001F3FE", 1},
+{"\U0001F6A3\U0001F3FF", 1},
+{"\U0001F6A3\u200D\u2642\uFE0F", 1},
+{"\U0001F6A3\u200D\u2642", 1},
+{"\U0001F6A3\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F6A3\U0001F3FB\u200D\u2642", 1},
+{"\U0001F6A3\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F6A3\U0001F3FC\u200D\u2642", 1},
+{"\U0001F6A3\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F6A3\U0001F3FD\u200D\u2642", 1},
+{"\U0001F6A3\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F6A3\U0001F3FE\u200D\u2642", 1},
+{"\U0001F6A3\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F6A3\U0001F3FF\u200D\u2642", 1},
+{"\U0001F6A3\u200D\u2640\uFE0F", 1},
+{"\U0001F6A3\u200D\u2640", 1},
+{"\U0001F6A3\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F6A3\U0001F3FB\u200D\u2640", 1},
+{"\U0001F6A3\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F6A3\U0001F3FC\u200D\u2640", 1},
+{"\U0001F6A3\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F6A3\U0001F3FD\u200D\u2640", 1},
+{"\U0001F6A3\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F6A3\U0001F3FE\u200D\u2640", 1},
+{"\U0001F6A3\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F6A3\U0001F3FF\u200D\u2640", 1},
+{"\U0001F3CA\U0001F3FB", 1},
+{"\U0001F3CA\U0001F3FC", 1},
+{"\U0001F3CA\U0001F3FD", 1},
+{"\U0001F3CA\U0001F3FE", 1},
+{"\U0001F3CA\U0001F3FF", 1},
+{"\U0001F3CA\u200D\u2642\uFE0F", 1},
+{"\U0001F3CA\u200D\u2642", 1},
+{"\U0001F3CA\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F3CA\U0001F3FB\u200D\u2642", 1},
+{"\U0001F3CA\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F3CA\U0001F3FC\u200D\u2642", 1},
+{"\U0001F3CA\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F3CA\U0001F3FD\u200D\u2642", 1},
+{"\U0001F3CA\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F3CA\U0001F3FE\u200D\u2642", 1},
+{"\U0001F3CA\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F3CA\U0001F3FF\u200D\u2642", 1},
+{"\U0001F3CA\u200D\u2640\uFE0F", 1},
+{"\U0001F3CA\u200D\u2640", 1},
+{"\U0001F3CA\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F3CA\U0001F3FB\u200D\u2640", 1},
+{"\U0001F3CA\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F3CA\U0001F3FC\u200D\u2640", 1},
+{"\U0001F3CA\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F3CA\U0001F3FD\u200D\u2640", 1},
+{"\U0001F3CA\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F3CA\U0001F3FE\u200D\u2640", 1},
+{"\U0001F3CA\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F3CA\U0001F3FF\u200D\u2640", 1},
+{"\u26F9\uFE0F", 1},
+{"\u26F9\U0001F3FB", 1},
+{"\u26F9\U0001F3FC", 1},
+{"\u26F9\U0001F3FD", 1},
+{"\u26F9\U0001F3FE", 1},
+{"\u26F9\U0001F3FF", 1},
+{"\u26F9\uFE0F\u200D\u2642\uFE0F", 1},
+{"\u26F9\u200D\u2642\uFE0F", 1},
+{"\u26F9\uFE0F\u200D\u2642", 1},
+{"\u26F9\u200D\u2642", 1},
+{"\u26F9\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\u26F9\U0001F3FB\u200D\u2642", 1},
+{"\u26F9\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\u26F9\U0001F3FC\u200D\u2642", 1},
+{"\u26F9\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\u26F9\U0001F3FD\u200D\u2642", 1},
+{"\u26F9\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\u26F9\U0001F3FE\u200D\u2642", 1},
+{"\u26F9\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\u26F9\U0001F3FF\u200D\u2642", 1},
+{"\u26F9\uFE0F\u200D\u2640\uFE0F", 1},
+{"\u26F9\u200D\u2640\uFE0F", 1},
+{"\u26F9\uFE0F\u200D\u2640", 1},
+{"\u26F9\u200D\u2640", 1},
+{"\u26F9\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\u26F9\U0001F3FB\u200D\u2640", 1},
+{"\u26F9\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\u26F9\U0001F3FC\u200D\u2640", 1},
+{"\u26F9\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\u26F9\U0001F3FD\u200D\u2640", 1},
+{"\u26F9\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\u26F9\U0001F3FE\u200D\u2640", 1},
+{"\u26F9\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\u26F9\U0001F3FF\u200D\u2640", 1},
+{"\U0001F3CB\uFE0F", 1},
+{"\U0001F3CB\U0001F3FB", 1},
+{"\U0001F3CB\U0001F3FC", 1},
+{"\U0001F3CB\U0001F3FD", 1},
+{"\U0001F3CB\U0001F3FE", 1},
+{"\U0001F3CB\U0001F3FF", 1},
+{"\U0001F3CB\uFE0F\u200D\u2642\uFE0F", 1},
+{"\U0001F3CB\u200D\u2642\uFE0F", 1},
+{"\U0001F3CB\uFE0F\u200D\u2642", 1},
+{"\U0001F3CB\u200D\u2642", 1},
+{"\U0001F3CB\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F3CB\U0001F3FB\u200D\u2642", 1},
+{"\U0001F3CB\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F3CB\U0001F3FC\u200D\u2642", 1},
+{"\U0001F3CB\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F3CB\U0001F3FD\u200D\u2642", 1},
+{"\U0001F3CB\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F3CB\U0001F3FE\u200D\u2642", 1},
+{"\U0001F3CB\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F3CB\U0001F3FF\u200D\u2642", 1},
+{"\U0001F3CB\uFE0F\u200D\u2640\uFE0F", 1},
+{"\U0001F3CB\u200D\u2640\uFE0F", 1},
+{"\U0001F3CB\uFE0F\u200D\u2640", 1},
+{"\U0001F3CB\u200D\u2640", 1},
+{"\U0001F3CB\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F3CB\U0001F3FB\u200D\u2640", 1},
+{"\U0001F3CB\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F3CB\U0001F3FC\u200D\u2640", 1},
+{"\U0001F3CB\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F3CB\U0001F3FD\u200D\u2640", 1},
+{"\U0001F3CB\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F3CB\U0001F3FE\u200D\u2640", 1},
+{"\U0001F3CB\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F3CB\U0001F3FF\u200D\u2640", 1},
+{"\U0001F6B4\U0001F3FB", 1},
+{"\U0001F6B4\U0001F3FC", 1},
+{"\U0001F6B4\U0001F3FD", 1},
+{"\U0001F6B4\U0001F3FE", 1},
+{"\U0001F6B4\U0001F3FF", 1},
+{"\U0001F6B4\u200D\u2642\uFE0F", 1},
+{"\U0001F6B4\u200D\u2642", 1},
+{"\U0001F6B4\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F6B4\U0001F3FB\u200D\u2642", 1},
+{"\U0001F6B4\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F6B4\U0001F3FC\u200D\u2642", 1},
+{"\U0001F6B4\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F6B4\U0001F3FD\u200D\u2642", 1},
+{"\U0001F6B4\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F6B4\U0001F3FE\u200D\u2642", 1},
+{"\U0001F6B4\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F6B4\U0001F3FF\u200D\u2642", 1},
+{"\U0001F6B4\u200D\u2640\uFE0F", 1},
+{"\U0001F6B4\u200D\u2640", 1},
+{"\U0001F6B4\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F6B4\U0001F3FB\u200D\u2640", 1},
+{"\U0001F6B4\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F6B4\U0001F3FC\u200D\u2640", 1},
+{"\U0001F6B4\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F6B4\U0001F3FD\u200D\u2640", 1},
+{"\U0001F6B4\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F6B4\U0001F3FE\u200D\u2640", 1},
+{"\U0001F6B4\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F6B4\U0001F3FF\u200D\u2640", 1},
+{"\U0001F6B5\U0001F3FB", 1},
+{"\U0001F6B5\U0001F3FC", 1},
+{"\U0001F6B5\U0001F3FD", 1},
+{"\U0001F6B5\U0001F3FE", 1},
+{"\U0001F6B5\U0001F3FF", 1},
+{"\U0001F6B5\u200D\u2642\uFE0F", 1},
+{"\U0001F6B5\u200D\u2642", 1},
+{"\U0001F6B5\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F6B5\U0001F3FB\u200D\u2642", 1},
+{"\U0001F6B5\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F6B5\U0001F3FC\u200D\u2642", 1},
+{"\U0001F6B5\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F6B5\U0001F3FD\u200D\u2642", 1},
+{"\U0001F6B5\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F6B5\U0001F3FE\u200D\u2642", 1},
+{"\U0001F6B5\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F6B5\U0001F3FF\u200D\u2642", 1},
+{"\U0001F6B5\u200D\u2640\uFE0F", 1},
+{"\U0001F6B5\u200D\u2640", 1},
+{"\U0001F6B5\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F6B5\U0001F3FB\u200D\u2640", 1},
+{"\U0001F6B5\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F6B5\U0001F3FC\u200D\u2640", 1},
+{"\U0001F6B5\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F6B5\U0001F3FD\u200D\u2640", 1},
+{"\U0001F6B5\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F6B5\U0001F3FE\u200D\u2640", 1},
+{"\U0001F6B5\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F6B5\U0001F3FF\u200D\u2640", 1},
+{"\U0001F938\U0001F3FB", 1},
+{"\U0001F938\U0001F3FC", 1},
+{"\U0001F938\U0001F3FD", 1},
+{"\U0001F938\U0001F3FE", 1},
+{"\U0001F938\U0001F3FF", 1},
+{"\U0001F938\u200D\u2642\uFE0F", 1},
+{"\U0001F938\u200D\u2642", 1},
+{"\U0001F938\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F938\U0001F3FB\u200D\u2642", 1},
+{"\U0001F938\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F938\U0001F3FC\u200D\u2642", 1},
+{"\U0001F938\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F938\U0001F3FD\u200D\u2642", 1},
+{"\U0001F938\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F938\U0001F3FE\u200D\u2642", 1},
+{"\U0001F938\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F938\U0001F3FF\u200D\u2642", 1},
+{"\U0001F938\u200D\u2640\uFE0F", 1},
+{"\U0001F938\u200D\u2640", 1},
+{"\U0001F938\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F938\U0001F3FB\u200D\u2640", 1},
+{"\U0001F938\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F938\U0001F3FC\u200D\u2640", 1},
+{"\U0001F938\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F938\U0001F3FD\u200D\u2640", 1},
+{"\U0001F938\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F938\U0001F3FE\u200D\u2640", 1},
+{"\U0001F938\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F938\U0001F3FF\u200D\u2640", 1},
+{"\U0001F93C\u200D\u2642\uFE0F", 1},
+{"\U0001F93C\u200D\u2642", 1},
+{"\U0001F93C\u200D\u2640\uFE0F", 1},
+{"\U0001F93C\u200D\u2640", 1},
+{"\U0001F93D\U0001F3FB", 1},
+{"\U0001F93D\U0001F3FC", 1},
+{"\U0001F93D\U0001F3FD", 1},
+{"\U0001F93D\U0001F3FE", 1},
+{"\U0001F93D\U0001F3FF", 1},
+{"\U0001F93D\u200D\u2642\uFE0F", 1},
+{"\U0001F93D\u200D\u2642", 1},
+{"\U0001F93D\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F93D\U0001F3FB\u200D\u2642", 1},
+{"\U0001F93D\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F93D\U0001F3FC\u200D\u2642", 1},
+{"\U0001F93D\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F93D\U0001F3FD\u200D\u2642", 1},
+{"\U0001F93D\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F93D\U0001F3FE\u200D\u2642", 1},
+{"\U0001F93D\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F93D\U0001F3FF\u200D\u2642", 1},
+{"\U0001F93D\u200D\u2640\uFE0F", 1},
+{"\U0001F93D\u200D\u2640", 1},
+{"\U0001F93D\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F93D\U0001F3FB\u200D\u2640", 1},
+{"\U0001F93D\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F93D\U0001F3FC\u200D\u2640", 1},
+{"\U0001F93D\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F93D\U0001F3FD\u200D\u2640", 1},
+{"\U0001F93D\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F93D\U0001F3FE\u200D\u2640", 1},
+{"\U0001F93D\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F93D\U0001F3FF\u200D\u2640", 1},
+{"\U0001F93E\U0001F3FB", 1},
+{"\U0001F93E\U0001F3FC", 1},
+{"\U0001F93E\U0001F3FD", 1},
+{"\U0001F93E\U0001F3FE", 1},
+{"\U0001F93E\U0001F3FF", 1},
+{"\U0001F93E\u200D\u2642\uFE0F", 1},
+{"\U0001F93E\u200D\u2642", 1},
+{"\U0001F93E\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F93E\U0001F3FB\u200D\u2642", 1},
+{"\U0001F93E\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F93E\U0001F3FC\u200D\u2642", 1},
+{"\U0001F93E\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F93E\U0001F3FD\u200D\u2642", 1},
+{"\U0001F93E\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F93E\U0001F3FE\u200D\u2642", 1},
+{"\U0001F93E\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F93E\U0001F3FF\u200D\u2642", 1},
+{"\U0001F93E\u200D\u2640\uFE0F", 1},
+{"\U0001F93E\u200D\u2640", 1},
+{"\U0001F93E\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F93E\U0001F3FB\u200D\u2640", 1},
+{"\U0001F93E\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F93E\U0001F3FC\u200D\u2640", 1},
+{"\U0001F93E\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F93E\U0001F3FD\u200D\u2640", 1},
+{"\U0001F93E\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F93E\U0001F3FE\u200D\u2640", 1},
+{"\U0001F93E\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F93E\U0001F3FF\u200D\u2640", 1},
+{"\U0001F939\U0001F3FB", 1},
+{"\U0001F939\U0001F3FC", 1},
+{"\U0001F939\U0001F3FD", 1},
+{"\U0001F939\U0001F3FE", 1},
+{"\U0001F939\U0001F3FF", 1},
+{"\U0001F939\u200D\u2642\uFE0F", 1},
+{"\U0001F939\u200D\u2642", 1},
+{"\U0001F939\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F939\U0001F3FB\u200D\u2642", 1},
+{"\U0001F939\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F939\U0001F3FC\u200D\u2642", 1},
+{"\U0001F939\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F939\U0001F3FD\u200D\u2642", 1},
+{"\U0001F939\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F939\U0001F3FE\u200D\u2642", 1},
+{"\U0001F939\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F939\U0001F3FF\u200D\u2642", 1},
+{"\U0001F939\u200D\u2640\uFE0F", 1},
+{"\U0001F939\u200D\u2640", 1},
+{"\U0001F939\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F939\U0001F3FB\u200D\u2640", 1},
+{"\U0001F939\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F939\U0001F3FC\u200D\u2640", 1},
+{"\U0001F939\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F939\U0001F3FD\u200D\u2640", 1},
+{"\U0001F939\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F939\U0001F3FE\u200D\u2640", 1},
+{"\U0001F939\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F939\U0001F3FF\u200D\u2640", 1},
+{"\U0001F9D8\U0001F3FB", 1},
+{"\U0001F9D8\U0001F3FC", 1},
+{"\U0001F9D8\U0001F3FD", 1},
+{"\U0001F9D8\U0001F3FE", 1},
+{"\U0001F9D8\U0001F3FF", 1},
+{"\U0001F9D8\u200D\u2642\uFE0F", 1},
+{"\U0001F9D8\u200D\u2642", 1},
+{"\U0001F9D8\U0001F3FB\u200D\u2642\uFE0F", 1},
+{"\U0001F9D8\U0001F3FB\u200D\u2642", 1},
+{"\U0001F9D8\U0001F3FC\u200D\u2642\uFE0F", 1},
+{"\U0001F9D8\U0001F3FC\u200D\u2642", 1},
+{"\U0001F9D8\U0001F3FD\u200D\u2642\uFE0F", 1},
+{"\U0001F9D8\U0001F3FD\u200D\u2642", 1},
+{"\U0001F9D8\U0001F3FE\u200D\u2642\uFE0F", 1},
+{"\U0001F9D8\U0001F3FE\u200D\u2642", 1},
+{"\U0001F9D8\U0001F3FF\u200D\u2642\uFE0F", 1},
+{"\U0001F9D8\U0001F3FF\u200D\u2642", 1},
+{"\U0001F9D8\u200D\u2640\uFE0F", 1},
+{"\U0001F9D8\u200D\u2640", 1},
+{"\U0001F9D8\U0001F3FB\u200D\u2640\uFE0F", 1},
+{"\U0001F9D8\U0001F3FB\u200D\u2640", 1},
+{"\U0001F9D8\U0001F3FC\u200D\u2640\uFE0F", 1},
+{"\U0001F9D8\U0001F3FC\u200D\u2640", 1},
+{"\U0001F9D8\U0001F3FD\u200D\u2640\uFE0F", 1},
+{"\U0001F9D8\U0001F3FD\u200D\u2640", 1},
+{"\U0001F9D8\U0001F3FE\u200D\u2640\uFE0F", 1},
+{"\U0001F9D8\U0001F3FE\u200D\u2640", 1},
+{"\U0001F9D8\U0001F3FF\u200D\u2640\uFE0F", 1},
+{"\U0001F9D8\U0001F3FF\u200D\u2640", 1},
+{"\U0001F6C0\U0001F3FB", 1},
+{"\U0001F6C0\U0001F3FC", 1},
+{"\U0001F6C0\U0001F3FD", 1},
+{"\U0001F6C0\U0001F3FE", 1},
+{"\U0001F6C0\U0001F3FF", 1},
+{"\U0001F6CC\U0001F3FB", 1},
+{"\U0001F6CC\U0001F3FC", 1},
+{"\U0001F6CC\U0001F3FD", 1},
+{"\U0001F6CC\U0001F3FE", 1},
+{"\U0001F6CC\U0001F3FF", 1},
+{"\U0001F9D1\u200D\U0001F91D\u200D\U0001F9D1", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FB\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FC\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FD\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FE\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FF\u200D\U0001F91D\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F46D\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F46D\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F46D\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F46D\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F46D\U0001F3FF", 1},
+{"\U0001F46B\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F46B\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F46B\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F46B\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F46B\U0001F3FF", 1},
+{"\U0001F46C\U0001F3FB", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FB\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F46C\U0001F3FC", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FC\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F46C\U0001F3FD", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FD\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F46C\U0001F3FE", 1},
+{"\U0001F468\U0001F3FE\u200D\U0001F91D\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FF\u200D\U0001F91D\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F46C\U0001F3FF", 1},
+{"\U0001F48F\U0001F3FB", 1},
+{"\U0001F48F\U0001F3FC", 1},
+{"\U0001F48F\U0001F3FD", 1},
+{"\U0001F48F\U0001F3FE", 1},
+{"\U0001F48F\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F469\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468", 1},
+{"\U0001F469\u200D\u2764\u200D\U0001F48B\u200D\U0001F468", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468", 1},
+{"\U0001F468\u200D\u2764\u200D\U0001F48B\u200D\U0001F468", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469", 1},
+{"\U0001F469\u200D\u2764\u200D\U0001F48B\u200D\U0001F469", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F48B\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F491\U0001F3FB", 1},
+{"\U0001F491\U0001F3FC", 1},
+{"\U0001F491\U0001F3FD", 1},
+{"\U0001F491\U0001F3FE", 1},
+{"\U0001F491\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FB\u200D\u2764\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FC\u200D\u2764\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FD\u200D\u2764\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FE\u200D\u2764\u200D\U0001F9D1\U0001F3FF", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F9D1\U0001F3FB", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F9D1\U0001F3FC", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F9D1\U0001F3FD", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F9D1\U0001F3FF\u200D\u2764\u200D\U0001F9D1\U0001F3FE", 1},
+{"\U0001F469\u200D\u2764\uFE0F\u200D\U0001F468", 1},
+{"\U0001F469\u200D\u2764\u200D\U0001F468", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\u200D\u2764\uFE0F\u200D\U0001F468", 1},
+{"\U0001F468\u200D\u2764\u200D\U0001F468", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FB\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FC\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FD\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FE\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FB", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FC", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FD", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FE", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F468\U0001F3FF\u200D\u2764\u200D\U0001F468\U0001F3FF", 1},
+{"\U0001F469\u200D\u2764\uFE0F\u200D\U0001F469", 1},
+{"\U0001F469\u200D\u2764\u200D\U0001F469", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FB\u200D\u2764\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FC\u200D\u2764\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FD\u200D\u2764\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FE\u200D\u2764\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FB", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FC", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FD", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FE", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\uFE0F\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F469\U0001F3FF\u200D\u2764\u200D\U0001F469\U0001F3FF", 1},
+{"\U0001F468\u200D\U0001F469\u200D\U0001F466", 1},
+{"\U0001F468\u200D\U0001F469\u200D\U0001F467", 1},
+{"\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 1},
+{"\U0001F468\u200D\U0001F469\u200D\U0001F466\u200D\U0001F466", 1},
+{"\U0001F468\u200D\U0001F469\u200D\U0001F467\u200D\U0001F467", 1},
+{"\U0001F468\u200D\U0001F468\u200D\U0001F466", 1},
+{"\U0001F468\u200D\U0001F468\u200D\U0001F467", 1},
+{"\U0001F468\u200D\U0001F468\u200D\U0001F467\u200D\U0001F466", 1},
+{"\U0001F468\u200D\U0001F468\u200D\U0001F466\u200D\U0001F466", 1},
+{"\U0001F468\u200D\U0001F468\u200D\U0001F467\u200D\U0001F467", 1},
+{"\U0001F469\u200D\U0001F469\u200D\U0001F466", 1},
+{"\U0001F469\u200D\U0001F469\u200D\U0001F467", 1},
+{"\U0001F469\u200D\U0001F469\u200D\U0001F467\u200D\U0001F466", 1},
+{"\U0001F469\u200D\U0001F469\u200D\U0001F466\u200D\U0001F466", 1},
+{"\U0001F469\u200D\U0001F469\u200D\U0001F467\u200D\U0001F467", 1},
+{"\U0001F468\u200D\U0001F466", 1},
+{"\U0001F468\u200D\U0001F466\u200D\U0001F466", 1},
+{"\U0001F468\u200D\U0001F467", 1},
+{"\U0001F468\u200D\U0001F467\u200D\U0001F466", 1},
+{"\U0001F468\u200D\U0001F467\u200D\U0001F467", 1},
+{"\U0001F469\u200D\U0001F466", 1},
+{"\U0001F469\u200D\U0001F466\u200D\U0001F466", 1},
+{"\U0001F469\u200D\U0001F467", 1},
+{"\U0001F469\u200D\U0001F467\u200D\U0001F466", 1},
+{"\U0001F469\u200D\U0001F467\u200D\U0001F467", 1},
+{"\U0001F5E3\uFE0F", 1},
+{"\U0001F9D1\u200D\U0001F9D1\u200D\U0001F9D2", 1},
+{"\U0001F9D1\u200D\U0001F9D1\u200D\U0001F9D2\u200D\U0001F9D2", 1},
+{"\U0001F9D1\u200D\U0001F9D2", 1},
+{"\U0001F9D1\u200D\U0001F9D2\u200D\U0001F9D2", 1},
+{"\U0001F415\u200D\U0001F9BA", 1},
+{"\U0001F408\u200D\u2B1B", 1},
+{"\U0001F43F\uFE0F", 1},
+{"\U0001F43B\u200D\u2744\uFE0F", 1},
+{"\U0001F43B\u200D\u2744", 1},
+{"\U0001F54A\uFE0F", 1},
+{"\U0001F426\u200D\u2B1B", 1},
+{"\U0001F426\u200D\U0001F525", 1},
+{"\U0001F577\uFE0F", 1},
+{"\U0001F578\uFE0F", 1},
+{"\U0001F3F5\uFE0F", 1},
+{"\u2618\uFE0F", 1},
+{"\U0001F34B\u200D\U0001F7E9", 1},
+{"\U0001F336\uFE0F", 1},
+{"\U0001F344\u200D\U0001F7EB", 1},
+{"\U0001F37D\uFE0F", 1},
+{"\U0001F5FA\uFE0F", 1},
+{"\U0001F3D4\uFE0F", 1},
+{"\u26F0\uFE0F", 1},
+{"\U0001F3D5\uFE0F", 1},
+{"\U0001F3D6\uFE0F", 1},
+{"\U0001F3DC\uFE0F", 1},
+{"\U0001F3DD\uFE0F", 1},
+{"\U0001F3DE\uFE0F", 1},
+{"\U0001F3DF\uFE0F", 1},
+{"\U0001F3DB\uFE0F", 1},
+{"\U0001F3D7\uFE0F", 1},
+{"\U0001F3D8\uFE0F", 1},
+{"\U0001F3DA\uFE0F", 1},
+{"\u26E9\uFE0F", 1},
+{"\U0001F3D9\uFE0F", 1},
+{"\u2668\uFE0F", 1},
+{"\U0001F3CE\uFE0F", 1},
+{"\U0001F3CD\uFE0F", 1},
+{"\U0001F6E3\uFE0F", 1},
+{"\U0001F6E4\uFE0F", 1},
+{"\U0001F6E2\uFE0F", 1},
+{"\U0001F6F3\uFE0F", 1},
+{"\u26F4\uFE0F", 1},
+{"\U0001F6E5\uFE0F", 1},
+{"\u2708\uFE0F", 1},
+{"\U0001F6E9\uFE0F", 1},
+{"\U0001F6F0\uFE0F", 1},
+{"\U0001F6CE\uFE0F", 1},
+{"\u23F1\uFE0F", 1},
+{"\u23F2\uFE0F", 1},
+{"\U0001F570\uFE0F", 1},
+{"\U0001F321\uFE0F", 1},
+{"\u2600\uFE0F", 1},
+{"\u2601\uFE0F", 1},
+{"\u26C8\uFE0F", 1},
+{"\U0001F324\uFE0F", 1},
+{"\U0001F325\uFE0F", 1},
+{"\U0001F326\uFE0F", 1},
+{"\U0001F327\uFE0F", 1},
+{"\U0001F328\uFE0F", 1},
+{"\U0001F329\uFE0F", 1},
+{"\U0001F32A\uFE0F", 1},
+{"\U0001F32B\uFE0F", 1},
+{"\U0001F32C\uFE0F", 1},
+{"\u2602\uFE0F", 1},
+{"\u26F1\uFE0F", 1},
+{"\u2744\uFE0F", 1},
+{"\u2603\uFE0F", 1},
+{"\u2604\uFE0F", 1},
+{"\U0001F397\uFE0F", 1},
+{"\U0001F39F\uFE0F", 1},
+{"\U0001F396\uFE0F", 1},
+{"\u26F8\uFE0F", 1},
+{"\U0001F579\uFE0F", 1},
+{"\u2660\uFE0F", 1},
+{"\u2665\uFE0F", 1},
+{"\u2666\uFE0F", 1},
+{"\u2663\uFE0F", 1},
+{"\u265F\uFE0F", 1},
+{"\U0001F5BC\uFE0F", 1},
+{"\U0001F576\uFE0F", 1},
+{"\U0001F6CD\uFE0F", 1},
+{"\u26D1\uFE0F", 1},
+{"\U0001F399\uFE0F", 1},
+{"\U0001F39A\uFE0F", 1},
+{"\U0001F39B\uFE0F", 1},
+{"\u260E\uFE0F", 1},
+{"\U0001F5A5\uFE0F", 1},
+{"\U0001F5A8\uFE0F", 1},
+{"\u2328\uFE0F", 1},
+{"\U0001F5B1\uFE0F", 1},
+{"\U0001F5B2\uFE0F", 1},
+{"\U0001F39E\uFE0F", 1},
+{"\U0001F4FD\uFE0F", 1},
+{"\U0001F56F\uFE0F", 1},
+{"\U0001F5DE\uFE0F", 1},
+{"\U0001F3F7\uFE0F", 1},
+{"\u2709\uFE0F", 1},
+{"\U0001F5F3\uFE0F", 1},
+{"\u270F\uFE0F", 1},
+{"\u2712\uFE0F", 1},
+{"\U0001F58B\uFE0F", 1},
+{"\U0001F58A\uFE0F", 1},
+{"\U0001F58C\uFE0F", 1},
+{"\U0001F58D\uFE0F", 1},
+{"\U0001F5C2\uFE0F", 1},
+{"\U0001F5D2\uFE0F", 1},
+{"\U0001F5D3\uFE0F", 1},
+{"\U0001F587\uFE0F", 1},
+{"\u2702\uFE0F", 1},
+{"\U0001F5C3\uFE0F", 1},
+{"\U0001F5C4\uFE0F", 1},
+{"\U0001F5D1\uFE0F", 1},
+{"\U0001F5DD\uFE0F", 1},
+{"\u26CF\uFE0F", 1},
+{"\u2692\uFE0F", 1},
+{"\U0001F6E0\uFE0F", 1},
+{"\U0001F5E1\uFE0F", 1},
+{"\u2694\uFE0F", 1},
+{"\U0001F6E1\uFE0F", 1},
+{"\u2699\uFE0F", 1},
+{"\U0001F5DC\uFE0F", 1},
+{"\u2696\uFE0F", 1},
+{"\u26D3\uFE0F\u200D\U0001F4A5", 1},
+{"\u26D3\u200D\U0001F4A5", 1},
+{"\u26D3\uFE0F", 1},
+{"\u2697\uFE0F", 1},
+{"\U0001F6CF\uFE0F", 1},
+{"\U0001F6CB\uFE0F", 1},
+{"\u26B0\uFE0F", 1},
+{"\u26B1\uFE0F", 1},
+{"\u26A0\uFE0F", 1},
+{"\u2622\uFE0F", 1},
+{"\u2623\uFE0F", 1},
+{"\u2B06\uFE0F", 1},
+{"\u2197\uFE0F", 1},
+{"\u27A1\uFE0F", 1},
+{"\u2198\uFE0F", 1},
+{"\u2B07\uFE0F", 1},
+{"\u2199\uFE0F", 1},
+{"\u2B05\uFE0F", 1},
+{"\u2196\uFE0F", 1},
+{"\u2195\uFE0F", 1},
+{"\u2194\uFE0F", 1},
+{"\u21A9\uFE0F", 1},
+{"\u21AA\uFE0F", 1},
+{"\u2934\uFE0F", 1},
+{"\u2935\uFE0F", 1},
+{"\u269B\uFE0F", 1},
+{"\U0001F549\uFE0F", 1},
+{"\u2721\uFE0F", 1},
+{"\u2638\uFE0F", 1},
+{"\u262F\uFE0F", 1},
+{"\u271D\uFE0F", 1},
+{"\u2626\uFE0F", 1},
+{"\u262A\uFE0F", 1},
+{"\u262E\uFE0F", 1},
+{"\u25B6\uFE0F", 1},
+{"\u23ED\uFE0F", 1},
+{"\u23EF\uFE0F", 1},
+{"\u25C0\uFE0F", 1},
+{"\u23EE\uFE0F", 1},
+{"\u23F8\uFE0F", 1},
+{"\u23F9\uFE0F", 1},
+{"\u23FA\uFE0F", 1},
+{"\u23CF\uFE0F", 1},
+{"\u2640\uFE0F", 1},
+{"\u2642\uFE0F", 1},
+{"\u26A7\uFE0F", 1},
+{"\u2716\uFE0F", 1},
+{"\u267E\uFE0F", 1},
+{"\u203C\uFE0F", 1},
+{"\u2049\uFE0F", 1},
+{"\u3030\uFE0F", 1},
+{"\u2695\uFE0F", 1},
+{"\u267B\uFE0F", 1},
+{"\u269C\uFE0F", 1},
+{"\u2611\uFE0F", 1},
+{"\u2714\uFE0F", 1},
+{"\u303D\uFE0F", 1},
+{"\u2733\uFE0F", 1},
+{"\u2734\uFE0F", 1},
+{"\u2747\uFE0F", 1},
+{"\u00A9\uFE0F", 1},
+{"\u00AE\uFE0F", 1},
+{"\u2122\uFE0F", 1},
+{"\u0023\uFE0F\u20E3", 1},
+{"\u0023\u20E3", 1},
+{"\u002A\uFE0F\u20E3", 1},
+{"\u002A\u20E3", 1},
+{"\u0030\uFE0F\u20E3", 1},
+{"\u0030\u20E3", 1},
+{"\u0031\uFE0F\u20E3", 1},
+{"\u0031\u20E3", 1},
+{"\u0032\uFE0F\u20E3", 1},
+{"\u0032\u20E3", 1},
+{"\u0033\uFE0F\u20E3", 1},
+{"\u0033\u20E3", 1},
+{"\u0034\uFE0F\u20E3", 1},
+{"\u0034\u20E3", 1},
+{"\u0035\uFE0F\u20E3", 1},
+{"\u0035\u20E3", 1},
+{"\u0036\uFE0F\u20E3", 1},
+{"\u0036\u20E3", 1},
+{"\u0037\uFE0F\u20E3", 1},
+{"\u0037\u20E3", 1},
+{"\u0038\uFE0F\u20E3", 1},
+{"\u0038\u20E3", 1},
+{"\u0039\uFE0F\u20E3", 1},
+{"\u0039\u20E3", 1},
+{"\U0001F170\uFE0F", 1},
+{"\U0001F171\uFE0F", 1},
+{"\u2139\uFE0F", 1},
+{"\u24C2\uFE0F", 1},
+{"\U0001F17E\uFE0F", 1},
+{"\U0001F17F\uFE0F", 1},
+{"\U0001F202\uFE0F", 1},
+{"\U0001F237\uFE0F", 1},
+{"\u3297\uFE0F", 1},
+{"\u3299\uFE0F", 1},
+{"\u25FC\uFE0F", 1},
+{"\u25FB\uFE0F", 1},
+{"\u25AA\uFE0F", 1},
+{"\u25AB\uFE0F", 1},
+{"\U0001F3F3\uFE0F", 1},
+{"\U0001F3F3\uFE0F\u200D\U0001F308", 1},
+{"\U0001F3F3\u200D\U0001F308", 1},
+{"\U0001F3F3\uFE0F\u200D\u26A7\uFE0F", 1},
+{"\U0001F3F3\u200D\u26A7\uFE0F", 1},
+{"\U0001F3F3\uFE0F\u200D\u26A7", 1},
+{"\U0001F3F3\u200D\u26A7", 1},
+{"\U0001F3F4\u200D\u2620\uFE0F", 1},
+{"\U0001F3F4\u200D\u2620", 1},
+{"\U0001F1E6\U0001F1E8", 1},
+{"\U0001F1E6\U0001F1E9", 1},
+{"\U0001F1E6\U0001F1EA", 1},
+{"\U0001F1E6\U0001F1EB", 1},
+{"\U0001F1E6\U0001F1EC", 1},
+{"\U0001F1E6\U0001F1EE", 1},
+{"\U0001F1E6\U0001F1F1", 1},
+{"\U0001F1E6\U0001F1F2", 1},
+{"\U0001F1E6\U0001F1F4", 1},
+{"\U0001F1E6\U0001F1F6", 1},
+{"\U0001F1E6\U0001F1F7", 1},
+{"\U0001F1E6\U0001F1F8", 1},
+{"\U0001F1E6\U0001F1F9", 1},
+{"\U0001F1E6\U0001F1FA", 1},
+{"\U0001F1E6\U0001F1FC", 1},
+{"\U0001F1E6\U0001F1FD", 1},
+{"\U0001F1E6\U0001F1FF", 1},
+{"\U0001F1E7\U0001F1E6", 1},
+{"\U0001F1E7\U0001F1E7", 1},
+{"\U0001F1E7\U0001F1E9", 1},
+{"\U0001F1E7\U0001F1EA", 1},
+{"\U0001F1E7\U0001F1EB", 1},
+{"\U0001F1E7\U0001F1EC", 1},
+{"\U0001F1E7\U0001F1ED", 1},
+{"\U0001F1E7\U0001F1EE", 1},
+{"\U0001F1E7\U0001F1EF", 1},
+{"\U0001F1E7\U0001F1F1", 1},
+{"\U0001F1E7\U0001F1F2", 1},
+{"\U0001F1E7\U0001F1F3", 1},
+{"\U0001F1E7\U0001F1F4", 1},
+{"\U0001F1E7\U0001F1F6", 1},
+{"\U0001F1E7\U0001F1F7", 1},
+{"\U0001F1E7\U0001F1F8", 1},
+{"\U0001F1E7\U0001F1F9", 1},
+{"\U0001F1E7\U0001F1FB", 1},
+{"\U0001F1E7\U0001F1FC", 1},
+{"\U0001F1E7\U0001F1FE", 1},
+{"\U0001F1E7\U0001F1FF", 1},
+{"\U0001F1E8\U0001F1E6", 1},
+{"\U0001F1E8\U0001F1E8", 1},
+{"\U0001F1E8\U0001F1E9", 1},
+{"\U0001F1E8\U0001F1EB", 1},
+{"\U0001F1E8\U0001F1EC", 1},
+{"\U0001F1E8\U0001F1ED", 1},
+{"\U0001F1E8\U0001F1EE", 1},
+{"\U0001F1E8\U0001F1F0", 1},
+{"\U0001F1E8\U0001F1F1", 1},
+{"\U0001F1E8\U0001F1F2", 1},
+{"\U0001F1E8\U0001F1F3", 1},
+{"\U0001F1E8\U0001F1F4", 1},
+{"\U0001F1E8\U0001F1F5", 1},
+{"\U0001F1E8\U0001F1F7", 1},
+{"\U0001F1E8\U0001F1FA", 1},
+{"\U0001F1E8\U0001F1FB", 1},
+{"\U0001F1E8\U0001F1FC", 1},
+{"\U0001F1E8\U0001F1FD", 1},
+{"\U0001F1E8\U0001F1FE", 1},
+{"\U0001F1E8\U0001F1FF", 1},
+{"\U0001F1E9\U0001F1EA", 1},
+{"\U0001F1E9\U0001F1EC", 1},
+{"\U0001F1E9\U0001F1EF", 1},
+{"\U0001F1E9\U0001F1F0", 1},
+{"\U0001F1E9\U0001F1F2", 1},
+{"\U0001F1E9\U0001F1F4", 1},
+{"\U0001F1E9\U0001F1FF", 1},
+{"\U0001F1EA\U0001F1E6", 1},
+{"\U0001F1EA\U0001F1E8", 1},
+{"\U0001F1EA\U0001F1EA", 1},
+{"\U0001F1EA\U0001F1EC", 1},
+{"\U0001F1EA\U0001F1ED", 1},
+{"\U0001F1EA\U0001F1F7", 1},
+{"\U0001F1EA\U0001F1F8", 1},
+{"\U0001F1EA\U0001F1F9", 1},
+{"\U0001F1EA\U0001F1FA", 1},
+{"\U0001F1EB\U0001F1EE", 1},
+{"\U0001F1EB\U0001F1EF", 1},
+{"\U0001F1EB\U0001F1F0", 1},
+{"\U0001F1EB\U0001F1F2", 1},
+{"\U0001F1EB\U0001F1F4", 1},
+{"\U0001F1EB\U0001F1F7", 1},
+{"\U0001F1EC\U0001F1E6", 1},
+{"\U0001F1EC\U0001F1E7", 1},
+{"\U0001F1EC\U0001F1E9", 1},
+{"\U0001F1EC\U0001F1EA", 1},
+{"\U0001F1EC\U0001F1EB", 1},
+{"\U0001F1EC\U0001F1EC", 1},
+{"\U0001F1EC\U0001F1ED", 1},
+{"\U0001F1EC\U0001F1EE", 1},
+{"\U0001F1EC\U0001F1F1", 1},
+{"\U0001F1EC\U0001F1F2", 1},
+{"\U0001F1EC\U0001F1F3", 1},
+{"\U0001F1EC\U0001F1F5", 1},
+{"\U0001F1EC\U0001F1F6", 1},
+{"\U0001F1EC\U0001F1F7", 1},
+{"\U0001F1EC\U0001F1F8", 1},
+{"\U0001F1EC\U0001F1F9", 1},
+{"\U0001F1EC\U0001F1FA", 1},
+{"\U0001F1EC\U0001F1FC", 1},
+{"\U0001F1EC\U0001F1FE", 1},
+{"\U0001F1ED\U0001F1F0", 1},
+{"\U0001F1ED\U0001F1F2", 1},
+{"\U0001F1ED\U0001F1F3", 1},
+{"\U0001F1ED\U0001F1F7", 1},
+{"\U0001F1ED\U0001F1F9", 1},
+{"\U0001F1ED\U0001F1FA", 1},
+{"\U0001F1EE\U0001F1E8", 1},
+{"\U0001F1EE\U0001F1E9", 1},
+{"\U0001F1EE\U0001F1EA", 1},
+{"\U0001F1EE\U0001F1F1", 1},
+{"\U0001F1EE\U0001F1F2", 1},
+{"\U0001F1EE\U0001F1F3", 1},
+{"\U0001F1EE\U0001F1F4", 1},
+{"\U0001F1EE\U0001F1F6", 1},
+{"\U0001F1EE\U0001F1F7", 1},
+{"\U0001F1EE\U0001F1F8", 1},
+{"\U0001F1EE\U0001F1F9", 1},
+{"\U0001F1EF\U0001F1EA", 1},
+{"\U0001F1EF\U0001F1F2", 1},
+{"\U0001F1EF\U0001F1F4", 1},
+{"\U0001F1EF\U0001F1F5", 1},
+{"\U0001F1F0\U0001F1EA", 1},
+{"\U0001F1F0\U0001F1EC", 1},
+{"\U0001F1F0\U0001F1ED", 1},
+{"\U0001F1F0\U0001F1EE", 1},
+{"\U0001F1F0\U0001F1F2", 1},
+{"\U0001F1F0\U0001F1F3", 1},
+{"\U0001F1F0\U0001F1F5", 1},
+{"\U0001F1F0\U0001F1F7", 1},
+{"\U0001F1F0\U0001F1FC", 1},
+{"\U0001F1F0\U0001F1FE", 1},
+{"\U0001F1F0\U0001F1FF", 1},
+{"\U0001F1F1\U0001F1E6", 1},
+{"\U0001F1F1\U0001F1E7", 1},
+{"\U0001F1F1\U0001F1E8", 1},
+{"\U0001F1F1\U0001F1EE", 1},
+{"\U0001F1F1\U0001F1F0", 1},
+{"\U0001F1F1\U0001F1F7", 1},
+{"\U0001F1F1\U0001F1F8", 1},
+{"\U0001F1F1\U0001F1F9", 1},
+{"\U0001F1F1\U0001F1FA", 1},
+{"\U0001F1F1\U0001F1FB", 1},
+{"\U0001F1F1\U0001F1FE", 1},
+{"\U0001F1F2\U0001F1E6", 1},
+{"\U0001F1F2\U0001F1E8", 1},
+{"\U0001F1F2\U0001F1E9", 1},
+{"\U0001F1F2\U0001F1EA", 1},
+{"\U0001F1F2\U0001F1EB", 1},
+{"\U0001F1F2\U0001F1EC", 1},
+{"\U0001F1F2\U0001F1ED", 1},
+{"\U0001F1F2\U0001F1F0", 1},
+{"\U0001F1F2\U0001F1F1", 1},
+{"\U0001F1F2\U0001F1F2", 1},
+{"\U0001F1F2\U0001F1F3", 1},
+{"\U0001F1F2\U0001F1F4", 1},
+{"\U0001F1F2\U0001F1F5", 1},
+{"\U0001F1F2\U0001F1F6", 1},
+{"\U0001F1F2\U0001F1F7", 1},
+{"\U0001F1F2\U0001F1F8", 1},
+{"\U0001F1F2\U0001F1F9", 1},
+{"\U0001F1F2\U0001F1FA", 1},
+{"\U0001F1F2\U0001F1FB", 1},
+{"\U0001F1F2\U0001F1FC", 1},
+{"\U0001F1F2\U0001F1FD", 1},
+{"\U0001F1F2\U0001F1FE", 1},
+{"\U0001F1F2\U0001F1FF", 1},
+{"\U0001F1F3\U0001F1E6", 1},
+{"\U0001F1F3\U0001F1E8", 1},
+{"\U0001F1F3\U0001F1EA", 1},
+{"\U0001F1F3\U0001F1EB", 1},
+{"\U0001F1F3\U0001F1EC", 1},
+{"\U0001F1F3\U0001F1EE", 1},
+{"\U0001F1F3\U0001F1F1", 1},
+{"\U0001F1F3\U0001F1F4", 1},
+{"\U0001F1F3\U0001F1F5", 1},
+{"\U0001F1F3\U0001F1F7", 1},
+{"\U0001F1F3\U0001F1FA", 1},
+{"\U0001F1F3\U0001F1FF", 1},
+{"\U0001F1F4\U0001F1F2", 1},
+{"\U0001F1F5\U0001F1E6", 1},
+{"\U0001F1F5\U0001F1EA", 1},
+{"\U0001F1F5\U0001F1EB", 1},
+{"\U0001F1F5\U0001F1EC", 1},
+{"\U0001F1F5\U0001F1ED", 1},
+{"\U0001F1F5\U0001F1F0", 1},
+{"\U0001F1F5\U0001F1F1", 1},
+{"\U0001F1F5\U0001F1F2", 1},
+{"\U0001F1F5\U0001F1F3", 1},
+{"\U0001F1F5\U0001F1F7", 1},
+{"\U0001F1F5\U0001F1F8", 1},
+{"\U0001F1F5\U0001F1F9", 1},
+{"\U0001F1F5\U0001F1FC", 1},
+{"\U0001F1F5\U0001F1FE", 1},
+{"\U0001F1F6\U0001F1E6", 1},
+{"\U0001F1F7\U0001F1EA", 1},
+{"\U0001F1F7\U0001F1F4", 1},
+{"\U0001F1F7\U0001F1F8", 1},
+{"\U0001F1F7\U0001F1FA", 1},
+{"\U0001F1F7\U0001F1FC", 1},
+{"\U0001F1F8\U0001F1E6", 1},
+{"\U0001F1F8\U0001F1E7", 1},
+{"\U0001F1F8\U0001F1E8", 1},
+{"\U0001F1F8\U0001F1E9", 1},
+{"\U0001F1F8\U0001F1EA", 1},
+{"\U0001F1F8\U0001F1EC", 1},
+{"\U0001F1F8\U0001F1ED", 1},
+{"\U0001F1F8\U0001F1EE", 1},
+{"\U0001F1F8\U0001F1EF", 1},
+{"\U0001F1F8\U0001F1F0", 1},
+{"\U0001F1F8\U0001F1F1", 1},
+{"\U0001F1F8\U0001F1F2", 1},
+{"\U0001F1F8\U0001F1F3", 1},
+{"\U0001F1F8\U0001F1F4", 1},
+{"\U0001F1F8\U0001F1F7", 1},
+{"\U0001F1F8\U0001F1F8", 1},
+{"\U0001F1F8\U0001F1F9", 1},
+{"\U0001F1F8\U0001F1FB", 1},
+{"\U0001F1F8\U0001F1FD", 1},
+{"\U0001F1F8\U0001F1FE", 1},
+{"\U0001F1F8\U0001F1FF", 1},
+{"\U0001F1F9\U0001F1E6", 1},
+{"\U0001F1F9\U0001F1E8", 1},
+{"\U0001F1F9\U0001F1E9", 1},
+{"\U0001F1F9\U0001F1EB", 1},
+{"\U0001F1F9\U0001F1EC", 1},
+{"\U0001F1F9\U0001F1ED", 1},
+{"\U0001F1F9\U0001F1EF", 1},
+{"\U0001F1F9\U0001F1F0", 1},
+{"\U0001F1F9\U0001F1F1", 1},
+{"\U0001F1F9\U0001F1F2", 1},
+{"\U0001F1F9\U0001F1F3", 1},
+{"\U0001F1F9\U0001F1F4", 1},
+{"\U0001F1F9\U0001F1F7", 1},
+{"\U0001F1F9\U0001F1F9", 1},
+{"\U0001F1F9\U0001F1FB", 1},
+{"\U0001F1F9\U0001F1FC", 1},
+{"\U0001F1F9\U0001F1FF", 1},
+{"\U0001F1FA\U0001F1E6", 1},
+{"\U0001F1FA\U0001F1EC", 1},
+{"\U0001F1FA\U0001F1F2", 1},
+{"\U0001F1FA\U0001F1F3", 1},
+{"\U0001F1FA\U0001F1F8", 1},
+{"\U0001F1FA\U0001F1FE", 1},
+{"\U0001F1FA\U0001F1FF", 1},
+{"\U0001F1FB\U0001F1E6", 1},
+{"\U0001F1FB\U0001F1E8", 1},
+{"\U0001F1FB\U0001F1EA", 1},
+{"\U0001F1FB\U0001F1EC", 1},
+{"\U0001F1FB\U0001F1EE", 1},
+{"\U0001F1FB\U0001F1F3", 1},
+{"\U0001F1FB\U0001F1FA", 1},
+{"\U0001F1FC\U0001F1EB", 1},
+{"\U0001F1FC\U0001F1F8", 1},
+{"\U0001F1FD\U0001F1F0", 1},
+{"\U0001F1FE\U0001F1EA", 1},
+{"\U0001F1FE\U0001F1F9", 1},
+{"\U0001F1FF\U0001F1E6", 1},
+{"\U0001F1FF\U0001F1F2", 1},
+{"\U0001F1FF\U0001F1FC", 1},
+{"\U0001F3F4\U000E0067\U000E0062\U000E0065\U000E006E\U000E0067\U000E007F", 1},
+{"\U0001F3F4\U000E0067\U000E0062\U000E0073\U000E0063\U000E0074\U000E007F", 1},
+{"\U0001F3F4\U000E0067\U000E0062\U000E0077\U000E006C\U000E0073\U000E007F", 1},
+}

+ 41 - 34
tests/internal/test_map.odin

@@ -16,10 +16,10 @@ map_insert_random_key_value :: proc(t: ^testing.T) {
 		defer delete(m)
 
 		unique_keys := 0
-		r := rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
 		for _ in 0..<entries {
-			k := rand.int63(&r)
-			v := rand.int63(&r)
+			k := rand.int63()
+			v := rand.int63()
 
 			if k not_in m {
 				unique_keys += 1
@@ -36,12 +36,12 @@ map_insert_random_key_value :: proc(t: ^testing.T) {
 		testing.expectf(t, len(m)    == unique_keys, "Expected len(map) to equal %v, got %v",  unique_keys, len(m))
 
 		// Reset randomizer and verify
-		r = rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
 
 		num_fails := 0
 		for _ in 0..<entries {
-			k := rand.int63(&r)
-			v := rand.int63(&r)
+			k := rand.int63()
+			v := rand.int63()
 
 			cond := m[k] == v
 			if !cond {
@@ -66,10 +66,11 @@ map_update_random_key_value :: proc(t: ^testing.T) {
 		defer delete(m)
 
 		unique_keys := 0
-		r := rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
+
 		for _ in 0..<entries {
-			k := rand.int63(&r)
-			v := rand.int63(&r)
+			k := rand.int63()
+			v := rand.int63()
 
 			if k not_in m {
 				unique_keys += 1
@@ -88,21 +89,22 @@ map_update_random_key_value :: proc(t: ^testing.T) {
 		half_entries := entries / 2
 
 		// Reset randomizer and update half the entries
-		r = rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
+
 		for _ in 0..<half_entries {
-			k := rand.int63(&r)
-			v := rand.int63(&r)
+			k := rand.int63()
+			v := rand.int63()
 
 			m[k] = v + 42
 		}
 
 		// Reset randomizer and verify
-		r = rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
 
 		num_fails := 0
 		for i in 0..<entries {
-			k := rand.int63(&r)
-			v := rand.int63(&r)
+			k := rand.int63()
+			v := rand.int63()
 
 			diff := i64(42) if i < half_entries else i64(0)
 			cond := m[k] == (v + diff)
@@ -128,10 +130,11 @@ map_delete_random_key_value :: proc(t: ^testing.T) {
 		defer delete(m)
 
 		unique_keys := 0
-		r := rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
+
 		for _ in 0..<entries {
-			k := rand.int63(&r)
-			v := rand.int63(&r)
+			k := rand.int63()
+			v := rand.int63()
 
 			if k not_in m {
 				unique_keys += 1
@@ -150,21 +153,22 @@ map_delete_random_key_value :: proc(t: ^testing.T) {
 		half_entries := entries / 2
 
 		// Reset randomizer and delete half the entries
-		r = rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
+
 		for _ in 0..<half_entries {
-			k := rand.int63(&r)
-			_  = rand.int63(&r)
+			k := rand.int63()
+			_  = rand.int63()
 
 			delete_key(&m, k)
 		}
 
 		// Reset randomizer and verify
-		r = rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
 
 		num_fails := 0
 		for i in 0..<entries {
-			k := rand.int63(&r)
-			v := rand.int63(&r)
+			k := rand.int63()
+			v := rand.int63()
 
 			if i < half_entries {
 				if k in m {
@@ -206,9 +210,10 @@ set_insert_random_key_value :: proc(t: ^testing.T) {
 		defer delete(m)
 
 		unique_keys := 0
-		r := rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
+
 		for _ in 0..<entries {
-			k := rand.int63(&r)
+			k := rand.int63()
 			if k not_in m {
 				unique_keys += 1
 			}
@@ -224,11 +229,11 @@ set_insert_random_key_value :: proc(t: ^testing.T) {
 		testing.expectf(t, len(m)    == unique_keys, "Expected len(map) to equal %v, got %v",  unique_keys, len(m))
 
 		// Reset randomizer and verify
-		r = rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
 
 		num_fails := 0
 		for _ in 0..<entries {
-			k := rand.int63(&r)
+			k := rand.int63()
 
 			cond := k in m
 			if !cond {
@@ -253,9 +258,10 @@ set_delete_random_key_value :: proc(t: ^testing.T) {
 		defer delete(m)
 
 		unique_keys := 0
-		r := rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
+
 		for _ in 0..<entries {
-			k := rand.int63(&r)
+			k := rand.int63()
 
 			if k not_in m {
 				unique_keys += 1
@@ -274,18 +280,19 @@ set_delete_random_key_value :: proc(t: ^testing.T) {
 		half_entries := entries / 2
 
 		// Reset randomizer and delete half the entries
-		r = rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
+
 		for _ in 0..<half_entries {
-			k := rand.int63(&r)
+			k := rand.int63()
 			delete_key(&m, k)
 		}
 
 		// Reset randomizer and verify
-		r = rand.create(t.seed + seed_incr)
+		rand.reset(t.seed + seed_incr)
 
 		num_fails := 0
 		for i in 0..<entries {
-			k := rand.int63(&r)
+			k := rand.int63()
 
 			if i < half_entries {
 				if k in m {