From edb685f04be9aef407dc8abac7bb76fb51184f9f Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 6 Jun 2024 18:35:43 -0400 Subject: [PATCH 01/60] Add package `core:flags` --- core/flags/LICENSE | 28 + core/flags/constants.odin | 38 + core/flags/doc.odin | 181 ++++ core/flags/errors.odin | 58 ++ core/flags/example/example.odin | 132 +++ core/flags/internal_assignment.odin | 262 +++++ core/flags/internal_parsing.odin | 162 +++ core/flags/internal_rtti.odin | 548 ++++++++++ core/flags/internal_validation.odin | 243 +++++ core/flags/parsing.odin | 94 ++ core/flags/rtti.odin | 43 + core/flags/usage.odin | 293 ++++++ core/flags/util.odin | 130 +++ core/flags/validation.odin | 37 + tests/core/flags/test_core_flags.odin | 1350 +++++++++++++++++++++++++ 15 files changed, 3599 insertions(+) create mode 100644 core/flags/LICENSE create mode 100644 core/flags/constants.odin create mode 100644 core/flags/doc.odin create mode 100644 core/flags/errors.odin create mode 100644 core/flags/example/example.odin create mode 100644 core/flags/internal_assignment.odin create mode 100644 core/flags/internal_parsing.odin create mode 100644 core/flags/internal_rtti.odin create mode 100644 core/flags/internal_validation.odin create mode 100644 core/flags/parsing.odin create mode 100644 core/flags/rtti.odin create mode 100644 core/flags/usage.odin create mode 100644 core/flags/util.odin create mode 100644 core/flags/validation.odin create mode 100644 tests/core/flags/test_core_flags.odin diff --git a/core/flags/LICENSE b/core/flags/LICENSE new file mode 100644 index 000000000..e4e21e62d --- /dev/null +++ b/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. diff --git a/core/flags/constants.odin b/core/flags/constants.odin new file mode 100644 index 000000000..ab3dc9a0a --- /dev/null +++ b/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 :: "" + +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 diff --git a/core/flags/doc.odin b/core/flags/doc.odin new file mode 100644 index 000000000..c3663c419 --- /dev/null +++ b/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 +------------ ------------------------ + depends on struct layout +- set a bool true +- set flag to option +- set flag to option, alternative syntax +-:= 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 + ( + 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") + } +} diff --git a/core/flags/internal_assignment.odin b/core/flags/internal_assignment.odin new file mode 100644 index 000000000..1e715998d --- /dev/null +++ b/core/flags/internal_assignment.odin @@ -0,0 +1,262 @@ +//+private +package flags + +import "base:intrinsics" +import "base:runtime" +import "core:container/bit_array" +import "core:fmt" +import "core:mem" +import "core:reflect" +import "core:strconv" +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), + } +} diff --git a/core/flags/internal_parsing.odin b/core/flags/internal_parsing.odin new file mode 100644 index 000000000..349afdd29 --- /dev/null +++ b/core/flags/internal_parsing.odin @@ -0,0 +1,162 @@ +//+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 -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:] + } + + 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` +// ` (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 +} diff --git a/core/flags/internal_rtti.odin b/core/flags/internal_rtti.odin new file mode 100644 index 000000000..8a11ebbc1 --- /dev/null +++ b/core/flags/internal_rtti.odin @@ -0,0 +1,548 @@ +//+private +package flags + +import "base:intrinsics" +import "base:runtime" +import "core:fmt" +import "core:mem" +@require import "core:net" +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 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 + } + + (cast(^net.Host_Or_Endpoint)ptr)^ = addr + 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 +} diff --git a/core/flags/internal_validation.odin b/core/flags/internal_validation.odin new file mode 100644 index 000000000..cfa1794cd --- /dev/null +++ b/core/flags/internal_validation.odin @@ -0,0 +1,243 @@ +//+private +package flags + +import "base:runtime" +import "core:container/bit_array" +import "core:fmt" +import "core:mem" +import "core:os" +import "core:reflect" +import "core:strconv" +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 + + 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 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 +} diff --git a/core/flags/parsing.odin b/core/flags/parsing.odin new file mode 100644 index 000000000..d8aea513f --- /dev/null +++ b/core/flags/parsing.odin @@ -0,0 +1,94 @@ +package flags + +import "core:container/bit_array" + +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 /**/; future_args > 0; future_args -= 1 { + i += 1 + if i == len(args) { + 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 +} diff --git a/core/flags/rtti.odin b/core/flags/rtti.odin new file mode 100644 index 000000000..ce7a23773 --- /dev/null +++ b/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 +} diff --git a/core/flags/usage.odin b/core/flags/usage.odin new file mode 100644 index 000000000..48137b6cd --- /dev/null +++ b/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)) +} diff --git a/core/flags/util.odin b/core/flags/util.odin new file mode 100644 index 000000000..e4f32eea1 --- /dev/null +++ b/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 +} diff --git a/core/flags/validation.odin b/core/flags/validation.odin new file mode 100644 index 000000000..e370cff48 --- /dev/null +++ b/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 +} diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin new file mode 100644 index 000000000..a9efa5e14 --- /dev/null +++ b/tests/core/flags/test_core_flags.odin @@ -0,0 +1,1350 @@ +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) +} + +// 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) { + 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) + + if file_contents_equal { + // Delete the file now that we're done. + // + // This is not done as a defer or all the time, just in case the file + // is useful to debugging. + testing.expect_value(t, os.remove(TEMPORARY_FILENAME), os.ERROR_NONE) + } +} + +@(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:, required | some number + -number: | some other number + -name: + Multi-line documentation + gets formatted + very nicely. + -bars:, exactly 3 | + -bots:, at least 1 | + -foos:, between 2 and 3 | + -gadgets:, at least 1 | + -widgets:, at most 2 | + | + -array:, multiple | + -count: | + -greek: | + -map-type:= | + -verbose | + | +` + + 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 , required | some number + --number | some other number + --name + Multi-line documentation + gets formatted + very nicely. + --bars , exactly 3 | + --bots , at least 1 | + --foos , between 2 and 3 | + --gadgets , at least 1 | + --variadic-flag , at least 2 | + --widgets , at most 2 | + | + --array , multiple | + --count | + --greek | + --verbose | + | +` + + 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) +} From 6454c6f0879add3ed018065eff073a75d45229b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20H=C3=B6ltermann?= Date: Tue, 11 Jun 2024 09:35:59 +0200 Subject: [PATCH 02/60] Added some missing functions to core/sys/windows --- core/sys/windows/kernel32.odin | 14 ++++++++++++++ core/sys/windows/types.odin | 9 +++++++++ core/sys/windows/user32.odin | 11 +++++++++++ core/sys/windows/winerror.odin | 2 +- 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index eba275522..d3b5a148b 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -64,6 +64,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, @@ -464,6 +465,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 @@ -1210,6 +1213,15 @@ SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct { DummyUnion: DUMMYUNIONNAME_u, } +SYSTEM_POWER_STATUS :: struct { + ACLineStatus: BYTE, + BatteryFlag: BYTE, + BatteryLifePercent: BYTE, + SystemStatusFlag: BYTE, + BatteryLifeTime: DWORD, + BatteryFullLifeTime: DWORD, +} + /* Global Memory Flags */ GMEM_FIXED :: 0x0000 GMEM_MOVEABLE :: 0x0002 @@ -1228,3 +1240,5 @@ GMEM_INVALID_HANDLE :: 0x8000 GHND :: (GMEM_MOVEABLE | GMEM_ZEROINIT) GPTR :: (GMEM_FIXED | GMEM_ZEROINIT) + +LPTOP_LEVEL_EXCEPTION_FILTER :: PVECTORED_EXCEPTION_HANDLER diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 11d2774d6..7ed322169 100644 --- a/core/sys/windows/types.odin +++ b/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, diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index 1fc0116f5..6c053fed0 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -17,6 +17,17 @@ 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, dwmFlags: DWORD, + ) -> HWINEVENTHOOK --- + + IsChild :: proc(hWndParent, hWnd: HWND) -> BOOL --- + RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM --- RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM --- UnregisterClassW :: proc(lpClassName: LPCWSTR, hInstance: HINSTANCE) -> BOOL --- diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin index 118327ffa..c66a22322 100644 --- a/core/sys/windows/winerror.odin +++ b/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 { From 50564a301e0d70476db54c84d9835bd4a6c27bfd Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 13 Jun 2024 10:20:58 +0200 Subject: [PATCH 03/60] Update kernel32 struct DCB. --- core/sys/windows/kernel32.odin | 139 +++++++-------------------------- 1 file changed, 30 insertions(+), 109 deletions(-) diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 3c60cfc43..28d63db8e 100755 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -1009,31 +1009,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, @@ -1058,92 +1036,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, } From 657c51636080e9b3c9c2215d05b08f3cccf68e70 Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Wed, 12 Jun 2024 20:58:28 +0200 Subject: [PATCH 04/60] =?UTF-8?q?Pad=20=E2=80=98^~~~^=E2=80=99-style=20dia?= =?UTF-8?q?gnostic=20ranges=20properly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/error.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index 03d96219b..5a4cb6a0c 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -323,8 +323,13 @@ gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end) { } error_out("\n\t"); - for (i32 i = 0; i < offset; i++) { - error_out(" "); + for (i32 rune_width, off = 0; off < offset; off += rune_width) { + i32 rune; + rune_width = cast(i32)utf8proc_iterate((u8 const *)line_text + off, line_len - off, &rune); + int w = utf8proc_charwidth(rune); + if (w > 0) { + error_out("%.*s", w, " "); + } } terminal_set_colours(TerminalStyle_Bold, TerminalColour_Green); From 9f190f3937628595ecd8f6c784b85d7be6e0254a Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Thu, 13 Jun 2024 16:42:04 +0200 Subject: [PATCH 05/60] Generate ranges of the correct length --- src/error.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index 5a4cb6a0c..cdf3b806a 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -336,15 +336,27 @@ gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end) { error_out("^"); if (end.file_id == pos.file_id) { + i32 rune; + if (end.line > pos.line) { - for (i32 i = offset; i < line_len; i++) { - error_out("~"); + for (i32 rune, rune_width, off = offset; off < line_len; off += rune_width) { + rune_width = cast(i32)utf8proc_iterate((u8 const *)line_text + off, line_len - off, &rune); + int w = utf8proc_charwidth(rune); + if (w > 0) { + error_out("%.*s", w, "~~~~"); + } } } else if (end.line == pos.line && end.column > pos.column) { - for (i32 i = 1; i < error_length-1+squiggle_extra; i++) { + i32 columns = squiggle_extra; + for (i32 rune, rune_width, off = offset; off < offset + error_length - 1; off += rune_width) { + rune_width = cast(i32)utf8proc_iterate((u8 const *)line_text + off, line_len - off, &rune); + columns += utf8proc_charwidth(rune); + } + + for (i32 i = 0; i < columns - 1; i++) { error_out("~"); } - if (error_length > 1 && squiggle_extra == 0) { + if (columns > 0 && squiggle_extra == 0) { error_out("^"); } } From ca9d1f940d4118f0652d11cbcd50dc4209d54720 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 13 Jun 2024 17:23:30 +0200 Subject: [PATCH 06/60] Just change squiggle_extra type to i32. --- src/error.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.cpp b/src/error.cpp index cdf3b806a..a63dec43a 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -296,7 +296,7 @@ gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end) { terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); - isize squiggle_extra = 0; + i32 squiggle_extra = 0; if (line_len > MAX_LINE_LENGTH_PADDED) { i32 left = MAX_TAB_WIDTH; From 9f7ac1469fc364330ada811032d295ba36f6e078 Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Thu, 13 Jun 2024 16:42:04 +0200 Subject: [PATCH 07/60] Generate ranges of the correct length --- src/error.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index 5a4cb6a0c..1e24325c5 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -336,15 +336,27 @@ gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end) { error_out("^"); if (end.file_id == pos.file_id) { + i32 rune; + if (end.line > pos.line) { - for (i32 i = offset; i < line_len; i++) { - error_out("~"); + for (i32 rune, rune_width, off = offset; off < line_len; off += rune_width) { + rune_width = cast(i32)utf8proc_iterate((u8 const *)line_text + off, line_len - off, &rune); + int w = utf8proc_charwidth(rune); + if (w > 0) { + error_out("%.*s", w, "~~~~"); + } } } else if (end.line == pos.line && end.column > pos.column) { - for (i32 i = 1; i < error_length-1+squiggle_extra; i++) { + isize columns = squiggle_extra; + for (i32 rune, rune_width, off = offset; off < offset + error_length - 1; off += rune_width) { + rune_width = cast(i32)utf8proc_iterate((u8 const *)line_text + off, line_len - off, &rune); + columns += utf8proc_charwidth(rune); + } + + for (isize i = 1; i < columns; i++) { error_out("~"); } - if (error_length > 1 && squiggle_extra == 0) { + if (columns > 0 && squiggle_extra == 0) { error_out("^"); } } From c560553c21b970d38a302a11e94181de5415d043 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 13 Jun 2024 13:01:51 -0400 Subject: [PATCH 08/60] Fix compiler silently returning if `parse_packages` fails --- src/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 70def5802..f4cd40fe9 100644 --- a/src/main.cpp +++ b/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()) { From ff4787070d9673a417f549f1b9452e675c96f992 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 14 Jun 2024 00:06:55 +0200 Subject: [PATCH 09/60] Revert "Merge pull request #3744 from Mango0x45/master" This reverts commit 45044de0b756f9ab018979abd5350533334a54ac, reversing changes made to 20c17ba6f971cf572da4fa5e9601e0df8d517112. --- src/error.cpp | 34 +++++++++------------------------- 1 file changed, 9 insertions(+), 25 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index d0089c952..03d96219b 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -296,7 +296,7 @@ gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end) { terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); - i32 squiggle_extra = 0; + isize squiggle_extra = 0; if (line_len > MAX_LINE_LENGTH_PADDED) { i32 left = MAX_TAB_WIDTH; @@ -323,39 +323,23 @@ gb_internal isize show_error_on_line(TokenPos const &pos, TokenPos end) { } error_out("\n\t"); - for (i32 rune_width, off = 0; off < offset; off += rune_width) { - i32 rune; - rune_width = cast(i32)utf8proc_iterate((u8 const *)line_text + off, line_len - off, &rune); - int w = utf8proc_charwidth(rune); - if (w > 0) { - error_out("%.*s", w, " "); - } + for (i32 i = 0; i < offset; i++) { + error_out(" "); } terminal_set_colours(TerminalStyle_Bold, TerminalColour_Green); error_out("^"); if (end.file_id == pos.file_id) { - i32 rune; - if (end.line > pos.line) { - for (i32 rune, rune_width, off = offset; off < line_len; off += rune_width) { - rune_width = cast(i32)utf8proc_iterate((u8 const *)line_text + off, line_len - off, &rune); - int w = utf8proc_charwidth(rune); - if (w > 0) { - error_out("%.*s", w, "~~~~"); - } - } - } else if (end.line == pos.line && end.column > pos.column) { - i32 columns = squiggle_extra; - for (i32 rune, rune_width, off = offset; off < offset + error_length - 1; off += rune_width) { - rune_width = cast(i32)utf8proc_iterate((u8 const *)line_text + off, line_len - off, &rune); - columns += utf8proc_charwidth(rune); - } - for (i32 i = 1; i < columns; i++) { + for (i32 i = offset; i < line_len; i++) { error_out("~"); } - if (columns > 0 && squiggle_extra == 0) { + } else if (end.line == pos.line && end.column > pos.column) { + for (i32 i = 1; i < error_length-1+squiggle_extra; i++) { + error_out("~"); + } + if (error_length > 1 && squiggle_extra == 0) { error_out("^"); } } From 4baf101f1524dd1ed6812b0c231907d291e6fe5f Mon Sep 17 00:00:00 2001 From: Matias Fernandez Date: Fri, 14 Jun 2024 05:36:23 -0400 Subject: [PATCH 10/60] Handle empty structs in fmt with hash --- core/fmt/fmt.odin | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 4c65dd01f..b1c95866f 100644 --- a/core/fmt/fmt.odin +++ b/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.. 0 { + for _ in 0.. Date: Fri, 14 Jun 2024 16:32:41 +0200 Subject: [PATCH 11/60] Improved win32 bindings according to Kelimions suggestions with enums and bitsets --- core/sys/windows/kernel32.odin | 21 ++++++++++++++++++--- core/sys/windows/user32.odin | 12 +++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index e50f18984..86f6b86f0 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -1214,13 +1214,28 @@ SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct { } SYSTEM_POWER_STATUS :: struct { - ACLineStatus: BYTE, - BatteryFlag: BYTE, + 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 diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index 6c053fed0..380c0ac56 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -23,7 +23,8 @@ foreign user32 { eventMin, eventMax: DWORD, hmodWinEventProc: HMODULE, pfnWinEvenProc: WINEVENTPROC, - idProcess, idThread, dwmFlags: DWORD, + idProcess, idThread: DWORD, + dwFlags: WinEventFlags, ) -> HWINEVENTHOOK --- IsChild :: proc(hWndParent, hWnd: HWND) -> BOOL --- @@ -579,3 +580,12 @@ RedrawWindowFlags :: enum UINT { RDW_FRAME = 0x0400, RDW_NOFRAME = 0x0800, } + +WinEventFlags :: bit_set[WinEventFlag; DWORD] + +WinEventFlag :: enum DWORD { + WINEVENT_OUTOFCONTEXT = 0, + WINEVENT_SKIPOWNTHREAD = 1, + WINEVENT_SKIPOWNPROCESS = 2, + WINEVENT_INCONTEXT = 4, +} From 3c3f0f90c2c8d063845cf0ecb61a23705749e445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Franz=20H=C3=B6ltermann?= Date: Fri, 14 Jun 2024 17:41:21 +0200 Subject: [PATCH 12/60] Fixed WinEventFlags --- core/sys/windows/user32.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index 380c0ac56..3bc083acb 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -581,11 +581,11 @@ RedrawWindowFlags :: enum UINT { RDW_NOFRAME = 0x0800, } +// OUTOFCONTEXT is the zero value, use {} WinEventFlags :: bit_set[WinEventFlag; DWORD] WinEventFlag :: enum DWORD { - WINEVENT_OUTOFCONTEXT = 0, - WINEVENT_SKIPOWNTHREAD = 1, - WINEVENT_SKIPOWNPROCESS = 2, - WINEVENT_INCONTEXT = 4, + SKIPOWNTHREAD = 0, + SKIPOWNPROCESS = 1, + INCONTEXT = 2, } From 8ba644dd79ef878d619abbca9de8b52856ae96c1 Mon Sep 17 00:00:00 2001 From: Damian Tarnawski Date: Fri, 14 Jun 2024 19:45:54 +0200 Subject: [PATCH 13/60] Add `#no_bounds_check` to `slice.equal` --- core/slice/slice.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/slice/slice.odin b/core/slice/slice.odin index 3cab9189d..d8f4df88e 100644 --- a/core/slice/slice.odin +++ b/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 } @@ -736,4 +736,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} \ No newline at end of file +bitset_to_enum_slice :: proc{bitset_to_enum_slice_with_make, bitset_to_enum_slice_with_buffer} From 759139089fc38c59882a29bce7dee031fa6385b3 Mon Sep 17 00:00:00 2001 From: Josh Jones Date: Fri, 14 Jun 2024 19:58:13 +0200 Subject: [PATCH 14/60] Fix big.shrink not actually shrinking --- core/math/big/internal.odin | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index fa747e46a..360d063e9 100644 --- a/core/math/big/internal.odin +++ b/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 && allow_shrink { + /* + Same applies to builtin.shrink here as resize above + */ + builtin.shrink(&a.digit, needed) } /* Let's see if the allocation/resize worked as expected. From 908a6ff2d487401b7e72cd1b7a3c1bd32c86c99a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 14 Jun 2024 21:34:05 +0200 Subject: [PATCH 15/60] Elide unnecessary condition --- core/math/big/internal.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 360d063e9..29bdf555c 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -2187,7 +2187,7 @@ internal_int_grow :: proc(a: ^Int, digits: int, allow_shrink := false, allocator `[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 && allow_shrink { + } else if cap > needed { /* Same applies to builtin.shrink here as resize above */ From c3acdeb310c1426d009004956c1b3e59e4920808 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 14 Jun 2024 23:59:01 -0400 Subject: [PATCH 16/60] Add `--` parsing to UNIX-style `core:flags` This will allow a user to indicate all arguments after `--` are parsed into the variadic array. --- core/flags/internal_parsing.odin | 8 ++++++++ tests/core/flags/test_core_flags.odin | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/core/flags/internal_parsing.odin b/core/flags/internal_parsing.odin index 349afdd29..7a769b17c 100644 --- a/core/flags/internal_parsing.odin +++ b/core/flags/internal_parsing.odin @@ -90,6 +90,14 @@ parse_one_unix_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> ( 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 diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin index a9efa5e14..cbe5e93a5 100644 --- a/tests/core/flags/test_core_flags.odin +++ b/tests/core/flags/test_core_flags.odin @@ -1028,6 +1028,33 @@ test_unix_positional_with_variadic :: proc(t: ^testing.T) { 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") +} + // This test ensures there are no bad frees with cstrings. @(test) test_if_dynamic_cstrings_get_freed :: proc(t: ^testing.T) { From 42a5a2cf17e4547ff6bce445ef82404486e3b714 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 00:01:33 -0400 Subject: [PATCH 17/60] Fix memory leak in `core:flags` validation --- core/flags/internal_validation.odin | 1 + 1 file changed, 1 insertion(+) diff --git a/core/flags/internal_validation.odin b/core/flags/internal_validation.odin index cfa1794cd..9f94621d2 100644 --- a/core/flags/internal_validation.odin +++ b/core/flags/internal_validation.odin @@ -14,6 +14,7 @@ import "core:strings" @(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 { From 76575e834b9fd523179071340f794a8a1dcb3638 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 00:18:49 -0400 Subject: [PATCH 18/60] Raise error on spaced UNIX-style flag with no value --- core/flags/parsing.odin | 9 ++++++++- tests/core/flags/test_core_flags.odin | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/core/flags/parsing.odin b/core/flags/parsing.odin index d8aea513f..f09c4c690 100644 --- a/core/flags/parsing.odin +++ b/core/flags/parsing.odin @@ -1,6 +1,7 @@ package flags import "core:container/bit_array" +import "core:fmt" Parsing_Style :: enum { // Odin-style: `-flag`, `-flag:option`, `-map:key=value` @@ -71,9 +72,15 @@ parse :: proc( return } - for /**/; future_args > 0; future_args -= 1 { + 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] diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin index cbe5e93a5..71fc34701 100644 --- a/tests/core/flags/test_core_flags.odin +++ b/tests/core/flags/test_core_flags.odin @@ -1055,6 +1055,23 @@ test_unix_double_dash_variadic :: proc(t: ^testing.T) { 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) { From 53e2cdf7afad6a75f9620d489003933949e753bc Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 00:28:47 -0400 Subject: [PATCH 19/60] Add `core:flags` to `examples/all` --- examples/all/all_main.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 6c3972987..5202d72be 100644 --- a/examples/all/all_main.odin +++ b/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 From f28c6c3bbaf3e55c75340125a1244e42c3462385 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 00:50:12 -0400 Subject: [PATCH 20/60] Keep `-vet` happy --- core/flags/internal_assignment.odin | 10 +++++----- core/flags/internal_validation.odin | 16 ++++++++-------- core/flags/parsing.odin | 4 ++-- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/core/flags/internal_assignment.odin b/core/flags/internal_assignment.odin index 1e715998d..4c4ee58fe 100644 --- a/core/flags/internal_assignment.odin +++ b/core/flags/internal_assignment.odin @@ -2,13 +2,13 @@ package flags import "base:intrinsics" -import "base:runtime" +@require import "base:runtime" import "core:container/bit_array" -import "core:fmt" -import "core:mem" +@require import "core:fmt" +@require import "core:mem" import "core:reflect" -import "core:strconv" -import "core:strings" +@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. diff --git a/core/flags/internal_validation.odin b/core/flags/internal_validation.odin index 9f94621d2..fd4bafeef 100644 --- a/core/flags/internal_validation.odin +++ b/core/flags/internal_validation.odin @@ -1,14 +1,14 @@ //+private package flags -import "base:runtime" -import "core:container/bit_array" -import "core:fmt" -import "core:mem" -import "core:os" -import "core:reflect" -import "core:strconv" -import "core:strings" +@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) diff --git a/core/flags/parsing.odin b/core/flags/parsing.odin index f09c4c690..b6b63fdb6 100644 --- a/core/flags/parsing.odin +++ b/core/flags/parsing.odin @@ -1,7 +1,7 @@ package flags -import "core:container/bit_array" -import "core:fmt" +@require import "core:container/bit_array" +@require import "core:fmt" Parsing_Style :: enum { // Odin-style: `-flag`, `-flag:option`, `-map:key=value` From 51a013fcf142fd7fcfed370bdbaf9de6db19020e Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 01:32:28 -0400 Subject: [PATCH 21/60] Work around BSD lack of `core:net` support --- core/flags/errors.odin | 8 ------- core/flags/errors_bsd.odin | 9 ++++++++ core/flags/errors_nonbsd.odin | 11 ++++++++++ core/flags/internal_rtti.odin | 14 +------------ core/flags/internal_rtti_nonbsd.odin | 31 ++++++++++++++++++++++++++++ 5 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 core/flags/errors_bsd.odin create mode 100644 core/flags/errors_nonbsd.odin create mode 100644 core/flags/internal_rtti_nonbsd.odin diff --git a/core/flags/errors.odin b/core/flags/errors.odin index 1862a7a00..21ea05477 100644 --- a/core/flags/errors.odin +++ b/core/flags/errors.odin @@ -1,7 +1,5 @@ package flags -import "base:runtime" -import "core:net" import "core:os" Parse_Error_Reason :: enum { @@ -20,12 +18,6 @@ Parse_Error_Reason :: enum { Unsupported_Type, } -Unified_Parse_Error_Reason :: union #shared_nil { - Parse_Error_Reason, - runtime.Allocator_Error, - net.Parse_Endpoint_Error, -} - // Raised during parsing, naturally. Parse_Error :: struct { reason: Unified_Parse_Error_Reason, diff --git a/core/flags/errors_bsd.odin b/core/flags/errors_bsd.odin new file mode 100644 index 000000000..10c04b506 --- /dev/null +++ b/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, +} diff --git a/core/flags/errors_nonbsd.odin b/core/flags/errors_nonbsd.odin new file mode 100644 index 000000000..ff46f38ed --- /dev/null +++ b/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, +} diff --git a/core/flags/internal_rtti.odin b/core/flags/internal_rtti.odin index 8a11ebbc1..c9b1f59fd 100644 --- a/core/flags/internal_rtti.odin +++ b/core/flags/internal_rtti.odin @@ -5,7 +5,6 @@ import "base:intrinsics" import "base:runtime" import "core:fmt" import "core:mem" -@require import "core:net" import "core:os" import "core:reflect" import "core:strconv" @@ -309,18 +308,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type: } when IMPORTING_NET { - 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 - } - - (cast(^net.Host_Or_Endpoint)ptr)^ = addr + if try_net_parse_workaround(data_type, str, ptr, out_error) { return } } diff --git a/core/flags/internal_rtti_nonbsd.odin b/core/flags/internal_rtti_nonbsd.odin new file mode 100644 index 000000000..196c27ab8 --- /dev/null +++ b/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 +} From dc4ec8638c1b1dcf493c296b56b39afabe90098b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 14:45:57 +0100 Subject: [PATCH 22/60] Add `runtime.Random_Generator` interface --- base/runtime/core.odin | 19 +++++++++++++++++++ core/crypto/crypto.odin | 20 ++++++++++++++++++++ core/math/rand/rand.odin | 27 +++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 8671920f5..9669e86aa 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -397,11 +397,30 @@ Logger :: struct { options: Logger_Options, } + +Random_Generator_Mode :: enum { + Read, + Query_Info, +} + +Random_Generator_Query_Info_Flag :: enum u32 { + Cryptographic, + Uniform, + External_Entropy, +} +Random_Generator_Query_Info :: distinct bit_set[Random_Generator_Query_Info_Flag; u32] + +Random_Generator :: struct { + procedure: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte), + 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, diff --git a/core/crypto/crypto.odin b/core/crypto/crypto.odin index f0874cc6d..99835e66f 100644 --- a/core/crypto/crypto.odin +++ b/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,22 @@ rand_bytes :: proc (dst: []byte) { _rand_bytes(dst) } + + +to_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 .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, + } +} \ No newline at end of file diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 664d6abc9..552af6b8d 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -5,6 +5,7 @@ Package core:math/rand implements various random number generators package rand import "base:intrinsics" +import "base:runtime" import "core:crypto" import "core:math" import "core:mem" @@ -15,6 +16,28 @@ Rand :: struct { is_system: bool, } +to_random_generator :: proc(r: ^Rand) -> runtime.Random_Generator { + return { + procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) { + r := (^Rand)(data) + switch mode { + case .Read: + _ = read(p, r) + 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} + if r.is_system { + info^ += {.External_Entropy} + } + } + }, + data = r, + } +} + @(private) global_rand := create(u64(intrinsics.read_cycle_counter())) @@ -150,6 +173,10 @@ _random_u64 :: proc(r: ^Rand) -> u64 { r := r switch { case r == nil: + if res: u64; runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res)) { + return res + } + r = &global_rand case r.is_system: value: u64 From 827cb24023d38c5e14397bd50e363962528b4329 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 14:47:37 +0100 Subject: [PATCH 23/60] Add random_generator.odin --- base/runtime/random_generator.odin | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 base/runtime/random_generator.odin diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin new file mode 100644 index 000000000..2b66b3e0b --- /dev/null +++ b/base/runtime/random_generator.odin @@ -0,0 +1,27 @@ +package runtime + +@(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 +} \ No newline at end of file From c2a01096c4c83516c1bf966aa52f06f8d53a9d47 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 14:49:09 +0100 Subject: [PATCH 24/60] Remove unneeded reassignment of `r` --- core/math/rand/normal.odin | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin index c8681db80..31b9a2387 100644 --- a/core/math/rand/normal.odin +++ b/core/math/rand/normal.odin @@ -115,13 +115,6 @@ 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)) i := j & 0x7f From eaec8a2bbf26c80f20aee10798236a4eb21dcf24 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 15:08:49 +0100 Subject: [PATCH 25/60] Add `runtime.default_random_generator` --- base/runtime/core.odin | 7 +++- base/runtime/random_generator.odin | 64 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 9669e86aa..a4a433d94 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -410,8 +410,10 @@ Random_Generator_Query_Info_Flag :: enum u32 { } 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: proc(data: rawptr, mode: Random_Generator_Mode, p: []byte), + procedure: Random_Generator_Proc, data: rawptr, } @@ -727,6 +729,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) -> ! { diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin index 2b66b3e0b..7746f1429 100644 --- a/base/runtime/random_generator.odin +++ b/base/runtime/random_generator.odin @@ -1,5 +1,7 @@ package runtime +import "base:intrinsics" + @(require_results) random_generator_read_bytes :: proc(rg: Random_Generator, p: []byte) -> bool { if rg.procedure != nil { @@ -24,4 +26,66 @@ random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Gener rg.procedure(rg.data, .Query_Info, ([^]byte)(&info)[:size_of(info)]) } return +} + + +@(private="file") +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 + + switch mode { + case .Read: + r := &global_rand_seed + + if r.state == 0 && + r.inc == 0 { + seed := u64(intrinsics.read_cycle_counter()) + r.state = 0 + r.inc = (seed << 1) | 1 + _ = read_u64(r) + r.state += seed + _ = read_u64(r) + } + + 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 + } + return + case .Query_Info: + if len(p) != size_of(Random_Generator_Query_Info) { + return + } + info := (^Random_Generator_Query_Info)(raw_data(p)) + info^ += {.Uniform} + } +} + +default_random_generator :: proc "contextless" () -> Random_Generator { + return { + procedure = default_random_generator_proc, + data = nil, + } } \ No newline at end of file From 318d5e4a7eb25e2204ec04a1b0c4a82e75726d19 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 15:17:23 +0100 Subject: [PATCH 26/60] Add `Reset` mode --- base/runtime/core.odin | 2 ++ base/runtime/random_generator.odin | 36 +++++++++++++++++++----------- core/crypto/crypto.odin | 2 ++ core/math/rand/rand.odin | 12 ++++++++++ 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/base/runtime/core.odin b/base/runtime/core.odin index a4a433d94..550daed3b 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -400,6 +400,7 @@ Logger :: struct { Random_Generator_Mode :: enum { Read, + Reset, Query_Info, } @@ -407,6 +408,7 @@ 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] diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin index 7746f1429..43d7ffd0c 100644 --- a/base/runtime/random_generator.odin +++ b/base/runtime/random_generator.odin @@ -29,7 +29,6 @@ random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Gener } -@(private="file") Default_Random_State :: struct { state: u64, inc: u64, @@ -48,18 +47,24 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, @(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 := &global_rand_seed + switch mode { case .Read: - r := &global_rand_seed - - if r.state == 0 && - r.inc == 0 { - seed := u64(intrinsics.read_cycle_counter()) - r.state = 0 - r.inc = (seed << 1) | 1 - _ = read_u64(r) - r.state += seed - _ = read_u64(r) + if r.state == 0 && r.inc == 0 { + init(r, 0) } pos := i8(0) @@ -73,13 +78,18 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, val >>= 8 pos -= 1 } - return + + 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} + info^ += {.Uniform, .Resettable} } } diff --git a/core/crypto/crypto.odin b/core/crypto/crypto.odin index 99835e66f..d995ecf7d 100644 --- a/core/crypto/crypto.odin +++ b/core/crypto/crypto.odin @@ -67,6 +67,8 @@ to_random_generator :: proc() -> runtime.Random_Generator { 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 diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 552af6b8d..3f85277e8 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -23,6 +23,16 @@ to_random_generator :: proc(r: ^Rand) -> runtime.Random_Generator { switch mode { case .Read: _ = read(p, r) + + case .Reset: + if r.is_system { + return + } + seed: u64 + runtime.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(runtime.Random_Generator_Query_Info) { return @@ -31,6 +41,8 @@ to_random_generator :: proc(r: ^Rand) -> runtime.Random_Generator { info^ += {.Uniform} if r.is_system { info^ += {.External_Entropy} + } else { + info^ += {.Resettable} } } }, From 7ec17ecf98c5151b31f7b0a3e090d6b5a4d12c54 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 15:33:24 +0100 Subject: [PATCH 27/60] Update `core:math/rand` to use `context.random_generator` and remove `rand.Rand` --- base/runtime/random_generator.odin | 18 +- core/math/rand/distributions.odin | 120 ++++----- core/math/rand/exp.odin | 8 +- core/math/rand/normal.odin | 10 +- core/math/rand/rand.odin | 351 +++++--------------------- tests/core/hash/test_core_hash.odin | 3 +- tests/core/slice/test_core_slice.odin | 12 +- tests/internal/test_map.odin | 62 +++-- 8 files changed, 197 insertions(+), 387 deletions(-) diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin index 43d7ffd0c..205d7eb7e 100644 --- a/base/runtime/random_generator.odin +++ b/base/runtime/random_generator.odin @@ -29,6 +29,20 @@ random_generator_query_info :: proc(rg: Random_Generator) -> (info: Random_Gener } +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, @@ -93,9 +107,9 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, } } -default_random_generator :: proc "contextless" () -> Random_Generator { +default_random_generator :: proc "contextless" (state: ^Default_Random_State = nil) -> Random_Generator { return { procedure = default_random_generator_proc, - data = nil, + data = state, } } \ No newline at end of file diff --git a/core/math/rand/distributions.odin b/core/math/rand/distributions.odin index 9365e8b76..a10ea3238 100644 --- a/core/math/rand/distributions.odin +++ b/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))) } diff --git a/core/math/rand/exp.odin b/core/math/rand/exp.odin index ebc849b2f..f30f11f50 100644 --- a/core/math/rand/exp.odin +++ b/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 } } diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin index 31b9a2387..eefa013df 100644 --- a/core/math/rand/normal.odin +++ b/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) @@ -116,7 +116,7 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 { } for { - j := i32(uint32(r)) + j := i32(uint32()) i := j & 0x7f x := f64(j) * f64(wn[i]) if u32(abs(j)) < kn[i] { @@ -126,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 } } diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 3f85277e8..7bb78cc1d 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -10,52 +10,18 @@ import "core:crypto" 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 + +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 } -to_random_generator :: proc(r: ^Rand) -> runtime.Random_Generator { - return { - procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) { - r := (^Rand)(data) - switch mode { - case .Read: - _ = read(p, r) - - case .Reset: - if r.is_system { - return - } - seed: u64 - runtime.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(runtime.Random_Generator_Query_Info) { - return - } - info := (^runtime.Random_Generator_Query_Info)(raw_data(p)) - info^ += {.Uniform} - if r.is_system { - info^ += {.External_Entropy} - } else { - info^ += {.Resettable} - } - } - }, - data = r, - } -} - - -@(private) -global_rand := create(u64(intrinsics.read_cycle_counter())) - /* -Sets the seed used by the global random number generator. +Reset the seed used by the context.random_generator. Inputs: - seed: The seed value @@ -72,143 +38,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 +- seed: The seed value Example: import "core:math/rand" import "core:fmt" - create_example :: proc() { - my_rand := rand.create(1) - fmt.println(rand.uint64(&my_rand)) + set_global_seed_example :: proc() { + rand.set_global_seed(1) + fmt.println(rand.uint64()) } Possible Output: 10 - */ -@(require_results) -create :: proc(seed: u64) -> (res: Rand) { - r: Rand - init(&r, seed) - return r +reset :: proc(seed: u64) { + runtime.random_generator_reset_u64(context.random_generator, seed) } -/* -Initialises a random number generator. - -Inputs: -- r: The random number generator to initialise -- seed: The seed value to initialise this random number generator - -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)) - } - -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) -} - -/* -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: - if res: u64; runtime.random_generator_read_ptr(context.random_generator, &res, size_of(res)) { - return res - } - - 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 @@ -217,11 +86,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: @@ -231,14 +96,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 @@ -247,11 +109,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: @@ -261,14 +119,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 @@ -277,11 +132,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: @@ -291,9 +142,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 } @@ -301,9 +152,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 @@ -312,11 +160,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: @@ -325,15 +169,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 @@ -342,11 +183,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: @@ -355,15 +192,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 @@ -372,11 +206,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: @@ -385,14 +215,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)` @@ -404,11 +233,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: @@ -418,17 +243,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 } @@ -438,7 +263,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)` @@ -450,11 +274,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: @@ -464,17 +284,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 } @@ -484,7 +304,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)` @@ -496,11 +315,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: @@ -510,17 +325,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 } @@ -530,7 +345,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)` @@ -542,11 +356,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: @@ -556,23 +366,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)` @@ -581,11 +388,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: @@ -594,14 +397,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)` @@ -610,11 +410,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: @@ -623,7 +419,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. @@ -633,7 +429,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) @@ -643,11 +438,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: @@ -656,9 +447,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)) } @@ -671,7 +462,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) @@ -683,11 +473,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: @@ -696,9 +482,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)) } @@ -711,7 +497,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 @@ -721,7 +506,6 @@ Example: import "core:fmt" read_example :: proc() { - // Using the global random number generator data: [8]byte n := rand.read(data[:]) fmt.println(n) @@ -735,12 +519,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) @@ -757,7 +541,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: @@ -770,16 +553,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 } @@ -790,10 +567,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 } @@ -805,14 +582,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[:]) @@ -825,14 +600,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] } } @@ -842,7 +617,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` @@ -852,7 +626,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[:])) @@ -869,17 +642,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, @@ -887,11 +660,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) } diff --git a/tests/core/hash/test_core_hash.odin b/tests/core/hash/test_core_hash.odin index c332383e7..0255717a2 100644 --- a/tests/core/hash/test_core_hash.odin +++ b/tests/core/hash/test_core_hash.odin @@ -54,8 +54,9 @@ test_xxhash_zero_streamed_random_updates :: proc(t: ^testing.T) { // XXH3_128_update random_seed := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&random_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 } diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 23de1b482..003021a8a 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -11,6 +11,7 @@ test_sort_with_indices :: proc(t: ^testing.T) { for test_size in test_sizes { r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) 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 @@ -62,6 +63,7 @@ test_sort_by_indices :: proc(t: ^testing.T) { for test_size in test_sizes { r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -72,7 +74,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 +82,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) diff --git a/tests/internal/test_map.odin b/tests/internal/test_map.odin index a9a8cf5d4..ab7e52f33 100644 --- a/tests/internal/test_map.odin +++ b/tests/internal/test_map.odin @@ -17,9 +17,10 @@ map_insert_random_key_value :: proc(t: ^testing.T) { unique_keys := 0 r := rand.create(t.seed + seed_incr) + context.random_generator = rand.default_random_generator(&r) for _ in 0.. Date: Sat, 15 Jun 2024 15:43:57 +0100 Subject: [PATCH 28/60] Remove the need for `rand` in `core:math/big` --- core/math/big/helpers.odin | 10 +++++----- core/math/big/internal.odin | 10 +++++----- core/math/big/prime.odin | 8 +++----- core/math/rand/rand.odin | 1 - 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin index 1969fac49..ee09bb2c7 100644 --- a/core/math/big/helpers.odin +++ b/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, } diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin index 29bdf555c..c9b331e55 100644 --- a/core/math/big/internal.odin +++ b/core/math/big/internal.odin @@ -2817,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.") } @@ -2829,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 @@ -2846,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) diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin index 7fc78c7e5..832c75119 100644 --- a/core/math/big/prime.odin +++ b/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 diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 7bb78cc1d..8e3c0264d 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -6,7 +6,6 @@ package rand import "base:intrinsics" import "base:runtime" -import "core:crypto" import "core:math" import "core:mem" From 784408358d39346b410df65b9f657007c467a010 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 10:25:58 -0400 Subject: [PATCH 29/60] Call `cleanups` after test signal --- core/testing/runner.odin | 11 +++++++++-- core/testing/testing.odin | 12 +++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 328186c35..147c6d094 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -604,10 +604,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 } } } @@ -655,6 +655,13 @@ runner :: proc(internal_tests: []Internal_Test) -> bool { } + find_task_data_for_stop_signal: 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_for_stop_signal + } + } + when FANCY_OUTPUT { bypass_progress_overwrite = true signals_were_raised = true diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 92b4d391d..07e2063ca 100644 --- a/core/testing/testing.odin +++ b/core/testing/testing.odin @@ -94,7 +94,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}) } From 2380720fa293bdc0eb2c564b232b5083d9742154 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 15:46:48 +0100 Subject: [PATCH 30/60] Fix `core:sync/chan` for rand --- core/sync/chan/chan.odin | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/sync/chan/chan.odin b/core/sync/chan/chan.odin index f4774e4f8..0c3f4a725 100644 --- a/core/sync/chan/chan.odin +++ b/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 { From c97ffbecbc80d29aaab5d257048aef5bfd28e4db Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 15:50:35 +0100 Subject: [PATCH 31/60] Fix `rand` in container tests --- tests/core/container/test_core_avl.odin | 7 ++++--- tests/core/container/test_core_rbtree.odin | 9 +++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/core/container/test_core_avl.odin b/tests/core/container/test_core_avl.odin index 99dbba8b2..db4428905 100644 --- a/tests/core/container/test_core_avl.odin +++ b/tests/core/container/test_core_avl.odin @@ -21,14 +21,15 @@ test_avl :: proc(t: ^testing.T) { testing.expect(t, avl.iterator_get(&iter) == nil, "empty/iterator: first node should be nil") r: rand.Rand - rand.init(&r, t.seed) + r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) // 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 +79,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)") diff --git a/tests/core/container/test_core_rbtree.odin b/tests/core/container/test_core_rbtree.odin index 8def8edb6..1aaad7372 100644 --- a/tests/core/container/test_core_rbtree.odin +++ b/tests/core/container/test_core_rbtree.odin @@ -15,7 +15,8 @@ test_rbtree_integer :: proc(t: ^testing.T, $Key: typeid, $Value: typeid) { context.allocator = mem.tracking_allocator(&track) r: rand.Rand - rand.init(&r, t.seed) + r := rand.create(t.seed) + context.random_generator = rand.default_random_generator(&r) 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) @@ -35,9 +36,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 +93,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) { From 1c940e3968a210ba0f06d663c54d8135790e27c1 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 15:53:16 +0100 Subject: [PATCH 32/60] Remove dead variable --- tests/core/container/test_core_avl.odin | 1 - tests/core/container/test_core_rbtree.odin | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/core/container/test_core_avl.odin b/tests/core/container/test_core_avl.odin index db4428905..556f371af 100644 --- a/tests/core/container/test_core_avl.odin +++ b/tests/core/container/test_core_avl.odin @@ -20,7 +20,6 @@ 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 r := rand.create(t.seed) context.random_generator = rand.default_random_generator(&r) diff --git a/tests/core/container/test_core_rbtree.odin b/tests/core/container/test_core_rbtree.odin index 1aaad7372..425a9b440 100644 --- a/tests/core/container/test_core_rbtree.odin +++ b/tests/core/container/test_core_rbtree.odin @@ -14,7 +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 r := rand.create(t.seed) context.random_generator = rand.default_random_generator(&r) From bb823d5ba0aa412c40a0e23c9b27f00c734e9866 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 10:36:05 -0400 Subject: [PATCH 33/60] Make `testing.fail_now` divergent This is in line with the old way it worked on Windows. --- core/testing/runner.odin | 37 +++++++++++++++++++++++-------------- core/testing/testing.odin | 15 ++++++++++----- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 147c6d094..6a33436f6 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -497,6 +497,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) } @@ -645,28 +646,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 { - end_t(&data.t) + 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 } diff --git a/core/testing/testing.odin b/core/testing/testing.odin index 07e2063ca..29fe853ef 100644 --- a/core/testing/testing.odin +++ b/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 { From f353adc7fbb9a185724e36fba1b8bb5ec1059913 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 10:39:28 -0400 Subject: [PATCH 34/60] Prefer `log.error` over `fail_now` in this case --- tests/core/slice/test_core_slice.odin | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 23de1b482..3a15ddaa0 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/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) { @@ -205,7 +206,7 @@ test_permutation_iterator :: proc(t: ^testing.T) { 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 From 753516c3926766c51e78f8ff8933a4bef29affff Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 11:18:40 -0400 Subject: [PATCH 35/60] Add `ODIN_TEST_SHORT_LOGS` define Strips out the procedure, date, and time information, for when you just need to know the file, line, and message. --- core/testing/logging.odin | 23 ++++++++++++++++------- core/testing/runner.odin | 2 ++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/core/testing/logging.odin b/core/testing/logging.odin index 5bbbffeae..f1e75d33c 100644 --- a/core/testing/logging.odin +++ b/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 { diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 328186c35..134ef72fc 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -41,6 +41,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 { From e41ad2bf16e2164328e3e6f912271c32f8eb5390 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 15 Jun 2024 16:24:01 +0100 Subject: [PATCH 36/60] `to_random_generator` -> `random_generator` --- core/crypto/crypto.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/crypto/crypto.odin b/core/crypto/crypto.odin index d995ecf7d..cd80567e2 100644 --- a/core/crypto/crypto.odin +++ b/core/crypto/crypto.odin @@ -61,7 +61,7 @@ rand_bytes :: proc (dst: []byte) { } -to_random_generator :: proc() -> runtime.Random_Generator { +random_generator :: proc() -> runtime.Random_Generator { return { procedure = proc(data: rawptr, mode: runtime.Random_Generator_Mode, p: []byte) { switch mode { From 53140dca15c44f4bf44b42c887c886765248e030 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 12:54:30 -0400 Subject: [PATCH 37/60] Fix `default_random_generator_proc` not using state --- base/runtime/random_generator.odin | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/base/runtime/random_generator.odin b/base/runtime/random_generator.odin index 205d7eb7e..0a9cab860 100644 --- a/base/runtime/random_generator.odin +++ b/base/runtime/random_generator.odin @@ -73,7 +73,12 @@ default_random_generator_proc :: proc(data: rawptr, mode: Random_Generator_Mode, _ = read_u64(r) } - r := &global_rand_seed + r: ^Default_Random_State = --- + if data == nil { + r = &global_rand_seed + } else { + r = cast(^Default_Random_State)data + } switch mode { case .Read: From 5dfd303fd15750f2128f6f45b8d3ef157a2090d8 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 13:26:40 -0400 Subject: [PATCH 38/60] Setup default `context.random_generator` for tests --- core/testing/runner.odin | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/testing/runner.odin b/core/testing/runner.odin index a0f9eee31..01464e1aa 100644 --- a/core/testing/runner.odin +++ b/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" @@ -108,6 +109,9 @@ run_test_task :: proc(task: thread.Task) { free_all(context.temp_allocator) + random_generator_state := rand.create(data.t.seed) + context.random_generator = rand.default_random_generator(&random_generator_state) + data.it.p(&data.t) end_t(&data.t) From 1a52cf1f1c4becff3f107111038e50b0a0c74594 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 13:37:04 -0400 Subject: [PATCH 39/60] Use test's random generator This removes the `create` calls when a test was only setting up a generator, and it replaces them with `reset` when run in a loop. --- tests/core/container/test_core_avl.odin | 3 -- tests/core/container/test_core_rbtree.odin | 3 -- tests/core/hash/test_core_hash.odin | 3 +- tests/core/slice/test_core_slice.odin | 6 ++-- tests/internal/test_map.odin | 39 ++++++++-------------- 5 files changed, 16 insertions(+), 38 deletions(-) diff --git a/tests/core/container/test_core_avl.odin b/tests/core/container/test_core_avl.odin index 556f371af..0e2d0d94a 100644 --- a/tests/core/container/test_core_avl.odin +++ b/tests/core/container/test_core_avl.odin @@ -20,9 +20,6 @@ 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.create(t.seed) - context.random_generator = rand.default_random_generator(&r) - // Test insertion. NR_INSERTS :: 32 + 1 // Ensure at least 1 collision. inserted_map := make(map[int]^avl.Node(int)) diff --git a/tests/core/container/test_core_rbtree.odin b/tests/core/container/test_core_rbtree.odin index 425a9b440..b686ef6dd 100644 --- a/tests/core/container/test_core_rbtree.odin +++ b/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.create(t.seed) - context.random_generator = rand.default_random_generator(&r) - 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) diff --git a/tests/core/hash/test_core_hash.odin b/tests/core/hash/test_core_hash.odin index 0255717a2..c3f0bee91 100644 --- a/tests/core/hash/test_core_hash.odin +++ b/tests/core/hash/test_core_hash.odin @@ -53,8 +53,7 @@ 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) - context.random_generator = rand.default_random_generator(&random_seed) + rand.reset(t.seed) for len(b) > 0 { update_size := min(len(b), rand.int_max(8192)) if update_size > 4096 { diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 432636664..5825c49b6 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -11,8 +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) - context.random_generator = rand.default_random_generator(&r) + rand.reset(t.seed) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index @@ -63,8 +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) - context.random_generator = rand.default_random_generator(&r) + rand.reset(t.seed) vals := make([]u64, test_size) r_idx := make([]int, test_size) // Reverse index diff --git a/tests/internal/test_map.odin b/tests/internal/test_map.odin index ab7e52f33..9bd5d34ea 100644 --- a/tests/internal/test_map.odin +++ b/tests/internal/test_map.odin @@ -16,8 +16,7 @@ map_insert_random_key_value :: proc(t: ^testing.T) { defer delete(m) unique_keys := 0 - r := rand.create(t.seed + seed_incr) - context.random_generator = rand.default_random_generator(&r) + rand.reset(t.seed + seed_incr) for _ in 0.. Date: Sat, 15 Jun 2024 13:50:00 -0400 Subject: [PATCH 40/60] Add tests for `core:math/rand` --- tests/core/math/rand/test_core_math_rand.odin | 35 +++++++++++++++++++ tests/core/normal.odin | 1 + 2 files changed, 36 insertions(+) create mode 100644 tests/core/math/rand/test_core_math_rand.odin diff --git a/tests/core/math/rand/test_core_math_rand.odin b/tests/core/math/rand/test_core_math_rand.odin new file mode 100644 index 000000000..392d3d241 --- /dev/null +++ b/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.") +} diff --git a/tests/core/normal.odin b/tests/core/normal.odin index 7620d7d6e..c8bb59269 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -24,6 +24,7 @@ download_assets :: proc() { @(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" From 8b31cddaba18cc646d471b35249a3f2771e20651 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 15 Jun 2024 13:50:21 -0400 Subject: [PATCH 41/60] Keep `-vet` happy --- tests/core/slice/test_core_slice.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 5825c49b6..1f8f539c7 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -201,7 +201,7 @@ 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 } From e41878a64f3de4ba1eefe9f2364156f3065e4a40 Mon Sep 17 00:00:00 2001 From: Sandro Cavazzoni Date: Sun, 16 Jun 2024 11:36:20 +0200 Subject: [PATCH 42/60] Fix `slice.unique` wrong result When you try to make this array unique `[]int{1, 2, 4, 4, 5}` you get `[]int{1, 4, 5}` instead of `[]int{1, 2, 4, 5}`. Our index `i` should be increased even with both indices `i` and `j` have the same value --- core/slice/slice.odin | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/slice/slice.odin b/core/slice/slice.odin index d8f4df88e..06f08fda2 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -495,8 +495,10 @@ unique :: proc(s: $S/[]$T) -> S where intrinsics.type_is_comparable(T) #no_bound } i := 1 for j in 1.. bool) -> S #no_bounds_check { } i := 1 for j in 1.. Date: Sun, 16 Jun 2024 13:41:25 +0200 Subject: [PATCH 43/60] Add tests for `slice.unique` and `slice.unique_proc` --- tests/core/slice/test_core_slice.odin | 62 ++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/tests/core/slice/test_core_slice.odin b/tests/core/slice/test_core_slice.odin index 432636664..76aa4756c 100644 --- a/tests/core/slice/test_core_slice.odin +++ b/tests/core/slice/test_core_slice.odin @@ -203,7 +203,7 @@ 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 } @@ -218,3 +218,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) + } +} \ No newline at end of file From 78a5a27212a6b892e32e58a84ea0c25cd83fac3c Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 16 Jun 2024 12:27:42 -0400 Subject: [PATCH 44/60] Fix `bit_set` construction crash on constant non-integer field --- src/check_expr.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 359b30276..e548eac88 100644 --- a/src/check_expr.cpp +++ b/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); From eebc0dd026b50df123eaf40dd286af870354ea40 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 16 Jun 2024 19:11:54 +0200 Subject: [PATCH 45/60] Fix `rc.exe` bug with double-quoted `.res` path. --- src/linker.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/linker.cpp b/src/linker.cpp index 25c54a6ab..9eed14ea9 100644 --- a/src/linker.cpp +++ b/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) From 1a93dfd28f8e256c164d67985866f0aab3659d44 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 17 Jun 2024 13:24:47 -0400 Subject: [PATCH 46/60] Fix indentation --- core/unicode/letter.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/unicode/letter.odin b/core/unicode/letter.odin index 891c90bf3..ff167334c 100644 --- a/core/unicode/letter.odin +++ b/core/unicode/letter.odin @@ -144,10 +144,10 @@ 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)) } From 1620a69398234cb3a58c531bb57416a6ae801136 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 17 Jun 2024 21:21:06 -0400 Subject: [PATCH 47/60] Add `decode_grapheme_clusters` to `core:unicode/utf8` --- core/unicode/letter.odin | 222 +++ core/unicode/tables.odin | 2449 +++++++++++++++++++++++++++++++ core/unicode/utf8/grapheme.odin | 387 +++++ 3 files changed, 3058 insertions(+) create mode 100644 core/unicode/utf8/grapheme.odin diff --git a/core/unicode/letter.odin b/core/unicode/letter.odin index ff167334c..46b84849f 100644 --- a/core/unicode/letter.odin +++ b/core/unicode/letter.odin @@ -5,6 +5,9 @@ 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' + binary_search :: proc(c: i32, table: []i32, length, stride: int) -> int { n := length t := 0 @@ -193,3 +196,222 @@ is_symbol :: proc(r: rune) -> bool { } return false } + +// +// The procedures below are accurate as of Unicode 15.1.0. +// + +// Emoji_Modifier +is_emoji_modifier :: proc(r: rune) -> bool { + return 0x1F3FB <= r && r <= 0x1F3FF +} + +// Regional_Indicator +is_regional_indicator :: proc(r: rune) -> bool { + return 0x1F1E6 <= r && r <= 0x1F1FF +} + +// General_Category=Enclosing_Mark +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 +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 +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 +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 +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 +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 +is_hangul_syllable_leading :: proc(r: rune) -> bool { + return 0x1100 <= r && r <= 0x115F || 0xA960 <= r && r <= 0xA97C +} + +// Hangul_Syllable_Type=Vowel_Jamo +is_hangul_syllable_vowel :: proc(r: rune) -> bool { + return 0x1160 <= r && r <= 0x11A7 || 0xD7B0 <= r && r <= 0xD7C6 +} + +// Hangul_Syllable_Type=Trailing_Jamo +is_hangul_syllable_trailing :: proc(r: rune) -> bool { + return 0x11A8 <= r && r <= 0x11FF || 0xD7CB <= r && r <= 0xD7FB +} + +// Hangul_Syllable_Type=LV_Syllable +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 +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 +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 +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 +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 +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 +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 +``` +*/ +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. +``` +*/ +is_gcb_extend_class :: proc(r: rune) -> bool { + return is_grapheme_extend(r) || is_emoji_modifier(r) +} + +// +// End of Unicode 15.1.0 block. +// diff --git a/core/unicode/tables.odin b/core/unicode/tables.odin index dfa5caaa2..5812e3132 100644 --- a/core/unicode/tables.odin +++ b/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. +// diff --git a/core/unicode/utf8/grapheme.odin b/core/unicode/utf8/grapheme.odin new file mode 100644 index 000000000..c0851c6ea --- /dev/null +++ b/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 +} From 63973f431e7fdbea44c717015d573864040100b6 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 17 Jun 2024 21:30:00 -0400 Subject: [PATCH 48/60] Make `core:unicode` letter procs `@(require_results)` --- core/unicode/letter.odin | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/core/unicode/letter.odin b/core/unicode/letter.odin index 46b84849f..11bddedda 100644 --- a/core/unicode/letter.odin +++ b/core/unicode/letter.odin @@ -8,6 +8,7 @@ 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 @@ -27,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) @@ -39,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) @@ -51,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) @@ -61,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 @@ -77,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 @@ -94,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 @@ -114,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' @@ -127,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 { @@ -143,6 +153,7 @@ is_space :: proc(r: rune) -> bool { return false } +@(require_results) is_combining :: proc(r: rune) -> bool { c := i32(r) @@ -155,6 +166,7 @@ is_combining :: proc(r: rune) -> bool { +@(require_results) is_graphic :: proc(r: rune) -> bool { if u32(r) <= MAX_LATIN1 { return char_properties[u8(r)]&pg != 0 @@ -162,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 @@ -169,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 @@ -176,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 @@ -183,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 @@ -190,6 +206,7 @@ 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 @@ -202,16 +219,19 @@ is_symbol :: proc(r: rune) -> bool { // // 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, @@ -226,6 +246,7 @@ is_enclosing_mark :: proc(r: rune) -> bool { } // Prepended_Concatenation_Mark +@(require_results) is_prepended_concatenation_mark :: proc(r: rune) -> bool { switch r { case 0x00600 ..= 0x00605, @@ -242,6 +263,7 @@ is_prepended_concatenation_mark :: proc(r: rune) -> bool { } // 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) @@ -252,6 +274,7 @@ is_spacing_mark :: proc(r: rune) -> bool { } // 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) @@ -262,6 +285,7 @@ is_nonspacing_mark :: proc(r: rune) -> bool { } // 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) @@ -272,6 +296,7 @@ is_emoji_extended_pictographic :: proc(r: rune) -> bool { } // 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) @@ -283,21 +308,25 @@ is_grapheme_extend :: proc(r: rune) -> bool { // 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) @@ -308,6 +337,7 @@ is_hangul_syllable_lv :: proc(r: rune) -> bool { } // 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) @@ -319,6 +349,7 @@ is_hangul_syllable_lvt :: proc(r: rune) -> bool { // Indic_Syllabic_Category=Consonant_Preceding_Repha +@(require_results) is_indic_consonant_preceding_repha :: proc(r: rune) -> bool { switch r { case 0x00D4E, @@ -332,6 +363,7 @@ is_indic_consonant_preceding_repha :: proc(r: rune) -> bool { } // Indic_Syllabic_Category=Consonant_Prefixed +@(require_results) is_indic_consonant_prefixed :: proc(r: rune) -> bool { switch r { case 0x111C2 ..= 0x111C3, @@ -345,6 +377,7 @@ is_indic_consonant_prefixed :: proc(r: rune) -> bool { } // Indic_Conjunct_Break=Linker +@(require_results) is_indic_conjunct_break_linker :: proc(r: rune) -> bool { switch r { case 0x094D, @@ -360,6 +393,7 @@ is_indic_conjunct_break_linker :: proc(r: rune) -> bool { } // 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) @@ -370,6 +404,7 @@ is_indic_conjunct_break_consonant :: proc(r: rune) -> bool { } // 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) @@ -389,6 +424,7 @@ 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) } @@ -408,6 +444,7 @@ 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) } From 9e4899d35c2df96a28fc928a6ece4b23b5857703 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Mon, 17 Jun 2024 21:33:12 -0400 Subject: [PATCH 49/60] Add tests for `decode_grapheme_clusters` --- tests/core/unicode/test_core_unicode.odin | 73 + .../core/unicode/test_core_unicode_data.odin | 4912 +++++++++++++++++ 2 files changed, 4985 insertions(+) create mode 100644 tests/core/unicode/test_core_unicode.odin create mode 100644 tests/core/unicode/test_core_unicode_data.odin diff --git a/tests/core/unicode/test_core_unicode.odin b/tests/core/unicode/test_core_unicode.odin new file mode 100644 index 000000000..c097d518a --- /dev/null +++ b/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) +} diff --git a/tests/core/unicode/test_core_unicode_data.odin b/tests/core/unicode/test_core_unicode_data.odin new file mode 100644 index 000000000..594af3c65 --- /dev/null +++ b/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}, +} From eb5a66c944b62104537ae2762ca26911ef447f2f Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 18 Jun 2024 01:28:05 -0400 Subject: [PATCH 50/60] Forbid private test cases --- src/checker.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/checker.cpp b/src/checker.cpp index 852fb89bb..49726c090 100644 --- a/src/checker.cpp +++ b/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) { From 0196cc46e35eb0074ffd5f8dd24cdf8c42fde9e0 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 18 Jun 2024 01:47:51 -0400 Subject: [PATCH 51/60] Allow custom `main` when `-no-entry-point` is set --- src/check_decl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 02445cbc6..818556951 100644 --- a/src/check_decl.cpp +++ b/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) { From 11e586494bb6c7dcdb0cd484711d4e6a9c606e55 Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Tue, 18 Jun 2024 12:46:50 +0200 Subject: [PATCH 52/60] Clarify that the arena in base:runtime shouldn't be used for anything but the default temp allocator. This is done by renaming the file in which it lives, and also by improving the comment above `Arena :: struct {`. This should avoid some confusion where people end up using the Arena in base:runtime because they thought it was the 'default arena'. --- ...allocators_arena.odin => default_temp_allocator_arena.odin} | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) rename base/runtime/{default_allocators_arena.odin => default_temp_allocator_arena.odin} (98%) diff --git a/base/runtime/default_allocators_arena.odin b/base/runtime/default_temp_allocator_arena.odin similarity index 98% rename from base/runtime/default_allocators_arena.odin rename to base/runtime/default_temp_allocator_arena.odin index 571590f93..ab9f7df4a 100644 --- a/base/runtime/default_allocators_arena.odin +++ b/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, From de44dd5412b40c950cc718ad7a713ff1b6d77cc5 Mon Sep 17 00:00:00 2001 From: destroycomputers Date: Tue, 18 Jun 2024 13:31:45 +0200 Subject: [PATCH 53/60] Loosen constraint on the Key type in rb & avl tree The current constraint on the `init_ordered' function accepts only numeric types. While one still can init a non-numerically-keyed tree by passing a comparator explicitly, the reason to disallow non-numeric ordered types is nowhere to be found. --- core/container/avl/avl.odin | 2 +- core/container/rbtree/rbtree.odin | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/container/avl/avl.odin b/core/container/avl/avl.odin index 582cd87fd..8a9d1f3d9 100644 --- a/core/container/avl/avl.odin +++ b/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) } diff --git a/core/container/rbtree/rbtree.odin b/core/container/rbtree/rbtree.odin index 8ab131b3b..57a68a948 100644 --- a/core/container/rbtree/rbtree.odin +++ b/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) } From 3d9db5641047fc522fa82ff02ea787d30f38b54c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 18 Jun 2024 12:53:56 +0100 Subject: [PATCH 54/60] Minor formatting changes to unicode stuff --- core/unicode/letter.odin | 3 +- core/unicode/tables.odin | 4818 +++++++++++++++++++------------------- 2 files changed, 2411 insertions(+), 2410 deletions(-) diff --git a/core/unicode/letter.odin b/core/unicode/letter.odin index 11bddedda..f88907501 100644 --- a/core/unicode/letter.odin +++ b/core/unicode/letter.odin @@ -239,7 +239,8 @@ is_enclosing_mark :: proc(r: rune) -> bool { 0x1ABE, 0x20DD ..= 0x20E0, 0x20E2 ..= 0x20E4, - 0xA670 ..= 0xA672: return true + 0xA670 ..= 0xA672: + return true } return false diff --git a/core/unicode/tables.odin b/core/unicode/tables.odin index 5812e3132..5b3d0806e 100644 --- a/core/unicode/tables.odin +++ b/core/unicode/tables.odin @@ -1277,2443 +1277,2443 @@ to_title_singlets := [?]i32{ @(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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, } // From 6b25d17ef9262612b0b62dfc47ebada7ac974949 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 18 Jun 2024 10:38:43 -0400 Subject: [PATCH 55/60] Add missing imports to `core` test suite --- tests/core/normal.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/core/normal.odin b/tests/core/normal.odin index 7620d7d6e..cb49fed6b 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -19,6 +19,7 @@ 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" @@ -37,3 +38,4 @@ download_assets :: proc() { @(require) import "text/match" @(require) import "thread" @(require) import "time" +@(require) import "unicode" From e3f4772d013217e94d0b64268e67107a49a0ef35 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 18 Jun 2024 11:06:49 -0400 Subject: [PATCH 56/60] Fix removal of temporary file in `core:flags` test --- tests/core/flags/test_core_flags.odin | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin index 71fc34701..e32c6832c 100644 --- a/tests/core/flags/test_core_flags.odin +++ b/tests/core/flags/test_core_flags.odin @@ -1113,6 +1113,13 @@ test_if_map_cstrings_get_freed :: proc(t: ^testing.T) { @(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!" @@ -1147,14 +1154,6 @@ test_os_handle :: proc(t: ^testing.T) { 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) - - if file_contents_equal { - // Delete the file now that we're done. - // - // This is not done as a defer or all the time, just in case the file - // is useful to debugging. - testing.expect_value(t, os.remove(TEMPORARY_FILENAME), os.ERROR_NONE) - } } @(test) From d4803583ffced81e08d3e68f947fa3c230f26923 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 18 Jun 2024 23:21:04 -0400 Subject: [PATCH 57/60] Work around Windows test failure I am uncertain why this works, but it does. Previously, `rtti_test` was failing due to non-zero data appearing in the `l_buggy` `Buggy_Struct`. The issue was caused by calling `runtime.default_random_generator` with a pointer to the state, somehow. The pointer could be on the stack or in the heap; it did not matter. I found two workarounds. - One is to move the RNG setup behind the call to `free_all`. - The other is to construct the random generator manually. Despite my digging and testing, I could find no reason as to why this works or what the fundamental issue was to begin with. If anyone comes upon this in the future with direct access to a Windows machine, I recommend stepping through the program with a debugger to investigate more deeply into why this happens. --- core/testing/runner.odin | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 01464e1aa..3510856c7 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -107,10 +107,14 @@ run_test_task :: proc(task: thread.Task) { options = Default_Test_Logger_Opts, } - free_all(context.temp_allocator) + 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) - random_generator_state := rand.create(data.t.seed) - context.random_generator = rand.default_random_generator(&random_generator_state) + free_all(context.temp_allocator) data.it.p(&data.t) From 53755824fb5ec7014d0e256fad124d3d805fd1ca Mon Sep 17 00:00:00 2001 From: "Maurizio M. Gavioli" Date: Wed, 5 Jun 2024 10:32:23 +0200 Subject: [PATCH 58/60] Separate the I18N calls for immutable strings and for pluraliseable strings. Also update tests. --- core/text/i18n/doc.odin | 52 ++++-- core/text/i18n/i18n.odin | 162 +++++++++++++----- tests/core/text/i18n/test_core_text_i18n.odin | 40 +++-- 3 files changed, 177 insertions(+), 77 deletions(-) diff --git a/core/text/i18n/doc.odin b/core/text/i18n/doc.odin index 54bf8b80f..56ce9bde5 100644 --- a/core/text/i18n/doc.odin +++ b/core/text/i18n/doc.odin @@ -1,31 +1,44 @@ /* -The `i18n` package is flexible and easy to use. +The `i18n` package is a flexible and easy to use way to localise applications. -It has one call to get a translation: `get`, which the user can alias into something like `T`. +It has two calls to get a translation: `get()` amd `get_n()`, which the user can alias into something like `T` and `Tn` +with statements like: + T :: i18n.get + Tn :: i18n.get_n. -`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. +`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 semtece is fixed and it will have only one possible translation in any other language. -- `T(key)` returns the translation of `key`. -- `T(key, n)` returns a pluralized translation of `key` according to value `n`. +`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 which be used to select the correct form according to the pluraliser attached to the message catalogue when initially loaded; +for instance, to summarise a rather complex matter, some languages use the singular form when reerring 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 an a universal plural form: +for istance, Chinese has just one form for any quantity, while Welsh may have up to 6 different forms for specific different quantities. -- `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`. +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 speciic catalogue is supplied -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)` 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. -- `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. +- `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 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 numbe of item and returning which plural slot should be used. You can also assign it to a loaded catalog after parsing, of course. @@ -35,6 +48,7 @@ Example: import "core:text/i18n" T :: i18n.get + Tn :: i18n.get_n mo :: proc() { using fmt @@ -60,9 +74,9 @@ 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. @@ -99,8 +113,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 diff --git a/core/text/i18n/i18n.odin b/core/text/i18n/i18n.odin index 64593c4e8..87bd77045 100644 --- a/core/text/i18n/i18n.odin +++ b/core/text/i18n/i18n.odin @@ -84,64 +84,149 @@ DEFAULT_PARSE_OPTIONS :: Parse_Options{ merge_sections = false, } -/* - 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. -*/ -get_single_section :: proc(key: string, number := 1, 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 +// ***************** +// get() PROC GROUP +// ***************** - if catalog.pluralize != nil { - plural = catalog.pluralize(number) - } - return get_by_slot(key, plural, catalog) +/* + Returns the first translation string for the passed `key`. + It is also aliases 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, catalog: ^Translation = ACTIVE) -> (value: string) { + return get_by_slot(key, 0, 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. -*/ -get_by_section :: proc(section, key: string, number := 1, 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 + Returns the first translation string for the passed `key` in a specific section or context. + It is also aliases with `get( )`. - if catalog.pluralize != nil { - plural = catalog.pluralize(number) - } - return get_by_slot(section, key, plural, catalog) + 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 (sometime also called 'context') from which to lookup 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} +// ***************** +// get_n() PROC GROUP +// ***************** + /* - 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. + Returns the translation string for the passed `key` in a specific plural form (if present in the catalogue). + It is also aliases 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. + - 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_single_section_w_plural :: 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. + */ + slot := 1 if quantity != 1 else 0 + + if catalog.pluralize != nil { + slot = catalog.pluralize(quantity) + } + return get_by_slot(key, slot, catalog) +} + +/* + 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_w_plural :: 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. + */ + slot := 1 if quantity != 1 else 0 + + if catalog.pluralize != nil { + slot = catalog.pluralize(quantity) + } + return get_by_slot(section, key, slot, catalog) +} +get_n :: proc{get_single_section_w_plural, get_by_section_w_plural} + +// ***************** +// get_by_slot() PROC GROUP +// ***************** + +/* + 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. + - qantity: 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. + - qantity: 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. @@ -161,7 +246,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. */ @@ -181,4 +265,4 @@ destroy :: proc(catalog: ^Translation = ACTIVE, allocator := context.allocator) delete(catalog.k_v) strings.intern_destroy(&catalog.intern) free(catalog) -} \ No newline at end of file +} diff --git a/tests/core/text/i18n/test_core_text_i18n.odin b/tests/core/text/i18n/test_core_text_i18n.odin index f6cffc318..4c70bd8b9 100644 --- a/tests/core/text/i18n/test_core_text_i18n.odin +++ b/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) } } From e0de52efa114f461c7b9bdbd04a4e53d39b01d15 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 19 Jun 2024 11:15:17 +0200 Subject: [PATCH 59/60] Spell check doc.odin. --- core/text/i18n/doc.odin | 52 ++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/core/text/i18n/doc.odin b/core/text/i18n/doc.odin index 56ce9bde5..d590fd123 100644 --- a/core/text/i18n/doc.odin +++ b/core/text/i18n/doc.odin @@ -2,25 +2,25 @@ /* The `i18n` package is a flexible and easy to use way to localise applications. -It has two calls to get a translation: `get()` amd `get_n()`, which the user can alias into something like `T` and `Tn` +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 + T :: i18n.get Tn :: i18n.get_n. -`get()` is used for retrieving the translation of sentences which **never**change in form, +`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 semtece is fixed and it will have only one possible translation in any other language. +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 which be used to select the correct form according to the pluraliser attached to the message catalogue when initially loaded; -for instance, to summarise a rather complex matter, some languages use the singular form when reerring 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 an a universal plural form: -for istance, Chinese has just one form for any quantity, while Welsh may have up to 6 different forms for specific different quantities. +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 speciic catalogue is supplied +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. @@ -34,11 +34,11 @@ By default lookup take place in the global `i18n.ACTIVE` catalogue for ease of u 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 returns the singular form (in slot 0). -Passing n != 1 returns plural form in slot 1 (if any). +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 numbe of item 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. @@ -47,7 +47,7 @@ Example: import "core:fmt" import "core:text/i18n" - T :: i18n.get + T :: i18n.get Tn :: i18n.get_n mo :: proc() { @@ -55,17 +55,13 @@ Example: 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("-----") @@ -78,9 +74,7 @@ Example: // We pass 42 into `T` to get the plural format string, then 42 again into printf. 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!")) } @@ -90,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("-----") From 322b7f1f2f9ff32366429155d8a87279089799f5 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 19 Jun 2024 11:41:58 +0200 Subject: [PATCH 60/60] Spell check i18n.odin. --- core/text/i18n/i18n.odin | 112 +++++++++++++-------------------------- 1 file changed, 38 insertions(+), 74 deletions(-) diff --git a/core/text/i18n/i18n.odin b/core/text/i18n/i18n.odin index 87bd77045..0190ef0f7 100644 --- a/core/text/i18n/i18n.odin +++ b/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, @@ -84,23 +64,19 @@ DEFAULT_PARSE_OPTIONS :: Parse_Options{ merge_sections = false, } -// ***************** -// get() PROC GROUP -// ***************** - /* Returns the first translation string for the passed `key`. - It is also aliases with `get( )`. + 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. + - 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. + - 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. + Returns: the translated string, or the original `key` if no translation was found. */ get_single_section :: proc(key: string, catalog: ^Translation = ACTIVE) -> (value: string) { return get_by_slot(key, 0, catalog) @@ -108,18 +84,18 @@ get_single_section :: proc(key: string, catalog: ^Translation = ACTIVE) -> (valu /* Returns the first translation string for the passed `key` in a specific section or context. - It is also aliases with `get( )`. + 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. + - 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 (sometime also called 'context') from which to lookup the translation - - key: the string to translate. + - 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. + 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) @@ -127,26 +103,22 @@ get_by_section :: proc(section, key: string, catalog: ^Translation = ACTIVE) -> get :: proc{get_single_section, get_by_section} -// ***************** -// get_n() PROC GROUP -// ***************** - /* Returns the translation string for the passed `key` in a specific plural form (if present in the catalogue). - It is also aliases with `get_n( )`. + 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. + - get_n(key, quantity, catalog) to grab text from a specific loaded catalogue Inputs: - - 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) + - 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. + Returns: the translated string, or the original `key` if no translation was found. */ -get_single_section_w_plural :: proc(key: string, quantity: int, catalog: ^Translation = ACTIVE) -> (value: string) { +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. */ @@ -161,21 +133,21 @@ get_single_section_w_plural :: proc(key: string, quantity: int, catalog: ^Transl /* 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( )`. + 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. + - 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. + - 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. + Returns: the translated string, or the original `key` if no translation was found */ -get_by_section_w_plural :: proc(section, key: string, quantity: int, 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. */ @@ -186,11 +158,7 @@ get_by_section_w_plural :: proc(section, key: string, quantity: int, catalog: ^T } return get_by_slot(section, key, slot, catalog) } -get_n :: proc{get_single_section_w_plural, get_by_section_w_plural} - -// ***************** -// get_by_slot() PROC GROUP -// ***************** +get_n :: proc{get_single_section_with_quantity, get_by_section_with_quantity} /* Two ways to use: @@ -202,10 +170,10 @@ get_n :: proc{get_single_section_w_plural, get_by_section_w_plural} Inputs: - key: the string to translate. - - qantity: the translation slot to choose (slots refer to plural forms specific for each language and their meaning changes from catalogue to catalogue). + - 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. + Returns: the translated string, or the original `key` if no translation was found. */ get_by_slot_single_section :: proc(key: string, slot: int, catalog: ^Translation = ACTIVE) -> (value: string) { return get_by_slot_by_section("", key, slot, catalog) @@ -221,22 +189,18 @@ get_by_slot_single_section :: proc(key: string, slot: int, catalog: ^Translation Inputs: - section: the catalogue section (sometime also called 'context') from which to lookup the translation - key: the string to translate. - - qantity: the translation slot to choose (slots refer to plural forms specific for each language and their meaning changes from catalogue to catalogue). + - 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: 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] @@ -265,4 +229,4 @@ destroy :: proc(catalog: ^Translation = ACTIVE, allocator := context.allocator) delete(catalog.k_v) strings.intern_destroy(&catalog.intern) free(catalog) -} +} \ No newline at end of file