From f5d278fe90e4feac335aeef8f6b8b72a2f720a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Fri, 6 Mar 2026 17:20:04 +0200 Subject: [PATCH] Update HarfBuzz to 13.1.1 and optionally use new `hb-raster` API to render glyphs. --- doc/classes/Font.xml | 22 + doc/classes/FontVariation.xml | 6 + doc/classes/TextServer.xml | 55 +- doc/classes/TextServerExtension.xml | 53 + .../editor_property_name_processor.cpp | 1 + .../4.1-stable_4.2-stable/GH-80954.txt | 2 +- .../4.2-stable_4.3-stable/GH-87668.txt | 2 +- .../4.6-stable/GH-117149.txt | 5 + modules/text_server_adv/SCsub | 24 +- modules/text_server_adv/text_server_adv.cpp | 305 +- modules/text_server_adv/text_server_adv.h | 33 +- modules/text_server_fb/text_server_fb.cpp | 45 + modules/text_server_fb/text_server_fb.h | 8 + scene/resources/font.compat.inc | 9 +- scene/resources/font.cpp | 108 +- scene/resources/font.h | 22 +- servers/text/text_server.cpp | 10 +- servers/text/text_server.h | 8 + servers/text/text_server_extension.cpp | 45 + servers/text/text_server_extension.h | 15 + thirdparty/README.md | 2 +- thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh | 5 +- thirdparty/harfbuzz/src/OT/Color/svg/svg.hh | 686 +- .../src/OT/Layout/GPOS/SinglePosFormat1.hh | 2 +- .../src/OT/Layout/GPOS/SinglePosFormat2.hh | 2 +- thirdparty/harfbuzz/src/OT/Var/VARC/VARC.cc | 68 +- thirdparty/harfbuzz/src/OT/glyf/Glyph.hh | 28 +- thirdparty/harfbuzz/src/hb-algs.hh | 84 +- thirdparty/harfbuzz/src/hb-atomic.hh | 3 + .../harfbuzz/src/hb-bit-set-invertible.hh | 9 +- thirdparty/harfbuzz/src/hb-bit-set.hh | 18 +- thirdparty/harfbuzz/src/hb-blob.hh | 14 + .../src/hb-buffer-deserialize-json.hh | 8 +- .../src/hb-buffer-deserialize-text-glyphs.hh | 10 +- .../src/hb-buffer-deserialize-text-unicode.hh | 10 +- .../harfbuzz/src/hb-buffer-serialize.cc | 44 +- thirdparty/harfbuzz/src/hb-buffer.cc | 55 +- thirdparty/harfbuzz/src/hb-buffer.h | 14 +- thirdparty/harfbuzz/src/hb-buffer.hh | 39 +- thirdparty/harfbuzz/src/hb-cairo.cc | 3 +- .../harfbuzz/src/hb-cff-interp-common.hh | 41 +- thirdparty/harfbuzz/src/hb-cff-specializer.hh | 273 + .../harfbuzz/src/hb-cff-width-optimizer.hh | 207 + thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh | 2 +- thirdparty/harfbuzz/src/hb-config.hh | 1 + thirdparty/harfbuzz/src/hb-coretext-shape.cc | 2 +- thirdparty/harfbuzz/src/hb-cplusplus.hh | 64 +- thirdparty/harfbuzz/src/hb-directwrite.cc | 3 + thirdparty/harfbuzz/src/hb-limits.hh | 12 + thirdparty/harfbuzz/src/hb-number-parser.hh | 8 +- thirdparty/harfbuzz/src/hb-object.hh | 17 +- thirdparty/harfbuzz/src/hb-open-type.hh | 21 +- thirdparty/harfbuzz/src/hb-ot-cmap-table.hh | 170 +- thirdparty/harfbuzz/src/hb-ot-color.cc | 68 + thirdparty/harfbuzz/src/hb-ot-color.h | 14 + .../harfbuzz/src/hb-ot-face-table-list.hh | 2 + thirdparty/harfbuzz/src/hb-ot-font.cc | 2 + .../harfbuzz/src/hb-ot-layout-common.hh | 27 +- .../harfbuzz/src/hb-ot-layout-gsubgpos.hh | 56 +- thirdparty/harfbuzz/src/hb-ot-shape.cc | 8 +- .../harfbuzz/src/hb-ot-shaper-arabic-pua.hh | 138 +- .../harfbuzz/src/hb-ot-shaper-arabic-table.hh | 267 +- .../harfbuzz/src/hb-ot-shaper-arabic.cc | 47 +- .../harfbuzz/src/hb-ot-shaper-hangul.cc | 4 +- .../src/hb-ot-shaper-indic-machine.hh | 14 +- .../harfbuzz/src/hb-ot-shaper-indic-table.cc | 484 +- .../src/hb-ot-shaper-khmer-machine.hh | 14 +- .../src/hb-ot-shaper-myanmar-machine.hh | 14 +- thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc | 12 +- .../harfbuzz/src/hb-ot-shaper-use-machine.hh | 14 +- .../harfbuzz/src/hb-ot-shaper-use-table.hh | 1052 +- .../src/hb-ot-shaper-vowel-constraints.cc | 2 +- thirdparty/harfbuzz/src/hb-ot-tag-table.hh | 1278 ++- thirdparty/harfbuzz/src/hb-ot-tag.cc | 153 +- thirdparty/harfbuzz/src/hb-ot-var-common.hh | 48 +- .../harfbuzz/src/hb-ot-var-gvar-table.hh | 109 +- thirdparty/harfbuzz/src/hb-paint.cc | 12 +- thirdparty/harfbuzz/src/hb-paint.h | 37 +- thirdparty/harfbuzz/src/hb-raster-draw.cc | 1386 +++ thirdparty/harfbuzz/src/hb-raster-image.cc | 967 ++ thirdparty/harfbuzz/src/hb-raster-image.hh | 63 + thirdparty/harfbuzz/src/hb-raster-paint.cc | 2253 +++++ thirdparty/harfbuzz/src/hb-raster-paint.hh | 205 + thirdparty/harfbuzz/src/hb-raster-svg-base.cc | 501 + thirdparty/harfbuzz/src/hb-raster-svg-base.hh | 228 + thirdparty/harfbuzz/src/hb-raster-svg-bbox.cc | 109 + thirdparty/harfbuzz/src/hb-raster-svg-bbox.hh | 39 + thirdparty/harfbuzz/src/hb-raster-svg-clip.cc | 528 + thirdparty/harfbuzz/src/hb-raster-svg-clip.hh | 53 + .../harfbuzz/src/hb-raster-svg-color.cc | 340 + .../harfbuzz/src/hb-raster-svg-color.hh | 44 + .../harfbuzz/src/hb-raster-svg-context.hh | 113 + .../harfbuzz/src/hb-raster-svg-defs-scan.cc | 108 + .../harfbuzz/src/hb-raster-svg-defs-scan.hh | 58 + thirdparty/harfbuzz/src/hb-raster-svg-defs.cc | 121 + thirdparty/harfbuzz/src/hb-raster-svg-defs.hh | 108 + thirdparty/harfbuzz/src/hb-raster-svg-fill.cc | 300 + thirdparty/harfbuzz/src/hb-raster-svg-fill.hh | 41 + .../harfbuzz/src/hb-raster-svg-gradient.cc | 245 + .../harfbuzz/src/hb-raster-svg-gradient.hh | 46 + .../harfbuzz/src/hb-raster-svg-parse.cc | 668 ++ .../harfbuzz/src/hb-raster-svg-parse.hh | 368 + .../harfbuzz/src/hb-raster-svg-render.cc | 548 ++ thirdparty/harfbuzz/src/hb-raster-svg-use.cc | 104 + thirdparty/harfbuzz/src/hb-raster-svg-use.hh | 48 + thirdparty/harfbuzz/src/hb-raster-svg.hh | 68 + thirdparty/harfbuzz/src/hb-raster-utils.hh | 81 + thirdparty/harfbuzz/src/hb-raster.cc | 68 + thirdparty/harfbuzz/src/hb-raster.h | 321 + .../harfbuzz/src/hb-subset-cff-common.cc | 8 +- .../harfbuzz/src/hb-subset-cff-common.hh | 26 +- .../harfbuzz/src/hb-subset-cff2-to-cff1.cc | 40 + .../harfbuzz/src/hb-subset-cff2-to-cff1.hh | 177 + thirdparty/harfbuzz/src/hb-subset-cff2.cc | 596 +- .../src/hb-subset-instancer-solver.cc | 16 +- thirdparty/harfbuzz/src/hb-subset.h | 4 + thirdparty/harfbuzz/src/hb-ucd-table.hh | 8449 +++++++++-------- .../harfbuzz/src/hb-unicode-emoji-table.hh | 61 +- thirdparty/harfbuzz/src/hb-vector-svg-draw.cc | 740 ++ .../harfbuzz/src/hb-vector-svg-paint.cc | 1830 ++++ thirdparty/harfbuzz/src/hb-vector-svg-path.cc | 144 + thirdparty/harfbuzz/src/hb-vector-svg-path.hh | 43 + .../harfbuzz/src/hb-vector-svg-subset.cc | 454 + .../harfbuzz/src/hb-vector-svg-subset.hh | 44 + .../harfbuzz/src/hb-vector-svg-utils.cc | 92 + .../harfbuzz/src/hb-vector-svg-utils.hh | 129 + thirdparty/harfbuzz/src/hb-vector-svg.hh | 366 + thirdparty/harfbuzz/src/hb-vector.cc | 41 + thirdparty/harfbuzz/src/hb-vector.h | 285 + thirdparty/harfbuzz/src/hb-vector.hh | 48 +- thirdparty/harfbuzz/src/hb-version.h | 8 +- thirdparty/harfbuzz/src/hb-zlib.cc | 98 + thirdparty/harfbuzz/src/hb-zlib.hh | 37 + thirdparty/harfbuzz/src/hb.hh | 2 +- 134 files changed, 23901 insertions(+), 6546 deletions(-) create mode 100644 misc/extension_api_validation/4.6-stable/GH-117149.txt create mode 100644 thirdparty/harfbuzz/src/hb-cff-specializer.hh create mode 100644 thirdparty/harfbuzz/src/hb-cff-width-optimizer.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-draw.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-image.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-image.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-paint.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-paint.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-base.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-base.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-bbox.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-bbox.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-clip.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-clip.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-color.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-color.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-context.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-defs-scan.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-defs-scan.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-defs.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-defs.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-fill.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-fill.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-gradient.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-gradient.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-parse.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-parse.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-render.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-use.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg-use.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-svg.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster-utils.hh create mode 100644 thirdparty/harfbuzz/src/hb-raster.cc create mode 100644 thirdparty/harfbuzz/src/hb-raster.h create mode 100644 thirdparty/harfbuzz/src/hb-subset-cff2-to-cff1.cc create mode 100644 thirdparty/harfbuzz/src/hb-subset-cff2-to-cff1.hh create mode 100644 thirdparty/harfbuzz/src/hb-vector-svg-draw.cc create mode 100644 thirdparty/harfbuzz/src/hb-vector-svg-paint.cc create mode 100644 thirdparty/harfbuzz/src/hb-vector-svg-path.cc create mode 100644 thirdparty/harfbuzz/src/hb-vector-svg-path.hh create mode 100644 thirdparty/harfbuzz/src/hb-vector-svg-subset.cc create mode 100644 thirdparty/harfbuzz/src/hb-vector-svg-subset.hh create mode 100644 thirdparty/harfbuzz/src/hb-vector-svg-utils.cc create mode 100644 thirdparty/harfbuzz/src/hb-vector-svg-utils.hh create mode 100644 thirdparty/harfbuzz/src/hb-vector-svg.hh create mode 100644 thirdparty/harfbuzz/src/hb-vector.cc create mode 100644 thirdparty/harfbuzz/src/hb-vector.h create mode 100644 thirdparty/harfbuzz/src/hb-zlib.cc create mode 100644 thirdparty/harfbuzz/src/hb-zlib.hh diff --git a/doc/classes/Font.xml b/doc/classes/Font.xml index 67b811a7d7..5d43b90170 100644 --- a/doc/classes/Font.xml +++ b/doc/classes/Font.xml @@ -125,6 +125,8 @@ + + Returns [TextServer] RID of the font cache for specific variation. @@ -226,6 +228,26 @@ Returns [Dictionary] with OpenType font name strings (localized font names, version, description, license information, sample text, etc.). + + + + + Returns the array in the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. Colors can be overridden using [FontVariation]. + + + + + + Returns the number of predefined color palettes. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. + + + + + + + Returns the name of the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. + + diff --git a/doc/classes/FontVariation.xml b/doc/classes/FontVariation.xml index 08c2ec9a4f..4945cc2e92 100644 --- a/doc/classes/FontVariation.xml +++ b/doc/classes/FontVariation.xml @@ -52,6 +52,12 @@ A set of OpenType feature tags. More info: [url=https://docs.microsoft.com/en-us/typography/opentype/spec/featuretags]OpenType feature tags[/url]. + + An array of colors to override predefined palette. Use [code]Color(0, 0, 0, 0)[/code], to keep predefined palette color at specific position. + + + A palette index. + Extra spacing at the bottom of the line in pixels. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 4d66c19812..759bdfbe00 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -406,6 +406,36 @@ Returns oversampling factor override. If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only. + + + + + + Returns the array in the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. Colors can be overridden using [method font_set_palette_custom_colors]. + + + + + + + Returns the number of predefined color palettes. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. + + + + + + + Returns array of custom colors to override predefined palette. + + + + + + + + Returns the name of the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. + + @@ -542,6 +572,13 @@ Returns thickness of the underline in pixels. + + + + + Returns used palette index. + + @@ -882,7 +919,7 @@ - + If set to [code]true[/code], color modulation is applied when drawing colored glyphs, otherwise it's applied to the monochrome glyphs only. @@ -936,6 +973,14 @@ If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only. + + + + + + Sets array of custom colors to override predefined palette. Set to empty array to reset overrides. Use [code]Color(0, 0, 0, 0)[/code], to keep predefined palette color at specific position. + + @@ -1044,6 +1089,14 @@ Sets thickness of the underline in pixels. + + + + + + Sets used palette index. + + diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml index 92bc817027..cfc8313c47 100644 --- a/doc/classes/TextServerExtension.xml +++ b/doc/classes/TextServerExtension.xml @@ -384,6 +384,36 @@ Returns oversampling factor override. If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only. + + + + + + Returns the array in the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. Colors can be overridden using [method _font_set_palette_custom_colors]. + + + + + + + Returns the number of predefined color palettes. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. + + + + + + + Returns array of custom colors to override predefined palette. + + + + + + + + Returns the name of the predefined color palette at [param index]. Palette contains all colors used to render font glyphs. Each palette has the same number of colors. + + @@ -520,6 +550,13 @@ Returns thickness of the underline in pixels. + + + + + Returns used palette index. + + @@ -919,6 +956,14 @@ If set to a positive value, overrides the oversampling factor of the viewport this font is used in. See [member Viewport.oversampling]. This value doesn't override the [code skip-lint]oversampling[/code] parameter of [code skip-lint]draw_*[/code] methods. Used by dynamic fonts only. + + + + + + Sets array of custom colors to override predefined palette. Set to empty array to reset overrides. Use [code]Color(0, 0, 0, 0)[/code], to keep predefined palette color at specific position. + + @@ -1024,6 +1069,14 @@ Sets thickness of the underline in pixels. + + + + + + Sets used palette index. + + diff --git a/editor/inspector/editor_property_name_processor.cpp b/editor/inspector/editor_property_name_processor.cpp index 8c0d6b0624..270a23bd2c 100644 --- a/editor/inspector/editor_property_name_processor.cpp +++ b/editor/inspector/editor_property_name_processor.cpp @@ -167,6 +167,7 @@ EditorPropertyNameProcessor::EditorPropertyNameProcessor() { capitalize_string_remaps["bptc"] = "BPTC"; capitalize_string_remaps["bvh"] = "BVH"; capitalize_string_remaps["ca"] = "CA"; + capitalize_string_remaps["colr"] = "COLR"; capitalize_string_remaps["ccdik"] = "CCDIK"; capitalize_string_remaps["cd"] = "CD"; capitalize_string_remaps["cpu"] = "CPU"; diff --git a/misc/extension_api_validation/4.1-stable_4.2-stable/GH-80954.txt b/misc/extension_api_validation/4.1-stable_4.2-stable/GH-80954.txt index a18b428c07..3d80724457 100644 --- a/misc/extension_api_validation/4.1-stable_4.2-stable/GH-80954.txt +++ b/misc/extension_api_validation/4.1-stable_4.2-stable/GH-80954.txt @@ -1,5 +1,5 @@ GH-80954 -------- -Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 4 to 9. +Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 4 to 11. Added optional arguments. Compatibility method registered. diff --git a/misc/extension_api_validation/4.2-stable_4.3-stable/GH-87668.txt b/misc/extension_api_validation/4.2-stable_4.3-stable/GH-87668.txt index 282a83be35..aea01b1472 100644 --- a/misc/extension_api_validation/4.2-stable_4.3-stable/GH-87668.txt +++ b/misc/extension_api_validation/4.2-stable_4.3-stable/GH-87668.txt @@ -1,5 +1,5 @@ GH-87668 -------- -Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 8 to 9. +Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 8 to 11. Added optional "baseline_offset" argument. Compatibility method registered. diff --git a/misc/extension_api_validation/4.6-stable/GH-117149.txt b/misc/extension_api_validation/4.6-stable/GH-117149.txt new file mode 100644 index 0000000000..54b67b0ae6 --- /dev/null +++ b/misc/extension_api_validation/4.6-stable/GH-117149.txt @@ -0,0 +1,5 @@ +GH-117149 +--------- +Validate extension JSON: Error: Field 'classes/Font/methods/find_variation/arguments': size changed value in new API, from 9 to 11. + +Optional "palette_index" and "custom_colors" arguments added. Compatibility method registered. diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub index 2c1fed6cf7..1111612788 100644 --- a/modules/text_server_adv/SCsub +++ b/modules/text_server_adv/SCsub @@ -99,6 +99,7 @@ if env["builtin_harfbuzz"]: # "src/hb-subset-cff-common.cc", # "src/hb-subset-cff1.cc", # "src/hb-subset-cff2.cc", + # "src/hb-subset-cff2-to-cff1.cc", # "src/hb-subset-input.cc", # "src/hb-subset-instancer-iup.cc", # "src/hb-subset-instancer-solver.cc", @@ -109,9 +110,30 @@ if env["builtin_harfbuzz"]: # "src/hb-subset-table-layout.cc", # "src/hb-subset-table-other.cc", # "src/hb-subset-table-var.cc", + "src/hb-raster.cc", + "src/hb-raster-draw.cc", + "src/hb-raster-image.cc", + "src/hb-raster-paint.cc", + "src/hb-raster-svg-base.cc", + "src/hb-raster-svg-bbox.cc", + "src/hb-raster-svg-color.cc", + "src/hb-raster-svg-clip.cc", + "src/hb-raster-svg-defs.cc", + "src/hb-raster-svg-defs-scan.cc", + "src/hb-raster-svg-gradient.cc", + "src/hb-raster-svg-fill.cc", + "src/hb-raster-svg-render.cc", + "src/hb-raster-svg-parse.cc", + "src/hb-raster-svg-use.cc", + "src/hb-vector-svg-draw.cc", + "src/hb-vector-svg-paint.cc", + "src/hb-vector-svg-path.cc", + "src/hb-vector-svg-subset.cc", + "src/hb-vector-svg-utils.cc", "src/hb-subset.cc", "src/hb-ucd.cc", "src/hb-unicode.cc", + "src/hb-zlib.cc", # "src/hb-uniscribe.cc", "src/OT/Var/VARC/VARC.cc", ] @@ -128,7 +150,7 @@ if env["builtin_harfbuzz"]: env_harfbuzz.Prepend(CPPPATH=["#thirdparty/harfbuzz/src"]) - env_harfbuzz.Append(CPPDEFINES=["HAVE_ICU"]) + env_harfbuzz.Append(CPPDEFINES=["HAVE_ICU", "HAVE_ZLIB", "HAVE_PNG"]) if env["builtin_icu4c"]: env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"]) env_harfbuzz.Append( diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index e0a0cd53e8..366520d590 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -1085,6 +1085,72 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_msdf( #endif #ifdef MODULE_FREETYPE_ENABLED +#if HB_VERSION_ATLEAST(13, 0, 0) +_FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_hb_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, hb_raster_image_t *p_image, const hb_raster_extents_t &p_ext, const Vector2 &p_advance, bool p_bgra) const { + FontGlyph chr; + chr.advance = p_advance * p_data->scale; + chr.found = true; + + int w = p_ext.width; + int h = p_ext.height; + + if (w == 0 || h == 0 || p_image == nullptr) { + chr.texture_idx = -1; + chr.uv_rect = Rect2(); + chr.rect = Rect2(); + return chr; + } + + int color_size = p_bgra ? 4 : 2; + + int mw = w + p_rect_margin * 4; + int mh = h + p_rect_margin * 4; + + ERR_FAIL_COND_V(mw > 4096, FontGlyph()); + ERR_FAIL_COND_V(mh > 4096, FontGlyph()); + + Image::Format require_format = color_size == 4 ? Image::FORMAT_RGBA8 : Image::FORMAT_LA8; + + FontTexturePosition tex_pos = find_texture_pos_for_glyph(p_data, color_size, require_format, mw, mh, false); + ERR_FAIL_COND_V(tex_pos.index < 0, FontGlyph()); + + const uint8_t *img_src = hb_raster_image_get_buffer(p_image); + + // Fit character in char texture. + ShelfPackTexture &tex = p_data->textures.write[tex_pos.index]; + + { + uint8_t *wr = tex.image->ptrw(); + + for (int i = 0; i < h; i++) { + for (int j = 0; j < w; j++) { + int ofs = ((i + tex_pos.y + p_rect_margin * 2) * tex.texture_w + j + tex_pos.x + p_rect_margin * 2) * color_size; + ERR_FAIL_COND_V(ofs >= tex.image->get_data_size(), FontGlyph()); + if (p_bgra) { + int ofs_color = i * p_ext.stride + (j << 2); + wr[ofs + 2] = img_src[ofs_color + 0]; + wr[ofs + 1] = img_src[ofs_color + 1]; + wr[ofs + 0] = img_src[ofs_color + 2]; + wr[ofs + 3] = img_src[ofs_color + 3]; + } else { + wr[ofs + 0] = 255; // grayscale as 1 + wr[ofs + 1] = img_src[i * p_ext.stride + j]; + } + } + } + } + + tex.dirty = true; + + chr.texture_idx = tex_pos.index; + + chr.uv_rect = Rect2(tex_pos.x + p_rect_margin, tex_pos.y + p_rect_margin, w + p_rect_margin * 2, h + p_rect_margin * 2); + chr.rect.position = Vector2(p_ext.x_origin - p_rect_margin, p_ext.y_origin - p_rect_margin) * p_data->scale; + chr.rect.size = chr.uv_rect.size * p_data->scale; + return chr; +} +#endif + _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const { FontGlyph chr; chr.advance = p_advance * p_data->scale; @@ -1093,7 +1159,7 @@ _FORCE_INLINE_ TextServerAdvanced::FontGlyph TextServerAdvanced::rasterize_bitma int w = p_bitmap.width; int h = p_bitmap.rows; - if (w == 0 || h == 0) { + if (w == 0 || h == 0 || p_bitmap.buffer == nullptr) { chr.texture_idx = -1; chr.uv_rect = Rect2(); chr.rect = Rect2(); @@ -1335,21 +1401,85 @@ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i } FT_GlyphSlot slot = p_font_data->face->glyph; - bool from_svg = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap. + bool fix_edge = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap. + bool from_bitmap = (slot->format == FT_GLYPH_FORMAT_BITMAP); if (!outline) { - if (!p_font_data->msdf) { - error = FT_Render_Glyph(slot, aa_mode); - } - if (!error) { - if (p_font_data->msdf) { + if (p_font_data->msdf) { #ifdef MODULE_MSDFGEN_ENABLED - gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); + gl = rasterize_msdf(p_font_data, fd, p_font_data->msdf_range, rect_range, &slot->outline, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0); #else - fd->glyph_map[p_glyph] = FontGlyph(); - ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!"); + fd->glyph_map[p_glyph] = FontGlyph(); + ERR_FAIL_V_MSG(false, "Compiled without MSDFGEN support!"); #endif + } else { +#if HB_VERSION_ATLEAST(13, 0, 0) + if (hb_rdr && hb_mono && p_font_data->antialiasing == FONT_ANTIALIASING_GRAY && !from_bitmap) { + bool is_rasterized = false; + float xshift = 0.0; + if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_QUARTER) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_QUARTER_MAX_SIZE * 64)) { + xshift = float((int)((p_glyph >> 27) & 3) << 4); + } else if ((p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_ONE_HALF) || (p_font_data->subpixel_positioning == SUBPIXEL_POSITIONING_AUTO && p_size.x <= SUBPIXEL_POSITIONING_ONE_HALF_MAX_SIZE * 64)) { + xshift = float((int)((p_glyph >> 27) & 3) << 5); + } + xshift += (p_font_data->embolden * double(p_size.x) / 64.0); + fix_edge = false; + if (fd->color_paint) { + hb_raster_paint_reset(hb_rdr); + if (p_font_data->transform != Transform2D()) { + hb_raster_paint_set_transform(hb_rdr, p_font_data->transform[0][0], p_font_data->transform[1][0], p_font_data->transform[0][1], -p_font_data->transform[1][1], 0.f, 0.f); + } else { + hb_raster_paint_set_transform(hb_rdr, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f); + } + hb_raster_paint_set_scale_factor(hb_rdr, 64.0, 64.0); + hb_raster_paint_clear_custom_palette_colors(hb_rdr); + if (!p_font_data->palette_custom_colors_hb.is_empty()) { + for (int col = 0; col < p_font_data->palette_custom_colors_hb.size(); col++) { + if (p_font_data->palette_custom_colors_hb[col] != 0) { + hb_raster_paint_set_custom_palette_color(hb_rdr, col, p_font_data->palette_custom_colors_hb[col]); + } + } + } + bool ok = hb_raster_paint_glyph(hb_rdr, fd->hb_handle, (hb_codepoint_t)glyph_index, xshift, 0, p_font_data->palette_index, (hb_color_t)0xFFFFFFFF); + if (ok) { + is_rasterized = true; + hb_raster_image_t *img = hb_raster_paint_render(hb_rdr); + hb_raster_extents_t ext = { 0, 0, 0, 0, 0 }; + if (img) { + hb_raster_image_get_extents(img, &ext); + gl = rasterize_hb_bitmap(fd, rect_range, img, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, true); + hb_raster_paint_recycle_image(hb_rdr, img); + } else { + gl = rasterize_hb_bitmap(fd, rect_range, nullptr, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, true); + } + } + } + if (!is_rasterized) { + hb_raster_draw_reset(hb_mono); + if (p_font_data->transform != Transform2D()) { + hb_raster_draw_set_transform(hb_mono, p_font_data->transform[0][0], p_font_data->transform[1][0], p_font_data->transform[0][1], -p_font_data->transform[1][1], 0.f, 0.f); + } else { + hb_raster_draw_set_transform(hb_mono, 1.f, 0.f, 0.f, -1.f, 0.f, 0.f); + } + hb_raster_draw_set_scale_factor(hb_mono, 64.0, 64.0); + hb_raster_draw_glyph(hb_mono, fd->hb_handle, (hb_codepoint_t)glyph_index, xshift, 0); + hb_raster_image_t *img = hb_raster_draw_render(hb_mono); + hb_raster_extents_t ext = { 0, 0, 0, 0, 0 }; + if (img) { + hb_raster_image_get_extents(img, &ext); + gl = rasterize_hb_bitmap(fd, rect_range, img, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, false); + hb_raster_draw_recycle_image(hb_mono, img); + } else { + gl = rasterize_hb_bitmap(fd, rect_range, nullptr, ext, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, false); + } + } } else { - gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra); +#else + { +#endif + error = FT_Render_Glyph(slot, aa_mode); + if (!error) { + gl = rasterize_bitmap(fd, rect_range, slot->bitmap, slot->bitmap_top, slot->bitmap_left, Vector2((h + (1 << 9)) >> 10, (v + (1 << 9)) >> 10) / 64.0, bgra); + } } } } else { @@ -1380,7 +1510,7 @@ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data, const Vector2i cleanup_stroker: FT_Stroker_Done(stroker); } - gl.from_svg = from_svg; + gl.fix_edge = fix_edge; E = fd->glyph_map.insert(p_glyph, gl); r_glyph = E->value; return gl.found; @@ -1501,6 +1631,10 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const fd->hb_handle = hb_ft_font_create(p_font_data->face, nullptr); + if (p_font_data->embolden != 0.0) { + hb_font_set_synthetic_bold(fd->hb_handle, p_font_data->embolden / 16.0, p_font_data->embolden / 16.0, true); + } + fd->ascent = (p_font_data->face->size->metrics.ascender / 64.0) * fd->scale; fd->descent = (-p_font_data->face->size->metrics.descender / 64.0) * fd->scale; fd->underline_position = (-FT_MulFix(p_font_data->face->underline_position, p_font_data->face->size->metrics.y_scale) / 64.0) * fd->scale; @@ -1512,16 +1646,56 @@ bool TextServerAdvanced::_ensure_cache_for_size(FontAdvanced *p_font_data, const #ifndef _MSC_VER #warning Building with HarfBuzz < 3.3.0, synthetic slant offset correction disabled. #endif +#endif +#if HB_VERSION_ATLEAST(13, 0, 0) + fd->hb_face = hb_font_get_face(fd->hb_handle); + fd->color_paint = (hb_ot_color_has_paint(fd->hb_face) || hb_ot_color_has_layers(fd->hb_face)); #endif if (!p_font_data->face_init) { + const static hb_language_t english = hb_language_from_string("en", -1); +#if HB_VERSION_ATLEAST(13, 0, 0) + hb_face_t *hb_face = fd->hb_face; + + p_font_data->palette_names.clear(); + p_font_data->palette_colors.clear(); + + unsigned int pal_count = hb_ot_color_palette_get_count(hb_face); + for (unsigned int pal = 0; pal < pal_count; pal++) { + String pal_name; + Vector pal_colors; + // Get name. + hb_ot_name_id_t name_id = hb_ot_color_palette_get_name_id(hb_face, pal); + if (name_id == HB_OT_NAME_ID_INVALID) { + pal_name = vformat("Palette %d", pal + 1); + } else { + unsigned int text_size = hb_ot_name_get_utf32(hb_face, name_id, english, nullptr, nullptr) + 1; + pal_name.resize_uninitialized(text_size); + hb_ot_name_get_utf32(hb_face, name_id, english, &text_size, (uint32_t *)pal_name.ptrw()); + } + // Get colors. + unsigned int color_count = 0; + color_count = hb_ot_color_palette_get_colors(hb_face, pal, 0, &color_count, nullptr); + Vector pal_colors_hb; + pal_colors_hb.resize_uninitialized(color_count); + hb_ot_color_palette_get_colors(hb_face, pal, 0, &color_count, pal_colors_hb.ptrw()); + for (unsigned int col = 0; col < color_count; col++) { + Color c = Color::from_rgba8(hb_color_get_red(pal_colors_hb[col]), hb_color_get_green(pal_colors_hb[col]), hb_color_get_blue(pal_colors_hb[col]), hb_color_get_alpha(pal_colors_hb[col])); + pal_colors.push_back(c); + } + + p_font_data->palette_names.push_back(pal_name); + p_font_data->palette_colors.push_back(pal_colors); + } +#else + hb_face_t *hb_face = hb_font_get_face(fd->hb_handle); +#endif // When a font does not provide a `family_name`, FreeType tries to synthesize one based on other names. // FreeType automatically converts non-ASCII characters to "?" in the synthesized name. // To avoid that behavior, use the format-specific name directly if available. - hb_face_t *hb_face = hb_font_get_face(fd->hb_handle); + unsigned int num_entries = 0; const hb_ot_name_entry_t *names = hb_ot_name_list_names(hb_face, &num_entries); - const hb_language_t english = hb_language_from_string("en", -1); for (unsigned int i = 0; i < num_entries; i++) { if (names[i].name_id != HB_OT_NAME_ID_FONT_FAMILY) { continue; @@ -2603,6 +2777,90 @@ bool TextServerAdvanced::_font_is_modulate_color_glyphs(const RID &p_font_rid) c return fd->modulate_color_glyphs; } +int64_t TextServerAdvanced::_font_get_palette_count(const RID &p_font_rid) const { + FontAdvanced *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, 0); + +#if HB_VERSION_ATLEAST(13, 0, 0) + return fd->palette_names.size(); +#else + return 0; +#endif +} + +String TextServerAdvanced::_font_get_palette_name(const RID &p_font_rid, int64_t p_index) const { + FontAdvanced *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, String()); + +#if HB_VERSION_ATLEAST(13, 0, 0) + ERR_FAIL_INDEX_V(p_index, fd->palette_names.size(), String()); + return fd->palette_names[p_index]; +#else + return String(); +#endif +} + +Vector TextServerAdvanced::_font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const { + FontAdvanced *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, Vector()); + +#if HB_VERSION_ATLEAST(13, 0, 0) + ERR_FAIL_INDEX_V(p_index, fd->palette_names.size(), Vector()); + return fd->palette_colors[p_index]; +#else + return Vector(); +#endif +} + +void TextServerAdvanced::_font_set_palette_custom_colors(const RID &p_font_rid, const Vector &p_colors) { + FontAdvanced *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL(fd); + +#if HB_VERSION_ATLEAST(13, 0, 0) + if (fd->palette_custom_colors != p_colors) { + fd->palette_custom_colors = p_colors; + fd->palette_custom_colors_hb.resize_uninitialized(p_colors.size()); + for (int col = 0; col < fd->palette_custom_colors.size(); col++) { + const Color &c = fd->palette_custom_colors[col]; + fd->palette_custom_colors_hb.write[col] = HB_COLOR(c.get_b8(), c.get_g8(), c.get_r8(), c.get_a8()); + } + _font_clear_cache(fd); + } +#endif +} + +Vector TextServerAdvanced::_font_get_palette_custom_colors(const RID &p_font_rid) const { + FontAdvanced *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, Vector()); + +#if HB_VERSION_ATLEAST(13, 0, 0) + return fd->palette_custom_colors; +#else + return Vector(); +#endif +} + +int64_t TextServerAdvanced::_font_get_used_palette(const RID &p_font_rid) const { + FontAdvanced *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, 0); +#if HB_VERSION_ATLEAST(13, 0, 0) + return fd->palette_index; +#else + return 0; +#endif +} + +void TextServerAdvanced::_font_set_used_palette(const RID &p_font_rid, int64_t p_index) { + FontAdvanced *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL(fd); +#if HB_VERSION_ATLEAST(13, 0, 0) + if (fd->palette_index != p_index) { + fd->palette_index = p_index; + _font_clear_cache(fd); + } +#endif +} + void TextServerAdvanced::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) { FontAdvanced *fd = _get_font_data(p_font_rid); ERR_FAIL_NULL(fd); @@ -3508,7 +3766,7 @@ RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const if (ffsd->textures[fgl.texture_idx].dirty) { ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref img = tex.image; - if (fgl.from_svg) { + if (fgl.fix_edge) { // Same as the "fix alpha border" process option when importing SVGs img->fix_alpha_edges(); } @@ -3558,7 +3816,7 @@ Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, co if (ffsd->textures[fgl.texture_idx].dirty) { ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref img = tex.image; - if (fgl.from_svg) { + if (fgl.fix_edge) { // Same as the "fix alpha border" process option when importing SVGs img->fix_alpha_edges(); } @@ -4034,7 +4292,7 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca if (ffsd->textures[fgl.texture_idx].dirty) { ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx]; Ref img = tex.image; - if (fgl.from_svg) { + if (fgl.fix_edge) { // Same as the "fix alpha border" process option when importing SVGs img->fix_alpha_edges(); } @@ -8119,7 +8377,10 @@ void TextServerAdvanced::_update_settings() { TextServerAdvanced::TextServerAdvanced() { os_locale = OS::get_singleton()->get_locale(); - +#if HB_VERSION_ATLEAST(13, 0, 0) + hb_rdr = hb_raster_paint_create_or_fail(); + hb_mono = hb_raster_draw_create_or_fail(); +#endif _insert_feature_sets(); _bmp_create_font_funcs(); _update_settings(); @@ -8144,6 +8405,14 @@ void TextServerAdvanced::_cleanup() { TextServerAdvanced::~TextServerAdvanced() { _bmp_free_font_funcs(); +#if HB_VERSION_ATLEAST(13, 0, 0) + if (hb_rdr != nullptr) { + hb_raster_paint_destroy(hb_rdr); + } + if (hb_mono != nullptr) { + hb_raster_draw_destroy(hb_mono); + } +#endif #ifdef MODULE_FREETYPE_ENABLED if (ft_library != nullptr) { FT_Done_FreeType(ft_library); diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 564bcbe47a..30b82b2cfb 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -91,6 +91,9 @@ GODOT_CLANG_WARNING_POP #include #include +#if HB_VERSION_ATLEAST(13, 0, 0) +#include +#endif /*************************************************************************/ @@ -228,7 +231,7 @@ class TextServerAdvanced : public TextServerExtension { Rect2 rect; Rect2 uv_rect; Vector2 advance; - bool from_svg = false; + bool fix_edge = false; }; struct FontAdvanced; @@ -249,6 +252,10 @@ class TextServerAdvanced : public TextServerExtension { HashMap glyph_map; HashMap kerning_map; hb_font_t *hb_handle = nullptr; +#if HB_VERSION_ATLEAST(13, 0, 0) + hb_face_t *hb_face = nullptr; + bool color_paint = false; +#endif #ifdef MODULE_FREETYPE_ENABLED FT_Size fsize = nullptr; @@ -311,6 +318,14 @@ class TextServerAdvanced : public TextServerExtension { HashMap cache; +#if HB_VERSION_ATLEAST(13, 0, 0) + Vector palette_names; + Vector palette_colors; + PackedColorArray palette_custom_colors; + Vector palette_custom_colors_hb; + unsigned int palette_index = 0; +#endif + bool face_init = false; HashSet supported_scripts; Dictionary supported_features; @@ -350,6 +365,9 @@ class TextServerAdvanced : public TextServerExtension { #endif #ifdef MODULE_FREETYPE_ENABLED _FORCE_INLINE_ FontGlyph rasterize_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, FT_Bitmap p_bitmap, int p_yofs, int p_xofs, const Vector2 &p_advance, bool p_bgra) const; +#if HB_VERSION_ATLEAST(13, 0, 0) + _FORCE_INLINE_ FontGlyph rasterize_hb_bitmap(FontForSizeAdvanced *p_data, int p_rect_margin, hb_raster_image_t *p_image, const hb_raster_extents_t &p_ext, const Vector2 &p_advance, bool p_bgra) const; +#endif #endif bool _ensure_glyph(FontAdvanced *p_font_data, const Vector2i &p_size, int32_t p_glyph, FontGlyph &r_glyph, uint32_t p_oversampling = 0) const; bool _ensure_cache_for_size(FontAdvanced *p_font_data, const Vector2i &p_size, FontForSizeAdvanced *&r_cache_for_size, bool p_silent = false, uint32_t p_oversampling = 0) const; @@ -555,6 +573,11 @@ class TextServerAdvanced : public TextServerExtension { mutable RID_PtrOwner font_owner; mutable RID_PtrOwner shaped_owner; +#if HB_VERSION_ATLEAST(13, 0, 0) + hb_raster_paint_t *hb_rdr = nullptr; + hb_raster_draw_t *hb_mono = nullptr; +#endif + _FORCE_INLINE_ FontAdvanced *_get_font_data(const RID &p_font_rid) const { RID rid = p_font_rid; FontAdvancedLinkedVariation *fdv = font_var_owner.get_or_null(rid); @@ -870,6 +893,14 @@ public: MODBIND2(font_set_modulate_color_glyphs, const RID &, bool); MODBIND1RC(bool, font_is_modulate_color_glyphs, const RID &); + MODBIND1RC(int64_t, font_get_palette_count, const RID &); + MODBIND2RC(String, font_get_palette_name, const RID &, int64_t); + MODBIND2RC(Vector, font_get_palette_colors, const RID &, int64_t); + MODBIND2(font_set_palette_custom_colors, const RID &, const Vector &); + MODBIND1RC(Vector, font_get_palette_custom_colors, const RID &); + MODBIND1RC(int64_t, font_get_used_palette, const RID &); + MODBIND2(font_set_used_palette, const RID &, int64_t); + MODBIND2(font_set_subpixel_positioning, const RID &, SubpixelPositioning); MODBIND1RC(SubpixelPositioning, font_get_subpixel_positioning, const RID &); diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 2209ddb954..447fc62783 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -1549,6 +1549,51 @@ bool TextServerFallback::_font_is_modulate_color_glyphs(const RID &p_font_rid) c return fd->modulate_color_glyphs; } +int64_t TextServerFallback::_font_get_palette_count(const RID &p_font_rid) const { + FontFallback *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, 0); + + return 0; +} + +String TextServerFallback::_font_get_palette_name(const RID &p_font_rid, int64_t p_index) const { + FontFallback *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, String()); + + return String(); +} + +Vector TextServerFallback::_font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const { + FontFallback *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, Vector()); + + return Vector(); +} + +void TextServerFallback::_font_set_palette_custom_colors(const RID &p_font_rid, const Vector &p_colors) { + FontFallback *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL(fd); +} + +Vector TextServerFallback::_font_get_palette_custom_colors(const RID &p_font_rid) const { + FontFallback *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, Vector()); + + return Vector(); +} + +int64_t TextServerFallback::_font_get_used_palette(const RID &p_font_rid) const { + FontFallback *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL_V(fd, 0); + + return 0; +} + +void TextServerFallback::_font_set_used_palette(const RID &p_font_rid, int64_t p_index) { + FontFallback *fd = _get_font_data(p_font_rid); + ERR_FAIL_NULL(fd); +} + void TextServerFallback::_font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) { FontFallback *fd = _get_font_data(p_font_rid); ERR_FAIL_NULL(fd); diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 88731e1981..f3310a35f6 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -649,6 +649,14 @@ public: MODBIND2(font_set_modulate_color_glyphs, const RID &, bool); MODBIND1RC(bool, font_is_modulate_color_glyphs, const RID &); + MODBIND1RC(int64_t, font_get_palette_count, const RID &); + MODBIND2RC(String, font_get_palette_name, const RID &, int64_t); + MODBIND2RC(Vector, font_get_palette_colors, const RID &, int64_t); + MODBIND2(font_set_palette_custom_colors, const RID &, const Vector &); + MODBIND1RC(Vector, font_get_palette_custom_colors, const RID &); + MODBIND1RC(int64_t, font_get_used_palette, const RID &); + MODBIND2(font_set_used_palette, const RID &, int64_t); + MODBIND2(font_set_subpixel_positioning, const RID &, SubpixelPositioning); MODBIND1RC(SubpixelPositioning, font_get_subpixel_positioning, const RID &); diff --git a/scene/resources/font.compat.inc b/scene/resources/font.compat.inc index 45bda9aff7..daac8b3f5c 100644 --- a/scene/resources/font.compat.inc +++ b/scene/resources/font.compat.inc @@ -59,11 +59,15 @@ real_t Font::_draw_char_outline_bind_compat_104872(RID p_canvas_item, const Poin } RID Font::_find_variation_bind_compat_80954(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform) const { - return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, 0, 0, 0, 0, 0.0); + return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, 0, 0, 0, 0, 0.0, 0, Vector()); } RID Font::_find_variation_bind_compat_87668(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph) const { - return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, 0.0); + return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, 0.0, 0, Vector()); +} + +RID Font::_find_variation_bind_compat_117149(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const { + return find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, 0, Vector()); } void Font::_bind_compatibility_methods() { @@ -78,6 +82,7 @@ void Font::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform"), &Font::_find_variation_bind_compat_80954, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D())); ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph"), &Font::_find_variation_bind_compat_87668, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0)); + ClassDB::bind_compatibility_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph", "baseline_offset"), &Font::_find_variation_bind_compat_117149, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0.0)); } #endif // DISABLE_DEPRECATED diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index 4abfe35596..11535d9e21 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -53,7 +53,7 @@ void Font::_bind_methods() { ClassDB::bind_method(D_METHOD("get_fallbacks"), &Font::get_fallbacks); // Output. - ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph", "baseline_offset"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0.0)); + ClassDB::bind_method(D_METHOD("find_variation", "variation_coordinates", "face_index", "strength", "transform", "spacing_top", "spacing_bottom", "spacing_space", "spacing_glyph", "baseline_offset", "palette_index", "custom_colors"), &Font::find_variation, DEFVAL(0), DEFVAL(0.0), DEFVAL(Transform2D()), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0), DEFVAL(0.0), DEFVAL(0), DEFVAL(Vector())); ClassDB::bind_method(D_METHOD("get_rids"), &Font::get_rids); // Font metrics. @@ -70,6 +70,10 @@ void Font::_bind_methods() { ClassDB::bind_method(D_METHOD("get_font_weight"), &Font::get_font_weight); ClassDB::bind_method(D_METHOD("get_font_stretch"), &Font::get_font_stretch); + ClassDB::bind_method(D_METHOD("get_palette_count"), &Font::get_palette_count); + ClassDB::bind_method(D_METHOD("get_palette_name", "index"), &Font::get_palette_name); + ClassDB::bind_method(D_METHOD("get_palette_colors", "index"), &Font::get_palette_colors); + ClassDB::bind_method(D_METHOD("get_spacing", "spacing"), &Font::get_spacing); ClassDB::bind_method(D_METHOD("get_opentype_features"), &Font::get_opentype_features); @@ -270,6 +274,18 @@ String Font::get_font_name() const { return TS->font_get_name(_get_rid()); } +int64_t Font::get_palette_count() const { + return TS->font_get_palette_count(_get_rid()); +} + +String Font::get_palette_name(int64_t p_index) const { + return TS->font_get_palette_name(_get_rid(), p_index); +} + +Vector Font::get_palette_colors(int64_t p_index) const { + return TS->font_get_palette_colors(_get_rid(), p_index); +} + Dictionary Font::get_ot_name_strings() const { return TS->font_get_ot_name_strings(_get_rid()); } @@ -2366,7 +2382,7 @@ real_t FontFile::get_oversampling() const { return oversampling_override; } -RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const { +RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset, int64_t p_palette_index, const Vector &p_custom_colors) const { // Find existing variation cache. const Dictionary &supported_coords = get_supported_variation_list(); int make_linked_from = -1; @@ -2378,6 +2394,8 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa match = match && (TS->font_get_face_index(cache[i]) == p_face_index); match = match && (TS->font_get_embolden(cache[i]) == p_strength); match = match && (TS->font_get_transform(cache[i]) == p_transform); + match = match && (TS->font_get_used_palette(cache[i]) == p_palette_index); + match = match && (TS->font_get_palette_custom_colors(cache[i]) == p_custom_colors); match_linked = match_linked && (TS->font_get_spacing(cache[i], TextServer::SPACING_TOP) == p_spacing_top); match_linked = match_linked && (TS->font_get_spacing(cache[i], TextServer::SPACING_BOTTOM) == p_spacing_bottom); match_linked = match_linked && (TS->font_get_spacing(cache[i], TextServer::SPACING_SPACE) == p_spacing_space); @@ -2427,6 +2445,8 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa TS->font_set_spacing(cache[idx], TextServer::SPACING_SPACE, p_spacing_space); TS->font_set_spacing(cache[idx], TextServer::SPACING_GLYPH, p_spacing_glyph); TS->font_set_baseline_offset(cache[idx], p_baseline_offset); + TS->font_set_used_palette(cache[idx], p_palette_index); + TS->font_set_palette_custom_colors(cache[idx], p_custom_colors); } else { _ensure_rid(idx); TS->font_set_variation_coordinates(cache[idx], p_variation_coordinates); @@ -2438,6 +2458,8 @@ RID FontFile::find_variation(const Dictionary &p_variation_coordinates, int p_fa TS->font_set_spacing(cache[idx], TextServer::SPACING_SPACE, p_spacing_space); TS->font_set_spacing(cache[idx], TextServer::SPACING_GLYPH, p_spacing_glyph); TS->font_set_baseline_offset(cache[idx], p_baseline_offset); + TS->font_set_used_palette(cache[idx], p_palette_index); + TS->font_set_palette_custom_colors(cache[idx], p_custom_colors); } return cache[idx]; } @@ -2905,6 +2927,12 @@ void FontVariation::_bind_methods() { ClassDB::bind_method(D_METHOD("set_baseline_offset", "baseline_offset"), &FontVariation::set_baseline_offset); ClassDB::bind_method(D_METHOD("get_baseline_offset"), &FontVariation::get_baseline_offset); + ClassDB::bind_method(D_METHOD("get_palette_index"), &FontVariation::get_palette_index); + ClassDB::bind_method(D_METHOD("set_palette_index", "palette_index"), &FontVariation::set_palette_index); + + ClassDB::bind_method(D_METHOD("get_palette_custom_colors"), &FontVariation::get_palette_custom_colors); + ClassDB::bind_method(D_METHOD("set_palette_custom_colors", "colors"), &FontVariation::set_palette_custom_colors); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "base_font", PROPERTY_HINT_RESOURCE_TYPE, Font::get_class_static()), "set_base_font", "get_base_font"); ADD_GROUP("Variation", "variation_"); @@ -2924,6 +2952,46 @@ void FontVariation::_bind_methods() { ADD_GROUP("Baseline", "baseline_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "baseline_offset", PROPERTY_HINT_RANGE, "-2,2,0.005"), "set_baseline_offset", "get_baseline_offset"); + + ADD_GROUP("Palette", "palette_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "palette_index", PROPERTY_HINT_NONE, ""), "set_palette_index", "get_palette_index"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "palette_custom_colors", PROPERTY_HINT_NONE, ""), "set_palette_custom_colors", "get_palette_custom_colors"); +} + +void FontVariation::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "palette_custom_colors") { + Ref f = _get_base_font_or_default(); + if (f.is_valid()) { + int64_t pal_count = f->get_palette_count(); + if (pal_count == 0 || f->get_palette_colors(0).is_empty()) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } else { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } else if (p_property.name == "palette_index") { + Ref f = _get_base_font_or_default(); + if (f.is_valid()) { + p_property.hint = PROPERTY_HINT_ENUM; + p_property.hint_string = String(); + + int64_t pal_count = f->get_palette_count(); + if (pal_count == 0) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } else { + for (int64_t i = 0; i < pal_count; i++) { + if (!p_property.hint_string.is_empty()) { + p_property.hint_string += ","; + } + p_property.hint_string += f->get_palette_name(i); + } + } + } else { + p_property.hint = PROPERTY_HINT_NONE; + p_property.hint_string = String(); + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } } void FontVariation::_update_rids() const { @@ -2965,6 +3033,8 @@ void FontVariation::reset_state() { extra_spacing[i] = 0; } baseline_offset = 0.0; + palette_index = 0; + custom_colors.clear(); Font::reset_state(); } @@ -3120,10 +3190,32 @@ float FontVariation::get_baseline_offset() const { return baseline_offset; } -RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const { +void FontVariation::set_palette_index(int64_t p_palette_index) { + if (palette_index != p_palette_index) { + palette_index = p_palette_index; + _invalidate_rids(); + } +} + +int64_t FontVariation::get_palette_index() const { + return palette_index; +} + +void FontVariation::set_palette_custom_colors(const Vector &p_colors) { + if (custom_colors != p_colors) { + custom_colors = p_colors; + _invalidate_rids(); + } +} + +Vector FontVariation::get_palette_custom_colors() const { + return custom_colors; +} + +RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset, int64_t p_palette_index, const Vector &p_custom_colors) const { Ref f = _get_base_font_or_default(); if (f.is_valid()) { - return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset); + return f->find_variation(p_variation_coordinates, p_face_index, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, p_palette_index, p_custom_colors); } return RID(); } @@ -3131,7 +3223,7 @@ RID FontVariation::find_variation(const Dictionary &p_variation_coordinates, int RID FontVariation::_get_rid() const { Ref f = _get_base_font_or_default(); if (f.is_valid()) { - return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform, extra_spacing[TextServer::SPACING_TOP], extra_spacing[TextServer::SPACING_BOTTOM], extra_spacing[TextServer::SPACING_SPACE], extra_spacing[TextServer::SPACING_GLYPH], baseline_offset); + return f->find_variation(variation.opentype, variation.face_index, variation.embolden, variation.transform, extra_spacing[TextServer::SPACING_TOP], extra_spacing[TextServer::SPACING_BOTTOM], extra_spacing[TextServer::SPACING_SPACE], extra_spacing[TextServer::SPACING_GLYPH], baseline_offset, palette_index, custom_colors); } return RID(); } @@ -3651,7 +3743,7 @@ int SystemFont::get_spacing(TextServer::SpacingType p_spacing) const { } } -RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset) const { +RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_face_index, float p_strength, Transform2D p_transform, int p_spacing_top, int p_spacing_bottom, int p_spacing_space, int p_spacing_glyph, float p_baseline_offset, int64_t p_palette_index, const Vector &p_custom_colors) const { Ref f = _get_base_font_or_default(); if (f.is_valid()) { Dictionary var = p_variation_coordinates; @@ -3667,9 +3759,9 @@ RID SystemFont::find_variation(const Dictionary &p_variation_coordinates, int p_ if (!face_indices.is_empty()) { int face_index = CLAMP(p_face_index, 0, face_indices.size() - 1); - return f->find_variation(var, face_indices[face_index], p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset); + return f->find_variation(var, face_indices[face_index], p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, p_palette_index, p_custom_colors); } else { - return f->find_variation(var, 0, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset); + return f->find_variation(var, 0, p_strength, p_transform, p_spacing_top, p_spacing_bottom, p_spacing_space, p_spacing_glyph, p_baseline_offset, p_palette_index, p_custom_colors); } } return RID(); diff --git a/scene/resources/font.h b/scene/resources/font.h index 02252210f1..996e6922fc 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -108,6 +108,7 @@ protected: real_t _draw_char_outline_bind_compat_104872(RID p_canvas_item, const Point2 &p_pos, char32_t p_char, int p_font_size = DEFAULT_FONT_SIZE, int p_size = 1, const Color &p_modulate = Color(1.0, 1.0, 1.0)) const; RID _find_variation_bind_compat_80954(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D()) const; RID _find_variation_bind_compat_87668(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0) const; + RID _find_variation_bind_compat_117149(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const; static void _bind_compatibility_methods(); #endif @@ -123,7 +124,7 @@ public: virtual TypedArray get_fallbacks() const; // Output. - virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const { return RID(); } + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector &p_custom_colors = Vector()) const { return RID(); } virtual RID _get_rid() const { return RID(); } virtual TypedArray get_rids() const; @@ -141,6 +142,10 @@ public: virtual int get_font_weight() const; virtual int get_font_stretch() const; + virtual int64_t get_palette_count() const; + virtual String get_palette_name(int64_t p_index) const; + virtual Vector get_palette_colors(int64_t p_index) const; + virtual int get_spacing(TextServer::SpacingType p_spacing) const { return 0; } virtual Dictionary get_opentype_features() const; @@ -300,7 +305,7 @@ public: virtual real_t get_oversampling() const; // Cache. - virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const override; + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector &p_custom_colors = Vector()) const override; virtual RID _get_rid() const override; virtual int get_cache_count() const; @@ -427,9 +432,12 @@ class FontVariation : public Font { Dictionary opentype_features; int extra_spacing[TextServer::SPACING_MAX]; float baseline_offset = 0.0; + int64_t palette_index = 0; + Vector custom_colors; protected: static void _bind_methods(); + void _validate_property(PropertyInfo &p_property) const; virtual void _update_rids() const override; @@ -461,8 +469,14 @@ public: virtual float get_baseline_offset() const; virtual void set_baseline_offset(float p_baseline_offset); + virtual int64_t get_palette_index() const; + virtual void set_palette_index(int64_t p_palette_index); + + virtual Vector get_palette_custom_colors() const; + virtual void set_palette_custom_colors(const Vector &p_colors); + // Output. - virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const override; + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector &p_custom_colors = Vector()) const override; virtual RID _get_rid() const override; FontVariation(); @@ -568,7 +582,7 @@ public: virtual int get_spacing(TextServer::SpacingType p_spacing) const override; - virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0) const override; + virtual RID find_variation(const Dictionary &p_variation_coordinates, int p_face_index = 0, float p_strength = 0.0, Transform2D p_transform = Transform2D(), int p_spacing_top = 0, int p_spacing_bottom = 0, int p_spacing_space = 0, int p_spacing_glyph = 0, float p_baseline_offset = 0.0, int64_t p_palette_index = 0, const Vector &p_custom_colors = Vector()) const override; virtual RID _get_rid() const override; int64_t get_face_count() const override; diff --git a/servers/text/text_server.cpp b/servers/text/text_server.cpp index d66f7fbc41..12a408b7c2 100644 --- a/servers/text/text_server.cpp +++ b/servers/text/text_server.cpp @@ -276,9 +276,17 @@ void TextServer::_bind_methods() { ClassDB::bind_method(D_METHOD("font_set_force_autohinter", "font_rid", "force_autohinter"), &TextServer::font_set_force_autohinter); ClassDB::bind_method(D_METHOD("font_is_force_autohinter", "font_rid"), &TextServer::font_is_force_autohinter); - ClassDB::bind_method(D_METHOD("font_set_modulate_color_glyphs", "font_rid", "force_autohinter"), &TextServer::font_set_modulate_color_glyphs); + ClassDB::bind_method(D_METHOD("font_set_modulate_color_glyphs", "font_rid", "modulate"), &TextServer::font_set_modulate_color_glyphs); ClassDB::bind_method(D_METHOD("font_is_modulate_color_glyphs", "font_rid"), &TextServer::font_is_modulate_color_glyphs); + ClassDB::bind_method(D_METHOD("font_get_palette_count", "font_rid"), &TextServer::font_get_palette_count); + ClassDB::bind_method(D_METHOD("font_get_palette_name", "font_rid", "index"), &TextServer::font_get_palette_name); + ClassDB::bind_method(D_METHOD("font_get_palette_colors", "font_rid", "index"), &TextServer::font_get_palette_colors); + ClassDB::bind_method(D_METHOD("font_set_palette_custom_colors", "font_rid", "colors"), &TextServer::font_set_palette_custom_colors); + ClassDB::bind_method(D_METHOD("font_get_palette_custom_colors", "font_rid"), &TextServer::font_get_palette_custom_colors); + ClassDB::bind_method(D_METHOD("font_get_used_palette", "font_rid"), &TextServer::font_get_used_palette); + ClassDB::bind_method(D_METHOD("font_set_used_palette", "font_rid", "index"), &TextServer::font_set_used_palette); + ClassDB::bind_method(D_METHOD("font_set_hinting", "font_rid", "hinting"), &TextServer::font_set_hinting); ClassDB::bind_method(D_METHOD("font_get_hinting", "font_rid"), &TextServer::font_get_hinting); diff --git a/servers/text/text_server.h b/servers/text/text_server.h index 89c9b5ef62..f2da4fc3e7 100644 --- a/servers/text/text_server.h +++ b/servers/text/text_server.h @@ -332,6 +332,14 @@ public: virtual void font_set_modulate_color_glyphs(const RID &p_font_rid, bool p_modulate) = 0; virtual bool font_is_modulate_color_glyphs(const RID &p_font_rid) const = 0; + virtual int64_t font_get_palette_count(const RID &p_font_rid) const = 0; + virtual String font_get_palette_name(const RID &p_font_rid, int64_t p_index) const = 0; + virtual Vector font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const = 0; + virtual void font_set_palette_custom_colors(const RID &p_font_rid, const Vector &p_colors) = 0; + virtual Vector font_get_palette_custom_colors(const RID &p_font_rid) const = 0; + virtual int64_t font_get_used_palette(const RID &p_font_rid) const = 0; + virtual void font_set_used_palette(const RID &p_font_rid, int64_t p_index) = 0; + virtual void font_set_hinting(const RID &p_font_rid, Hinting p_hinting) = 0; virtual Hinting font_get_hinting(const RID &p_font_rid) const = 0; diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp index a95d233ca1..49b3e1ec6c 100644 --- a/servers/text/text_server_extension.cpp +++ b/servers/text/text_server_extension.cpp @@ -115,6 +115,14 @@ void TextServerExtension::_bind_methods() { GDVIRTUAL_BIND(_font_set_modulate_color_glyphs, "font_rid", "modulate"); GDVIRTUAL_BIND(_font_is_modulate_color_glyphs, "font_rid"); + GDVIRTUAL_BIND(_font_get_palette_count, "font_rid"); + GDVIRTUAL_BIND(_font_get_palette_name, "font_rid", "index"); + GDVIRTUAL_BIND(_font_get_palette_colors, "font_rid", "index"); + GDVIRTUAL_BIND(_font_set_palette_custom_colors, "font_rid", "colors"); + GDVIRTUAL_BIND(_font_get_palette_custom_colors, "font_rid"); + GDVIRTUAL_BIND(_font_get_used_palette, "font_rid"); + GDVIRTUAL_BIND(_font_set_used_palette, "font_rid", "index"); + GDVIRTUAL_BIND(_font_set_hinting, "font_rid", "hinting"); GDVIRTUAL_BIND(_font_get_hinting, "font_rid"); @@ -690,6 +698,43 @@ bool TextServerExtension::font_is_modulate_color_glyphs(const RID &p_font_rid) c return ret; } +int64_t TextServerExtension::font_get_palette_count(const RID &p_font_rid) const { + int64_t ret = 0; + GDVIRTUAL_CALL(_font_get_palette_count, p_font_rid, ret); + return ret; +} + +String TextServerExtension::font_get_palette_name(const RID &p_font_rid, int64_t p_index) const { + String ret; + GDVIRTUAL_CALL(_font_get_palette_name, p_font_rid, p_index, ret); + return ret; +} +Vector TextServerExtension::font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const { + Vector ret; + GDVIRTUAL_CALL(_font_get_palette_colors, p_font_rid, p_index, ret); + return ret; +} + +void TextServerExtension::font_set_palette_custom_colors(const RID &p_font_rid, const Vector &p_colors) { + GDVIRTUAL_CALL(_font_set_palette_custom_colors, p_font_rid, p_colors); +} + +Vector TextServerExtension::font_get_palette_custom_colors(const RID &p_font_rid) const { + Vector ret; + GDVIRTUAL_CALL(_font_get_palette_custom_colors, p_font_rid, ret); + return ret; +} + +int64_t TextServerExtension::font_get_used_palette(const RID &p_font_rid) const { + int64_t ret = 0; + GDVIRTUAL_CALL(_font_get_used_palette, p_font_rid, ret); + return ret; +} + +void TextServerExtension::font_set_used_palette(const RID &p_font_rid, int64_t p_index) { + GDVIRTUAL_CALL(_font_set_used_palette, p_font_rid, p_index); +} + void TextServerExtension::font_set_hinting(const RID &p_font_rid, TextServer::Hinting p_hinting) { GDVIRTUAL_CALL(_font_set_hinting, p_font_rid, p_hinting); } diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h index b0556ee512..407aa55c07 100644 --- a/servers/text/text_server_extension.h +++ b/servers/text/text_server_extension.h @@ -213,6 +213,21 @@ public: GDVIRTUAL2(_font_set_modulate_color_glyphs, RID, bool); GDVIRTUAL1RC(bool, _font_is_modulate_color_glyphs, RID); + virtual int64_t font_get_palette_count(const RID &p_font_rid) const override; + GDVIRTUAL1RC(int64_t, _font_get_palette_count, const RID &); + virtual String font_get_palette_name(const RID &p_font_rid, int64_t p_index) const override; + GDVIRTUAL2RC(String, _font_get_palette_name, const RID &, int64_t); + virtual Vector font_get_palette_colors(const RID &p_font_rid, int64_t p_index) const override; + GDVIRTUAL2RC(Vector, _font_get_palette_colors, const RID &, int64_t); + virtual void font_set_palette_custom_colors(const RID &p_font_rid, const Vector &p_colors) override; + GDVIRTUAL2(_font_set_palette_custom_colors, const RID &, const Vector &); + virtual Vector font_get_palette_custom_colors(const RID &p_font_rid) const override; + GDVIRTUAL1RC(Vector, _font_get_palette_custom_colors, const RID &); + virtual int64_t font_get_used_palette(const RID &p_font_rid) const override; + GDVIRTUAL1RC(int64_t, _font_get_used_palette, const RID &); + virtual void font_set_used_palette(const RID &p_font_rid, int64_t p_index) override; + GDVIRTUAL2(_font_set_used_palette, const RID &, int64_t); + virtual void font_set_hinting(const RID &p_font_rid, Hinting p_hinting) override; virtual Hinting font_get_hinting(const RID &p_font_rid) const override; GDVIRTUAL2(_font_set_hinting, RID, Hinting); diff --git a/thirdparty/README.md b/thirdparty/README.md index 1f127f7928..cd511ac308 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -468,7 +468,7 @@ Patches: ## harfbuzz - Upstream: https://github.com/harfbuzz/harfbuzz -- Version: 12.3.2 (b42511e071162fe76102f613a6ccc009726c99af, 2026) +- Version: 13.1.1 (70b812d8d2fb4a2b6386c04bfd8565a0fcc05f9b, 2026) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh b/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh index 63d59af6ca..e8c73ffaab 100644 --- a/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh +++ b/thirdparty/harfbuzz/src/OT/Color/COLR/COLR.hh @@ -1814,10 +1814,7 @@ struct ClipList { auto *rec = clips.as_array ().bsearch (gid); if (rec) - { - rec->get_extents (extents, this, instancer); - return true; - } + return rec->get_extents (extents, this, instancer); return false; } diff --git a/thirdparty/harfbuzz/src/OT/Color/svg/svg.hh b/thirdparty/harfbuzz/src/OT/Color/svg/svg.hh index d6aca4a60e..d6fb71832b 100644 --- a/thirdparty/harfbuzz/src/OT/Color/svg/svg.hh +++ b/thirdparty/harfbuzz/src/OT/Color/svg/svg.hh @@ -27,7 +27,12 @@ #include "../../../hb-open-type.hh" #include "../../../hb-blob.hh" +#include "../../../hb-limits.hh" +#include "../../../hb-map.hh" #include "../../../hb-paint.hh" +#include "../../../hb-zlib.hh" +#include +#include /* * SVG -- SVG (Scalable Vector Graphics) @@ -39,12 +44,62 @@ namespace OT { +static inline hb_blob_t * +hb_ot_svg_reference_normalized_blob (hb_blob_t *image, + const char **svg, + unsigned *len) +{ + hb_blob_t *blob = hb_blob_reference (image); + unsigned data_len = 0; + const char *data = hb_blob_get_data (blob, &data_len); + + if (!data || !data_len) + goto fail; + + if (hb_blob_is_gzip (data, data_len)) + { + uint32_t expected_size = 0; + if (hb_gzip_get_uncompressed_size (data, data_len, &expected_size) && + unlikely ((size_t) expected_size > (size_t) HB_SVG_MAX_DOCUMENT_SIZE)) + goto fail; + + hb_blob_t *uncompressed = hb_blob_decompress_gzip (blob, + HB_SVG_MAX_DOCUMENT_SIZE); + if (!uncompressed) + goto fail; + + hb_blob_destroy (blob); + blob = uncompressed; + data = hb_blob_get_data (blob, &data_len); + if (!data || !data_len) + goto fail; + } + + if (unlikely ((size_t) data_len > (size_t) HB_SVG_MAX_DOCUMENT_SIZE)) + goto fail; + + if (svg) *svg = data; + if (len) *len = data_len; + return blob; + +fail: + hb_blob_destroy (blob); + if (svg) *svg = nullptr; + if (len) *len = 0; + return nullptr; +} struct SVGDocumentIndexEntry { int cmp (hb_codepoint_t g) const { return g < startGlyphID ? -1 : g > endGlyphID ? 1 : 0; } + hb_codepoint_t get_start_glyph () const + { return startGlyphID; } + + hb_codepoint_t get_end_glyph () const + { return endGlyphID; } + hb_blob_t *reference_blob (hb_blob_t *svg_blob, unsigned int index_offset) const { return hb_blob_create_sub_blob (svg_blob, @@ -78,13 +133,50 @@ struct SVG { static constexpr hb_tag_t tableTag = HB_OT_TAG_SVG; + struct svg_id_span_t + { + const char *p; + unsigned len; + + bool operator == (const svg_id_span_t &o) const + { + return len == o.len && !memcmp (p, o.p, len); + } + + uint32_t hash () const + { + uint32_t h = hb_hash (len); + for (unsigned i = 0; i < len; i++) + h = h * 33u + (unsigned char) p[i]; + return h; + } + }; + + struct svg_defs_entry_t + { + svg_id_span_t id; + unsigned start; + unsigned end; + }; + + struct svg_doc_cache_t + { + hb_blob_t *blob = nullptr; + const char *svg = nullptr; + unsigned len = 0; + hb_vector_t defs_entries; + hb_codepoint_t start_glyph = HB_CODEPOINT_INVALID; + hb_codepoint_t end_glyph = HB_CODEPOINT_INVALID; + hb_vector_t> glyph_spans; + hb_hashmap_t> id_spans; + }; + bool has_data () const { return svgDocEntries; } struct accelerator_t { - accelerator_t (hb_face_t *face) - { table = hb_sanitize_context_t ().reference_table (face); } - ~accelerator_t () { table.destroy (); } + accelerator_t (hb_face_t *face); + ~accelerator_t (); hb_blob_t *reference_blob_for_glyph (hb_codepoint_t glyph_id) const { @@ -92,8 +184,52 @@ struct SVG table->svgDocEntries); } + unsigned get_document_count () const + { return table->get_document_count (); } + + bool get_glyph_document_index (hb_codepoint_t glyph_id, unsigned *index) const + { return table->get_glyph_document_index (glyph_id, index); } + + bool get_document_glyph_range (unsigned index, + hb_codepoint_t *start_glyph, + hb_codepoint_t *end_glyph) const + { return table->get_document_glyph_range (index, start_glyph, end_glyph); } + bool has_data () const { return table->has_data (); } + const svg_doc_cache_t * + get_or_create_doc_cache (hb_blob_t *image, + const char *svg, + unsigned len, + unsigned doc_index, + hb_codepoint_t start_glyph, + hb_codepoint_t end_glyph) const; + + const char * + doc_cache_get_svg (const svg_doc_cache_t *doc, + unsigned *len) const; + + const hb_vector_t * + doc_cache_get_defs_entries (const svg_doc_cache_t *doc) const; + + bool + doc_cache_get_glyph_span (const svg_doc_cache_t *doc, + hb_codepoint_t glyph, + unsigned *start, + unsigned *end) const; + + bool + doc_cache_find_id_span (const svg_doc_cache_t *doc, + svg_id_span_t id, + unsigned *start, + unsigned *end) const; + + bool + doc_cache_find_id_cstr (const svg_doc_cache_t *doc, + const char *id, + unsigned *start, + unsigned *end) const; + bool paint_glyph (hb_font_t *font HB_UNUSED, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data) const { if (!has_data ()) @@ -117,14 +253,56 @@ struct SVG } private: + svg_doc_cache_t * + make_doc_cache (hb_blob_t *image, + const char *svg, + unsigned len, + hb_codepoint_t start_glyph, + hb_codepoint_t end_glyph) const; + + static void destroy_doc_cache (svg_doc_cache_t *doc); + hb_blob_ptr_t table; + mutable hb_vector_t> doc_caches; public: - DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t)); + DEFINE_SIZE_STATIC (sizeof (hb_blob_ptr_t) + + sizeof (hb_vector_t>)); }; const SVGDocumentIndexEntry &get_glyph_entry (hb_codepoint_t glyph_id) const { return (this+svgDocEntries).bsearch (glyph_id); } + unsigned get_document_count () const + { + if (!has_data ()) + return 0; + return (this + svgDocEntries).len; + } + + bool get_glyph_document_index (hb_codepoint_t glyph_id, unsigned *index) const + { + if (!has_data ()) + return false; + return (this + svgDocEntries).bfind (glyph_id, index); + } + + bool get_document_glyph_range (unsigned index, + hb_codepoint_t *start_glyph, + hb_codepoint_t *end_glyph) const + { + if (!has_data ()) + return false; + + const auto &entries = this + svgDocEntries; + if (index >= entries.len) + return false; + + const auto &entry = entries.arrayZ[index]; + if (start_glyph) *start_glyph = entry.get_start_glyph (); + if (end_glyph) *end_glyph = entry.get_end_glyph (); + return true; + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -143,6 +321,506 @@ struct SVG DEFINE_SIZE_STATIC (10); }; +namespace _hb_svg_cache_impl { + +struct glyph_entry_t +{ + hb_codepoint_t glyph; + uint32_t start; + uint32_t end; +}; + +struct id_entry_t +{ + SVG::svg_id_span_t id; + uint32_t start; + uint32_t end; +}; + +struct open_elem_t +{ + unsigned start; + SVG::svg_id_span_t id; + bool in_defs_content; + bool is_defs; +}; + +static const unsigned MAX_DEPTH = 128; + +static inline int +find_substr (const char *s, + unsigned n, + unsigned from, + const char *needle, + unsigned needle_len) +{ + if (!needle_len || from >= n || needle_len > n) + return -1; + for (unsigned i = from; i + needle_len <= n; i++) + if (s[i] == needle[0] && !memcmp (s + i, needle, needle_len)) + return (int) i; + return -1; +} + +static inline bool +parse_id_in_start_tag (const char *svg, + unsigned tag_start, + unsigned tag_end, + SVG::svg_id_span_t *id) +{ + unsigned p = tag_start; + while (p + 4 <= tag_end) + { + if (!memcmp (svg + p, "id=\"", 4)) + { + unsigned b = p + 4; + unsigned e = b; + while (e < tag_end && svg[e] != '"') e++; + if (e <= tag_end && e > b) + { + *id = {svg + b, e - b}; + return true; + } + } + if (!memcmp (svg + p, "id='", 4)) + { + unsigned b = p + 4; + unsigned e = b; + while (e < tag_end && svg[e] != '\'') e++; + if (e <= tag_end && e > b) + { + *id = {svg + b, e - b}; + return true; + } + } + p++; + } + return false; +} + +static inline bool +parse_glyph_id_span (const SVG::svg_id_span_t &id, + hb_codepoint_t *glyph) +{ + if (id.len <= 5 || memcmp (id.p, "glyph", 5)) + return false; + + hb_codepoint_t gid = 0; + for (unsigned i = 5; i < id.len; i++) + { + unsigned char c = (unsigned char) id.p[i]; + if (c < '0' || c > '9') + return false; + hb_codepoint_t digit = (hb_codepoint_t) (c - '0'); + if (unlikely (gid > HB_CODEPOINT_INVALID / 10 || + (gid == HB_CODEPOINT_INVALID / 10 && + digit > HB_CODEPOINT_INVALID % 10))) + return false; + gid = (hb_codepoint_t) (gid * 10 + digit); + } + + *glyph = gid; + return true; +} + +static inline bool +parse_cache_entries_linear (const char *svg, + unsigned len, + hb_vector_t *defs_entries, + hb_vector_t *glyph_spans, + hb_vector_t *id_entries) +{ + open_elem_t stack[MAX_DEPTH]; + unsigned depth = 0; + defs_entries->alloc (256); + id_entries->alloc (256); + + unsigned defs_depth = 0; + unsigned i = 0; + while (i < len) + { + if (svg[i] != '<') + { + i++; + continue; + } + + if (i + 4 <= len && !memcmp (svg + i, "", 3); + if (cend < 0) return false; + i = (unsigned) cend + 3; + continue; + } + if (i + 9 <= len && !memcmp (svg + i, "", 9)) + { + int cend = find_substr (svg, len, i + 9, "", 3); + if (cend < 0) return false; + i = (unsigned) cend + 3; + continue; + } + + bool closing = (i + 1 < len && svg[i + 1] == '/'); + bool special = (i + 1 < len && (svg[i + 1] == '!' || svg[i + 1] == '?')); + + unsigned gt = i + 1; + char quote = 0; + while (gt < len) + { + char c = svg[gt]; + if (quote) + { + if (c == quote) quote = 0; + } + else + { + if (c == '"' || c == '\'') + quote = c; + else if (c == '>') + break; + } + gt++; + } + if (gt >= len) + return false; + + if (special) + { + i = gt + 1; + continue; + } + + unsigned p = i + (closing ? 2 : 1); + while (p < gt && isspace ((unsigned char) svg[p])) p++; + const char *name = svg + p; + unsigned name_len = 0; + while (p + name_len < gt) + { + unsigned char c = (unsigned char) name[name_len]; + if (!(isalnum (c) || c == '_' || c == '-' || c == ':')) + break; + name_len++; + } + bool is_defs = (name_len == 4 && !memcmp (name, "defs", 4)); + + if (closing) + { + if (!depth) + { + i = gt + 1; + continue; + } + + open_elem_t e = stack[--depth]; + unsigned end = gt + 1; + + if (e.id.len) + { + auto *id_slot = id_entries->push (); + if (unlikely (!id_slot)) + return false; + *id_slot = {e.id, (uint32_t) e.start, (uint32_t) end}; + + if (e.in_defs_content) + { + auto *slot = defs_entries->push (); + if (unlikely (!slot)) + return false; + slot->id = e.id; + slot->start = e.start; + slot->end = end; + } + + hb_codepoint_t gid; + if (parse_glyph_id_span (e.id, &gid)) + { + auto *span = glyph_spans->push (); + if (unlikely (!span)) + return false; + span->glyph = gid; + span->start = (uint32_t) e.start; + span->end = (uint32_t) end; + } + } + + if (e.is_defs && defs_depth) + defs_depth--; + + i = end; + continue; + } + + SVG::svg_id_span_t id = {nullptr, 0}; + parse_id_in_start_tag (svg, i, gt, &id); + + unsigned r = gt; + while (r > i && isspace ((unsigned char) svg[r - 1])) r--; + bool self_closing = (r > i && svg[r - 1] == '/'); + + open_elem_t e = {i, id, defs_depth > 0, is_defs}; + + if (self_closing) + { + unsigned end = gt + 1; + if (e.id.len) + { + auto *id_slot = id_entries->push (); + if (unlikely (!id_slot)) + return false; + *id_slot = {e.id, (uint32_t) e.start, (uint32_t) end}; + + if (e.in_defs_content) + { + auto *slot = defs_entries->push (); + if (unlikely (!slot)) + return false; + slot->id = e.id; + slot->start = e.start; + slot->end = end; + } + + hb_codepoint_t gid; + if (parse_glyph_id_span (e.id, &gid)) + { + auto *span = glyph_spans->push (); + if (unlikely (!span)) + return false; + span->glyph = gid; + span->start = (uint32_t) e.start; + span->end = (uint32_t) end; + } + } + } + else + { + if (unlikely (depth >= MAX_DEPTH)) + return false; + stack[depth++] = e; + if (is_defs) + defs_depth++; + } + + i = gt + 1; + } + + return true; +} + +} /* namespace _hb_svg_cache_impl */ + +inline +SVG::accelerator_t::accelerator_t (hb_face_t *face) +{ + table = hb_sanitize_context_t ().reference_table (face); + doc_caches.init (); + unsigned doc_count = table->get_document_count (); + if (doc_count && unlikely (!doc_caches.resize (doc_count))) + doc_caches.resize (0); + for (unsigned i = 0; i < doc_caches.length; i++) + doc_caches.arrayZ[i].set_relaxed (nullptr); +} + +inline +SVG::accelerator_t::~accelerator_t () +{ + for (unsigned i = 0; i < doc_caches.length; i++) + destroy_doc_cache (doc_caches.arrayZ[i].get_relaxed ()); + doc_caches.fini (); + table.destroy (); +} + +inline void +SVG::accelerator_t::destroy_doc_cache (svg_doc_cache_t *doc) +{ + if (!doc) + return; + doc->glyph_spans.fini (); + doc->defs_entries.fini (); + doc->id_spans.fini (); + hb_blob_destroy (doc->blob); + hb_free (doc); +} + +inline SVG::svg_doc_cache_t * +SVG::accelerator_t::make_doc_cache (hb_blob_t *image, + const char *svg, + unsigned len, + hb_codepoint_t start_glyph, + hb_codepoint_t end_glyph) const +{ + static const uint32_t INVALID_SPAN = 0xFFFFFFFFu; + + auto *doc = (svg_doc_cache_t *) hb_malloc (sizeof (svg_doc_cache_t)); + if (!doc) + return nullptr; + + doc->blob = nullptr; + doc->svg = nullptr; + doc->len = 0; + doc->defs_entries.init (); + doc->start_glyph = HB_CODEPOINT_INVALID; + doc->end_glyph = HB_CODEPOINT_INVALID; + doc->glyph_spans.init (); + doc->id_spans.init (); + + doc->blob = hb_blob_reference (image); + doc->svg = svg; + doc->len = len; + doc->start_glyph = start_glyph; + doc->end_glyph = end_glyph; + + if (unlikely (start_glyph == HB_CODEPOINT_INVALID || end_glyph < start_glyph)) + { + destroy_doc_cache (doc); + return nullptr; + } + + unsigned glyph_count = end_glyph - start_glyph + 1; + if (!doc->glyph_spans.resize ((int) glyph_count)) + { + destroy_doc_cache (doc); + return nullptr; + } + for (unsigned i = 0; i < glyph_count; i++) + doc->glyph_spans.arrayZ[i] = hb_pair_t (INVALID_SPAN, INVALID_SPAN); + + hb_vector_t<_hb_svg_cache_impl::glyph_entry_t> glyph_spans; + glyph_spans.init (); + hb_vector_t<_hb_svg_cache_impl::id_entry_t> id_entries; + id_entries.init (); + if (!_hb_svg_cache_impl::parse_cache_entries_linear (svg, len, + &doc->defs_entries, + &glyph_spans, + &id_entries)) + { + id_entries.fini (); + glyph_spans.fini (); + destroy_doc_cache (doc); + return nullptr; + } + + for (unsigned i = 0; i < glyph_spans.length; i++) + { + const auto &span = glyph_spans.arrayZ[i]; + if (unlikely (span.glyph < start_glyph || span.glyph > end_glyph)) + continue; + doc->glyph_spans.arrayZ[span.glyph - start_glyph] = hb_pair_t (span.start, span.end); + } + + for (unsigned i = 0; i < id_entries.length; i++) + { + const auto &e = id_entries.arrayZ[i]; + hb_pair_t *out = nullptr; + if (doc->id_spans.has (e.id, &out)) + continue; + if (unlikely (!doc->id_spans.set (e.id, hb_pair_t (e.start, e.end)))) + { + id_entries.fini (); + glyph_spans.fini (); + destroy_doc_cache (doc); + return nullptr; + } + } + + id_entries.fini (); + glyph_spans.fini (); + return doc; +} + +inline const SVG::svg_doc_cache_t * +SVG::accelerator_t::get_or_create_doc_cache (hb_blob_t *image, + const char *svg, + unsigned len, + unsigned doc_index, + hb_codepoint_t start_glyph, + hb_codepoint_t end_glyph) const +{ + if (doc_index >= doc_caches.length) + return nullptr; + + auto &slot = doc_caches.arrayZ[doc_index]; + auto *doc = slot.get_acquire (); + if (doc) + return doc; + + auto *fresh = make_doc_cache (image, svg, len, start_glyph, end_glyph); + if (!fresh) + return nullptr; + + auto *expected = (svg_doc_cache_t *) nullptr; + if (slot.cmpexch (expected, fresh)) + return fresh; + + destroy_doc_cache (fresh); + return expected; +} + +inline const char * +SVG::accelerator_t::doc_cache_get_svg (const svg_doc_cache_t *doc, + unsigned *len) const +{ + if (!doc) + { + if (len) *len = 0; + return nullptr; + } + if (len) *len = doc->len; + return doc->svg; +} + +inline const hb_vector_t * +SVG::accelerator_t::doc_cache_get_defs_entries (const svg_doc_cache_t *doc) const +{ + return doc ? &doc->defs_entries : nullptr; +} + +inline bool +SVG::accelerator_t::doc_cache_get_glyph_span (const svg_doc_cache_t *doc, + hb_codepoint_t glyph, + unsigned *start, + unsigned *end) const +{ + static const uint32_t INVALID_SPAN = 0xFFFFFFFFu; + if (!doc || doc->start_glyph == HB_CODEPOINT_INVALID || + glyph < doc->start_glyph || glyph > doc->end_glyph) + return false; + + const auto &span = doc->glyph_spans.arrayZ[glyph - doc->start_glyph]; + if (span.first == INVALID_SPAN) + return false; + + if (start) *start = span.first; + if (end) *end = span.second; + return true; +} + +inline bool +SVG::accelerator_t::doc_cache_find_id_span (const svg_doc_cache_t *doc, + svg_id_span_t id, + unsigned *start, + unsigned *end) const +{ + if (!doc || !id.p || !id.len) + return false; + hb_pair_t *span = nullptr; + if (!doc->id_spans.has (id, &span)) + return false; + if (start) *start = span->first; + if (end) *end = span->second; + return true; +} + +inline bool +SVG::accelerator_t::doc_cache_find_id_cstr (const svg_doc_cache_t *doc, + const char *id, + unsigned *start, + unsigned *end) const +{ + if (!id) return false; + svg_id_span_t key = {id, (unsigned) strlen (id)}; + return doc_cache_find_id_span (doc, key, start, end); +} + struct SVG_accelerator_t : SVG::accelerator_t { SVG_accelerator_t (hb_face_t *face) : SVG::accelerator_t (face) {} }; diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh index e9fe392311..4792a4974d 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat1.hh @@ -100,7 +100,7 @@ struct SinglePosFormat1 : ValueBase if (likely (index == NOT_COVERED)) return false; /* This is ugly... */ - hb_buffer_t buffer; + hb_buffer_t buffer {}; buffer.props.direction = direction; OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob); diff --git a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh index 2fb7105898..dc92f6a523 100644 --- a/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh +++ b/thirdparty/harfbuzz/src/OT/Layout/GPOS/SinglePosFormat2.hh @@ -104,7 +104,7 @@ struct SinglePosFormat2 : ValueBase if (unlikely (index >= valueCount)) return false; /* This is ugly... */ - hb_buffer_t buffer; + hb_buffer_t buffer {}; buffer.props.direction = direction; OT::hb_ot_apply_context_t c (1, font, &buffer, table_blob); diff --git a/thirdparty/harfbuzz/src/OT/Var/VARC/VARC.cc b/thirdparty/harfbuzz/src/OT/Var/VARC/VARC.cc index 1a22f1d812..7524686d30 100644 --- a/thirdparty/harfbuzz/src/OT/Var/VARC/VARC.cc +++ b/thirdparty/harfbuzz/src/OT/Var/VARC/VARC.cc @@ -228,28 +228,28 @@ VarComponent::get_path_at (const hb_varc_context_t &c, #define PROCESS_TRANSFORM_COMPONENTS \ HB_STMT_START { \ - PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_X, translateX); \ - PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_Y, translateY); \ - PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_ROTATION, rotation); \ - PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_X, scaleX); \ - PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_Y, scaleY); \ - PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_X, skewX); \ - PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_Y, skewY); \ - PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_X, tCenterX); \ - PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_Y, tCenterY); \ + PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_X, translateX); \ + PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TRANSLATE_Y, translateY); \ + PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_ROTATION, rotation); \ + PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_X, scaleX); \ + PROCESS_TRANSFORM_COMPONENT (10, F6DOT10, HAVE_SCALE_Y, scaleY); \ + PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_X, skewX); \ + PROCESS_TRANSFORM_COMPONENT (12, F4DOT12, HAVE_SKEW_Y, skewY); \ + PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_X, tCenterX); \ + PROCESS_TRANSFORM_COMPONENT ( 0, FWORD, HAVE_TCENTER_Y, tCenterY); \ } HB_STMT_END hb_transform_decomposed_t<> transform; // Read transform components -#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ +#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \ if (flags & (unsigned) flags_t::flag) \ { \ static_assert (type::static_size == HBINT16::static_size, ""); \ if (unlikely (unsigned (end - record) < HBINT16::static_size)) \ return hb_ubytes_t (); \ hb_barrier (); \ - transform.name = mult * * (const HBINT16 *) record; \ + transform.name = * (const HBINT16 *) record; \ record += HBINT16::static_size; \ } PROCESS_TRANSFORM_COMPONENTS; @@ -270,9 +270,8 @@ VarComponent::get_path_at (const hb_varc_context_t &c, { // Only use coord_setter if there's actually any axis overrides. coord_setter_t coord_setter (axisIndices ? component_coords : hb_array ()); - // Go backwards, to reduce coord_setter vector reallocations. - for (unsigned i = axisIndices.length; i; i--) - coord_setter[axisIndices[i - 1]] = axisValues[i - 1]; + for (unsigned i = 0; i < axisIndices.length; i++) + coord_setter[axisIndices[i]] = roundf (axisValues[i]); if (axisIndices) component_coords = coord_setter.get_coords (); @@ -281,39 +280,35 @@ VarComponent::get_path_at (const hb_varc_context_t &c, { float transformValues[9]; unsigned numTransformValues = 0; -#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ +#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \ if (flags & (unsigned) flags_t::flag) \ - transformValues[numTransformValues++] = transform.name / mult; + transformValues[numTransformValues++] = transform.name; PROCESS_TRANSFORM_COMPONENTS; #undef PROCESS_TRANSFORM_COMPONENT varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache); numTransformValues = 0; -#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ +#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \ if (flags & (unsigned) flags_t::flag) \ - transform.name = transformValues[numTransformValues++] * mult; + transform.name = transformValues[numTransformValues++]; PROCESS_TRANSFORM_COMPONENTS; #undef PROCESS_TRANSFORM_COMPONENT } // Divide them by their divisors -#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \ - if (flags & (unsigned) flags_t::flag) \ - { \ - HBINT16 int_v; \ - int_v = roundf (transform.name); \ - type typed_v = * (const type *) &int_v; \ - float float_v = (float) typed_v; \ - transform.name = float_v; \ - } +#define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \ + if (shift && (flags & (unsigned) flags_t::flag)) \ + transform.name *= 1.f / (1 << shift); PROCESS_TRANSFORM_COMPONENTS; #undef PROCESS_TRANSFORM_COMPONENT if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y)) transform.scaleY = transform.scaleX; + transform.rotation *= HB_PI; + transform.skewX *= HB_PI; + transform.skewY *= HB_PI; + total_transform.transform (transform.to_transform ()); - total_transform.scale (c.font->x_mult ? 1.f / c.font->x_multf : 0.f, - c.font->y_mult ? 1.f / c.font->y_multf : 0.f); bool same_coords = component_coords.length == coords.length && component_coords.arrayZ == coords.arrayZ; @@ -348,14 +343,18 @@ VARC::get_path_at (const hb_varc_context_t &c, { if (c.draw_session) { + hb_transform_t<> leaf_transform = transform; + leaf_transform.x0 *= c.font->x_multf; + leaf_transform.y0 *= c.font->y_multf; + // Build a transforming pen to apply the transform. hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs (); - hb_transforming_pen_context_t context {transform, + hb_transforming_pen_context_t context {leaf_transform, c.draw_session->funcs, c.draw_session->draw_data, &c.draw_session->st}; hb_draw_session_t transformer_session {transformer_funcs, &context}; - hb_draw_session_t &shape_draw_session = transform.is_identity () ? *c.draw_session : transformer_session; + hb_draw_session_t &shape_draw_session = leaf_transform.is_identity () ? *c.draw_session : transformer_session; if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch)) return true; #ifndef HB_NO_CFF @@ -375,7 +374,10 @@ VARC::get_path_at (const hb_varc_context_t &c, return false; hb_extents_t<> comp_extents (glyph_extents); - transform.transform_extents (comp_extents); + hb_transform_t<> leaf_transform = transform; + leaf_transform.x0 *= c.font->x_multf; + leaf_transform.y0 *= c.font->y_multf; + leaf_transform.transform_extents (comp_extents); c.extents->union_ (comp_extents); } return true; @@ -399,8 +401,6 @@ VARC::get_path_at (const hb_varc_context_t &c, parent_cache : (this+varStore).create_cache (&static_cache); - transform.scale (c.font->x_multf, c.font->y_multf); - VarCompositeGlyph::get_path_at (c, glyph, coords, transform, diff --git a/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh index a3ec885e64..83d3eaed0a 100644 --- a/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh +++ b/thirdparty/harfbuzz/src/OT/glyf/Glyph.hh @@ -384,20 +384,24 @@ struct Glyph { #ifndef HB_NO_BEYOND_64K if (glyf_accelerator.GVAR->has_data ()) - glyf_accelerator.GVAR->apply_deltas_to_points (gid, - coords, - points.as_array ().sub_array (old_length), - scratch, - gvar_cache, - phantom_only && type == SIMPLE); + { + if (!glyf_accelerator.GVAR->apply_deltas_to_points (gid, + coords, + points.as_array ().sub_array (old_length), + scratch, + gvar_cache, + phantom_only && type == SIMPLE)) + return false; + } else #endif - glyf_accelerator.gvar->apply_deltas_to_points (gid, - coords, - points.as_array ().sub_array (old_length), - scratch, - gvar_cache, - phantom_only && type == SIMPLE); + if (!glyf_accelerator.gvar->apply_deltas_to_points (gid, + coords, + points.as_array ().sub_array (old_length), + scratch, + gvar_cache, + phantom_only && type == SIMPLE)) + return false; } #endif diff --git a/thirdparty/harfbuzz/src/hb-algs.hh b/thirdparty/harfbuzz/src/hb-algs.hh index 786ca95d2e..b28a587d47 100644 --- a/thirdparty/harfbuzz/src/hb-algs.hh +++ b/thirdparty/harfbuzz/src/hb-algs.hh @@ -89,7 +89,16 @@ static inline constexpr uint32_t hb_uint32_swap (uint32_t v) { return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); } template -struct __attribute__((packed)) hb_packed_t { Type v; }; +struct __attribute__((packed)) hb_packed_t +{ + hb_packed_t () = default; + constexpr hb_packed_t (Type V) : v (V) {} + operator Type () const { return v; } + hb_packed_t & operator = (Type V) { v = V; return *this; } + + private: + Type v; +}; #ifndef HB_FAST_NUM_ACCESS @@ -134,9 +143,9 @@ struct HBInt #if HB_FAST_NUM_ACCESS { if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) - ((hb_packed_t *) v)->v = V; + *((hb_packed_t *) v) = V; else - ((hb_packed_t *) v)->v = __builtin_bswap16 (V); + *((hb_packed_t *) v) = __builtin_bswap16 (V); } #else : v {BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V ) & 0xFF), @@ -147,9 +156,9 @@ struct HBInt { #if HB_FAST_NUM_ACCESS return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? - ((const hb_packed_t *) v)->v + (uint16_t) *((const hb_packed_t *) v) : - __builtin_bswap16 (((const hb_packed_t *) v)->v) + __builtin_bswap16 ((uint16_t) *((const hb_packed_t *) v)) ; #else return (BE ? (v[0] << 8) : (v[0] )) @@ -186,9 +195,9 @@ struct HBInt #if HB_FAST_NUM_ACCESS { if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) - ((hb_packed_t *) v)->v = V; + *((hb_packed_t *) v) = V; else - ((hb_packed_t *) v)->v = __builtin_bswap32 (V); + *((hb_packed_t *) v) = __builtin_bswap32 (V); } #else : v {BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V ) & 0xFF), @@ -200,9 +209,9 @@ struct HBInt constexpr operator Type () const { #if HB_FAST_NUM_ACCESS return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? - ((const hb_packed_t *) v)->v + (uint32_t) *((const hb_packed_t *) v) : - __builtin_bswap32 (((const hb_packed_t *) v)->v) + __builtin_bswap32 ((uint32_t) *((const hb_packed_t *) v)) ; #else return (BE ? (v[0] << 24) : (v[0] )) @@ -226,9 +235,9 @@ struct HBInt #if HB_FAST_NUM_ACCESS { if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) - ((hb_packed_t *) v)->v = V; + *((hb_packed_t *) v) = V; else - ((hb_packed_t *) v)->v = __builtin_bswap64 (V); + *((hb_packed_t *) v) = __builtin_bswap64 (V); } #else : v {BE ? uint8_t ((V >> 56) & 0xFF) : uint8_t ((V ) & 0xFF), @@ -244,9 +253,9 @@ struct HBInt constexpr operator Type () const { #if HB_FAST_NUM_ACCESS return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? - ((const hb_packed_t *) v)->v + (uint64_t) *((const hb_packed_t *) v) : - __builtin_bswap64 (((const hb_packed_t *) v)->v) + __builtin_bswap64 ((uint64_t) *((const hb_packed_t *) v)) ; #else return (BE ? (uint64_t (v[0]) << 56) : (uint64_t (v[0]) )) @@ -276,12 +285,12 @@ struct HBFloat { #if HB_FAST_NUM_ACCESS { - if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) - { - ((hb_packed_t *) v)->v = V; - return; - } - } + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + { + *((hb_packed_t *) v) = V; + return; + } + } #endif union { @@ -289,7 +298,7 @@ struct HBFloat hb_packed_t i; } u = {{V}}; - const HBInt I = u.i.v; + const HBInt I = (IntType) u.i; for (unsigned i = 0; i < Bytes; i++) v[i] = I.v[i]; } @@ -297,10 +306,10 @@ struct HBFloat /* c++14 constexpr */ operator Type () const { #if HB_FAST_NUM_ACCESS - { - if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) - return ((const hb_packed_t *) v)->v; - } + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + return (Type) *((const hb_packed_t *) v); + } #endif HBInt I; @@ -312,7 +321,7 @@ struct HBFloat hb_packed_t f; } u = {{I}}; - return u.f.v; + return (Type) u.f; } private: uint8_t v[Bytes]; }; @@ -1191,6 +1200,21 @@ hb_unsigned_mul_overflows (unsigned int count, unsigned int size, unsigned *resu return (size > 0) && (count >= ((unsigned int) -1) / size); } +static inline bool +hb_unsigned_add_overflows (unsigned int a, unsigned int b, unsigned *result = nullptr) +{ +#if hb_has_builtin(__builtin_add_overflow) + unsigned stack_result; + if (!result) + result = &stack_result; + return __builtin_add_overflow (a, b, result); +#endif + + if (result) + *result = a + b; + return b > (unsigned int) -1 - a; +} + /* * Sort and search. @@ -1664,6 +1688,13 @@ double solve_itp (func_t f, double min_y, double max_y, double &ya, double &yb, double &y) { + // Guard against degenerate interval + if (b - a <= 0.0) + { + y = ya; + return a; + } + unsigned n1_2 = (unsigned) (hb_max (ceil (log2 ((b - a) / epsilon)) - 1.0, 0.0)); const unsigned n0 = 1; // Hardwired const double k1 = 0.2 / (b - a); // Hardwired. @@ -1674,7 +1705,8 @@ double solve_itp (func_t f, { double x1_2 = 0.5 * (a + b); double r = scaled_epsilon - 0.5 * (b - a); - double xf = (yb * a - ya * b) / (yb - ya); + // Guard against yb == ya to prevent division by zero + double xf = (yb != ya) ? (yb * a - ya * b) / (yb - ya) : x1_2; double sigma = x1_2 - xf; double b_a = b - a; // This has k2 = 2 hardwired for efficiency. diff --git a/thirdparty/harfbuzz/src/hb-atomic.hh b/thirdparty/harfbuzz/src/hb-atomic.hh index 41e0dcc15d..5ea99e391e 100644 --- a/thirdparty/harfbuzz/src/hb-atomic.hh +++ b/thirdparty/harfbuzz/src/hb-atomic.hh @@ -193,6 +193,9 @@ struct hb_atomic_t T *get_acquire () const { return v.load (std::memory_order_acquire); } bool cmpexch (T *old, T *new_) { return v.compare_exchange_weak (old, new_, std::memory_order_acq_rel, std::memory_order_relaxed); } + hb_atomic_t &operator= (const hb_atomic_t& o) { set_relaxed (o.get_relaxed ()); return *this; } + hb_atomic_t &operator= (hb_atomic_t&& o){ set_relaxed (o.get_relaxed ()); o.set_relaxed ({}); return *this; } + operator bool () const { return get_acquire () != nullptr; } T *operator->() const { return get_acquire (); } template diff --git a/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh b/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh index c244316805..48b2e21842 100644 --- a/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh +++ b/thirdparty/harfbuzz/src/hb-bit-set-invertible.hh @@ -173,7 +173,14 @@ struct hb_bit_set_invertible_t bool is_subset (const hb_bit_set_invertible_t &larger_set) const { if (unlikely (inverted != larger_set.inverted)) - return hb_all (hb_iter (s) | hb_map (larger_set.s)); + { + if (inverted) + return hb_all (iter (), larger_set.s); + else + // larger set is inverted so larger_set.s is the set of things that are not present + // in larger_set, therefore if s has any of those it can't be a subset. + return !s.intersects (larger_set.s); + } else return unlikely (inverted) ? larger_set.s.is_subset (s) : s.is_subset (larger_set.s); } diff --git a/thirdparty/harfbuzz/src/hb-bit-set.hh b/thirdparty/harfbuzz/src/hb-bit-set.hh index db21477d35..05725048c2 100644 --- a/thirdparty/harfbuzz/src/hb-bit-set.hh +++ b/thirdparty/harfbuzz/src/hb-bit-set.hh @@ -438,23 +438,31 @@ struct hb_bit_set_t return false; uint32_t spi = 0; - for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++) + uint32_t lpi = 0; + while (spi < page_map.length && lpi < larger_set.page_map.length) { uint32_t spm = page_map.arrayZ[spi].major; uint32_t lpm = larger_set.page_map.arrayZ[lpi].major; auto sp = page_at (spi); - if (spm < lpm && !sp.is_empty ()) - return false; - - if (lpm < spm) + if (spm < lpm) { + if (!sp.is_empty ()) + return false; + spi++; continue; + } + + if (lpm < spm) { + lpi++; + continue; + } auto lp = larger_set.page_at (lpi); if (!sp.is_subset (lp)) return false; spi++; + lpi++; } while (spi < page_map.length) diff --git a/thirdparty/harfbuzz/src/hb-blob.hh b/thirdparty/harfbuzz/src/hb-blob.hh index b1b3b94d3d..7d28c161f7 100644 --- a/thirdparty/harfbuzz/src/hb-blob.hh +++ b/thirdparty/harfbuzz/src/hb-blob.hh @@ -50,6 +50,20 @@ struct hb_blob_t } } + void replace_buffer (const char *new_data, + unsigned new_length, + hb_memory_mode_t new_mode, + void *new_user_data, + hb_destroy_func_t new_destroy) + { + destroy_user_data (); + data = new_data; + length = new_length; + mode = new_mode; + user_data = new_user_data; + destroy = new_destroy; + } + HB_INTERNAL bool try_make_writable (); HB_INTERNAL bool try_make_writable_inplace (); HB_INTERNAL bool try_make_writable_inplace_unix (); diff --git a/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh b/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh index e57b6163c8..75a1a11298 100644 --- a/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh +++ b/thirdparty/harfbuzz/src/hb-buffer-deserialize-json.hh @@ -32,7 +32,7 @@ #include "hb.hh" -#line 33 "hb-buffer-deserialize-json.hh" +#line 36 "hb-buffer-deserialize-json.hh" static const unsigned char _deserialize_json_trans_keys[] = { 0u, 0u, 9u, 34u, 97u, 121u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, @@ -597,12 +597,12 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer, hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 594 "hb-buffer-deserialize-json.hh" +#line 601 "hb-buffer-deserialize-json.hh" { cs = deserialize_json_start; } -#line 597 "hb-buffer-deserialize-json.hh" +#line 606 "hb-buffer-deserialize-json.hh" { int _slen; int _trans; @@ -712,7 +712,7 @@ _resume: #line 56 "hb-buffer-deserialize-json.rl" { if (unlikely (!buffer->ensure_unicode ())) return false; } break; -#line 689 "hb-buffer-deserialize-json.hh" +#line 716 "hb-buffer-deserialize-json.hh" } _again: diff --git a/thirdparty/harfbuzz/src/hb-buffer-deserialize-text-glyphs.hh b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text-glyphs.hh index 8fc54b36c4..8c0353b3b2 100644 --- a/thirdparty/harfbuzz/src/hb-buffer-deserialize-text-glyphs.hh +++ b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text-glyphs.hh @@ -32,7 +32,7 @@ #include "hb.hh" -#line 33 "hb-buffer-deserialize-text-glyphs.hh" +#line 36 "hb-buffer-deserialize-text-glyphs.hh" static const unsigned char _deserialize_text_glyphs_trans_keys[] = { 0u, 0u, 35u, 124u, 48u, 57u, 60u, 124u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 62u, 62u, @@ -389,12 +389,12 @@ _hb_buffer_deserialize_text_glyphs (hb_buffer_t *buffer, hb_glyph_info_t info = {0}; hb_glyph_position_t pos = {0}; -#line 386 "hb-buffer-deserialize-text-glyphs.hh" +#line 393 "hb-buffer-deserialize-text-glyphs.hh" { cs = deserialize_text_glyphs_start; } -#line 389 "hb-buffer-deserialize-text-glyphs.hh" +#line 398 "hb-buffer-deserialize-text-glyphs.hh" { int _slen; int _trans; @@ -552,7 +552,7 @@ _resume: return false; } break; -#line 523 "hb-buffer-deserialize-text-glyphs.hh" +#line 556 "hb-buffer-deserialize-text-glyphs.hh" } _again: @@ -573,7 +573,7 @@ _again: *end_ptr = p; } break; -#line 542 "hb-buffer-deserialize-text-glyphs.hh" +#line 577 "hb-buffer-deserialize-text-glyphs.hh" } } diff --git a/thirdparty/harfbuzz/src/hb-buffer-deserialize-text-unicode.hh b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text-unicode.hh index 0bb00c4c2e..26ec0b4225 100644 --- a/thirdparty/harfbuzz/src/hb-buffer-deserialize-text-unicode.hh +++ b/thirdparty/harfbuzz/src/hb-buffer-deserialize-text-unicode.hh @@ -32,7 +32,7 @@ #include "hb.hh" -#line 33 "hb-buffer-deserialize-text-unicode.hh" +#line 36 "hb-buffer-deserialize-text-unicode.hh" static const unsigned char _deserialize_text_unicode_trans_keys[] = { 0u, 0u, 43u, 102u, 48u, 102u, 48u, 124u, 48u, 57u, 62u, 124u, 48u, 124u, 60u, 117u, 85u, 117u, 85u, 117u, 0 @@ -150,12 +150,12 @@ _hb_buffer_deserialize_text_unicode (hb_buffer_t *buffer, hb_glyph_info_t info = {0}; const hb_glyph_position_t pos = {0}; -#line 147 "hb-buffer-deserialize-text-unicode.hh" +#line 154 "hb-buffer-deserialize-text-unicode.hh" { cs = deserialize_text_unicode_start; } -#line 150 "hb-buffer-deserialize-text-unicode.hh" +#line 159 "hb-buffer-deserialize-text-unicode.hh" { int _slen; int _trans; @@ -215,7 +215,7 @@ _resume: hb_memset (&info, 0, sizeof (info)); } break; -#line 203 "hb-buffer-deserialize-text-unicode.hh" +#line 219 "hb-buffer-deserialize-text-unicode.hh" } _again: @@ -238,7 +238,7 @@ _again: *end_ptr = p; } break; -#line 224 "hb-buffer-deserialize-text-unicode.hh" +#line 242 "hb-buffer-deserialize-text-unicode.hh" } } diff --git a/thirdparty/harfbuzz/src/hb-buffer-serialize.cc b/thirdparty/harfbuzz/src/hb-buffer-serialize.cc index 09f0c30eba..923524d708 100644 --- a/thirdparty/harfbuzz/src/hb-buffer-serialize.cc +++ b/thirdparty/harfbuzz/src/hb-buffer-serialize.cc @@ -114,6 +114,17 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, *buf_consumed = 0; hb_position_t x = 0, y = 0; + + /* Calculate the advance of the previous glyphs */ + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + for (unsigned int i = 0; i < start; i++) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + for (unsigned int i = start; i < end; i++) { char b[1024]; @@ -151,7 +162,7 @@ _hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer, p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster)); } - if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + if (pos && !(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d", x+pos[i].x_offset, y+pos[i].y_offset)); @@ -272,6 +283,17 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, *buf_consumed = 0; hb_position_t x = 0, y = 0; + + /* Calculate the advance of the previous glyphs */ + if (pos && (flags & HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES)) + { + for (unsigned int i = 0; i < start; i++) + { + x += pos[i].x_advance; + y += pos[i].y_advance; + } + } + for (unsigned int i = start; i < end; i++) { char b[1024]; @@ -297,7 +319,7 @@ _hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer, p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster)); } - if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) + if (pos && !(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS)) { if (x+pos[i].x_offset || y+pos[i].y_offset) p += hb_max (0, snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", x+pos[i].x_offset, y+pos[i].y_offset)); @@ -417,9 +439,9 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, * A human-readable, plain text format. * The serialized glyphs will look something like: * - * ``` + * |[ * [uni0651=0@518,0+0|uni0628=0+1897] - * ``` + * ]| * * - The serialized glyphs are delimited with `[` and `]`. * - Glyphs are separated with `|` @@ -435,10 +457,10 @@ _hb_buffer_serialize_unicode_text (hb_buffer_t *buffer, * A machine-readable, structured format. * The serialized glyphs will look something like: * - * ``` + * |[ * [{"g":"uni0651","cl":0,"dx":518,"dy":0,"ax":0,"ay":0}, - * {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}] - * ``` + * {"g":"uni0628","cl":0,"dx":0,"dy":0,"ax":1897,"ay":0}] + * ]| * * Each glyph is a JSON object, with the following properties: * - `g`: the glyph name or glyph index if @@ -530,9 +552,9 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, * A human-readable, plain text format. * The serialized codepoints will look something like: * - * ``` + * |[ *   - * ``` + * ]| * * - Glyphs are separated with `|` * - Unicode codepoints are expressed as zero-padded four (or more) @@ -550,9 +572,9 @@ hb_buffer_serialize_glyphs (hb_buffer_t *buffer, * * For example: * - * ``` + * |[ * [{u:1617,cl:0},{u:1576,cl:1}] - * ``` + * ]| * * Return value: * The number of serialized items. diff --git a/thirdparty/harfbuzz/src/hb-buffer.cc b/thirdparty/harfbuzz/src/hb-buffer.cc index 00c004e8b7..3da6577bd1 100644 --- a/thirdparty/harfbuzz/src/hb-buffer.cc +++ b/thirdparty/harfbuzz/src/hb-buffer.cc @@ -547,12 +547,6 @@ void hb_buffer_t::merge_clusters_impl (unsigned int start, unsigned int end) { - if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level)) - { - unsafe_to_break (start, end); - return; - } - max_ops -= end - start; if (unlikely (max_ops < 0)) successful = false; @@ -581,15 +575,9 @@ hb_buffer_t::merge_clusters_impl (unsigned int start, set_cluster (info[i], cluster); } void -hb_buffer_t::merge_out_clusters (unsigned int start, - unsigned int end) +hb_buffer_t::merge_out_clusters_impl (unsigned int start, + unsigned int end) { - if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level)) - return; - - if (unlikely (end - start < 2)) - return; - max_ops -= end - start; if (unlikely (max_ops < 0)) successful = false; @@ -972,6 +960,9 @@ void hb_buffer_set_content_type (hb_buffer_t *buffer, hb_buffer_content_type_t content_type) { + if (unlikely (hb_object_is_immutable (buffer))) + return; + buffer->content_type = content_type; } @@ -2290,6 +2281,22 @@ hb_buffer_diff (hb_buffer_t *buffer, * Debugging. */ +void +hb_buffer_t::changed () +{ +#ifdef HB_NO_BUFFER_MESSAGE + return; +#else + if (!message_depth) + return; + + if (changed_func) + changed_func (this, changed_data); + else + update_digest (); +#endif +} + #ifndef HB_NO_BUFFER_MESSAGE /** * hb_buffer_set_message_func: @@ -2307,7 +2314,8 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, hb_buffer_message_func_t func, void *user_data, hb_destroy_func_t destroy) { - if (unlikely (hb_object_is_immutable (buffer))) + if (unlikely (hb_object_is_immutable (buffer)) || + unlikely (buffer->message_depth)) { if (destroy) destroy (user_data); @@ -2327,6 +2335,23 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, buffer->message_destroy = nullptr; } } +/** + * hb_buffer_changed: + * @buffer: An #hb_buffer_t + * + * Called by a message callback after modifying buffer glyph indices, + * to update internal caches. + * + * If not called from inside a message callback, does nothing. + * + * Since: 13.0.0 + **/ +void +hb_buffer_changed (hb_buffer_t *buffer) +{ + buffer->changed (); +} + bool hb_buffer_t::message_impl (hb_font_t *font, const char *fmt, va_list ap) { diff --git a/thirdparty/harfbuzz/src/hb-buffer.h b/thirdparty/harfbuzz/src/hb-buffer.h index 705b767b66..f534f9a3a7 100644 --- a/thirdparty/harfbuzz/src/hb-buffer.h +++ b/thirdparty/harfbuzz/src/hb-buffer.h @@ -495,7 +495,7 @@ typedef enum { (1u << HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES)))) /** - * HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS + * HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS: * @level: #hb_buffer_cluster_level_t to test * * Tests whether a cluster level does not group cluster values by graphemes. @@ -505,7 +505,7 @@ typedef enum { */ #define HB_BUFFER_CLUSTER_LEVEL_IS_CHARACTERS(level) \ ((bool) ((1u << (unsigned) (level)) & \ - ((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARCATERS) | \ + ((1u << HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS) | \ (1u << HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)))) HB_EXTERN void @@ -672,7 +672,12 @@ hb_buffer_normalize_glyphs (hb_buffer_t *buffer); * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_EXTENTS: serialize glyph extents. * @HB_BUFFER_SERIALIZE_FLAG_GLYPH_FLAGS: serialize glyph flags. Since: 1.5.0 * @HB_BUFFER_SERIALIZE_FLAG_NO_ADVANCES: do not serialize glyph advances, - * glyph offsets will reflect absolute glyph positions. Since: 1.8.0 + * glyph offsets will reflect absolute glyph positions. Since: 1.8.0. + * Note: when this flag is used with a partial range of the buffer (i.e. + * @start is not 0), calculating the absolute positions has a cost + * proportional to @start. If the buffer is serialized in many small + * chunks, this can lead to quadratic behavior. It is recommended to + * use a larger @buf_size to minimize this cost. * @HB_BUFFER_SERIALIZE_FLAG_DEFINED: All currently defined flags. Since: 4.4.0 * * Flags that control what glyph information are serialized in hb_buffer_serialize_glyphs(). @@ -865,6 +870,9 @@ hb_buffer_set_message_func (hb_buffer_t *buffer, hb_buffer_message_func_t func, void *user_data, hb_destroy_func_t destroy); +HB_EXTERN void +hb_buffer_changed (hb_buffer_t *buffer); + HB_END_DECLS diff --git a/thirdparty/harfbuzz/src/hb-buffer.hh b/thirdparty/harfbuzz/src/hb-buffer.hh index 52fac52465..770beeee3a 100644 --- a/thirdparty/harfbuzz/src/hb-buffer.hh +++ b/thirdparty/harfbuzz/src/hb-buffer.hh @@ -130,9 +130,13 @@ struct hb_buffer_t */ #ifndef HB_NO_BUFFER_MESSAGE + typedef void (*changed_func_t) (hb_buffer_t *buffer, void *user_data); + hb_buffer_message_func_t message_func; void *message_data; hb_destroy_func_t message_destroy; + changed_func_t changed_func; + void *changed_data; unsigned message_depth; /* How deeply are we inside a message callback? */ #else static constexpr unsigned message_depth = 0u; @@ -398,10 +402,42 @@ struct hb_buffer_t { if (end - start < 2) return; + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level)) + { + unsafe_to_break (start, end); + return; + } + merge_clusters_impl (start, end); + } + void merge_grapheme_clusters (unsigned int start, unsigned int end) + { + if (end - start < 2) + return; + if (!HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (cluster_level)) + { + unsafe_to_break (start, end); + return; + } merge_clusters_impl (start, end); } HB_INTERNAL void merge_clusters_impl (unsigned int start, unsigned int end); - HB_INTERNAL void merge_out_clusters (unsigned int start, unsigned int end); + void merge_out_clusters (unsigned int start, unsigned int end) + { + if (end - start < 2) + return; + if (!HB_BUFFER_CLUSTER_LEVEL_IS_MONOTONE (cluster_level)) + return; + merge_out_clusters_impl (start, end); + } + void merge_out_grapheme_clusters (unsigned int start, unsigned int end) + { + if (end - start < 2) + return; + if (!HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (cluster_level)) + return; + merge_out_clusters_impl (start, end); + } + HB_INTERNAL void merge_out_clusters_impl (unsigned int start, unsigned int end); /* Merge clusters for deleting current glyph, and skip it. */ HB_INTERNAL void delete_glyph (); HB_INTERNAL void delete_glyphs_inplace (bool (*filter) (const hb_glyph_info_t *info)); @@ -605,6 +641,7 @@ struct hb_buffer_t #endif } HB_INTERNAL bool message_impl (hb_font_t *font, const char *fmt, va_list ap) HB_PRINTF_FUNC(3, 0); + HB_INTERNAL void changed (); static void set_cluster (hb_glyph_info_t &inf, unsigned int cluster, unsigned int mask = 0) diff --git a/thirdparty/harfbuzz/src/hb-cairo.cc b/thirdparty/harfbuzz/src/hb-cairo.cc index 76aebaab25..23ebd6bfc3 100644 --- a/thirdparty/harfbuzz/src/hb-cairo.cc +++ b/thirdparty/harfbuzz/src/hb-cairo.cc @@ -602,10 +602,10 @@ hb_cairo_render_glyph (cairo_scaled_font_t *scaled_font, hb_position_t x_scale, y_scale; hb_font_get_scale (font, &x_scale, &y_scale); + cairo_scale (cr, +1. / (x_scale ? x_scale : 1), -1. / (y_scale ? y_scale : 1)); - if (hb_font_draw_glyph_or_fail (font, glyph, hb_cairo_draw_get_funcs (), cr)) cairo_fill (cr); @@ -882,7 +882,6 @@ hb_cairo_font_face_get_scale_factor (cairo_font_face_t *font_face) &hb_cairo_scale_factor_user_data_key); } - /** * hb_cairo_glyphs_from_buffer: * @buffer: a #hb_buffer_t containing glyphs diff --git a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh index b0491385f4..df90fb99a0 100644 --- a/thirdparty/harfbuzz/src/hb-cff-interp-common.hh +++ b/thirdparty/harfbuzz/src/hb-cff-interp-common.hh @@ -344,25 +344,25 @@ struct cff_stack_t { ELEM& operator [] (unsigned int i) { - if (unlikely (i >= count)) + if (unlikely (i >= length)) { set_error (); return Crap (ELEM); } - return elements[i]; + return arrayZ[i]; } void push (const ELEM &v) { - if (likely (count < LIMIT)) - elements[count++] = v; + if (likely (length < LIMIT)) + arrayZ[length++] = v; else set_error (); } ELEM &push () { - if (likely (count < LIMIT)) - return elements[count++]; + if (likely (length < LIMIT)) + return arrayZ[length++]; else { set_error (); @@ -372,8 +372,8 @@ struct cff_stack_t ELEM& pop () { - if (likely (count > 0)) - return elements[--count]; + if (likely (length > 0)) + return arrayZ[--length]; else { set_error (); @@ -382,45 +382,44 @@ struct cff_stack_t } void pop (unsigned int n) { - if (likely (count >= n)) - count -= n; + if (likely (length >= n)) + length -= n; else set_error (); } const ELEM& peek () { - if (unlikely (count == 0)) + if (unlikely (length == 0)) { set_error (); return Null (ELEM); } - return elements[count - 1]; + return arrayZ[length - 1]; } void unpop () { - if (likely (count < LIMIT)) - count++; + if (likely (length < LIMIT)) + length++; else set_error (); } - void clear () { count = 0; } + void clear () { length = 0; } bool in_error () const { return (error); } void set_error () { error = true; } - unsigned int get_count () const { return count; } - bool is_empty () const { return !count; } + unsigned int get_count () const { return length; } + bool is_empty () const { return !length; } hb_array_t sub_array (unsigned start, unsigned length) const - { return hb_array_t (elements).sub_array (start, length); } + { return hb_array_t (arrayZ).sub_array (start, length); } - private: bool error = false; - unsigned int count = 0; - ELEM elements[LIMIT]; + unsigned int length = 0; + ELEM arrayZ[LIMIT]; }; /* argument stack */ diff --git a/thirdparty/harfbuzz/src/hb-cff-specializer.hh b/thirdparty/harfbuzz/src/hb-cff-specializer.hh new file mode 100644 index 0000000000..5b4dce5d03 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-cff-specializer.hh @@ -0,0 +1,273 @@ +/* + * CFF CharString Specializer + * + * Optimizes CharString bytecode by using specialized operators + * (hlineto, vlineto, hhcurveto, etc.) to save bytes and respects + * CFF1 stack limit (48 values). + * + * Based on fontTools.cffLib.specializer + */ + +#ifndef HB_CFF_SPECIALIZER_HH +#define HB_CFF_SPECIALIZER_HH + +#include "hb.hh" +#include "hb-cff-interp-cs-common.hh" + +namespace CFF { + +/* CharString command representation - forward declared in hb-subset-cff-common.hh */ + +/* Check if a value is effectively zero */ +static inline bool +is_zero (const number_t &n) +{ + return n.to_int () == 0; +} + +/* Generalize CharString commands to canonical form + * + * Converts all operators to their general forms and breaks down + * multi-segment operators into single segments. This ensures we + * start from a clean baseline before specialization. + * + * Based on fontTools.cffLib.specializer.generalizeCommands + */ +static void +generalize_commands (hb_vector_t &commands) +{ + hb_vector_t result; + result.alloc (commands.length * 2); /* Estimate: might expand */ + + for (unsigned i = 0; i < commands.length; i++) + { + auto &cmd = commands[i]; + + switch (cmd.op) + { + case OpCode_hmoveto: + case OpCode_vmoveto: + { + /* Convert to rmoveto with explicit dx,dy */ + cs_command_t gen (OpCode_rmoveto); + gen.args.alloc (2); + + if (cmd.op == OpCode_hmoveto && cmd.args.length >= 1) + { + gen.args.push (cmd.args[0]); /* dx */ + number_t zero; zero.set_int (0); + gen.args.push (zero); /* dy = 0 */ + } + else if (cmd.op == OpCode_vmoveto && cmd.args.length >= 1) + { + number_t zero; zero.set_int (0); + gen.args.push (zero); /* dx = 0 */ + gen.args.push (cmd.args[0]); /* dy */ + } + result.push (gen); + break; + } + + case OpCode_hlineto: + case OpCode_vlineto: + { + /* Convert h/v lineto to rlineto, breaking into single segments + * hlineto alternates: dx1 (→ dx1,0) dy1 (→ 0,dy1) dx2 (→ dx2,0) ... + * vlineto alternates: dy1 (→ 0,dy1) dx1 (→ dx1,0) dy2 (→ 0,dy2) ... */ + bool is_h = (cmd.op == OpCode_hlineto); + number_t zero; zero.set_int (0); + + for (unsigned j = 0; j < cmd.args.length; j++) + { + cs_command_t seg (OpCode_rlineto); + seg.args.alloc (2); + + bool is_horizontal = is_h ? (j % 2 == 0) : (j % 2 == 1); + if (is_horizontal) + { + seg.args.push (cmd.args[j]); /* dx */ + seg.args.push (zero); /* dy = 0 */ + } + else + { + seg.args.push (zero); /* dx = 0 */ + seg.args.push (cmd.args[j]); /* dy */ + } + result.push (seg); + } + break; + } + + case OpCode_rlineto: + { + /* Break into single segments (dx,dy pairs) */ + for (unsigned j = 0; j + 1 < cmd.args.length; j += 2) + { + cs_command_t seg (OpCode_rlineto); + seg.args.alloc (2); + seg.args.push (cmd.args[j]); + seg.args.push (cmd.args[j + 1]); + result.push (seg); + } + break; + } + + case OpCode_rrcurveto: + { + /* Break into single segments (6 args each) */ + for (unsigned j = 0; j + 5 < cmd.args.length; j += 6) + { + cs_command_t seg (OpCode_rrcurveto); + seg.args.alloc (6); + for (unsigned k = 0; k < 6; k++) + seg.args.push (cmd.args[j + k]); + result.push (seg); + } + break; + } + + default: + /* Keep other operators as-is */ + result.push (cmd); + break; + } + } + + /* Replace commands with generalized result */ + commands.resize (0); + for (unsigned i = 0; i < result.length; i++) + commands.push (result[i]); +} + +/* Specialize CharString commands to optimize bytecode size + * + * Follows fontTools approach: + * 0. Generalize: Break down to canonical single-segment form + * 1. Specialize: Convert rmoveto/rlineto to h/v variants when dx or dy is zero + * 2. Combine: Merge adjacent compatible operators + * 3. Enforce: Respect maxstack limit (default 48 for CFF1) + * + * This ensures we never exceed stack depth while optimizing bytecode. + */ +static void +specialize_commands (hb_vector_t &commands, + unsigned maxstack = 48) +{ + if (commands.length == 0) return; + + /* Pass 0: Generalize to canonical form (fontTools does this first) */ + generalize_commands (commands); + + /* Pass 1: Specialize rmoveto/rlineto into h/v variants */ + for (unsigned i = 0; i < commands.length; i++) + { + auto &cmd = commands[i]; + + if ((cmd.op == OpCode_rmoveto || cmd.op == OpCode_rlineto) && + cmd.args.length == 2) + { + bool dx_zero = is_zero (cmd.args[0]); + bool dy_zero = is_zero (cmd.args[1]); + + if (dx_zero && !dy_zero) + { + /* Vertical movement (dx=0): keep only dy */ + cmd.op = (cmd.op == OpCode_rmoveto) ? OpCode_vmoveto : OpCode_vlineto; + /* Shift dy to position 0 */ + cmd.args[0] = cmd.args[1]; + cmd.args.resize (1); + } + else if (!dx_zero && dy_zero) + { + /* Horizontal movement (dy=0): keep only dx */ + cmd.op = (cmd.op == OpCode_rmoveto) ? OpCode_hmoveto : OpCode_hlineto; + cmd.args.resize (1); /* Keep only dx */ + } + /* else: both zero or both non-zero, keep as rmoveto/rlineto */ + } + } + + /* Pass 2: Combine adjacent hlineto/vlineto operators + * hlineto can take multiple args alternating with vlineto + * This saves operator bytes */ + for (int i = (int)commands.length - 1; i > 0; i--) + { + auto &cmd = commands[i]; + auto &prev = commands[i-1]; + + /* Combine adjacent hlineto + vlineto or vlineto + hlineto */ + if ((prev.op == OpCode_hlineto && cmd.op == OpCode_vlineto) || + (prev.op == OpCode_vlineto && cmd.op == OpCode_hlineto)) + { + /* Check stack depth */ + unsigned combined_args = prev.args.length + cmd.args.length; + if (combined_args < maxstack) + { + /* Merge into first command, keep its operator */ + for (unsigned j = 0; j < cmd.args.length; j++) + prev.args.push (cmd.args[j]); + commands.remove_ordered (i); + i++; /* Adjust for removed element */ + } + } + } + + /* Pass 3: Combine adjacent identical operators */ + for (int i = (int)commands.length - 1; i > 0; i--) + { + auto &cmd = commands[i]; + auto &prev = commands[i-1]; + + /* Combine same operators (e.g., rlineto + rlineto) */ + if (prev.op == cmd.op && + (cmd.op == OpCode_rlineto || cmd.op == OpCode_hlineto || + cmd.op == OpCode_vlineto || cmd.op == OpCode_rrcurveto)) + { + /* Check stack depth */ + unsigned combined_args = prev.args.length + cmd.args.length; + if (combined_args < maxstack) + { + /* Merge args */ + for (unsigned j = 0; j < cmd.args.length; j++) + prev.args.push (cmd.args[j]); + commands.remove_ordered (i); + i++; /* Adjust for removed element */ + } + } + } +} + +/* Encode commands back to binary CharString */ +static bool +encode_commands (const hb_vector_t &commands, + str_buff_t &output) +{ + for (const auto &cmd : commands) + { + str_encoder_t encoder (output); + + /* Encode arguments */ + for (const auto &arg : cmd.args) + encoder.encode_num_cs (arg); + + /* Encode operator */ + if (cmd.op != OpCode_Invalid) + encoder.encode_op (cmd.op); + + /* hintmask/cntrmask are followed by raw mask bytes. */ + if (cmd.op == OpCode_hintmask || cmd.op == OpCode_cntrmask) + { + for (const auto &byte : cmd.mask_bytes) + encoder.encode_byte (byte); + } + + if (encoder.in_error ()) + return false; + } + + return true; +} + +} /* namespace CFF */ + +#endif /* HB_CFF_SPECIALIZER_HH */ diff --git a/thirdparty/harfbuzz/src/hb-cff-width-optimizer.hh b/thirdparty/harfbuzz/src/hb-cff-width-optimizer.hh new file mode 100644 index 0000000000..e105dae4a5 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-cff-width-optimizer.hh @@ -0,0 +1,207 @@ +/* + * CFF Width Optimizer + * + * Determines optimal defaultWidthX and nominalWidthX values + * to minimize CharString byte cost. + * + * Based on fontTools.cffLib.width + */ + +#ifndef HB_CFF_WIDTH_OPTIMIZER_HH +#define HB_CFF_WIDTH_OPTIMIZER_HH + +#include "hb.hh" + +namespace CFF { + +/* Calculate byte cost for encoding a width delta */ +static inline unsigned +width_delta_cost (int delta) +{ + delta = abs (delta); + if (delta <= 107) return 1; + if (delta <= 1131) return 2; + return 5; +} + +/* Cumulative sum forward */ +static void +cumsum_forward (const hb_hashmap_t &freq, + unsigned min_w, unsigned max_w, + hb_vector_t &cumsum) +{ + cumsum.resize (max_w - min_w + 1); + unsigned v = 0; + for (unsigned x = min_w; x <= max_w; x++) + { + v += freq.get (x); + cumsum[x - min_w] = v; + } +} + +/* Cumulative max forward */ +static void +cummax_forward (const hb_hashmap_t &freq, + unsigned min_w, unsigned max_w, + hb_vector_t &cummax) +{ + cummax.resize (max_w - min_w + 1); + unsigned v = 0; + for (unsigned x = min_w; x <= max_w; x++) + { + v = hb_max (v, freq.get (x)); + cummax[x - min_w] = v; + } +} + +/* Cumulative sum backward */ +static void +cumsum_backward (const hb_hashmap_t &freq, + unsigned min_w, unsigned max_w, + hb_vector_t &cumsum) +{ + cumsum.resize (max_w - min_w + 1); + unsigned v = 0; + for (int x = (int) max_w; x >= (int) min_w; x--) + { + v += freq.get ((unsigned) x); + cumsum[x - min_w] = v; + } +} + +/* Cumulative max backward */ +static void +cummax_backward (const hb_hashmap_t &freq, + unsigned min_w, unsigned max_w, + hb_vector_t &cummax) +{ + cummax.resize (max_w - min_w + 1); + unsigned v = 0; + for (int x = (int) max_w; x >= (int) min_w; x--) + { + v = hb_max (v, freq.get ((unsigned) x)); + cummax[x - min_w] = v; + } +} + +/* Helper to safely get cumulative value with bounds checking */ +static inline unsigned +safe_get (const hb_vector_t &vec, int x, unsigned min_w, unsigned max_w) +{ + if (x < (int) min_w || x > (int) max_w) return 0; + return vec[x - min_w]; +} + +/* Optimize defaultWidthX and nominalWidthX from a list of widths + * O(UPEM+numGlyphs) algorithm from fontTools.cffLib.width */ +static void +optimize_widths (const hb_vector_t &width_list, + unsigned &default_width, + unsigned &nominal_width) +{ + if (width_list.length == 0) + { + default_width = nominal_width = 0; + return; + } + + /* Build frequency map */ + hb_hashmap_t widths; + unsigned min_w = width_list[0]; + unsigned max_w = width_list[0]; + + for (unsigned w : width_list) + { + widths.set (w, widths.get (w) + 1); + min_w = hb_min (min_w, w); + max_w = hb_max (max_w, w); + } + + /* Cumulative sum/max forward/backward */ + hb_vector_t cumFrqU, cumMaxU, cumFrqD, cumMaxD; + cumsum_forward (widths, min_w, max_w, cumFrqU); + cummax_forward (widths, min_w, max_w, cumMaxU); + cumsum_backward (widths, min_w, max_w, cumFrqD); + cummax_backward (widths, min_w, max_w, cumMaxD); + + /* Cost per nominal choice, without default consideration */ + auto nomnCost = [&] (unsigned x) -> unsigned { + return safe_get (cumFrqU, x, min_w, max_w) + + safe_get (cumFrqU, x - 108, min_w, max_w) + + safe_get (cumFrqU, x - 1132, min_w, max_w) * 3 + + safe_get (cumFrqD, x, min_w, max_w) + + safe_get (cumFrqD, x + 108, min_w, max_w) + + safe_get (cumFrqD, x + 1132, min_w, max_w) * 3 - + widths.get (x); + }; + + /* Cost-saving per nominal choice, by best default choice */ + auto dfltCost = [&] (unsigned x) -> unsigned { + unsigned u = hb_max (hb_max (safe_get (cumMaxU, x, min_w, max_w), + safe_get (cumMaxU, x - 108, min_w, max_w) * 2), + safe_get (cumMaxU, x - 1132, min_w, max_w) * 5); + unsigned d = hb_max (hb_max (safe_get (cumMaxD, x, min_w, max_w), + safe_get (cumMaxD, x + 108, min_w, max_w) * 2), + safe_get (cumMaxD, x + 1132, min_w, max_w) * 5); + return hb_max (u, d); + }; + + /* Find best nominal */ + unsigned best_nominal = min_w; + unsigned best_cost = nomnCost (min_w) - dfltCost (min_w); + + for (unsigned x = min_w + 1; x <= max_w; x++) + { + unsigned cost = nomnCost (x) - dfltCost (x); + if (cost < best_cost) + { + best_cost = cost; + best_nominal = x; + } + } + + /* Work back the best default */ + unsigned best_default = best_nominal; + unsigned best_default_cost = (unsigned) -1; + + /* Check candidates around best_nominal */ + int candidates[] = { + (int) best_nominal, + (int) best_nominal - 108, + (int) best_nominal - 1132, + (int) best_nominal + 108, + (int) best_nominal + 1132 + }; + + for (int candidate : candidates) + { + if (candidate < (int) min_w || candidate > (int) max_w) + continue; + + /* Compute actual cost with this default */ + unsigned cost = 0; + for (auto kv : widths.iter ()) + { + unsigned w = kv.first; + unsigned freq = kv.second; + + if (w == (unsigned) candidate) + continue; + + cost += freq * width_delta_cost ((int) w - (int) best_nominal); + } + + if (cost < best_default_cost) + { + best_default_cost = cost; + best_default = (unsigned) candidate; + } + } + + default_width = best_default; + nominal_width = best_nominal; +} + +} /* namespace CFF */ + +#endif /* HB_CFF_WIDTH_OPTIMIZER_HH */ diff --git a/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh index 27a0c8cf92..c44b024762 100644 --- a/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh +++ b/thirdparty/harfbuzz/src/hb-cff2-interp-cs.hh @@ -291,7 +291,7 @@ struct cff2_cs_opset_t : cs_opset_t, PAR for (unsigned int i = 0; i < n; i++) { const hb_array_t blends = env.argStack.sub_array (start + n + (i * k), k); - process_arg_blend (env, env.argStack[start + i], blends, n, i); + process_arg_blend (env, env.argStack.arrayZ[start + i], blends, n, i); } /* pop off blend values leaving default values now adorned with blend values */ diff --git a/thirdparty/harfbuzz/src/hb-config.hh b/thirdparty/harfbuzz/src/hb-config.hh index c522eeea2e..b508c4e797 100644 --- a/thirdparty/harfbuzz/src/hb-config.hh +++ b/thirdparty/harfbuzz/src/hb-config.hh @@ -43,6 +43,7 @@ #ifdef HB_TINY #define HB_LEAN #define HB_MINI +#define HB_NO_SVG #define HB_OPTIMIZE_SIZE #define HB_OPTIMIZE_SIZE_MORE #define HB_MINIMIZE_MEMORY_USAGE diff --git a/thirdparty/harfbuzz/src/hb-coretext-shape.cc b/thirdparty/harfbuzz/src/hb-coretext-shape.cc index ebd0bc1326..89bf090bba 100644 --- a/thirdparty/harfbuzz/src/hb-coretext-shape.cc +++ b/thirdparty/harfbuzz/src/hb-coretext-shape.cc @@ -199,7 +199,7 @@ _hb_coretext_shape (hb_shape_plan_t *shape_plan, hb_glyph_info_t *info = buffer->info; for (unsigned int i = 1; i < count; i++) if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (info[i].codepoint))) - buffer->merge_clusters (i - 1, i + 1); + buffer->merge_grapheme_clusters (i - 1, i + 1); } hb_vector_t range_records; diff --git a/thirdparty/harfbuzz/src/hb-cplusplus.hh b/thirdparty/harfbuzz/src/hb-cplusplus.hh index b6b5c8f781..14944eeeae 100644 --- a/thirdparty/harfbuzz/src/hb-cplusplus.hh +++ b/thirdparty/harfbuzz/src/hb-cplusplus.hh @@ -138,52 +138,56 @@ struct vtable_t static constexpr auto get_user_data = _get_user_data; }; -#define HB_DEFINE_VTABLE(name) \ +#define HB_DEFINE_VTABLE(name, empty) \ template<> \ struct vtable \ : vtable_t {} -HB_DEFINE_VTABLE (buffer); -HB_DEFINE_VTABLE (blob); -HB_DEFINE_VTABLE (face); -HB_DEFINE_VTABLE (font); -HB_DEFINE_VTABLE (font_funcs); -HB_DEFINE_VTABLE (map); -HB_DEFINE_VTABLE (set); -HB_DEFINE_VTABLE (shape_plan); -HB_DEFINE_VTABLE (unicode_funcs); -HB_DEFINE_VTABLE (draw_funcs); -HB_DEFINE_VTABLE (paint_funcs); - -#undef HB_DEFINE_VTABLE +HB_DEFINE_VTABLE (buffer, &hb_buffer_get_empty); +HB_DEFINE_VTABLE (blob, &hb_blob_get_empty); +HB_DEFINE_VTABLE (face, &hb_face_get_empty); +HB_DEFINE_VTABLE (font, &hb_font_get_empty); +HB_DEFINE_VTABLE (font_funcs, &hb_font_funcs_get_empty); +HB_DEFINE_VTABLE (map, &hb_map_get_empty); +HB_DEFINE_VTABLE (set, &hb_set_get_empty); +HB_DEFINE_VTABLE (shape_plan, &hb_shape_plan_get_empty); +HB_DEFINE_VTABLE (unicode_funcs, &hb_unicode_funcs_get_empty); +HB_DEFINE_VTABLE (draw_funcs, &hb_draw_funcs_get_empty); +HB_DEFINE_VTABLE (paint_funcs, &hb_paint_funcs_get_empty); #ifdef HB_SUBSET_H -#define HB_DEFINE_VTABLE(name) \ - template<> \ - struct vtable \ - : vtable_t {} - - -HB_DEFINE_VTABLE (subset_input); -HB_DEFINE_VTABLE (subset_plan); - -#undef HB_DEFINE_VTABLE +HB_DEFINE_VTABLE (subset_input, nullptr); +HB_DEFINE_VTABLE (subset_plan, nullptr); #endif +#ifdef HB_RASTER_H + +HB_DEFINE_VTABLE (raster_image, nullptr); +HB_DEFINE_VTABLE (raster_draw, nullptr); +HB_DEFINE_VTABLE (raster_paint, nullptr); + +#endif + + +#ifdef HB_VECTOR_H + +HB_DEFINE_VTABLE (vector_draw, nullptr); +HB_DEFINE_VTABLE (vector_paint, nullptr); + +#endif + +#undef HB_DEFINE_VTABLE + + } // namespace hb /* Workaround for GCC < 7, see: diff --git a/thirdparty/harfbuzz/src/hb-directwrite.cc b/thirdparty/harfbuzz/src/hb-directwrite.cc index 6e2bd914b6..d941049713 100644 --- a/thirdparty/harfbuzz/src/hb-directwrite.cc +++ b/thirdparty/harfbuzz/src/hb-directwrite.cc @@ -452,6 +452,7 @@ hb_directwrite_font_get_dw_font_face (hb_font_t *font) return (IDWriteFontFace *) (const void *) font->data.directwrite; } +#ifndef HB_DISABLE_DEPRECATED /** * hb_directwrite_font_get_dw_font: @@ -471,3 +472,5 @@ hb_directwrite_font_get_dw_font (hb_font_t *font) } #endif + +#endif diff --git a/thirdparty/harfbuzz/src/hb-limits.hh b/thirdparty/harfbuzz/src/hb-limits.hh index 857e183737..c115f9a866 100644 --- a/thirdparty/harfbuzz/src/hb-limits.hh +++ b/thirdparty/harfbuzz/src/hb-limits.hh @@ -108,5 +108,17 @@ #define HB_MAX_COMPOSITE_OPERATIONS_PER_GLYPH 64 #endif +#ifndef HB_SVG_MAX_PATH_SEGMENTS +#define HB_SVG_MAX_PATH_SEGMENTS 262144 +#endif + +#ifndef HB_SVG_MAX_DOCUMENT_SIZE +#define HB_SVG_MAX_DOCUMENT_SIZE ((size_t) 16 << 20) +#endif + +#ifndef HB_RASTER_MAX_BUFFER_SIZE +#define HB_RASTER_MAX_BUFFER_SIZE ((size_t) 1 << 24) +#endif + #endif /* HB_LIMITS_HH */ diff --git a/thirdparty/harfbuzz/src/hb-number-parser.hh b/thirdparty/harfbuzz/src/hb-number-parser.hh index ec68c3a728..1a9dbba6dd 100644 --- a/thirdparty/harfbuzz/src/hb-number-parser.hh +++ b/thirdparty/harfbuzz/src/hb-number-parser.hh @@ -31,7 +31,7 @@ #include "hb.hh" -#line 32 "hb-number-parser.hh" +#line 35 "hb-number-parser.hh" static const unsigned char _double_parser_trans_keys[] = { 0u, 0u, 43u, 57u, 46u, 57u, 48u, 57u, 43u, 57u, 48u, 57u, 48u, 101u, 48u, 57u, 46u, 101u, 0 @@ -135,12 +135,12 @@ strtod_rl (const char *p, const char **end_ptr /* IN/OUT */) int cs; -#line 132 "hb-number-parser.hh" +#line 139 "hb-number-parser.hh" { cs = double_parser_start; } -#line 135 "hb-number-parser.hh" +#line 144 "hb-number-parser.hh" { int _slen; int _trans; @@ -198,7 +198,7 @@ _resume: exp_overflow = true; } break; -#line 187 "hb-number-parser.hh" +#line 202 "hb-number-parser.hh" } _again: diff --git a/thirdparty/harfbuzz/src/hb-object.hh b/thirdparty/harfbuzz/src/hb-object.hh index 7b34d671fb..3fb387978f 100644 --- a/thirdparty/harfbuzz/src/hb-object.hh +++ b/thirdparty/harfbuzz/src/hb-object.hh @@ -272,6 +272,8 @@ static inline void hb_object_make_immutable (const Type *obj) obj->header.writable = false; } template +static inline void hb_object_fini (Type *obj); +template static inline Type *hb_object_reference (Type *obj) { hb_object_trace (obj, HB_FUNC); @@ -282,7 +284,7 @@ static inline Type *hb_object_reference (Type *obj) return obj; } template -static inline bool hb_object_destroy (Type *obj) +static inline bool hb_object_should_destroy (Type *obj) { hb_object_trace (obj, HB_FUNC); if (unlikely (!obj || obj->header.is_inert ())) @@ -290,12 +292,25 @@ static inline bool hb_object_destroy (Type *obj) assert (hb_object_is_valid (obj)); if (obj->header.ref_count.dec () != 1) return false; + return true; +} +template +static inline void hb_object_actually_destroy (Type *obj) +{ hb_object_fini (obj); if (!std::is_trivially_destructible::value) obj->~Type (); +} +template +static inline bool hb_object_destroy (Type *obj) +{ + if (!hb_object_should_destroy (obj)) + return false; + + hb_object_actually_destroy (obj); return true; } template diff --git a/thirdparty/harfbuzz/src/hb-open-type.hh b/thirdparty/harfbuzz/src/hb-open-type.hh index 095c8c073e..44508e7eb1 100644 --- a/thirdparty/harfbuzz/src/hb-open-type.hh +++ b/thirdparty/harfbuzz/src/hb-open-type.hh @@ -1275,11 +1275,22 @@ struct CFFIndex if (unlikely (!serialize_header (c, +it, data_size, min_off_size))) return_trace (false); unsigned char *ret = c->allocate_size (data_size, false); if (unlikely (!ret)) return_trace (false); + unsigned remaining = data_size; for (const auto &_ : +it) { unsigned len = _.length; + if (!len) continue; + + if (unlikely (len > remaining)) { + // We have more bytes to write then the computed data size, so the size calculation + // must have encountered overflow. + return_trace (c->check_success (false, HB_SERIALIZE_ERROR_INT_OVERFLOW)); + } + + remaining -= len; + if (len <= 1) { *ret++ = *_.arrayZ; @@ -1948,6 +1959,7 @@ struct TupleValues return true; } + public: void skip (unsigned n) { while (n) @@ -1961,6 +1973,7 @@ struct TupleValues } } + private: template void _add_to (hb_array_t out, float scale = 1.0f) { @@ -2035,14 +2048,6 @@ struct TupleValues public: void add_to (hb_array_t out, float scale = 1.0f) { - unsigned n = out.length; - - if (scale == 0.0f) - { - skip (n); - return; - } - #ifndef HB_OPTIMIZE_SIZE // The following branch is supposed to speed things up by avoiding // the multiplication in _add_to<> if scale is 1.0f. diff --git a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh index 53df655ba8..6765c557ff 100644 --- a/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-cmap-table.hh @@ -237,8 +237,6 @@ struct CmapSubtableFormat0 struct CmapSubtableFormat4 { - - template @@ -521,9 +519,10 @@ struct CmapSubtableFormat4 struct accelerator_t { accelerator_t () {} - accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); } + accelerator_t (const CmapSubtableFormat4 *subtable) = delete; - void init (const CmapSubtableFormat4 *subtable) + void init (const CmapSubtableFormat4 *subtable, + unsigned int subtable_data_size) { segCount = subtable->segCountX2 / 2; endCount = subtable->values.arrayZ; @@ -531,7 +530,11 @@ struct CmapSubtableFormat4 idDelta = startCount + segCount; idRangeOffset = idDelta + segCount; glyphIdArray = idRangeOffset + segCount; - glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2; + + unsigned int values_offset = 16 + 8 * segCount; + glyphIdArrayLength = subtable_data_size > values_offset + ? (subtable_data_size - values_offset) / 2 + : 0; } bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const @@ -671,23 +674,36 @@ struct CmapSubtableFormat4 unsigned int glyphIdArrayLength; }; - bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph, + unsigned int subtable_data_size) const { - accelerator_t accel (this); + accelerator_t accel; + accel.init (this, subtable_data_size); return accel.get_glyph_func (&accel, codepoint, glyph); } - void collect_unicodes (hb_set_t *out) const + bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const + { return false; } + + void collect_unicodes (hb_set_t *out, unsigned int subtable_data_size) const { - accelerator_t accel (this); + accelerator_t accel; + accel.init (this, subtable_data_size); accel.collect_unicodes (out); } + void collect_unicodes (hb_set_t *out) const + { collect_unicodes (out, length); } void collect_mapping (hb_set_t *unicodes, /* OUT */ - hb_map_t *mapping /* OUT */) const + hb_map_t *mapping, /* OUT */ + unsigned int subtable_data_size) const { - accelerator_t accel (this); + accelerator_t accel; + accel.init (this, subtable_data_size); accel.collect_mapping (unicodes, mapping); } + void collect_mapping (hb_set_t *unicodes, /* OUT */ + hb_map_t *mapping /* OUT */) const + { collect_mapping (unicodes, mapping, length); } bool sanitize (hb_sanitize_context_t *c) const { @@ -696,14 +712,9 @@ struct CmapSubtableFormat4 return_trace (false); hb_barrier (); - if (unlikely (!c->check_range (this, length))) - return_trace (false); - - return_trace (16 + 4 * (unsigned int) segCountX2 <= length); + return_trace (c->check_range (values, 2 + 4 * segCountX2)); } - - protected: HBUINT16 format; /* Format number is set to 4. */ HBUINT16 length; /* This is the length in bytes of the @@ -1485,11 +1496,14 @@ struct CmapSubtable /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ bool get_glyph (hb_codepoint_t codepoint, - hb_codepoint_t *glyph) const + hb_codepoint_t *glyph, + unsigned int subtable_data_size = 0) const { switch (u.format.v) { case 0: hb_barrier (); return u.format0 .get_glyph (codepoint, glyph); - case 4: hb_barrier (); return u.format4 .get_glyph (codepoint, glyph); + case 4: hb_barrier (); return subtable_data_size + ? u.format4.get_glyph (codepoint, glyph, subtable_data_size) + : false; case 6: hb_barrier (); return u.format6 .get_glyph (codepoint, glyph); case 10: hb_barrier (); return u.format10.get_glyph (codepoint, glyph); case 12: hb_barrier (); return u.format12.get_glyph (codepoint, glyph); @@ -1498,11 +1512,17 @@ struct CmapSubtable default: return false; } } - void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const + void collect_unicodes (hb_set_t *out, + unsigned int num_glyphs = UINT_MAX, + unsigned int subtable_data_size = 0) const { switch (u.format.v) { case 0: hb_barrier (); u.format0 .collect_unicodes (out); return; - case 4: hb_barrier (); u.format4 .collect_unicodes (out); return; + case 4: hb_barrier (); if (subtable_data_size) + u.format4.collect_unicodes (out, subtable_data_size); + else + u.format4.collect_unicodes (out); + return; case 6: hb_barrier (); u.format6 .collect_unicodes (out); return; case 10: hb_barrier (); u.format10.collect_unicodes (out); return; case 12: hb_barrier (); u.format12.collect_unicodes (out, num_glyphs); return; @@ -1514,11 +1534,16 @@ struct CmapSubtable void collect_mapping (hb_set_t *unicodes, /* OUT */ hb_map_t *mapping, /* OUT */ - unsigned num_glyphs = UINT_MAX) const + unsigned num_glyphs = UINT_MAX, + unsigned int subtable_data_size = 0) const { switch (u.format.v) { case 0: hb_barrier (); u.format0 .collect_mapping (unicodes, mapping); return; - case 4: hb_barrier (); u.format4 .collect_mapping (unicodes, mapping); return; + case 4: hb_barrier (); if (subtable_data_size) + u.format4.collect_mapping (unicodes, mapping, subtable_data_size); + else + u.format4.collect_mapping (unicodes, mapping); + return; case 6: hb_barrier (); u.format6 .collect_mapping (unicodes, mapping); return; case 10: hb_barrier (); u.format10.collect_mapping (unicodes, mapping); return; case 12: hb_barrier (); u.format12.collect_mapping (unicodes, mapping, num_glyphs); return; @@ -1633,7 +1658,7 @@ struct EncodingRecord CmapSubtable *cmapsubtable = c->push (); unsigned origin_length = c->length (); cmapsubtable->serialize (c, it, format, plan, &(base+subtable)); - if (c->length () - origin_length > 0 && !c->in_error()) *objidx = c->pop_pack (); + if (c->length () > origin_length && !c->in_error()) *objidx = c->pop_pack (); else c->pop_discard (); } @@ -1662,6 +1687,7 @@ struct SubtableUnicodesCache { private: hb_blob_ptr_t base_blob; const char* base; + unsigned int table_length; hb_hashmap_t> cached_unicodes; public: @@ -1686,15 +1712,18 @@ struct SubtableUnicodesCache { hb_free (cache); } - SubtableUnicodesCache(const void* cmap_base) + SubtableUnicodesCache(const void* cmap_base, + unsigned int table_length_ = 0) : base_blob(), base ((const char*) cmap_base), + table_length (table_length_), cached_unicodes () {} SubtableUnicodesCache(hb_blob_ptr_t base_blob_) : base_blob(base_blob_), base ((const char *) base_blob.get()), + table_length (base_blob.get_length ()), cached_unicodes () {} @@ -1725,7 +1754,10 @@ struct SubtableUnicodesCache { if (unlikely (s->in_error ())) return hb_set_get_empty (); - (base+record->subtable).collect_unicodes (s); + unsigned int subtable_data_size = record->subtable < table_length + ? table_length - (unsigned int) record->subtable + : 0; + (base+record->subtable).collect_unicodes (s, UINT_MAX, subtable_data_size); if (unlikely (!cached_unicodes.set ((unsigned) ((const char *) record - base), hb::unique_ptr {s}))) return hb_set_get_empty (); @@ -1737,7 +1769,7 @@ struct SubtableUnicodesCache { }; -static inline uint_fast16_t +static inline uint16_t _hb_symbol_pua_map (unsigned codepoint) { if (codepoint <= 0x00FFu) @@ -1784,7 +1816,8 @@ struct cmap EncodingRecIter encodingrec_iter, const void *base, hb_subset_plan_t *plan, - bool drop_format_4 = false) + bool drop_format_4 = false, + unsigned int source_table_length = 0) { if (unlikely (!c->extend_min ((*this)))) return false; this->version = 0; @@ -1792,7 +1825,7 @@ struct cmap unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; auto snap = c->snapshot (); - SubtableUnicodesCache local_unicodes_cache (base); + SubtableUnicodesCache local_unicodes_cache (base, source_table_length); const SubtableUnicodesCache* unicodes_cache = &local_unicodes_cache; if (plan->accelerator && @@ -1821,7 +1854,8 @@ struct cmap encodingrec_iter, base, plan, - true); + true, + source_table_length); } } @@ -1838,9 +1872,11 @@ struct cmap } else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); } - c->check_assign(this->encodingRecord.len, - (c->length () - cmap::min_size)/EncodingRecord::static_size, - HB_SERIALIZE_ERROR_INT_OVERFLOW); + unsigned length = c->length (); + unsigned available = length > cmap::min_size ? length - cmap::min_size : 0; + c->check_assign(this->encodingRecord.len, + available / EncodingRecord::static_size, + HB_SERIALIZE_ERROR_INT_OVERFLOW); // Fail if format 4 was dropped and there is no cmap12. return !drop_format_4 || format12objidx; @@ -1955,7 +1991,9 @@ struct cmap it, encodingrec_iter, this, - c->plan)); + c->plan, + false, + c->source_blob->length)); } const CmapSubtable *find_best_subtable (bool *symbol = nullptr, @@ -2034,32 +2072,60 @@ struct cmap this->get_glyph_data = subtable; #ifndef HB_NO_CMAP_LEGACY_SUBTABLES + bool is_format4 = subtable->u.format.v == 4; + auto set_format4_getter = [this] (bool (*func) (const void *, + hb_codepoint_t, + hb_codepoint_t *)) + { + this->format4_accel.init (&this->subtable->u.format4, + get_subtable_data_size (this->subtable)); + this->get_glyph_data = &this->format4_accel; + this->get_glyph_funcZ = func; + }; if (unlikely (symbol)) { switch ((unsigned) face->table.OS2->get_font_page ()) { case OS2::font_page_t::FONT_PAGE_NONE: - this->get_glyph_funcZ = get_glyph_from_symbol; + if (is_format4) + set_format4_getter (get_glyph_from_symbol); + else + this->get_glyph_funcZ = get_glyph_from_symbol; break; #ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK case OS2::font_page_t::FONT_PAGE_SIMP_ARABIC: - this->get_glyph_funcZ = get_glyph_from_symbol; + if (is_format4) + set_format4_getter (get_glyph_from_symbol); + else + this->get_glyph_funcZ = get_glyph_from_symbol; break; case OS2::font_page_t::FONT_PAGE_TRAD_ARABIC: - this->get_glyph_funcZ = get_glyph_from_symbol; + if (is_format4) + set_format4_getter (get_glyph_from_symbol); + else + this->get_glyph_funcZ = get_glyph_from_symbol; break; #endif default: - this->get_glyph_funcZ = get_glyph_from; + if (is_format4) + set_format4_getter (get_glyph_from); + else + this->get_glyph_funcZ = get_glyph_from; break; } } else if (unlikely (macroman)) { - this->get_glyph_funcZ = get_glyph_from_macroman; + if (is_format4) + set_format4_getter (get_glyph_from_macroman); + else + this->get_glyph_funcZ = get_glyph_from_macroman; } else if (unlikely (mac)) { - this->get_glyph_funcZ = get_glyph_from_ascii; + if (is_format4) + set_format4_getter (get_glyph_from_ascii); + else + this->get_glyph_funcZ = get_glyph_from_ascii; } else #endif @@ -2074,7 +2140,8 @@ struct cmap break; case 4: { - this->format4_accel.init (&subtable->u.format4); + this->format4_accel.init (&subtable->u.format4, + get_subtable_data_size (subtable)); this->get_glyph_data = &this->format4_accel; this->get_glyph_funcZ = this->format4_accel.get_glyph_func; break; @@ -2155,10 +2222,10 @@ struct cmap } void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const - { subtable->collect_unicodes (out, num_glyphs); } + { subtable->collect_unicodes (out, num_glyphs, get_subtable_data_size (subtable)); } void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping, unsigned num_glyphs = UINT_MAX) const - { subtable->collect_mapping (unicodes, mapping, num_glyphs); } + { subtable->collect_mapping (unicodes, mapping, num_glyphs, get_subtable_data_size (subtable)); } void collect_variation_selectors (hb_set_t *out) const { subtable_uvs->collect_variation_selectors (out); } void collect_variation_unicodes (hb_codepoint_t variation_selector, @@ -2169,7 +2236,7 @@ struct cmap typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph); - typedef uint_fast16_t (*hb_pua_remap_func_t) (unsigned); + typedef uint16_t (*hb_pua_remap_func_t) (unsigned); template HB_INTERNAL static bool get_glyph_from (const void *obj, @@ -2217,6 +2284,21 @@ struct cmap return c && typed_obj->get_glyph (c, glyph); } + unsigned int get_subtable_data_size (const CmapSubtable *subtable) const + { + unsigned int table_length = this->table.get_length (); + uintptr_t table_start = (uintptr_t) (const void *) this->table.get (); + uintptr_t subtable_addr = (uintptr_t) (const void *) subtable; + if (unlikely (subtable_addr < table_start)) + return 0; + + uintptr_t subtable_offset = subtable_addr - table_start; + if (unlikely (subtable_offset >= table_length)) + return 0; + + return table_length - (unsigned int) subtable_offset; + } + private: hb_nonnull_ptr_t subtable; hb_nonnull_ptr_t subtable_uvs; diff --git a/thirdparty/harfbuzz/src/hb-ot-color.cc b/thirdparty/harfbuzz/src/hb-ot-color.cc index 1436fe563f..34cdedb636 100644 --- a/thirdparty/harfbuzz/src/hb-ot-color.cc +++ b/thirdparty/harfbuzz/src/hb-ot-color.cc @@ -35,7 +35,9 @@ #include "OT/Color/COLR/COLR.hh" #include "OT/Color/CPAL/CPAL.hh" #include "OT/Color/sbix/sbix.hh" +#ifndef HB_NO_SVG #include "OT/Color/svg/svg.hh" +#endif /** @@ -274,6 +276,7 @@ hb_ot_color_glyph_get_layers (hb_face_t *face, * SVG */ +#ifndef HB_NO_SVG /** * hb_ot_color_has_svg: * @face: #hb_face_t to work upon. @@ -290,6 +293,70 @@ hb_ot_color_has_svg (hb_face_t *face) return face->table.SVG->has_data (); } +/** + * hb_ot_color_get_svg_document_count: + * @face: #hb_face_t to work upon. + * + * Gets the number of SVG documents in the face `SVG` table. + * + * Return value: number of SVG documents in the face. + * + * Since: 12.1.0 + */ +unsigned int +hb_ot_color_get_svg_document_count (hb_face_t *face) +{ + return face->table.SVG->get_document_count (); +} + +/** + * hb_ot_color_glyph_get_svg_document_index: + * @face: #hb_face_t to work upon. + * @glyph: glyph ID to query. + * @svg_document_index: (out) (nullable): output SVG document index. + * + * Gets the `SVG`-table document index associated with a glyph. + * + * Return value: `true` if @glyph maps to an SVG document, `false` otherwise. + * + * Since: 12.1.0 + */ +hb_bool_t +hb_ot_color_glyph_get_svg_document_index (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int *svg_document_index) +{ + unsigned doc_index = 0; + hb_bool_t ret = face->table.SVG->get_glyph_document_index (glyph, &doc_index); + if (ret && svg_document_index) + *svg_document_index = doc_index; + return ret; +} + +/** + * hb_ot_color_get_svg_document_glyph_range: + * @face: #hb_face_t to work upon. + * @svg_document_index: SVG document index. + * @start_glyph_id: (out) (nullable): output start glyph ID. + * @end_glyph_id: (out) (nullable): output end glyph ID. + * + * Gets the glyph range covered by an `SVG`-table document index. + * + * Return value: `true` if @svg_document_index is valid, `false` otherwise. + * + * Since: 13.0.0 + */ +hb_bool_t +hb_ot_color_get_svg_document_glyph_range (hb_face_t *face, + unsigned int svg_document_index, + hb_codepoint_t *start_glyph_id, + hb_codepoint_t *end_glyph_id) +{ + return face->table.SVG->get_document_glyph_range (svg_document_index, + start_glyph_id, + end_glyph_id); +} + /** * hb_ot_color_glyph_reference_svg: * @face: #hb_face_t to work upon @@ -308,6 +375,7 @@ hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph) { return face->table.SVG->reference_blob_for_glyph (glyph); } +#endif /* diff --git a/thirdparty/harfbuzz/src/hb-ot-color.h b/thirdparty/harfbuzz/src/hb-ot-color.h index 22ee497e38..eae339f82c 100644 --- a/thirdparty/harfbuzz/src/hb-ot-color.h +++ b/thirdparty/harfbuzz/src/hb-ot-color.h @@ -136,6 +136,20 @@ hb_ot_color_glyph_has_paint (hb_face_t *face, HB_EXTERN hb_bool_t hb_ot_color_has_svg (hb_face_t *face); +HB_EXTERN unsigned int +hb_ot_color_get_svg_document_count (hb_face_t *face); + +HB_EXTERN hb_bool_t +hb_ot_color_glyph_get_svg_document_index (hb_face_t *face, + hb_codepoint_t glyph, + unsigned int *svg_document_index /* OUT */); + +HB_EXTERN hb_bool_t +hb_ot_color_get_svg_document_glyph_range (hb_face_t *face, + unsigned int svg_document_index, + hb_codepoint_t *start_glyph_id, /* OUT */ + hb_codepoint_t *end_glyph_id /* OUT */); + HB_EXTERN hb_blob_t * hb_ot_color_glyph_reference_svg (hb_face_t *face, hb_codepoint_t glyph); diff --git a/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh b/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh index afc8aa476e..9047b2880a 100644 --- a/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh +++ b/thirdparty/harfbuzz/src/hb-ot-face-table-list.hh @@ -140,8 +140,10 @@ HB_OT_ACCELERATOR (OT, COLR) HB_OT_CORE_TABLE (OT, CPAL) HB_OT_ACCELERATOR (OT, CBDT) HB_OT_ACCELERATOR (OT, sbix) +#ifndef HB_NO_SVG HB_OT_ACCELERATOR (OT, SVG) #endif +#endif /* OpenType math. */ #ifndef HB_NO_MATH diff --git a/thirdparty/harfbuzz/src/hb-ot-font.cc b/thirdparty/harfbuzz/src/hb-ot-font.cc index 76c4bf28d9..bc43f5f0e7 100644 --- a/thirdparty/harfbuzz/src/hb-ot-font.cc +++ b/thirdparty/harfbuzz/src/hb-ot-font.cc @@ -937,7 +937,9 @@ hb_ot_paint_glyph_or_fail (hb_font_t *font, { #ifndef HB_NO_COLOR if (font->face->table.COLR->paint_glyph (font, glyph, paint_funcs, paint_data, palette, foreground)) return true; +#ifndef HB_NO_SVG if (font->face->table.SVG->paint_glyph (font, glyph, paint_funcs, paint_data)) return true; +#endif #ifndef HB_NO_OT_FONT_BITMAP if (font->face->table.CBDT->paint_glyph (font, glyph, paint_funcs, paint_data)) return true; if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return true; diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh index 5c0533aad3..a9c03db3e4 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout-common.hh +++ b/thirdparty/harfbuzz/src/hb-ot-layout-common.hh @@ -2543,13 +2543,13 @@ struct SparseVarRegionAxis struct hb_scalar_cache_t { private: - static constexpr unsigned STATIC_LENGTH = 16; + static constexpr unsigned STATIC_LENGTH = 128; static constexpr int INVALID = INT_MIN; static constexpr float MULTIPLIER = 1 << ((sizeof (int) * 8) - 2); static constexpr float DIVISOR = 1.f / MULTIPLIER; public: - hb_scalar_cache_t () : length (STATIC_LENGTH) { clear (); } + hb_scalar_cache_t () {} hb_scalar_cache_t (const hb_scalar_cache_t&) = delete; hb_scalar_cache_t (hb_scalar_cache_t&&) = delete; @@ -2561,8 +2561,9 @@ struct hb_scalar_cache_t { if (!count) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t); - if (scratch_cache && count <= scratch_cache->length) + if (scratch_cache && count <= STATIC_LENGTH) { + scratch_cache->length = count; scratch_cache->clear (); return scratch_cache; } @@ -2970,7 +2971,9 @@ struct VarData } if (unlikely (!c->extend_min (this))) return_trace (false); - itemCount = row_count; + if (unlikely (!c->check_assign (itemCount, row_count, + HB_SERIALIZE_ERROR_INT_OVERFLOW))) + return_trace (false); int min_threshold = has_long ? -65536 : -128; int max_threshold = has_long ? +65535 : +127; @@ -3256,12 +3259,24 @@ struct MultiVarData auto values_iter = deltaSets.fetcher (inner); unsigned regionCount = regionIndices.len; + unsigned skip = 0; for (unsigned regionIndex = 0; regionIndex < regionCount; regionIndex++) { float scalar = regions.evaluate (regionIndices.arrayZ[regionIndex], coords, coord_count, cache); - values_iter.add_to (out, scalar); + // We skip lazily. Helps with the tail end. + if (scalar == 0.0f) + skip += out.length; + else + { + if (skip) + { + values_iter.skip (skip); + skip = 0; + } + values_iter.add_to (out, scalar); + } } } @@ -4041,7 +4056,7 @@ struct ConditionValue bool evaluate (const int *coords, unsigned int coord_len, Instancer *instancer) const { - signed value = defaultValue; + float value = defaultValue; value += (*instancer)[varIdx]; return value > 0; } diff --git a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh index 9137726688..1f24376e72 100644 --- a/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh +++ b/thirdparty/harfbuzz/src/hb-ot-layout-gsubgpos.hh @@ -743,6 +743,10 @@ struct hb_ot_apply_context_t : hb_vector_t match_positions; uint32_t stack_match_positions[8]; +#ifndef HB_NO_BUFFER_MESSAGE + hb_buffer_t::changed_func_t orig_changed_func = nullptr; + void *orig_changed_data = nullptr; +#endif hb_ot_apply_context_t (unsigned int table_index_, hb_font_t *font_, @@ -773,6 +777,30 @@ struct hb_ot_apply_context_t : { init_iters (); match_positions.set_storage (stack_match_positions); + +#ifndef HB_NO_BUFFER_MESSAGE + if (buffer->messaging ()) + { + assert (buffer->changed_func == nullptr || + buffer->changed_func == buffer_changed_trampoline); + orig_changed_func = buffer->changed_func; + orig_changed_data = buffer->changed_data; + buffer->changed_func = buffer_changed_trampoline; + buffer->changed_data = this; + } +#endif + } + ~hb_ot_apply_context_t () + { +#ifndef HB_NO_BUFFER_MESSAGE + if (buffer->messaging ()) + { + assert (buffer->changed_func == buffer_changed_trampoline); + assert (buffer->changed_data == this); + buffer->changed_func = orig_changed_func; + buffer->changed_data = orig_changed_data; + } +#endif } void init_iters () @@ -801,6 +829,18 @@ struct hb_ot_apply_context_t : return buffer->random_state; } + static void buffer_changed_trampoline (hb_buffer_t *buffer HB_UNUSED, void *user_data) + { + ((hb_ot_apply_context_t *) user_data)->buffer_changed (); + } + void buffer_changed () + { + buffer->update_digest (); + if (likely (has_glyph_classes)) + for (unsigned i = 0; i < buffer->len; i++) + _set_glyph_class_props (buffer->info[i]); + } + HB_ALWAYS_INLINE HB_HOT bool match_properties_mark (const hb_glyph_info_t *info, @@ -870,8 +910,7 @@ struct hb_ot_apply_context_t : props |= HB_OT_LAYOUT_GLYPH_PROPS_MULTIPLIED; if (likely (has_glyph_classes)) { - props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE; - _hb_glyph_info_set_glyph_props (&buffer->cur(), props | gdef_accel.get_glyph_props (glyph_index)); + _set_glyph_class_props (buffer->cur(), props, glyph_index); } else if (class_guess) { @@ -881,6 +920,19 @@ struct hb_ot_apply_context_t : else _hb_glyph_info_set_glyph_props (&buffer->cur(), props); } + void _set_glyph_class_props (hb_glyph_info_t &info, + unsigned int props, + hb_codepoint_t glyph_index) + { + props &= HB_OT_LAYOUT_GLYPH_PROPS_PRESERVE; + _hb_glyph_info_set_glyph_props (&info, props | gdef_accel.get_glyph_props (glyph_index)); + } + void _set_glyph_class_props (hb_glyph_info_t &info) + { + _set_glyph_class_props (info, + _hb_glyph_info_get_glyph_props (&info), + info.codepoint); + } void replace_glyph (hb_codepoint_t glyph_index) { diff --git a/thirdparty/harfbuzz/src/hb-ot-shape.cc b/thirdparty/harfbuzz/src/hb-ot-shape.cc index e8d43180eb..7b3b854d57 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shape.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shape.cc @@ -580,12 +580,8 @@ hb_form_clusters (hb_buffer_t *buffer) if (!(buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_CONTINUATIONS)) return; - if (HB_BUFFER_CLUSTER_LEVEL_IS_GRAPHEMES (buffer->cluster_level)) - foreach_grapheme (buffer, start, end) - buffer->merge_clusters (start, end); - else - foreach_grapheme (buffer, start, end) - buffer->unsafe_to_break (start, end); + foreach_grapheme (buffer, start, end) + buffer->merge_grapheme_clusters (start, end); } static void diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-pua.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-pua.hh index ba86772f84..7464e5d8a3 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-pua.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-pua.hh @@ -9,41 +9,43 @@ #ifndef HB_OT_SHAPER_ARABIC_PUA_HH #define HB_OT_SHAPER_ARABIC_PUA_HH -static const uint8_t -_hb_arabic_u8[464] = +#include "hb.hh" + +#include + +static const uint8_t _hb_arabic_pua_u8[453]= { - 84, 86, 85, 85, 85, 85, 85,213, 16, 34, 34, 34, 34, 34, 35, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 36, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 82, 16, 0, 0, 0, 0, 1, 2, 3, 4, - 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 7, - 0, 0, 8, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 0, 0, 0, 22, 0, 23, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 16, 34, 34, 34, 35, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 66, 16, 50, 68, 68, 68, 68, 68, 68, - 68, 68, 68, 68,101, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, - 71, 68, 68, 68, 68, 68, 68, 68,152,186, 76, 77, 68,254, 16, 50, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 5, 6, - 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 10, 0, - 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 3, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 13, 0, 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 23, 23, 29, 30, 31, 32, 33, 0, 0, 0, 0, - 0, 0, 0, 34, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 40, - 41, 42, 0, 43, 44, 0, 0, 45, 46, 0, 47, 48, 49, 0, 0, 0, - 0, 50, 0, 0, 51, 52, 0, 53, 54, 55, 56, 57, 58, 0, 0, 0, - 0, 0, 59, 60, 61, 62, 63, 64, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 66, - 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, - 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, + 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 0, 7, 0, 0, 8, 0, 0, 0, 9, 0, 0, + 10, 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0, 0, 0, + 22, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 33, + 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 67, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,101, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0,152, + 186, 12, 13, 0,254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4, + 5, 0, 0, 6, 7, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, + 0, 0, 0, 11, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, + 0, 0, 13, 0, 0, 0, 0, 0, 14, 0, 0, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 1, 24, 25, 26, 27, 28, 1, 1, 29, 30, 31, 32, + 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 35, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 0, 0, 0, 0, 0, 0, + 0, 39, 0, 0, 40, 41, 42, 0, 43, 44, 0, 0, 45, 46, 0, 47, + 48, 49, 0, 0, 0, 0, 50, 0, 0, 51, 52, 0, 53, 54, 55, 56, + 57, 58, 0, 0, 0, 0, 0, 59, 60, 61, 62, 63, 64, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 65, 0, 0, 66, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, }; -static const uint16_t -_hb_arabic_u16[720] = +static const uint16_t _hb_arabic_pua_u16[717]= { 0, 0, 0, 0, 0, 0, 0, 0,61728,61729,61730, 0, 0,61733, 0, 0, 61736,61737,61738,61739,61790,61741,61742,61743,61872,61873,61874,61875,61876,61877,61878,61879, @@ -65,52 +67,46 @@ _hb_arabic_u16[720] = 61808,61813,61813,61811,61812,61816,61816,61814,61815,61818,61818,61817,61817,61820,61820,61819, 61819,61822,61822,61821,61821,61921,61921,61823,61823,61860,61859,61857,61858,61861,61861,61868, 61867,61864,61863,61862,61862,61888,61889,61886,61887,61890,61891,61885,61884, 0, 0, 0, - 0, 0, 0, 0,61984,61985,61986, 0, 0,61989, 0, 0,61992,61993,61994,61995, - 62046,61997,61998,61999, 0, 0,62010,62011, 0,62013, 0,62015, 0, 0, 0,62043, - 0,62045, 0, 0, 0, 0, 0,61987, 0, 0, 0,61988, 0, 0, 0,61990, - 0, 0, 0,61991,61996, 0, 0, 0, 0, 0, 0,62011, 0, 0, 0,62015, - 0,62165,62021,62019,62170,62023,62169,62017,62028,62161,62032,62036,62040,62048,62052,62053, - 62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,61952,61952,61952,61952, - 62047,62130,62134,62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181, - 62186,62185,62182,61952,61952,61952,61952, 0,62000,62001,62002,62003,62004,62005,62006,62007, - 62008,62009, 0,62046,62046, 0, 0, 0,61964,61965,61966,61967,62012,62014, 0, 0, - 61954, 0,61981, 0, 0, 0,61955, 0,61982, 0,61956, 0, 0, 0,62111, 0, - 0, 0, 0,61970,61971,61972,61957, 0,61980, 0, 0, 0, 0, 0,61958, 0, - 61983, 0, 0, 0, 0, 0,62191, 0,62188,62189,62192, 0, 0, 0,61973, 0, - 0,62098, 0, 0,61974, 0, 0,62099, 0, 0,62101, 0, 0,61975, 0, 0, - 62100, 0, 0, 0,62080,62081,62082,62102, 0,62083,62084,62085,62103, 0, 0, 0, - 62106, 0,62107, 0,62108, 0, 0, 0,61976, 0, 0, 0, 0,62086,62087,62088, - 62109,61978,62089,62090,62091,62110,62093,62094, 0,62104, 0, 0, 0, 0,62095,62096, - 62097,62105, 0, 0,61977, 0, 0, 0, 0, 0,62075,62077,61968, 0, 0, 0, - 0,62021,62022,62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028, - 62027,62025,62026,62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037, - 62038,62048,62044,62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059, - 62060,62064,62063,62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073, - 62074,62114,62113,62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123, - 62124,62130,62129,62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139, - 62140,62146,62145,62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164, - 62163,62160,62159,62157,62158,62176,62177,62174,62175,62178,62179,62172,62173, 0, 0, 0, + 0,61952,61952,61952,61952,61984,61985,61986, 0, 0,61989, 0, 0,61992,61993,61994, + 61995,62046,61997,61998,61999, 0, 0,62010,62011, 0,62013, 0,62015, 0, 0, 0, + 62043, 0,62045, 0, 0, 0, 0, 0,61987, 0, 0, 0,61988, 0, 0, 0, + 61990, 0, 0, 0,61991,61996, 0, 0, 0, 0, 0, 0,62011, 0, 0, 0, + 62015, 0,62165,62021,62019,62170,62023,62169,62017,62028,62161,62032,62036,62040,62048,62052, + 62053,62055,62057,62059,62064,62068,62072,62078,62114,62115,62122,62126,61952,62047,62130,62134, + 62138,62142,62146,62150,62154,62155,62164,62160,62183,62184,62187,62180,62181,62186,62185,62182, + 61952,61952,61952,61952, 0,62000,62001,62002,62003,62004,62005,62006,62007,62008,62009, 0, + 62046,62046, 0, 0, 0,61964,61965,61966,61967,62012,62014, 0, 0,61954, 0,61981, + 0, 0, 0,61955, 0,61982, 0,61956, 0, 0, 0,62111, 0, 0, 0, 0, + 61970,61971,61972,61957, 0,61980, 0, 0, 0, 0, 0,61958, 0,61983, 0, 0, + 0, 0, 0,62191, 0,62188,62189,62192, 0, 0, 0,61973, 0, 0,62098, 0, + 0,61974, 0, 0,62099, 0, 0,62101, 0, 0,61975, 0, 0,62100, 0, 0, + 0,62080,62081,62082,62102, 0,62083,62084,62085,62103, 0, 0, 0,62106, 0,62107, + 0,62108, 0, 0, 0,61976, 0, 0, 0, 0,62086,62087,62088,62109,61978,62089, + 62090,62091,62110,62093,62094, 0,62104, 0, 0, 0, 0,62095,62096,62097,62105, 0, + 0,61977, 0, 0, 0, 0, 0,62075,62077,61968, 0, 0, 0, 0,62021,62022, + 62019,62020,62170,62171,62023,62024,62169,62168,62166,62167,62017,62018,62028,62027,62025,62026, + 62161,62162,62032,62031,62029,62030,62036,62035,62033,62034,62040,62039,62037,62038,62048,62044, + 62041,62042,62052,62051,62049,62050,62053,62054,62055,62056,62057,62058,62059,62060,62064,62063, + 62061,62062,62068,62067,62065,62066,62072,62071,62069,62070,62078,62076,62073,62074,62114,62113, + 62079,62193,62118,62117,62115,62116,62122,62121,62119,62120,62126,62125,62123,62124,62130,62129, + 62127,62128,62134,62133,62131,62132,62138,62137,62135,62136,62142,62141,62139,62140,62146,62145, + 62143,62144,62150,62149,62147,62148,62154,62153,62151,62152,62155,62156,62164,62163,62160,62159, + 62157,62158,62176,62177,62174,62175,62178,62179,62172,62173, 0, 0, 0, }; -static inline unsigned -_hb_arabic_b2 (const uint8_t* a, unsigned i) +static inline uint8_t _hb_arabic_pua_b4 (const uint8_t* a, unsigned i) { - return (a[i>>2]>>((i&3u)<<1))&3u; + return (a[i>>1]>>((i&1)<<2))&15; } -static inline unsigned -_hb_arabic_b4 (const uint8_t* a, unsigned i) +static inline uint16_t _hb_arabic_pua_simp_map (unsigned u) { - return (a[i>>1]>>((i&1u)<<2))&15u; + /* packtab: [2^2,2^4,2^4,2^3] */ + return u<65277u ? (uint16_t)(_hb_arabic_pua_u16[((_hb_arabic_pua_u8[31u+((_hb_arabic_pua_b4(_hb_arabic_pua_u8,((((13835058055282164225ULL>>((((((((u)>>3))>>4))>>4))<<1))&3))<<4)+((((((u)>>3))>>4))&15)))<<4)+((((u)>>3))&15)])<<3)+((u)&7)]) : 0; } -static inline uint_fast16_t -_hb_arabic_pua_simp_map (unsigned u) +static inline uint16_t _hb_arabic_pua_trad_map (unsigned u) { - return u<65277u?_hb_arabic_u16[((_hb_arabic_u8[40+(((_hb_arabic_b4(8+_hb_arabic_u8,((_hb_arabic_b2(_hb_arabic_u8,u>>3>>4>>4))<<4)+((u>>3>>4)&15u)))<<4)+((u>>3)&15u))])<<3)+((u)&7u)]:0; -} -static inline uint_fast16_t -_hb_arabic_pua_trad_map (unsigned u) -{ - return u<65277u?_hb_arabic_u16[320+(((_hb_arabic_u8[208+(((_hb_arabic_b4(168+_hb_arabic_u8,((_hb_arabic_b4(136+_hb_arabic_u8,u>>2>>4>>4))<<4)+((u>>2>>4)&15u)))<<4)+((u>>2)&15u))])<<2)+((u)&3u))]:0; + /* packtab: [2^4,2^4,2^4,2^2] */ + return u<65277u ? (uint16_t)(_hb_arabic_pua_u16[317u+((_hb_arabic_pua_u8[197u+((_hb_arabic_pua_b4(_hb_arabic_pua_u8+159u,((_hb_arabic_pua_b4(_hb_arabic_pua_u8+127u,((((((u)>>2))>>4))>>4)))<<4)+((((((u)>>2))>>4))&15)))<<4)+((((u)>>2))&15)])<<2)+((u)&3)]) : 0; } #endif /* HB_OT_SHAPER_ARABIC_PUA_HH */ diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-table.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-table.hh index 09289d27df..39da24b8be 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic-table.hh @@ -16,222 +16,77 @@ #ifndef HB_OT_SHAPER_ARABIC_TABLE_HH #define HB_OT_SHAPER_ARABIC_TABLE_HH +#include "hb.hh" -#define A JOINING_GROUP_ALAPH -#define DR JOINING_GROUP_DALATH_RISH -#define C JOINING_TYPE_C -#define D JOINING_TYPE_D -#define L JOINING_TYPE_L -#define R JOINING_TYPE_R -#define T JOINING_TYPE_T -#define U JOINING_TYPE_U -#define X JOINING_TYPE_X +#include -static const uint8_t joining_table[] = +static const uint8_t _hb_arabic_joining_u8[737]= { + 0, 16, 2, 0, 0, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 96,135, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 16, 24, 32, 40, 48, 0, 56, 0, 64, 72, 80, 0, + 0, 0, 0, 88, 96,104, 0, 0, 0, 0, 0,112,120, 0, 0, 0, + 0, 0, 0, 0,128, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 0, + 0,144, 0, 0, 0, 0, 0,152, 0, 0, 0, 0, 0, 0,160,168, + 176,184,192, 0, 0,200,208, 0, 0, 0, 0, 0, 0, 0, 0,216, + 224, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 10, 11, 12, + 1, 1, 13, 0, 0, 0, 14, 15, 1, 1, 2, 2, 4, 1, 1, 1, + 1, 16, 17, 18, 3, 0, 19, 0, 20, 0, 21, 22, 23, 1, 24, 0, + 0, 0, 25, 1, 26, 1, 27, 28, 4, 0, 29, 1, 1, 1, 30, 0, + 31, 32, 5, 33, 34, 35, 36, 2, 2, 37, 38, 6, 0, 1, 39, 40, + 5, 1, 7, 0, 0, 41, 0, 0, 0, 42, 43, 0, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 7, 44, 1, 1, 1, 1, 45, 0, + 0, 0, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 48, 6, 0, + 0, 1, 1, 1, 1, 1, 1, 49, 0, 50, 51, 52, 53, 54, 55, 0, + 0, 56, 57, 58, 0, 0, 59, 0, 0, 60, 1, 1, 1, 61, 0, 0, + 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, + 1, 64, 0, 65, 0, 0, 0, 66, 1, 67, 0, 0, 0, 0, 0, 68, + 69, 70, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 0, 3, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 72, 73, 0, 0, 0, 0, 0, 0,119,119,119,119, 51, 51, 51, + 51, 34, 34, 34, 34,119,119, 7,119, 34, 51, 51, 51, 35, 51, 51, + 51, 0,119,119,119,115,119,119,119, 0, 0, 0,119,112, 7,119, + 119, 3, 34, 34, 35, 35, 51, 51, 35, 34, 50, 51, 51, 50,115,119, + 119,119,119,119, 51, 39, 34, 32, 34, 50, 35, 34, 34, 34, 34, 35, + 35, 51, 34, 39,119,119,119,119, 34,119, 51,115, 55,119,119,119, + 103,116, 51, 83, 37, 34, 51, 51, 50, 50, 53, 50, 83,119,119, 39, + 51, 35, 34, 51, 51, 51, 35, 50, 51, 35, 35, 50, 51,119, 51, 51, + 51, 51,115,119,119,119,115,119,119, 50, 51, 51, 34, 51, 51, 50, + 34,114,119,119,119, 3, 51, 51, 32, 35,114,119,119, 34, 50, 51, + 3, 48, 51, 51, 50, 51, 34, 2, 50, 35, 50, 51, 51,119,112,119, + 119,119,119,119, 48,119,115,119,112, 0, 0, 96, 54,115,115,119, + 119,119,119, 48,119,119,119,119, 7,119,119,119, 0, 51, 1,119, + 119, 51, 51, 35, 32, 32, 2, 16, 34, 34, 50, 51, 19, 51, 51, 35, + 51, 35, 0,114,119,119, 55, 51, 35, 35, 35, 34, 51, 35, 51, 50, + 34, 35,119,119,119, 39, 34, 50, 3, 49, 51, 51, 51, 51, 50,119, + 119,119, 50,115, 51, 51, 35, 51, 51, 51, 51, 3,119, 55, 51,114, + 119, 51, 51, 34, 51, 51,119,119,119, 3, 51, 34, 2, 35, 50, 35, + 51, 48, 34, 3, 0, 32, 19,119,119, 51, 51,119,119,119,103,119, + 119, +}; -#define joining_offset_0x0600u 0 - - /* Arabic */ - - /* 0600 */ U,U,U,U,U,U,X,X,U,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 0620 */ D,U,R,R,R,R,D,R,D,R,D,D,D,D,D,R,R,R,R,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 0640 */ C,D,D,D,D,D,D,D,R,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 0660 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,D,D,X,R,R,R,U,R,R,R,D,D,D,D,D,D,D,D, - /* 0680 */ D,D,D,D,D,D,D,D,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,D,D,D,D,D,D, - /* 06A0 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 06C0 */ R,D,D,R,R,R,R,R,R,R,R,R,D,R,D,R,D,D,R,R,X,R,X,X,X,X,X,X,X,U,X,X, - /* 06E0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,R,R,X,X,X,X,X,X,X,X,X,X,D,D,D,X,X,D, - - /* Syriac */ - - /* 0700 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,T,A,X,D,D,D,DR,DR,R,R,R,D,D,D,D,R,D, - /* 0720 */ D,D,D,D,D,D,D,D,R,D,DR,D,R,D,D,DR,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 0740 */ X,X,X,X,X,X,X,X,X,X,X,X,X,R,D,D, - - /* Arabic Supplement */ - - /* 0740 */ D,D,D,D,D,D,D,D,D,R,R,R,D,D,D,D, - /* 0760 */ D,D,D,D,D,D,D,D,D,D,D,R,R,D,D,D,D,R,D,R,R,D,D,D,R,R,D,D,D,D,D,D, - - /* FILLER */ - - /* 0780 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 07A0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - - /* NKo */ - - /* 07C0 */ X,X,X,X,X,X,X,X,X,X,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 07E0 */ D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,C,X,X,X,X,X, - - /* FILLER */ - - /* 0800 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 0820 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - - /* Mandaic */ - - /* 0840 */ R,D,D,D,D,D,R,R,D,R,D,D,D,D,D,D,D,D,D,D,R,D,R,R,R,X,X,X,X,X,X,X, - - /* Syriac Supplement */ - - /* 0860 */ D,U,D,D,D,D,U,R,D,R,R,X,X,X,X,X, - - /* Arabic Extended-B */ - - /* 0860 */ R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R, - /* 0880 */ R,R,R,C,C,C,D,U,U,D,D,D,D,D,R,D,U,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - - /* Arabic Extended-A */ - - /* 08A0 */ D,D,D,D,D,D,D,D,D,D,R,R,R,U,R,D,D,R,R,D,D,D,D,D,D,R,D,D,D,D,D,D, - /* 08C0 */ D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 08E0 */ X,X,U, - -#define joining_offset_0x1806u 739 - - /* Mongolian */ - - /* 1800 */ U,D,X,X,C,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 1820 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 1840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 1860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,X,X,X,X,X,X,X, - /* 1880 */ U,U,U,U,U,T,T,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 18A0 */ D,D,D,D,D,D,D,D,D,X,D, - -#define joining_offset_0x200cu 904 - - /* General Punctuation */ - - /* 2000 */ U,C,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 2020 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,U,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 2040 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 2060 */ X,X,X,X,X,X,U,U,U,U, - -#define joining_offset_0xa840u 998 - - /* Phags-pa */ - - /* A840 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* A860 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,L,U, - -#define joining_offset_0x10ac0u 1050 - - /* Manichaean */ - - /* 10AC0 */ D,D,D,D,D,R,U,R,U,R,R,U,U,L,R,R,R,R,R,D,D,D,D,L,D,D,D,D,D,R,D,D, - /* 10AE0 */ D,R,U,U,R,X,X,X,X,X,X,D,D,D,D,R, - -#define joining_offset_0x10b80u 1098 - - /* Psalter Pahlavi */ - - /* 10B80 */ D,R,D,R,R,R,D,D,D,R,D,D,R,D,R,R,D,R,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 10BA0 */ X,X,X,X,X,X,X,X,X,R,R,R,R,D,D,U, - -#define joining_offset_0x10d00u 1146 - - /* Hanifi Rohingya */ - - /* 10D00 */ L,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 10D20 */ D,D,R,D, - -#define joining_offset_0x10ec2u 1182 - - /* Arabic Extended-C */ - - /* 10EC0 */ R,D,D,X,D,D, - -#define joining_offset_0x10f30u 1188 - - /* Sogdian */ - - /* 10F20 */ D,D,D,R,D,D,D,D,D,D,D,D,D,D,D,D, - /* 10F40 */ D,D,D,D,D,U,X,X,X,X,X,X,X,X,X,X,X,D,D,D,R,X,X,X,X,X,X,X,X,X,X,X, - /* 10F60 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - - /* Old Uyghur */ - - /* 10F60 */ D,D,D,D,R,R,D,D,D,D,D,D,D,D,D,D, - /* 10F80 */ D,D,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - /* 10FA0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,X,X,X, - - /* Chorasmian */ - - /* 10FA0 */ D,U,D,D,R,R,R,U,D,R,R,D,D,R,D,D, - /* 10FC0 */ U,D,R,R,D,U,U,U,U,R,D,L, - -#define joining_offset_0x110bdu 1344 - - /* Kaithi */ - - /* 110A0 */ U,X,X, - /* 110C0 */ X,X,X,X,X,X,X,X,X,X,X,X,X,U, - -#define joining_offset_0x1e900u 1361 - - /* Adlam */ - - /* 1E900 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 1E920 */ D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D,D, - /* 1E940 */ D,D,D,D,X,X,X,X,X,X,X,T, - -}; /* Table items: 1437; occupancy: 58% */ - +static inline uint8_t _hb_arabic_joining_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1)<<2))&15; +} +static inline uint8_t _hb_arabic_joining_joining_type (unsigned u) +{ + /* packtab: [2^4,2^3,2^3,2^3] */ + return u<125260u ? (uint8_t)(_hb_arabic_joining_b4(_hb_arabic_joining_u8+441u,((_hb_arabic_joining_u8[209u+_hb_arabic_joining_u8[123u+((_hb_arabic_joining_b4(_hb_arabic_joining_u8,((((((u)>>3))>>3))>>3)))<<3)+((((((u)>>3))>>3))&7)]+((((u)>>3))&7)])<<3)+((u)&7))) : 7; +} static unsigned int joining_type (hb_codepoint_t u) { - switch (u >> 12) - { - case 0x0u: - if (hb_in_range (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u]; - break; - - case 0x1u: - if (hb_in_range (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u]; - break; - - case 0x2u: - if (hb_in_range (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu]; - break; - - case 0xAu: - if (hb_in_range (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u]; - break; - - case 0x10u: - if (hb_in_range (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u]; - if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u]; - if (hb_in_range (u, 0x10D00u, 0x10D23u)) return joining_table[u - 0x10D00u + joining_offset_0x10d00u]; - if (hb_in_range (u, 0x10EC2u, 0x10EC7u)) return joining_table[u - 0x10EC2u + joining_offset_0x10ec2u]; - if (hb_in_range (u, 0x10F30u, 0x10FCBu)) return joining_table[u - 0x10F30u + joining_offset_0x10f30u]; - break; - - case 0x11u: - if (hb_in_range (u, 0x110BDu, 0x110CDu)) return joining_table[u - 0x110BDu + joining_offset_0x110bdu]; - break; - - case 0x1Eu: - if (hb_in_range (u, 0x1E900u, 0x1E94Bu)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u]; - break; - - default: - break; - } - return X; + return _hb_arabic_joining_joining_type (u); } -#undef A -#undef DR -#undef C -#undef D -#undef L -#undef R -#undef T -#undef U -#undef X - static const uint16_t shaping_table[][4] = { diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc index c5104c9489..2a05af1462 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-arabic.cc @@ -77,8 +77,8 @@ enum hb_arabic_joining_type_t { JOINING_GROUP_DALATH_RISH = 5, NUM_STATE_MACHINE_COLS = 6, - JOINING_TYPE_T = 7, - JOINING_TYPE_X = 8 /* means: use general-category to choose between U or T. */ + JOINING_TYPE_T = 6, + JOINING_TYPE_X = 7 /* means: use general-category to choose between U or T. */ }; #include "hb-ot-shaper-arabic-table.hh" @@ -561,20 +561,29 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%" PRId32, n_fixed, w_fixed); DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%" PRId32, n_repeating, w_repeating); - /* Number of additional times to repeat each repeating tile. */ - int n_copies = 0; + static constexpr unsigned STCH_MAX_GLYPHS = 256; - hb_position_t w_remaining = w_total - w_fixed; - if (sign * w_remaining > sign * w_repeating && sign * w_repeating > 0) - n_copies = (sign * w_remaining) / (sign * w_repeating) - 1; + /* Number of additional times to repeat each repeating tile. */ + unsigned int n_copies = 0; + + int64_t w_remaining_signed = (int64_t) w_total - w_fixed; + int64_t w_repeating_signed = w_repeating; + if (sign < 0) + { + w_remaining_signed = -w_remaining_signed; + w_repeating_signed = -w_repeating_signed; + } + hb_position_t w_remaining = (hb_position_t) (w_total - w_fixed); + if (w_remaining_signed > w_repeating_signed && w_repeating_signed > 0) + n_copies = w_remaining_signed / w_repeating_signed - 1; /* See if we can improve the fit by adding an extra repeat and squeezing them together a bit. */ hb_position_t extra_repeat_overlap = 0; - hb_position_t shortfall = sign * w_remaining - sign * w_repeating * (n_copies + 1); + int64_t shortfall = w_remaining_signed - w_repeating_signed * (n_copies + 1); if (shortfall > 0 && n_repeating > 0) { ++n_copies; - hb_position_t excess = (n_copies + 1) * sign * w_repeating - sign * w_remaining; + int64_t excess = (n_copies + 1) * w_repeating_signed - w_remaining_signed; if (excess > 0) { extra_repeat_overlap = excess / (n_copies * n_repeating); @@ -582,10 +591,22 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, } } + unsigned int max_copies = 0; + if (n_repeating > 0) + { + unsigned int base_glyphs = n_fixed + n_repeating; + if (base_glyphs < STCH_MAX_GLYPHS) + max_copies = (STCH_MAX_GLYPHS - base_glyphs) / n_repeating; + } + n_copies = hb_min (n_copies, max_copies); + if (step == MEASURE) { - extra_glyphs_needed += n_copies * n_repeating; - DEBUG_MSG (ARABIC, nullptr, "will add extra %d copies of repeating tiles", n_copies); + unsigned int added_glyphs = 0; + if (unlikely (hb_unsigned_mul_overflows (n_copies, n_repeating, &added_glyphs) || + hb_unsigned_add_overflows (extra_glyphs_needed, added_glyphs, &extra_glyphs_needed))) + break; + DEBUG_MSG (ARABIC, nullptr, "will add extra %u copies of repeating tiles", n_copies); } else { @@ -629,7 +650,9 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED, if (step == MEASURE) { - if (unlikely (!buffer->ensure (count + extra_glyphs_needed))) + unsigned int total_glyphs = 0; + if (unlikely (hb_unsigned_add_overflows (count, extra_glyphs_needed, &total_glyphs) || + !buffer->ensure (total_glyphs))) break; } else diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-hangul.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-hangul.cc index 792b658bf6..ce87a91564 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-hangul.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-hangul.cc @@ -298,7 +298,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, end = start + 2; if (unlikely (!buffer->successful)) break; - buffer->merge_out_clusters (start, end); + buffer->merge_out_grapheme_clusters (start, end); continue; } } @@ -371,7 +371,7 @@ preprocess_text_hangul (const hb_ot_shape_plan_t *plan HB_UNUSED, if (i < end) info[i++].hangul_shaping_feature() = TJMO; - buffer->merge_out_clusters (start, end); + buffer->merge_out_grapheme_clusters (start, end); continue; } else if ((!tindex && buffer->idx + 1 < count && isT (buffer->cur(+1).codepoint))) diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh index 92fb64ad07..6ff65c30a3 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-machine.hh @@ -53,7 +53,7 @@ enum indic_syllable_type_t { }; -#line 54 "hb-ot-shaper-indic-machine.hh" +#line 57 "hb-ot-shaper-indic-machine.hh" #define indic_syllable_machine_ex_A 9u #define indic_syllable_machine_ex_C 1u #define indic_syllable_machine_ex_CM 16u @@ -77,7 +77,7 @@ enum indic_syllable_type_t { #define indic_syllable_machine_ex_ZWNJ 5u -#line 76 "hb-ot-shaper-indic-machine.hh" +#line 81 "hb-ot-shaper-indic-machine.hh" static const unsigned char _indic_syllable_machine_trans_keys[] = { 8u, 57u, 4u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 8u, 57u, 5u, 57u, 5u, 57u, 13u, 13u, 4u, 57u, 4u, 57u, 4u, 57u, 4u, 57u, @@ -1126,7 +1126,7 @@ find_syllables_indic (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 1119 "hb-ot-shaper-indic-machine.hh" +#line 1130 "hb-ot-shaper-indic-machine.hh" { cs = indic_syllable_machine_start; ts = 0; @@ -1142,7 +1142,7 @@ find_syllables_indic (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 1131 "hb-ot-shaper-indic-machine.hh" +#line 1146 "hb-ot-shaper-indic-machine.hh" { int _slen; int _trans; @@ -1156,7 +1156,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 1143 "hb-ot-shaper-indic-machine.hh" +#line 1160 "hb-ot-shaper-indic-machine.hh" } _keys = _indic_syllable_machine_trans_keys + (cs<<1); @@ -1268,7 +1268,7 @@ _eof_trans: #line 117 "hb-ot-shaper-indic-machine.rl" {act = 7;} break; -#line 1232 "hb-ot-shaper-indic-machine.hh" +#line 1272 "hb-ot-shaper-indic-machine.hh" } _again: @@ -1277,7 +1277,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1239 "hb-ot-shaper-indic-machine.hh" +#line 1281 "hb-ot-shaper-indic-machine.hh" } if ( ++p != pe ) diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc index bf27efee21..e84b73eafd 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-indic-table.cc @@ -130,392 +130,112 @@ static_assert (OT_VPst == M_Cat(VPst), ""); #define _(S,M) INDIC_COMBINE_CATEGORIES (_OT_##S, _POS_##M) -static const uint16_t indic_table[] = { - - -#define indic_offset_0x0028u 0 - - - /* Basic Latin */ - - /* 0028 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X), - /* 0030 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 0038 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - -#define indic_offset_0x00b0u 24 - - - /* Latin-1 Supplement */ - - /* 00B0 */ _(X,X), _(X,X),_(SP,SM),_(SP,SM), _(X,X), _(X,X), _(X,X), _(X,X), - /* 00B8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 00C0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 00C8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 00D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), - -#define indic_offset_0x0900u 64 - - - /* Devanagari */ - - /* 0900 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C), _(V,C), - /* 0908 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), - /* 0910 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), - /* 0918 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0920 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0928 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0930 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0938 */ _(C,C), _(C,C), _(M,AS), _(M,AS), _(N,X), _(S,SM), _(M,AS), _(M,LM), - /* 0940 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), - /* 0948 */ _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(H,B), _(M,LM), _(M,AS), - /* 0950 */ _(X,X), _(A,SM), _(A,SM),_(SM,SM),_(SM,SM), _(M,AS), _(M,AS), _(M,AS), - /* 0958 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0960 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C), - /* 0968 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 0970 */ _(X,X), _(X,X), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), - /* 0978 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - - /* Bengali */ - - /* 0980 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), - /* 0988 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C), - /* 0990 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), - /* 0998 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 09A0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 09A8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 09B0 */ _(R,C), _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), - /* 09B8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM), - /* 09C0 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM), - /* 09C8 */ _(M,LM), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(C,C), _(X,X), - /* 09D0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), - /* 09D8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), - /* 09E0 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C), - /* 09E8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 09F0 */ _(R,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 09F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(X,X),_(SM,SM), _(X,X), - - /* Gurmukhi */ - - /* 0A00 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), - /* 0A08 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), - /* 0A10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), - /* 0A18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0A20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0A28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0A30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(X,X), - /* 0A38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(X,X), _(M,AP), _(M,LM), - /* 0A40 */_(MP,AP), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), - /* 0A48 */ _(M,AP), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), - /* 0A50 */ _(X,X), _(M,B), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0A58 */ _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X), - /* 0A60 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), - /* 0A68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 0A70 */_(SM,SM),_(SM,SM), _(C,C), _(C,C), _(X,X), _(CM,C), _(X,X), _(X,X), - /* 0A78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - - /* Gujarati */ - - /* 0A80 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), - /* 0A88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), - /* 0A90 */ _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), - /* 0A98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0AA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0AA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0AB0 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), - /* 0AB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,LM), - /* 0AC0 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AS), _(X,X), _(M,AS), - /* 0AC8 */ _(M,AS), _(M,AP), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), - /* 0AD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0AD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0AE0 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C), - /* 0AE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 0AF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0AF8 */ _(X,X), _(C,C), _(A,SM), _(N,X), _(A,SM), _(N,X), _(N,X), _(N,X), - - /* Oriya */ - - /* 0B00 */ _(X,X),_(SM,BS),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), - /* 0B08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(V,C), - /* 0B10 */ _(V,C), _(X,X), _(X,X), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), - /* 0B18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0B20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0B28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0B30 */ _(R,C), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), - /* 0B38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,AP), _(M,A), - /* 0B40 */ _(M,AP), _(M,AS), _(M,AS), _(M,AS), _(M,AS), _(X,X), _(X,X), _(M,LM), - /* 0B48 */ _(M,A), _(X,X), _(X,X), _(M,AP), _(M,AP), _(H,B), _(X,X), _(X,X), - /* 0B50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(N,X), _(M,A), _(M,AP), - /* 0B58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), - /* 0B60 */ _(V,C), _(V,C), _(M,AS), _(M,AS), _(X,X), _(X,X), _(GB,C), _(GB,C), - /* 0B68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 0B70 */ _(X,X), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0B78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - - /* Tamil */ - - /* 0B80 */ _(X,X), _(X,X),_(SM,SM), _(X,X), _(X,X), _(V,C), _(V,C), _(V,C), - /* 0B88 */ _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(X,X), _(V,C), _(V,C), - /* 0B90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(X,X), _(X,X), - /* 0B98 */ _(X,X), _(C,C), _(C,C), _(X,X), _(C,C), _(X,X), _(C,C), _(C,C), - /* 0BA0 */ _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), - /* 0BA8 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), - /* 0BB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0BB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), _(M,AP), - /* 0BC0 */ _(M,AS), _(M,AP), _(M,AP), _(X,X), _(X,X), _(X,X), _(M,LM), _(M,LM), - /* 0BC8 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(X,X), _(X,X), - /* 0BD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AP), - /* 0BD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0BE0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), - /* 0BE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 0BF0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0BF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - - /* Telugu */ - - /* 0C00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(V,C), _(V,C), _(V,C), - /* 0C08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), - /* 0C10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), - /* 0C18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0C20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0C28 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0C30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0C38 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS), - /* 0C40 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,BS), - /* 0C48 */ _(M,BS), _(X,X), _(M,BS), _(M,BS), _(M,BS), _(H,T), _(X,X), _(X,X), - /* 0C50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,BS), _(M,BS), _(X,X), - /* 0C58 */ _(C,C), _(C,C), _(C,C), _(X,X), _(X,X), _(C,C), _(X,X), _(X,X), - /* 0C60 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C), - /* 0C68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 0C70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0C78 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - - /* Kannada */ - - /* 0C80 */ _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(V,C), _(V,C), _(V,C), - /* 0C88 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), - /* 0C90 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), - /* 0C98 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0CA0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0CA8 */ _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0CB0 */ _(R,C), _(C,C), _(C,C), _(C,C), _(X,X), _(C,C), _(C,C), _(C,C), - /* 0CB8 */ _(C,C), _(C,C), _(X,X), _(X,X), _(N,X), _(S,SM), _(M,BS), _(M,BS), - /* 0CC0 */ _(M,BS), _(M,BS), _(M,BS), _(M,AS), _(M,AS), _(X,X), _(M,BS), _(M,AS), - /* 0CC8 */ _(M,AS), _(X,X), _(M,AS), _(M,AS), _(M,BS), _(H,T), _(X,X), _(X,X), - /* 0CD0 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(M,AS), _(M,AS), _(X,X), - /* 0CD8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(X,X), - /* 0CE0 */ _(V,C), _(V,C), _(M,BS), _(M,BS), _(X,X), _(X,X), _(GB,C), _(GB,C), - /* 0CE8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 0CF0 */ _(X,X), _(CS,C), _(CS,C),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0CF8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - - /* Malayalam */ - - /* 0D00 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(GB,C), _(V,C), _(V,C), _(V,C), - /* 0D08 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(V,C), _(V,C), - /* 0D10 */ _(V,C), _(X,X), _(V,C), _(V,C), _(V,C), _(C,C), _(C,C), _(C,C), - /* 0D18 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0D20 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0D28 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0D30 */ _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 0D38 */ _(C,C), _(C,C), _(C,C), _(M,AS), _(M,AS), _(S,SM), _(M,AP), _(M,AP), - /* 0D40 */ _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(M,AP), _(X,X), _(M,LM), _(M,LM), - /* 0D48 */ _(M,LM), _(X,X), _(M,AP), _(M,AP), _(M,AP), _(H,T), _(Rf,X), _(X,X), - /* 0D50 */ _(X,X), _(X,X), _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(M,AP), - /* 0D58 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), - /* 0D60 */ _(V,C), _(V,C), _(M,AP), _(M,AP), _(X,X), _(X,X), _(GB,C), _(GB,C), - /* 0D68 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 0D70 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 0D78 */ _(X,X), _(X,X), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - -#define indic_offset_0x1000u 1216 - - - /* Myanmar */ - - /* 1000 */ _(C,C), _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), - /* 1008 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 1010 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 1018 */ _(C,C), _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 1020 */ _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), - /* 1028 */ _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R), _(VA,T), _(VA,T), _(VB,B), - /* 1030 */ _(VB,B), _(VL,L), _(A,SM), _(VA,T), _(VA,T), _(VA,T), _(A,SM), _(N,X), - /* 1038 */_(SM,SM), _(H,X), _(As,X), _(MY,X), _(MR,X), _(MW,X), _(MH,X), _(C,C), - /* 1040 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 1048 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), _(C,C), _(X,X), - /* 1050 */ _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(VR,R), _(VR,R), - /* 1058 */ _(VB,B), _(VB,B), _(R,C), _(C,C), _(C,C), _(C,C), _(MY,X), _(MY,X), - /* 1060 */ _(ML,X), _(C,C), _(VR,R), _(PT,X), _(PT,X), _(C,C), _(C,C), _(VR,R), - /* 1068 */ _(VR,R), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(PT,X), _(C,C), _(C,C), - /* 1070 */ _(C,C), _(VA,T), _(VA,T), _(VA,T), _(VA,T), _(C,C), _(C,C), _(C,C), - /* 1078 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 1080 */ _(C,C), _(C,C), _(MW,X), _(VR,R), _(VL,L), _(VA,T), _(VA,T),_(SM,SM), - /* 1088 */_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM),_(SM,SM), _(C,C),_(SM,SM), - /* 1090 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 1098 */ _(GB,C), _(GB,C),_(SM,SM),_(SM,SM),_(SM,SM), _(VA,T), _(X,X), _(X,X), - -#define indic_offset_0x1780u 1376 - - - /* Khmer */ - - /* 1780 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 1788 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 1790 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 1798 */ _(C,C), _(C,C), _(R,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* 17A0 */ _(C,C), _(C,C), _(C,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), - /* 17A8 */ _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), _(V,C), - /* 17B0 */ _(V,C), _(V,C), _(V,C), _(V,C), _(X,X), _(X,X), _(VR,R), _(VA,T), - /* 17B8 */ _(VA,T), _(VA,T), _(VA,T), _(VB,B), _(VB,B), _(VB,B), _(VA,T), _(VR,R), - /* 17C0 */ _(VR,R), _(VL,L), _(VL,L), _(VL,L), _(VR,R), _(VR,R), _(Xg,X), _(Yg,X), - /* 17C8 */ _(Yg,X), _(Rt,X), _(Rt,X), _(Xg,X), _(Rt,X), _(Xg,X), _(Xg,X), _(Xg,X), - /* 17D0 */ _(Xg,X), _(Xg,X), _(H,X), _(Yg,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 17D8 */ _(X,X), _(GB,C), _(X,X), _(X,X), _(S,SM), _(Yg,X), _(X,X), _(X,X), - /* 17E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 17E8 */ _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - -#define indic_offset_0x1cd0u 1488 - - - /* Vedic Extensions */ - - /* 1CD0 */ _(A,SM), _(A,SM), _(A,SM), _(X,X), _(A,SM), _(A,SM), _(A,SM), _(A,SM), - /* 1CD8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), - /* 1CE0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), - /* 1CE8 */ _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(A,SM), _(S,SM), _(S,SM), - /* 1CF0 */ _(S,SM), _(S,SM), _(C,C), _(C,C), _(A,SM), _(C,C), _(C,C), _(A,SM), - /* 1CF8 */ _(A,SM), _(A,SM), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - -#define indic_offset_0x2008u 1536 - - - /* General Punctuation */ - - /* 2008 */ _(X,X), _(X,X), _(X,X), _(X,X),_(ZWNJ,X),_(ZWJ,X), _(X,X), _(X,X), - /* 2010 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), - /* 2018 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 2020 */ _(X,X), _(X,X), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - -#define indic_offset_0x2070u 1568 - - - /* Superscripts and Subscripts */ - - /* 2070 */ _(X,X), _(X,X), _(X,X), _(X,X),_(SP,SM), _(X,X), _(X,X), _(X,X), - /* 2078 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), - /* 2080 */ _(X,X), _(X,X),_(SP,SM),_(SP,SM),_(SP,SM), _(X,X), _(X,X), _(X,X), - -#define indic_offset_0x25f8u 1592 - - - /* Geometric Shapes */ - - /* 25F8 */ _(X,X), _(X,X), _(X,X), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), - -#define indic_offset_0xa8e0u 1600 - - - /* Devanagari Extended */ - - /* A8E0 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), - /* A8E8 */ _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), _(A,SM), - /* A8F0 */ _(A,SM), _(A,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), _(S,SM), - /* A8F8 */ _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(X,X), _(V,C), _(M,AS), - -#define indic_offset_0xa9e0u 1632 - - - /* Myanmar Extended-B */ - - /* A9E0 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(VA,T), _(X,X), _(C,C), - /* A9E8 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* A9F0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* A9F8 */ _(GB,C), _(GB,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(X,X), - -#define indic_offset_0xaa60u 1664 - - - /* Myanmar Extended-A */ - - /* AA60 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* AA68 */ _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), _(C,C), - /* AA70 */ _(X,X), _(C,C), _(C,C), _(C,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), - /* AA78 */ _(X,X), _(X,X), _(C,C), _(PT,X), _(N,X), _(N,X), _(C,C), _(C,C), - -#define indic_offset_0xfe00u 1696 - - - /* Variation Selectors */ - - /* FE00 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), - /* FE08 */ _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), _(VS,X), - -#define indic_offset_0x11300u 1712 - - - /* Grantha */ - - /* 11300 */ _(X,X),_(SM,SM),_(SM,SM),_(SM,SM), _(X,X), _(X,X), _(X,X), _(X,X), - -#define indic_offset_0x11338u 1720 - - /* 11338 */ _(X,X), _(X,X), _(X,X), _(N,X), _(N,X), _(X,X), _(X,X), _(X,X), - -#define indic_offset_0x116d0u 1728 - - - /* Myanmar Extended-C */ - - /* 116D0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 116D8 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(GB,C), - /* 116E0 */ _(GB,C), _(GB,C), _(GB,C), _(GB,C), _(X,X), _(X,X), _(X,X), _(X,X), - -}; /* Table items: 1752; occupancy: 71% */ +#include + +static const uint16_t _hb_indic_values[42]= +{ + _(A,SM), _(As,X), _(C,C), _(CM,C), _(CS,C), _(DC,C), _(GB,C), _(H,B), + _(H,T), _(H,X), _(M,A), _(M,AP), _(M,AS), _(M,B), _(M,BS), _(M,LM), + _(MH,X), _(ML,X), _(MP,AP), _(MR,X), _(MW,X), _(MY,X), _(N,X), _(PT,X), + _(R,C), _(Rf,X), _(Rt,X), _(S,SM), _(SM,BS), _(SM,SM), _(SP,SM), _(V,C), + _(VA,T), _(VB,B), _(VL,L), _(VR,R), _(VS,X), _(X,X), _(Xg,X), _(Yg,X), + _(ZWJ,X),_(ZWNJ,X), +}; +static const uint8_t _hb_indic_u8[1220]= +{ + 1, 0, 50, 4, 5, 96, 0, 7, 8, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,186, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,192, + 0, 0, 0, 0,208,224, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, + 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 0, 0, 22, 23, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 26, 0, 0, + 0, 27, 0, 0, 0, 0, 28, 29, 30, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 31, 0, 0, 0, 32, 0, 0, 0, 33, 0, 34, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 36, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 25, 2, 10, 0, 0, 0, 0, 26, 0, + 27, 0, 0, 0, 28, 0, 0, 0, 0, 0, 29, 11, 30, 1, 1, 1, + 4, 31, 32, 33, 34, 1, 6, 2, 35, 1, 12, 13, 7, 1, 1, 3, + 36, 14, 15, 37, 16, 17, 6, 2, 38, 39, 18, 40, 7, 1, 1, 3, + 41, 42, 43, 44, 45, 46, 19, 2, 47, 0, 18, 48, 49, 1, 1, 3, + 20, 14, 50, 51, 0, 0, 21, 2, 0, 52, 53, 13, 7, 1, 1, 3, + 20, 54, 15, 55, 56, 17, 6, 2, 57, 0, 58, 59, 60, 61, 62, 63, + 4, 64, 65, 66, 16, 0, 19, 2, 0, 0, 67, 8, 9, 1, 1, 3, + 4, 22, 68, 69, 70, 71, 23, 2, 0, 0, 12, 8, 9, 1, 1, 3, + 72, 22, 73, 74, 75, 76, 23, 2, 77, 0, 78, 8, 9, 1, 1, 1, + 4, 79, 80, 81, 82, 83, 21, 2, 0, 84, 85, 1, 1, 86, 87, 88, + 89, 90, 2, 91, 92, 93, 94, 95, 96, 1, 97, 98, 2, 99, 0, 0, + 0, 0, 1, 1, 1,100,101, 11,102,103,104,105,106,107, 2, 10, + 0, 0, 0, 0,108, 5, 5,109,110,111, 0,112,113, 0,114, 0, + 0, 0, 0, 0, 0, 0, 0, 0,115, 0,116, 0, 0, 0, 0, 0, + 0, 0, 0,117, 0, 0, 0, 0, 0,118, 0, 0, 0, 0, 5, 5, + 119,120, 0, 0, 0, 0,121, 1, 2,122, 0, 0, 0, 0, 1, 1, + 123,124, 24, 24, 0, 0, 0, 0, 0, 0,125, 0, 0, 0, 0, 0, + 0,126, 0, 0, 2, 2, 2, 10, 0, 0, 0, 0, 2, 2, 2, 2, + 4, 4, 4, 4, 10, 2, 2, 2, 26, 2, 2, 2, 8, 8, 8, 8, + 6, 18, 0, 4, 20, 14, 24, 2, 6, 6, 20, 6, 20, 6, 24, 2, + 4, 0, 0, 0, 6, 6, 6, 6, 66, 12, 14, 6, 6, 6, 20, 14, + 2, 0, 36, 50, 52, 18, 38, 68, 0, 0, 0, 32, 0, 0, 2, 16, + 56, 12, 14, 6, 0, 0, 0, 4, 44, 2, 16, 2, 6, 22, 0, 4, + 2, 0, 36, 28, 6, 28, 0, 4, 30, 30, 30, 30, 0, 0, 42, 0, + 34, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 42, 12, 12, 6, 6, + 6, 6, 24, 2, 2, 18, 36,112, 18, 18, 18, 18, 18, 18,114,116, + 118,120,122, 18, 0, 6, 6, 6, 44, 10, 0, 2, 54, 32, 46, 10, + 26, 0, 0, 0, 0, 0, 34, 70, 6, 20, 0, 14, 44, 2, 16, 10, + 2, 0, 72, 50,124, 48, 0, 32, 48, 32, 46, 0,126, 0, 0, 0, + 16, 2, 10, 10, 12, 2,128, 0, 6, 6, 6, 14, 6, 14, 24, 2, + 22, 22, 52, 74, 76, 32, 46, 0, 16, 58, 58, 78,130, 12, 14, 6, + 2, 0, 36,132,134, 32, 46, 0, 0, 0, 80,136, 16, 0, 0, 0, + 0, 70, 14, 6, 6, 20, 0, 6, 20, 6, 24, 0, 16, 10, 10, 2, + 0, 16, 10, 0, 2, 10, 0, 2, 2, 0, 0, 22, 76, 48, 0, 82, + 54, 22, 84, 0, 12, 12,138, 6, 28, 60, 38, 28, 86, 28, 88, 0, + 0, 0,140, 86, 2, 10, 16, 0, 26, 2, 16, 2, 28, 60, 38, 60, + 38, 18, 88, 0, 0, 0, 74, 38, 0, 0, 16, 10,142,144, 0, 0, + 12, 12,146, 6, 2,148,150, 22, 22, 22, 48, 82, 54, 22, 84,152, + 0, 0, 2,154, 0, 0, 0, 14, 0, 2, 2, 2, 2, 2, 26, 2, + 2,156, 2, 2, 90, 6, 6, 6, 6,158, 92, 94,160,162, 62, 58, + 164,166,168,170, 4, 4, 0, 10, 2, 6, 6, 96, 98, 26, 2,172, + 174,100,176,178,100,102,102, 2,104, 62,180, 2, 2,182,184,186, + 12, 12, 12,188, 4, 12,190, 0, 2, 26, 2, 2, 2, 90, 6, 6, + 6, 6, 0, 92, 62, 94, 98,192,194,196, 96,198,200,106,106,108, + 108,202, 0, 0, 42, 0,204, 0, 8,206, 8, 8,208, 40,210, 40, + 40, 2,212,214, 8, 34, 0, 0, 0, 0,216, 0, 4, 4, 4, 0, + 0, 34, 0, 0, 0, 0,110, 0, 0, 64,110, 0, 0, 0,218, 0, + 0, 42, 4, 34, 8, 40, 40, 40, 0, 0, 0,220, 2, 2,104, 16, + 4, 2, 2, 10, 16, 2, 4, 34, 0,222, 78, 2, 56, 12, 0, 0, + 0, 80, 72, 0, 37, 37, 2, 2, 6, 6, 31, 31, 0, 0, 2, 37, + 29, 29, 37, 31, 37, 2, 12, 12, 31, 37, 11, 11, 31, 2, 24, 2, + 14, 14, 36, 36, 37, 11, 6, 37, 22, 27, 12, 37, 27, 27, 37, 6, + 24, 37, 11, 7, 11, 37, 11, 15, 11, 12, 15, 37, 37, 29, 0, 22, + 14, 12, 32, 32, 30, 30, 6, 29, 37, 15, 29, 37, 22, 37, 37, 12, + 12, 11, 22, 22, 37, 22, 15, 15, 11, 8, 14, 37, 14, 8, 2, 31, + 35, 32, 32, 33, 35, 35, 33, 33, 35, 23, 23, 23, 2, 32, 26, 38, + 38, 38, 30, 37, 12, 15, 12, 7, 15, 12, 37, 0, 0, 29, 29, 12, + 18, 11, 37, 13, 37, 3, 37, 28, 11, 10, 10, 37, 10, 11, 29, 31, + 37, 14, 37, 4, 4, 29, 6, 31, 2, 12, 12, 27, 25, 37, 2, 11, + 2, 24, 31, 35, 33, 34, 0, 32, 29, 9, 1, 21, 19, 20, 16, 2, + 21, 21, 17, 2, 23, 2, 2, 35, 32, 2, 20, 35, 34, 32, 32, 29, + 2, 29, 29, 32, 32, 35, 35, 34, 34, 34, 38, 39, 39, 26, 9, 39, + 27, 39, 0, 37, 0, 27, 27, 0, 0, 2, 2, 0, 41, 40, 5, 37, + 31, 12, 2, 23, +}; + +static inline uint8_t _hb_indic_b4 (const uint8_t* a, unsigned i) +{ + return (a[i>>1]>>((i&1)<<2))&15; +} +static inline uint8_t _hb_indic_get_categories_index (unsigned u) +{ + /* packtab: [2^4,2^3,2^3,2^2,2^1] */ + return u<71396u ? (uint8_t)(_hb_indic_u8[996u+_hb_indic_u8[488u+((_hb_indic_u8[186u+((_hb_indic_u8[70u+((_hb_indic_b4(_hb_indic_u8,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)])<<3)+((((((u)>>1))>>2))&7)])<<2)+((((u)>>1))&3)]+((u)&1)]) : 37; +} uint16_t hb_indic_get_categories (hb_codepoint_t u) { - switch (u >> 12) - { - case 0x0u: - if (unlikely (u == 0x00A0u)) return _(GB,C); - if (hb_in_range (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u]; - if (hb_in_range (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u]; - if (hb_in_range (u, 0x0900u, 0x0D7Fu)) return indic_table[u - 0x0900u + indic_offset_0x0900u]; - break; - - case 0x1u: - if (hb_in_range (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u]; - if (hb_in_range (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u]; - if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u]; - break; - - case 0x2u: - if (unlikely (u == 0x25CCu)) return _(DC,C); - if (hb_in_range (u, 0x2008u, 0x2027u)) return indic_table[u - 0x2008u + indic_offset_0x2008u]; - if (hb_in_range (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u]; - if (hb_in_range (u, 0x25F8u, 0x25FFu)) return indic_table[u - 0x25F8u + indic_offset_0x25f8u]; - break; - - case 0xAu: - if (hb_in_range (u, 0xA8E0u, 0xA8FFu)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u]; - if (hb_in_range (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u]; - if (hb_in_range (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u]; - break; - - case 0xFu: - if (hb_in_range (u, 0xFE00u, 0xFE0Fu)) return indic_table[u - 0xFE00u + indic_offset_0xfe00u]; - break; - - case 0x11u: - if (hb_in_range (u, 0x11300u, 0x11307u)) return indic_table[u - 0x11300u + indic_offset_0x11300u]; - if (hb_in_range (u, 0x11338u, 0x1133Fu)) return indic_table[u - 0x11338u + indic_offset_0x11338u]; - if (hb_in_range (u, 0x116D0u, 0x116E7u)) return indic_table[u - 0x116D0u + indic_offset_0x116d0u]; - break; - - default: - break; - } - return _(X,X); + return _hb_indic_values[_hb_indic_get_categories_index (u)]; } #undef _ diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-khmer-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-khmer-machine.hh index 848ed231f7..f1e7a91f05 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-khmer-machine.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-khmer-machine.hh @@ -48,7 +48,7 @@ enum khmer_syllable_type_t { }; -#line 49 "hb-ot-shaper-khmer-machine.hh" +#line 52 "hb-ot-shaper-khmer-machine.hh" #define khmer_syllable_machine_ex_C 1u #define khmer_syllable_machine_ex_DOTTEDCIRCLE 11u #define khmer_syllable_machine_ex_H 4u @@ -66,7 +66,7 @@ enum khmer_syllable_type_t { #define khmer_syllable_machine_ex_ZWNJ 5u -#line 65 "hb-ot-shaper-khmer-machine.hh" +#line 70 "hb-ot-shaper-khmer-machine.hh" static const unsigned char _khmer_syllable_machine_trans_keys[] = { 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 5u, 26u, 1u, 15u, 5u, 26u, 5u, 26u, @@ -294,7 +294,7 @@ find_syllables_khmer (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 287 "hb-ot-shaper-khmer-machine.hh" +#line 298 "hb-ot-shaper-khmer-machine.hh" { cs = khmer_syllable_machine_start; ts = 0; @@ -310,7 +310,7 @@ find_syllables_khmer (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 299 "hb-ot-shaper-khmer-machine.hh" +#line 314 "hb-ot-shaper-khmer-machine.hh" { int _slen; int _trans; @@ -324,7 +324,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 311 "hb-ot-shaper-khmer-machine.hh" +#line 328 "hb-ot-shaper-khmer-machine.hh" } _keys = _khmer_syllable_machine_trans_keys + (cs<<1); @@ -394,7 +394,7 @@ _eof_trans: #line 98 "hb-ot-shaper-khmer-machine.rl" {act = 3;} break; -#line 368 "hb-ot-shaper-khmer-machine.hh" +#line 398 "hb-ot-shaper-khmer-machine.hh" } _again: @@ -403,7 +403,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 375 "hb-ot-shaper-khmer-machine.hh" +#line 407 "hb-ot-shaper-khmer-machine.hh" } if ( ++p != pe ) diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh index 292bc9f3df..4b8da586d3 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-myanmar-machine.hh @@ -50,7 +50,7 @@ enum myanmar_syllable_type_t { }; -#line 51 "hb-ot-shaper-myanmar-machine.hh" +#line 54 "hb-ot-shaper-myanmar-machine.hh" #define myanmar_syllable_machine_ex_A 9u #define myanmar_syllable_machine_ex_As 32u #define myanmar_syllable_machine_ex_C 1u @@ -78,7 +78,7 @@ enum myanmar_syllable_type_t { #define myanmar_syllable_machine_ex_ZWNJ 5u -#line 77 "hb-ot-shaper-myanmar-machine.hh" +#line 82 "hb-ot-shaper-myanmar-machine.hh" static const unsigned char _myanmar_syllable_machine_trans_keys[] = { 1u, 57u, 3u, 57u, 5u, 57u, 5u, 57u, 3u, 57u, 5u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 3u, 57u, 5u, 57u, 1u, 15u, 3u, 57u, 3u, 57u, 3u, 57u, @@ -549,7 +549,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) int cs; hb_glyph_info_t *info = buffer->info; -#line 542 "hb-ot-shaper-myanmar-machine.hh" +#line 553 "hb-ot-shaper-myanmar-machine.hh" { cs = myanmar_syllable_machine_start; ts = 0; @@ -565,7 +565,7 @@ find_syllables_myanmar (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 554 "hb-ot-shaper-myanmar-machine.hh" +#line 569 "hb-ot-shaper-myanmar-machine.hh" { int _slen; int _trans; @@ -579,7 +579,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 566 "hb-ot-shaper-myanmar-machine.hh" +#line 583 "hb-ot-shaper-myanmar-machine.hh" } _keys = _myanmar_syllable_machine_trans_keys + (cs<<1); @@ -649,7 +649,7 @@ _eof_trans: #line 113 "hb-ot-shaper-myanmar-machine.rl" {act = 3;} break; -#line 623 "hb-ot-shaper-myanmar-machine.hh" +#line 653 "hb-ot-shaper-myanmar-machine.hh" } _again: @@ -658,7 +658,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 630 "hb-ot-shaper-myanmar-machine.hh" +#line 662 "hb-ot-shaper-myanmar-machine.hh" } if ( ++p != pe ) diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc index 9487e1c5b0..52566fe29a 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-thai.cc @@ -356,13 +356,11 @@ preprocess_text_thai (const hb_ot_shape_plan_t *plan, sizeof (buffer->out_info[0]) * (end - start - 2)); buffer->out_info[start] = t; } - else - { - /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the - * previous cluster. */ - if (start) - buffer->merge_out_clusters (start - 1, end); - } + + /* Since we decomposed, and NIKHAHIT is combining, merge clusters with the + * previous cluster. */ + if (start) + buffer->merge_out_grapheme_clusters (start - 1, end); } buffer->sync (); diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh index 5072e4d38e..65b6adc36d 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-use-machine.hh @@ -53,7 +53,7 @@ enum use_syllable_type_t { }; -#line 54 "hb-ot-shaper-use-machine.hh" +#line 57 "hb-ot-shaper-use-machine.hh" #define use_syllable_machine_ex_B 1u #define use_syllable_machine_ex_CGJ 6u #define use_syllable_machine_ex_CMAbv 31u @@ -100,7 +100,7 @@ enum use_syllable_type_t { #define use_syllable_machine_ex_ZWNJ 14u -#line 99 "hb-ot-shaper-use-machine.hh" +#line 104 "hb-ot-shaper-use-machine.hh" static const unsigned char _use_syllable_machine_trans_keys[] = { 49u, 51u, 0u, 56u, 11u, 56u, 11u, 56u, 1u, 53u, 14u, 48u, 14u, 47u, 14u, 47u, 14u, 47u, 14u, 46u, 14u, 46u, 14u, 14u, 14u, 48u, 14u, 48u, 14u, 48u, 1u, 14u, @@ -929,7 +929,7 @@ find_syllables_use (hb_buffer_t *buffer) unsigned int act HB_UNUSED; int cs; -#line 922 "hb-ot-shaper-use-machine.hh" +#line 933 "hb-ot-shaper-use-machine.hh" { cs = use_syllable_machine_start; ts = 0; @@ -942,7 +942,7 @@ find_syllables_use (hb_buffer_t *buffer) unsigned int syllable_serial = 1; -#line 931 "hb-ot-shaper-use-machine.hh" +#line 946 "hb-ot-shaper-use-machine.hh" { int _slen; int _trans; @@ -956,7 +956,7 @@ _resume: #line 1 "NONE" {ts = p;} break; -#line 943 "hb-ot-shaper-use-machine.hh" +#line 960 "hb-ot-shaper-use-machine.hh" } _keys = _use_syllable_machine_trans_keys + (cs<<1); @@ -1078,7 +1078,7 @@ _eof_trans: #line 181 "hb-ot-shaper-use-machine.rl" {act = 9;} break; -#line 1039 "hb-ot-shaper-use-machine.hh" +#line 1082 "hb-ot-shaper-use-machine.hh" } _again: @@ -1087,7 +1087,7 @@ _again: #line 1 "NONE" {ts = 0;} break; -#line 1046 "hb-ot-shaper-use-machine.hh" +#line 1091 "hb-ot-shaper-use-machine.hh" } if ( ++p != pe ) diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh b/thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh index aa16a64998..dad4fc8ae7 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-use-table.hh @@ -2,7 +2,7 @@ /* * The following table is generated by running: * - * ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt IndicSyllabicCategory-Additional.txt IndicPositionalCategory-Additional.txt + * gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt ArabicShaping.txt DerivedCoreProperties.txt UnicodeData.txt Blocks.txt Scripts.txt IndicSyllabicCategory-Additional.txt IndicPositionalCategory-Additional.txt * * on files with these headers: * @@ -105,272 +105,272 @@ static const uint8_t hb_use_u8[3343]= { - 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 15, 0, 1, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 4, 2, 2, - 5, 6, 2, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2, 2, 17, - 18, 19, 20, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 2, 33, 2, 2, 2, - 2, 34, 35, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 2, 2, 2, - 37, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 38, 2, 39, 2, 2, + 33, 3, 0, 0, 0, 4, 0, 80, 118, 128, 9, 10, 11, 192, 208, 14, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 5, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 7, 0, 0, + 8, 9, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 20, + 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 36, 0, 0, 0, + 0, 37, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, + 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 0, 42, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 43, 44, 45, 46, 47, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, + 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 54, 0, 0, 0, + 0, 0, 0, 0, 0, 55, 56, 0, 57, 0, 0, 58, 59, 0, 60, 61, + 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 0, 73, 74, 75, 76, + 0, 77, 0, 78, 79, 80, 81, 82, 0, 83, 84, 85, 86, 0, 87, 88, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 89, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 90, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 91, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 92, 93, 0, 0, 0, 94, 0, 0, 0, 95, + 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 97, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 99, 0, 0, 0, 0, 0, + 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 101, 0, 0, 102, 0, 0, 0, 103, 0, 104, 0, 0, 0, + 0, 0, 0, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 106, 107, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 40, 41, 42, 43, 44, 45, 2, 46, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 47, 48, 2, - 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 50, 51, 2, 2, 2, - 2, 2, 2, 2, 2, 52, 53, 2, 54, 2, 2, 55, 56, 2, 57, 58, - 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 2, 70, 71, 72, 73, - 2, 74, 2, 75, 76, 77, 78, 79, 2, 80, 81, 82, 83, 2, 84, 85, - 2, 86, 86, 86, 86, 86, 86, 86, 86, 87, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, 86, - 86, 86, 86, 86, 86, 86, 86, 86, 88, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 89, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 90, 91, 2, 2, 2, 92, 2, 2, 2, 93, - 94, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 95, 95, 95, 96, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 97, 98, 2, 2, 2, 2, 2, - 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 100, 2, 2, 101, 2, 2, 2, 102, 2, 103, 2, 2, 2, - 2, 2, 2, 104, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 105, 105, 106, 107, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, - 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 11, - 11, 11, 11, 0, 0, 0, 9, 12, 0, 2, 2, 2, 2, 13, 14, 0, - 0, 11, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 16, 17, - 18, 19, 20, 21, 22, 16, 23, 24, 25, 12, 26, 27, 20, 2, 2, 2, - 2, 2, 20, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, - 2, 28, 29, 30, 2, 2, 2, 9, 30, 9, 30, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 9, 0, 2, 2, 0, 17, - 18, 19, 20, 31, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 2, - 30, 2, 0, 0, 0, 0, 0, 9, 36, 12, 15, 30, 2, 2, 9, 0, - 30, 9, 2, 30, 9, 2, 0, 37, 18, 19, 31, 0, 27, 38, 27, 39, - 0, 40, 0, 0, 0, 30, 2, 9, 9, 0, 0, 0, 2, 2, 2, 2, - 2, 41, 42, 43, 0, 0, 0, 0, 0, 12, 15, 30, 2, 2, 2, 2, - 30, 2, 30, 2, 2, 2, 2, 2, 2, 9, 2, 30, 2, 2, 0, 17, - 18, 19, 20, 21, 27, 22, 35, 24, 0, 0, 0, 0, 0, 30, 41, 41, - 44, 12, 29, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 0, 17, - 45, 0, 0, 27, 22, 0, 0, 2, 30, 30, 0, 0, 0, 0, 0, 0, - 0, 0, 46, 30, 2, 2, 9, 0, 2, 9, 2, 2, 0, 30, 9, 9, - 2, 0, 30, 9, 0, 2, 9, 0, 2, 2, 2, 2, 2, 2, 0, 0, - 23, 16, 47, 0, 48, 33, 48, 34, 0, 0, 0, 0, 35, 0, 0, 0, - 0, 15, 29, 49, 2, 2, 2, 9, 2, 9, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 0, 17, 22, 16, 23, 47, 22, 38, 22, 39, - 0, 0, 0, 27, 31, 2, 9, 0, 0, 10, 29, 30, 2, 2, 2, 9, - 2, 2, 2, 30, 2, 2, 0, 17, 45, 0, 0, 35, 47, 0, 0, 0, - 9, 50, 51, 0, 0, 0, 0, 0, 0, 11, 29, 2, 2, 2, 2, 9, - 2, 2, 2, 2, 2, 2, 52, 53, 23, 19, 20, 31, 48, 33, 48, 34, - 54, 0, 0, 0, 35, 0, 0, 0, 30, 12, 29, 30, 2, 2, 2, 2, - 2, 2, 2, 2, 9, 0, 2, 2, 2, 2, 30, 2, 2, 2, 2, 30, - 0, 2, 2, 2, 9, 0, 55, 0, 35, 23, 22, 31, 31, 18, 48, 48, - 25, 0, 23, 0, 0, 0, 0, 0, 0, 2, 0, 2, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 2, 56, 56, 57, 0, 0, - 18, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, - 0, 58, 21, 59, 22, 22, 20, 20, 46, 21, 11, 31, 11, 2, 2, 60, - 61, 61, 61, 61, 61, 62, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, - 61, 61, 61, 61, 61, 61, 61, 63, 0, 0, 0, 0, 64, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 65, 45, 59, 66, 22, 22, 67, 68, 69, 70, - 71, 2, 2, 2, 2, 2, 1, 0, 5, 2, 2, 2, 23, 20, 2, 2, - 72, 71, 73, 74, 65, 73, 29, 29, 2, 52, 22, 53, 2, 2, 2, 2, - 2, 2, 75, 76, 77, 29, 29, 78, 79, 2, 2, 2, 2, 2, 29, 45, - 0, 2, 59, 80, 0, 0, 0, 0, 30, 2, 59, 47, 0, 0, 0, 0, - 0, 2, 59, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 9, - 2, 9, 59, 0, 0, 0, 0, 0, 0, 2, 2, 81, 45, 22, 59, 20, - 48, 48, 48, 48, 15, 82, 83, 84, 85, 86, 87, 0, 0, 0, 0, 88, - 0, 9, 0, 0, 30, 0, 89, 81, 90, 2, 2, 2, 2, 9, 0, 0, - 0, 42, 42, 91, 92, 2, 2, 2, 2, 2, 2, 2, 2, 13, 9, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 9, 22, 80, 45, 22, 93, 61, 0, - 0, 94, 95, 94, 94, 96, 97, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 0, 2, 2, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, - 0, 2, 2, 2, 2, 29, 0, 0, 0, 2, 2, 2, 2, 2, 9, 0, - 0, 2, 2, 2, 52, 98, 45, 0, 0, 2, 2, 99, 100, 101, 102, 61, - 63, 103, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 104, 46, - 40, 11, 105, 74, 2, 2, 2, 2, 2, 2, 2, 106, 22, 20, 20, 22, - 48, 48, 22, 107, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 108, 109, - 109, 109, 109, 0, 0, 0, 0, 0, 0, 105, 74, 2, 2, 2, 2, 2, - 2, 60, 61, 59, 25, 22, 110, 61, 2, 2, 2, 2, 106, 22, 23, 45, - 45, 101, 111, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 112, - 101, 101, 101, 113, 114, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, - 2, 11, 46, 115, 115, 115, 11, 115, 115, 15, 115, 115, 115, 26, 0, 40, - 0, 0, 0, 116, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 117, 0, - 0, 0, 0, 0, 0, 0, 6, 118, 119, 42, 42, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 119, 120, 119, 119, 119, 119, 119, 119, 119, - 119, 0, 0, 121, 0, 0, 0, 0, 0, 0, 7, 121, 0, 0, 0, 0, - 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, - 0, 0, 0, 0, 122, 122, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, - 30, 0, 0, 0, 0, 0, 0, 0, 123, 0, 122, 122, 0, 0, 0, 0, - 0, 2, 53, 2, 107, 2, 10, 2, 2, 2, 65, 19, 16, 0, 0, 31, - 0, 2, 2, 0, 0, 0, 0, 0, 0, 29, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 124, 23, 23, 23, 23, 23, 23, 23, 125, 0, 0, 0, 0, - 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 0, 0, 0, 0, 0, - 52, 2, 2, 2, 22, 22, 126, 115, 0, 2, 2, 2, 127, 20, 59, 20, - 112, 101, 128, 0, 0, 0, 0, 0, 0, 11, 129, 2, 2, 2, 2, 2, - 2, 2, 130, 23, 22, 20, 48, 131, 132, 133, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 52, 30, 2, 2, 2, 2, 2, 2, 2, 2, 10, 22, 59, - 98, 76, 134, 135, 136, 0, 0, 0, 0, 2, 137, 2, 2, 2, 2, 138, - 0, 30, 2, 42, 5, 0, 79, 15, 2, 139, 20, 53, 127, 139, 2, 2, - 140, 10, 9, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 141, 21, - 25, 0, 0, 142, 143, 0, 0, 0, 0, 2, 65, 45, 23, 80, 47, 144, - 0, 81, 81, 81, 81, 81, 81, 81, 81, 0, 0, 0, 0, 0, 0, 0, - 6, 119, 119, 119, 119, 120, 0, 0, 0, 2, 2, 2, 2, 2, 9, 2, - 2, 2, 9, 2, 30, 2, 2, 2, 2, 2, 30, 2, 2, 2, 30, 9, - 0, 127, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, - 2, 2, 2, 2, 0, 14, 37, 0, 147, 2, 2, 13, 37, 0, 30, 2, - 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, - 9, 2, 2, 11, 41, 0, 0, 0, 0, 2, 2, 2, 0, 27, 22, 22, - 30, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, - 0, 2, 2, 2, 115, 115, 115, 115, 115, 148, 2, 9, 0, 0, 0, 0, - 0, 2, 14, 14, 0, 0, 0, 0, 0, 9, 2, 2, 9, 2, 2, 2, - 2, 30, 2, 9, 0, 30, 2, 0, 0, 149, 150, 151, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 22, 22, 20, 20, 20, 22, 22, 133, 0, 0, 0, - 0, 0, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 2, 2, 2, 2, - 2, 53, 52, 53, 0, 0, 0, 0, 153, 11, 74, 2, 2, 2, 2, 2, - 2, 18, 19, 21, 16, 24, 37, 0, 0, 0, 31, 0, 0, 0, 0, 0, - 0, 11, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 127, 20, 22, 154, - 22, 21, 155, 156, 2, 2, 2, 2, 2, 0, 0, 65, 157, 0, 0, 0, - 0, 2, 13, 0, 0, 0, 0, 0, 0, 2, 65, 25, 20, 20, 20, 22, - 22, 107, 158, 0, 0, 56, 159, 31, 160, 30, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 23, 19, 22, 22, 161, 44, 0, 0, 0, - 49, 127, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 9, 2, 2, - 30, 2, 2, 2, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, - 10, 18, 19, 21, 22, 162, 31, 0, 0, 11, 11, 30, 2, 2, 2, 9, - 30, 9, 2, 30, 2, 2, 58, 17, 23, 16, 23, 47, 32, 33, 32, 34, - 0, 0, 0, 0, 35, 0, 0, 0, 2, 2, 23, 0, 11, 11, 11, 46, - 0, 11, 11, 46, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 30, 0, - 9, 2, 2, 2, 30, 45, 59, 20, 20, 31, 33, 32, 32, 25, 163, 29, - 164, 165, 37, 0, 0, 0, 0, 0, 0, 12, 26, 0, 0, 0, 0, 0, - 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 125, 15, 17, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 0, 0, 166, 167, 0, 0, 0, 0, 0, 0, - 0, 18, 19, 20, 20, 66, 98, 25, 160, 11, 168, 9, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 65, 25, 20, 20, 0, 48, 48, 11, - 169, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, - 0, 23, 19, 20, 20, 21, 16, 82, 169, 38, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 10, 170, 25, 20, 22, 22, 168, 9, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 43, 135, 23, 22, 20, 76, 21, 22, 0, - 0, 2, 2, 2, 9, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 18, - 19, 20, 21, 22, 104, 169, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, - 2, 2, 2, 30, 9, 2, 2, 2, 2, 23, 23, 18, 32, 33, 12, 171, - 165, 172, 173, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, - 2, 65, 25, 20, 20, 0, 22, 23, 29, 107, 0, 33, 0, 0, 0, 0, - 0, 52, 20, 22, 22, 22, 139, 2, 2, 2, 174, 140, 11, 15, 175, 61, - 176, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, - 2, 2, 2, 158, 158, 158, 177, 177, 177, 177, 177, 177, 15, 178, 0, 30, - 0, 16, 20, 16, 16, 0, 0, 0, 0, 22, 20, 20, 31, 22, 22, 11, - 169, 0, 61, 61, 61, 61, 61, 61, 61, 66, 21, 82, 46, 0, 0, 0, - 0, 2, 2, 2, 9, 2, 30, 2, 2, 52, 22, 22, 31, 0, 38, 22, - 27, 11, 159, 179, 180, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, - 2, 2, 2, 2, 2, 2, 23, 23, 47, 22, 35, 82, 68, 0, 0, 0, - 0, 2, 181, 66, 47, 0, 0, 0, 0, 11, 182, 2, 2, 2, 2, 2, - 2, 2, 2, 23, 22, 20, 31, 0, 48, 16, 143, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 156, 0, 0, 183, 183, 183, 183, 183, 183, 183, - 183, 184, 184, 184, 185, 186, 184, 183, 183, 187, 183, 183, 188, 189, 189, 189, - 189, 189, 189, 189, 0, 0, 0, 0, 0, 183, 183, 183, 183, 183, 190, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 191, 192, - 193, 11, 11, 11, 46, 0, 0, 0, 0, 29, 74, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 65, 47, 0, 2, 2, 2, 2, 2, 9, 0, - 58, 194, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 0, 0, 0, 40, 115, 26, 0, 0, 0, 0, 0, - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 30, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 119, 119, 119, 120, 0, - 0, 11, 11, 11, 49, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, 2, 2, 2, 2, 2, 11, - 11, 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, - 20, 2, 2, 44, 44, 44, 92, 0, 0, O, O, O, GB, B, B, O, - SB, O, SE, GB, O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv, - VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, - VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, - VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, O, O, VPre, VPre, O, VPre, H, O, - VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, - MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv, B, O, CS, CS, - VMPst, B, VAbv, VAbv, B, R, O, HVM, O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, - VBlw, B, SUB, SUB, SUB, O, SUB, SUB, O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv, - VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, - VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv, - FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB, - CMAbv,CMAbv, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, - MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, - B, O,SMAbv,SMAbv,SMAbv, VPst, IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw, - VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, - H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, - MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, - B, VBlw,VMAbv, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, - IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, - IS,CMAbv, O, VPst, B, R, R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, - O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, - VPst, MPst, R, MPst,CMBlw, B,FMBlw, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, IS, VBlw, - IS, R, MBlw, GB, VAbv, R,VMPst, G, G, J, J, J, SB, SE, J, HR, - G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, + 4, 2, 2, 2, 2, 5, 2, 2, 2, 2, 6, 0, 2, 2, 2, 2, + 2, 5, 8, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, + 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, + 0, 2, 2, 2, 2, 5, 0, 0, 0, 2, 2, 2, 2, 2, 5, 0, + 0, 2, 8, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 74, 75, + 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 2, 2, 2, 2, 2, 2, 2, 17, 6, 13, 29, 33, 29, 45, + 0, 15, 11, 27, 7, 38, 7, 68, 0, 2, 2, 2, 2, 8, 2, 2, + 2, 10, 10, 10, 10, 80, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, + 5, 2, 2, 5, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 5, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, + 0, 9, 41, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 23, 6, 6, 6, 6, 6, 6, 6, + 6, 0, 0, 0, 0, 0, 0, 48, 0, 44, 0, 0, 0, 0, 0, 49, + 0, 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 103, 2, 2, 2, 2, 2, 36, 9, + 9, 9, 9, 0, 0, 0, 5, 31, 0, 2, 2, 2, 2, 50, 51, 0, + 0, 9, 24, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 32, + 19, 17, 6, 18, 7, 15, 11, 67, 22, 31, 52, 25, 6, 2, 2, 2, + 2, 104, 14, 8, 2, 2, 2, 5, 8, 5, 5, 0, 2, 2, 0, 32, + 19, 0, 0, 0, 34, 0, 0, 2, 8, 2, 0, 0, 0, 0, 0, 5, + 105, 31, 24, 8, 2, 2, 5, 0, 8, 5, 2, 8, 5, 2, 0, 35, + 19, 17, 13, 0, 25, 38, 25, 68, 0, 53, 0, 0, 0, 8, 2, 5, + 5, 54, 39, 77, 0, 0, 0, 0, 0, 31, 24, 8, 2, 2, 2, 2, + 8, 5, 2, 8, 2, 2, 0, 32, 19, 17, 6, 18, 25, 7, 34, 67, + 0, 0, 0, 0, 0, 8, 54, 54, 46, 31, 14, 8, 2, 2, 2, 5, + 8, 5, 2, 8, 2, 2, 0, 32, 20, 0, 0, 25, 7, 0, 0, 2, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 26, 8, 2, 2, 5, 0, + 2, 5, 2, 2, 0, 8, 5, 5, 2, 0, 8, 5, 0, 2, 5, 0, + 2, 2, 2, 2, 2, 2, 0, 0, 11, 15, 27, 0, 12, 33, 12, 45, + 0, 0, 0, 0, 34, 0, 0, 0, 0, 24, 14, 55, 2, 2, 2, 5, + 2, 2, 2, 2, 2, 2, 0, 32, 7, 0, 0, 25, 13, 2, 5, 0, + 0, 36, 14, 8, 2, 2, 2, 5, 2, 2, 2, 8, 2, 2, 0, 32, + 20, 0, 0, 34, 27, 0, 0, 0, 5, 106, 78, 0, 0, 0, 0, 0, + 0, 9, 14, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 30, 40, + 11, 17, 6, 13, 12, 33, 12, 45, 107, 0, 0, 0, 34, 0, 0, 0, + 8, 31, 14, 8, 2, 2, 2, 2, 2, 2, 2, 2, 5, 0, 2, 2, + 2, 2, 8, 2, 2, 2, 2, 8, 0, 2, 2, 2, 5, 0, 108, 0, + 34, 11, 7, 13, 13, 19, 12, 12, 22, 0, 11, 0, 0, 0, 0, 0, + 0, 2, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, + 0, 2, 2, 69, 69, 109, 0, 0, 19, 56, 18, 21, 7, 7, 6, 6, + 26, 18, 9, 13, 9, 2, 2, 79, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 81, 0, 0, 0, 0, 110, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 23, 20, 21, 57, 7, 7, 111, 82, 112, 113, + 83, 2, 2, 2, 2, 2, 48, 0, 44, 2, 2, 2, 11, 6, 2, 2, + 114, 83, 84, 41, 23, 84, 14, 14, 2, 30, 7, 40, 2, 2, 2, 2, + 2, 2, 115, 58, 116, 14, 14, 117, 85, 2, 2, 2, 2, 2, 14, 20, + 0, 2, 21, 59, 0, 0, 0, 0, 8, 2, 21, 27, 0, 0, 0, 0, + 0, 2, 21, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 5, + 2, 5, 21, 0, 0, 0, 0, 0, 0, 2, 2, 4, 20, 7, 21, 6, + 12, 12, 12, 12, 24, 60, 118, 119, 120, 121, 122, 0, 0, 0, 0, 123, + 0, 5, 0, 0, 8, 0, 124, 4, 125, 39, 39, 126, 86, 2, 2, 2, + 2, 2, 2, 2, 2, 50, 5, 0, 0, 7, 59, 20, 7, 127, 10, 0, + 0, 70, 128, 70, 70, 129, 130, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 2, 2, 14, 0, 0, 0, 2, 2, 2, 30, 71, 20, 0, + 0, 2, 2, 131, 132, 42, 133, 10, 81, 134, 15, 20, 7, 21, 18, 59, + 12, 12, 58, 9, 9, 9, 87, 26, 53, 9, 88, 41, 2, 2, 2, 2, + 2, 2, 2, 89, 7, 6, 6, 7, 12, 12, 7, 61, 2, 2, 2, 5, + 0, 0, 0, 0, 0, 0, 135, 62, 62, 62, 62, 0, 0, 0, 0, 0, + 0, 88, 41, 2, 2, 2, 2, 2, 2, 79, 10, 21, 22, 7, 136, 10, + 2, 2, 2, 2, 89, 7, 11, 20, 20, 42, 137, 0, 0, 0, 0, 0, + 0, 2, 2, 10, 19, 12, 11, 90, 42, 42, 42, 138, 139, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 8, 2, 9, 26, 16, 16, 16, 9, 16, + 16, 24, 16, 16, 16, 52, 0, 53, 0, 0, 0, 140, 78, 9, 44, 0, + 0, 0, 0, 0, 0, 0, 141, 0, 0, 0, 0, 0, 0, 0, 49, 142, + 3, 39, 39, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, + 72, 0, 0, 91, 0, 0, 0, 0, 0, 0, 76, 91, 0, 0, 0, 0, + 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 63, 63, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, 143, 0, 63, 63, 0, 0, 0, 0, + 0, 2, 40, 2, 61, 2, 36, 2, 2, 2, 23, 17, 15, 0, 0, 13, + 0, 14, 2, 2, 2, 2, 2, 2, 2, 2, 2, 144, 11, 11, 11, 11, + 11, 11, 11, 92, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 2, 0, 0, 0, 0, 0, 30, 2, 2, 2, 7, 7, 145, 16, + 0, 2, 2, 2, 47, 6, 21, 6, 90, 42, 146, 0, 0, 0, 0, 0, + 0, 9, 147, 2, 2, 2, 2, 2, 2, 2, 148, 11, 7, 6, 12, 149, + 150, 93, 0, 0, 0, 0, 0, 0, 0, 2, 2, 30, 8, 2, 2, 2, + 2, 2, 2, 2, 2, 36, 7, 21, 71, 58, 151, 94, 152, 0, 0, 0, + 0, 2, 153, 2, 2, 2, 2, 154, 0, 8, 2, 39, 44, 0, 85, 24, + 2, 73, 6, 40, 47, 73, 2, 2, 95, 36, 5, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 155, 18, 22, 0, 0, 156, 96, 0, 0, 0, + 0, 2, 23, 20, 11, 59, 27, 157, 0, 0, 0, 0, 0, 0, 0, 0, + 49, 3, 3, 3, 3, 72, 0, 0, 0, 2, 5, 2, 8, 2, 2, 2, + 2, 2, 8, 2, 2, 2, 8, 5, 0, 47, 6, 25, 13, 0, 0, 158, + 159, 2, 2, 8, 2, 8, 2, 2, 2, 2, 2, 2, 0, 51, 35, 0, + 97, 2, 2, 50, 35, 0, 8, 2, 2, 0, 0, 0, 0, 8, 2, 2, + 5, 2, 2, 9, 54, 0, 0, 0, 0, 2, 2, 2, 0, 25, 7, 7, + 8, 2, 2, 2, 2, 2, 25, 38, 0, 2, 2, 2, 16, 16, 16, 16, + 16, 160, 2, 5, 0, 0, 0, 0, 0, 2, 51, 51, 0, 0, 0, 0, + 0, 5, 2, 2, 5, 2, 2, 2, 2, 8, 2, 5, 0, 8, 2, 0, + 0, 161, 162, 163, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 6, + 6, 6, 7, 7, 93, 0, 0, 0, 0, 0, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 2, 2, 2, 2, 2, 40, 30, 40, 0, 0, 0, 0, + 164, 19, 17, 18, 15, 67, 35, 0, 0, 0, 13, 0, 0, 0, 0, 0, + 0, 9, 55, 2, 2, 2, 2, 2, 2, 2, 2, 2, 47, 6, 7, 165, + 7, 18, 166, 98, 2, 2, 2, 2, 2, 0, 0, 23, 167, 0, 0, 0, + 0, 2, 50, 0, 0, 0, 0, 0, 0, 2, 23, 22, 6, 6, 6, 7, + 7, 61, 64, 0, 0, 69, 99, 13, 100, 2, 2, 2, 2, 2, 2, 11, + 17, 7, 7, 168, 46, 0, 0, 0, 55, 47, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 5, 5, 2, 2, 8, 2, 2, 2, 2, 2, 2, 2, + 8, 2, 2, 2, 2, 2, 2, 2, 36, 19, 17, 18, 7, 169, 13, 0, + 0, 9, 9, 8, 2, 2, 2, 5, 8, 5, 2, 8, 2, 2, 56, 32, + 11, 15, 11, 27, 29, 33, 29, 45, 0, 0, 0, 0, 34, 0, 0, 0, + 2, 2, 11, 0, 9, 9, 9, 26, 0, 9, 9, 26, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 8, 0, 5, 2, 2, 2, 8, 20, 21, 6, + 6, 13, 33, 29, 29, 22, 170, 14, 171, 101, 35, 0, 0, 0, 0, 0, + 0, 31, 52, 0, 0, 0, 0, 0, 0, 2, 2, 23, 22, 6, 6, 6, + 7, 11, 92, 24, 32, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, + 172, 173, 0, 0, 0, 0, 0, 0, 0, 19, 17, 6, 6, 57, 71, 22, + 100, 9, 102, 5, 0, 0, 0, 0, 0, 22, 6, 6, 0, 12, 12, 9, + 65, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 6, + 0, 11, 17, 6, 6, 18, 15, 60, 65, 38, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 36, 174, 22, 6, 7, 7, 102, 5, 0, 0, + 0, 2, 2, 2, 2, 2, 5, 77, 94, 11, 7, 6, 58, 18, 7, 0, + 0, 2, 2, 2, 5, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 19, + 17, 6, 18, 7, 87, 65, 35, 0, 0, 2, 2, 2, 5, 8, 0, 2, + 2, 2, 2, 8, 5, 2, 2, 2, 2, 11, 11, 19, 29, 33, 31, 175, + 101, 176, 177, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, + 2, 23, 22, 6, 6, 0, 7, 11, 14, 61, 0, 33, 0, 0, 0, 0, + 0, 30, 6, 7, 7, 7, 73, 2, 2, 2, 178, 95, 9, 24, 179, 10, + 180, 0, 0, 48, 97, 0, 0, 0, 0, 30, 6, 7, 15, 17, 6, 2, + 2, 2, 2, 64, 64, 64, 43, 43, 43, 43, 43, 43, 24, 181, 0, 8, + 0, 15, 6, 15, 15, 0, 0, 0, 0, 7, 6, 6, 13, 7, 7, 9, + 65, 0, 10, 10, 10, 10, 10, 10, 10, 57, 18, 60, 26, 0, 0, 0, + 0, 2, 2, 2, 5, 2, 8, 2, 2, 30, 7, 7, 13, 0, 38, 7, + 25, 9, 99, 182, 183, 0, 0, 0, 0, 2, 2, 2, 8, 5, 2, 2, + 2, 2, 2, 2, 2, 2, 11, 11, 27, 7, 34, 60, 82, 0, 0, 0, + 0, 2, 184, 57, 27, 0, 0, 0, 0, 9, 185, 2, 2, 2, 2, 2, + 2, 2, 2, 11, 7, 6, 13, 0, 12, 15, 96, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 98, 0, 0, 66, 66, 66, 186, 187, 66, 1, + 1, 188, 1, 1, 189, 37, 37, 37, 37, 37, 37, 37, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 190, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 191, 192, 193, 9, 9, 9, 26, 0, 0, 0, + 0, 14, 41, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23, 27, + 0, 2, 2, 2, 2, 2, 5, 0, 56, 194, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 0, 0, 0, 53, 16, 52, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 2, 2, 2, 2, 2, 0, 56, 35, 0, 49, 3, 3, 3, 72, 0, + 0, 9, 9, 9, 55, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 26, 2, 2, 2, 2, 2, 2, 9, + 9, 2, 2, 2, 2, 2, 2, 7, 7, 2, 2, 2, 2, 2, 2, 2, + 6, 2, 2, 46, 46, 46, 86, 0, 0, O, O, G, G, B, B, WJ, + WJ, CGJ, CGJ, B, O, VBlw, VBlw, VAbv, VAbv, O, B,VMAbv,VMAbv, SUB, SUB, VPst, + VPst, VPre, VPre, VBlw, O,VMPst,VMPst, VAbv, VPst,VMBlw,VMBlw, VPst, VBlw, VBlw, VAbv, VPst, + VPre, VPst, VAbv, VAbv, VBlw, VPre, VPst, B, VPst,VMAbv,VMPst, O, VAbv,VMAbv, O, VPst, + O, N, N, O, VPre, B, VAbv, O,VMAbv,CMBlw, B, VPre, O, O, VPst,CMBlw, + O, B,VMAbv, HM, HM, VAbv, O, GB, GB, VAbv, B,VMPst, B, FAbv, FAbv, FBlw, + FBlw, GB, O, VPre, H,CMAbv,CMAbv, B, VBlw, O, GB, O, WJ, B,CMBlw,CMBlw, + CMBlw,VMBlw, O, O,VMBlw,VMAbv,CMAbv,VMAbv, B, O,CMBlw, VBlw, VPre, VPre, VAbv, VBlw, + VPst, VPst,VMAbv, H, B,SMAbv,SMAbv, SB, SE, R, R,VMPst, H, J, J, VPst, + H, VAbv, H, O,FMBlw, FPst, FPst, VAbv, VPre, WJ, O, VBlw, B, O, SB, O, + SE,FMPst,FMPst, O, MBlw, CS,VMPst, B, SUB, O, SUB, SUB, O,VMPst, IS, MBlw, + B, VPst,VMPst, B,VMPst,CMAbv, B, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, VBlw, FAbv,FMPst, + O, H,VMAbv, H, O, MPre, MAbv, VBlw,VMAbv, IS, O, O, IS,CMAbv, O,CMBlw, + VAbv, VPre,VMAbv, IS, R, H,CMBlw, O, CGJ, GB,VMAbv,FMAbv, O, O, CS, R, + O, HVM, O, O,CMAbv,FMBlw, O,VMAbv,VMBlw, VAbv, MPst, MPre, MBlw, MBlw, MBlw, MBlw, + VPst, VAbv,VMPst,VMPst,VMBlw,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B, + FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv, VAbv, SUB,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, B, + MPre, MBlw, SUB, MAbv, SUB, Sk, VPst, O,SMAbv, VPst, IS, RK, RK,VMPre,VMPre,FMAbv, + CMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, O, H, MPst, VPst, VAbv,VMBlw, FPst, VPst, FAbv, + VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, VBlw, MPst, MBlw, O, B, FAbv, FAbv, FPst, B, + VPre, O,VMPst,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, + B, O, HN, VPre, VBlw, VAbv, IS, VPst, B,VMAbv, H, VAbv,CMBlw,VMPst, O,VMAbv, + CMBlw,FMAbv, B, CS, CS,VMPst, VAbv,VMAbv, VPst, MPst, R, MPst,CMBlw, B,FMBlw, CS, + SUB, SUB, GB,CMAbv, IS, VBlw, IS, R, MBlw, GB, VAbv, R,VMPst, J, SB, SE, + J, HR, G, G, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, }; static const uint16_t hb_use_u16[864]= { - 0, 0, 1, 2, 0, 3, 0, 3, 0, 0, 4, 5, 0, 6, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, - 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 9, 10, 11, 12, - 0, 0, 0, 0, 10, 13, 0, 0, 14, 10, 10, 15, 16, 17, 18, 19, - 20, 21, 22, 23, 24, 25, 18, 26, 27, 21, 22, 28, 29, 30, 31, 32, - 33, 34, 22, 35, 36, 0, 18, 37, 38, 21, 22, 39, 24, 40, 18, 41, - 42, 43, 44, 45, 46, 47, 31, 0, 48, 49, 22, 50, 51, 52, 18, 0, - 53, 49, 22, 54, 51, 55, 18, 56, 57, 49, 10, 58, 59, 60, 18, 0, - 61, 62, 10, 63, 64, 65, 31, 66, 67, 68, 10, 69, 70, 10, 71, 72, - 73, 74, 75, 76, 77, 0, 0, 0, 10, 10, 78, 79, 80, 81, 82, 83, - 84, 85, 0, 0, 0, 0, 0, 0, 10, 86, 10, 87, 10, 88, 89, 90, - 10, 10, 10, 91, 92, 93, 2, 0, 94, 0, 10, 10, 10, 10, 10, 95, - 96, 10, 97, 0, 0, 0, 0, 0, 10, 98, 99,100, 31, 10,101,102, - 10, 10,103, 10,104,105, 0, 0, 10,106, 10, 10, 10,107,108,109, - 2, 2, 0, 0, 0, 0, 0, 0,110, 10, 10,111,112, 2,113,114, - 115, 10,116, 10, 10, 10,117,118, 10, 10,119,120,121, 0, 0, 0, - 0, 0, 0, 0, 0,122,123,124, 0, 0, 0, 0, 0, 0, 0,125, - 126,127,128, 0, 0, 0,129,130,131, 0, 0, 0, 0, 0, 0,132, - 0, 0, 0, 0,133, 0, 0, 0, 0, 0, 0, 0, 0, 0,134, 0, - 0, 0, 0, 10, 10, 10,135,136, 0, 0,137, 0, 0, 0, 0, 0, - 138, 10,139, 0, 10, 10, 10,140,141, 10, 10,142,143, 2,144,145, - 10, 10,146, 10,147,148, 0, 0,149, 10, 10,150,151, 2,152, 98, - 10, 10,153,154,155, 2, 10,156, 10, 10, 10,157,158, 0,159,160, - 0, 0, 0, 0, 10, 10,161, 2,162, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,163, 0, 0, 0, 0, 0, 0, 0,164, - 0, 0, 0, 0, 0, 0, 0,165,165,166, 34,167, 0, 0, 0, 0, - 168,169, 10,170, 95, 0, 0, 0, 0, 0, 0, 0, 70, 10,171, 0, - 10,172,173, 0, 0, 0, 0, 0, 10, 10,174, 2, 9, 10,175, 10, - 176, 0, 0, 0, 0, 0, 0, 0, 10, 10,177,172, 0, 0, 0, 0, - 0, 0, 0, 10,178,179, 0, 10,180, 0, 0,181,182, 0, 0, 0, - 183, 10, 10,184,185,186,187,188,189, 10, 10,190,191, 0, 0, 0, - 192, 10,193,194,195, 10, 10,196,189, 10, 10,197,198,105,199,102, - 10, 34,200,201,202, 0, 0, 0,203,204, 95, 10, 10,205,206, 2, - 207, 21, 22,208,209,210,211,212,213, 10, 10,214,215,216,217, 0, - 10, 10, 10,218,219,220,221, 0,199, 10, 10,222,223, 2, 0, 0, - 10, 10,224,225,226,227, 0, 0, 10, 10, 10,228,229, 2, 0, 0, - 10, 10,230,231, 2, 10,140, 0, 10,232,233,103,234, 0, 0, 0, - 10, 10,235,236, 0, 0, 0, 0,237,238, 10,239,240, 2, 0, 0, - 0, 0,241, 10, 10,242,243, 0,244, 10, 10,245,246,247, 10, 10, - 248,249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,250, 0, - 22, 10,224,251, 8, 10, 71, 19, 10,252, 74,253, 0, 0, 0, 0, - 254, 10, 10,255,256, 2,257, 10,258,259, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 10,260,261, 49, 10,262,263,264, 0, 0, - 265,265,265,265,265,265,265,265,265,265,265,266,267,268,265,265, - 265,265,265,265,265,265,265,269, 10,270,271, 2, 0, 0, 0, 0, - 0, 0, 0, 0, 2, 0, 0, 0, 10, 10, 10,272, 0, 0, 0, 0, - 0, 0, 0, 0,273, 10,274, 2, 10, 10, 10, 10,275,276,277,277, - 278,279, 0, 0, 0, 0,280, 0, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10,176, 0,281, 10, 10, 10, 10, 10, 10,105, 71, - 95,282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,283, - 10, 10, 71,284,285, 0, 0, 0, 0, 10,286, 0, 10, 10,287, 2, - 0, 0, 0, 0, 0, 10,288, 2, 0, 0, 0, 0, 0, 10,289,105, - 10, 10, 10, 10,290, 2, 0, 0,129,129,129,129,129,129,129,129, - 162,162,162,162,162,162,162,162,162,162,162,162,162,162,162,129, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 0, 0, 272, 32, 0, 120, 0, 120, 0, 0, 280, 288, 0, 296, 0, 0, + 0, 0, 0, 0, 304, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, + 0, 0, 0, 0, 136, 24, 312, 320, 0, 0, 0, 0, 24, 328, 0, 0, + 336, 24, 24, 344, 352, 360, 56, 144, 368, 64, 48, 376, 152, 384, 56, 392, + 400, 64, 48, 408, 416, 424, 72, 432, 440, 112, 48, 448, 456, 0, 56, 464, + 472, 64, 48, 480, 152, 488, 56, 496, 504, 512, 520, 528, 536, 544, 72, 0, + 552, 80, 48, 560, 160, 568, 56, 0, 576, 80, 48, 584, 160, 592, 56, 600, + 608, 80, 24, 616, 624, 632, 56, 0, 640, 648, 24, 656, 664, 672, 72, 680, + 688, 696, 24, 704, 168, 24, 88, 712, 720, 176, 728, 736, 744, 0, 0, 0, + 24, 24, 752, 760, 768, 776, 784, 792, 800, 808, 0, 0, 0, 0, 0, 0, + 24, 816, 24, 824, 24, 832, 840, 848, 24, 24, 24, 856, 864, 872, 32, 0, + 880, 0, 24, 24, 24, 24, 24, 96, 888, 24, 896, 0, 0, 0, 0, 0, + 24, 184, 904, 912, 72, 24, 920, 192, 24, 24, 200, 24, 928, 104, 0, 0, + 24, 936, 24, 24, 24, 944, 952, 960, 32, 32, 0, 0, 0, 0, 0, 0, + 968, 24, 24, 976, 984, 32, 992,1000,1008, 24,1016, 24, 24, 24,1024,1032, + 24, 24,1040,1048,1056, 0, 0, 0, 0, 0, 0, 0, 0,1064,1072,1080, + 0, 0, 0, 0, 0, 0, 0,1088,1096,1104,1112, 0, 0, 0, 16,1120, + 1128, 0, 0, 0, 0, 0, 0,1136, 0, 0, 0, 0,1144, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1152, 0, 0, 0, 0, 24, 24, 24,1160,1168, + 0, 0,1176, 0, 0, 0, 0, 0,1184, 24,1192, 0, 24, 24, 24, 208, + 1200, 24, 24,1208,1216, 32,1224,1232, 24, 24,1240, 24,1248,1256, 0, 0, + 1264, 24, 24,1272,1280, 32,1288, 184, 24, 24,1296,1304,1312, 32, 24,1320, + 24, 24, 24,1328,1336, 0,1344,1352, 0, 0, 0, 0, 24, 24,1360, 32, + 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1368, + 0, 0, 0, 0, 0, 0, 0,1376, 0, 0, 0, 0, 0, 0, 0, 216, + 216,1384, 112,1392, 0, 0, 0, 0,1400,1408, 24,1416, 96, 0, 0, 0, + 0, 0, 0, 0, 168, 24,1424, 0, 24, 224,1432, 0, 0, 0, 0, 0, + 24, 24,1440, 32, 136, 24,1448, 24, 232, 0, 0, 0, 0, 0, 0, 0, + 24, 24,1456, 224, 0, 0, 0, 0, 0, 0, 0, 24,1464,1472, 0, 24, + 1480, 0, 0,1488,1496, 0, 0, 0,1504, 24, 24,1512,1520,1528,1536,1544, + 240, 24, 24,1552,1560, 0, 0, 0,1568, 24,1576,1584,1592, 24, 24,1600, + 240, 24, 24,1608,1616, 104, 248, 192, 24, 112,1624,1632,1640, 0, 0, 0, + 1648,1656, 96, 24, 24,1664,1672, 32,1680, 64, 48,1688,1696,1704,1712,1720, + 1728, 24, 24,1736,1744,1752,1760, 0, 24, 24, 24,1768,1776,1784,1792, 0, + 248, 24, 24,1800,1808, 32, 0, 0, 24, 24, 256,1816,1824,1832, 0, 0, + 24, 24, 24,1840,1848, 32, 0, 0, 24, 24,1856,1864, 32, 24, 208, 0, + 24,1872,1880, 200,1888, 0, 0, 0, 24, 24,1896,1904, 0, 0, 0, 0, + 1912,1920, 24,1928,1936, 32, 0, 0, 0, 0,1944, 24, 24,1952,1960, 0, + 1968, 24, 24,1976,1984,1992, 24, 24,2000,2008, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,2016, 0, 48, 24, 256,2024, 128, 24, 88, 144, + 24,2032, 176,2040, 0, 0, 0, 0,2048, 24, 24,2056,2064, 32,2072, 24, + 2080,2088, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24,2096, + 2104, 80, 24,2112,2120,2128, 0, 0, 8, 8, 8,2136,2144,2152, 8, 8, + 8, 8, 8, 8, 8, 8, 8,2160, 24,2168,2176, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 32, 0, 0, 0, 24, 24, 24,2184, 0, 0, 0, 0, + 0, 0, 0, 0,2192, 24,2200, 32, 24, 24, 24, 24,2208,2216, 264, 264, + 2224,2232, 0, 0, 0, 0,2240, 0, 24, 24, 24, 24, 24, 232, 0,2248, + 24, 24, 24, 24, 24, 24, 104, 88, 96,2256, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,2264, 24, 24, 88,2272,2280, 0, 0, 0, + 0, 24,2288, 0, 24, 24,2296, 32, 0, 0, 0, 0, 0, 24,2304, 32, + 0, 0, 0, 0, 0, 24,2312, 104, 24, 24, 24, 24,2320, 32, 0, 0, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 16, }; static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i) @@ -379,7 +379,8 @@ static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i) } static inline uint8_t hb_use_get_category (unsigned u) { - return u<921600 ? hb_use_u8[2953u+((hb_use_u8[625u+((hb_use_u16[((hb_use_u8[113u+((hb_use_b4(hb_use_u8,((((((((u)>>1))>>3))>>3))>>5)))<<5)+((((((((u)>>1))>>3))>>3))&31)])<<3)+((((((u)>>1))>>3))&7)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : O; + /* packtab: [2^4,2^5,2^3,2^3,2^1] */ + return u<921600u ? (uint8_t)(hb_use_u8[2953u+((hb_use_u8[625u+hb_use_u16[((hb_use_u8[113u+((hb_use_b4(hb_use_u8,((((((((u)>>1))>>3))>>3))>>5)))<<5)+((((((((u)>>1))>>3))>>3))&31)])<<3)+((((((u)>>1))>>3))&7)]+((((u)>>1))&7)])<<1)+((u)&1)]) : O; } @@ -389,269 +390,269 @@ static inline uint8_t hb_use_get_category (unsigned u) static const uint8_t hb_use_u8[3663]= { - 16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, - 15, 0, 1, 1, 2, 1, 1, 3, 4, 5, 6, 7, 8, 9, 10, 1, - 11, 12, 1, 1, 1, 1, 1, 1, 13, 14, 15, 16, 17, 18, 19, 1, - 1, 20, 1, 1, 1, 1, 21, 1, 22, 1, 1, 1, 1, 1, 23, 24, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 25, 26, 27, 28, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 29, - 30, 1, 1, 1, 1, 1, 31, 1, 1, 1, 1, 32, 33, 1, 34, 35, - 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, - 52, 53, 53, 53, 53, 54, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 56, 1, 1, 1, 1, 1, 1, 1, 1, 57, 58, 1, 59, 1, - 60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 61, 62, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 63, 1, 1, - 1, 1, 64, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 65, 66, 1, 67, 68, 1, 1, 1, 69, 1, 1, 1, 1, 1, - 1, 70, 71, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, - 70, 0, 1, 2, 2, 0, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 9, 0, 0, 0, 0, - 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 37, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 0, 56, 57, 58, 59, 60, 0, 0, 0, 61, 62, 63, 64, 56, 65, 66, - 67, 68, 56, 56, 69, 70, 71, 0, 0, 72, 73, 74, 75, 56, 76, 77, - 0, 78, 56, 79, 80, 81, 0, 0, 0, 82, 83, 84, 85, 86, 87, 56, - 88, 56, 89, 90, 0, 0, 0, 91, 92, 0, 0, 0, 0, 0, 0, 0, - 93, 94, 95, 0, 96, 97, 0, 0, 98, 0, 0, 0, 0, 0, 0, 99, - 0, 0, 0, 0, 0, 0, 0, 0, 100, 0, 101, 56, 102, 0, 0, 0, - 0, 0, 103, 0, 0, 0, 0, 0, 0, 104, 105, 56, 106, 107, 108, 109, - 110, 56, 111, 112, 0, 113, 114, 115, 116, 56, 117, 118, 119, 56, 120, 121, - 122, 0, 0, 0, 0, 0, 0, 56, 123, 124, 0, 0, 0, 0, 0, 0, - 125, 0, 0, 0, 0, 0, 0, 0, 126, 0, 0, 0, 127, 128, 129, 0, - 0, 130, 131, 132, 0, 0, 0, 51, 133, 0, 0, 0, 0, 134, 135, 0, - 0, 56, 136, 7, 137, 138, 0, 0, 0, 0, 0, 0, 0, 56, 139, 0, - 0, 0, 101, 140, 101, 141, 142, 143, 0, 144, 145, 146, 147, 148, 149, 150, - 0, 151, 152, 153, 154, 148, 155, 156, 157, 158, 159, 160, 0, 161, 162, 163, - 164, 165, 166, 167, 168, 169, 170, 171, 172, 56, 173, 174, 175, 176, 177, 178, - 0, 0, 0, 0, 0, 56, 179, 180, 0, 56, 181, 182, 0, 56, 183, 184, - 185, 186, 187, 188, 0, 0, 0, 0, 0, 56, 189, 0, 0, 0, 0, 0, - 0, 190, 191, 192, 0, 0, 193, 194, 195, 196, 197, 198, 56, 199, 0, 0, - 0, 0, 0, 0, 200, 0, 0, 0, 0, 201, 202, 203, 204, 205, 206, 0, - 0, 207, 208, 209, 210, 211, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 212, 213, 214, 215, 0, 0, 0, 0, 0, 216, 216, 216, 216, 216, 216, 216, - 216, 216, 217, 218, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, - 219, 220, 221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, - 0, 56, 222, 0, 0, 0, 0, 0, 0, 0, 0, 223, 224, 0, 0, 0, - 0, 56, 56, 225, 226, 227, 0, 0, 228, 56, 56, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 56, 56, 56, 229, 230, 56, 56, 56, 231, 232, 0, 0, - 0, 0, 0, 0, 233, 0, 0, 0, 0, 56, 234, 235, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 101, 236, 56, 237, 0, 0, 0, 0, 0, 0, 101, - 238, 0, 0, 0, 0, 0, 0, 101, 239, 56, 56, 240, 0, 0, 0, 0, - 0, 241, 241, 241, 241, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, - 243, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, - 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 3, 4, - 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 11, - 11, 11, 11, 0, 0, 0, 9, 12, 0, 2, 2, 2, 2, 13, 14, 0, - 0, 11, 15, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 16, 17, - 18, 19, 20, 21, 22, 16, 23, 24, 25, 12, 26, 27, 20, 2, 2, 2, - 2, 2, 20, 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, - 2, 28, 29, 30, 2, 2, 2, 9, 30, 9, 30, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 9, 2, 2, 2, 9, 9, 0, 2, 2, 0, 17, - 18, 19, 20, 31, 32, 33, 32, 34, 0, 0, 0, 0, 35, 0, 0, 2, - 30, 2, 0, 0, 0, 0, 0, 9, 36, 12, 15, 30, 2, 2, 9, 0, - 30, 9, 2, 30, 9, 2, 0, 37, 18, 19, 31, 0, 27, 38, 27, 39, - 0, 40, 0, 0, 0, 30, 2, 9, 9, 0, 0, 0, 2, 2, 2, 2, - 2, 41, 42, 43, 0, 0, 0, 0, 0, 12, 15, 30, 2, 2, 2, 2, - 30, 2, 30, 2, 2, 2, 2, 2, 2, 9, 2, 30, 2, 2, 0, 17, - 18, 19, 20, 21, 27, 22, 35, 24, 0, 0, 0, 0, 0, 30, 41, 41, - 44, 12, 29, 30, 2, 2, 2, 9, 30, 9, 2, 30, 2, 2, 0, 17, - 45, 0, 0, 27, 22, 0, 0, 2, 30, 30, 0, 0, 0, 0, 0, 0, - 0, 0, 46, 30, 2, 2, 9, 0, 2, 9, 2, 2, 0, 30, 9, 9, - 2, 0, 30, 9, 0, 2, 9, 0, 2, 2, 2, 2, 2, 2, 0, 0, - 23, 16, 47, 0, 48, 33, 48, 34, 0, 0, 0, 0, 35, 0, 0, 0, - 0, 15, 29, 49, 2, 2, 2, 9, 2, 9, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 0, 17, 22, 16, 23, 47, 22, 38, 22, 39, - 0, 0, 0, 27, 31, 2, 9, 0, 0, 10, 29, 30, 2, 2, 2, 9, - 2, 2, 2, 30, 2, 2, 0, 17, 45, 0, 0, 35, 47, 0, 0, 0, - 9, 50, 51, 0, 0, 0, 0, 0, 0, 11, 29, 2, 2, 2, 2, 9, - 2, 2, 2, 2, 2, 2, 52, 53, 23, 19, 20, 31, 48, 33, 48, 34, - 54, 0, 0, 0, 35, 0, 0, 0, 30, 12, 29, 30, 2, 2, 2, 2, - 2, 2, 2, 2, 9, 0, 2, 2, 2, 2, 30, 2, 2, 2, 2, 30, - 0, 2, 2, 2, 9, 0, 55, 0, 35, 23, 22, 31, 31, 18, 48, 48, - 25, 0, 23, 0, 0, 0, 0, 0, 0, 2, 0, 2, 9, 0, 0, 0, - 0, 0, 0, 0, 0, 20, 0, 0, 0, 2, 2, 56, 56, 57, 0, 0, - 18, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, 2, 2, 9, - 0, 58, 21, 59, 22, 22, 20, 20, 46, 21, 11, 31, 11, 2, 2, 60, - 61, 61, 61, 61, 61, 62, 61, 61, 61, 61, 61, 61, 61, 61, 61, 61, - 61, 61, 61, 61, 61, 61, 61, 63, 0, 0, 0, 0, 64, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 65, 45, 59, 66, 22, 22, 67, 68, 69, 70, - 71, 2, 2, 2, 2, 2, 1, 0, 5, 2, 2, 2, 23, 20, 2, 2, - 72, 71, 73, 74, 65, 73, 29, 29, 2, 52, 22, 53, 2, 2, 2, 2, - 2, 2, 75, 76, 77, 29, 29, 78, 79, 2, 2, 2, 2, 2, 29, 45, - 0, 2, 59, 80, 0, 0, 0, 0, 30, 2, 59, 47, 0, 0, 0, 0, - 0, 2, 59, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 9, - 2, 9, 59, 0, 0, 0, 0, 0, 0, 2, 2, 81, 45, 22, 59, 20, - 48, 48, 48, 48, 15, 82, 83, 84, 85, 86, 87, 0, 0, 0, 0, 88, - 0, 9, 0, 0, 30, 0, 89, 81, 90, 2, 2, 2, 2, 9, 0, 0, - 0, 42, 42, 91, 92, 2, 2, 2, 2, 2, 2, 2, 2, 13, 9, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 9, 22, 80, 45, 22, 93, 61, 0, - 0, 94, 95, 94, 94, 96, 97, 0, 0, 2, 2, 2, 2, 2, 2, 2, - 0, 2, 2, 9, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, - 0, 2, 2, 2, 2, 29, 0, 0, 0, 2, 2, 2, 2, 2, 9, 0, - 0, 2, 2, 2, 52, 98, 45, 0, 0, 2, 2, 99, 100, 101, 102, 61, - 63, 103, 16, 45, 22, 59, 21, 80, 48, 48, 76, 11, 11, 11, 104, 46, - 40, 11, 105, 74, 2, 2, 2, 2, 2, 2, 2, 106, 22, 20, 20, 22, - 48, 48, 22, 107, 2, 2, 2, 9, 0, 0, 0, 0, 0, 0, 108, 109, - 109, 109, 109, 0, 0, 0, 0, 0, 0, 105, 74, 2, 2, 2, 2, 2, - 2, 60, 61, 59, 25, 22, 110, 61, 2, 2, 2, 2, 106, 22, 23, 45, - 45, 101, 111, 0, 0, 0, 0, 0, 0, 2, 2, 61, 18, 48, 23, 112, - 101, 101, 101, 113, 114, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 30, - 2, 11, 46, 115, 115, 115, 11, 115, 115, 15, 115, 115, 115, 26, 0, 40, - 0, 0, 0, 116, 51, 11, 5, 0, 0, 0, 0, 0, 0, 0, 117, 0, - 0, 0, 0, 0, 0, 0, 6, 118, 119, 42, 42, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 119, 119, 120, 119, 119, 119, 119, 119, 119, 119, - 119, 0, 0, 121, 0, 0, 0, 0, 0, 0, 7, 121, 0, 0, 0, 0, - 0, 46, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, - 0, 0, 0, 0, 122, 122, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, - 30, 0, 0, 0, 0, 0, 0, 0, 123, 0, 122, 122, 0, 0, 0, 0, - 0, 2, 53, 2, 107, 2, 10, 2, 2, 2, 65, 19, 16, 0, 0, 31, - 0, 2, 2, 0, 0, 0, 0, 0, 0, 29, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 124, 23, 23, 23, 23, 23, 23, 23, 125, 0, 0, 0, 0, - 0, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 0, 0, 0, 0, 0, - 52, 2, 2, 2, 22, 22, 126, 115, 0, 2, 2, 2, 127, 20, 59, 20, - 112, 101, 128, 0, 0, 0, 0, 0, 0, 11, 129, 2, 2, 2, 2, 2, - 2, 2, 130, 23, 22, 20, 48, 131, 132, 133, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 52, 30, 2, 2, 2, 2, 2, 2, 2, 2, 10, 22, 59, - 98, 76, 134, 135, 136, 0, 0, 0, 0, 2, 137, 2, 2, 2, 2, 138, - 0, 30, 2, 42, 5, 0, 79, 15, 2, 139, 20, 53, 127, 139, 2, 2, - 140, 10, 9, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 141, 21, - 25, 0, 0, 142, 143, 0, 0, 0, 0, 2, 65, 45, 23, 80, 47, 144, - 0, 81, 81, 81, 81, 81, 81, 81, 81, 0, 0, 0, 0, 0, 0, 0, - 6, 119, 119, 119, 119, 120, 0, 0, 0, 2, 2, 2, 2, 2, 9, 2, - 2, 2, 9, 2, 30, 2, 2, 2, 2, 2, 30, 2, 2, 2, 30, 9, - 0, 127, 20, 27, 31, 0, 0, 145, 146, 2, 2, 30, 2, 30, 2, 2, - 2, 2, 2, 2, 0, 14, 37, 0, 147, 2, 2, 13, 37, 0, 30, 2, - 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 2, 2, - 9, 2, 2, 11, 41, 0, 0, 0, 0, 2, 2, 2, 0, 27, 22, 22, - 30, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 27, 38, - 0, 2, 2, 2, 115, 115, 115, 115, 115, 148, 2, 9, 0, 0, 0, 0, - 0, 2, 14, 14, 0, 0, 0, 0, 0, 9, 2, 2, 9, 2, 2, 2, - 2, 30, 2, 9, 0, 30, 2, 0, 0, 149, 150, 151, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 22, 22, 20, 20, 20, 22, 22, 133, 0, 0, 0, - 0, 0, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 2, 2, 2, 2, - 2, 53, 52, 53, 0, 0, 0, 0, 153, 11, 74, 2, 2, 2, 2, 2, - 2, 18, 19, 21, 16, 24, 37, 0, 0, 0, 31, 0, 0, 0, 0, 0, - 0, 11, 49, 2, 2, 2, 2, 2, 2, 2, 2, 2, 127, 20, 22, 154, - 22, 21, 155, 156, 2, 2, 2, 2, 2, 0, 0, 65, 157, 0, 0, 0, - 0, 2, 13, 0, 0, 0, 0, 0, 0, 2, 65, 25, 20, 20, 20, 22, - 22, 107, 158, 0, 0, 56, 159, 31, 160, 30, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 23, 19, 22, 22, 161, 44, 0, 0, 0, - 49, 127, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 9, 9, 2, 2, - 30, 2, 2, 2, 2, 2, 2, 2, 30, 2, 2, 2, 2, 2, 2, 2, - 10, 18, 19, 21, 22, 162, 31, 0, 0, 11, 11, 30, 2, 2, 2, 9, - 30, 9, 2, 30, 2, 2, 58, 17, 23, 16, 23, 47, 32, 33, 32, 34, - 0, 0, 0, 0, 35, 0, 0, 0, 2, 2, 23, 0, 11, 11, 11, 46, - 0, 11, 11, 46, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 30, 0, - 9, 2, 2, 2, 30, 45, 59, 20, 20, 31, 33, 32, 32, 25, 163, 29, - 164, 165, 37, 0, 0, 0, 0, 0, 0, 12, 26, 0, 0, 0, 0, 0, - 0, 2, 2, 65, 25, 20, 20, 20, 22, 23, 125, 15, 17, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 0, 0, 166, 167, 0, 0, 0, 0, 0, 0, - 0, 18, 19, 20, 20, 66, 98, 25, 160, 11, 168, 9, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 65, 25, 20, 20, 0, 48, 48, 11, - 169, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 20, - 0, 23, 19, 20, 20, 21, 16, 82, 169, 38, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 10, 170, 25, 20, 22, 22, 168, 9, 0, 0, - 0, 2, 2, 2, 2, 2, 9, 43, 135, 23, 22, 20, 76, 21, 22, 0, - 0, 2, 2, 2, 9, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 18, - 19, 20, 21, 22, 104, 169, 37, 0, 0, 2, 2, 2, 9, 30, 0, 2, - 2, 2, 2, 30, 9, 2, 2, 2, 2, 23, 23, 18, 32, 33, 12, 171, - 165, 172, 173, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, - 2, 65, 25, 20, 20, 0, 22, 23, 29, 107, 0, 33, 0, 0, 0, 0, - 0, 52, 20, 22, 22, 22, 139, 2, 2, 2, 174, 140, 11, 15, 175, 61, - 176, 0, 0, 1, 147, 0, 0, 0, 0, 52, 20, 22, 16, 19, 20, 2, - 2, 2, 2, 158, 158, 158, 177, 177, 177, 177, 177, 177, 15, 178, 0, 30, - 0, 16, 20, 16, 16, 0, 0, 0, 0, 22, 20, 20, 31, 22, 22, 11, - 169, 0, 61, 61, 61, 61, 61, 61, 61, 66, 21, 82, 46, 0, 0, 0, - 0, 2, 2, 2, 9, 2, 30, 2, 2, 52, 22, 22, 31, 0, 38, 22, - 27, 11, 159, 179, 180, 0, 0, 0, 0, 2, 2, 2, 30, 9, 2, 2, - 2, 2, 2, 2, 2, 2, 23, 23, 47, 22, 35, 82, 68, 0, 0, 0, - 0, 2, 181, 66, 47, 0, 0, 0, 0, 11, 182, 2, 2, 2, 2, 2, - 2, 2, 2, 23, 22, 20, 31, 0, 48, 16, 143, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 2, 2, 156, 0, 0, 183, 183, 183, 183, 183, 183, 183, - 183, 184, 184, 184, 185, 186, 184, 183, 183, 187, 183, 183, 188, 189, 189, 189, - 189, 189, 189, 189, 0, 0, 0, 0, 0, 183, 183, 183, 183, 183, 190, 0, - 0, 2, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, 22, 22, 191, 192, - 193, 11, 11, 11, 46, 0, 0, 0, 0, 29, 74, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 65, 47, 0, 2, 2, 2, 2, 2, 9, 0, - 58, 194, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, - 20, 20, 20, 20, 20, 0, 0, 0, 40, 115, 26, 0, 0, 0, 0, 0, - 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 30, 2, 2, 2, 2, 2, 0, 58, 37, 0, 6, 119, 119, 119, 120, 0, - 0, 11, 11, 11, 49, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, - 2, 2, 2, 2, 2, 2, 2, 2, 46, 2, 2, 2, 2, 2, 2, 11, - 11, 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, - 20, 2, 2, 44, 44, 44, 92, 0, 0, O, O, O, GB, B, B, O, - SB, O, SE, GB, O, O, WJ,FMPst,FMPst, O, CGJ, B, O, B,VMAbv,VMAbv, - VMAbv, O,VMAbv, B,CMBlw,CMBlw,CMBlw,VMAbv,VMPst, VAbv, VPst,CMBlw, B, VPst, VPre, VPst, - VBlw, VBlw, VBlw, VBlw, VAbv, VAbv, VAbv, VPst, VPst, VPst, H, VPre, VPst,VMBlw, O, O, - VAbv, GB,VMAbv,VMPst,VMPst, O, B, VBlw, O, O, VPre, VPre, O, VPre, H, O, - VPst,FMAbv, O,CMBlw, O, VAbv, O, VAbv, H, O,VMBlw,VMAbv,CMAbv, GB, GB, O, - MBlw,CMAbv,CMAbv, VPst, VAbv,VMAbv, O, VPst, O, VPre, VPre,VMAbv, B, O, CS, CS, - VMPst, B, VAbv, VAbv, B, R, O, HVM, O, O,FMBlw, O,CMAbv, O,CMBlw, VAbv, - VBlw, B, SUB, SUB, SUB, O, SUB, SUB, O,FMBlw, O, B, VPst, VBlw, VPre,VMAbv, - VMBlw,VMPst, IS, VAbv, MPst, MPre, MBlw, MBlw, B, MBlw, MBlw, VPst,VMPst,VMPst, B, MBlw, - VPst, VPre, VAbv, VAbv,VMPst,VMPst,VMBlw, B,VMPst, VBlw, VPst, CGJ, CGJ, VPst,VMAbv,VMAbv, - FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B,FMAbv, B, CGJ, WJ, CGJ, GB, - CMAbv,CMAbv, B, VAbv, SUB, FPst, FPst,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, VAbv, VPre, B, - MPre, MBlw, SUB, FAbv, FAbv, MAbv, SUB, Sk, VPst, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, H, - B, O,SMAbv,SMAbv,SMAbv, VPst, IS, RK, RK, VBlw, FAbv,VMPre,VMPre,FMAbv,CMBlw,VMBlw, - VMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, WJ, WJ, WJ, O,FMPst, O, SB, SE, O, - H, MPst, VPst, H,VMAbv, VAbv,VMBlw, B, VBlw, FPst, VPst, FAbv,VMPst, B,CMAbv, VAbv, - MBlw, MPst, MBlw, H, O, VBlw, MPst, MPre, MAbv, MBlw, O, B, FAbv, FAbv, FPst, VBlw, - B, VBlw,VMAbv, B, VPre, O,VMPst, IS, O,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv, O, - IS,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, B, N, N, O, HN, VPre, VBlw, VAbv, - IS,CMAbv, O, VPst, B, R, R,CMBlw, VAbv, VPre,VMAbv,VMAbv, H, VAbv,CMBlw,VMPst, - O,VMAbv,CMBlw, IS, R,FMAbv, B, CS, CS, H,CMBlw,VMPst, H,VMPst, VAbv,VMAbv, - VPst, MPst, R, MPst,CMBlw, B,FMBlw, CS, SUB, SUB, GB, FBlw, FBlw,CMAbv, IS, VBlw, - IS, R, MBlw, GB, VAbv, R,VMPst, G, G, J, J, J, SB, SE, J, HR, - G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, + 33, 3, 0, 0, 0, 4, 0, 80, 118, 128, 9, 10, 11, 192, 208, 14, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 4, 0, 0, 5, 6, 7, 8, 9, 10, 11, 12, 0, + 13, 14, 0, 0, 0, 0, 0, 0, 15, 16, 17, 18, 19, 20, 21, 0, + 0, 22, 0, 0, 0, 0, 23, 0, 24, 0, 0, 0, 0, 0, 25, 26, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 28, 29, 30, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, + 32, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 34, 35, 0, 36, 37, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 1, 1, 1, 1, 55, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 0, 60, 0, + 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, + 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 66, 67, 0, 68, 69, 0, 0, 0, 70, 0, 0, 0, 0, 0, + 0, 2, 71, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 0, 12, 7, 7, 0, 13, 14, + 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 8, 17, 0, 0, 18, 0, 0, 0, 0, + 0, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 9, 46, 47, 48, + 49, 50, 51, 52, 9, 53, 54, 55, 56, 57, 58, 10, 59, 60, 61, 62, + 0, 3, 63, 64, 65, 66, 0, 0, 0, 67, 68, 69, 70, 3, 71, 72, + 6, 73, 3, 3, 74, 75, 76, 0, 0, 77, 78, 79, 80, 3, 81, 82, + 0, 83, 3, 84, 85, 86, 0, 0, 0, 87, 88, 89, 90, 91, 92, 3, + 93, 3, 94, 95, 0, 0, 0, 96, 97, 0, 0, 0, 0, 0, 0, 0, + 98, 99, 100, 0, 101, 102, 0, 0, 103, 0, 0, 0, 0, 0, 0, 104, + 0, 0, 0, 0, 0, 0, 0, 0, 105, 0, 5, 3, 106, 0, 0, 0, + 0, 0, 107, 0, 0, 0, 0, 0, 0, 108, 109, 3, 110, 111, 112, 113, + 114, 3, 115, 116, 0, 117, 118, 119, 120, 3, 121, 122, 123, 3, 124, 125, + 126, 0, 0, 0, 0, 0, 0, 3, 127, 128, 0, 0, 0, 0, 0, 0, + 129, 0, 0, 0, 0, 0, 0, 0, 130, 0, 0, 0, 131, 132, 133, 0, + 0, 134, 135, 136, 0, 0, 0, 10, 137, 0, 0, 0, 0, 138, 139, 0, + 0, 3, 140, 8, 141, 142, 0, 0, 0, 0, 0, 0, 0, 3, 143, 0, + 0, 0, 5, 144, 5, 145, 146, 147, 0, 148, 149, 150, 151, 11, 152, 153, + 0, 154, 155, 156, 157, 11, 158, 159, 160, 161, 162, 163, 0, 164, 165, 166, + 167, 168, 169, 170, 171, 172, 173, 174, 175, 3, 176, 177, 178, 179, 180, 181, + 0, 0, 0, 0, 0, 3, 182, 183, 0, 3, 184, 185, 0, 3, 186, 187, + 188, 189, 190, 191, 0, 0, 0, 0, 0, 3, 192, 0, 0, 0, 0, 0, + 0, 193, 194, 195, 0, 0, 196, 197, 198, 199, 200, 201, 3, 202, 0, 0, + 0, 0, 0, 0, 203, 0, 0, 0, 0, 204, 205, 206, 207, 208, 209, 0, + 0, 210, 211, 212, 213, 214, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 215, 216, 217, 218, 0, 0, 0, 0, 0, 1, 219, 220, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 221, 222, 223, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 224, 0, 0, 0, 0, 0, + 0, 0, 0, 225, 226, 0, 0, 0, 0, 3, 3, 227, 228, 229, 0, 0, + 230, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 231, + 232, 3, 3, 3, 233, 234, 0, 0, 0, 0, 0, 0, 235, 0, 0, 0, + 0, 3, 236, 237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 238, 3, + 239, 0, 0, 0, 0, 0, 0, 5, 240, 0, 0, 0, 0, 0, 0, 5, + 241, 3, 3, 242, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, + 243, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, + 4, 2, 2, 2, 2, 5, 2, 2, 2, 2, 6, 0, 2, 2, 2, 2, + 2, 5, 8, 2, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, + 2, 5, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 5, + 0, 2, 2, 2, 2, 5, 0, 0, 0, 2, 2, 2, 2, 2, 5, 0, + 0, 2, 8, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 74, 75, + 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, + 2, 0, 2, 2, 2, 2, 2, 2, 2, 17, 6, 13, 29, 33, 29, 45, + 0, 15, 11, 27, 7, 38, 7, 68, 0, 2, 2, 2, 2, 8, 2, 2, + 2, 10, 10, 10, 10, 80, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, + 5, 2, 2, 5, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, + 0, 2, 2, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 5, 2, + 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, + 0, 9, 41, 2, 2, 2, 2, 2, 2, 8, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 23, 6, 6, 6, 6, 6, 6, 6, + 6, 0, 0, 0, 0, 0, 0, 48, 0, 44, 0, 0, 0, 0, 0, 49, + 0, 0, 76, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 103, 2, 2, 2, 2, 2, 36, 9, + 9, 9, 9, 0, 0, 0, 5, 31, 0, 2, 2, 2, 2, 50, 51, 0, + 0, 9, 24, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 15, 32, + 19, 17, 6, 18, 7, 15, 11, 67, 22, 31, 52, 25, 6, 2, 2, 2, + 2, 104, 14, 8, 2, 2, 2, 5, 8, 5, 5, 0, 2, 2, 0, 32, + 19, 0, 0, 0, 34, 0, 0, 2, 8, 2, 0, 0, 0, 0, 0, 5, + 105, 31, 24, 8, 2, 2, 5, 0, 8, 5, 2, 8, 5, 2, 0, 35, + 19, 17, 13, 0, 25, 38, 25, 68, 0, 53, 0, 0, 0, 8, 2, 5, + 5, 54, 39, 77, 0, 0, 0, 0, 0, 31, 24, 8, 2, 2, 2, 2, + 8, 5, 2, 8, 2, 2, 0, 32, 19, 17, 6, 18, 25, 7, 34, 67, + 0, 0, 0, 0, 0, 8, 54, 54, 46, 31, 14, 8, 2, 2, 2, 5, + 8, 5, 2, 8, 2, 2, 0, 32, 20, 0, 0, 25, 7, 0, 0, 2, + 8, 8, 0, 0, 0, 0, 0, 0, 0, 0, 26, 8, 2, 2, 5, 0, + 2, 5, 2, 2, 0, 8, 5, 5, 2, 0, 8, 5, 0, 2, 5, 0, + 2, 2, 2, 2, 2, 2, 0, 0, 11, 15, 27, 0, 12, 33, 12, 45, + 0, 0, 0, 0, 34, 0, 0, 0, 0, 24, 14, 55, 2, 2, 2, 5, + 2, 2, 2, 2, 2, 2, 0, 32, 7, 0, 0, 25, 13, 2, 5, 0, + 0, 36, 14, 8, 2, 2, 2, 5, 2, 2, 2, 8, 2, 2, 0, 32, + 20, 0, 0, 34, 27, 0, 0, 0, 5, 106, 78, 0, 0, 0, 0, 0, + 0, 9, 14, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 2, 30, 40, + 11, 17, 6, 13, 12, 33, 12, 45, 107, 0, 0, 0, 34, 0, 0, 0, + 8, 31, 14, 8, 2, 2, 2, 2, 2, 2, 2, 2, 5, 0, 2, 2, + 2, 2, 8, 2, 2, 2, 2, 8, 0, 2, 2, 2, 5, 0, 108, 0, + 34, 11, 7, 13, 13, 19, 12, 12, 22, 0, 11, 0, 0, 0, 0, 0, + 0, 2, 0, 2, 5, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, + 0, 2, 2, 69, 69, 109, 0, 0, 19, 56, 18, 21, 7, 7, 6, 6, + 26, 18, 9, 13, 9, 2, 2, 79, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 81, 0, 0, 0, 0, 110, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 23, 20, 21, 57, 7, 7, 111, 82, 112, 113, + 83, 2, 2, 2, 2, 2, 48, 0, 44, 2, 2, 2, 11, 6, 2, 2, + 114, 83, 84, 41, 23, 84, 14, 14, 2, 30, 7, 40, 2, 2, 2, 2, + 2, 2, 115, 58, 116, 14, 14, 117, 85, 2, 2, 2, 2, 2, 14, 20, + 0, 2, 21, 59, 0, 0, 0, 0, 8, 2, 21, 27, 0, 0, 0, 0, + 0, 2, 21, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 5, + 2, 5, 21, 0, 0, 0, 0, 0, 0, 2, 2, 4, 20, 7, 21, 6, + 12, 12, 12, 12, 24, 60, 118, 119, 120, 121, 122, 0, 0, 0, 0, 123, + 0, 5, 0, 0, 8, 0, 124, 4, 125, 39, 39, 126, 86, 2, 2, 2, + 2, 2, 2, 2, 2, 50, 5, 0, 0, 7, 59, 20, 7, 127, 10, 0, + 0, 70, 128, 70, 70, 129, 130, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 2, 2, 14, 0, 0, 0, 2, 2, 2, 30, 71, 20, 0, + 0, 2, 2, 131, 132, 42, 133, 10, 81, 134, 15, 20, 7, 21, 18, 59, + 12, 12, 58, 9, 9, 9, 87, 26, 53, 9, 88, 41, 2, 2, 2, 2, + 2, 2, 2, 89, 7, 6, 6, 7, 12, 12, 7, 61, 2, 2, 2, 5, + 0, 0, 0, 0, 0, 0, 135, 62, 62, 62, 62, 0, 0, 0, 0, 0, + 0, 88, 41, 2, 2, 2, 2, 2, 2, 79, 10, 21, 22, 7, 136, 10, + 2, 2, 2, 2, 89, 7, 11, 20, 20, 42, 137, 0, 0, 0, 0, 0, + 0, 2, 2, 10, 19, 12, 11, 90, 42, 42, 42, 138, 139, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 0, 8, 2, 9, 26, 16, 16, 16, 9, 16, + 16, 24, 16, 16, 16, 52, 0, 53, 0, 0, 0, 140, 78, 9, 44, 0, + 0, 0, 0, 0, 0, 0, 141, 0, 0, 0, 0, 0, 0, 0, 49, 142, + 3, 39, 39, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, + 72, 0, 0, 91, 0, 0, 0, 0, 0, 0, 76, 91, 0, 0, 0, 0, + 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, + 0, 0, 0, 0, 63, 63, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, + 8, 0, 0, 0, 0, 0, 0, 0, 143, 0, 63, 63, 0, 0, 0, 0, + 0, 2, 40, 2, 61, 2, 36, 2, 2, 2, 23, 17, 15, 0, 0, 13, + 0, 14, 2, 2, 2, 2, 2, 2, 2, 2, 2, 144, 11, 11, 11, 11, + 11, 11, 11, 92, 0, 0, 0, 0, 0, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 2, 0, 0, 0, 0, 0, 30, 2, 2, 2, 7, 7, 145, 16, + 0, 2, 2, 2, 47, 6, 21, 6, 90, 42, 146, 0, 0, 0, 0, 0, + 0, 9, 147, 2, 2, 2, 2, 2, 2, 2, 148, 11, 7, 6, 12, 149, + 150, 93, 0, 0, 0, 0, 0, 0, 0, 2, 2, 30, 8, 2, 2, 2, + 2, 2, 2, 2, 2, 36, 7, 21, 71, 58, 151, 94, 152, 0, 0, 0, + 0, 2, 153, 2, 2, 2, 2, 154, 0, 8, 2, 39, 44, 0, 85, 24, + 2, 73, 6, 40, 47, 73, 2, 2, 95, 36, 5, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 155, 18, 22, 0, 0, 156, 96, 0, 0, 0, + 0, 2, 23, 20, 11, 59, 27, 157, 0, 0, 0, 0, 0, 0, 0, 0, + 49, 3, 3, 3, 3, 72, 0, 0, 0, 2, 5, 2, 8, 2, 2, 2, + 2, 2, 8, 2, 2, 2, 8, 5, 0, 47, 6, 25, 13, 0, 0, 158, + 159, 2, 2, 8, 2, 8, 2, 2, 2, 2, 2, 2, 0, 51, 35, 0, + 97, 2, 2, 50, 35, 0, 8, 2, 2, 0, 0, 0, 0, 8, 2, 2, + 5, 2, 2, 9, 54, 0, 0, 0, 0, 2, 2, 2, 0, 25, 7, 7, + 8, 2, 2, 2, 2, 2, 25, 38, 0, 2, 2, 2, 16, 16, 16, 16, + 16, 160, 2, 5, 0, 0, 0, 0, 0, 2, 51, 51, 0, 0, 0, 0, + 0, 5, 2, 2, 5, 2, 2, 2, 2, 8, 2, 5, 0, 8, 2, 0, + 0, 161, 162, 163, 2, 2, 2, 2, 2, 2, 2, 2, 2, 7, 7, 6, + 6, 6, 7, 7, 93, 0, 0, 0, 0, 0, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 2, 2, 2, 2, 2, 40, 30, 40, 0, 0, 0, 0, + 164, 19, 17, 18, 15, 67, 35, 0, 0, 0, 13, 0, 0, 0, 0, 0, + 0, 9, 55, 2, 2, 2, 2, 2, 2, 2, 2, 2, 47, 6, 7, 165, + 7, 18, 166, 98, 2, 2, 2, 2, 2, 0, 0, 23, 167, 0, 0, 0, + 0, 2, 50, 0, 0, 0, 0, 0, 0, 2, 23, 22, 6, 6, 6, 7, + 7, 61, 64, 0, 0, 69, 99, 13, 100, 2, 2, 2, 2, 2, 2, 11, + 17, 7, 7, 168, 46, 0, 0, 0, 55, 47, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 5, 5, 2, 2, 8, 2, 2, 2, 2, 2, 2, 2, + 8, 2, 2, 2, 2, 2, 2, 2, 36, 19, 17, 18, 7, 169, 13, 0, + 0, 9, 9, 8, 2, 2, 2, 5, 8, 5, 2, 8, 2, 2, 56, 32, + 11, 15, 11, 27, 29, 33, 29, 45, 0, 0, 0, 0, 34, 0, 0, 0, + 2, 2, 11, 0, 9, 9, 9, 26, 0, 9, 9, 26, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 8, 0, 5, 2, 2, 2, 8, 20, 21, 6, + 6, 13, 33, 29, 29, 22, 170, 14, 171, 101, 35, 0, 0, 0, 0, 0, + 0, 31, 52, 0, 0, 0, 0, 0, 0, 2, 2, 23, 22, 6, 6, 6, + 7, 11, 92, 24, 32, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, + 172, 173, 0, 0, 0, 0, 0, 0, 0, 19, 17, 6, 6, 57, 71, 22, + 100, 9, 102, 5, 0, 0, 0, 0, 0, 22, 6, 6, 0, 12, 12, 9, + 65, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 6, + 0, 11, 17, 6, 6, 18, 15, 60, 65, 38, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 36, 174, 22, 6, 7, 7, 102, 5, 0, 0, + 0, 2, 2, 2, 2, 2, 5, 77, 94, 11, 7, 6, 58, 18, 7, 0, + 0, 2, 2, 2, 5, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 19, + 17, 6, 18, 7, 87, 65, 35, 0, 0, 2, 2, 2, 5, 8, 0, 2, + 2, 2, 2, 8, 5, 2, 2, 2, 2, 11, 11, 19, 29, 33, 31, 175, + 101, 176, 177, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 2, 2, + 2, 23, 22, 6, 6, 0, 7, 11, 14, 61, 0, 33, 0, 0, 0, 0, + 0, 30, 6, 7, 7, 7, 73, 2, 2, 2, 178, 95, 9, 24, 179, 10, + 180, 0, 0, 48, 97, 0, 0, 0, 0, 30, 6, 7, 15, 17, 6, 2, + 2, 2, 2, 64, 64, 64, 43, 43, 43, 43, 43, 43, 24, 181, 0, 8, + 0, 15, 6, 15, 15, 0, 0, 0, 0, 7, 6, 6, 13, 7, 7, 9, + 65, 0, 10, 10, 10, 10, 10, 10, 10, 57, 18, 60, 26, 0, 0, 0, + 0, 2, 2, 2, 5, 2, 8, 2, 2, 30, 7, 7, 13, 0, 38, 7, + 25, 9, 99, 182, 183, 0, 0, 0, 0, 2, 2, 2, 8, 5, 2, 2, + 2, 2, 2, 2, 2, 2, 11, 11, 27, 7, 34, 60, 82, 0, 0, 0, + 0, 2, 184, 57, 27, 0, 0, 0, 0, 9, 185, 2, 2, 2, 2, 2, + 2, 2, 2, 11, 7, 6, 13, 0, 12, 15, 96, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 2, 98, 0, 0, 66, 66, 66, 186, 187, 66, 1, + 1, 188, 1, 1, 189, 37, 37, 37, 37, 37, 37, 37, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 190, 0, 0, 2, 2, 2, 2, 2, 2, 2, + 7, 7, 7, 7, 7, 7, 191, 192, 193, 9, 9, 9, 26, 0, 0, 0, + 0, 14, 41, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 23, 27, + 0, 2, 2, 2, 2, 2, 5, 0, 56, 194, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 0, 0, 0, 53, 16, 52, 0, 0, 0, 0, 0, + 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 2, 2, 2, 2, 2, 0, 56, 35, 0, 49, 3, 3, 3, 72, 0, + 0, 9, 9, 9, 55, 2, 2, 2, 0, 2, 2, 2, 2, 2, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 26, 2, 2, 2, 2, 2, 2, 9, + 9, 2, 2, 2, 2, 2, 2, 7, 7, 2, 2, 2, 2, 2, 2, 2, + 6, 2, 2, 46, 46, 46, 86, 0, 0, O, O, G, G, B, B, WJ, + WJ, CGJ, CGJ, B, O, VBlw, VBlw, VAbv, VAbv, O, B,VMAbv,VMAbv, SUB, SUB, VPst, + VPst, VPre, VPre, VBlw, O,VMPst,VMPst, VAbv, VPst,VMBlw,VMBlw, VPst, VBlw, VBlw, VAbv, VPst, + VPre, VPst, VAbv, VAbv, VBlw, VPre, VPst, B, VPst,VMAbv,VMPst, O, VAbv,VMAbv, O, VPst, + O, N, N, O, VPre, B, VAbv, O,VMAbv,CMBlw, B, VPre, O, O, VPst,CMBlw, + O, B,VMAbv, HM, HM, VAbv, O, GB, GB, VAbv, B,VMPst, B, FAbv, FAbv, FBlw, + FBlw, GB, O, VPre, H,CMAbv,CMAbv, B, VBlw, O, GB, O, WJ, B,CMBlw,CMBlw, + CMBlw,VMBlw, O, O,VMBlw,VMAbv,CMAbv,VMAbv, B, O,CMBlw, VBlw, VPre, VPre, VAbv, VBlw, + VPst, VPst,VMAbv, H, B,SMAbv,SMAbv, SB, SE, R, R,VMPst, H, J, J, VPst, + H, VAbv, H, O,FMBlw, FPst, FPst, VAbv, VPre, WJ, O, VBlw, B, O, SB, O, + SE,FMPst,FMPst, O, MBlw, CS,VMPst, B, SUB, O, SUB, SUB, O,VMPst, IS, MBlw, + B, VPst,VMPst, B,VMPst,CMAbv, B, VAbv,VMAbv,VMAbv, FAbv,CMAbv, VPst, VBlw, FAbv,FMPst, + O, H,VMAbv, H, O, MPre, MAbv, VBlw,VMAbv, IS, O, O, IS,CMAbv, O,CMBlw, + VAbv, VPre,VMAbv, IS, R, H,CMBlw, O, CGJ, GB,VMAbv,FMAbv, O, O, CS, R, + O, HVM, O, O,CMAbv,FMBlw, O,VMAbv,VMBlw, VAbv, MPst, MPre, MBlw, MBlw, MBlw, MBlw, + VPst, VAbv,VMPst,VMPst,VMBlw,VMAbv,FMAbv, FAbv,CMAbv,FMAbv,VMAbv,FMAbv, VAbv, IS,FMAbv, B, + FMAbv, B, CGJ, WJ, CGJ, GB,CMAbv, VAbv, SUB,VMBlw, FPst, FPst, FBlw,VMAbv,FMBlw, B, + MPre, MBlw, SUB, MAbv, SUB, Sk, VPst, O,SMAbv, VPst, IS, RK, RK,VMPre,VMPre,FMAbv, + CMBlw,VMAbv, CS, O,FMAbv, ZWNJ, CGJ, O, H, MPst, VPst, VAbv,VMBlw, FPst, VPst, FAbv, + VMPst, B,CMAbv, VAbv, MBlw, MPst, MBlw, VBlw, MPst, MBlw, O, B, FAbv, FAbv, FPst, B, + VPre, O,VMPst,VMPst, VBlw, VPst,VMBlw,VMBlw,VMAbv,VMBlw, B,VMPst,VMAbv,VMPst, CS, CS, + B, O, HN, VPre, VBlw, VAbv, IS, VPst, B,VMAbv, H, VAbv,CMBlw,VMPst, O,VMAbv, + CMBlw,FMAbv, B, CS, CS,VMPst, VAbv,VMAbv, VPst, MPst, R, MPst,CMBlw, B,FMBlw, CS, + SUB, SUB, GB,CMAbv, IS, VBlw, IS, R, MBlw, GB, VAbv, R,VMPst, J, SB, SE, + J, HR, G, G, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw, }; static const uint16_t hb_use_u16[488]= { - 0, 0, 1, 2, 0, 3, 4, 5, 0, 6, 7, 0, 8, 0, 9, 10, - 11, 12, 10, 13, 14, 10, 10, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 18, 26, 27, 21, 22, 28, 29, 30, 31, 32, 33, 34, 22, 35, - 36, 0, 18, 37, 38, 21, 22, 39, 24, 40, 18, 41, 42, 43, 44, 45, - 46, 47, 31, 0, 48, 49, 22, 50, 51, 52, 18, 0, 53, 49, 22, 54, - 51, 55, 18, 56, 57, 49, 10, 58, 59, 60, 61, 62, 10, 63, 64, 65, - 31, 66, 67, 68, 10, 69, 70, 10, 71, 72, 73, 74, 75, 76, 77, 0, - 10, 10, 78, 79, 80, 81, 82, 83, 84, 85, 10, 86, 10, 87, 10, 88, - 89, 90, 10, 91, 92, 93, 2, 0, 94, 0, 10, 95, 96, 10, 97, 0, - 10, 98, 99,100, 31, 10,101,102,103, 10,104,105, 10,106, 10,107, - 108,109, 2, 2,110, 10, 10,111,112, 2,113,114,115, 10,116, 10, - 117,118,119,120,121, 0, 0,122,123,124, 0,125,126,127,128, 0, - 129,130,131, 0, 0,132,133, 0,134, 0, 0, 10,135,136,137, 0, - 138, 10,139, 0, 10,140,141, 10, 10,142,143, 2,144,145,146, 10, - 147,148,149, 10, 10,150,151, 2,152, 98,153,154,155, 2, 10,156, - 10,157,158, 0,159,160,161, 2,162, 0, 0,163, 0,164, 0,165, - 165,166, 34,167,168,169, 10,170, 95, 0,171, 0, 10,172,173, 0, - 174, 2,175, 10,176, 0,177,172,178,179,180, 0, 0,181,182, 0, - 183, 10, 10,184,185,186,187,188,189, 10, 10,190,191, 0,192, 10, - 193,194,195, 10, 10,196, 10,197,198,105,199,102, 10, 34,200,201, - 202, 0,203,204, 95, 10, 10,205,206, 2,207, 21, 22,208,209,210, - 211,212,213, 10, 10,214,215,216,217, 0, 10,218,219,220,221, 0, - 199, 10, 10,222,223, 2,224,225,226,227, 10,228,229, 2,230,231, - 2, 10,140, 0, 10,232,233,103,234, 0,235,236,237,238, 10,239, - 240, 2,241, 10, 10,242,243, 0,244, 10, 10,245,246,247,248,249, - 250, 0, 22, 10,224,251, 8, 10, 71, 19, 10,252, 74,253,254, 10, - 10,255,256, 2,257, 10,258,259, 10,260,261, 49, 10,262,263,264, - 265,265,265,266,267,268,265,269, 10,270,271, 2, 10,272,273, 10, - 274, 2,275,276,277,277,278,279,280, 0, 10,176, 0,281,105, 71, - 95,282, 0,283, 71,284,285, 0,286, 0,287, 2,288, 2,289,105, - 290, 2,129,129,162,162,162,129, + 0, 0, 8, 8, 16, 16, 24, 24, 40, 40, 0, 24, 32, 0, 0, 120, + 136, 24, 56, 0, 168, 24, 240, 24, 272, 32, 280, 288, 0, 296, 304, 0, + 128, 0, 312, 320, 24, 328, 336, 24, 24, 344, 352, 360, 56, 144, 368, 64, + 48, 376, 152, 384, 56, 392, 400, 64, 48, 408, 416, 424, 72, 432, 440, 112, + 48, 448, 456, 0, 56, 464, 472, 64, 48, 480, 152, 488, 56, 496, 504, 512, + 520, 528, 536, 544, 72, 0, 552, 80, 48, 560, 160, 568, 576, 80, 48, 584, + 160, 592, 56, 600, 608, 80, 24, 616, 624, 632, 640, 648, 24, 656, 664, 672, + 72, 680, 688, 696, 24, 704, 88, 712, 720, 176, 728, 736, 744, 0, 752, 760, + 768, 776, 784, 792, 800, 808, 24, 816, 24, 824, 24, 832, 840, 848, 24, 856, + 864, 872, 880, 0, 24, 96, 888, 24, 896, 0, 24, 184, 904, 912, 72, 24, + 920, 192, 200, 24, 928, 104, 24, 936, 24, 944, 952, 960, 32, 32, 968, 24, + 24, 976, 984, 32, 992,1000,1008, 24,1016, 24,1024,1032,1040,1048,1056, 0, + 0,1064,1072,1080, 0,1088,1096,1104,1112, 0, 16,1120,1128, 0, 0,1136, + 1144, 0,1152, 0,1160,1168,1176, 0,1184, 24,1192, 0, 24, 208,1200, 24, + 24,1208,1216, 32,1224,1232,1240, 24,1248,1256,1264, 24, 24,1272,1280, 32, + 1288, 184,1296,1304,1312, 32, 24,1320, 24,1328,1336, 0,1344,1352,1360, 32, + 40, 0, 0,1368, 0,1376, 0, 216, 216,1384, 112,1392,1400,1408, 24,1416, + 96, 0,1424, 0, 24, 224,1432, 0,1440, 32,1448, 24, 232, 0,1456, 224, + 1464,1472,1480, 0, 0,1488,1496, 0,1504, 24, 24,1512,1520,1528,1536,1544, + 24,1552,1560, 0,1568, 24,1576,1584,1592, 24, 24,1600, 24,1608,1616, 104, + 248, 192, 24, 112,1624,1632,1640, 0,1648,1656, 96, 24, 24,1664,1672, 32, + 1680, 64, 48,1688,1696,1704,1712,1720,1728, 24, 24,1736,1744,1752,1760, 0, + 24,1768,1776,1784,1792, 0, 248, 24, 24,1800,1808, 32, 256,1816,1824,1832, + 24,1840,1848, 32,1856,1864, 32, 24, 208, 0, 24,1872,1880, 200,1888, 0, + 1896,1904,1912,1920, 24,1928,1936, 32,1944, 24, 24,1952,1960, 0,1968, 24, + 24,1976,1984,1992,2000,2008,2016, 0, 48, 24, 256,2024, 128, 24, 88, 144, + 24,2032, 176,2040,2048, 24, 24,2056,2064, 32,2072, 24,2080,2088, 24,2096, + 2104, 80, 24,2112,2120,2128, 8,2136,2144,2152, 8,2160, 24,2168,2176, 32, + 24,2184,2192, 24,2200, 32,2208,2216, 264, 264,2224,2232,2240, 0, 24, 232, + 0,2248, 104, 88, 96,2256, 0,2264, 88,2272,2280, 0,2288, 0,2296, 32, + 2304, 32,2312, 104,2320, 32, 40, 16, }; static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i) @@ -660,7 +661,8 @@ static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i) } static inline uint8_t hb_use_get_category (unsigned u) { - return u<921600 ? hb_use_u8[3273u+((hb_use_u8[945u+((hb_use_u16[((hb_use_u8[369u+((hb_use_u8[113u+((hb_use_b4(hb_use_u8,((((((((((u)>>1))>>3))>>1))>>3))>>4)))<<4)+((((((((((u)>>1))>>3))>>1))>>3))&15)])<<3)+((((((((u)>>1))>>3))>>1))&7)])<<1)+((((((u)>>1))>>3))&1)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : O; + /* packtab: [2^4,2^4,2^3,2^1,2^3,2^1] */ + return u<921600u ? (uint8_t)(hb_use_u8[3273u+((hb_use_u8[945u+hb_use_u16[((hb_use_u8[369u+((hb_use_u8[113u+((hb_use_b4(hb_use_u8,((((((((((u)>>1))>>3))>>1))>>3))>>4)))<<4)+((((((((((u)>>1))>>3))>>1))>>3))&15)])<<3)+((((((((u)>>1))>>3))>>1))&7)])<<1)+((((((u)>>1))>>3))&1)]+((((u)>>1))&7)])<<1)+((u)&1)]) : O; } diff --git a/thirdparty/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc b/thirdparty/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc index 079fee9bc6..eacaaf2baf 100644 --- a/thirdparty/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc +++ b/thirdparty/harfbuzz/src/hb-ot-shaper-vowel-constraints.cc @@ -2,7 +2,7 @@ /* * The following functions are generated by running: * - * ./gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt + * gen-vowel-constraints.py ms-use/IndicShapingInvalidCluster.txt Scripts.txt * * on files with these headers: * diff --git a/thirdparty/harfbuzz/src/hb-ot-tag-table.hh b/thirdparty/harfbuzz/src/hb-ot-tag-table.hh index 29905984a0..124599e7cc 100644 --- a/thirdparty/harfbuzz/src/hb-ot-tag-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-tag-table.hh @@ -2,7 +2,7 @@ /* * The following table is generated by running: * - * ./gen-tag-table.py languagetags language-subtag-registry + * gen-tag-table.py languagetags language-subtag-registry * * on files with these headers: * @@ -221,55 +221,201 @@ static const LangTag ot_languages2[] = { }; #ifndef HB_NO_LANGUAGE_LONG +static const hb_tag_t ot_languages3_blocked[] = { + HB_TAG('a','b','a',' '), /* Abé != Abaza */ + HB_TAG('a','f','k',' '), /* Nanubae != Afrikaans */ + HB_TAG('a','g','w',' '), /* Kahua != Agaw */ + HB_TAG('a','r','i',' '), /* Arikara != Aari */ + HB_TAG('a','r','k',' '), /* Arikapú != Rakhine */ + HB_TAG('b','a','g',' '), /* Tuki != Baghelkhandi */ + HB_TAG('b','a','u',' '), /* Bada (Nigeria) != Baulé */ + HB_TAG('b','b','r',' '), /* Girawa != Berber */ + HB_TAG('b','c','h',' '), /* Bariai != Bench */ + HB_TAG('b','i','l',' '), /* Bile != Bilen */ + HB_TAG('b','k','f',' '), /* Beeke != Blackfoot */ + HB_TAG('b','l','i',' '), /* Bolia != Baluchi */ + HB_TAG('b','l','t',' '), /* Tai Dam != Balti */ + HB_TAG('b','m','b',' '), /* Bembe != Bambara (Bamanankan) */ + HB_TAG('b','m','l',' '), /* Bomboli != Bamileke */ + HB_TAG('b','r','i',' '), /* Mokpwe != Braj Bhasha */ + HB_TAG('b','r','m',' '), /* Barambu != Burmese */ + HB_TAG('b','s','h',' '), /* Kati != Bashkir */ + HB_TAG('b','t','i',' '), /* Burate != Beti */ + HB_TAG('c','h','g',' '), /* Chagatai != Chaha Gurage */ + HB_TAG('c','h','h',' '), /* Chinook != Chattisgarhi */ + HB_TAG('c','r','r',' '), /* Carolina Algonquian != Carrier */ + HB_TAG('c','r','t',' '), /* Iyojwa'ja Chorote != Crimean Tatar */ + HB_TAG('c','s','l',' '), /* Chinese Sign Language != Church Slavonic */ + HB_TAG('d','h','v',' '), /* Dehu != Divehi (Dhivehi, Maldivian) (deprecated) */ + HB_TAG('d','n','k',' '), /* Dengka != Dinka */ + HB_TAG('d','r','i',' '), /* C'Lela != Dari */ + HB_TAG('d','u','n',' '), /* Dusun Deyah != Dungan */ + HB_TAG('d','z','n',' '), /* Dzando != Dzongkha */ + HB_TAG('e','c','r',' '), /* Eteocretan != Eastern Cree */ + HB_TAG('e','u','q',' '), /* Basque [collection] != Basque */ + HB_TAG('f','a','r',' '), /* Fataleka != Persian */ + HB_TAG('f','o','s',' '), /* Siraya != Faroese */ + HB_TAG('g','a','d',' '), /* Gaddang != Ga */ + HB_TAG('g','a','e',' '), /* Guarequena != Scottish Gaelic */ + HB_TAG('g','a','l',' '), /* Galolen != Galician */ + HB_TAG('g','a','r',' '), /* Galeya != Garshuni */ + HB_TAG('g','a','w',' '), /* Nobonob != Garhwali */ + HB_TAG('g','m','z',' '), /* Mgbolizhia != Gumuz */ + HB_TAG('g','r','o',' '), /* Groma != Garo */ + HB_TAG('g','u','a',' '), /* Shiki != Guarani */ + HB_TAG('h','a','l',' '), /* Halang != Halam (Falam Chin) */ + HB_TAG('h','b','n',' '), /* Heiban != Hammer-Banna */ + HB_TAG('i','r','i',' '), /* Rigwe != Irish */ + HB_TAG('i','s','m',' '), /* Masimasi != Inari Sami */ + HB_TAG('j','a','n',' '), /* Jandai != Japanese */ + HB_TAG('j','i','i',' '), /* Jiiddu != Yiddish */ + HB_TAG('j','u','d',' '), /* Worodougou != Ladino */ + HB_TAG('j','u','l',' '), /* Jirel != Jula */ + HB_TAG('k','a','c',' '), /* Kachin != Kachchi */ + HB_TAG('k','e','b',' '), /* Kélé != Kebena */ + HB_TAG('k','g','e',' '), /* Komering != Khutsuri Georgian */ + HB_TAG('k','h','n',' '), /* Khandesi != Khamti Shan (Microsoft fonts) */ + HB_TAG('k','h','s',' '), /* Kasua != Khanty-Shurishkar */ + HB_TAG('k','h','v',' '), /* Khvarshi != Khanty-Vakhi */ + HB_TAG('k','i','s',' '), /* Kis != Kisii */ + HB_TAG('k','k','n',' '), /* Kon Keu != Kokni */ + HB_TAG('k','l','m',' '), /* Migum != Kalmyk */ + HB_TAG('k','m','n',' '), /* Awtuw != Kumaoni */ + HB_TAG('k','m','o',' '), /* Kwoma != Komo */ + HB_TAG('k','m','s',' '), /* Kamasau != Komso */ + HB_TAG('k','n','r',' '), /* Kaningra != Kanuri */ + HB_TAG('k','o','d',' '), /* Kodi != Kodagu */ + HB_TAG('k','o','h',' '), /* Koyo != Korean Old Hangul */ + HB_TAG('k','o','p',' '), /* Waube != Komi-Permyak */ + HB_TAG('k','o','z',' '), /* Korak != Komi-Zyrian */ + HB_TAG('k','p','l',' '), /* Kpala != Kpelle */ + HB_TAG('k','r','k',' '), /* Kerek != Karakalpak */ + HB_TAG('k','r','m',' '), /* Krim (retired code) != Karaim */ + HB_TAG('k','r','n',' '), /* Sapo != Karen */ + HB_TAG('k','s','i',' '), /* Krisa != Khasi */ + HB_TAG('k','s','m',' '), /* Kumba != Kildin Sami */ + HB_TAG('k','u','i',' '), /* Kuikúro-Kalapálo != Kui */ + HB_TAG('k','u','l',' '), /* Kulere != Kulvi */ + HB_TAG('k','u','y',' '), /* Kuuku-Ya'u != Kuy */ + HB_TAG('k','y','k',' '), /* Kamayo != Koryak */ + HB_TAG('l','a','h',' '), /* Lahnda [macrolanguage] != Lahuli */ + HB_TAG('l','a','k',' '), /* Laka (Nigeria) (retired code) != Lak */ + HB_TAG('l','a','m',' '), /* Lamba != Lambani */ + HB_TAG('l','a','z',' '), /* Aribwatsa != Laz */ + HB_TAG('l','d','k',' '), /* Leelau != Ladakhi */ + HB_TAG('l','m','a',' '), /* East Limba != Low Mari */ + HB_TAG('l','m','b',' '), /* Merei != Limbu */ + HB_TAG('l','m','w',' '), /* Lake Miwok != Lomwe */ + HB_TAG('l','s','b',' '), /* Burundian Sign Language != Lower Sorbian */ + HB_TAG('l','t','h',' '), /* Thur != Lithuanian */ + HB_TAG('l','v','i',' '), /* Lavi != Latvian */ + HB_TAG('m','a','j',' '), /* Jalapa De Díaz Mazatec != Majang */ + HB_TAG('m','a','p',' '), /* Austronesian [collection] != Mapudungun */ + HB_TAG('m','a','w',' '), /* Mampruli != Marwari */ + HB_TAG('m','b','n',' '), /* Macaguán != Mbundu */ + HB_TAG('m','c','h',' '), /* Maquiritari != Manchu */ + HB_TAG('m','c','r',' '), /* Menya != Moose Cree */ + HB_TAG('m','d','e',' '), /* Maba (Chad) != Mende */ + HB_TAG('m','i','z',' '), /* Coatzospan Mixtec != Mizo */ + HB_TAG('m','k','r',' '), /* Malas != Makasar */ + HB_TAG('m','l','e',' '), /* Manambu != Male */ + HB_TAG('m','l','n',' '), /* Malango != Malinke */ + HB_TAG('m','l','r',' '), /* Vame != Malayalam Reformed */ + HB_TAG('m','n','d',' '), /* Mondé != Mandinka */ + HB_TAG('m','n','g',' '), /* Eastern Mnong != Mongolian */ + HB_TAG('m','n','x',' '), /* Manikion != Manx */ + HB_TAG('m','o','k',' '), /* Morori != Moksha */ + HB_TAG('m','o','r',' '), /* Moro != Moroccan */ + HB_TAG('m','t','h',' '), /* Munggui != Maithili */ + HB_TAG('m','t','s',' '), /* Yora != Maltese */ + HB_TAG('m','u','n',' '), /* Munda [collection] != Mundari */ + HB_TAG('n','a','s',' '), /* Naasioi != Naskapi */ + HB_TAG('n','c','r',' '), /* Ncane != N-Cree */ + HB_TAG('n','d','b',' '), /* Kenswei Nsei != Ndebele */ + HB_TAG('n','d','g',' '), /* Ndengereko != Ndonga */ + HB_TAG('n','g','r',' '), /* Engdewu != Nagari */ + HB_TAG('n','i','s',' '), /* Nimi != Nisi */ + HB_TAG('n','k','o',' '), /* Nkonya != N’Ko */ + HB_TAG('n','s','m',' '), /* Sumi Naga != Northern Sami */ + HB_TAG('n','t','o',' '), /* Ntomba != Esperanto */ + HB_TAG('o','r','o',' '), /* Orokolo != Oromo */ + HB_TAG('p','a','a',' '), /* Papuan [collection] != Palestinian Aramaic */ + HB_TAG('p','a','l',' '), /* Pahlavi != Pali */ + HB_TAG('p','a','s',' '), /* Papasena != Pashto */ + HB_TAG('p','i','l',' '), /* Yom != Filipino */ + HB_TAG('p','l','k',' '), /* Kohistani Shina != Polish */ + HB_TAG('r','m','s',' '), /* Romanian Sign Language != Romansh */ + HB_TAG('s','a','d',' '), /* Sandawe != Sadri */ + HB_TAG('s','a','y',' '), /* Saya != Sayisi */ + HB_TAG('s','g','o',' '), /* Songa (retired code) != Sango */ + HB_TAG('s','i','b',' '), /* Sebop != Sibe */ + HB_TAG('s','i','g',' '), /* Paasaal != Silte Gurage */ + HB_TAG('s','k','s',' '), /* Maia != Skolt Sami */ + HB_TAG('s','k','y',' '), /* Sikaiana != Slovak */ + HB_TAG('s','l','a',' '), /* Slavic [collection] != Slavey */ + HB_TAG('s','m','l',' '), /* Central Sama != Somali */ + HB_TAG('s','n','h',' '), /* Shinabo (retired code) != Sinhala (Sinhalese) */ + HB_TAG('s','o','g',' '), /* Sogdian != Sodo Gurage */ + HB_TAG('s','r','b',' '), /* Sora != Serbian */ + HB_TAG('s','r','k',' '), /* Serudung Murut != Saraiki */ + HB_TAG('s','s','l',' '), /* Western Sisaala != South Slavey */ + HB_TAG('s','s','m',' '), /* Semnam != Southern Sami */ + HB_TAG('s','u','r',' '), /* Mwaghavul != Suri */ + HB_TAG('s','v','e',' '), /* Serili != Swedish */ + HB_TAG('s','w','k',' '), /* Malawi Sena != Swahili */ + HB_TAG('t','a','j',' '), /* Eastern Tamang != Tajiki */ + HB_TAG('t','g','n',' '), /* Tandaganon != Tongan */ + HB_TAG('t','g','r',' '), /* Tareng != Tigre */ + HB_TAG('t','g','y',' '), /* Togoyo != Tigrinya */ + HB_TAG('t','k','m',' '), /* Takelma != Turkmen */ + HB_TAG('t','m','n',' '), /* Taman (Indonesia) != Temne */ + HB_TAG('t','n','a',' '), /* Tacana != Tswana */ + HB_TAG('t','n','e',' '), /* Tinoc Kallahan (retired code) != Tundra Enets */ + HB_TAG('t','n','g',' '), /* Tobanga != Tonga */ + HB_TAG('t','r','k',' '), /* Turkic [collection] != Turkish */ + HB_TAG('t','s','g',' '), /* Tausug != Tsonga */ + HB_TAG('t','u','a',' '), /* Wiarumus != Turoyo Aramaic */ + HB_TAG('t','u','l',' '), /* Tula != Tulu */ + HB_TAG('t','u','v',' '), /* Turkana != Tuvin */ + HB_TAG('v','i','t',' '), /* Viti != Vietnamese */ + HB_TAG('w','a','g',' '), /* Wa'ema != Wagdi */ + HB_TAG('x','b','d',' '), /* Bindal != Lü */ + HB_TAG('y','a','k',' '), /* Yakama != Sakha */ + HB_TAG('y','b','a',' '), /* Yala != Yoruba */ + HB_TAG('y','i','m',' '), /* Yimchungru Naga != Yi Modern */ + HB_TAG('z','n','d',' '), /* Zande [collection] != Zande */ +}; + static const LangTag ot_languages3[] = { {HB_TAG('a','a','e',' '), HB_TAG('S','Q','I',' ')}, /* Arbëreshë Albanian -> Albanian */ {HB_TAG('a','a','o',' '), HB_TAG('A','R','A',' ')}, /* Algerian Saharan Arabic -> Arabic */ -/*{HB_TAG('a','a','q',' '), HB_TAG('A','A','Q',' ')},*/ /* Eastern Abnaki -> Eastern Abenaki */ {HB_TAG('a','a','t',' '), HB_TAG('S','Q','I',' ')}, /* Arvanitika Albanian -> Albanian */ - {HB_TAG('a','b','a',' '), HB_TAG_NONE }, /* Abé != Abaza */ {HB_TAG('a','b','h',' '), HB_TAG('A','R','A',' ')}, /* Tajiki Arabic -> Arabic */ {HB_TAG('a','b','q',' '), HB_TAG('A','B','A',' ')}, /* Abaza */ {HB_TAG('a','b','s',' '), HB_TAG('C','P','P',' ')}, /* Ambonese Malay -> Creoles */ {HB_TAG('a','b','v',' '), HB_TAG('A','R','A',' ')}, /* Baharna Arabic -> Arabic */ - {HB_TAG('a','c','f',' '), HB_TAG('F','A','N',' ')}, /* Saint Lucian Creole French -> French Antillean */ - {HB_TAG('a','c','f',' '), HB_TAG('C','P','P',' ')}, /* Saint Lucian Creole French -> Creoles */ -/*{HB_TAG('a','c','h',' '), HB_TAG('A','C','H',' ')},*/ /* Acoli -> Acholi */ {HB_TAG('a','c','m',' '), HB_TAG('A','R','A',' ')}, /* Mesopotamian Arabic -> Arabic */ {HB_TAG('a','c','q',' '), HB_TAG('A','R','A',' ')}, /* Ta'izzi-Adeni Arabic -> Arabic */ - {HB_TAG('a','c','r',' '), HB_TAG('A','C','R',' ')}, /* Achi */ - {HB_TAG('a','c','r',' '), HB_TAG('M','Y','N',' ')}, /* Achi -> Mayan */ {HB_TAG('a','c','w',' '), HB_TAG('A','R','A',' ')}, /* Hijazi Arabic -> Arabic */ {HB_TAG('a','c','x',' '), HB_TAG('A','R','A',' ')}, /* Omani Arabic -> Arabic */ - {HB_TAG('a','c','y',' '), HB_TAG('A','C','Y',' ')}, /* Cypriot Arabic */ - {HB_TAG('a','c','y',' '), HB_TAG('A','R','A',' ')}, /* Cypriot Arabic -> Arabic */ {HB_TAG('a','d','a',' '), HB_TAG('D','N','G',' ')}, /* Adangme -> Dangme */ {HB_TAG('a','d','f',' '), HB_TAG('A','R','A',' ')}, /* Dhofari Arabic -> Arabic */ {HB_TAG('a','d','p',' '), HB_TAG('D','Z','N',' ')}, /* Adap (retired code) -> Dzongkha */ -/*{HB_TAG('a','d','y',' '), HB_TAG('A','D','Y',' ')},*/ /* Adyghe */ {HB_TAG('a','e','b',' '), HB_TAG('A','R','A',' ')}, /* Tunisian Arabic -> Arabic */ {HB_TAG('a','e','c',' '), HB_TAG('A','R','A',' ')}, /* Saidi Arabic -> Arabic */ {HB_TAG('a','f','b',' '), HB_TAG('A','R','A',' ')}, /* Gulf Arabic -> Arabic */ - {HB_TAG('a','f','k',' '), HB_TAG_NONE }, /* Nanubae != Afrikaans */ {HB_TAG('a','f','s',' '), HB_TAG('C','P','P',' ')}, /* Afro-Seminole Creole -> Creoles */ {HB_TAG('a','g','u',' '), HB_TAG('M','Y','N',' ')}, /* Aguacateco -> Mayan */ - {HB_TAG('a','g','w',' '), HB_TAG_NONE }, /* Kahua != Agaw */ {HB_TAG('a','h','g',' '), HB_TAG('A','G','W',' ')}, /* Qimant -> Agaw */ {HB_TAG('a','h','t',' '), HB_TAG('A','T','H',' ')}, /* Ahtena -> Athapaskan */ {HB_TAG('a','i','g',' '), HB_TAG('C','P','P',' ')}, /* Antigua and Barbuda Creole English -> Creoles */ - {HB_TAG('a','i','i',' '), HB_TAG('S','W','A',' ')}, /* Assyrian Neo-Aramaic -> Swadaya Aramaic */ - {HB_TAG('a','i','i',' '), HB_TAG('S','Y','R',' ')}, /* Assyrian Neo-Aramaic -> Syriac */ -/*{HB_TAG('a','i','o',' '), HB_TAG('A','I','O',' ')},*/ /* Aiton */ {HB_TAG('a','i','w',' '), HB_TAG('A','R','I',' ')}, /* Aari */ {HB_TAG('a','j','p',' '), HB_TAG('A','R','A',' ')}, /* South Levantine Arabic (retired code) -> Arabic */ {HB_TAG('a','j','t',' '), HB_TAG('A','R','A',' ')}, /* Judeo-Tunisian Arabic (retired code) -> Arabic */ - {HB_TAG('a','k','b',' '), HB_TAG('A','K','B',' ')}, /* Batak Angkola */ - {HB_TAG('a','k','b',' '), HB_TAG('B','T','K',' ')}, /* Batak Angkola -> Batak */ {HB_TAG('a','l','n',' '), HB_TAG('S','Q','I',' ')}, /* Gheg Albanian -> Albanian */ {HB_TAG('a','l','s',' '), HB_TAG('S','Q','I',' ')}, /* Tosk Albanian -> Albanian */ -/*{HB_TAG('a','l','t',' '), HB_TAG('A','L','T',' ')},*/ /* Southern Altai -> Altai */ {HB_TAG('a','m','f',' '), HB_TAG('H','B','N',' ')}, /* Hamer-Banna -> Hammer-Banna */ {HB_TAG('a','m','w',' '), HB_TAG('S','Y','R',' ')}, /* Western Neo-Aramaic -> Syriac */ -/*{HB_TAG('a','n','g',' '), HB_TAG('A','N','G',' ')},*/ /* Old English (ca. 450-1100) -> Anglo-Saxon */ {HB_TAG('a','o','a',' '), HB_TAG('C','P','P',' ')}, /* Angolar -> Creoles */ {HB_TAG('a','p','a',' '), HB_TAG('A','T','H',' ')}, /* Apache [collection] -> Athapaskan */ {HB_TAG('a','p','c',' '), HB_TAG('A','R','A',' ')}, /* Levantine Arabic -> Arabic */ @@ -280,61 +426,39 @@ static const LangTag ot_languages3[] = { {HB_TAG('a','p','m',' '), HB_TAG('A','T','H',' ')}, /* Mescalero-Chiricahua Apache -> Athapaskan */ {HB_TAG('a','p','w',' '), HB_TAG('A','T','H',' ')}, /* Western Apache -> Athapaskan */ {HB_TAG('a','r','b',' '), HB_TAG('A','R','A',' ')}, /* Standard Arabic -> Arabic */ - {HB_TAG('a','r','i',' '), HB_TAG_NONE }, /* Arikara != Aari */ - {HB_TAG('a','r','k',' '), HB_TAG_NONE }, /* Arikapú != Rakhine */ {HB_TAG('a','r','n',' '), HB_TAG('M','A','P',' ')}, /* Mapudungun */ {HB_TAG('a','r','q',' '), HB_TAG('A','R','A',' ')}, /* Algerian Arabic -> Arabic */ {HB_TAG('a','r','s',' '), HB_TAG('A','R','A',' ')}, /* Najdi Arabic -> Arabic */ - {HB_TAG('a','r','y',' '), HB_TAG('M','O','R',' ')}, /* Moroccan Arabic -> Moroccan */ - {HB_TAG('a','r','y',' '), HB_TAG('A','R','A',' ')}, /* Moroccan Arabic -> Arabic */ {HB_TAG('a','r','z',' '), HB_TAG('A','R','A',' ')}, /* Egyptian Arabic -> Arabic */ -/*{HB_TAG('a','s','t',' '), HB_TAG('A','S','T',' ')},*/ /* Asturian */ -/*{HB_TAG('a','t','h',' '), HB_TAG('A','T','H',' ')},*/ /* Athapascan [collection] -> Athapaskan */ {HB_TAG('a','t','j',' '), HB_TAG('R','C','R',' ')}, /* Atikamekw -> R-Cree */ -/*{HB_TAG('a','t','s',' '), HB_TAG('A','T','S',' ')},*/ /* Gros Ventre (Atsina) */ {HB_TAG('a','t','v',' '), HB_TAG('A','L','T',' ')}, /* Northern Altai -> Altai */ {HB_TAG('a','u','j',' '), HB_TAG('B','B','R',' ')}, /* Awjilah -> Berber */ {HB_TAG('a','u','z',' '), HB_TAG('A','R','A',' ')}, /* Uzbeki Arabic -> Arabic */ {HB_TAG('a','v','l',' '), HB_TAG('A','R','A',' ')}, /* Eastern Egyptian Bedawi Arabic -> Arabic */ -/*{HB_TAG('a','v','n',' '), HB_TAG('A','V','N',' ')},*/ /* Avatime */ -/*{HB_TAG('a','w','a',' '), HB_TAG('A','W','A',' ')},*/ /* Awadhi */ {HB_TAG('a','y','c',' '), HB_TAG('A','Y','M',' ')}, /* Southern Aymara -> Aymara */ {HB_TAG('a','y','h',' '), HB_TAG('A','R','A',' ')}, /* Hadrami Arabic -> Arabic */ {HB_TAG('a','y','l',' '), HB_TAG('A','R','A',' ')}, /* Libyan Arabic -> Arabic */ {HB_TAG('a','y','n',' '), HB_TAG('A','R','A',' ')}, /* Sanaani Arabic -> Arabic */ {HB_TAG('a','y','p',' '), HB_TAG('A','R','A',' ')}, /* North Mesopotamian Arabic -> Arabic */ {HB_TAG('a','y','r',' '), HB_TAG('A','Y','M',' ')}, /* Central Aymara -> Aymara */ - {HB_TAG('a','z','b',' '), HB_TAG('A','Z','B',' ')}, /* South Azerbaijani -> Torki */ - {HB_TAG('a','z','b',' '), HB_TAG('A','Z','E',' ')}, /* South Azerbaijani -> Azerbaijani */ {HB_TAG('a','z','d',' '), HB_TAG('N','A','H',' ')}, /* Eastern Durango Nahuatl -> Nahuatl */ {HB_TAG('a','z','j',' '), HB_TAG('A','Z','E',' ')}, /* North Azerbaijani -> Azerbaijani */ {HB_TAG('a','z','n',' '), HB_TAG('N','A','H',' ')}, /* Western Durango Nahuatl -> Nahuatl */ {HB_TAG('a','z','z',' '), HB_TAG('N','A','H',' ')}, /* Highland Puebla Nahuatl -> Nahuatl */ {HB_TAG('b','a','d',' '), HB_TAG('B','A','D','0')}, /* Banda [collection] */ - {HB_TAG('b','a','g',' '), HB_TAG_NONE }, /* Tuki != Baghelkhandi */ {HB_TAG('b','a','h',' '), HB_TAG('C','P','P',' ')}, /* Bahamas Creole English -> Creoles */ {HB_TAG('b','a','i',' '), HB_TAG('B','M','L',' ')}, /* Bamileke [collection] */ {HB_TAG('b','a','l',' '), HB_TAG('B','L','I',' ')}, /* Baluchi [macrolanguage] */ -/*{HB_TAG('b','a','n',' '), HB_TAG('B','A','N',' ')},*/ /* Balinese */ -/*{HB_TAG('b','a','r',' '), HB_TAG('B','A','R',' ')},*/ /* Bavarian */ - {HB_TAG('b','a','u',' '), HB_TAG_NONE }, /* Bada (Nigeria) != Baulé */ - {HB_TAG('b','b','c',' '), HB_TAG('B','B','C',' ')}, /* Batak Toba */ - {HB_TAG('b','b','c',' '), HB_TAG('B','T','K',' ')}, /* Batak Toba -> Batak */ {HB_TAG('b','b','j',' '), HB_TAG('B','M','L',' ')}, /* Ghomálá' -> Bamileke */ {HB_TAG('b','b','p',' '), HB_TAG('B','A','D','0')}, /* West Central Banda -> Banda */ - {HB_TAG('b','b','r',' '), HB_TAG_NONE }, /* Girawa != Berber */ {HB_TAG('b','b','z',' '), HB_TAG('A','R','A',' ')}, /* Babalia Creole Arabic (retired code) -> Arabic */ {HB_TAG('b','c','c',' '), HB_TAG('B','L','I',' ')}, /* Southern Balochi -> Baluchi */ - {HB_TAG('b','c','h',' '), HB_TAG_NONE }, /* Bariai != Bench */ {HB_TAG('b','c','i',' '), HB_TAG('B','A','U',' ')}, /* Baoulé -> Baulé */ {HB_TAG('b','c','l',' '), HB_TAG('B','I','K',' ')}, /* Central Bikol -> Bikol */ {HB_TAG('b','c','q',' '), HB_TAG('B','C','H',' ')}, /* Bench */ {HB_TAG('b','c','r',' '), HB_TAG('A','T','H',' ')}, /* Babine -> Athapaskan */ -/*{HB_TAG('b','d','c',' '), HB_TAG('B','D','C',' ')},*/ /* Emberá-Baudó */ -/*{HB_TAG('b','d','y',' '), HB_TAG('B','D','Y',' ')},*/ /* Bandjalang */ {HB_TAG('b','e','a',' '), HB_TAG('A','T','H',' ')}, /* Beaver -> Athapaskan */ {HB_TAG('b','e','b',' '), HB_TAG('B','T','I',' ')}, /* Bebele -> Beti */ -/*{HB_TAG('b','e','m',' '), HB_TAG('B','E','M',' ')},*/ /* Bemba (Zambia) */ {HB_TAG('b','e','r',' '), HB_TAG('B','B','R',' ')}, /* Berber [collection] */ {HB_TAG('b','e','w',' '), HB_TAG('C','P','P',' ')}, /* Betawi -> Creoles */ {HB_TAG('b','f','l',' '), HB_TAG('B','A','D','0')}, /* Banda-Ndélé -> Banda */ @@ -342,70 +466,35 @@ static const LangTag ot_languages3[] = { {HB_TAG('b','f','t',' '), HB_TAG('B','L','T',' ')}, /* Balti */ {HB_TAG('b','f','u',' '), HB_TAG('L','A','H',' ')}, /* Gahri -> Lahuli */ {HB_TAG('b','f','y',' '), HB_TAG('B','A','G',' ')}, /* Bagheli -> Baghelkhandi */ -/*{HB_TAG('b','g','c',' '), HB_TAG('B','G','C',' ')},*/ /* Haryanvi */ {HB_TAG('b','g','n',' '), HB_TAG('B','L','I',' ')}, /* Western Balochi -> Baluchi */ {HB_TAG('b','g','p',' '), HB_TAG('B','L','I',' ')}, /* Eastern Balochi -> Baluchi */ - {HB_TAG('b','g','q',' '), HB_TAG('B','G','Q',' ')}, /* Bagri */ - {HB_TAG('b','g','q',' '), HB_TAG('R','A','J',' ')}, /* Bagri -> Rajasthani */ {HB_TAG('b','g','r',' '), HB_TAG('Q','I','N',' ')}, /* Bawm Chin -> Chin */ {HB_TAG('b','h','b',' '), HB_TAG('B','H','I',' ')}, /* Bhili */ -/*{HB_TAG('b','h','i',' '), HB_TAG('B','H','I',' ')},*/ /* Bhilali -> Bhili */ {HB_TAG('b','h','k',' '), HB_TAG('B','I','K',' ')}, /* Albay Bicolano (retired code) -> Bikol */ -/*{HB_TAG('b','h','o',' '), HB_TAG('B','H','O',' ')},*/ /* Bhojpuri */ {HB_TAG('b','h','r',' '), HB_TAG('M','L','G',' ')}, /* Bara Malagasy -> Malagasy */ -/*{HB_TAG('b','i','k',' '), HB_TAG('B','I','K',' ')},*/ /* Bikol [macrolanguage] */ - {HB_TAG('b','i','l',' '), HB_TAG_NONE }, /* Bile != Bilen */ {HB_TAG('b','i','n',' '), HB_TAG('E','D','O',' ')}, /* Edo */ {HB_TAG('b','i','u',' '), HB_TAG('Q','I','N',' ')}, /* Biete -> Chin */ -/*{HB_TAG('b','j','j',' '), HB_TAG('B','J','J',' ')},*/ /* Kanauji */ {HB_TAG('b','j','n',' '), HB_TAG('M','L','Y',' ')}, /* Banjar -> Malay */ {HB_TAG('b','j','o',' '), HB_TAG('B','A','D','0')}, /* Mid-Southern Banda -> Banda */ {HB_TAG('b','j','q',' '), HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy (retired code) -> Malagasy */ {HB_TAG('b','j','s',' '), HB_TAG('C','P','P',' ')}, /* Bajan -> Creoles */ {HB_TAG('b','j','t',' '), HB_TAG('B','L','N',' ')}, /* Balanta-Ganja -> Balante */ - {HB_TAG('b','k','f',' '), HB_TAG_NONE }, /* Beeke != Blackfoot */ {HB_TAG('b','k','o',' '), HB_TAG('B','M','L',' ')}, /* Kwa' -> Bamileke */ {HB_TAG('b','l','a',' '), HB_TAG('B','K','F',' ')}, /* Siksika -> Blackfoot */ {HB_TAG('b','l','e',' '), HB_TAG('B','L','N',' ')}, /* Balanta-Kentohe -> Balante */ {HB_TAG('b','l','g',' '), HB_TAG('I','B','A',' ')}, /* Balau (retired code) -> Iban */ - {HB_TAG('b','l','i',' '), HB_TAG_NONE }, /* Bolia != Baluchi */ - {HB_TAG('b','l','k',' '), HB_TAG('B','L','K',' ')}, /* Pa’o Karen */ - {HB_TAG('b','l','k',' '), HB_TAG('K','R','N',' ')}, /* Pa'o Karen -> Karen */ {HB_TAG('b','l','n',' '), HB_TAG('B','I','K',' ')}, /* Southern Catanduanes Bikol -> Bikol */ - {HB_TAG('b','l','t',' '), HB_TAG_NONE }, /* Tai Dam != Balti */ - {HB_TAG('b','m','b',' '), HB_TAG_NONE }, /* Bembe != Bambara (Bamanankan) */ - {HB_TAG('b','m','l',' '), HB_TAG_NONE }, /* Bomboli != Bamileke */ {HB_TAG('b','m','m',' '), HB_TAG('M','L','G',' ')}, /* Northern Betsimisaraka Malagasy -> Malagasy */ {HB_TAG('b','p','d',' '), HB_TAG('B','A','D','0')}, /* Banda-Banda -> Banda */ {HB_TAG('b','p','l',' '), HB_TAG('C','P','P',' ')}, /* Broome Pearling Lugger Pidgin -> Creoles */ {HB_TAG('b','p','q',' '), HB_TAG('C','P','P',' ')}, /* Banda Malay -> Creoles */ -/*{HB_TAG('b','p','y',' '), HB_TAG('B','P','Y',' ')},*/ /* Bishnupriya -> Bishnupriya Manipuri */ {HB_TAG('b','q','i',' '), HB_TAG('L','R','C',' ')}, /* Bakhtiari -> Luri */ {HB_TAG('b','q','k',' '), HB_TAG('B','A','D','0')}, /* Banda-Mbrès -> Banda */ {HB_TAG('b','r','a',' '), HB_TAG('B','R','I',' ')}, /* Braj -> Braj Bhasha */ {HB_TAG('b','r','c',' '), HB_TAG('C','P','P',' ')}, /* Berbice Creole Dutch -> Creoles */ -/*{HB_TAG('b','r','h',' '), HB_TAG('B','R','H',' ')},*/ /* Brahui */ - {HB_TAG('b','r','i',' '), HB_TAG_NONE }, /* Mokpwe != Braj Bhasha */ - {HB_TAG('b','r','m',' '), HB_TAG_NONE }, /* Barambu != Burmese */ -/*{HB_TAG('b','r','x',' '), HB_TAG('B','R','X',' ')},*/ /* Bodo (India) */ - {HB_TAG('b','s','h',' '), HB_TAG_NONE }, /* Kati != Bashkir */ -/*{HB_TAG('b','s','k',' '), HB_TAG('B','S','K',' ')},*/ /* Burushaski */ {HB_TAG('b','t','b',' '), HB_TAG('B','T','I',' ')}, /* Beti (Cameroon) (retired code) */ - {HB_TAG('b','t','d',' '), HB_TAG('B','T','D',' ')}, /* Batak Dairi (Pakpak) */ - {HB_TAG('b','t','d',' '), HB_TAG('B','T','K',' ')}, /* Batak Dairi -> Batak */ - {HB_TAG('b','t','i',' '), HB_TAG_NONE }, /* Burate != Beti */ {HB_TAG('b','t','j',' '), HB_TAG('M','L','Y',' ')}, /* Bacanese Malay -> Malay */ -/*{HB_TAG('b','t','k',' '), HB_TAG('B','T','K',' ')},*/ /* Batak [collection] */ - {HB_TAG('b','t','m',' '), HB_TAG('B','T','M',' ')}, /* Batak Mandailing */ - {HB_TAG('b','t','m',' '), HB_TAG('B','T','K',' ')}, /* Batak Mandailing -> Batak */ {HB_TAG('b','t','o',' '), HB_TAG('B','I','K',' ')}, /* Rinconada Bikol -> Bikol */ - {HB_TAG('b','t','s',' '), HB_TAG('B','T','S',' ')}, /* Batak Simalungun */ - {HB_TAG('b','t','s',' '), HB_TAG('B','T','K',' ')}, /* Batak Simalungun -> Batak */ - {HB_TAG('b','t','x',' '), HB_TAG('B','T','X',' ')}, /* Batak Karo */ - {HB_TAG('b','t','x',' '), HB_TAG('B','T','K',' ')}, /* Batak Karo -> Batak */ - {HB_TAG('b','t','z',' '), HB_TAG('B','T','Z',' ')}, /* Batak Alas-Kluet */ - {HB_TAG('b','t','z',' '), HB_TAG('B','T','K',' ')}, /* Batak Alas-Kluet -> Batak */ -/*{HB_TAG('b','u','g',' '), HB_TAG('B','U','G',' ')},*/ /* Buginese -> Bugis */ {HB_TAG('b','u','m',' '), HB_TAG('B','T','I',' ')}, /* Bulu (Cameroon) -> Beti */ {HB_TAG('b','v','e',' '), HB_TAG('M','L','Y',' ')}, /* Berau Malay -> Malay */ {HB_TAG('b','v','u',' '), HB_TAG('M','L','Y',' ')}, /* Bukit Malay -> Malay */ @@ -415,52 +504,26 @@ static const LangTag ot_languages3[] = { {HB_TAG('b','x','p',' '), HB_TAG('B','T','I',' ')}, /* Bebil -> Beti */ {HB_TAG('b','x','r',' '), HB_TAG('R','B','U',' ')}, /* Russia Buriat -> Russian Buriat */ {HB_TAG('b','y','n',' '), HB_TAG('B','I','L',' ')}, /* Bilin -> Bilen */ - {HB_TAG('b','y','v',' '), HB_TAG('B','Y','V',' ')}, /* Medumba */ - {HB_TAG('b','y','v',' '), HB_TAG('B','M','L',' ')}, /* Medumba -> Bamileke */ {HB_TAG('b','z','c',' '), HB_TAG('M','L','G',' ')}, /* Southern Betsimisaraka Malagasy -> Malagasy */ {HB_TAG('b','z','j',' '), HB_TAG('C','P','P',' ')}, /* Belize Kriol English -> Creoles */ {HB_TAG('b','z','k',' '), HB_TAG('C','P','P',' ')}, /* Nicaragua Creole English -> Creoles */ {HB_TAG('c','a','a',' '), HB_TAG('M','Y','N',' ')}, /* Chortí -> Mayan */ {HB_TAG('c','a','c',' '), HB_TAG('M','Y','N',' ')}, /* Chuj -> Mayan */ - {HB_TAG('c','a','f',' '), HB_TAG('C','R','R',' ')}, /* Southern Carrier -> Carrier */ - {HB_TAG('c','a','f',' '), HB_TAG('A','T','H',' ')}, /* Southern Carrier -> Athapaskan */ - {HB_TAG('c','a','k',' '), HB_TAG('C','A','K',' ')}, /* Kaqchikel */ - {HB_TAG('c','a','k',' '), HB_TAG('M','Y','N',' ')}, /* Kaqchikel -> Mayan */ -/*{HB_TAG('c','a','y',' '), HB_TAG('C','A','Y',' ')},*/ /* Cayuga */ -/*{HB_TAG('c','b','g',' '), HB_TAG('C','B','G',' ')},*/ /* Chimila */ - {HB_TAG('c','b','k',' '), HB_TAG('C','B','K',' ')}, /* Chavacano -> Zamboanga Chavacano */ - {HB_TAG('c','b','k',' '), HB_TAG('C','P','P',' ')}, /* Chavacano -> Creoles */ {HB_TAG('c','b','l',' '), HB_TAG('Q','I','N',' ')}, /* Bualkhaw Chin -> Chin */ {HB_TAG('c','c','l',' '), HB_TAG('C','P','P',' ')}, /* Cutchi-Swahili -> Creoles */ {HB_TAG('c','c','m',' '), HB_TAG('C','P','P',' ')}, /* Malaccan Creole Malay -> Creoles */ {HB_TAG('c','c','o',' '), HB_TAG('C','C','H','N')}, /* Comaltepec Chinantec -> Chinantec */ {HB_TAG('c','c','q',' '), HB_TAG('A','R','K',' ')}, /* Chaungtha (retired code) -> Rakhine */ {HB_TAG('c','d','o',' '), HB_TAG('Z','H','S',' ')}, /* Min Dong Chinese -> Chinese, Simplified */ -/*{HB_TAG('c','e','b',' '), HB_TAG('C','E','B',' ')},*/ /* Cebuano */ {HB_TAG('c','e','k',' '), HB_TAG('Q','I','N',' ')}, /* Eastern Khumi Chin -> Chin */ {HB_TAG('c','e','y',' '), HB_TAG('Q','I','N',' ')}, /* Ekai Chin -> Chin */ - {HB_TAG('c','f','m',' '), HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) */ - {HB_TAG('c','f','m',' '), HB_TAG('Q','I','N',' ')}, /* Falam Chin -> Chin */ -/*{HB_TAG('c','g','g',' '), HB_TAG('C','G','G',' ')},*/ /* Chiga */ {HB_TAG('c','h','f',' '), HB_TAG('M','Y','N',' ')}, /* Tabasco Chontal -> Mayan */ - {HB_TAG('c','h','g',' '), HB_TAG_NONE }, /* Chagatai != Chaha Gurage */ - {HB_TAG('c','h','h',' '), HB_TAG_NONE }, /* Chinook != Chattisgarhi */ {HB_TAG('c','h','j',' '), HB_TAG('C','C','H','N')}, /* Ojitlán Chinantec -> Chinantec */ {HB_TAG('c','h','k',' '), HB_TAG('C','H','K','0')}, /* Chuukese */ - {HB_TAG('c','h','m',' '), HB_TAG('H','M','A',' ')}, /* Mari (Russia) [macrolanguage] -> High Mari */ - {HB_TAG('c','h','m',' '), HB_TAG('L','M','A',' ')}, /* Mari (Russia) [macrolanguage] -> Low Mari */ {HB_TAG('c','h','n',' '), HB_TAG('C','P','P',' ')}, /* Chinook jargon -> Creoles */ -/*{HB_TAG('c','h','o',' '), HB_TAG('C','H','O',' ')},*/ /* Choctaw */ - {HB_TAG('c','h','p',' '), HB_TAG('C','H','P',' ')}, /* Chipewyan */ - {HB_TAG('c','h','p',' '), HB_TAG('S','A','Y',' ')}, /* Chipewyan -> Sayisi */ - {HB_TAG('c','h','p',' '), HB_TAG('A','T','H',' ')}, /* Chipewyan -> Athapaskan */ {HB_TAG('c','h','q',' '), HB_TAG('C','C','H','N')}, /* Quiotepec Chinantec -> Chinantec */ -/*{HB_TAG('c','h','r',' '), HB_TAG('C','H','R',' ')},*/ /* Cherokee */ -/*{HB_TAG('c','h','y',' '), HB_TAG('C','H','Y',' ')},*/ /* Cheyenne */ {HB_TAG('c','h','z',' '), HB_TAG('C','C','H','N')}, /* Ozumacín Chinantec -> Chinantec */ {HB_TAG('c','i','w',' '), HB_TAG('O','J','B',' ')}, /* Chippewa -> Ojibway */ -/*{HB_TAG('c','j','a',' '), HB_TAG('C','J','A',' ')},*/ /* Western Cham */ -/*{HB_TAG('c','j','m',' '), HB_TAG('C','J','M',' ')},*/ /* Eastern Cham */ {HB_TAG('c','j','y',' '), HB_TAG('Z','H','S',' ')}, /* Jinyu Chinese -> Chinese, Simplified */ {HB_TAG('c','k','a',' '), HB_TAG('Q','I','N',' ')}, /* Khumi Awa Chin (retired code) -> Chin */ {HB_TAG('c','k','b',' '), HB_TAG('K','U','R',' ')}, /* Central Kurdish -> Kurdish */ @@ -474,7 +537,6 @@ static const LangTag ot_languages3[] = { {HB_TAG('c','l','j',' '), HB_TAG('Q','I','N',' ')}, /* Laitu Chin -> Chin */ {HB_TAG('c','l','s',' '), HB_TAG('S','A','N',' ')}, /* Classical Sanskrit -> Sanskrit */ {HB_TAG('c','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Lautu Chin -> Chin */ -/*{HB_TAG('c','m','i',' '), HB_TAG('C','M','I',' ')},*/ /* Emberá-Chamí */ {HB_TAG('c','m','n',' '), HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese, Simplified */ {HB_TAG('c','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */ {HB_TAG('c','n','b',' '), HB_TAG('Q','I','N',' ')}, /* Chinbon Chin -> Chin */ @@ -488,120 +550,63 @@ static const LangTag ot_languages3[] = { {HB_TAG('c','n','w',' '), HB_TAG('Q','I','N',' ')}, /* Ngawn Chin -> Chin */ {HB_TAG('c','o','a',' '), HB_TAG('M','L','Y',' ')}, /* Cocos Islands Malay -> Malay */ {HB_TAG('c','o','b',' '), HB_TAG('M','Y','N',' ')}, /* Chicomuceltec -> Mayan */ -/*{HB_TAG('c','o','o',' '), HB_TAG('C','O','O',' ')},*/ /* Comox */ -/*{HB_TAG('c','o','p',' '), HB_TAG('C','O','P',' ')},*/ /* Coptic */ {HB_TAG('c','o','q',' '), HB_TAG('A','T','H',' ')}, /* Coquille -> Athapaskan */ {HB_TAG('c','p','a',' '), HB_TAG('C','C','H','N')}, /* Palantla Chinantec -> Chinantec */ {HB_TAG('c','p','e',' '), HB_TAG('C','P','P',' ')}, /* English-based creoles and pidgins [collection] -> Creoles */ {HB_TAG('c','p','f',' '), HB_TAG('C','P','P',' ')}, /* French-based creoles and pidgins [collection] -> Creoles */ {HB_TAG('c','p','i',' '), HB_TAG('C','P','P',' ')}, /* Chinese Pidgin English -> Creoles */ -/*{HB_TAG('c','p','p',' '), HB_TAG('C','P','P',' ')},*/ /* Portuguese-based creoles and pidgins [collection] -> Creoles */ {HB_TAG('c','p','x',' '), HB_TAG('Z','H','S',' ')}, /* Pu-Xian Chinese -> Chinese, Simplified */ {HB_TAG('c','q','d',' '), HB_TAG('H','M','N',' ')}, /* Chuanqiandian Cluster Miao -> Hmong */ - {HB_TAG('c','q','u',' '), HB_TAG('Q','U','H',' ')}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */ - {HB_TAG('c','q','u',' '), HB_TAG('Q','U','Z',' ')}, /* Chilean Quechua (retired code) -> Quechua */ {HB_TAG('c','r','h',' '), HB_TAG('C','R','T',' ')}, /* Crimean Tatar */ {HB_TAG('c','r','i',' '), HB_TAG('C','P','P',' ')}, /* Sãotomense -> Creoles */ - {HB_TAG('c','r','j',' '), HB_TAG('E','C','R',' ')}, /* Southern East Cree -> Eastern Cree */ - {HB_TAG('c','r','j',' '), HB_TAG('Y','C','R',' ')}, /* Southern East Cree -> Y-Cree */ - {HB_TAG('c','r','j',' '), HB_TAG('C','R','E',' ')}, /* Southern East Cree -> Cree */ - {HB_TAG('c','r','k',' '), HB_TAG('W','C','R',' ')}, /* Plains Cree -> West-Cree */ - {HB_TAG('c','r','k',' '), HB_TAG('Y','C','R',' ')}, /* Plains Cree -> Y-Cree */ - {HB_TAG('c','r','k',' '), HB_TAG('C','R','E',' ')}, /* Plains Cree -> Cree */ - {HB_TAG('c','r','l',' '), HB_TAG('E','C','R',' ')}, /* Northern East Cree -> Eastern Cree */ - {HB_TAG('c','r','l',' '), HB_TAG('Y','C','R',' ')}, /* Northern East Cree -> Y-Cree */ - {HB_TAG('c','r','l',' '), HB_TAG('C','R','E',' ')}, /* Northern East Cree -> Cree */ - {HB_TAG('c','r','m',' '), HB_TAG('M','C','R',' ')}, /* Moose Cree */ - {HB_TAG('c','r','m',' '), HB_TAG('L','C','R',' ')}, /* Moose Cree -> L-Cree */ - {HB_TAG('c','r','m',' '), HB_TAG('C','R','E',' ')}, /* Moose Cree -> Cree */ {HB_TAG('c','r','p',' '), HB_TAG('C','P','P',' ')}, /* Creoles and pidgins [collection] -> Creoles */ - {HB_TAG('c','r','r',' '), HB_TAG_NONE }, /* Carolina Algonquian != Carrier */ {HB_TAG('c','r','s',' '), HB_TAG('C','P','P',' ')}, /* Seselwa Creole French -> Creoles */ - {HB_TAG('c','r','t',' '), HB_TAG_NONE }, /* Iyojwa'ja Chorote != Crimean Tatar */ - {HB_TAG('c','r','x',' '), HB_TAG('C','R','R',' ')}, /* Carrier */ - {HB_TAG('c','r','x',' '), HB_TAG('A','T','H',' ')}, /* Carrier -> Athapaskan */ {HB_TAG('c','s','a',' '), HB_TAG('C','C','H','N')}, /* Chiltepec Chinantec -> Chinantec */ -/*{HB_TAG('c','s','b',' '), HB_TAG('C','S','B',' ')},*/ /* Kashubian */ {HB_TAG('c','s','h',' '), HB_TAG('Q','I','N',' ')}, /* Asho Chin -> Chin */ {HB_TAG('c','s','j',' '), HB_TAG('Q','I','N',' ')}, /* Songlai Chin -> Chin */ - {HB_TAG('c','s','l',' '), HB_TAG_NONE }, /* Chinese Sign Language != Church Slavonic */ {HB_TAG('c','s','o',' '), HB_TAG('C','C','H','N')}, /* Sochiapam Chinantec -> Chinantec */ {HB_TAG('c','s','p',' '), HB_TAG('Z','H','S',' ')}, /* Southern Ping Chinese -> Chinese, Simplified */ {HB_TAG('c','s','v',' '), HB_TAG('Q','I','N',' ')}, /* Sumtu Chin -> Chin */ - {HB_TAG('c','s','w',' '), HB_TAG('N','C','R',' ')}, /* Swampy Cree -> N-Cree */ - {HB_TAG('c','s','w',' '), HB_TAG('N','H','C',' ')}, /* Swampy Cree -> Norway House Cree */ - {HB_TAG('c','s','w',' '), HB_TAG('C','R','E',' ')}, /* Swampy Cree -> Cree */ {HB_TAG('c','s','y',' '), HB_TAG('Q','I','N',' ')}, /* Siyin Chin -> Chin */ {HB_TAG('c','t','c',' '), HB_TAG('A','T','H',' ')}, /* Chetco -> Athapaskan */ {HB_TAG('c','t','d',' '), HB_TAG('Q','I','N',' ')}, /* Tedim Chin -> Chin */ {HB_TAG('c','t','e',' '), HB_TAG('C','C','H','N')}, /* Tepinapa Chinantec -> Chinantec */ -/*{HB_TAG('c','t','g',' '), HB_TAG('C','T','G',' ')},*/ /* Chittagonian */ {HB_TAG('c','t','h',' '), HB_TAG('Q','I','N',' ')}, /* Thaiphum Chin -> Chin */ {HB_TAG('c','t','l',' '), HB_TAG('C','C','H','N')}, /* Tlacoatzintepec Chinantec -> Chinantec */ -/*{HB_TAG('c','t','o',' '), HB_TAG('C','T','O',' ')},*/ /* Emberá-Catío */ {HB_TAG('c','t','s',' '), HB_TAG('B','I','K',' ')}, /* Northern Catanduanes Bikol -> Bikol */ -/*{HB_TAG('c','t','t',' '), HB_TAG('C','T','T',' ')},*/ /* Wayanad Chetti */ {HB_TAG('c','t','u',' '), HB_TAG('M','Y','N',' ')}, /* Chol -> Mayan */ {HB_TAG('c','u','c',' '), HB_TAG('C','C','H','N')}, /* Usila Chinantec -> Chinantec */ -/*{HB_TAG('c','u','k',' '), HB_TAG('C','U','K',' ')},*/ /* San Blas Kuna */ {HB_TAG('c','v','n',' '), HB_TAG('C','C','H','N')}, /* Valle Nacional Chinantec -> Chinantec */ - {HB_TAG('c','w','d',' '), HB_TAG('D','C','R',' ')}, /* Woods Cree */ - {HB_TAG('c','w','d',' '), HB_TAG('T','C','R',' ')}, /* Woods Cree -> TH-Cree */ - {HB_TAG('c','w','d',' '), HB_TAG('C','R','E',' ')}, /* Woods Cree -> Cree */ {HB_TAG('c','z','h',' '), HB_TAG('Z','H','S',' ')}, /* Huizhou Chinese -> Chinese, Simplified */ {HB_TAG('c','z','o',' '), HB_TAG('Z','H','S',' ')}, /* Min Zhong Chinese -> Chinese, Simplified */ {HB_TAG('c','z','t',' '), HB_TAG('Q','I','N',' ')}, /* Zotung Chin -> Chin */ -/*{HB_TAG('d','a','g',' '), HB_TAG('D','A','G',' ')},*/ /* Dagbani */ {HB_TAG('d','a','o',' '), HB_TAG('Q','I','N',' ')}, /* Daai Chin -> Chin */ {HB_TAG('d','a','p',' '), HB_TAG('N','I','S',' ')}, /* Nisi (India) (retired code) */ -/*{HB_TAG('d','a','r',' '), HB_TAG('D','A','R',' ')},*/ /* Dargwa */ -/*{HB_TAG('d','a','x',' '), HB_TAG('D','A','X',' ')},*/ /* Dayi */ {HB_TAG('d','c','r',' '), HB_TAG('C','P','P',' ')}, /* Negerhollands -> Creoles */ - {HB_TAG('d','e','n',' '), HB_TAG('S','L','A',' ')}, /* Slave (Athapascan) [macrolanguage] -> Slavey */ - {HB_TAG('d','e','n',' '), HB_TAG('A','T','H',' ')}, /* Slave (Athapascan) [macrolanguage] -> Athapaskan */ {HB_TAG('d','e','p',' '), HB_TAG('C','P','P',' ')}, /* Pidgin Delaware -> Creoles */ - {HB_TAG('d','g','o',' '), HB_TAG('D','G','O',' ')}, /* Dogri (individual language) */ - {HB_TAG('d','g','o',' '), HB_TAG('D','G','R',' ')}, /* Dogri (macrolanguage) */ {HB_TAG('d','g','r',' '), HB_TAG('A','T','H',' ')}, /* Tlicho -> Athapaskan */ {HB_TAG('d','h','d',' '), HB_TAG('M','A','W',' ')}, /* Dhundari -> Marwari */ -/*{HB_TAG('d','h','g',' '), HB_TAG('D','H','G',' ')},*/ /* Dhangu */ - {HB_TAG('d','h','v',' '), HB_TAG_NONE }, /* Dehu != Divehi (Dhivehi, Maldivian) (deprecated) */ {HB_TAG('d','i','b',' '), HB_TAG('D','N','K',' ')}, /* South Central Dinka -> Dinka */ {HB_TAG('d','i','k',' '), HB_TAG('D','N','K',' ')}, /* Southwestern Dinka -> Dinka */ {HB_TAG('d','i','n',' '), HB_TAG('D','N','K',' ')}, /* Dinka [macrolanguage] */ {HB_TAG('d','i','p',' '), HB_TAG('D','N','K',' ')}, /* Northeastern Dinka -> Dinka */ - {HB_TAG('d','i','q',' '), HB_TAG('D','I','Q',' ')}, /* Dimli */ - {HB_TAG('d','i','q',' '), HB_TAG('Z','Z','A',' ')}, /* Dimli -> Zazaki */ {HB_TAG('d','i','w',' '), HB_TAG('D','N','K',' ')}, /* Northwestern Dinka -> Dinka */ {HB_TAG('d','j','e',' '), HB_TAG('D','J','R',' ')}, /* Zarma */ {HB_TAG('d','j','k',' '), HB_TAG('C','P','P',' ')}, /* Eastern Maroon Creole -> Creoles */ {HB_TAG('d','j','r',' '), HB_TAG('D','J','R','0')}, /* Djambarrpuyngu */ {HB_TAG('d','k','s',' '), HB_TAG('D','N','K',' ')}, /* Southeastern Dinka -> Dinka */ {HB_TAG('d','n','g',' '), HB_TAG('D','U','N',' ')}, /* Dungan */ -/*{HB_TAG('d','n','j',' '), HB_TAG('D','N','J',' ')},*/ /* Dan */ - {HB_TAG('d','n','k',' '), HB_TAG_NONE }, /* Dengka != Dinka */ {HB_TAG('d','o','i',' '), HB_TAG('D','G','R',' ')}, /* Dogri (macrolanguage) [macrolanguage] */ {HB_TAG('d','r','h',' '), HB_TAG('M','N','G',' ')}, /* Darkhat (retired code) -> Mongolian */ - {HB_TAG('d','r','i',' '), HB_TAG_NONE }, /* C'Lela != Dari */ - {HB_TAG('d','r','w',' '), HB_TAG('D','R','I',' ')}, /* Darwazi (retired code) -> Dari */ - {HB_TAG('d','r','w',' '), HB_TAG('F','A','R',' ')}, /* Darwazi (retired code) -> Persian */ {HB_TAG('d','s','b',' '), HB_TAG('L','S','B',' ')}, /* Lower Sorbian */ {HB_TAG('d','t','y',' '), HB_TAG('N','E','P',' ')}, /* Dotyali -> Nepali */ -/*{HB_TAG('d','u','j',' '), HB_TAG('D','U','J',' ')},*/ /* Dhuwal (retired code) */ - {HB_TAG('d','u','n',' '), HB_TAG_NONE }, /* Dusun Deyah != Dungan */ {HB_TAG('d','u','p',' '), HB_TAG('M','L','Y',' ')}, /* Duano -> Malay */ {HB_TAG('d','w','k',' '), HB_TAG('K','U','I',' ')}, /* Dawik Kui -> Kui */ {HB_TAG('d','w','u',' '), HB_TAG('D','U','J',' ')}, /* Dhuwal */ {HB_TAG('d','w','y',' '), HB_TAG('D','U','J',' ')}, /* Dhuwaya -> Dhuwal */ {HB_TAG('d','y','u',' '), HB_TAG('J','U','L',' ')}, /* Dyula -> Jula */ - {HB_TAG('d','z','n',' '), HB_TAG_NONE }, /* Dzando != Dzongkha */ - {HB_TAG('e','c','r',' '), HB_TAG_NONE }, /* Eteocretan != Eastern Cree */ -/*{HB_TAG('e','f','i',' '), HB_TAG('E','F','I',' ')},*/ /* Efik */ {HB_TAG('e','k','k',' '), HB_TAG('E','T','I',' ')}, /* Standard Estonian -> Estonian */ {HB_TAG('e','k','y',' '), HB_TAG('K','R','N',' ')}, /* Eastern Kayah -> Karen */ - {HB_TAG('e','m','k',' '), HB_TAG('E','M','K',' ')}, /* Eastern Maninkakan */ - {HB_TAG('e','m','k',' '), HB_TAG('M','N','K',' ')}, /* Eastern Maninkakan -> Maninka */ -/*{HB_TAG('e','m','p',' '), HB_TAG('E','M','P',' ')},*/ /* Northern Emberá */ {HB_TAG('e','m','y',' '), HB_TAG('M','Y','N',' ')}, /* Epigraphic Mayan -> Mayan */ {HB_TAG('e','n','b',' '), HB_TAG('K','A','L',' ')}, /* Markweeta -> Kalenjin */ {HB_TAG('e','n','f',' '), HB_TAG('F','N','E',' ')}, /* Forest Enets */ @@ -609,52 +614,27 @@ static const LangTag ot_languages3[] = { {HB_TAG('e','s','g',' '), HB_TAG('G','O','N',' ')}, /* Aheri Gondi -> Gondi */ {HB_TAG('e','s','i',' '), HB_TAG('I','P','K',' ')}, /* North Alaskan Inupiatun -> Inupiat */ {HB_TAG('e','s','k',' '), HB_TAG('I','P','K',' ')}, /* Northwest Alaska Inupiatun -> Inupiat */ -/*{HB_TAG('e','s','u',' '), HB_TAG('E','S','U',' ')},*/ /* Central Yupik */ {HB_TAG('e','t','o',' '), HB_TAG('B','T','I',' ')}, /* Eton (Cameroon) -> Beti */ - {HB_TAG('e','u','q',' '), HB_TAG_NONE }, /* Basque [collection] != Basque */ {HB_TAG('e','v','e',' '), HB_TAG('E','V','N',' ')}, /* Even */ {HB_TAG('e','v','n',' '), HB_TAG('E','V','K',' ')}, /* Evenki */ {HB_TAG('e','w','o',' '), HB_TAG('B','T','I',' ')}, /* Ewondo -> Beti */ {HB_TAG('e','y','o',' '), HB_TAG('K','A','L',' ')}, /* Keiyo -> Kalenjin */ {HB_TAG('f','a','b',' '), HB_TAG('C','P','P',' ')}, /* Fa d'Ambu -> Creoles */ - {HB_TAG('f','a','n',' '), HB_TAG('F','A','N','0')}, /* Fang (Equatorial Guinea) */ - {HB_TAG('f','a','n',' '), HB_TAG('B','T','I',' ')}, /* Fang (Equatorial Guinea) -> Beti */ - {HB_TAG('f','a','r',' '), HB_TAG_NONE }, /* Fataleka != Persian */ - {HB_TAG('f','a','t',' '), HB_TAG('F','A','T',' ')}, /* Fanti */ - {HB_TAG('f','a','t',' '), HB_TAG('A','K','A',' ')}, /* Fanti -> Akan */ {HB_TAG('f','b','l',' '), HB_TAG('B','I','K',' ')}, /* West Albay Bikol -> Bikol */ {HB_TAG('f','f','m',' '), HB_TAG('F','U','L',' ')}, /* Maasina Fulfulde -> Fulah */ {HB_TAG('f','i','l',' '), HB_TAG('P','I','L',' ')}, /* Filipino */ - {HB_TAG('f','l','m',' '), HB_TAG('H','A','L',' ')}, /* Halam (Falam Chin) (retired code) */ - {HB_TAG('f','l','m',' '), HB_TAG('Q','I','N',' ')}, /* Falam Chin (retired code) -> Chin */ - {HB_TAG('f','m','p',' '), HB_TAG('F','M','P',' ')}, /* Fe’fe’ */ - {HB_TAG('f','m','p',' '), HB_TAG('B','M','L',' ')}, /* Fe'fe' -> Bamileke */ {HB_TAG('f','n','g',' '), HB_TAG('C','P','P',' ')}, /* Fanagalo -> Creoles */ -/*{HB_TAG('f','o','n',' '), HB_TAG('F','O','N',' ')},*/ /* Fon */ - {HB_TAG('f','o','s',' '), HB_TAG_NONE }, /* Siraya != Faroese */ {HB_TAG('f','p','e',' '), HB_TAG('C','P','P',' ')}, /* Fernando Po Creole English -> Creoles */ -/*{HB_TAG('f','r','c',' '), HB_TAG('F','R','C',' ')},*/ /* Cajun French */ -/*{HB_TAG('f','r','p',' '), HB_TAG('F','R','P',' ')},*/ /* Arpitan */ {HB_TAG('f','u','b',' '), HB_TAG('F','U','L',' ')}, /* Adamawa Fulfulde -> Fulah */ {HB_TAG('f','u','c',' '), HB_TAG('F','U','L',' ')}, /* Pulaar -> Fulah */ {HB_TAG('f','u','e',' '), HB_TAG('F','U','L',' ')}, /* Borgu Fulfulde -> Fulah */ - {HB_TAG('f','u','f',' '), HB_TAG('F','T','A',' ')}, /* Pular -> Futa */ - {HB_TAG('f','u','f',' '), HB_TAG('F','U','L',' ')}, /* Pular -> Fulah */ {HB_TAG('f','u','h',' '), HB_TAG('F','U','L',' ')}, /* Western Niger Fulfulde -> Fulah */ {HB_TAG('f','u','i',' '), HB_TAG('F','U','L',' ')}, /* Bagirmi Fulfulde -> Fulah */ {HB_TAG('f','u','q',' '), HB_TAG('F','U','L',' ')}, /* Central-Eastern Niger Fulfulde -> Fulah */ {HB_TAG('f','u','r',' '), HB_TAG('F','R','L',' ')}, /* Friulian */ - {HB_TAG('f','u','v',' '), HB_TAG('F','U','V',' ')}, /* Nigerian Fulfulde */ - {HB_TAG('f','u','v',' '), HB_TAG('F','U','L',' ')}, /* Nigerian Fulfulde -> Fulah */ {HB_TAG('g','a','a',' '), HB_TAG('G','A','D',' ')}, /* Ga */ {HB_TAG('g','a','c',' '), HB_TAG('C','P','P',' ')}, /* Mixed Great Andamanese -> Creoles */ - {HB_TAG('g','a','d',' '), HB_TAG_NONE }, /* Gaddang != Ga */ - {HB_TAG('g','a','e',' '), HB_TAG_NONE }, /* Guarequena != Scottish Gaelic */ -/*{HB_TAG('g','a','g',' '), HB_TAG('G','A','G',' ')},*/ /* Gagauz */ - {HB_TAG('g','a','l',' '), HB_TAG_NONE }, /* Galolen != Galician */ {HB_TAG('g','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese, Simplified */ - {HB_TAG('g','a','r',' '), HB_TAG_NONE }, /* Galeya != Garshuni */ - {HB_TAG('g','a','w',' '), HB_TAG_NONE }, /* Nobonob != Garhwali */ {HB_TAG('g','a','x',' '), HB_TAG('O','R','O',' ')}, /* Borana-Arsi-Guji Oromo -> Oromo */ {HB_TAG('g','a','z',' '), HB_TAG('O','R','O',' ')}, /* West Central Oromo -> Oromo */ {HB_TAG('g','b','m',' '), HB_TAG('G','A','W',' ')}, /* Garhwali */ @@ -663,69 +643,46 @@ static const LangTag ot_languages3[] = { {HB_TAG('g','c','l',' '), HB_TAG('C','P','P',' ')}, /* Grenadian Creole English -> Creoles */ {HB_TAG('g','c','r',' '), HB_TAG('C','P','P',' ')}, /* Guianese Creole French -> Creoles */ {HB_TAG('g','d','a',' '), HB_TAG('R','A','J',' ')}, /* Gade Lohar -> Rajasthani */ -/*{HB_TAG('g','e','z',' '), HB_TAG('G','E','Z',' ')},*/ /* Geez */ {HB_TAG('g','g','o',' '), HB_TAG('G','O','N',' ')}, /* Southern Gondi (retired code) -> Gondi */ {HB_TAG('g','h','a',' '), HB_TAG('B','B','R',' ')}, /* Ghadamès -> Berber */ {HB_TAG('g','h','c',' '), HB_TAG('I','R','T',' ')}, /* Hiberno-Scottish Gaelic -> Irish Traditional */ {HB_TAG('g','h','k',' '), HB_TAG('K','R','N',' ')}, /* Geko Karen -> Karen */ {HB_TAG('g','h','o',' '), HB_TAG('B','B','R',' ')}, /* Ghomara -> Berber */ {HB_TAG('g','i','b',' '), HB_TAG('C','P','P',' ')}, /* Gibanawa -> Creoles */ -/*{HB_TAG('g','i','h',' '), HB_TAG('G','I','H',' ')},*/ /* Githabul */ {HB_TAG('g','i','l',' '), HB_TAG('G','I','L','0')}, /* Kiribati (Gilbertese) */ {HB_TAG('g','j','u',' '), HB_TAG('R','A','J',' ')}, /* Gujari -> Rajasthani */ - {HB_TAG('g','k','p',' '), HB_TAG('G','K','P',' ')}, /* Guinea Kpelle -> Kpelle (Guinea) */ - {HB_TAG('g','k','p',' '), HB_TAG('K','P','L',' ')}, /* Guinea Kpelle -> Kpelle */ {HB_TAG('g','l','d',' '), HB_TAG('N','A','N',' ')}, /* Nanai */ -/*{HB_TAG('g','l','k',' '), HB_TAG('G','L','K',' ')},*/ /* Gilaki */ - {HB_TAG('g','m','z',' '), HB_TAG_NONE }, /* Mgbolizhia != Gumuz */ {HB_TAG('g','n','b',' '), HB_TAG('Q','I','N',' ')}, /* Gangte -> Chin */ -/*{HB_TAG('g','n','n',' '), HB_TAG('G','N','N',' ')},*/ /* Gumatj */ {HB_TAG('g','n','o',' '), HB_TAG('G','O','N',' ')}, /* Northern Gondi -> Gondi */ {HB_TAG('g','n','w',' '), HB_TAG('G','U','A',' ')}, /* Western Bolivian Guaraní -> Guarani */ -/*{HB_TAG('g','o','g',' '), HB_TAG('G','O','G',' ')},*/ /* Gogo */ {HB_TAG('g','o','m',' '), HB_TAG('K','O','K',' ')}, /* Goan Konkani -> Konkani */ -/*{HB_TAG('g','o','n',' '), HB_TAG('G','O','N',' ')},*/ /* Gondi [macrolanguage] */ {HB_TAG('g','o','q',' '), HB_TAG('C','P','P',' ')}, /* Gorap -> Creoles */ {HB_TAG('g','o','x',' '), HB_TAG('B','A','D','0')}, /* Gobu -> Banda */ {HB_TAG('g','p','e',' '), HB_TAG('C','P','P',' ')}, /* Ghanaian Pidgin English -> Creoles */ - {HB_TAG('g','r','o',' '), HB_TAG_NONE }, /* Groma != Garo */ {HB_TAG('g','r','r',' '), HB_TAG('B','B','R',' ')}, /* Taznatit -> Berber */ {HB_TAG('g','r','t',' '), HB_TAG('G','R','O',' ')}, /* Garo */ {HB_TAG('g','r','u',' '), HB_TAG('S','O','G',' ')}, /* Kistane -> Sodo Gurage */ {HB_TAG('g','s','w',' '), HB_TAG('A','L','S',' ')}, /* Alsatian */ - {HB_TAG('g','u','a',' '), HB_TAG_NONE }, /* Shiki != Guarani */ -/*{HB_TAG('g','u','c',' '), HB_TAG('G','U','C',' ')},*/ /* Wayuu */ -/*{HB_TAG('g','u','f',' '), HB_TAG('G','U','F',' ')},*/ /* Gupapuyngu */ {HB_TAG('g','u','g',' '), HB_TAG('G','U','A',' ')}, /* Paraguayan Guaraní -> Guarani */ {HB_TAG('g','u','i',' '), HB_TAG('G','U','A',' ')}, /* Eastern Bolivian Guaraní -> Guarani */ {HB_TAG('g','u','k',' '), HB_TAG('G','M','Z',' ')}, /* Gumuz */ {HB_TAG('g','u','l',' '), HB_TAG('C','P','P',' ')}, /* Sea Island Creole English -> Creoles */ {HB_TAG('g','u','n',' '), HB_TAG('G','U','A',' ')}, /* Mbyá Guaraní -> Guarani */ -/*{HB_TAG('g','u','z',' '), HB_TAG('G','U','Z',' ')},*/ /* Gusii */ {HB_TAG('g','w','i',' '), HB_TAG('A','T','H',' ')}, /* Gwichʼin -> Athapaskan */ {HB_TAG('g','y','n',' '), HB_TAG('C','P','P',' ')}, /* Guyanese Creole English -> Creoles */ {HB_TAG('h','a','a',' '), HB_TAG('A','T','H',' ')}, /* Hän -> Athapaskan */ {HB_TAG('h','a','e',' '), HB_TAG('O','R','O',' ')}, /* Eastern Oromo -> Oromo */ {HB_TAG('h','a','i',' '), HB_TAG('H','A','I','0')}, /* Haida [macrolanguage] */ {HB_TAG('h','a','k',' '), HB_TAG('Z','H','S',' ')}, /* Hakka Chinese -> Chinese, Simplified */ - {HB_TAG('h','a','l',' '), HB_TAG_NONE }, /* Halang != Halam (Falam Chin) */ {HB_TAG('h','a','r',' '), HB_TAG('H','R','I',' ')}, /* Harari */ -/*{HB_TAG('h','a','w',' '), HB_TAG('H','A','W',' ')},*/ /* Hawaiian */ {HB_TAG('h','a','x',' '), HB_TAG('H','A','I','0')}, /* Southern Haida -> Haida */ -/*{HB_TAG('h','a','y',' '), HB_TAG('H','A','Y',' ')},*/ /* Haya */ -/*{HB_TAG('h','a','z',' '), HB_TAG('H','A','Z',' ')},*/ /* Hazaragi */ - {HB_TAG('h','b','n',' '), HB_TAG_NONE }, /* Heiban != Hammer-Banna */ {HB_TAG('h','c','a',' '), HB_TAG('C','P','P',' ')}, /* Andaman Creole Hindi -> Creoles */ {HB_TAG('h','d','n',' '), HB_TAG('H','A','I','0')}, /* Northern Haida -> Haida */ {HB_TAG('h','e','a',' '), HB_TAG('H','M','N',' ')}, /* Northern Qiandong Miao -> Hmong */ -/*{HB_TAG('h','e','i',' '), HB_TAG('H','E','I',' ')},*/ /* Heiltsuk */ -/*{HB_TAG('h','i','l',' '), HB_TAG('H','I','L',' ')},*/ /* Hiligaynon */ {HB_TAG('h','j','i',' '), HB_TAG('M','L','Y',' ')}, /* Haji -> Malay */ {HB_TAG('h','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Matu Chin -> Chin */ {HB_TAG('h','m','a',' '), HB_TAG('H','M','N',' ')}, /* Southern Mashan Hmong -> Hmong */ {HB_TAG('h','m','c',' '), HB_TAG('H','M','N',' ')}, /* Central Huishui Hmong -> Hmong */ - {HB_TAG('h','m','d',' '), HB_TAG('H','M','D',' ')}, /* Large Flowery Miao -> A-Hmao */ - {HB_TAG('h','m','d',' '), HB_TAG('H','M','N',' ')}, /* Large Flowery Miao -> Hmong */ {HB_TAG('h','m','e',' '), HB_TAG('H','M','N',' ')}, /* Eastern Huishui Hmong -> Hmong */ {HB_TAG('h','m','g',' '), HB_TAG('H','M','N',' ')}, /* Southwestern Guiyang Hmong -> Hmong */ {HB_TAG('h','m','h',' '), HB_TAG('H','M','N',' ')}, /* Southwestern Huishui Hmong -> Hmong */ @@ -733,36 +690,27 @@ static const LangTag ot_languages3[] = { {HB_TAG('h','m','j',' '), HB_TAG('H','M','N',' ')}, /* Ge -> Hmong */ {HB_TAG('h','m','l',' '), HB_TAG('H','M','N',' ')}, /* Luopohe Hmong -> Hmong */ {HB_TAG('h','m','m',' '), HB_TAG('H','M','N',' ')}, /* Central Mashan Hmong -> Hmong */ -/*{HB_TAG('h','m','n',' '), HB_TAG('H','M','N',' ')},*/ /* Hmong [macrolanguage] */ {HB_TAG('h','m','p',' '), HB_TAG('H','M','N',' ')}, /* Northern Mashan Hmong -> Hmong */ {HB_TAG('h','m','q',' '), HB_TAG('H','M','N',' ')}, /* Eastern Qiandong Miao -> Hmong */ {HB_TAG('h','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Hmar -> Chin */ {HB_TAG('h','m','s',' '), HB_TAG('H','M','N',' ')}, /* Southern Qiandong Miao -> Hmong */ {HB_TAG('h','m','w',' '), HB_TAG('H','M','N',' ')}, /* Western Mashan Hmong -> Hmong */ {HB_TAG('h','m','y',' '), HB_TAG('H','M','N',' ')}, /* Southern Guiyang Hmong -> Hmong */ - {HB_TAG('h','m','z',' '), HB_TAG('H','M','Z',' ')}, /* Hmong Shua -> Hmong Shuat */ - {HB_TAG('h','m','z',' '), HB_TAG('H','M','N',' ')}, /* Hmong Shua -> Hmong */ -/*{HB_TAG('h','n','d',' '), HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */ {HB_TAG('h','n','e',' '), HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */ {HB_TAG('h','n','j',' '), HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */ {HB_TAG('h','n','m',' '), HB_TAG('Z','H','S',' ')}, /* Hainanese -> Chinese, Simplified */ {HB_TAG('h','n','o',' '), HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */ {HB_TAG('h','o','c',' '), HB_TAG('H','O',' ',' ')}, /* Ho */ {HB_TAG('h','o','i',' '), HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */ - {HB_TAG('h','o','j',' '), HB_TAG('H','A','R',' ')}, /* Hadothi -> Harauti */ - {HB_TAG('h','o','j',' '), HB_TAG('R','A','J',' ')}, /* Hadothi -> Rajasthani */ {HB_TAG('h','r','a',' '), HB_TAG('Q','I','N',' ')}, /* Hrangkhol -> Chin */ {HB_TAG('h','r','m',' '), HB_TAG('H','M','N',' ')}, /* Horned Miao -> Hmong */ {HB_TAG('h','s','b',' '), HB_TAG('U','S','B',' ')}, /* Upper Sorbian */ {HB_TAG('h','s','n',' '), HB_TAG('Z','H','S',' ')}, /* Xiang Chinese -> Chinese, Simplified */ {HB_TAG('h','u','j',' '), HB_TAG('H','M','N',' ')}, /* Northern Guiyang Hmong -> Hmong */ {HB_TAG('h','u','p',' '), HB_TAG('A','T','H',' ')}, /* Hupa -> Athapaskan */ -/*{HB_TAG('h','u','r',' '), HB_TAG('H','U','R',' ')},*/ /* Halkomelem */ {HB_TAG('h','u','s',' '), HB_TAG('M','Y','N',' ')}, /* Huastec -> Mayan */ {HB_TAG('h','w','c',' '), HB_TAG('C','P','P',' ')}, /* Hawai'i Creole English -> Creoles */ {HB_TAG('h','y','w',' '), HB_TAG('H','Y','E',' ')}, /* Western Armenian -> Armenian */ -/*{HB_TAG('i','b','a',' '), HB_TAG('I','B','A',' ')},*/ /* Iban */ -/*{HB_TAG('i','b','b',' '), HB_TAG('I','B','B',' ')},*/ /* Ibibio */ {HB_TAG('i','b','y',' '), HB_TAG('I','J','O',' ')}, /* Ibani -> Ijo */ {HB_TAG('i','c','r',' '), HB_TAG('C','P','P',' ')}, /* Islander Creole English -> Creoles */ {HB_TAG('i','d','a',' '), HB_TAG('L','U','H',' ')}, /* Idakho-Isukha-Tiriki -> Luyia */ @@ -772,156 +720,71 @@ static const LangTag ot_languages3[] = { {HB_TAG('i','j','c',' '), HB_TAG('I','J','O',' ')}, /* Izon -> Ijo */ {HB_TAG('i','j','e',' '), HB_TAG('I','J','O',' ')}, /* Biseni -> Ijo */ {HB_TAG('i','j','n',' '), HB_TAG('I','J','O',' ')}, /* Kalabari -> Ijo */ -/*{HB_TAG('i','j','o',' '), HB_TAG('I','J','O',' ')},*/ /* Ijo [collection] */ {HB_TAG('i','j','s',' '), HB_TAG('I','J','O',' ')}, /* Southeast Ijo -> Ijo */ - {HB_TAG('i','k','e',' '), HB_TAG('I','N','U',' ')}, /* Eastern Canadian Inuktitut -> Inuktitut */ - {HB_TAG('i','k','e',' '), HB_TAG('I','N','U','K')}, /* Eastern Canadian Inuktitut -> Nunavik Inuktitut */ {HB_TAG('i','k','t',' '), HB_TAG('I','N','U',' ')}, /* Inuinnaqtun -> Inuktitut */ -/*{HB_TAG('i','l','o',' '), HB_TAG('I','L','O',' ')},*/ /* Iloko -> Ilokano */ {HB_TAG('i','n','g',' '), HB_TAG('A','T','H',' ')}, /* Degexit'an -> Athapaskan */ {HB_TAG('i','n','h',' '), HB_TAG('I','N','G',' ')}, /* Ingush */ - {HB_TAG('i','r','i',' '), HB_TAG_NONE }, /* Rigwe != Irish */ -/*{HB_TAG('i','r','u',' '), HB_TAG('I','R','U',' ')},*/ /* Irula */ - {HB_TAG('i','s','m',' '), HB_TAG_NONE }, /* Masimasi != Inari Sami */ {HB_TAG('i','t','z',' '), HB_TAG('M','Y','N',' ')}, /* Itzá -> Mayan */ {HB_TAG('i','x','l',' '), HB_TAG('M','Y','N',' ')}, /* Ixil -> Mayan */ {HB_TAG('j','a','c',' '), HB_TAG('M','Y','N',' ')}, /* Popti' -> Mayan */ {HB_TAG('j','a','k',' '), HB_TAG('M','L','Y',' ')}, /* Jakun -> Malay */ - {HB_TAG('j','a','m',' '), HB_TAG('J','A','M',' ')}, /* Jamaican Creole English -> Jamaican Creole */ - {HB_TAG('j','a','m',' '), HB_TAG('C','P','P',' ')}, /* Jamaican Creole English -> Creoles */ - {HB_TAG('j','a','n',' '), HB_TAG_NONE }, /* Jandai != Japanese */ {HB_TAG('j','a','x',' '), HB_TAG('M','L','Y',' ')}, /* Jambi Malay -> Malay */ {HB_TAG('j','b','e',' '), HB_TAG('B','B','R',' ')}, /* Judeo-Berber -> Berber */ {HB_TAG('j','b','n',' '), HB_TAG('B','B','R',' ')}, /* Nafusi -> Berber */ -/*{HB_TAG('j','b','o',' '), HB_TAG('J','B','O',' ')},*/ /* Lojban */ -/*{HB_TAG('j','c','t',' '), HB_TAG('J','C','T',' ')},*/ /* Krymchak */ -/*{HB_TAG('j','d','t',' '), HB_TAG('J','D','T',' ')},*/ /* Judeo-Tat */ {HB_TAG('j','g','o',' '), HB_TAG('B','M','L',' ')}, /* Ngomba -> Bamileke */ - {HB_TAG('j','i','i',' '), HB_TAG_NONE }, /* Jiiddu != Yiddish */ {HB_TAG('j','k','m',' '), HB_TAG('K','R','N',' ')}, /* Mobwa Karen -> Karen */ {HB_TAG('j','k','p',' '), HB_TAG('K','R','N',' ')}, /* Paku Karen -> Karen */ - {HB_TAG('j','u','d',' '), HB_TAG_NONE }, /* Worodougou != Ladino */ - {HB_TAG('j','u','l',' '), HB_TAG_NONE }, /* Jirel != Jula */ {HB_TAG('j','v','d',' '), HB_TAG('C','P','P',' ')}, /* Javindo -> Creoles */ {HB_TAG('k','a','a',' '), HB_TAG('K','R','K',' ')}, /* Karakalpak */ - {HB_TAG('k','a','b',' '), HB_TAG('K','A','B','0')}, /* Kabyle */ - {HB_TAG('k','a','b',' '), HB_TAG('B','B','R',' ')}, /* Kabyle -> Berber */ - {HB_TAG('k','a','c',' '), HB_TAG_NONE }, /* Kachin != Kachchi */ {HB_TAG('k','a','m',' '), HB_TAG('K','M','B',' ')}, /* Kamba (Kenya) */ {HB_TAG('k','a','r',' '), HB_TAG('K','R','N',' ')}, /* Karen [collection] */ -/*{HB_TAG('k','a','w',' '), HB_TAG('K','A','W',' ')},*/ /* Kawi (Old Javanese) */ -/*{HB_TAG('k','b','c',' '), HB_TAG('K','B','C',' ')},*/ /* Kadiwéu */ {HB_TAG('k','b','d',' '), HB_TAG('K','A','B',' ')}, /* Kabardian */ {HB_TAG('k','b','y',' '), HB_TAG('K','N','R',' ')}, /* Manga Kanuri -> Kanuri */ - {HB_TAG('k','c','a',' '), HB_TAG('K','H','K',' ')}, /* Khanty -> Khanty-Kazim */ - {HB_TAG('k','c','a',' '), HB_TAG('K','H','S',' ')}, /* Khanty -> Khanty-Shurishkar */ - {HB_TAG('k','c','a',' '), HB_TAG('K','H','V',' ')}, /* Khanty -> Khanty-Vakhi */ {HB_TAG('k','c','n',' '), HB_TAG('C','P','P',' ')}, /* Nubi -> Creoles */ -/*{HB_TAG('k','d','e',' '), HB_TAG('K','D','E',' ')},*/ /* Makonde */ {HB_TAG('k','d','r',' '), HB_TAG('K','R','M',' ')}, /* Karaim */ {HB_TAG('k','d','t',' '), HB_TAG('K','U','Y',' ')}, /* Kuy */ - {HB_TAG('k','e','a',' '), HB_TAG('K','E','A',' ')}, /* Kabuverdianu (Crioulo) */ - {HB_TAG('k','e','a',' '), HB_TAG('C','P','P',' ')}, /* Kabuverdianu -> Creoles */ - {HB_TAG('k','e','b',' '), HB_TAG_NONE }, /* Kélé != Kebena */ - {HB_TAG('k','e','k',' '), HB_TAG('K','E','K',' ')}, /* Kekchi */ - {HB_TAG('k','e','k',' '), HB_TAG('M','Y','N',' ')}, /* Kekchí -> Mayan */ {HB_TAG('k','e','x',' '), HB_TAG('K','K','N',' ')}, /* Kukna -> Kokni */ {HB_TAG('k','f','a',' '), HB_TAG('K','O','D',' ')}, /* Kodava -> Kodagu */ {HB_TAG('k','f','r',' '), HB_TAG('K','A','C',' ')}, /* Kachhi -> Kachchi */ {HB_TAG('k','f','x',' '), HB_TAG('K','U','L',' ')}, /* Kullu Pahari -> Kulvi */ {HB_TAG('k','f','y',' '), HB_TAG('K','M','N',' ')}, /* Kumaoni */ - {HB_TAG('k','g','e',' '), HB_TAG_NONE }, /* Komering != Khutsuri Georgian */ -/*{HB_TAG('k','g','f',' '), HB_TAG('K','G','F',' ')},*/ /* Kube */ {HB_TAG('k','h','a',' '), HB_TAG('K','S','I',' ')}, /* Khasi */ {HB_TAG('k','h','b',' '), HB_TAG('X','B','D',' ')}, /* Lü */ {HB_TAG('k','h','k',' '), HB_TAG('M','N','G',' ')}, /* Halh Mongolian -> Mongolian */ - {HB_TAG('k','h','n',' '), HB_TAG_NONE }, /* Khandesi != Khamti Shan (Microsoft fonts) */ - {HB_TAG('k','h','s',' '), HB_TAG_NONE }, /* Kasua != Khanty-Shurishkar */ - {HB_TAG('k','h','t',' '), HB_TAG('K','H','T',' ')}, /* Khamti -> Khamti Shan */ - {HB_TAG('k','h','t',' '), HB_TAG('K','H','N',' ')}, /* Khamti -> Khamti Shan (Microsoft fonts) */ - {HB_TAG('k','h','v',' '), HB_TAG_NONE }, /* Khvarshi != Khanty-Vakhi */ -/*{HB_TAG('k','h','w',' '), HB_TAG('K','H','W',' ')},*/ /* Khowar */ - {HB_TAG('k','i','s',' '), HB_TAG_NONE }, /* Kis != Kisii */ - {HB_TAG('k','i','u',' '), HB_TAG('K','I','U',' ')}, /* Kirmanjki */ - {HB_TAG('k','i','u',' '), HB_TAG('Z','Z','A',' ')}, /* Kirmanjki -> Zazaki */ {HB_TAG('k','j','b',' '), HB_TAG('M','Y','N',' ')}, /* Q'anjob'al -> Mayan */ -/*{HB_TAG('k','j','d',' '), HB_TAG('K','J','D',' ')},*/ /* Southern Kiwai */ {HB_TAG('k','j','h',' '), HB_TAG('K','H','A',' ')}, /* Khakas -> Khakass */ -/*{HB_TAG('k','j','j',' '), HB_TAG('K','J','J',' ')},*/ /* Khinalugh -> Khinalug */ - {HB_TAG('k','j','p',' '), HB_TAG('K','J','P',' ')}, /* Pwo Eastern Karen -> Eastern Pwo Karen */ - {HB_TAG('k','j','p',' '), HB_TAG('K','R','N',' ')}, /* Pwo Eastern Karen -> Karen */ {HB_TAG('k','j','t',' '), HB_TAG('K','R','N',' ')}, /* Phrae Pwo Karen -> Karen */ -/*{HB_TAG('k','j','z',' '), HB_TAG('K','J','Z',' ')},*/ /* Bumthangkha */ - {HB_TAG('k','k','n',' '), HB_TAG_NONE }, /* Kon Keu != Kokni */ {HB_TAG('k','k','z',' '), HB_TAG('A','T','H',' ')}, /* Kaska -> Athapaskan */ - {HB_TAG('k','l','m',' '), HB_TAG_NONE }, /* Migum != Kalmyk */ {HB_TAG('k','l','n',' '), HB_TAG('K','A','L',' ')}, /* Kalenjin [macrolanguage] */ {HB_TAG('k','m','b',' '), HB_TAG('M','B','N',' ')}, /* Kimbundu -> Mbundu */ -/*{HB_TAG('k','m','g',' '), HB_TAG('K','M','G',' ')},*/ /* Kâte */ - {HB_TAG('k','m','n',' '), HB_TAG_NONE }, /* Awtuw != Kumaoni */ - {HB_TAG('k','m','o',' '), HB_TAG_NONE }, /* Kwoma != Komo */ {HB_TAG('k','m','r',' '), HB_TAG('K','U','R',' ')}, /* Northern Kurdish -> Kurdish */ - {HB_TAG('k','m','s',' '), HB_TAG_NONE }, /* Kamasau != Komso */ {HB_TAG('k','m','v',' '), HB_TAG('C','P','P',' ')}, /* Karipúna Creole French -> Creoles */ {HB_TAG('k','m','w',' '), HB_TAG('K','M','O',' ')}, /* Komo (Democratic Republic of Congo) */ -/*{HB_TAG('k','m','z',' '), HB_TAG('K','M','Z',' ')},*/ /* Khorasani Turkish -> Khorasani Turkic */ {HB_TAG('k','n','c',' '), HB_TAG('K','N','R',' ')}, /* Central Kanuri -> Kanuri */ {HB_TAG('k','n','g',' '), HB_TAG('K','O','N','0')}, /* Koongo -> Kongo */ {HB_TAG('k','n','j',' '), HB_TAG('M','Y','N',' ')}, /* Western Kanjobal -> Mayan */ {HB_TAG('k','n','n',' '), HB_TAG('K','O','K',' ')}, /* Konkani */ - {HB_TAG('k','n','r',' '), HB_TAG_NONE }, /* Kaningra != Kanuri */ - {HB_TAG('k','o','d',' '), HB_TAG_NONE }, /* Kodi != Kodagu */ - {HB_TAG('k','o','h',' '), HB_TAG_NONE }, /* Koyo != Korean Old Hangul */ - {HB_TAG('k','o','i',' '), HB_TAG('K','O','P',' ')}, /* Komi-Permyak */ - {HB_TAG('k','o','i',' '), HB_TAG('K','O','M',' ')}, /* Komi-Permyak -> Komi */ -/*{HB_TAG('k','o','k',' '), HB_TAG('K','O','K',' ')},*/ /* Konkani [macrolanguage] */ - {HB_TAG('k','o','p',' '), HB_TAG_NONE }, /* Waube != Komi-Permyak */ -/*{HB_TAG('k','o','s',' '), HB_TAG('K','O','S',' ')},*/ /* Kosraean */ {HB_TAG('k','o','y',' '), HB_TAG('A','T','H',' ')}, /* Koyukon -> Athapaskan */ - {HB_TAG('k','o','z',' '), HB_TAG_NONE }, /* Korak != Komi-Zyrian */ {HB_TAG('k','p','e',' '), HB_TAG('K','P','L',' ')}, /* Kpelle [macrolanguage] */ - {HB_TAG('k','p','l',' '), HB_TAG_NONE }, /* Kpala != Kpelle */ {HB_TAG('k','p','p',' '), HB_TAG('K','R','N',' ')}, /* Paku Karen (retired code) -> Karen */ - {HB_TAG('k','p','v',' '), HB_TAG('K','O','Z',' ')}, /* Komi-Zyrian */ - {HB_TAG('k','p','v',' '), HB_TAG('K','O','M',' ')}, /* Komi-Zyrian -> Komi */ {HB_TAG('k','p','y',' '), HB_TAG('K','Y','K',' ')}, /* Koryak */ {HB_TAG('k','q','s',' '), HB_TAG('K','I','S',' ')}, /* Northern Kissi -> Kisii */ {HB_TAG('k','q','y',' '), HB_TAG('K','R','T',' ')}, /* Koorete */ - {HB_TAG('k','r','c',' '), HB_TAG('K','A','R',' ')}, /* Karachay-Balkar -> Karachay */ - {HB_TAG('k','r','c',' '), HB_TAG('B','A','L',' ')}, /* Karachay-Balkar -> Balkar */ - {HB_TAG('k','r','i',' '), HB_TAG('K','R','I',' ')}, /* Krio */ - {HB_TAG('k','r','i',' '), HB_TAG('C','P','P',' ')}, /* Krio -> Creoles */ - {HB_TAG('k','r','k',' '), HB_TAG_NONE }, /* Kerek != Karakalpak */ -/*{HB_TAG('k','r','l',' '), HB_TAG('K','R','L',' ')},*/ /* Karelian */ - {HB_TAG('k','r','m',' '), HB_TAG_NONE }, /* Krim (retired code) != Karaim */ - {HB_TAG('k','r','n',' '), HB_TAG_NONE }, /* Sapo != Karen */ {HB_TAG('k','r','t',' '), HB_TAG('K','N','R',' ')}, /* Tumari Kanuri -> Kanuri */ {HB_TAG('k','r','u',' '), HB_TAG('K','U','U',' ')}, /* Kurukh */ {HB_TAG('k','s','h',' '), HB_TAG('K','S','H','0')}, /* Kölsch -> Ripuarian */ - {HB_TAG('k','s','i',' '), HB_TAG_NONE }, /* Krisa != Khasi */ - {HB_TAG('k','s','m',' '), HB_TAG_NONE }, /* Kumba != Kildin Sami */ {HB_TAG('k','s','s',' '), HB_TAG('K','I','S',' ')}, /* Southern Kisi -> Kisii */ -/*{HB_TAG('k','s','u',' '), HB_TAG('K','S','U',' ')},*/ /* Khamyang */ - {HB_TAG('k','s','w',' '), HB_TAG('K','S','W',' ')}, /* S’gaw Karen */ - {HB_TAG('k','s','w',' '), HB_TAG('K','R','N',' ')}, /* S'gaw Karen -> Karen */ {HB_TAG('k','t','b',' '), HB_TAG('K','E','B',' ')}, /* Kambaata -> Kebena */ {HB_TAG('k','t','u',' '), HB_TAG('K','O','N',' ')}, /* Kituba (Democratic Republic of Congo) -> Kikongo */ {HB_TAG('k','t','w',' '), HB_TAG('A','T','H',' ')}, /* Kato -> Athapaskan */ - {HB_TAG('k','u','i',' '), HB_TAG_NONE }, /* Kuikúro-Kalapálo != Kui */ - {HB_TAG('k','u','l',' '), HB_TAG_NONE }, /* Kulere != Kulvi */ -/*{HB_TAG('k','u','m',' '), HB_TAG('K','U','M',' ')},*/ /* Kumyk */ {HB_TAG('k','u','u',' '), HB_TAG('A','T','H',' ')}, /* Upper Kuskokwim -> Athapaskan */ {HB_TAG('k','u','w',' '), HB_TAG('B','A','D','0')}, /* Kpagua -> Banda */ - {HB_TAG('k','u','y',' '), HB_TAG_NONE }, /* Kuuku-Ya'u != Kuy */ {HB_TAG('k','v','b',' '), HB_TAG('M','L','Y',' ')}, /* Kubu -> Malay */ {HB_TAG('k','v','l',' '), HB_TAG('K','R','N',' ')}, /* Kayaw -> Karen */ - {HB_TAG('k','v','q',' '), HB_TAG('K','V','Q',' ')}, /* Geba Karen */ - {HB_TAG('k','v','q',' '), HB_TAG('K','R','N',' ')}, /* Geba Karen -> Karen */ {HB_TAG('k','v','r',' '), HB_TAG('M','L','Y',' ')}, /* Kerinci -> Malay */ {HB_TAG('k','v','t',' '), HB_TAG('K','R','N',' ')}, /* Lahta Karen -> Karen */ {HB_TAG('k','v','u',' '), HB_TAG('K','R','N',' ')}, /* Yinbaw Karen -> Karen */ {HB_TAG('k','v','y',' '), HB_TAG('K','R','N',' ')}, /* Yintale Karen -> Karen */ -/*{HB_TAG('k','w','k',' '), HB_TAG('K','W','K',' ')},*/ /* Kwakʼwala */ {HB_TAG('k','w','w',' '), HB_TAG('C','P','P',' ')}, /* Kwinti -> Creoles */ {HB_TAG('k','w','y',' '), HB_TAG('K','O','N','0')}, /* San Salvador Kongo -> Kongo */ {HB_TAG('k','x','c',' '), HB_TAG('K','M','S',' ')}, /* Konso -> Komso */ @@ -930,195 +793,98 @@ static const LangTag ot_languages3[] = { {HB_TAG('k','x','k',' '), HB_TAG('K','R','N',' ')}, /* Zayein Karen -> Karen */ {HB_TAG('k','x','l',' '), HB_TAG('K','U','U',' ')}, /* Nepali Kurux (retired code) -> Kurukh */ {HB_TAG('k','x','u',' '), HB_TAG('K','U','I',' ')}, /* Kui (India) (retired code) */ - {HB_TAG('k','y','k',' '), HB_TAG_NONE }, /* Kamayo != Koryak */ - {HB_TAG('k','y','u',' '), HB_TAG('K','Y','U',' ')}, /* Western Kayah */ - {HB_TAG('k','y','u',' '), HB_TAG('K','R','N',' ')}, /* Western Kayah -> Karen */ {HB_TAG('l','a','c',' '), HB_TAG('M','Y','N',' ')}, /* Lacandon -> Mayan */ {HB_TAG('l','a','d',' '), HB_TAG('J','U','D',' ')}, /* Ladino */ - {HB_TAG('l','a','h',' '), HB_TAG_NONE }, /* Lahnda [macrolanguage] != Lahuli */ - {HB_TAG('l','a','k',' '), HB_TAG_NONE }, /* Laka (Nigeria) (retired code) != Lak */ - {HB_TAG('l','a','m',' '), HB_TAG_NONE }, /* Lamba != Lambani */ - {HB_TAG('l','a','z',' '), HB_TAG_NONE }, /* Aribwatsa != Laz */ {HB_TAG('l','b','e',' '), HB_TAG('L','A','K',' ')}, /* Lak */ {HB_TAG('l','b','j',' '), HB_TAG('L','D','K',' ')}, /* Ladakhi */ {HB_TAG('l','b','l',' '), HB_TAG('B','I','K',' ')}, /* Libon Bikol -> Bikol */ {HB_TAG('l','c','e',' '), HB_TAG('M','L','Y',' ')}, /* Loncong -> Malay */ {HB_TAG('l','c','f',' '), HB_TAG('M','L','Y',' ')}, /* Lubu -> Malay */ {HB_TAG('l','d','i',' '), HB_TAG('K','O','N','0')}, /* Laari -> Kongo */ - {HB_TAG('l','d','k',' '), HB_TAG_NONE }, /* Leelau != Ladakhi */ -/*{HB_TAG('l','e','f',' '), HB_TAG('L','E','F',' ')},*/ /* Lelemi */ -/*{HB_TAG('l','e','z',' '), HB_TAG('L','E','Z',' ')},*/ /* Lezghian -> Lezgi */ {HB_TAG('l','i','f',' '), HB_TAG('L','M','B',' ')}, /* Limbu */ -/*{HB_TAG('l','i','j',' '), HB_TAG('L','I','J',' ')},*/ /* Ligurian */ {HB_TAG('l','i','r',' '), HB_TAG('C','P','P',' ')}, /* Liberian English -> Creoles */ -/*{HB_TAG('l','i','s',' '), HB_TAG('L','I','S',' ')},*/ /* Lisu */ -/*{HB_TAG('l','i','v',' '), HB_TAG('L','I','V',' ')},*/ /* Liv */ {HB_TAG('l','i','w',' '), HB_TAG('M','L','Y',' ')}, /* Col -> Malay */ {HB_TAG('l','i','y',' '), HB_TAG('B','A','D','0')}, /* Banda-Bambari -> Banda */ -/*{HB_TAG('l','j','p',' '), HB_TAG('L','J','P',' ')},*/ /* Lampung Api -> Lampung */ {HB_TAG('l','k','b',' '), HB_TAG('L','U','H',' ')}, /* Kabras -> Luyia */ -/*{HB_TAG('l','k','i',' '), HB_TAG('L','K','I',' ')},*/ /* Laki */ {HB_TAG('l','k','o',' '), HB_TAG('L','U','H',' ')}, /* Khayo -> Luyia */ {HB_TAG('l','k','s',' '), HB_TAG('L','U','H',' ')}, /* Kisa -> Luyia */ {HB_TAG('l','l','d',' '), HB_TAG('L','A','D',' ')}, /* Ladin */ - {HB_TAG('l','m','a',' '), HB_TAG_NONE }, /* East Limba != Low Mari */ - {HB_TAG('l','m','b',' '), HB_TAG_NONE }, /* Merei != Limbu */ {HB_TAG('l','m','n',' '), HB_TAG('L','A','M',' ')}, /* Lambadi -> Lambani */ -/*{HB_TAG('l','m','o',' '), HB_TAG('L','M','O',' ')},*/ /* Lombard */ - {HB_TAG('l','m','w',' '), HB_TAG_NONE }, /* Lake Miwok != Lomwe */ {HB_TAG('l','n','a',' '), HB_TAG('B','A','D','0')}, /* Langbashe -> Banda */ {HB_TAG('l','n','l',' '), HB_TAG('B','A','D','0')}, /* South Central Banda -> Banda */ -/*{HB_TAG('l','o','m',' '), HB_TAG('L','O','M',' ')},*/ /* Loma (Liberia) */ {HB_TAG('l','o','u',' '), HB_TAG('C','P','P',' ')}, /* Louisiana Creole -> Creoles */ -/*{HB_TAG('l','p','o',' '), HB_TAG('L','P','O',' ')},*/ /* Lipo */ -/*{HB_TAG('l','r','c',' '), HB_TAG('L','R','C',' ')},*/ /* Northern Luri -> Luri */ {HB_TAG('l','r','i',' '), HB_TAG('L','U','H',' ')}, /* Marachi -> Luyia */ {HB_TAG('l','r','m',' '), HB_TAG('L','U','H',' ')}, /* Marama -> Luyia */ {HB_TAG('l','r','t',' '), HB_TAG('C','P','P',' ')}, /* Larantuka Malay -> Creoles */ - {HB_TAG('l','s','b',' '), HB_TAG_NONE }, /* Burundian Sign Language != Lower Sorbian */ {HB_TAG('l','s','m',' '), HB_TAG('L','U','H',' ')}, /* Saamia -> Luyia */ {HB_TAG('l','t','g',' '), HB_TAG('L','V','I',' ')}, /* Latgalian -> Latvian */ - {HB_TAG('l','t','h',' '), HB_TAG_NONE }, /* Thur != Lithuanian */ {HB_TAG('l','t','o',' '), HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */ {HB_TAG('l','t','s',' '), HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */ -/*{HB_TAG('l','u','a',' '), HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */ {HB_TAG('l','u','h',' '), HB_TAG('Z','H','S',' ')}, /* Leizhou Chinese -> Chinese, Simplified */ -/*{HB_TAG('l','u','o',' '), HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */ - {HB_TAG('l','u','s',' '), HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */ - {HB_TAG('l','u','s',' '), HB_TAG('Q','I','N',' ')}, /* Lushai -> Chin */ -/*{HB_TAG('l','u','t',' '), HB_TAG('L','U','T',' ')},*/ /* Lushootseed */ {HB_TAG('l','u','y',' '), HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */ {HB_TAG('l','u','z',' '), HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */ - {HB_TAG('l','v','i',' '), HB_TAG_NONE }, /* Lavi != Latvian */ {HB_TAG('l','v','s',' '), HB_TAG('L','V','I',' ')}, /* Standard Latvian -> Latvian */ {HB_TAG('l','w','g',' '), HB_TAG('L','U','H',' ')}, /* Wanga -> Luyia */ {HB_TAG('l','z','h',' '), HB_TAG('Z','H','T',' ')}, /* Literary Chinese -> Chinese, Traditional */ {HB_TAG('l','z','z',' '), HB_TAG('L','A','Z',' ')}, /* Laz */ -/*{HB_TAG('m','a','d',' '), HB_TAG('M','A','D',' ')},*/ /* Madurese -> Madura */ -/*{HB_TAG('m','a','g',' '), HB_TAG('M','A','G',' ')},*/ /* Magahi */ {HB_TAG('m','a','i',' '), HB_TAG('M','T','H',' ')}, /* Maithili */ - {HB_TAG('m','a','j',' '), HB_TAG_NONE }, /* Jalapa De Díaz Mazatec != Majang */ {HB_TAG('m','a','k',' '), HB_TAG('M','K','R',' ')}, /* Makasar */ - {HB_TAG('m','a','m',' '), HB_TAG('M','A','M',' ')}, /* Mam */ - {HB_TAG('m','a','m',' '), HB_TAG('M','Y','N',' ')}, /* Mam -> Mayan */ {HB_TAG('m','a','n',' '), HB_TAG('M','N','K',' ')}, /* Mandingo [macrolanguage] -> Maninka */ - {HB_TAG('m','a','p',' '), HB_TAG_NONE }, /* Austronesian [collection] != Mapudungun */ - {HB_TAG('m','a','w',' '), HB_TAG_NONE }, /* Mampruli != Marwari */ - {HB_TAG('m','a','x',' '), HB_TAG('M','L','Y',' ')}, /* North Moluccan Malay -> Malay */ - {HB_TAG('m','a','x',' '), HB_TAG('C','P','P',' ')}, /* North Moluccan Malay -> Creoles */ {HB_TAG('m','b','f',' '), HB_TAG('C','P','P',' ')}, /* Baba Malay -> Creoles */ - {HB_TAG('m','b','n',' '), HB_TAG_NONE }, /* Macaguán != Mbundu */ -/*{HB_TAG('m','b','o',' '), HB_TAG('M','B','O',' ')},*/ /* Mbo (Cameroon) */ - {HB_TAG('m','c','h',' '), HB_TAG_NONE }, /* Maquiritari != Manchu */ {HB_TAG('m','c','m',' '), HB_TAG('C','P','P',' ')}, /* Malaccan Creole Portuguese -> Creoles */ - {HB_TAG('m','c','r',' '), HB_TAG_NONE }, /* Menya != Moose Cree */ {HB_TAG('m','c','t',' '), HB_TAG('B','T','I',' ')}, /* Mengisa -> Beti */ - {HB_TAG('m','d','e',' '), HB_TAG_NONE }, /* Maba (Chad) != Mende */ {HB_TAG('m','d','f',' '), HB_TAG('M','O','K',' ')}, /* Moksha */ -/*{HB_TAG('m','d','r',' '), HB_TAG('M','D','R',' ')},*/ /* Mandar */ {HB_TAG('m','d','y',' '), HB_TAG('M','L','E',' ')}, /* Male (Ethiopia) */ {HB_TAG('m','e','n',' '), HB_TAG('M','D','E',' ')}, /* Mende (Sierra Leone) */ {HB_TAG('m','e','o',' '), HB_TAG('M','L','Y',' ')}, /* Kedah Malay -> Malay */ -/*{HB_TAG('m','e','r',' '), HB_TAG('M','E','R',' ')},*/ /* Meru */ -/*{HB_TAG('m','e','v',' '), HB_TAG('M','E','V',' ')},*/ /* Mano */ - {HB_TAG('m','f','a',' '), HB_TAG('M','F','A',' ')}, /* Pattani Malay */ - {HB_TAG('m','f','a',' '), HB_TAG('M','L','Y',' ')}, /* Pattani Malay -> Malay */ {HB_TAG('m','f','b',' '), HB_TAG('M','L','Y',' ')}, /* Bangka -> Malay */ - {HB_TAG('m','f','e',' '), HB_TAG('M','F','E',' ')}, /* Morisyen */ - {HB_TAG('m','f','e',' '), HB_TAG('C','P','P',' ')}, /* Morisyen -> Creoles */ {HB_TAG('m','f','p',' '), HB_TAG('C','P','P',' ')}, /* Makassar Malay -> Creoles */ {HB_TAG('m','g','a',' '), HB_TAG('S','G','A',' ')}, /* Middle Irish (900-1200) -> Old Irish */ {HB_TAG('m','h','c',' '), HB_TAG('M','Y','N',' ')}, /* Mocho -> Mayan */ {HB_TAG('m','h','r',' '), HB_TAG('L','M','A',' ')}, /* Eastern Mari -> Low Mari */ {HB_TAG('m','h','v',' '), HB_TAG('A','R','K',' ')}, /* Arakanese (retired code) -> Rakhine */ - {HB_TAG('m','i','n',' '), HB_TAG('M','I','N',' ')}, /* Minangkabau */ - {HB_TAG('m','i','n',' '), HB_TAG('M','L','Y',' ')}, /* Minangkabau -> Malay */ - {HB_TAG('m','i','z',' '), HB_TAG_NONE }, /* Coatzospan Mixtec != Mizo */ {HB_TAG('m','k','n',' '), HB_TAG('C','P','P',' ')}, /* Kupang Malay -> Creoles */ - {HB_TAG('m','k','r',' '), HB_TAG_NONE }, /* Malas != Makasar */ {HB_TAG('m','k','u',' '), HB_TAG('M','N','K',' ')}, /* Konyanka Maninka -> Maninka */ -/*{HB_TAG('m','k','w',' '), HB_TAG('M','K','W',' ')},*/ /* Kituba (Congo) */ - {HB_TAG('m','l','e',' '), HB_TAG_NONE }, /* Manambu != Male */ - {HB_TAG('m','l','n',' '), HB_TAG_NONE }, /* Malango != Malinke */ - {HB_TAG('m','l','q',' '), HB_TAG('M','L','N',' ')}, /* Western Maninkakan -> Malinke */ - {HB_TAG('m','l','q',' '), HB_TAG('M','N','K',' ')}, /* Western Maninkakan -> Maninka */ - {HB_TAG('m','l','r',' '), HB_TAG_NONE }, /* Vame != Malayalam Reformed */ {HB_TAG('m','m','r',' '), HB_TAG('H','M','N',' ')}, /* Western Xiangxi Miao -> Hmong */ {HB_TAG('m','n','c',' '), HB_TAG('M','C','H',' ')}, /* Manchu */ - {HB_TAG('m','n','d',' '), HB_TAG_NONE }, /* Mondé != Mandinka */ - {HB_TAG('m','n','g',' '), HB_TAG_NONE }, /* Eastern Mnong != Mongolian */ {HB_TAG('m','n','h',' '), HB_TAG('B','A','D','0')}, /* Mono (Democratic Republic of Congo) -> Banda */ -/*{HB_TAG('m','n','i',' '), HB_TAG('M','N','I',' ')},*/ /* Manipuri */ - {HB_TAG('m','n','k',' '), HB_TAG('M','N','D',' ')}, /* Mandinka */ - {HB_TAG('m','n','k',' '), HB_TAG('M','N','K',' ')}, /* Mandinka -> Maninka */ {HB_TAG('m','n','p',' '), HB_TAG('Z','H','S',' ')}, /* Min Bei Chinese -> Chinese, Simplified */ {HB_TAG('m','n','s',' '), HB_TAG('M','A','N',' ')}, /* Mansi */ - {HB_TAG('m','n','w',' '), HB_TAG('M','O','N',' ')}, /* Mon */ - {HB_TAG('m','n','w',' '), HB_TAG('M','O','N','T')}, /* Mon -> Thailand Mon */ - {HB_TAG('m','n','x',' '), HB_TAG_NONE }, /* Manikion != Manx */ {HB_TAG('m','o','d',' '), HB_TAG('C','P','P',' ')}, /* Mobilian -> Creoles */ -/*{HB_TAG('m','o','h',' '), HB_TAG('M','O','H',' ')},*/ /* Mohawk */ - {HB_TAG('m','o','k',' '), HB_TAG_NONE }, /* Morori != Moksha */ {HB_TAG('m','o','p',' '), HB_TAG('M','Y','N',' ')}, /* Mopán Maya -> Mayan */ - {HB_TAG('m','o','r',' '), HB_TAG_NONE }, /* Moro != Moroccan */ -/*{HB_TAG('m','o','s',' '), HB_TAG('M','O','S',' ')},*/ /* Mossi */ {HB_TAG('m','p','e',' '), HB_TAG('M','A','J',' ')}, /* Majang */ {HB_TAG('m','q','g',' '), HB_TAG('M','L','Y',' ')}, /* Kota Bangun Kutai Malay -> Malay */ {HB_TAG('m','r','h',' '), HB_TAG('Q','I','N',' ')}, /* Mara Chin -> Chin */ {HB_TAG('m','r','j',' '), HB_TAG('H','M','A',' ')}, /* Western Mari -> High Mari */ {HB_TAG('m','s','c',' '), HB_TAG('M','N','K',' ')}, /* Sankaran Maninka -> Maninka */ {HB_TAG('m','s','h',' '), HB_TAG('M','L','G',' ')}, /* Masikoro Malagasy -> Malagasy */ - {HB_TAG('m','s','i',' '), HB_TAG('M','L','Y',' ')}, /* Sabah Malay -> Malay */ - {HB_TAG('m','s','i',' '), HB_TAG('C','P','P',' ')}, /* Sabah Malay -> Creoles */ - {HB_TAG('m','t','h',' '), HB_TAG_NONE }, /* Munggui != Maithili */ {HB_TAG('m','t','r',' '), HB_TAG('M','A','W',' ')}, /* Mewari -> Marwari */ - {HB_TAG('m','t','s',' '), HB_TAG_NONE }, /* Yora != Maltese */ {HB_TAG('m','u','d',' '), HB_TAG('C','P','P',' ')}, /* Mednyj Aleut -> Creoles */ {HB_TAG('m','u','i',' '), HB_TAG('M','L','Y',' ')}, /* Musi -> Malay */ - {HB_TAG('m','u','n',' '), HB_TAG_NONE }, /* Munda [collection] != Mundari */ {HB_TAG('m','u','p',' '), HB_TAG('R','A','J',' ')}, /* Malvi -> Rajasthani */ {HB_TAG('m','u','q',' '), HB_TAG('H','M','N',' ')}, /* Eastern Xiangxi Miao -> Hmong */ -/*{HB_TAG('m','u','s',' '), HB_TAG('M','U','S',' ')},*/ /* Creek -> Muscogee */ {HB_TAG('m','v','b',' '), HB_TAG('A','T','H',' ')}, /* Mattole -> Athapaskan */ {HB_TAG('m','v','e',' '), HB_TAG('M','A','W',' ')}, /* Marwari (Pakistan) */ {HB_TAG('m','v','f',' '), HB_TAG('M','N','G',' ')}, /* Peripheral Mongolian -> Mongolian */ {HB_TAG('m','w','k',' '), HB_TAG('M','N','K',' ')}, /* Kita Maninkakan -> Maninka */ -/*{HB_TAG('m','w','l',' '), HB_TAG('M','W','L',' ')},*/ /* Mirandese */ {HB_TAG('m','w','q',' '), HB_TAG('Q','I','N',' ')}, /* Mün Chin -> Chin */ {HB_TAG('m','w','r',' '), HB_TAG('M','A','W',' ')}, /* Marwari [macrolanguage] */ - {HB_TAG('m','w','w',' '), HB_TAG('M','W','W',' ')}, /* Hmong Daw */ - {HB_TAG('m','w','w',' '), HB_TAG('H','M','N',' ')}, /* Hmong Daw -> Hmong */ {HB_TAG('m','y','m',' '), HB_TAG('M','E','N',' ')}, /* Me’en */ -/*{HB_TAG('m','y','n',' '), HB_TAG('M','Y','N',' ')},*/ /* Mayan [collection] */ {HB_TAG('m','y','q',' '), HB_TAG('M','N','K',' ')}, /* Forest Maninka (retired code) -> Maninka */ {HB_TAG('m','y','v',' '), HB_TAG('E','R','Z',' ')}, /* Erzya */ {HB_TAG('m','z','b',' '), HB_TAG('B','B','R',' ')}, /* Tumzabt -> Berber */ -/*{HB_TAG('m','z','n',' '), HB_TAG('M','Z','N',' ')},*/ /* Mazanderani */ {HB_TAG('m','z','s',' '), HB_TAG('C','P','P',' ')}, /* Macanese -> Creoles */ - {HB_TAG('n','a','g',' '), HB_TAG('N','A','G',' ')}, /* Naga Pidgin -> Naga-Assamese */ - {HB_TAG('n','a','g',' '), HB_TAG('C','P','P',' ')}, /* Naga Pidgin -> Creoles */ -/*{HB_TAG('n','a','h',' '), HB_TAG('N','A','H',' ')},*/ /* Nahuatl [collection] */ {HB_TAG('n','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Min Nan Chinese -> Chinese, Simplified */ -/*{HB_TAG('n','a','p',' '), HB_TAG('N','A','P',' ')},*/ /* Neapolitan */ - {HB_TAG('n','a','s',' '), HB_TAG_NONE }, /* Naasioi != Naskapi */ {HB_TAG('n','a','z',' '), HB_TAG('N','A','H',' ')}, /* Coatepec Nahuatl -> Nahuatl */ {HB_TAG('n','c','h',' '), HB_TAG('N','A','H',' ')}, /* Central Huasteca Nahuatl -> Nahuatl */ {HB_TAG('n','c','i',' '), HB_TAG('N','A','H',' ')}, /* Classical Nahuatl -> Nahuatl */ {HB_TAG('n','c','j',' '), HB_TAG('N','A','H',' ')}, /* Northern Puebla Nahuatl -> Nahuatl */ {HB_TAG('n','c','l',' '), HB_TAG('N','A','H',' ')}, /* Michoacán Nahuatl -> Nahuatl */ - {HB_TAG('n','c','r',' '), HB_TAG_NONE }, /* Ncane != N-Cree */ {HB_TAG('n','c','x',' '), HB_TAG('N','A','H',' ')}, /* Central Puebla Nahuatl -> Nahuatl */ - {HB_TAG('n','d','b',' '), HB_TAG_NONE }, /* Kenswei Nsei != Ndebele */ -/*{HB_TAG('n','d','c',' '), HB_TAG('N','D','C',' ')},*/ /* Ndau */ - {HB_TAG('n','d','g',' '), HB_TAG_NONE }, /* Ndengereko != Ndonga */ -/*{HB_TAG('n','d','s',' '), HB_TAG('N','D','S',' ')},*/ /* Low Saxon */ {HB_TAG('n','e','f',' '), HB_TAG('C','P','P',' ')}, /* Nefamese -> Creoles */ -/*{HB_TAG('n','e','w',' '), HB_TAG('N','E','W',' ')},*/ /* Newari */ -/*{HB_TAG('n','g','a',' '), HB_TAG('N','G','A',' ')},*/ /* Ngbaka */ {HB_TAG('n','g','l',' '), HB_TAG('L','M','W',' ')}, /* Lomwe */ {HB_TAG('n','g','m',' '), HB_TAG('C','P','P',' ')}, /* Ngatik Men's Creole -> Creoles */ {HB_TAG('n','g','o',' '), HB_TAG('S','X','T',' ')}, /* Ngoni (retired code) -> Sutu */ - {HB_TAG('n','g','r',' '), HB_TAG_NONE }, /* Engdewu != Nagari */ {HB_TAG('n','g','u',' '), HB_TAG('N','A','H',' ')}, /* Guerrero Nahuatl -> Nahuatl */ {HB_TAG('n','h','c',' '), HB_TAG('N','A','H',' ')}, /* Tabasco Nahuatl -> Nahuatl */ {HB_TAG('n','h','d',' '), HB_TAG('G','U','A',' ')}, /* Chiripá -> Guarani */ @@ -1137,12 +903,9 @@ static const LangTag ot_languages3[] = { {HB_TAG('n','h','y',' '), HB_TAG('N','A','H',' ')}, /* Northern Oaxaca Nahuatl -> Nahuatl */ {HB_TAG('n','h','z',' '), HB_TAG('N','A','H',' ')}, /* Santa María La Alta Nahuatl -> Nahuatl */ {HB_TAG('n','i','q',' '), HB_TAG('K','A','L',' ')}, /* Nandi -> Kalenjin */ - {HB_TAG('n','i','s',' '), HB_TAG_NONE }, /* Nimi != Nisi */ -/*{HB_TAG('n','i','u',' '), HB_TAG('N','I','U',' ')},*/ /* Niuean */ {HB_TAG('n','i','v',' '), HB_TAG('G','I','L',' ')}, /* Gilyak */ {HB_TAG('n','j','t',' '), HB_TAG('C','P','P',' ')}, /* Ndyuka-Trio Pidgin -> Creoles */ {HB_TAG('n','j','z',' '), HB_TAG('N','I','S',' ')}, /* Nyishi -> Nisi */ - {HB_TAG('n','k','o',' '), HB_TAG_NONE }, /* Nkonya != N’Ko */ {HB_TAG('n','k','x',' '), HB_TAG('I','J','O',' ')}, /* Nkoroo -> Ijo */ {HB_TAG('n','l','a',' '), HB_TAG('B','M','L',' ')}, /* Ngombale -> Bamileke */ {HB_TAG('n','l','e',' '), HB_TAG('L','U','H',' ')}, /* East Nyala -> Luyia */ @@ -1151,329 +914,147 @@ static const LangTag ot_languages3[] = { {HB_TAG('n','n','h',' '), HB_TAG('B','M','L',' ')}, /* Ngiemboon -> Bamileke */ {HB_TAG('n','n','z',' '), HB_TAG('B','M','L',' ')}, /* Nda'nda' -> Bamileke */ {HB_TAG('n','o','d',' '), HB_TAG('N','T','A',' ')}, /* Northern Thai -> Northern Tai */ -/*{HB_TAG('n','o','e',' '), HB_TAG('N','O','E',' ')},*/ /* Nimadi */ -/*{HB_TAG('n','o','g',' '), HB_TAG('N','O','G',' ')},*/ /* Nogai */ -/*{HB_TAG('n','o','p',' '), HB_TAG('N','O','P',' ')},*/ /* Numanggang */ -/*{HB_TAG('n','o','v',' '), HB_TAG('N','O','V',' ')},*/ /* Novial */ {HB_TAG('n','p','i',' '), HB_TAG('N','E','P',' ')}, /* Nepali */ {HB_TAG('n','p','l',' '), HB_TAG('N','A','H',' ')}, /* Southeastern Puebla Nahuatl -> Nahuatl */ {HB_TAG('n','q','o',' '), HB_TAG('N','K','O',' ')}, /* N’Ko */ {HB_TAG('n','s','k',' '), HB_TAG('N','A','S',' ')}, /* Naskapi */ - {HB_TAG('n','s','m',' '), HB_TAG_NONE }, /* Sumi Naga != Northern Sami */ -/*{HB_TAG('n','s','o',' '), HB_TAG('N','S','O',' ')},*/ /* Northern Sotho */ {HB_TAG('n','s','u',' '), HB_TAG('N','A','H',' ')}, /* Sierra Negra Nahuatl -> Nahuatl */ - {HB_TAG('n','t','o',' '), HB_TAG_NONE }, /* Ntomba != Esperanto */ {HB_TAG('n','u','e',' '), HB_TAG('B','A','D','0')}, /* Ngundu -> Banda */ -/*{HB_TAG('n','u','k',' '), HB_TAG('N','U','K',' ')},*/ /* Nuu-chah-nulth */ {HB_TAG('n','u','u',' '), HB_TAG('B','A','D','0')}, /* Ngbundu -> Banda */ {HB_TAG('n','u','z',' '), HB_TAG('N','A','H',' ')}, /* Tlamacazapa Nahuatl -> Nahuatl */ {HB_TAG('n','w','e',' '), HB_TAG('B','M','L',' ')}, /* Ngwe -> Bamileke */ {HB_TAG('n','y','d',' '), HB_TAG('L','U','H',' ')}, /* Nyore -> Luyia */ -/*{HB_TAG('n','y','m',' '), HB_TAG('N','Y','M',' ')},*/ /* Nyamwezi */ {HB_TAG('n','y','n',' '), HB_TAG('N','K','L',' ')}, /* Nyankole */ -/*{HB_TAG('n','z','a',' '), HB_TAG('N','Z','A',' ')},*/ /* Tigon Mbembe -> Mbembe Tigon */ -/*{HB_TAG('o','j','b',' '), HB_TAG('O','J','B',' ')},*/ /* Northwestern Ojibwa -> Ojibway */ {HB_TAG('o','j','c',' '), HB_TAG('O','J','B',' ')}, /* Central Ojibwa -> Ojibway */ {HB_TAG('o','j','g',' '), HB_TAG('O','J','B',' ')}, /* Eastern Ojibwa -> Ojibway */ - {HB_TAG('o','j','s',' '), HB_TAG('O','C','R',' ')}, /* Severn Ojibwa -> Oji-Cree */ - {HB_TAG('o','j','s',' '), HB_TAG('O','J','B',' ')}, /* Severn Ojibwa -> Ojibway */ {HB_TAG('o','j','w',' '), HB_TAG('O','J','B',' ')}, /* Western Ojibwa -> Ojibway */ {HB_TAG('o','k','d',' '), HB_TAG('I','J','O',' ')}, /* Okodia -> Ijo */ {HB_TAG('o','k','i',' '), HB_TAG('K','A','L',' ')}, /* Okiek -> Kalenjin */ {HB_TAG('o','k','m',' '), HB_TAG('K','O','H',' ')}, /* Middle Korean (10th-16th cent.) -> Korean Old Hangul */ {HB_TAG('o','k','r',' '), HB_TAG('I','J','O',' ')}, /* Kirike -> Ijo */ -/*{HB_TAG('o','n','e',' '), HB_TAG('O','N','E',' ')},*/ /* Oneida */ -/*{HB_TAG('o','n','o',' '), HB_TAG('O','N','O',' ')},*/ /* Onondaga */ {HB_TAG('o','n','x',' '), HB_TAG('C','P','P',' ')}, /* Onin Based Pidgin -> Creoles */ {HB_TAG('o','o','r',' '), HB_TAG('C','P','P',' ')}, /* Oorlams -> Creoles */ {HB_TAG('o','r','c',' '), HB_TAG('O','R','O',' ')}, /* Orma -> Oromo */ {HB_TAG('o','r','n',' '), HB_TAG('M','L','Y',' ')}, /* Orang Kanaq -> Malay */ - {HB_TAG('o','r','o',' '), HB_TAG_NONE }, /* Orokolo != Oromo */ {HB_TAG('o','r','r',' '), HB_TAG('I','J','O',' ')}, /* Oruma -> Ijo */ {HB_TAG('o','r','s',' '), HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */ {HB_TAG('o','r','y',' '), HB_TAG('O','R','I',' ')}, /* Odia */ {HB_TAG('o','t','w',' '), HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */ {HB_TAG('o','u','a',' '), HB_TAG('B','B','R',' ')}, /* Tagargrent -> Berber */ - {HB_TAG('p','a','a',' '), HB_TAG_NONE }, /* Papuan [collection] != Palestinian Aramaic */ -/*{HB_TAG('p','a','g',' '), HB_TAG('P','A','G',' ')},*/ /* Pangasinan */ - {HB_TAG('p','a','l',' '), HB_TAG_NONE }, /* Pahlavi != Pali */ -/*{HB_TAG('p','a','m',' '), HB_TAG('P','A','M',' ')},*/ /* Pampanga -> Pampangan */ - {HB_TAG('p','a','p',' '), HB_TAG('P','A','P','0')}, /* Papiamento -> Papiamentu */ - {HB_TAG('p','a','p',' '), HB_TAG('C','P','P',' ')}, /* Papiamento -> Creoles */ - {HB_TAG('p','a','s',' '), HB_TAG_NONE }, /* Papasena != Pashto */ -/*{HB_TAG('p','a','u',' '), HB_TAG('P','A','U',' ')},*/ /* Palauan */ {HB_TAG('p','b','t',' '), HB_TAG('P','A','S',' ')}, /* Southern Pashto -> Pashto */ {HB_TAG('p','b','u',' '), HB_TAG('P','A','S',' ')}, /* Northern Pashto -> Pashto */ -/*{HB_TAG('p','c','c',' '), HB_TAG('P','C','C',' ')},*/ /* Bouyei */ -/*{HB_TAG('p','c','d',' '), HB_TAG('P','C','D',' ')},*/ /* Picard */ {HB_TAG('p','c','e',' '), HB_TAG('P','L','G',' ')}, /* Ruching Palaung -> Palaung */ {HB_TAG('p','c','k',' '), HB_TAG('Q','I','N',' ')}, /* Paite Chin -> Chin */ {HB_TAG('p','c','m',' '), HB_TAG('C','P','P',' ')}, /* Nigerian Pidgin -> Creoles */ -/*{HB_TAG('p','d','c',' '), HB_TAG('P','D','C',' ')},*/ /* Pennsylvania German */ {HB_TAG('p','d','u',' '), HB_TAG('K','R','N',' ')}, /* Kayan -> Karen */ {HB_TAG('p','e','a',' '), HB_TAG('C','P','P',' ')}, /* Peranakan Indonesian -> Creoles */ {HB_TAG('p','e','l',' '), HB_TAG('M','L','Y',' ')}, /* Pekal -> Malay */ {HB_TAG('p','e','s',' '), HB_TAG('F','A','R',' ')}, /* Iranian Persian -> Persian */ {HB_TAG('p','e','y',' '), HB_TAG('C','P','P',' ')}, /* Petjo -> Creoles */ - {HB_TAG('p','g','a',' '), HB_TAG('A','R','A',' ')}, /* Sudanese Creole Arabic -> Arabic */ - {HB_TAG('p','g','a',' '), HB_TAG('C','P','P',' ')}, /* Sudanese Creole Arabic -> Creoles */ -/*{HB_TAG('p','h','k',' '), HB_TAG('P','H','K',' ')},*/ /* Phake */ - {HB_TAG('p','i','h',' '), HB_TAG('P','I','H',' ')}, /* Pitcairn-Norfolk -> Norfolk */ - {HB_TAG('p','i','h',' '), HB_TAG('C','P','P',' ')}, /* Pitcairn-Norfolk -> Creoles */ - {HB_TAG('p','i','l',' '), HB_TAG_NONE }, /* Yom != Filipino */ {HB_TAG('p','i','s',' '), HB_TAG('C','P','P',' ')}, /* Pijin -> Creoles */ {HB_TAG('p','k','h',' '), HB_TAG('Q','I','N',' ')}, /* Pankhu -> Chin */ {HB_TAG('p','k','o',' '), HB_TAG('K','A','L',' ')}, /* Pökoot -> Kalenjin */ {HB_TAG('p','l','g',' '), HB_TAG('P','L','G','0')}, /* Pilagá */ - {HB_TAG('p','l','k',' '), HB_TAG_NONE }, /* Kohistani Shina != Polish */ {HB_TAG('p','l','l',' '), HB_TAG('P','L','G',' ')}, /* Shwe Palaung -> Palaung */ {HB_TAG('p','l','n',' '), HB_TAG('C','P','P',' ')}, /* Palenquero -> Creoles */ {HB_TAG('p','l','p',' '), HB_TAG('P','A','P',' ')}, /* Palpa (retired code) */ {HB_TAG('p','l','t',' '), HB_TAG('M','L','G',' ')}, /* Plateau Malagasy -> Malagasy */ {HB_TAG('p','m','l',' '), HB_TAG('C','P','P',' ')}, /* Lingua Franca -> Creoles */ -/*{HB_TAG('p','m','s',' '), HB_TAG('P','M','S',' ')},*/ /* Piemontese */ {HB_TAG('p','m','y',' '), HB_TAG('C','P','P',' ')}, /* Papuan Malay -> Creoles */ -/*{HB_TAG('p','n','b',' '), HB_TAG('P','N','B',' ')},*/ /* Western Panjabi */ {HB_TAG('p','o','c',' '), HB_TAG('M','Y','N',' ')}, /* Poqomam -> Mayan */ - {HB_TAG('p','o','h',' '), HB_TAG('P','O','H',' ')}, /* Poqomchi' -> Pocomchi */ - {HB_TAG('p','o','h',' '), HB_TAG('M','Y','N',' ')}, /* Poqomchi' -> Mayan */ -/*{HB_TAG('p','o','n',' '), HB_TAG('P','O','N',' ')},*/ /* Pohnpeian */ {HB_TAG('p','o','v',' '), HB_TAG('C','P','P',' ')}, /* Upper Guinea Crioulo -> Creoles */ {HB_TAG('p','p','a',' '), HB_TAG('B','A','G',' ')}, /* Pao (retired code) -> Baghelkhandi */ {HB_TAG('p','r','e',' '), HB_TAG('C','P','P',' ')}, /* Principense -> Creoles */ -/*{HB_TAG('p','r','o',' '), HB_TAG('P','R','O',' ')},*/ /* Old Provençal (to 1500) -> Provençal / Old Provençal */ {HB_TAG('p','r','p',' '), HB_TAG('G','U','J',' ')}, /* Parsi (retired code) -> Gujarati */ - {HB_TAG('p','r','s',' '), HB_TAG('D','R','I',' ')}, /* Dari */ - {HB_TAG('p','r','s',' '), HB_TAG('F','A','R',' ')}, /* Dari -> Persian */ {HB_TAG('p','s','e',' '), HB_TAG('M','L','Y',' ')}, /* Central Malay -> Malay */ {HB_TAG('p','s','t',' '), HB_TAG('P','A','S',' ')}, /* Central Pashto -> Pashto */ {HB_TAG('p','u','b',' '), HB_TAG('Q','I','N',' ')}, /* Purum -> Chin */ {HB_TAG('p','u','z',' '), HB_TAG('Q','I','N',' ')}, /* Purum Naga (retired code) -> Chin */ - {HB_TAG('p','w','o',' '), HB_TAG('P','W','O',' ')}, /* Pwo Western Karen -> Western Pwo Karen */ - {HB_TAG('p','w','o',' '), HB_TAG('K','R','N',' ')}, /* Pwo Western Karen -> Karen */ {HB_TAG('p','w','w',' '), HB_TAG('K','R','N',' ')}, /* Pwo Northern Karen -> Karen */ - {HB_TAG('q','u','b',' '), HB_TAG('Q','W','H',' ')}, /* Huallaga Huánuco Quechua -> Quechua (Peru) */ - {HB_TAG('q','u','b',' '), HB_TAG('Q','U','Z',' ')}, /* Huallaga Huánuco Quechua -> Quechua */ - {HB_TAG('q','u','c',' '), HB_TAG('Q','U','C',' ')}, /* K’iche’ */ - {HB_TAG('q','u','c',' '), HB_TAG('M','Y','N',' ')}, /* K'iche' -> Mayan */ - {HB_TAG('q','u','d',' '), HB_TAG('Q','V','I',' ')}, /* Calderón Highland Quichua -> Quechua (Ecuador) */ - {HB_TAG('q','u','d',' '), HB_TAG('Q','U','Z',' ')}, /* Calderón Highland Quichua -> Quechua */ {HB_TAG('q','u','f',' '), HB_TAG('Q','U','Z',' ')}, /* Lambayeque Quechua -> Quechua */ - {HB_TAG('q','u','g',' '), HB_TAG('Q','V','I',' ')}, /* Chimborazo Highland Quichua -> Quechua (Ecuador) */ - {HB_TAG('q','u','g',' '), HB_TAG('Q','U','Z',' ')}, /* Chimborazo Highland Quichua -> Quechua */ - {HB_TAG('q','u','h',' '), HB_TAG('Q','U','H',' ')}, /* South Bolivian Quechua -> Quechua (Bolivia) */ - {HB_TAG('q','u','h',' '), HB_TAG('Q','U','Z',' ')}, /* South Bolivian Quechua -> Quechua */ {HB_TAG('q','u','k',' '), HB_TAG('Q','U','Z',' ')}, /* Chachapoyas Quechua -> Quechua */ - {HB_TAG('q','u','l',' '), HB_TAG('Q','U','H',' ')}, /* North Bolivian Quechua -> Quechua (Bolivia) */ - {HB_TAG('q','u','l',' '), HB_TAG('Q','U','Z',' ')}, /* North Bolivian Quechua -> Quechua */ {HB_TAG('q','u','m',' '), HB_TAG('M','Y','N',' ')}, /* Sipacapense -> Mayan */ - {HB_TAG('q','u','p',' '), HB_TAG('Q','V','I',' ')}, /* Southern Pastaza Quechua -> Quechua (Ecuador) */ - {HB_TAG('q','u','p',' '), HB_TAG('Q','U','Z',' ')}, /* Southern Pastaza Quechua -> Quechua */ - {HB_TAG('q','u','r',' '), HB_TAG('Q','W','H',' ')}, /* Yanahuanca Pasco Quechua -> Quechua (Peru) */ - {HB_TAG('q','u','r',' '), HB_TAG('Q','U','Z',' ')}, /* Yanahuanca Pasco Quechua -> Quechua */ - {HB_TAG('q','u','s',' '), HB_TAG('Q','U','H',' ')}, /* Santiago del Estero Quichua -> Quechua (Bolivia) */ - {HB_TAG('q','u','s',' '), HB_TAG('Q','U','Z',' ')}, /* Santiago del Estero Quichua -> Quechua */ {HB_TAG('q','u','v',' '), HB_TAG('M','Y','N',' ')}, /* Sacapulteco -> Mayan */ - {HB_TAG('q','u','w',' '), HB_TAG('Q','V','I',' ')}, /* Tena Lowland Quichua -> Quechua (Ecuador) */ - {HB_TAG('q','u','w',' '), HB_TAG('Q','U','Z',' ')}, /* Tena Lowland Quichua -> Quechua */ - {HB_TAG('q','u','x',' '), HB_TAG('Q','W','H',' ')}, /* Yauyos Quechua -> Quechua (Peru) */ - {HB_TAG('q','u','x',' '), HB_TAG('Q','U','Z',' ')}, /* Yauyos Quechua -> Quechua */ {HB_TAG('q','u','y',' '), HB_TAG('Q','U','Z',' ')}, /* Ayacucho Quechua -> Quechua */ -/*{HB_TAG('q','u','z',' '), HB_TAG('Q','U','Z',' ')},*/ /* Cusco Quechua -> Quechua */ - {HB_TAG('q','v','a',' '), HB_TAG('Q','W','H',' ')}, /* Ambo-Pasco Quechua -> Quechua (Peru) */ - {HB_TAG('q','v','a',' '), HB_TAG('Q','U','Z',' ')}, /* Ambo-Pasco Quechua -> Quechua */ {HB_TAG('q','v','c',' '), HB_TAG('Q','U','Z',' ')}, /* Cajamarca Quechua -> Quechua */ {HB_TAG('q','v','e',' '), HB_TAG('Q','U','Z',' ')}, /* Eastern Apurímac Quechua -> Quechua */ - {HB_TAG('q','v','h',' '), HB_TAG('Q','W','H',' ')}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */ - {HB_TAG('q','v','h',' '), HB_TAG('Q','U','Z',' ')}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua */ - {HB_TAG('q','v','i',' '), HB_TAG('Q','V','I',' ')}, /* Imbabura Highland Quichua -> Quechua (Ecuador) */ - {HB_TAG('q','v','i',' '), HB_TAG('Q','U','Z',' ')}, /* Imbabura Highland Quichua -> Quechua */ - {HB_TAG('q','v','j',' '), HB_TAG('Q','V','I',' ')}, /* Loja Highland Quichua -> Quechua (Ecuador) */ - {HB_TAG('q','v','j',' '), HB_TAG('Q','U','Z',' ')}, /* Loja Highland Quichua -> Quechua */ - {HB_TAG('q','v','l',' '), HB_TAG('Q','W','H',' ')}, /* Cajatambo North Lima Quechua -> Quechua (Peru) */ - {HB_TAG('q','v','l',' '), HB_TAG('Q','U','Z',' ')}, /* Cajatambo North Lima Quechua -> Quechua */ - {HB_TAG('q','v','m',' '), HB_TAG('Q','W','H',' ')}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */ - {HB_TAG('q','v','m',' '), HB_TAG('Q','U','Z',' ')}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua */ - {HB_TAG('q','v','n',' '), HB_TAG('Q','W','H',' ')}, /* North Junín Quechua -> Quechua (Peru) */ - {HB_TAG('q','v','n',' '), HB_TAG('Q','U','Z',' ')}, /* North Junín Quechua -> Quechua */ - {HB_TAG('q','v','o',' '), HB_TAG('Q','V','I',' ')}, /* Napo Lowland Quechua -> Quechua (Ecuador) */ - {HB_TAG('q','v','o',' '), HB_TAG('Q','U','Z',' ')}, /* Napo Lowland Quechua -> Quechua */ - {HB_TAG('q','v','p',' '), HB_TAG('Q','W','H',' ')}, /* Pacaraos Quechua -> Quechua (Peru) */ - {HB_TAG('q','v','p',' '), HB_TAG('Q','U','Z',' ')}, /* Pacaraos Quechua -> Quechua */ {HB_TAG('q','v','s',' '), HB_TAG('Q','U','Z',' ')}, /* San Martín Quechua -> Quechua */ - {HB_TAG('q','v','w',' '), HB_TAG('Q','W','H',' ')}, /* Huaylla Wanca Quechua -> Quechua (Peru) */ - {HB_TAG('q','v','w',' '), HB_TAG('Q','U','Z',' ')}, /* Huaylla Wanca Quechua -> Quechua */ - {HB_TAG('q','v','z',' '), HB_TAG('Q','V','I',' ')}, /* Northern Pastaza Quichua -> Quechua (Ecuador) */ - {HB_TAG('q','v','z',' '), HB_TAG('Q','U','Z',' ')}, /* Northern Pastaza Quichua -> Quechua */ - {HB_TAG('q','w','a',' '), HB_TAG('Q','W','H',' ')}, /* Corongo Ancash Quechua -> Quechua (Peru) */ - {HB_TAG('q','w','a',' '), HB_TAG('Q','U','Z',' ')}, /* Corongo Ancash Quechua -> Quechua */ {HB_TAG('q','w','c',' '), HB_TAG('Q','U','Z',' ')}, /* Classical Quechua -> Quechua */ - {HB_TAG('q','w','h',' '), HB_TAG('Q','W','H',' ')}, /* Huaylas Ancash Quechua -> Quechua (Peru) */ - {HB_TAG('q','w','h',' '), HB_TAG('Q','U','Z',' ')}, /* Huaylas Ancash Quechua -> Quechua */ - {HB_TAG('q','w','s',' '), HB_TAG('Q','W','H',' ')}, /* Sihuas Ancash Quechua -> Quechua (Peru) */ - {HB_TAG('q','w','s',' '), HB_TAG('Q','U','Z',' ')}, /* Sihuas Ancash Quechua -> Quechua */ {HB_TAG('q','w','t',' '), HB_TAG('A','T','H',' ')}, /* Kwalhioqua-Tlatskanai -> Athapaskan */ - {HB_TAG('q','x','a',' '), HB_TAG('Q','W','H',' ')}, /* Chiquián Ancash Quechua -> Quechua (Peru) */ - {HB_TAG('q','x','a',' '), HB_TAG('Q','U','Z',' ')}, /* Chiquián Ancash Quechua -> Quechua */ - {HB_TAG('q','x','c',' '), HB_TAG('Q','W','H',' ')}, /* Chincha Quechua -> Quechua (Peru) */ - {HB_TAG('q','x','c',' '), HB_TAG('Q','U','Z',' ')}, /* Chincha Quechua -> Quechua */ - {HB_TAG('q','x','h',' '), HB_TAG('Q','W','H',' ')}, /* Panao Huánuco Quechua -> Quechua (Peru) */ - {HB_TAG('q','x','h',' '), HB_TAG('Q','U','Z',' ')}, /* Panao Huánuco Quechua -> Quechua */ - {HB_TAG('q','x','l',' '), HB_TAG('Q','V','I',' ')}, /* Salasaca Highland Quichua -> Quechua (Ecuador) */ - {HB_TAG('q','x','l',' '), HB_TAG('Q','U','Z',' ')}, /* Salasaca Highland Quichua -> Quechua */ - {HB_TAG('q','x','n',' '), HB_TAG('Q','W','H',' ')}, /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */ - {HB_TAG('q','x','n',' '), HB_TAG('Q','U','Z',' ')}, /* Northern Conchucos Ancash Quechua -> Quechua */ - {HB_TAG('q','x','o',' '), HB_TAG('Q','W','H',' ')}, /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */ - {HB_TAG('q','x','o',' '), HB_TAG('Q','U','Z',' ')}, /* Southern Conchucos Ancash Quechua -> Quechua */ {HB_TAG('q','x','p',' '), HB_TAG('Q','U','Z',' ')}, /* Puno Quechua -> Quechua */ - {HB_TAG('q','x','r',' '), HB_TAG('Q','V','I',' ')}, /* Cañar Highland Quichua -> Quechua (Ecuador) */ - {HB_TAG('q','x','r',' '), HB_TAG('Q','U','Z',' ')}, /* Cañar Highland Quichua -> Quechua */ - {HB_TAG('q','x','t',' '), HB_TAG('Q','W','H',' ')}, /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */ - {HB_TAG('q','x','t',' '), HB_TAG('Q','U','Z',' ')}, /* Santa Ana de Tusi Pasco Quechua -> Quechua */ {HB_TAG('q','x','u',' '), HB_TAG('Q','U','Z',' ')}, /* Arequipa-La Unión Quechua -> Quechua */ - {HB_TAG('q','x','w',' '), HB_TAG('Q','W','H',' ')}, /* Jauja Wanca Quechua -> Quechua (Peru) */ - {HB_TAG('q','x','w',' '), HB_TAG('Q','U','Z',' ')}, /* Jauja Wanca Quechua -> Quechua */ {HB_TAG('r','a','g',' '), HB_TAG('L','U','H',' ')}, /* Logooli -> Luyia */ -/*{HB_TAG('r','a','j',' '), HB_TAG('R','A','J',' ')},*/ /* Rajasthani [macrolanguage] */ {HB_TAG('r','a','l',' '), HB_TAG('Q','I','N',' ')}, /* Ralte -> Chin */ -/*{HB_TAG('r','a','r',' '), HB_TAG('R','A','R',' ')},*/ /* Rarotongan */ {HB_TAG('r','b','b',' '), HB_TAG('P','L','G',' ')}, /* Rumai Palaung -> Palaung */ {HB_TAG('r','b','l',' '), HB_TAG('B','I','K',' ')}, /* Miraya Bikol -> Bikol */ {HB_TAG('r','c','f',' '), HB_TAG('C','P','P',' ')}, /* Réunion Creole French -> Creoles */ -/*{HB_TAG('r','e','j',' '), HB_TAG('R','E','J',' ')},*/ /* Rejang */ -/*{HB_TAG('r','h','g',' '), HB_TAG('R','H','G',' ')},*/ /* Rohingya */ -/*{HB_TAG('r','i','a',' '), HB_TAG('R','I','A',' ')},*/ /* Riang (India) */ - {HB_TAG('r','i','f',' '), HB_TAG('R','I','F',' ')}, /* Tarifit */ - {HB_TAG('r','i','f',' '), HB_TAG('B','B','R',' ')}, /* Tarifit -> Berber */ -/*{HB_TAG('r','i','t',' '), HB_TAG('R','I','T',' ')},*/ /* Ritharrngu -> Ritarungo */ {HB_TAG('r','k','i',' '), HB_TAG('A','R','K',' ')}, /* Rakhine */ -/*{HB_TAG('r','k','w',' '), HB_TAG('R','K','W',' ')},*/ /* Arakwal */ {HB_TAG('r','m','c',' '), HB_TAG('R','O','Y',' ')}, /* Carpathian Romani -> Romany */ {HB_TAG('r','m','f',' '), HB_TAG('R','O','Y',' ')}, /* Kalo Finnish Romani -> Romany */ {HB_TAG('r','m','l',' '), HB_TAG('R','O','Y',' ')}, /* Baltic Romani -> Romany */ {HB_TAG('r','m','n',' '), HB_TAG('R','O','Y',' ')}, /* Balkan Romani -> Romany */ {HB_TAG('r','m','o',' '), HB_TAG('R','O','Y',' ')}, /* Sinte Romani -> Romany */ - {HB_TAG('r','m','s',' '), HB_TAG_NONE }, /* Romanian Sign Language != Romansh */ {HB_TAG('r','m','w',' '), HB_TAG('R','O','Y',' ')}, /* Welsh Romani -> Romany */ - {HB_TAG('r','m','y',' '), HB_TAG('R','M','Y',' ')}, /* Vlax Romani */ - {HB_TAG('r','m','y',' '), HB_TAG('R','O','Y',' ')}, /* Vlax Romani -> Romany */ {HB_TAG('r','m','z',' '), HB_TAG('A','R','K',' ')}, /* Marma -> Rakhine */ {HB_TAG('r','o','m',' '), HB_TAG('R','O','Y',' ')}, /* Romany [macrolanguage] */ {HB_TAG('r','o','p',' '), HB_TAG('C','P','P',' ')}, /* Kriol -> Creoles */ {HB_TAG('r','t','c',' '), HB_TAG('Q','I','N',' ')}, /* Rungtu Chin -> Chin */ -/*{HB_TAG('r','t','m',' '), HB_TAG('R','T','M',' ')},*/ /* Rotuman */ {HB_TAG('r','u','e',' '), HB_TAG('R','S','Y',' ')}, /* Rusyn */ -/*{HB_TAG('r','u','p',' '), HB_TAG('R','U','P',' ')},*/ /* Aromanian */ {HB_TAG('r','w','r',' '), HB_TAG('M','A','W',' ')}, /* Marwari (India) */ - {HB_TAG('s','a','d',' '), HB_TAG_NONE }, /* Sandawe != Sadri */ {HB_TAG('s','a','h',' '), HB_TAG('Y','A','K',' ')}, /* Yakut -> Sakha */ {HB_TAG('s','a','m',' '), HB_TAG('P','A','A',' ')}, /* Samaritan Aramaic -> Palestinian Aramaic */ -/*{HB_TAG('s','a','s',' '), HB_TAG('S','A','S',' ')},*/ /* Sasak */ -/*{HB_TAG('s','a','t',' '), HB_TAG('S','A','T',' ')},*/ /* Santali */ - {HB_TAG('s','a','y',' '), HB_TAG_NONE }, /* Saya != Sayisi */ {HB_TAG('s','c','f',' '), HB_TAG('C','P','P',' ')}, /* San Miguel Creole French -> Creoles */ {HB_TAG('s','c','h',' '), HB_TAG('Q','I','N',' ')}, /* Sakachep -> Chin */ {HB_TAG('s','c','i',' '), HB_TAG('C','P','P',' ')}, /* Sri Lankan Creole Malay -> Creoles */ {HB_TAG('s','c','k',' '), HB_TAG('S','A','D',' ')}, /* Sadri */ -/*{HB_TAG('s','c','n',' '), HB_TAG('S','C','N',' ')},*/ /* Sicilian */ -/*{HB_TAG('s','c','o',' '), HB_TAG('S','C','O',' ')},*/ /* Scots */ - {HB_TAG('s','c','s',' '), HB_TAG('S','C','S',' ')}, /* North Slavey */ - {HB_TAG('s','c','s',' '), HB_TAG('S','L','A',' ')}, /* North Slavey -> Slavey */ - {HB_TAG('s','c','s',' '), HB_TAG('A','T','H',' ')}, /* North Slavey -> Athapaskan */ {HB_TAG('s','d','c',' '), HB_TAG('S','R','D',' ')}, /* Sassarese Sardinian -> Sardinian */ {HB_TAG('s','d','h',' '), HB_TAG('K','U','R',' ')}, /* Southern Kurdish -> Kurdish */ {HB_TAG('s','d','n',' '), HB_TAG('S','R','D',' ')}, /* Gallurese Sardinian -> Sardinian */ {HB_TAG('s','d','s',' '), HB_TAG('B','B','R',' ')}, /* Sened -> Berber */ -/*{HB_TAG('s','e','e',' '), HB_TAG('S','E','E',' ')},*/ /* Seneca */ {HB_TAG('s','e','h',' '), HB_TAG('S','N','A',' ')}, /* Sena */ {HB_TAG('s','e','k',' '), HB_TAG('A','T','H',' ')}, /* Sekani -> Athapaskan */ -/*{HB_TAG('s','e','l',' '), HB_TAG('S','E','L',' ')},*/ /* Selkup */ {HB_TAG('s','e','z',' '), HB_TAG('Q','I','N',' ')}, /* Senthang Chin -> Chin */ - {HB_TAG('s','f','m',' '), HB_TAG('S','F','M',' ')}, /* Small Flowery Miao */ - {HB_TAG('s','f','m',' '), HB_TAG('H','M','N',' ')}, /* Small Flowery Miao -> Hmong */ -/*{HB_TAG('s','g','a',' '), HB_TAG('S','G','A',' ')},*/ /* Old Irish (to 900) */ {HB_TAG('s','g','c',' '), HB_TAG('K','A','L',' ')}, /* Kipsigis -> Kalenjin */ - {HB_TAG('s','g','o',' '), HB_TAG_NONE }, /* Songa (retired code) != Sango */ -/*{HB_TAG('s','g','s',' '), HB_TAG('S','G','S',' ')},*/ /* Samogitian */ {HB_TAG('s','g','w',' '), HB_TAG('C','H','G',' ')}, /* Sebat Bet Gurage -> Chaha Gurage */ - {HB_TAG('s','h','i',' '), HB_TAG('S','H','I',' ')}, /* Tachelhit */ - {HB_TAG('s','h','i',' '), HB_TAG('B','B','R',' ')}, /* Tachelhit -> Berber */ {HB_TAG('s','h','l',' '), HB_TAG('Q','I','N',' ')}, /* Shendu -> Chin */ -/*{HB_TAG('s','h','n',' '), HB_TAG('S','H','N',' ')},*/ /* Shan */ {HB_TAG('s','h','u',' '), HB_TAG('A','R','A',' ')}, /* Chadian Arabic -> Arabic */ {HB_TAG('s','h','y',' '), HB_TAG('B','B','R',' ')}, /* Tachawit -> Berber */ - {HB_TAG('s','i','b',' '), HB_TAG_NONE }, /* Sebop != Sibe */ -/*{HB_TAG('s','i','d',' '), HB_TAG('S','I','D',' ')},*/ /* Sidamo */ - {HB_TAG('s','i','g',' '), HB_TAG_NONE }, /* Paasaal != Silte Gurage */ {HB_TAG('s','i','z',' '), HB_TAG('B','B','R',' ')}, /* Siwi -> Berber */ -/*{HB_TAG('s','j','a',' '), HB_TAG('S','J','A',' ')},*/ /* Epena */ {HB_TAG('s','j','c',' '), HB_TAG('Z','H','S',' ')}, /* Shaojiang Chinese -> Chinese, Simplified */ {HB_TAG('s','j','d',' '), HB_TAG('K','S','M',' ')}, /* Kildin Sami */ -/*{HB_TAG('s','j','e',' '), HB_TAG('S','J','E',' ')},*/ /* Pite Sami */ {HB_TAG('s','j','o',' '), HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */ {HB_TAG('s','j','s',' '), HB_TAG('B','B','R',' ')}, /* Senhaja De Srair -> Berber */ -/*{HB_TAG('s','j','u',' '), HB_TAG('S','J','U',' ')},*/ /* Ume Sami */ {HB_TAG('s','k','g',' '), HB_TAG('M','L','G',' ')}, /* Sakalava Malagasy -> Malagasy */ {HB_TAG('s','k','r',' '), HB_TAG('S','R','K',' ')}, /* Saraiki */ - {HB_TAG('s','k','s',' '), HB_TAG_NONE }, /* Maia != Skolt Sami */ {HB_TAG('s','k','w',' '), HB_TAG('C','P','P',' ')}, /* Skepi Creole Dutch -> Creoles */ - {HB_TAG('s','k','y',' '), HB_TAG_NONE }, /* Sikaiana != Slovak */ - {HB_TAG('s','l','a',' '), HB_TAG_NONE }, /* Slavic [collection] != Slavey */ {HB_TAG('s','m','a',' '), HB_TAG('S','S','M',' ')}, /* Southern Sami */ {HB_TAG('s','m','d',' '), HB_TAG('M','B','N',' ')}, /* Sama (retired code) -> Mbundu */ {HB_TAG('s','m','j',' '), HB_TAG('L','S','M',' ')}, /* Lule Sami */ - {HB_TAG('s','m','l',' '), HB_TAG_NONE }, /* Central Sama != Somali */ {HB_TAG('s','m','n',' '), HB_TAG('I','S','M',' ')}, /* Inari Sami */ {HB_TAG('s','m','s',' '), HB_TAG('S','K','S',' ')}, /* Skolt Sami */ {HB_TAG('s','m','t',' '), HB_TAG('Q','I','N',' ')}, /* Simte -> Chin */ {HB_TAG('s','n','b',' '), HB_TAG('I','B','A',' ')}, /* Sebuyau (retired code) -> Iban */ - {HB_TAG('s','n','h',' '), HB_TAG_NONE }, /* Shinabo (retired code) != Sinhala (Sinhalese) */ -/*{HB_TAG('s','n','k',' '), HB_TAG('S','N','K',' ')},*/ /* Soninke */ - {HB_TAG('s','o','g',' '), HB_TAG_NONE }, /* Sogdian != Sodo Gurage */ -/*{HB_TAG('s','o','p',' '), HB_TAG('S','O','P',' ')},*/ /* Songe */ {HB_TAG('s','p','v',' '), HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia */ {HB_TAG('s','p','y',' '), HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */ - {HB_TAG('s','r','b',' '), HB_TAG_NONE }, /* Sora != Serbian */ {HB_TAG('s','r','c',' '), HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */ - {HB_TAG('s','r','k',' '), HB_TAG_NONE }, /* Serudung Murut != Saraiki */ {HB_TAG('s','r','m',' '), HB_TAG('C','P','P',' ')}, /* Saramaccan -> Creoles */ {HB_TAG('s','r','n',' '), HB_TAG('C','P','P',' ')}, /* Sranan Tongo -> Creoles */ {HB_TAG('s','r','o',' '), HB_TAG('S','R','D',' ')}, /* Campidanese Sardinian -> Sardinian */ -/*{HB_TAG('s','r','r',' '), HB_TAG('S','R','R',' ')},*/ /* Serer */ {HB_TAG('s','r','s',' '), HB_TAG('A','T','H',' ')}, /* Sarsi -> Athapaskan */ {HB_TAG('s','s','h',' '), HB_TAG('A','R','A',' ')}, /* Shihhi Arabic -> Arabic */ - {HB_TAG('s','s','l',' '), HB_TAG_NONE }, /* Western Sisaala != South Slavey */ - {HB_TAG('s','s','m',' '), HB_TAG_NONE }, /* Semnam != Southern Sami */ {HB_TAG('s','t','a',' '), HB_TAG('C','P','P',' ')}, /* Settla -> Creoles */ -/*{HB_TAG('s','t','q',' '), HB_TAG('S','T','Q',' ')},*/ /* Saterfriesisch -> Saterland Frisian */ -/*{HB_TAG('s','t','r',' '), HB_TAG('S','T','R',' ')},*/ /* Straits Salish */ {HB_TAG('s','t','v',' '), HB_TAG('S','I','G',' ')}, /* Silt'e -> Silte Gurage */ -/*{HB_TAG('s','u','k',' '), HB_TAG('S','U','K',' ')},*/ /* Sukuma */ {HB_TAG('s','u','q',' '), HB_TAG('S','U','R',' ')}, /* Suri */ - {HB_TAG('s','u','r',' '), HB_TAG_NONE }, /* Mwaghavul != Suri */ -/*{HB_TAG('s','v','a',' '), HB_TAG('S','V','A',' ')},*/ /* Svan */ {HB_TAG('s','v','c',' '), HB_TAG('C','P','P',' ')}, /* Vincentian Creole English -> Creoles */ - {HB_TAG('s','v','e',' '), HB_TAG_NONE }, /* Serili != Swedish */ {HB_TAG('s','w','b',' '), HB_TAG('C','M','R',' ')}, /* Maore Comorian -> Comorian */ {HB_TAG('s','w','c',' '), HB_TAG('S','W','K',' ')}, /* Congo Swahili -> Swahili */ {HB_TAG('s','w','h',' '), HB_TAG('S','W','K',' ')}, /* Swahili */ - {HB_TAG('s','w','k',' '), HB_TAG_NONE }, /* Malawi Sena != Swahili */ {HB_TAG('s','w','n',' '), HB_TAG('B','B','R',' ')}, /* Sawknah -> Berber */ {HB_TAG('s','w','v',' '), HB_TAG('M','A','W',' ')}, /* Shekhawati -> Marwari */ -/*{HB_TAG('s','x','u',' '), HB_TAG('S','X','U',' ')},*/ /* Upper Saxon */ {HB_TAG('s','y','c',' '), HB_TAG('S','Y','R',' ')}, /* Classical Syriac -> Syriac */ -/*{HB_TAG('s','y','l',' '), HB_TAG('S','Y','L',' ')},*/ /* Sylheti */ -/*{HB_TAG('s','y','r',' '), HB_TAG('S','Y','R',' ')},*/ /* Syriac [macrolanguage] */ -/*{HB_TAG('s','z','l',' '), HB_TAG('S','Z','L',' ')},*/ /* Silesian */ {HB_TAG('t','a','a',' '), HB_TAG('A','T','H',' ')}, /* Lower Tanana -> Athapaskan */ -/*{HB_TAG('t','a','b',' '), HB_TAG('T','A','B',' ')},*/ /* Tabassaran -> Tabasaran */ - {HB_TAG('t','a','j',' '), HB_TAG_NONE }, /* Eastern Tamang != Tajiki */ - {HB_TAG('t','a','q',' '), HB_TAG('T','A','Q',' ')}, /* Tamasheq */ - {HB_TAG('t','a','q',' '), HB_TAG('T','M','H',' ')}, /* Tamasheq -> Tamashek */ - {HB_TAG('t','a','q',' '), HB_TAG('B','B','R',' ')}, /* Tamasheq -> Berber */ {HB_TAG('t','a','s',' '), HB_TAG('C','P','P',' ')}, /* Tay Boi -> Creoles */ {HB_TAG('t','a','u',' '), HB_TAG('A','T','H',' ')}, /* Upper Tanana -> Athapaskan */ -/*{HB_TAG('t','b','v',' '), HB_TAG('T','B','V',' ')},*/ /* Tobo */ {HB_TAG('t','c','b',' '), HB_TAG('A','T','H',' ')}, /* Tanacross -> Athapaskan */ {HB_TAG('t','c','e',' '), HB_TAG('A','T','H',' ')}, /* Southern Tutchone -> Athapaskan */ {HB_TAG('t','c','h',' '), HB_TAG('C','P','P',' ')}, /* Turks And Caicos Creole English -> Creoles */ @@ -1481,115 +1062,54 @@ static const LangTag ot_languages3[] = { {HB_TAG('t','c','s',' '), HB_TAG('C','P','P',' ')}, /* Torres Strait Creole -> Creoles */ {HB_TAG('t','c','y',' '), HB_TAG('T','U','L',' ')}, /* Tulu */ {HB_TAG('t','c','z',' '), HB_TAG('Q','I','N',' ')}, /* Thado Chin -> Chin */ -/*{HB_TAG('t','d','c',' '), HB_TAG('T','D','C',' ')},*/ /* Emberá-Tadó */ -/*{HB_TAG('t','d','d',' '), HB_TAG('T','D','D',' ')},*/ /* Tai Nüa -> Dehong Dai */ {HB_TAG('t','d','x',' '), HB_TAG('M','L','G',' ')}, /* Tandroy-Mahafaly Malagasy -> Malagasy */ {HB_TAG('t','e','c',' '), HB_TAG('K','A','L',' ')}, /* Terik -> Kalenjin */ {HB_TAG('t','e','m',' '), HB_TAG('T','M','N',' ')}, /* Timne -> Temne */ -/*{HB_TAG('t','e','t',' '), HB_TAG('T','E','T',' ')},*/ /* Tetum */ {HB_TAG('t','e','z',' '), HB_TAG('B','B','R',' ')}, /* Tetserret -> Berber */ {HB_TAG('t','f','n',' '), HB_TAG('A','T','H',' ')}, /* Tanaina -> Athapaskan */ {HB_TAG('t','g','h',' '), HB_TAG('C','P','P',' ')}, /* Tobagonian Creole English -> Creoles */ {HB_TAG('t','g','j',' '), HB_TAG('N','I','S',' ')}, /* Tagin -> Nisi */ - {HB_TAG('t','g','n',' '), HB_TAG_NONE }, /* Tandaganon != Tongan */ - {HB_TAG('t','g','r',' '), HB_TAG_NONE }, /* Tareng != Tigre */ {HB_TAG('t','g','x',' '), HB_TAG('A','T','H',' ')}, /* Tagish -> Athapaskan */ - {HB_TAG('t','g','y',' '), HB_TAG_NONE }, /* Togoyo != Tigrinya */ -/*{HB_TAG('t','h','p',' '), HB_TAG('T','H','P',' ')},*/ /* Thompson */ {HB_TAG('t','h','t',' '), HB_TAG('A','T','H',' ')}, /* Tahltan -> Athapaskan */ - {HB_TAG('t','h','v',' '), HB_TAG('T','H','V',' ')}, /* Tahaggart Tamahaq */ - {HB_TAG('t','h','v',' '), HB_TAG('T','M','H',' ')}, /* Tahaggart Tamahaq -> Tamashek */ - {HB_TAG('t','h','v',' '), HB_TAG('B','B','R',' ')}, /* Tahaggart Tamahaq -> Berber */ - {HB_TAG('t','h','z',' '), HB_TAG('T','H','Z',' ')}, /* Tayart Tamajeq */ - {HB_TAG('t','h','z',' '), HB_TAG('T','M','H',' ')}, /* Tayart Tamajeq -> Tamashek */ - {HB_TAG('t','h','z',' '), HB_TAG('B','B','R',' ')}, /* Tayart Tamajeq -> Berber */ {HB_TAG('t','i','a',' '), HB_TAG('B','B','R',' ')}, /* Tidikelt Tamazight -> Berber */ {HB_TAG('t','i','g',' '), HB_TAG('T','G','R',' ')}, /* Tigre */ -/*{HB_TAG('t','i','v',' '), HB_TAG('T','I','V',' ')},*/ /* Tiv */ -/*{HB_TAG('t','j','l',' '), HB_TAG('T','J','L',' ')},*/ /* Tai Laing */ {HB_TAG('t','j','o',' '), HB_TAG('B','B','R',' ')}, /* Temacine Tamazight -> Berber */ {HB_TAG('t','k','g',' '), HB_TAG('M','L','G',' ')}, /* Tesaka Malagasy -> Malagasy */ - {HB_TAG('t','k','m',' '), HB_TAG_NONE }, /* Takelma != Turkmen */ -/*{HB_TAG('t','l','i',' '), HB_TAG('T','L','I',' ')},*/ /* Tlingit */ -/*{HB_TAG('t','l','y',' '), HB_TAG('T','L','Y',' ')},*/ /* Talysh */ {HB_TAG('t','m','g',' '), HB_TAG('C','P','P',' ')}, /* Ternateño -> Creoles */ - {HB_TAG('t','m','h',' '), HB_TAG('T','M','H',' ')}, /* Tamashek [macrolanguage] */ - {HB_TAG('t','m','h',' '), HB_TAG('B','B','R',' ')}, /* Tamashek [macrolanguage] -> Berber */ - {HB_TAG('t','m','n',' '), HB_TAG_NONE }, /* Taman (Indonesia) != Temne */ {HB_TAG('t','m','w',' '), HB_TAG('M','L','Y',' ')}, /* Temuan -> Malay */ - {HB_TAG('t','n','a',' '), HB_TAG_NONE }, /* Tacana != Tswana */ - {HB_TAG('t','n','e',' '), HB_TAG_NONE }, /* Tinoc Kallahan (retired code) != Tundra Enets */ - {HB_TAG('t','n','f',' '), HB_TAG('D','R','I',' ')}, /* Tangshewi (retired code) -> Dari */ - {HB_TAG('t','n','f',' '), HB_TAG('F','A','R',' ')}, /* Tangshewi (retired code) -> Persian */ - {HB_TAG('t','n','g',' '), HB_TAG_NONE }, /* Tobanga != Tonga */ {HB_TAG('t','o','d',' '), HB_TAG('T','O','D','0')}, /* Toma */ {HB_TAG('t','o','i',' '), HB_TAG('T','N','G',' ')}, /* Tonga (Zambia) */ {HB_TAG('t','o','j',' '), HB_TAG('M','Y','N',' ')}, /* Tojolabal -> Mayan */ {HB_TAG('t','o','l',' '), HB_TAG('A','T','H',' ')}, /* Tolowa -> Athapaskan */ {HB_TAG('t','o','r',' '), HB_TAG('B','A','D','0')}, /* Togbo-Vara Banda -> Banda */ - {HB_TAG('t','p','i',' '), HB_TAG('T','P','I',' ')}, /* Tok Pisin */ - {HB_TAG('t','p','i',' '), HB_TAG('C','P','P',' ')}, /* Tok Pisin -> Creoles */ {HB_TAG('t','r','f',' '), HB_TAG('C','P','P',' ')}, /* Trinidadian Creole English -> Creoles */ - {HB_TAG('t','r','k',' '), HB_TAG_NONE }, /* Turkic [collection] != Turkish */ - {HB_TAG('t','r','u',' '), HB_TAG('T','U','A',' ')}, /* Turoyo -> Turoyo Aramaic */ - {HB_TAG('t','r','u',' '), HB_TAG('S','Y','R',' ')}, /* Turoyo -> Syriac */ - {HB_TAG('t','s','g',' '), HB_TAG_NONE }, /* Tausug != Tsonga */ -/*{HB_TAG('t','s','j',' '), HB_TAG('T','S','J',' ')},*/ /* Tshangla */ {HB_TAG('t','t','c',' '), HB_TAG('M','Y','N',' ')}, /* Tektiteko -> Mayan */ {HB_TAG('t','t','m',' '), HB_TAG('A','T','H',' ')}, /* Northern Tutchone -> Athapaskan */ - {HB_TAG('t','t','q',' '), HB_TAG('T','T','Q',' ')}, /* Tawallammat Tamajaq */ - {HB_TAG('t','t','q',' '), HB_TAG('T','M','H',' ')}, /* Tawallammat Tamajaq -> Tamashek */ - {HB_TAG('t','t','q',' '), HB_TAG('B','B','R',' ')}, /* Tawallammat Tamajaq -> Berber */ - {HB_TAG('t','u','a',' '), HB_TAG_NONE }, /* Wiarumus != Turoyo Aramaic */ - {HB_TAG('t','u','l',' '), HB_TAG_NONE }, /* Tula != Tulu */ -/*{HB_TAG('t','u','m',' '), HB_TAG('T','U','M',' ')},*/ /* Tumbuka */ -/*{HB_TAG('t','u','s',' '), HB_TAG('T','U','S',' ')},*/ /* Tuscarora */ {HB_TAG('t','u','u',' '), HB_TAG('A','T','H',' ')}, /* Tututni -> Athapaskan */ - {HB_TAG('t','u','v',' '), HB_TAG_NONE }, /* Turkana != Tuvin */ {HB_TAG('t','u','y',' '), HB_TAG('K','A','L',' ')}, /* Tugen -> Kalenjin */ -/*{HB_TAG('t','v','l',' '), HB_TAG('T','V','L',' ')},*/ /* Tuvalu */ {HB_TAG('t','v','y',' '), HB_TAG('C','P','P',' ')}, /* Timor Pidgin -> Creoles */ {HB_TAG('t','x','c',' '), HB_TAG('A','T','H',' ')}, /* Tsetsaut -> Athapaskan */ {HB_TAG('t','x','y',' '), HB_TAG('M','L','G',' ')}, /* Tanosy Malagasy -> Malagasy */ {HB_TAG('t','y','v',' '), HB_TAG('T','U','V',' ')}, /* Tuvinian -> Tuvin */ -/*{HB_TAG('t','y','z',' '), HB_TAG('T','Y','Z',' ')},*/ /* Tày */ {HB_TAG('t','z','h',' '), HB_TAG('M','Y','N',' ')}, /* Tzeltal -> Mayan */ {HB_TAG('t','z','j',' '), HB_TAG('M','Y','N',' ')}, /* Tz'utujil -> Mayan */ - {HB_TAG('t','z','m',' '), HB_TAG('T','Z','M',' ')}, /* Central Atlas Tamazight -> Tamazight */ - {HB_TAG('t','z','m',' '), HB_TAG('B','B','R',' ')}, /* Central Atlas Tamazight -> Berber */ - {HB_TAG('t','z','o',' '), HB_TAG('T','Z','O',' ')}, /* Tzotzil */ - {HB_TAG('t','z','o',' '), HB_TAG('M','Y','N',' ')}, /* Tzotzil -> Mayan */ {HB_TAG('u','b','l',' '), HB_TAG('B','I','K',' ')}, /* Buhi'non Bikol -> Bikol */ -/*{HB_TAG('u','d','i',' '), HB_TAG('U','D','I',' ')},*/ /* Udi */ -/*{HB_TAG('u','d','m',' '), HB_TAG('U','D','M',' ')},*/ /* Udmurt */ {HB_TAG('u','k','i',' '), HB_TAG('K','U','I',' ')}, /* Kui (India) */ {HB_TAG('u','l','n',' '), HB_TAG('C','P','P',' ')}, /* Unserdeutsch -> Creoles */ -/*{HB_TAG('u','m','b',' '), HB_TAG('U','M','B',' ')},*/ /* Umbundu */ {HB_TAG('u','n','r',' '), HB_TAG('M','U','N',' ')}, /* Mundari */ {HB_TAG('u','r','k',' '), HB_TAG('M','L','Y',' ')}, /* Urak Lawoi' -> Malay */ {HB_TAG('u','s','p',' '), HB_TAG('M','Y','N',' ')}, /* Uspanteco -> Mayan */ {HB_TAG('u','z','n',' '), HB_TAG('U','Z','B',' ')}, /* Northern Uzbek -> Uzbek */ {HB_TAG('u','z','s',' '), HB_TAG('U','Z','B',' ')}, /* Southern Uzbek -> Uzbek */ {HB_TAG('v','a','p',' '), HB_TAG('Q','I','N',' ')}, /* Vaiphei -> Chin */ -/*{HB_TAG('v','e','c',' '), HB_TAG('V','E','C',' ')},*/ /* Venetian */ {HB_TAG('v','i','c',' '), HB_TAG('C','P','P',' ')}, /* Virgin Islands Creole English -> Creoles */ - {HB_TAG('v','i','t',' '), HB_TAG_NONE }, /* Viti != Vietnamese */ {HB_TAG('v','k','k',' '), HB_TAG('M','L','Y',' ')}, /* Kaur -> Malay */ {HB_TAG('v','k','p',' '), HB_TAG('C','P','P',' ')}, /* Korlai Creole Portuguese -> Creoles */ {HB_TAG('v','k','t',' '), HB_TAG('M','L','Y',' ')}, /* Tenggarong Kutai Malay -> Malay */ {HB_TAG('v','l','s',' '), HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */ {HB_TAG('v','m','w',' '), HB_TAG('M','A','K',' ')}, /* Makhuwa */ - {HB_TAG('v','r','o',' '), HB_TAG('V','R','O',' ')}, /* Võro */ - {HB_TAG('v','r','o',' '), HB_TAG('E','T','I',' ')}, /* Võro -> Estonian */ {HB_TAG('v','s','n',' '), HB_TAG('S','A','N',' ')}, /* Vedic Sanskrit -> Sanskrit */ - {HB_TAG('w','a','g',' '), HB_TAG_NONE }, /* Wa'ema != Wagdi */ -/*{HB_TAG('w','a','r',' '), HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */ -/*{HB_TAG('w','b','l',' '), HB_TAG('W','B','L',' ')},*/ /* Wakhi */ {HB_TAG('w','b','m',' '), HB_TAG('W','A',' ',' ')}, /* Wa */ - {HB_TAG('w','b','r',' '), HB_TAG('W','A','G',' ')}, /* Wagdi */ - {HB_TAG('w','b','r',' '), HB_TAG('R','A','J',' ')}, /* Wagdi -> Rajasthani */ -/*{HB_TAG('w','c','i',' '), HB_TAG('W','C','I',' ')},*/ /* Waci Gbe */ -/*{HB_TAG('w','d','t',' '), HB_TAG('W','D','T',' ')},*/ /* Wendat */ {HB_TAG('w','e','a',' '), HB_TAG('K','R','N',' ')}, /* Wewaw -> Karen */ {HB_TAG('w','e','s',' '), HB_TAG('C','P','P',' ')}, /* Cameroon Pidgin -> Creoles */ {HB_TAG('w','e','u',' '), HB_TAG('Q','I','N',' ')}, /* Rawngtu Chin -> Chin */ @@ -1599,62 +1119,31 @@ static const LangTag ot_languages3[] = { {HB_TAG('w','n','i',' '), HB_TAG('C','M','R',' ')}, /* Ndzwani Comorian -> Comorian */ {HB_TAG('w','r','y',' '), HB_TAG('M','A','W',' ')}, /* Merwari -> Marwari */ {HB_TAG('w','s','g',' '), HB_TAG('G','O','N',' ')}, /* Adilabad Gondi -> Gondi */ -/*{HB_TAG('w','t','m',' '), HB_TAG('W','T','M',' ')},*/ /* Mewati */ {HB_TAG('w','u','u',' '), HB_TAG('Z','H','S',' ')}, /* Wu Chinese -> Chinese, Simplified */ - {HB_TAG('w','y','a',' '), HB_TAG('W','D','T',' ')}, /* Wyandot (retired code) -> Wendat */ - {HB_TAG('w','y','a',' '), HB_TAG('W','Y','N',' ')}, /* Wyandot (retired code) */ -/*{HB_TAG('w','y','n',' '), HB_TAG('W','Y','N',' ')},*/ /* Wyandot */ - {HB_TAG('x','a','l',' '), HB_TAG('K','L','M',' ')}, /* Kalmyk */ - {HB_TAG('x','a','l',' '), HB_TAG('T','O','D',' ')}, /* Kalmyk -> Todo */ {HB_TAG('x','a','n',' '), HB_TAG('S','E','K',' ')}, /* Xamtanga -> Sekota */ - {HB_TAG('x','b','d',' '), HB_TAG_NONE }, /* Bindal != Lü */ -/*{HB_TAG('x','j','b',' '), HB_TAG('X','J','B',' ')},*/ /* Minjungbal -> Minjangbal */ -/*{HB_TAG('x','k','f',' '), HB_TAG('X','K','F',' ')},*/ /* Khengkha */ {HB_TAG('x','m','g',' '), HB_TAG('B','M','L',' ')}, /* Mengaka -> Bamileke */ - {HB_TAG('x','m','m',' '), HB_TAG('M','L','Y',' ')}, /* Manado Malay -> Malay */ - {HB_TAG('x','m','m',' '), HB_TAG('C','P','P',' ')}, /* Manado Malay -> Creoles */ {HB_TAG('x','m','v',' '), HB_TAG('M','L','G',' ')}, /* Antankarana Malagasy -> Malagasy */ {HB_TAG('x','m','w',' '), HB_TAG('M','L','G',' ')}, /* Tsimihety Malagasy -> Malagasy */ {HB_TAG('x','n','j',' '), HB_TAG('S','X','T',' ')}, /* Ngoni (Tanzania) -> Sutu */ {HB_TAG('x','n','q',' '), HB_TAG('S','X','T',' ')}, /* Ngoni (Mozambique) -> Sutu */ {HB_TAG('x','n','r',' '), HB_TAG('D','G','R',' ')}, /* Kangri -> Dogri (macrolanguage) */ -/*{HB_TAG('x','o','g',' '), HB_TAG('X','O','G',' ')},*/ /* Soga */ - {HB_TAG('x','p','e',' '), HB_TAG('X','P','E',' ')}, /* Liberia Kpelle -> Kpelle (Liberia) */ - {HB_TAG('x','p','e',' '), HB_TAG('K','P','L',' ')}, /* Liberia Kpelle -> Kpelle */ - {HB_TAG('x','s','l',' '), HB_TAG('S','S','L',' ')}, /* South Slavey */ - {HB_TAG('x','s','l',' '), HB_TAG('S','L','A',' ')}, /* South Slavey -> Slavey */ - {HB_TAG('x','s','l',' '), HB_TAG('A','T','H',' ')}, /* South Slavey -> Athapaskan */ {HB_TAG('x','s','t',' '), HB_TAG('S','I','G',' ')}, /* Silt'e (retired code) -> Silte Gurage */ -/*{HB_TAG('x','u','b',' '), HB_TAG('X','U','B',' ')},*/ /* Betta Kurumba -> Bette Kuruma */ -/*{HB_TAG('x','u','j',' '), HB_TAG('X','U','J',' ')},*/ /* Jennu Kurumba -> Jennu Kuruma */ {HB_TAG('x','u','p',' '), HB_TAG('A','T','H',' ')}, /* Upper Umpqua -> Athapaskan */ {HB_TAG('x','w','o',' '), HB_TAG('T','O','D',' ')}, /* Written Oirat -> Todo */ {HB_TAG('y','a','j',' '), HB_TAG('B','A','D','0')}, /* Banda-Yangere -> Banda */ - {HB_TAG('y','a','k',' '), HB_TAG_NONE }, /* Yakama != Sakha */ -/*{HB_TAG('y','a','o',' '), HB_TAG('Y','A','O',' ')},*/ /* Yao */ -/*{HB_TAG('y','a','p',' '), HB_TAG('Y','A','P',' ')},*/ /* Yapese */ - {HB_TAG('y','b','a',' '), HB_TAG_NONE }, /* Yala != Yoruba */ {HB_TAG('y','b','b',' '), HB_TAG('B','M','L',' ')}, /* Yemba -> Bamileke */ {HB_TAG('y','b','d',' '), HB_TAG('A','R','K',' ')}, /* Yangbye (retired code) -> Rakhine */ {HB_TAG('y','c','r',' '), HB_TAG('C','P','P',' ')}, /* Yilan Creole -> Creoles */ {HB_TAG('y','d','d',' '), HB_TAG('J','I','I',' ')}, /* Eastern Yiddish -> Yiddish */ -/*{HB_TAG('y','g','p',' '), HB_TAG('Y','G','P',' ')},*/ /* Gepo */ {HB_TAG('y','i','h',' '), HB_TAG('J','I','I',' ')}, /* Western Yiddish -> Yiddish */ - {HB_TAG('y','i','m',' '), HB_TAG_NONE }, /* Yimchungru Naga != Yi Modern */ -/*{HB_TAG('y','n','a',' '), HB_TAG('Y','N','A',' ')},*/ /* Aluo */ {HB_TAG('y','o','s',' '), HB_TAG('Q','I','N',' ')}, /* Yos (retired code) -> Chin */ {HB_TAG('y','u','a',' '), HB_TAG('M','Y','N',' ')}, /* Yucateco -> Mayan */ {HB_TAG('y','u','e',' '), HB_TAG('Z','H','H',' ')}, /* Yue Chinese -> Chinese, Traditional, Hong Kong SAR */ -/*{HB_TAG('y','u','f',' '), HB_TAG('Y','U','F',' ')},*/ /* Havasupai-Walapai-Yavapai */ -/*{HB_TAG('y','w','q',' '), HB_TAG('Y','W','Q',' ')},*/ /* Wuding-Luquan Yi */ {HB_TAG('z','c','h',' '), HB_TAG('Z','H','A',' ')}, /* Central Hongshuihe Zhuang -> Zhuang */ {HB_TAG('z','d','j',' '), HB_TAG('C','M','R',' ')}, /* Ngazidja Comorian -> Comorian */ -/*{HB_TAG('z','e','a',' '), HB_TAG('Z','E','A',' ')},*/ /* Zeeuws -> Zealandic */ {HB_TAG('z','e','h',' '), HB_TAG('Z','H','A',' ')}, /* Eastern Hongshuihe Zhuang -> Zhuang */ {HB_TAG('z','e','n',' '), HB_TAG('B','B','R',' ')}, /* Zenaga -> Berber */ {HB_TAG('z','g','b',' '), HB_TAG('Z','H','A',' ')}, /* Guibei Zhuang -> Zhuang */ - {HB_TAG('z','g','h',' '), HB_TAG('Z','G','H',' ')}, /* Standard Moroccan Tamazight */ - {HB_TAG('z','g','h',' '), HB_TAG('B','B','R',' ')}, /* Standard Moroccan Tamazight -> Berber */ {HB_TAG('z','g','m',' '), HB_TAG('Z','H','A',' ')}, /* Minz Zhuang -> Zhuang */ {HB_TAG('z','g','n',' '), HB_TAG('Z','H','A',' ')}, /* Guibian Zhuang -> Zhuang */ {HB_TAG('z','h','d',' '), HB_TAG('Z','H','A',' ')}, /* Dai Zhuang -> Zhuang */ @@ -1666,7 +1155,6 @@ static const LangTag ot_languages3[] = { {HB_TAG('z','l','q',' '), HB_TAG('Z','H','A',' ')}, /* Liuqian Zhuang -> Zhuang */ {HB_TAG('z','m','i',' '), HB_TAG('M','L','Y',' ')}, /* Negeri Sembilan Malay -> Malay */ {HB_TAG('z','m','z',' '), HB_TAG('B','A','D','0')}, /* Mbandja -> Banda */ - {HB_TAG('z','n','d',' '), HB_TAG_NONE }, /* Zande [collection] != Zande */ {HB_TAG('z','n','e',' '), HB_TAG('Z','N','D',' ')}, /* Zande */ {HB_TAG('z','o','m',' '), HB_TAG('Q','I','N',' ')}, /* Zou -> Chin */ {HB_TAG('z','q','e',' '), HB_TAG('Z','H','A',' ')}, /* Qiubei Zhuang -> Zhuang */ @@ -1677,9 +1165,439 @@ static const LangTag ot_languages3[] = { {HB_TAG('z','y','j',' '), HB_TAG('Z','H','A',' ')}, /* Youjiang Zhuang -> Zhuang */ {HB_TAG('z','y','n',' '), HB_TAG('Z','H','A',' ')}, /* Yongnan Zhuang -> Zhuang */ {HB_TAG('z','y','p',' '), HB_TAG('Q','I','N',' ')}, /* Zyphe Chin -> Chin */ -/*{HB_TAG('z','z','a',' '), HB_TAG('Z','Z','A',' ')},*/ /* Zazaki [macrolanguage] */ {HB_TAG('z','z','j',' '), HB_TAG('Z','H','A',' ')}, /* Zuojiang Zhuang -> Zhuang */ }; + +static const hb_tag_t ot_languages3_multi_values[] = { + HB_TAG('F','A','N',' '), /* Saint Lucian Creole French -> French Antillean */ + HB_TAG('C','P','P',' '), /* Saint Lucian Creole French -> Creoles */ + HB_TAG('A','C','R',' '), /* Achi */ + HB_TAG('M','Y','N',' '), /* Achi -> Mayan */ + HB_TAG('A','C','Y',' '), /* Cypriot Arabic */ + HB_TAG('A','R','A',' '), /* Cypriot Arabic -> Arabic */ + HB_TAG('S','W','A',' '), /* Assyrian Neo-Aramaic -> Swadaya Aramaic */ + HB_TAG('S','Y','R',' '), /* Assyrian Neo-Aramaic -> Syriac */ + HB_TAG('A','K','B',' '), /* Batak Angkola */ + HB_TAG('B','T','K',' '), /* Batak Angkola -> Batak */ + HB_TAG('M','O','R',' '), /* Moroccan Arabic -> Moroccan */ + HB_TAG('A','R','A',' '), /* Moroccan Arabic -> Arabic */ + HB_TAG('A','Z','B',' '), /* South Azerbaijani -> Torki */ + HB_TAG('A','Z','E',' '), /* South Azerbaijani -> Azerbaijani */ + HB_TAG('B','B','C',' '), /* Batak Toba */ + HB_TAG('B','T','K',' '), /* Batak Toba -> Batak */ + HB_TAG('B','G','Q',' '), /* Bagri */ + HB_TAG('R','A','J',' '), /* Bagri -> Rajasthani */ + HB_TAG('B','L','K',' '), /* Pa’o Karen */ + HB_TAG('K','R','N',' '), /* Pa'o Karen -> Karen */ + HB_TAG('B','T','D',' '), /* Batak Dairi (Pakpak) */ + HB_TAG('B','T','K',' '), /* Batak Dairi -> Batak */ + HB_TAG('B','T','M',' '), /* Batak Mandailing */ + HB_TAG('B','T','K',' '), /* Batak Mandailing -> Batak */ + HB_TAG('B','T','S',' '), /* Batak Simalungun */ + HB_TAG('B','T','K',' '), /* Batak Simalungun -> Batak */ + HB_TAG('B','T','X',' '), /* Batak Karo */ + HB_TAG('B','T','K',' '), /* Batak Karo -> Batak */ + HB_TAG('B','T','Z',' '), /* Batak Alas-Kluet */ + HB_TAG('B','T','K',' '), /* Batak Alas-Kluet -> Batak */ + HB_TAG('B','Y','V',' '), /* Medumba */ + HB_TAG('B','M','L',' '), /* Medumba -> Bamileke */ + HB_TAG('C','R','R',' '), /* Southern Carrier -> Carrier */ + HB_TAG('A','T','H',' '), /* Southern Carrier -> Athapaskan */ + HB_TAG('C','A','K',' '), /* Kaqchikel */ + HB_TAG('M','Y','N',' '), /* Kaqchikel -> Mayan */ + HB_TAG('C','B','K',' '), /* Chavacano -> Zamboanga Chavacano */ + HB_TAG('C','P','P',' '), /* Chavacano -> Creoles */ + HB_TAG('H','A','L',' '), /* Halam (Falam Chin) */ + HB_TAG('Q','I','N',' '), /* Falam Chin -> Chin */ + HB_TAG('H','M','A',' '), /* Mari (Russia) [macrolanguage] -> High Mari */ + HB_TAG('L','M','A',' '), /* Mari (Russia) [macrolanguage] -> Low Mari */ + HB_TAG('C','H','P',' '), /* Chipewyan */ + HB_TAG('S','A','Y',' '), /* Chipewyan -> Sayisi */ + HB_TAG('A','T','H',' '), /* Chipewyan -> Athapaskan */ + HB_TAG('Q','U','H',' '), /* Chilean Quechua (retired code) -> Quechua (Bolivia) */ + HB_TAG('Q','U','Z',' '), /* Chilean Quechua (retired code) -> Quechua */ + HB_TAG('E','C','R',' '), /* Southern East Cree -> Eastern Cree */ + HB_TAG('Y','C','R',' '), /* Southern East Cree -> Y-Cree */ + HB_TAG('C','R','E',' '), /* Southern East Cree -> Cree */ + HB_TAG('W','C','R',' '), /* Plains Cree -> West-Cree */ + HB_TAG('Y','C','R',' '), /* Plains Cree -> Y-Cree */ + HB_TAG('C','R','E',' '), /* Plains Cree -> Cree */ + HB_TAG('E','C','R',' '), /* Northern East Cree -> Eastern Cree */ + HB_TAG('Y','C','R',' '), /* Northern East Cree -> Y-Cree */ + HB_TAG('C','R','E',' '), /* Northern East Cree -> Cree */ + HB_TAG('M','C','R',' '), /* Moose Cree */ + HB_TAG('L','C','R',' '), /* Moose Cree -> L-Cree */ + HB_TAG('C','R','E',' '), /* Moose Cree -> Cree */ + HB_TAG('C','R','R',' '), /* Carrier */ + HB_TAG('A','T','H',' '), /* Carrier -> Athapaskan */ + HB_TAG('N','C','R',' '), /* Swampy Cree -> N-Cree */ + HB_TAG('N','H','C',' '), /* Swampy Cree -> Norway House Cree */ + HB_TAG('C','R','E',' '), /* Swampy Cree -> Cree */ + HB_TAG('D','C','R',' '), /* Woods Cree */ + HB_TAG('T','C','R',' '), /* Woods Cree -> TH-Cree */ + HB_TAG('C','R','E',' '), /* Woods Cree -> Cree */ + HB_TAG('S','L','A',' '), /* Slave (Athapascan) [macrolanguage] -> Slavey */ + HB_TAG('A','T','H',' '), /* Slave (Athapascan) [macrolanguage] -> Athapaskan */ + HB_TAG('D','G','O',' '), /* Dogri (individual language) */ + HB_TAG('D','G','R',' '), /* Dogri (macrolanguage) */ + HB_TAG('D','I','Q',' '), /* Dimli */ + HB_TAG('Z','Z','A',' '), /* Dimli -> Zazaki */ + HB_TAG('D','R','I',' '), /* Darwazi (retired code) -> Dari */ + HB_TAG('F','A','R',' '), /* Darwazi (retired code) -> Persian */ + HB_TAG('E','M','K',' '), /* Eastern Maninkakan */ + HB_TAG('M','N','K',' '), /* Eastern Maninkakan -> Maninka */ + HB_TAG('F','A','N','0'), /* Fang (Equatorial Guinea) */ + HB_TAG('B','T','I',' '), /* Fang (Equatorial Guinea) -> Beti */ + HB_TAG('F','A','T',' '), /* Fanti */ + HB_TAG('A','K','A',' '), /* Fanti -> Akan */ + HB_TAG('H','A','L',' '), /* Halam (Falam Chin) (retired code) */ + HB_TAG('Q','I','N',' '), /* Falam Chin (retired code) -> Chin */ + HB_TAG('F','M','P',' '), /* Fe’fe’ */ + HB_TAG('B','M','L',' '), /* Fe'fe' -> Bamileke */ + HB_TAG('F','T','A',' '), /* Pular -> Futa */ + HB_TAG('F','U','L',' '), /* Pular -> Fulah */ + HB_TAG('F','U','V',' '), /* Nigerian Fulfulde */ + HB_TAG('F','U','L',' '), /* Nigerian Fulfulde -> Fulah */ + HB_TAG('G','K','P',' '), /* Guinea Kpelle -> Kpelle (Guinea) */ + HB_TAG('K','P','L',' '), /* Guinea Kpelle -> Kpelle */ + HB_TAG('H','M','D',' '), /* Large Flowery Miao -> A-Hmao */ + HB_TAG('H','M','N',' '), /* Large Flowery Miao -> Hmong */ + HB_TAG('H','M','Z',' '), /* Hmong Shua -> Hmong Shuat */ + HB_TAG('H','M','N',' '), /* Hmong Shua -> Hmong */ + HB_TAG('H','A','R',' '), /* Hadothi -> Harauti */ + HB_TAG('R','A','J',' '), /* Hadothi -> Rajasthani */ + HB_TAG('I','N','U',' '), /* Eastern Canadian Inuktitut -> Inuktitut */ + HB_TAG('I','N','U','K'), /* Eastern Canadian Inuktitut -> Nunavik Inuktitut */ + HB_TAG('J','A','M',' '), /* Jamaican Creole English -> Jamaican Creole */ + HB_TAG('C','P','P',' '), /* Jamaican Creole English -> Creoles */ + HB_TAG('K','A','B','0'), /* Kabyle */ + HB_TAG('B','B','R',' '), /* Kabyle -> Berber */ + HB_TAG('K','H','K',' '), /* Khanty -> Khanty-Kazim */ + HB_TAG('K','H','S',' '), /* Khanty -> Khanty-Shurishkar */ + HB_TAG('K','H','V',' '), /* Khanty -> Khanty-Vakhi */ + HB_TAG('K','E','A',' '), /* Kabuverdianu (Crioulo) */ + HB_TAG('C','P','P',' '), /* Kabuverdianu -> Creoles */ + HB_TAG('K','E','K',' '), /* Kekchi */ + HB_TAG('M','Y','N',' '), /* Kekchí -> Mayan */ + HB_TAG('K','H','T',' '), /* Khamti -> Khamti Shan */ + HB_TAG('K','H','N',' '), /* Khamti -> Khamti Shan (Microsoft fonts) */ + HB_TAG('K','I','U',' '), /* Kirmanjki */ + HB_TAG('Z','Z','A',' '), /* Kirmanjki -> Zazaki */ + HB_TAG('K','J','P',' '), /* Pwo Eastern Karen -> Eastern Pwo Karen */ + HB_TAG('K','R','N',' '), /* Pwo Eastern Karen -> Karen */ + HB_TAG('K','O','P',' '), /* Komi-Permyak */ + HB_TAG('K','O','M',' '), /* Komi-Permyak -> Komi */ + HB_TAG('K','O','Z',' '), /* Komi-Zyrian */ + HB_TAG('K','O','M',' '), /* Komi-Zyrian -> Komi */ + HB_TAG('K','A','R',' '), /* Karachay-Balkar -> Karachay */ + HB_TAG('B','A','L',' '), /* Karachay-Balkar -> Balkar */ + HB_TAG('K','R','I',' '), /* Krio */ + HB_TAG('C','P','P',' '), /* Krio -> Creoles */ + HB_TAG('K','S','W',' '), /* S’gaw Karen */ + HB_TAG('K','R','N',' '), /* S'gaw Karen -> Karen */ + HB_TAG('K','V','Q',' '), /* Geba Karen */ + HB_TAG('K','R','N',' '), /* Geba Karen -> Karen */ + HB_TAG('K','Y','U',' '), /* Western Kayah */ + HB_TAG('K','R','N',' '), /* Western Kayah -> Karen */ + HB_TAG('M','I','Z',' '), /* Lushai -> Mizo */ + HB_TAG('Q','I','N',' '), /* Lushai -> Chin */ + HB_TAG('M','A','M',' '), /* Mam */ + HB_TAG('M','Y','N',' '), /* Mam -> Mayan */ + HB_TAG('M','L','Y',' '), /* North Moluccan Malay -> Malay */ + HB_TAG('C','P','P',' '), /* North Moluccan Malay -> Creoles */ + HB_TAG('M','F','A',' '), /* Pattani Malay */ + HB_TAG('M','L','Y',' '), /* Pattani Malay -> Malay */ + HB_TAG('M','F','E',' '), /* Morisyen */ + HB_TAG('C','P','P',' '), /* Morisyen -> Creoles */ + HB_TAG('M','I','N',' '), /* Minangkabau */ + HB_TAG('M','L','Y',' '), /* Minangkabau -> Malay */ + HB_TAG('M','L','N',' '), /* Western Maninkakan -> Malinke */ + HB_TAG('M','N','K',' '), /* Western Maninkakan -> Maninka */ + HB_TAG('M','N','D',' '), /* Mandinka */ + HB_TAG('M','N','K',' '), /* Mandinka -> Maninka */ + HB_TAG('M','O','N',' '), /* Mon */ + HB_TAG('M','O','N','T'), /* Mon -> Thailand Mon */ + HB_TAG('M','L','Y',' '), /* Sabah Malay -> Malay */ + HB_TAG('C','P','P',' '), /* Sabah Malay -> Creoles */ + HB_TAG('M','W','W',' '), /* Hmong Daw */ + HB_TAG('H','M','N',' '), /* Hmong Daw -> Hmong */ + HB_TAG('N','A','G',' '), /* Naga Pidgin -> Naga-Assamese */ + HB_TAG('C','P','P',' '), /* Naga Pidgin -> Creoles */ + HB_TAG('O','C','R',' '), /* Severn Ojibwa -> Oji-Cree */ + HB_TAG('O','J','B',' '), /* Severn Ojibwa -> Ojibway */ + HB_TAG('P','A','P','0'), /* Papiamento -> Papiamentu */ + HB_TAG('C','P','P',' '), /* Papiamento -> Creoles */ + HB_TAG('A','R','A',' '), /* Sudanese Creole Arabic -> Arabic */ + HB_TAG('C','P','P',' '), /* Sudanese Creole Arabic -> Creoles */ + HB_TAG('P','I','H',' '), /* Pitcairn-Norfolk -> Norfolk */ + HB_TAG('C','P','P',' '), /* Pitcairn-Norfolk -> Creoles */ + HB_TAG('P','O','H',' '), /* Poqomchi' -> Pocomchi */ + HB_TAG('M','Y','N',' '), /* Poqomchi' -> Mayan */ + HB_TAG('D','R','I',' '), /* Dari */ + HB_TAG('F','A','R',' '), /* Dari -> Persian */ + HB_TAG('P','W','O',' '), /* Pwo Western Karen -> Western Pwo Karen */ + HB_TAG('K','R','N',' '), /* Pwo Western Karen -> Karen */ + HB_TAG('Q','W','H',' '), /* Huallaga Huánuco Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Huallaga Huánuco Quechua -> Quechua */ + HB_TAG('Q','U','C',' '), /* K’iche’ */ + HB_TAG('M','Y','N',' '), /* K'iche' -> Mayan */ + HB_TAG('Q','V','I',' '), /* Calderón Highland Quichua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Calderón Highland Quichua -> Quechua */ + HB_TAG('Q','V','I',' '), /* Chimborazo Highland Quichua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Chimborazo Highland Quichua -> Quechua */ + HB_TAG('Q','U','H',' '), /* South Bolivian Quechua -> Quechua (Bolivia) */ + HB_TAG('Q','U','Z',' '), /* South Bolivian Quechua -> Quechua */ + HB_TAG('Q','U','H',' '), /* North Bolivian Quechua -> Quechua (Bolivia) */ + HB_TAG('Q','U','Z',' '), /* North Bolivian Quechua -> Quechua */ + HB_TAG('Q','V','I',' '), /* Southern Pastaza Quechua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Southern Pastaza Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Yanahuanca Pasco Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Yanahuanca Pasco Quechua -> Quechua */ + HB_TAG('Q','U','H',' '), /* Santiago del Estero Quichua -> Quechua (Bolivia) */ + HB_TAG('Q','U','Z',' '), /* Santiago del Estero Quichua -> Quechua */ + HB_TAG('Q','V','I',' '), /* Tena Lowland Quichua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Tena Lowland Quichua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Yauyos Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Yauyos Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Ambo-Pasco Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Ambo-Pasco Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua */ + HB_TAG('Q','V','I',' '), /* Imbabura Highland Quichua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Imbabura Highland Quichua -> Quechua */ + HB_TAG('Q','V','I',' '), /* Loja Highland Quichua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Loja Highland Quichua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Cajatambo North Lima Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Cajatambo North Lima Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Margos-Yarowilca-Lauricocha Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* North Junín Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* North Junín Quechua -> Quechua */ + HB_TAG('Q','V','I',' '), /* Napo Lowland Quechua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Napo Lowland Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Pacaraos Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Pacaraos Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Huaylla Wanca Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Huaylla Wanca Quechua -> Quechua */ + HB_TAG('Q','V','I',' '), /* Northern Pastaza Quichua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Northern Pastaza Quichua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Corongo Ancash Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Corongo Ancash Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Huaylas Ancash Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Huaylas Ancash Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Sihuas Ancash Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Sihuas Ancash Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Chiquián Ancash Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Chiquián Ancash Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Chincha Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Chincha Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Panao Huánuco Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Panao Huánuco Quechua -> Quechua */ + HB_TAG('Q','V','I',' '), /* Salasaca Highland Quichua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Salasaca Highland Quichua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Northern Conchucos Ancash Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Southern Conchucos Ancash Quechua -> Quechua */ + HB_TAG('Q','V','I',' '), /* Cañar Highland Quichua -> Quechua (Ecuador) */ + HB_TAG('Q','U','Z',' '), /* Cañar Highland Quichua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Santa Ana de Tusi Pasco Quechua -> Quechua */ + HB_TAG('Q','W','H',' '), /* Jauja Wanca Quechua -> Quechua (Peru) */ + HB_TAG('Q','U','Z',' '), /* Jauja Wanca Quechua -> Quechua */ + HB_TAG('R','I','F',' '), /* Tarifit */ + HB_TAG('B','B','R',' '), /* Tarifit -> Berber */ + HB_TAG('R','M','Y',' '), /* Vlax Romani */ + HB_TAG('R','O','Y',' '), /* Vlax Romani -> Romany */ + HB_TAG('S','C','S',' '), /* North Slavey */ + HB_TAG('S','L','A',' '), /* North Slavey -> Slavey */ + HB_TAG('A','T','H',' '), /* North Slavey -> Athapaskan */ + HB_TAG('S','F','M',' '), /* Small Flowery Miao */ + HB_TAG('H','M','N',' '), /* Small Flowery Miao -> Hmong */ + HB_TAG('S','H','I',' '), /* Tachelhit */ + HB_TAG('B','B','R',' '), /* Tachelhit -> Berber */ + HB_TAG('T','A','Q',' '), /* Tamasheq */ + HB_TAG('T','M','H',' '), /* Tamasheq -> Tamashek */ + HB_TAG('B','B','R',' '), /* Tamasheq -> Berber */ + HB_TAG('T','H','V',' '), /* Tahaggart Tamahaq */ + HB_TAG('T','M','H',' '), /* Tahaggart Tamahaq -> Tamashek */ + HB_TAG('B','B','R',' '), /* Tahaggart Tamahaq -> Berber */ + HB_TAG('T','H','Z',' '), /* Tayart Tamajeq */ + HB_TAG('T','M','H',' '), /* Tayart Tamajeq -> Tamashek */ + HB_TAG('B','B','R',' '), /* Tayart Tamajeq -> Berber */ + HB_TAG('T','M','H',' '), /* Tamashek [macrolanguage] */ + HB_TAG('B','B','R',' '), /* Tamashek [macrolanguage] -> Berber */ + HB_TAG('D','R','I',' '), /* Tangshewi (retired code) -> Dari */ + HB_TAG('F','A','R',' '), /* Tangshewi (retired code) -> Persian */ + HB_TAG('T','P','I',' '), /* Tok Pisin */ + HB_TAG('C','P','P',' '), /* Tok Pisin -> Creoles */ + HB_TAG('T','U','A',' '), /* Turoyo -> Turoyo Aramaic */ + HB_TAG('S','Y','R',' '), /* Turoyo -> Syriac */ + HB_TAG('T','T','Q',' '), /* Tawallammat Tamajaq */ + HB_TAG('T','M','H',' '), /* Tawallammat Tamajaq -> Tamashek */ + HB_TAG('B','B','R',' '), /* Tawallammat Tamajaq -> Berber */ + HB_TAG('T','Z','M',' '), /* Central Atlas Tamazight -> Tamazight */ + HB_TAG('B','B','R',' '), /* Central Atlas Tamazight -> Berber */ + HB_TAG('T','Z','O',' '), /* Tzotzil */ + HB_TAG('M','Y','N',' '), /* Tzotzil -> Mayan */ + HB_TAG('V','R','O',' '), /* Võro */ + HB_TAG('E','T','I',' '), /* Võro -> Estonian */ + HB_TAG('W','A','G',' '), /* Wagdi */ + HB_TAG('R','A','J',' '), /* Wagdi -> Rajasthani */ + HB_TAG('W','D','T',' '), /* Wyandot (retired code) -> Wendat */ + HB_TAG('W','Y','N',' '), /* Wyandot (retired code) */ + HB_TAG('K','L','M',' '), /* Kalmyk */ + HB_TAG('T','O','D',' '), /* Kalmyk -> Todo */ + HB_TAG('M','L','Y',' '), /* Manado Malay -> Malay */ + HB_TAG('C','P','P',' '), /* Manado Malay -> Creoles */ + HB_TAG('X','P','E',' '), /* Liberia Kpelle -> Kpelle (Liberia) */ + HB_TAG('K','P','L',' '), /* Liberia Kpelle -> Kpelle */ + HB_TAG('S','S','L',' '), /* South Slavey */ + HB_TAG('S','L','A',' '), /* South Slavey -> Slavey */ + HB_TAG('A','T','H',' '), /* South Slavey -> Athapaskan */ + HB_TAG('Z','G','H',' '), /* Standard Moroccan Tamazight */ + HB_TAG('B','B','R',' '), /* Standard Moroccan Tamazight -> Berber */ +}; + +static const LangTagRange ot_languages3_multi[] = { + {HB_TAG('a','c','f',' '), 0, 2}, /* Saint Lucian Creole French -> French Antillean */ + {HB_TAG('a','c','r',' '), 2, 2}, /* Achi */ + {HB_TAG('a','c','y',' '), 4, 2}, /* Cypriot Arabic */ + {HB_TAG('a','i','i',' '), 6, 2}, /* Assyrian Neo-Aramaic -> Swadaya Aramaic */ + {HB_TAG('a','k','b',' '), 8, 2}, /* Batak Angkola */ + {HB_TAG('a','r','y',' '), 10, 2}, /* Moroccan Arabic -> Moroccan */ + {HB_TAG('a','z','b',' '), 12, 2}, /* South Azerbaijani -> Torki */ + {HB_TAG('b','b','c',' '), 14, 2}, /* Batak Toba */ + {HB_TAG('b','g','q',' '), 16, 2}, /* Bagri */ + {HB_TAG('b','l','k',' '), 18, 2}, /* Pa’o Karen */ + {HB_TAG('b','t','d',' '), 20, 2}, /* Batak Dairi (Pakpak) */ + {HB_TAG('b','t','m',' '), 22, 2}, /* Batak Mandailing */ + {HB_TAG('b','t','s',' '), 24, 2}, /* Batak Simalungun */ + {HB_TAG('b','t','x',' '), 26, 2}, /* Batak Karo */ + {HB_TAG('b','t','z',' '), 28, 2}, /* Batak Alas-Kluet */ + {HB_TAG('b','y','v',' '), 30, 2}, /* Medumba */ + {HB_TAG('c','a','f',' '), 32, 2}, /* Southern Carrier -> Carrier */ + {HB_TAG('c','a','k',' '), 34, 2}, /* Kaqchikel */ + {HB_TAG('c','b','k',' '), 36, 2}, /* Chavacano -> Zamboanga Chavacano */ + {HB_TAG('c','f','m',' '), 38, 2}, /* Halam (Falam Chin) */ + {HB_TAG('c','h','m',' '), 40, 2}, /* Mari (Russia) [macrolanguage] -> High Mari */ + {HB_TAG('c','h','p',' '), 42, 3}, /* Chipewyan */ + {HB_TAG('c','q','u',' '), 45, 2}, /* Chilean Quechua (retired code) -> Quechua (Bolivia) */ + {HB_TAG('c','r','j',' '), 47, 3}, /* Southern East Cree -> Eastern Cree */ + {HB_TAG('c','r','k',' '), 50, 3}, /* Plains Cree -> West-Cree */ + {HB_TAG('c','r','l',' '), 53, 3}, /* Northern East Cree -> Eastern Cree */ + {HB_TAG('c','r','m',' '), 56, 3}, /* Moose Cree */ + {HB_TAG('c','r','x',' '), 59, 2}, /* Carrier */ + {HB_TAG('c','s','w',' '), 61, 3}, /* Swampy Cree -> N-Cree */ + {HB_TAG('c','w','d',' '), 64, 3}, /* Woods Cree */ + {HB_TAG('d','e','n',' '), 67, 2}, /* Slave (Athapascan) [macrolanguage] -> Slavey */ + {HB_TAG('d','g','o',' '), 69, 2}, /* Dogri (individual language) */ + {HB_TAG('d','i','q',' '), 71, 2}, /* Dimli */ + {HB_TAG('d','r','w',' '), 73, 2}, /* Darwazi (retired code) -> Dari */ + {HB_TAG('e','m','k',' '), 75, 2}, /* Eastern Maninkakan */ + {HB_TAG('f','a','n',' '), 77, 2}, /* Fang (Equatorial Guinea) */ + {HB_TAG('f','a','t',' '), 79, 2}, /* Fanti */ + {HB_TAG('f','l','m',' '), 81, 2}, /* Halam (Falam Chin) (retired code) */ + {HB_TAG('f','m','p',' '), 83, 2}, /* Fe’fe’ */ + {HB_TAG('f','u','f',' '), 85, 2}, /* Pular -> Futa */ + {HB_TAG('f','u','v',' '), 87, 2}, /* Nigerian Fulfulde */ + {HB_TAG('g','k','p',' '), 89, 2}, /* Guinea Kpelle -> Kpelle (Guinea) */ + {HB_TAG('h','m','d',' '), 91, 2}, /* Large Flowery Miao -> A-Hmao */ + {HB_TAG('h','m','z',' '), 93, 2}, /* Hmong Shua -> Hmong Shuat */ + {HB_TAG('h','o','j',' '), 95, 2}, /* Hadothi -> Harauti */ + {HB_TAG('i','k','e',' '), 97, 2}, /* Eastern Canadian Inuktitut -> Inuktitut */ + {HB_TAG('j','a','m',' '), 99, 2}, /* Jamaican Creole English -> Jamaican Creole */ + {HB_TAG('k','a','b',' '), 101, 2}, /* Kabyle */ + {HB_TAG('k','c','a',' '), 103, 3}, /* Khanty -> Khanty-Kazim */ + {HB_TAG('k','e','a',' '), 106, 2}, /* Kabuverdianu (Crioulo) */ + {HB_TAG('k','e','k',' '), 108, 2}, /* Kekchi */ + {HB_TAG('k','h','t',' '), 110, 2}, /* Khamti -> Khamti Shan */ + {HB_TAG('k','i','u',' '), 112, 2}, /* Kirmanjki */ + {HB_TAG('k','j','p',' '), 114, 2}, /* Pwo Eastern Karen -> Eastern Pwo Karen */ + {HB_TAG('k','o','i',' '), 116, 2}, /* Komi-Permyak */ + {HB_TAG('k','p','v',' '), 118, 2}, /* Komi-Zyrian */ + {HB_TAG('k','r','c',' '), 120, 2}, /* Karachay-Balkar -> Karachay */ + {HB_TAG('k','r','i',' '), 122, 2}, /* Krio */ + {HB_TAG('k','s','w',' '), 124, 2}, /* S’gaw Karen */ + {HB_TAG('k','v','q',' '), 126, 2}, /* Geba Karen */ + {HB_TAG('k','y','u',' '), 128, 2}, /* Western Kayah */ + {HB_TAG('l','u','s',' '), 130, 2}, /* Lushai -> Mizo */ + {HB_TAG('m','a','m',' '), 132, 2}, /* Mam */ + {HB_TAG('m','a','x',' '), 134, 2}, /* North Moluccan Malay -> Malay */ + {HB_TAG('m','f','a',' '), 136, 2}, /* Pattani Malay */ + {HB_TAG('m','f','e',' '), 138, 2}, /* Morisyen */ + {HB_TAG('m','i','n',' '), 140, 2}, /* Minangkabau */ + {HB_TAG('m','l','q',' '), 142, 2}, /* Western Maninkakan -> Malinke */ + {HB_TAG('m','n','k',' '), 144, 2}, /* Mandinka */ + {HB_TAG('m','n','w',' '), 146, 2}, /* Mon */ + {HB_TAG('m','s','i',' '), 148, 2}, /* Sabah Malay -> Malay */ + {HB_TAG('m','w','w',' '), 150, 2}, /* Hmong Daw */ + {HB_TAG('n','a','g',' '), 152, 2}, /* Naga Pidgin -> Naga-Assamese */ + {HB_TAG('o','j','s',' '), 154, 2}, /* Severn Ojibwa -> Oji-Cree */ + {HB_TAG('p','a','p',' '), 156, 2}, /* Papiamento -> Papiamentu */ + {HB_TAG('p','g','a',' '), 158, 2}, /* Sudanese Creole Arabic -> Arabic */ + {HB_TAG('p','i','h',' '), 160, 2}, /* Pitcairn-Norfolk -> Norfolk */ + {HB_TAG('p','o','h',' '), 162, 2}, /* Poqomchi' -> Pocomchi */ + {HB_TAG('p','r','s',' '), 164, 2}, /* Dari */ + {HB_TAG('p','w','o',' '), 166, 2}, /* Pwo Western Karen -> Western Pwo Karen */ + {HB_TAG('q','u','b',' '), 168, 2}, /* Huallaga Huánuco Quechua -> Quechua (Peru) */ + {HB_TAG('q','u','c',' '), 170, 2}, /* K’iche’ */ + {HB_TAG('q','u','d',' '), 172, 2}, /* Calderón Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','u','g',' '), 174, 2}, /* Chimborazo Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','u','h',' '), 176, 2}, /* South Bolivian Quechua -> Quechua (Bolivia) */ + {HB_TAG('q','u','l',' '), 178, 2}, /* North Bolivian Quechua -> Quechua (Bolivia) */ + {HB_TAG('q','u','p',' '), 180, 2}, /* Southern Pastaza Quechua -> Quechua (Ecuador) */ + {HB_TAG('q','u','r',' '), 182, 2}, /* Yanahuanca Pasco Quechua -> Quechua (Peru) */ + {HB_TAG('q','u','s',' '), 184, 2}, /* Santiago del Estero Quichua -> Quechua (Bolivia) */ + {HB_TAG('q','u','w',' '), 186, 2}, /* Tena Lowland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','u','x',' '), 188, 2}, /* Yauyos Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','a',' '), 190, 2}, /* Ambo-Pasco Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','h',' '), 192, 2}, /* Huamalíes-Dos de Mayo Huánuco Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','i',' '), 194, 2}, /* Imbabura Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','v','j',' '), 196, 2}, /* Loja Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','v','l',' '), 198, 2}, /* Cajatambo North Lima Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','m',' '), 200, 2}, /* Margos-Yarowilca-Lauricocha Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','n',' '), 202, 2}, /* North Junín Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','o',' '), 204, 2}, /* Napo Lowland Quechua -> Quechua (Ecuador) */ + {HB_TAG('q','v','p',' '), 206, 2}, /* Pacaraos Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','w',' '), 208, 2}, /* Huaylla Wanca Quechua -> Quechua (Peru) */ + {HB_TAG('q','v','z',' '), 210, 2}, /* Northern Pastaza Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','w','a',' '), 212, 2}, /* Corongo Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','w','h',' '), 214, 2}, /* Huaylas Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','w','s',' '), 216, 2}, /* Sihuas Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','a',' '), 218, 2}, /* Chiquián Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','c',' '), 220, 2}, /* Chincha Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','h',' '), 222, 2}, /* Panao Huánuco Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','l',' '), 224, 2}, /* Salasaca Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','x','n',' '), 226, 2}, /* Northern Conchucos Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','o',' '), 228, 2}, /* Southern Conchucos Ancash Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','r',' '), 230, 2}, /* Cañar Highland Quichua -> Quechua (Ecuador) */ + {HB_TAG('q','x','t',' '), 232, 2}, /* Santa Ana de Tusi Pasco Quechua -> Quechua (Peru) */ + {HB_TAG('q','x','w',' '), 234, 2}, /* Jauja Wanca Quechua -> Quechua (Peru) */ + {HB_TAG('r','i','f',' '), 236, 2}, /* Tarifit */ + {HB_TAG('r','m','y',' '), 238, 2}, /* Vlax Romani */ + {HB_TAG('s','c','s',' '), 240, 3}, /* North Slavey */ + {HB_TAG('s','f','m',' '), 243, 2}, /* Small Flowery Miao */ + {HB_TAG('s','h','i',' '), 245, 2}, /* Tachelhit */ + {HB_TAG('t','a','q',' '), 247, 3}, /* Tamasheq */ + {HB_TAG('t','h','v',' '), 250, 3}, /* Tahaggart Tamahaq */ + {HB_TAG('t','h','z',' '), 253, 3}, /* Tayart Tamajeq */ + {HB_TAG('t','m','h',' '), 256, 2}, /* Tamashek [macrolanguage] */ + {HB_TAG('t','n','f',' '), 258, 2}, /* Tangshewi (retired code) -> Dari */ + {HB_TAG('t','p','i',' '), 260, 2}, /* Tok Pisin */ + {HB_TAG('t','r','u',' '), 262, 2}, /* Turoyo -> Turoyo Aramaic */ + {HB_TAG('t','t','q',' '), 264, 3}, /* Tawallammat Tamajaq */ + {HB_TAG('t','z','m',' '), 267, 2}, /* Central Atlas Tamazight -> Tamazight */ + {HB_TAG('t','z','o',' '), 269, 2}, /* Tzotzil */ + {HB_TAG('v','r','o',' '), 271, 2}, /* Võro */ + {HB_TAG('w','b','r',' '), 273, 2}, /* Wagdi */ + {HB_TAG('w','y','a',' '), 275, 2}, /* Wyandot (retired code) -> Wendat */ + {HB_TAG('x','a','l',' '), 277, 2}, /* Kalmyk */ + {HB_TAG('x','m','m',' '), 279, 2}, /* Manado Malay -> Malay */ + {HB_TAG('x','p','e',' '), 281, 2}, /* Liberia Kpelle -> Kpelle (Liberia) */ + {HB_TAG('x','s','l',' '), 283, 3}, /* South Slavey */ + {HB_TAG('z','g','h',' '), 286, 2}, /* Standard Moroccan Tamazight */ +}; #endif /** @@ -3205,7 +3123,7 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag) case HB_TAG('Z','H','T','M'): /* Chinese, Traditional, Macao SAR */ return hb_language_from_string ("zh-MO", -1); /* Chinese [macrolanguage]; Macao */ case HB_TAG('Z','Z','A',' '): /* Zazaki */ - return hb_language_from_string ("zza", -1); /* Zazaki [macrolanguage] */ + return hb_language_from_string ("zza", -1); /* Zaza [macrolanguage] */ default: return HB_LANGUAGE_INVALID; } diff --git a/thirdparty/harfbuzz/src/hb-ot-tag.cc b/thirdparty/harfbuzz/src/hb-ot-tag.cc index b70db154c5..900f37ab42 100644 --- a/thirdparty/harfbuzz/src/hb-ot-tag.cc +++ b/thirdparty/harfbuzz/src/hb-ot-tag.cc @@ -234,6 +234,29 @@ lang_matches (const char *lang_str, (lang_str[spec_len] == '\0' || lang_str[spec_len] == '-'); } +static bool +bfind_tag (const hb_tag_t *array, + unsigned len, + hb_tag_t key) +{ + unsigned min = 0; + unsigned max = len; + + while (min < max) + { + unsigned mid = min + (max - min) / 2; + hb_tag_t val = array[mid]; + if (key < val) + max = mid; + else if (key > val) + min = mid + 1; + else + return true; + } + + return false; +} + struct LangTag { hb_tag_t language; @@ -247,6 +270,20 @@ struct LangTag { return cmp (that->language); } }; +struct LangTagRange +{ + hb_tag_t language; + uint16_t offset; + uint8_t count; + + int cmp (hb_tag_t a) const + { + return a < this->language ? -1 : a > this->language ? +1 : 0; + } + int cmp (const LangTagRange *that) const + { return cmp (that->language); } +}; + #include "hb-ot-tag-table.hh" /* The corresponding languages IDs for the following IDs are unclear, @@ -307,46 +344,84 @@ hb_ot_tags_from_language (const char *lang_str, lang_str = s + 1; } #endif - const LangTag *ot_languages = nullptr; - unsigned ot_languages_len = 0; const char *dash = strchr (lang_str, '-'); unsigned first_len = dash ? dash - lang_str : limit - lang_str; + hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len); + if (first_len == 2) { - ot_languages = ot_languages2; - ot_languages_len = ARRAY_LENGTH (ot_languages2); + static hb_atomic_t last_tag_idx_2 = 0; /* Poor man's cache. */ + unsigned tag_idx = last_tag_idx_2; + + if (likely (tag_idx < ARRAY_LENGTH (ot_languages2) && + ot_languages2[tag_idx].language == lang_tag) || + hb_sorted_array (ot_languages2).bfind (lang_tag, &tag_idx)) + { + last_tag_idx_2 = tag_idx; + unsigned int i; + while (tag_idx != 0 && + ot_languages2[tag_idx].language == ot_languages2[tag_idx - 1].language) + tag_idx--; + for (i = 0; + i < *count && + tag_idx + i < ARRAY_LENGTH (ot_languages2) && + ot_languages2[tag_idx + i].tag != HB_TAG_NONE && + ot_languages2[tag_idx + i].language == ot_languages2[tag_idx].language; + i++) + tags[i] = ot_languages2[tag_idx + i].tag; + *count = i; + return; + } } #ifndef HB_NO_LANGUAGE_LONG else if (first_len == 3) { - ot_languages = ot_languages3; - ot_languages_len = ARRAY_LENGTH (ot_languages3); - } -#endif + static hb_atomic_t last_tag_idx_3 = 0; /* Poor man's cache. */ + unsigned tag_idx = last_tag_idx_3; - hb_tag_t lang_tag = hb_tag_from_string (lang_str, first_len); + if (likely (tag_idx < ARRAY_LENGTH (ot_languages3) && + ot_languages3[tag_idx].language == lang_tag) || + hb_sorted_array (ot_languages3).bfind (lang_tag, &tag_idx)) + { + last_tag_idx_3 = tag_idx; + if (*count) + { + tags[0] = ot_languages3[tag_idx].tag; + *count = 1; + } + else + *count = 0; + return; + } - static hb_atomic_t last_tag_idx = 0; /* Poor man's cache. */ - unsigned tag_idx = last_tag_idx; + static hb_atomic_t last_tag_idx_3_multi = 0; /* Poor man's cache. */ + unsigned multi_tag_idx = last_tag_idx_3_multi; - if (likely (tag_idx < ot_languages_len && ot_languages[tag_idx].language == lang_tag) || - hb_sorted_array (ot_languages, ot_languages_len).bfind (lang_tag, &tag_idx)) - { - last_tag_idx = tag_idx; - unsigned int i; - while (tag_idx != 0 && - ot_languages[tag_idx].language == ot_languages[tag_idx - 1].language) - tag_idx--; - for (i = 0; - i < *count && - tag_idx + i < ot_languages_len && - ot_languages[tag_idx + i].tag != HB_TAG_NONE && - ot_languages[tag_idx + i].language == ot_languages[tag_idx].language; - i++) - tags[i] = ot_languages[tag_idx + i].tag; - *count = i; + if (likely (multi_tag_idx < ARRAY_LENGTH (ot_languages3_multi) && + ot_languages3_multi[multi_tag_idx].language == lang_tag) || + hb_sorted_array (ot_languages3_multi).bfind (lang_tag, &multi_tag_idx)) + { + last_tag_idx_3_multi = multi_tag_idx; + const LangTagRange &range = ot_languages3_multi[multi_tag_idx]; + unsigned int i; + for (i = 0; i < *count && i < range.count; i++) + tags[i] = ot_languages3_multi_values[range.offset + i]; + *count = i; + return; + } + + if (bfind_tag (ot_languages3_blocked, ARRAY_LENGTH (ot_languages3_blocked), lang_tag)) + { + *count = 0; + return; + } + + /* Assume it's ISO-639-3 and upper-case and use it. */ + tags[0] = lang_tag & ~0x20202000u; + *count = 1; return; } +#endif } #ifndef HB_NO_LANGUAGE_LONG @@ -525,6 +600,13 @@ hb_ot_tag_to_language (hb_tag_t tag) hb_tag_to_string (ot_languages3[i].language, buf); return hb_language_from_string (buf, 3); } + for (i = 0; i < ARRAY_LENGTH (ot_languages3_multi); i++) + for (unsigned int j = 0; j < ot_languages3_multi[i].count; j++) + if (ot_languages3_multi_values[ot_languages3_multi[i].offset + j] == tag) + { + hb_tag_to_string (ot_languages3_multi[i].language, buf); + return hb_language_from_string (buf, 3); + } #endif /* Return a custom language in the form of "x-hbot-AABBCCDD". @@ -642,6 +724,23 @@ test_langs_sorted () abort(); } } + for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3_multi); i++) + { + int c = ot_languages3_multi[i].cmp (&ot_languages3_multi[i - 1]); + if (c > 0) + { + fprintf (stderr, "ot_languages3_multi not sorted at index %u: %08x %d %08x\n", + i, ot_languages3_multi[i-1].language, c, ot_languages3_multi[i].language); + abort(); + } + } + for (unsigned int i = 1; i < ARRAY_LENGTH (ot_languages3_blocked); i++) + if (ot_languages3_blocked[i] < ot_languages3_blocked[i - 1]) + { + fprintf (stderr, "ot_languages3_blocked not sorted at index %u: %08x < %08x\n", + i, ot_languages3_blocked[i], ot_languages3_blocked[i - 1]); + abort(); + } #endif } diff --git a/thirdparty/harfbuzz/src/hb-ot-var-common.hh b/thirdparty/harfbuzz/src/hb-ot-var-common.hh index 174816193e..74ce1b2855 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-common.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-common.hh @@ -409,6 +409,13 @@ struct tuple_delta_t return; } + if (!axis_limit.is_point () && + !(-1.0 <= axis_limit.minimum && + axis_limit.minimum <= axis_limit.middle && + axis_limit.middle <= axis_limit.maximum && + axis_limit.maximum <= +1.0)) + return; + rebase_tent_result_t &solutions = scratch.first; rebase_tent (*tent, axis_limit, axis_triple_distances, solutions, scratch.second); for (unsigned i = 0; i < solutions.length; i++) @@ -1700,6 +1707,16 @@ struct item_variations_t const hb_map_t& get_varidx_map () const { return varidx_map; } + bool add_vardata_encoding_for_testing (hb_vector_t*> &&rows, + unsigned num_cols) + { + encodings.push (delta_row_encoding_t (std::move (rows), num_cols)); + return !encodings.in_error (); + } + + bool compile_varidx_map_for_testing (const hb_hashmap_t*>& front_mapping) + { return compile_varidx_map (front_mapping); } + bool instantiate (const ItemVariationStore& varStore, const hb_subset_plan_t *plan, bool optimize=true, @@ -2046,27 +2063,46 @@ struct item_variations_t { /* full encoding_row -> new VarIdxes mapping */ hb_hashmap_t*, unsigned> back_mapping; + hb_vector_t split_encodings; - for (unsigned major = 0; major < encodings.length; major++) + for (unsigned i = 0; i < encodings.length; i++) { - delta_row_encoding_t& encoding = encodings[major]; + delta_row_encoding_t& encoding = encodings[i]; /* just sanity check, this shouldn't happen */ if (encoding.is_empty ()) return false; unsigned num_rows = encoding.items.length; + unsigned num_cols = encoding.chars.length; /* sort rows, make result deterministic */ encoding.items.qsort (_cmp_row); - /* compile old to new var_idxes mapping */ - for (unsigned minor = 0; minor < num_rows; minor++) + for (unsigned start = 0; start < num_rows; start += 0xFFFFu) { - unsigned new_varidx = (major << 16) + minor; - back_mapping.set (encoding.items.arrayZ[minor], new_varidx); + unsigned chunk_len = hb_min (num_rows - start, 0xFFFFu); + hb_vector_t*> rows; + + if (!rows.alloc (chunk_len)) + return false; + + unsigned major = split_encodings.length; + for (unsigned minor = 0; minor < chunk_len; minor++) + { + const hb_vector_t *row = encoding.items.arrayZ[start + minor]; + rows.push (row); + if (!back_mapping.set (row, (major << 16) + minor)) + return false; + } + + split_encodings.push (delta_row_encoding_t (std::move (rows), num_cols)); } } + encodings = std::move (split_encodings); + if (encodings.in_error () || back_mapping.in_error ()) + return false; + for (auto _ : front_mapping.iter ()) { unsigned old_varidx = _.first; diff --git a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh index a22bbca01b..a4959dbc43 100644 --- a/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh +++ b/thirdparty/harfbuzz/src/hb-ot-var-gvar-table.hh @@ -634,6 +634,83 @@ struct gvar_GVAR static unsigned int next_index (unsigned int i, unsigned int start, unsigned int end) { return (i >= end) ? start : (i + 1); } +#ifndef HB_OPTIMIZE_SIZE + template +#endif + static bool decompile_deltas_add_to_points (const HBUINT8 *&p /* IN/OUT */, + hb_array_t points, + float scalar, + const HBUINT8 *end, + unsigned start +#ifdef HB_OPTIMIZE_SIZE + , bool is_x +#endif + ) + { + unsigned i = 0; + unsigned count = points.length; + while (i < count) + { + if (unlikely (p + 1 > end)) return false; + unsigned control = *p++; + unsigned run_count = (control & TupleValues::VALUE_RUN_COUNT_MASK) + 1; + unsigned stop = i + run_count; + if (unlikely (stop > count)) return false; + + unsigned skip = i < start ? hb_min (start - i, run_count) : 0; + i += skip; + + switch (control & TupleValues::VALUES_SIZE_MASK) + { + case TupleValues::VALUES_ARE_ZEROS: + i = stop; + break; + case TupleValues::VALUES_ARE_WORDS: + { + if (unlikely (p + run_count * HBINT16::static_size > end)) return false; + p += skip * HBINT16::static_size; + const auto *pp = (const HBINT16 *) p; + for (; i < stop; i++) + { + float v = *pp++ * scalar; + if (is_x) points.arrayZ[i].x += v; + else points.arrayZ[i].y += v; + } + p = (const HBUINT8 *) pp; + } + break; + case TupleValues::VALUES_ARE_LONGS: + { + if (unlikely (p + run_count * HBINT32::static_size > end)) return false; + p += skip * HBINT32::static_size; + const auto *pp = (const HBINT32 *) p; + for (; i < stop; i++) + { + float v = *pp++ * scalar; + if (is_x) points.arrayZ[i].x += v; + else points.arrayZ[i].y += v; + } + p = (const HBUINT8 *) pp; + } + break; + case TupleValues::VALUES_ARE_BYTES: + { + if (unlikely (p + run_count > end)) return false; + p += skip * HBINT8::static_size; + const auto *pp = (const HBINT8 *) p; + for (; i < stop; i++) + { + float v = *pp++ * scalar; + if (is_x) points.arrayZ[i].x += v; + else points.arrayZ[i].y += v; + } + p = (const HBUINT8 *) pp; + } + break; + } + } + return true; + } public: bool apply_deltas_to_points (hb_codepoint_t glyph, hb_array_t coords, @@ -656,6 +733,9 @@ struct gvar_GVAR shared_indices, &iterator)) return true; /* so isn't applied at all */ + bool any_private_points = false; + bool private_points_checked = false; + /* Save original points for inferred delta calculation */ auto &orig_points_vec = scratch.orig_points; orig_points_vec.clear (); // Populated lazily @@ -682,6 +762,20 @@ struct gvar_GVAR gvar_cache); if (scalar == 0.f) continue; + + if (!private_points_checked) + { + auto scan = iterator; + do + { + if (scan.current_tuple->has_private_points ()) + { + any_private_points = true; + break; + } + } while (scan.move_to_next ()); + private_points_checked = true; + } const HBUINT8 *p = iterator.get_serialized_data (); unsigned int length = iterator.current_tuple->get_data_size (); if (unlikely (!iterator.var_data_bytes.check_range (p, length))) @@ -706,6 +800,19 @@ struct gvar_GVAR bool apply_to_all = (indices.length == 0); unsigned num_deltas = apply_to_all ? points.length : indices.length; unsigned start_deltas = (apply_to_all && phantom_only && num_deltas >= 4 ? num_deltas - 4 : 0); + + if (apply_to_all && !any_private_points) + { +#ifdef HB_OPTIMIZE_SIZE + if (unlikely (!decompile_deltas_add_to_points (p, points, scalar, end, start_deltas, true))) return false; + if (unlikely (!decompile_deltas_add_to_points (p, points, scalar, end, start_deltas, false))) return false; +#else + if (unlikely (!decompile_deltas_add_to_points (p, points, scalar, end, start_deltas))) return false; + if (unlikely (!decompile_deltas_add_to_points (p, points, scalar, end, start_deltas))) return false; +#endif + continue; + } + if (unlikely (!x_deltas.resize_dirty (num_deltas))) return false; if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end, false, start_deltas))) return false; if (unlikely (!y_deltas.resize_dirty (num_deltas))) return false; @@ -724,8 +831,6 @@ struct gvar_GVAR { for (unsigned int i = phantom_only ? count - 4 : 0; i < count; i++) points.arrayZ[i].translate (deltas.arrayZ[i]); - flush = false; - } hb_memset (deltas.arrayZ + (phantom_only ? count - 4 : 0), 0, (phantom_only ? 4 : count) * sizeof (deltas[0])); diff --git a/thirdparty/harfbuzz/src/hb-paint.cc b/thirdparty/harfbuzz/src/hb-paint.cc index 5dd97d5bb1..ffdd3c3a59 100644 --- a/thirdparty/harfbuzz/src/hb-paint.cc +++ b/thirdparty/harfbuzz/src/hb-paint.cc @@ -742,14 +742,14 @@ hb_paint_pop_group (hb_paint_funcs_t *funcs, void *paint_data, /** * hb_paint_custom_palette_color: - * @funcs: paint functions - * @paint_data: associated data passed by the caller - * @color_index: color index - * @color: (out): fetched color + * @funcs: paint functions. + * @paint_data: associated data passed by the caller. + * @color_index: color index to fetch. + * @color: (out): fetched color. * - * Gets the custom palette color for @color_index. + * Gets the custom palette override color for @color_index. * - * Return value: `true` if found, `false` otherwise + * Return value: `true` if a custom color is provided, `false` otherwise. * * Since: 7.0.0 */ diff --git a/thirdparty/harfbuzz/src/hb-paint.h b/thirdparty/harfbuzz/src/hb-paint.h index 3785077298..787118c46a 100644 --- a/thirdparty/harfbuzz/src/hb-paint.h +++ b/thirdparty/harfbuzz/src/hb-paint.h @@ -684,23 +684,24 @@ typedef void (*hb_paint_pop_group_func_t) (hb_paint_funcs_t *funcs, /** * hb_paint_custom_palette_color_func_t: - * @funcs: paint functions object - * @paint_data: The data accompanying the paint functions in hb_font_paint_glyph() - * @color_index: the color index - * @color: (out): fetched color - * @user_data: User data pointer passed to hb_paint_funcs_set_pop_group_func() + * @funcs: paint functions object. + * @paint_data: data accompanying the paint functions in hb_font_paint_glyph(). + * @color_index: color index to fetch. + * @color: (out): fetched color. + * @user_data: user data pointer passed to hb_paint_funcs_set_custom_palette_color_func(). * - * A virtual method for the #hb_paint_funcs_t to fetch a color from the custom - * color palette. + * A virtual method for #hb_paint_funcs_t to fetch a custom palette override + * color for @color_index. * - * Custom palette colors override the colors from the fonts selected color - * palette. It is not necessary to override all palette entries; for entries - * that should be taken from the font palette, return `false`. + * Custom palette colors override colors from the font's selected color palette. + * It is not necessary to override all palette entries; return `false` for + * entries that should be taken from the font palette. * - * This function might get called multiple times, but the custom palette is - * expected to remain unchanged for duration of a hb_font_paint_glyph() call. + * This function might be called multiple times, but the custom palette is + * expected to remain unchanged for the duration of one + * hb_font_paint_glyph() call. * - * Return value: `true` if found, `false` otherwise + * Return value: `true` if a custom color is provided, `false` otherwise. * * Since: 7.0.0 */ @@ -934,12 +935,12 @@ hb_paint_funcs_set_pop_group_func (hb_paint_funcs_t *funcs, /** * hb_paint_funcs_set_custom_palette_color_func: - * @funcs: A paint functions struct - * @func: (closure user_data) (destroy destroy) (scope notified): The custom-palette-color callback - * @user_data: Data to pass to @func - * @destroy: (nullable): Function to call when @user_data is no longer needed + * @funcs: a paint functions struct. + * @func: (closure user_data) (destroy destroy) (scope notified): custom-palette-color callback. + * @user_data: data to pass to @func. + * @destroy: (nullable): function to call when @user_data is no longer needed. * - * Sets the custom-palette-color callback on the paint functions struct. + * Sets the custom-palette-color callback on @funcs. * * Since: 7.0.0 */ diff --git a/thirdparty/harfbuzz/src/hb-raster-draw.cc b/thirdparty/harfbuzz/src/hb-raster-draw.cc new file mode 100644 index 0000000000..ba11965afc --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-draw.cc @@ -0,0 +1,1386 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-raster-image.hh" +#include "hb-geometry.hh" +#include "hb-machinery.hh" + +#if defined(__aarch64__) || defined(_M_ARM64) +#include +#define HB_RASTER_NEON 1 +#elif defined(__SSE2__) || defined(_M_X64) || defined(_M_AMD64) +#include +#define HB_RASTER_SSE2 1 +#endif + + +/* Fixed-point precision for sub-pixel coordinates. + 8 bits = 24.8: 256 sub-pixel units per pixel. */ +#define HB_RASTER_PIXEL_BITS 8 +#define HB_RASTER_ONE_PIXEL (1 << HB_RASTER_PIXEL_BITS) +#define HB_RASTER_PIXEL_MASK (HB_RASTER_ONE_PIXEL - 1) +/* Full-coverage alpha = 2 * ONE_PIXEL^2 */ +#define HB_RASTER_FULL_COVERAGE (2 * HB_RASTER_ONE_PIXEL * HB_RASTER_ONE_PIXEL) +/* Flatness threshold for Bézier flattening: max deviation in pixels */ +#define HB_RASTER_FLAT_THRESH 0.25f + + +/* Normalized edge: yH > yL always */ +struct hb_raster_edge_t +{ + int32_t xL, yL; /* lower endpoint (fixed-point) */ + int32_t xH, yH; /* upper endpoint (fixed-point) */ + int64_t slope; /* dx/dy in 16.16 fixed point: ((int64_t)dx << 16) / dy */ + int32_t wind; /* +1 or -1 */ +}; + +/* hb_raster_draw_t — outline rasterizer */ +struct hb_raster_draw_t +{ + hb_object_header_t header; + + /* Configuration */ + hb_transform_t<> transform = {1, 0, 0, 1, 0, 0}; + float x_scale_factor = 1.f; + float y_scale_factor = 1.f; + hb_raster_extents_t fixed_extents = {}; + bool has_extents = false; + + /* Accumulated geometry */ + hb_vector_t edges; + + /* Scratch — reused across render() calls */ + hb_vector_t row_area; + hb_vector_t row_cover; + hb_vector_t> edge_buckets; + hb_vector_t active_edges; + + /* Recycled image for zero-malloc render */ + hb_raster_image_t *recycled_image = nullptr; +}; + +static HB_ALWAYS_INLINE void +hb_raster_draw_transform_point (const hb_raster_draw_t *draw, + float x, float y, + float &tx, float &ty) +{ + tx = x; ty = y; + draw->transform.transform_point (tx, ty); + tx /= draw->x_scale_factor; + ty /= draw->y_scale_factor; +} + + +/* hb_raster_draw_t */ + +/** + * hb_raster_draw_create_or_fail: + * + * Creates a new rasterizer object. + * + * Return value: (transfer full): + * A newly allocated #hb_raster_draw_t with a reference count of 1. The + * initial reference count should be released with hb_raster_draw_destroy() + * when you are done using the #hb_raster_draw_t, or `NULL` on + * allocation failure. + * + * Since: 13.0.0 + **/ +hb_raster_draw_t * +hb_raster_draw_create_or_fail (void) +{ + hb_raster_draw_t *draw = hb_object_create (); + return draw; +} + +/** + * hb_raster_draw_reference: (skip) + * @draw: a rasterizer + * + * Increases the reference count on @draw by one. + * + * This prevents @draw from being destroyed until a matching + * call to hb_raster_draw_destroy() is made. + * + * Return value: (transfer full): + * The referenced #hb_raster_draw_t. + * + * Since: 13.0.0 + **/ +hb_raster_draw_t * +hb_raster_draw_reference (hb_raster_draw_t *draw) +{ + return hb_object_reference (draw); +} + +/** + * hb_raster_draw_destroy: (skip) + * @draw: a rasterizer + * + * Decreases the reference count on @draw by one. When the + * reference count reaches zero, the rasterizer is freed. + * + * Since: 13.0.0 + **/ +void +hb_raster_draw_destroy (hb_raster_draw_t *draw) +{ + if (!hb_object_should_destroy (draw)) + return; + + hb_raster_image_destroy (draw->recycled_image); + hb_object_actually_destroy (draw); + hb_free (draw); +} + +/** + * hb_raster_draw_set_user_data: (skip) + * @draw: a rasterizer + * @key: the user-data key + * @data: a pointer to the user data + * @destroy: (nullable): a callback to call when @data is not needed anymore + * @replace: whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified rasterizer. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_draw_set_user_data (hb_raster_draw_t *draw, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (draw, key, data, destroy, replace); +} + +/** + * hb_raster_draw_get_user_data: (skip) + * @draw: a rasterizer + * @key: the user-data key + * + * Fetches the user-data associated with the specified key, + * attached to the specified rasterizer. + * + * Return value: (transfer none): + * A pointer to the user data + * + * Since: 13.0.0 + **/ +void * +hb_raster_draw_get_user_data (hb_raster_draw_t *draw, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (draw, key); +} + +/** + * hb_raster_draw_set_transform: + * @draw: a rasterizer + * @xx: xx component of the transform matrix + * @yx: yx component of the transform matrix + * @xy: xy component of the transform matrix + * @yy: yy component of the transform matrix + * @dx: x translation + * @dy: y translation + * + * Sets a 2×3 affine transform applied to all incoming draw + * coordinates before rasterization. The default is the identity. + * + * Since: 13.0.0 + **/ +void +hb_raster_draw_set_transform (hb_raster_draw_t *draw, + float xx, float yx, + float xy, float yy, + float dx, float dy) +{ + draw->transform = {xx, yx, xy, yy, dx, dy}; +} + +/** + * hb_raster_draw_set_scale_factor: + * @draw: a rasterizer + * @x_scale_factor: x-axis minification factor + * @y_scale_factor: y-axis minification factor + * + * Sets post-transform minification factors applied during rasterization. + * Factors larger than 1 shrink the output in pixels. The default is 1. + * + * Since: 13.0.0 + **/ +void +hb_raster_draw_set_scale_factor (hb_raster_draw_t *draw, + float x_scale_factor, + float y_scale_factor) +{ + draw->x_scale_factor = x_scale_factor > 0.f ? x_scale_factor : 1.f; + draw->y_scale_factor = y_scale_factor > 0.f ? y_scale_factor : 1.f; +} + +/** + * hb_raster_draw_get_scale_factor: + * @draw: a rasterizer + * @x_scale_factor: (out) (nullable): x-axis minification factor + * @y_scale_factor: (out) (nullable): y-axis minification factor + * + * Fetches the current post-transform minification factors. + * + * Since: 13.0.0 + **/ +void +hb_raster_draw_get_scale_factor (hb_raster_draw_t *draw, + float *x_scale_factor, + float *y_scale_factor) +{ + if (x_scale_factor) *x_scale_factor = draw->x_scale_factor; + if (y_scale_factor) *y_scale_factor = draw->y_scale_factor; +} + +/** + * hb_raster_draw_get_transform: + * @draw: a rasterizer + * @xx: (out) (nullable): xx component of the transform matrix + * @yx: (out) (nullable): yx component of the transform matrix + * @xy: (out) (nullable): xy component of the transform matrix + * @yy: (out) (nullable): yy component of the transform matrix + * @dx: (out) (nullable): x translation + * @dy: (out) (nullable): y translation + * + * Fetches the current affine transform of the rasterizer. + * + * Since: 13.0.0 + **/ +void +hb_raster_draw_get_transform (hb_raster_draw_t *draw, + float *xx, float *yx, + float *xy, float *yy, + float *dx, float *dy) +{ + if (xx) *xx = draw->transform.xx; + if (yx) *yx = draw->transform.yx; + if (xy) *xy = draw->transform.xy; + if (yy) *yy = draw->transform.yy; + if (dx) *dx = draw->transform.x0; + if (dy) *dy = draw->transform.y0; +} + +/** + * hb_raster_draw_set_extents: + * @draw: a rasterizer + * @extents: the desired output extents + * + * Overrides the output image extents for the next render. When set, + * hb_raster_draw_render() uses the given extents instead of + * auto-computing them from the accumulated geometry. + * + * Since: 13.0.0 + **/ +void +hb_raster_draw_set_extents (hb_raster_draw_t *draw, + const hb_raster_extents_t *extents) +{ + draw->fixed_extents = *extents; + draw->has_extents = true; +} + +/** + * hb_raster_draw_get_extents: + * @draw: a rasterizer + * @extents: (out) (nullable): where to write current extents + * + * Gets currently configured output extents. + * + * Return value: `true` if extents are set, `false` otherwise. + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_draw_get_extents (hb_raster_draw_t *draw, + hb_raster_extents_t *extents) +{ + if (!draw->has_extents) + return false; + + if (extents) + *extents = draw->fixed_extents; + return true; +} + +/** + * hb_raster_draw_set_glyph_extents: + * @draw: a rasterizer + * @glyph_extents: glyph extents from hb_font_get_glyph_extents() + * + * Transforms @glyph_extents with the rasterizer's current transform and + * sets the resulting pixel extents for the next render. + * + * This is equivalent to computing a transformed bounding box in pixel + * space and calling hb_raster_draw_set_extents(). + * + * Return value: `true` if transformed extents are non-empty and set; + * `false` otherwise. + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_draw_set_glyph_extents (hb_raster_draw_t *draw, + const hb_glyph_extents_t *glyph_extents) +{ + float x0 = (float) glyph_extents->x_bearing; + float y0 = (float) glyph_extents->y_bearing; + float x1 = (float) glyph_extents->x_bearing + glyph_extents->width; + float y1 = (float) glyph_extents->y_bearing + glyph_extents->height; + + float xmin = hb_min (x0, x1); + float xmax = hb_max (x0, x1); + float ymin = hb_min (y0, y1); + float ymax = hb_max (y0, y1); + + float px[4] = {xmin, xmin, xmax, xmax}; + float py[4] = {ymin, ymax, ymin, ymax}; + + float tx, ty; + hb_raster_draw_transform_point (draw, px[0], py[0], tx, ty); + float tx_min = tx, tx_max = tx; + float ty_min = ty, ty_max = ty; + + for (unsigned i = 1; i < 4; i++) + { + hb_raster_draw_transform_point (draw, px[i], py[i], tx, ty); + tx_min = hb_min (tx_min, tx); + tx_max = hb_max (tx_max, tx); + ty_min = hb_min (ty_min, ty); + ty_max = hb_max (ty_max, ty); + } + + int ex0 = (int) floorf (tx_min); + int ey0 = (int) floorf (ty_min); + int ex1 = (int) ceilf (tx_max); + int ey1 = (int) ceilf (ty_max); + + if (ex1 <= ex0 || ey1 <= ey0) + { + draw->fixed_extents = {}; + draw->has_extents = false; + return false; + } + + draw->fixed_extents = { + ex0, ey0, + (unsigned) (ex1 - ex0), + (unsigned) (ey1 - ey0), + 0 + }; + draw->has_extents = true; + return true; +} + +/** + * hb_raster_draw_reset: + * @draw: a rasterizer + * + * Resets the rasterizer to its initial state, clearing all accumulated + * geometry, the transform, and fixed extents. The object can then be + * reused for a new glyph. + * + * Internal scratch buffers and recycled image cache are preserved for + * reuse across subsequent renders. + * + * Since: 13.0.0 + **/ +void +hb_raster_draw_reset (hb_raster_draw_t *draw) +{ + draw->transform = {1, 0, 0, 1, 0, 0}; + draw->x_scale_factor = 1.f; + draw->y_scale_factor = 1.f; + draw->fixed_extents = {}; + draw->has_extents = false; + draw->edges.clear (); + draw->active_edges.clear (); +} + +/** + * hb_raster_draw_recycle_image: + * @draw: a rasterizer + * @image: a raster image to recycle + * + * Recycles @image for reuse by a subsequent hb_raster_draw_render() + * call, avoiding per-render memory allocation. The caller transfers + * ownership of @image to @draw and must not use it afterwards. + * + * If @draw already holds a recycled image, the previously recycled + * image is destroyed. + * + * Since: 13.0.0 + **/ +void +hb_raster_draw_recycle_image (hb_raster_draw_t *draw, + hb_raster_image_t *image) +{ + hb_raster_image_destroy (draw->recycled_image); + draw->recycled_image = image; +} + + +/* + * Draw callbacks — flatten on the fly into hb_raster_edge_t + */ + +static inline void +transform_point (const hb_raster_draw_t *draw, + float x, float y, + float &tx, float &ty) +{ + hb_raster_draw_transform_point (draw, x, y, tx, ty); +} + +static void +emit_segment (hb_raster_draw_t *draw, + float x0, float y0, + float x1, float y1) +{ + int32_t X0 = (int32_t) roundf (x0 * HB_RASTER_ONE_PIXEL); + int32_t Y0 = (int32_t) roundf (y0 * HB_RASTER_ONE_PIXEL); + int32_t X1 = (int32_t) roundf (x1 * HB_RASTER_ONE_PIXEL); + int32_t Y1 = (int32_t) roundf (y1 * HB_RASTER_ONE_PIXEL); + + if (Y0 == Y1) return; /* horizontal — skip */ + + hb_raster_edge_t e; + if (Y0 < Y1) { + e.xL = X0; e.yL = Y0; e.xH = X1; e.yH = Y1; e.wind = +1; + } else { + e.xL = X1; e.yL = Y1; e.xH = X0; e.yH = Y0; e.wind = -1; + } + e.slope = ((int64_t) (e.xH - e.xL) << 16) / (e.yH - e.yL); + + draw->edges.push (e); +} + +/* Quadratic Bézier flattener — iterative de Casteljau at t=0.5. */ +static inline void +flatten_quadratic_recursive (hb_raster_draw_t *draw, + float x0, float y0, + float x1, float y1, + float x2, float y2, + int depth = 0) +{ + struct quad_node_t + { + float x0, y0, x1, y1, x2, y2; + int depth; + }; + + quad_node_t stack[16]; + unsigned top = 0; + + while (true) + { + bool is_flat; + if (false) + { + /* Old behavior: midpoint deviation from chord midpoint. */ + float mx = x0 * 0.25f + x1 * 0.5f + x2 * 0.25f; + float my = y0 * 0.25f + y1 * 0.5f + y2 * 0.25f; + float chord_mx = (x0 + x2) * 0.5f; + float chord_my = (y0 + y2) * 0.5f; + float dx = mx - chord_mx; + float dy = my - chord_my; + static const float flat_thresh = HB_RASTER_FLAT_THRESH * HB_RASTER_FLAT_THRESH; + is_flat = (dx * dx + dy * dy) <= flat_thresh; + } + else + { + /* FreeType behavior: control-point deviation from chord center. */ + const float flat_thresh = 0.25f; + float dx = x0 + x2 - 2.f * x1; + float dy = y0 + y2 - 2.f * y1; + if (dx < 0) dx = -dx; + if (dy < 0) dy = -dy; + is_flat = dx <= flat_thresh && dy <= flat_thresh; + } + + if (depth >= 16 || is_flat) + { + emit_segment (draw, x0, y0, x2, y2); + if (!top) return; + const quad_node_t &n = stack[--top]; + x0 = n.x0; y0 = n.y0; + x1 = n.x1; y1 = n.y1; + x2 = n.x2; y2 = n.y2; + depth = n.depth; + continue; + } + + float x01 = (x0 + x1) * 0.5f, y01 = (y0 + y1) * 0.5f; + float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; + float xm = (x01 + x12) * 0.5f, ym = (y01 + y12) * 0.5f; + + /* Depth is capped at 16, so stack capacity 16 is sufficient. */ + stack[top++] = {xm, ym, x12, y12, x2, y2, depth + 1}; + x2 = xm; y2 = ym; + x1 = x01; y1 = y01; + depth++; + } +} + +/* Quadratic Bézier flattener using forward differencing. + The error (midpoint deviation) shrinks exactly 4× per de Casteljau + subdivision, so we compute the subdivision count upfront and iterate + with constant-cost additions instead of recursive branching. */ +static inline void +flatten_quadratic_fd (hb_raster_draw_t *draw, + float x0, float y0, + float x1, float y1, + float x2, float y2) +{ + /* Deviation of curve midpoint from chord midpoint (squared). */ + float devx = (x0 - 2 * x1 + x2) * 0.25f; + float devy = (y0 - 2 * y1 + y2) * 0.25f; + float err2 = devx * devx + devy * devy; + + static const float flat_thresh = HB_RASTER_FLAT_THRESH * HB_RASTER_FLAT_THRESH; + + if (err2 <= flat_thresh) + { + emit_segment (draw, x0, y0, x2, y2); + return; + } + + /* err² shrinks 16× per subdivision level. Find n such that + err2 / 16^n <= flat_thresh, i.e. n = ceil(log₁₆(err2/flat_thresh)). */ + unsigned n = 1; + { + float ratio = err2 / flat_thresh; + while (ratio > 16.f) { ratio *= (1.f / 16.f); n++; } + if (n > 16) n = 16; + } + unsigned N = 1u << n; /* number of line segments */ + float h = 1.f / N; + + /* Quadratic: B(t) = a·t² + b·t + c + Forward differences with step h: + d²f = 2·a·h² (constant) + df₀ = a·h² + b·h + f₀ = c = P₀ */ + float ax = x0 - 2 * x1 + x2; + float ay = y0 - 2 * y1 + y2; + float bx = 2 * (x1 - x0); + float by = 2 * (y1 - y0); + + float d2fx = 2 * ax * h * h; + float d2fy = 2 * ay * h * h; + float dfx = ax * h * h + bx * h; + float dfy = ay * h * h + by * h; + float fx = x0; + float fy = y0; + + for (unsigned i = 1; i < N; i++) + { + float nx = fx + dfx; + float ny = fy + dfy; + emit_segment (draw, fx, fy, nx, ny); + fx = nx; + fy = ny; + dfx += d2fx; + dfy += d2fy; + } + /* Last segment uses exact endpoint to avoid drift. */ + emit_segment (draw, fx, fy, x2, y2); +} + +static void +flatten_quadratic (hb_raster_draw_t *draw, + float x0, float y0, + float x1, float y1, + float x2, float y2) +{ + if (false) + flatten_quadratic_fd (draw, x0, y0, x1, y1, x2, y2); + else + flatten_quadratic_recursive (draw, x0, y0, x1, y1, x2, y2); +} + +/* For cubic B(t), the max deviation from its chord on [0,1] is bounded by: + max||B(t)-L(t)|| <= max_t ||B''(t)|| / 8. + B''(t) is linear, so max norm is attained at t=0 or t=1: + B''(0)=6*(P0-2P1+P2), B''(1)=6*(P1-2P2+P3). */ +static inline float +cubic_chord_error_bound2 (float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + float d20x = x0 - 2 * x1 + x2; + float d20y = y0 - 2 * y1 + y2; + float d21x = x1 - 2 * x2 + x3; + float d21y = y1 - 2 * y2 + y3; + float m0 = d20x * d20x + d20y * d20y; + float m1 = d21x * d21x + d21y * d21y; + float m = m0 > m1 ? m0 : m1; + /* (max||B''||/8)^2 = (6/8)^2 * max||d2||^2 = (3/4)^2 * m. */ + return m * (9.f / 16.f); +} + +/* Cubic Bézier flattener — iterative de Casteljau at t=0.5. */ +static inline void +flatten_cubic_recursive (hb_raster_draw_t *draw, + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3, + int depth = 0) +{ + struct cubic_node_t + { + float x0, y0, x1, y1, x2, y2, x3, y3; + int depth; + }; + + cubic_node_t stack[16]; + unsigned top = 0; + + while (true) + { + bool is_flat; + if (false) + { + /* Old behavior: curvature/chord-error bound. */ + float err2 = cubic_chord_error_bound2 (x0, y0, x1, y1, x2, y2, x3, y3); + static const float flat_thresh = HB_RASTER_FLAT_THRESH * HB_RASTER_FLAT_THRESH; + is_flat = err2 <= flat_thresh; + } + else + { + /* FreeType behavior: chord-trisection distance test. */ + const float flat_thresh = 0.5f; + + float d10x = 2.f * x0 - 3.f * x1 + x3; + float d10y = 2.f * y0 - 3.f * y1 + y3; + float d20x = x0 - 3.f * x2 + 2.f * x3; + float d20y = y0 - 3.f * y2 + 2.f * y3; + + if (d10x < 0) d10x = -d10x; + if (d10y < 0) d10y = -d10y; + if (d20x < 0) d20x = -d20x; + if (d20y < 0) d20y = -d20y; + + is_flat = d10x <= flat_thresh && + d10y <= flat_thresh && + d20x <= flat_thresh && + d20y <= flat_thresh; + } + + if (depth >= 16 || is_flat) + { + emit_segment (draw, x0, y0, x3, y3); + if (!top) return; + const cubic_node_t &n = stack[--top]; + x0 = n.x0; y0 = n.y0; + x1 = n.x1; y1 = n.y1; + x2 = n.x2; y2 = n.y2; + x3 = n.x3; y3 = n.y3; + depth = n.depth; + continue; + } + + float x01 = (x0 + x1) * 0.5f, y01 = (y0 + y1) * 0.5f; + float x12 = (x1 + x2) * 0.5f, y12 = (y1 + y2) * 0.5f; + float x23 = (x2 + x3) * 0.5f, y23 = (y2 + y3) * 0.5f; + float x012 = (x01 + x12) * 0.5f, y012 = (y01 + y12) * 0.5f; + float x123 = (x12 + x23) * 0.5f, y123 = (y12 + y23) * 0.5f; + float xm = (x012 + x123) * 0.5f, ym = (y012 + y123) * 0.5f; + + /* Depth is capped at 16, so stack capacity 16 is sufficient. */ + stack[top++] = {xm, ym, x123, y123, x23, y23, x3, y3, depth + 1}; + x3 = xm; y3 = ym; + x2 = x012; y2 = y012; + x1 = x01; y1 = y01; + depth++; + } +} + +/* Cubic Bézier flattener using forward differencing. + Use a curvature-based chord-error bound (max||B''||/8), then choose a + uniform subdivision count n such that the bound drops below threshold. + The cubic adds a constant third difference d³f = 6·a·h³. */ +static inline void +flatten_cubic_fd (hb_raster_draw_t *draw, + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + float err2 = cubic_chord_error_bound2 (x0, y0, x1, y1, x2, y2, x3, y3); + + static const float flat_thresh = HB_RASTER_FLAT_THRESH * HB_RASTER_FLAT_THRESH; + + if (err2 <= flat_thresh) + { + emit_segment (draw, x0, y0, x3, y3); + return; + } + + /* The bound scales with h², so err² shrinks 16× per subdivision level. */ + unsigned n = 1; + { + float ratio = err2 / flat_thresh; + while (ratio > 16.f) { ratio *= (1.f / 16.f); n++; } + if (n > 16) n = 16; + } + unsigned N = 1u << n; + float h = 1.f / N; + + /* Cubic: B(t) = a·t³ + b·t² + c·t + d + a = -P₀ + 3P₁ - 3P₂ + P₃ + b = 3P₀ - 6P₁ + 3P₂ + c = 3(P₁ - P₀) + d = P₀ + Forward differences with step h: + d³f = 6·a·h³ (constant) + d²f₀ = 6·a·h³ + 2·b·h² + d¹f₀ = a·h³ + b·h² + c·h + f₀ = d = P₀ */ + float ax = -x0 + 3*x1 - 3*x2 + x3; + float ay = -y0 + 3*y1 - 3*y2 + y3; + float bx = 3*x0 - 6*x1 + 3*x2; + float by = 3*y0 - 6*y1 + 3*y2; + float cx = 3*(x1 - x0); + float cy = 3*(y1 - y0); + + float h2 = h * h, h3 = h2 * h; + float d3fx = 6 * ax * h3; + float d3fy = 6 * ay * h3; + float d2fx = d3fx + 2 * bx * h2; + float d2fy = d3fy + 2 * by * h2; + float dfx = ax * h3 + bx * h2 + cx * h; + float dfy = ay * h3 + by * h2 + cy * h; + float fx = x0; + float fy = y0; + + for (unsigned i = 1; i < N; i++) + { + float nx = fx + dfx; + float ny = fy + dfy; + emit_segment (draw, fx, fy, nx, ny); + fx = nx; fy = ny; + dfx += d2fx; dfy += d2fy; + d2fx += d3fx; d2fy += d3fy; + } + /* Last segment uses exact endpoint to avoid drift. */ + emit_segment (draw, fx, fy, x3, y3); +} + +static void +flatten_cubic (hb_raster_draw_t *draw, + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + if (false) + flatten_cubic_fd (draw, x0, y0, x1, y1, x2, y2, x3, y3); + else + flatten_cubic_recursive (draw, x0, y0, x1, y1, x2, y2, x3, y3); +} + + +/* Draw callback implementations */ + +static void +hb_raster_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float to_x HB_UNUSED, float to_y HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* no-op: state tracked by hb_draw_state_t */ +} + +static void +hb_raster_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_raster_draw_t *draw = (hb_raster_draw_t *) draw_data; + + float tx0, ty0, tx1, ty1; + transform_point (draw, st->current_x, st->current_y, tx0, ty0); + transform_point (draw, to_x, to_y, tx1, ty1); + emit_segment (draw, tx0, ty0, tx1, ty1); +} + +static void +hb_raster_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_raster_draw_t *draw = (hb_raster_draw_t *) draw_data; + + float tx0, ty0, tx1, ty1, tx2, ty2; + transform_point (draw, st->current_x, st->current_y, tx0, ty0); + transform_point (draw, control_x, control_y, tx1, ty1); + transform_point (draw, to_x, to_y, tx2, ty2); + flatten_quadratic (draw, tx0, ty0, tx1, ty1, tx2, ty2); +} + +static void +hb_raster_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_raster_draw_t *draw = (hb_raster_draw_t *) draw_data; + + float tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3; + transform_point (draw, st->current_x, st->current_y, tx0, ty0); + transform_point (draw, control1_x, control1_y, tx1, ty1); + transform_point (draw, control2_x, control2_y, tx2, ty2); + transform_point (draw, to_x, to_y, tx3, ty3); + flatten_cubic (draw, tx0, ty0, tx1, ty1, tx2, ty2, tx3, ty3); +} + +static void +hb_raster_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data HB_UNUSED, + hb_draw_state_t *st HB_UNUSED, + void *user_data HB_UNUSED) +{ + /* no-op: hb_draw_funcs_t already emits closing line_to before us */ +} + + +/* Lazy-loader singleton for draw funcs */ + +static inline void free_static_raster_draw_funcs (); + +static struct hb_raster_draw_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + + hb_draw_funcs_set_move_to_func (funcs, hb_raster_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, hb_raster_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, hb_raster_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, hb_raster_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, hb_raster_close_path, nullptr, nullptr); + + hb_draw_funcs_make_immutable (funcs); + + hb_atexit (free_static_raster_draw_funcs); + + return funcs; + } +} static_raster_draw_funcs; + +static inline void +free_static_raster_draw_funcs () +{ + static_raster_draw_funcs.free_instance (); +} + +/** + * hb_raster_draw_get_funcs: + * + * Fetches the singleton #hb_draw_funcs_t that feeds outline data + * into an #hb_raster_draw_t. Pass the #hb_raster_draw_t as the + * @draw_data argument when calling the draw functions. + * + * Return value: (transfer none): + * The rasterizer draw functions + * + * Since: 13.0.0 + **/ +hb_draw_funcs_t * +hb_raster_draw_get_funcs (void) +{ + return static_raster_draw_funcs.get_unconst (); +} + +/** + * hb_raster_draw_glyph: + * @draw: a rasterizer + * @font: font to draw from + * @glyph: glyph ID to draw + * @pen_x: glyph origin x in font coordinates (pre-transform) + * @pen_y: glyph origin y in font coordinates (pre-transform) + * + * Convenience wrapper to draw one glyph at (@pen_x, @pen_y) using the + * rasterizer's current transform. The pen coordinates are applied before + * minification and are transformed by the current affine transform. + * + * Since: 13.0.0 + **/ +void +hb_raster_draw_glyph (hb_raster_draw_t *draw, + hb_font_t *font, + hb_codepoint_t glyph, + float pen_x, + float pen_y) +{ + float xx = draw->transform.xx; + float yx = draw->transform.yx; + float xy = draw->transform.xy; + float yy = draw->transform.yy; + float dx = draw->transform.x0; + float dy = draw->transform.y0; + + hb_raster_draw_set_transform (draw, + xx, yx, xy, yy, + dx + xx * pen_x + xy * pen_y, + dy + yx * pen_x + yy * pen_y); + hb_font_draw_glyph (font, glyph, hb_raster_draw_get_funcs (), draw); + hb_raster_draw_set_transform (draw, xx, yx, xy, yy, dx, dy); +} + + +/* + * Analytic coverage rasterizer + * + * For each line-segment edge and each pixel row it crosses, we compute + * exact area/cover contributions per pixel cell. A left-to-right sweep + * then converts accumulated (area, cover) into alpha values. + * + * Coordinates are fixed-point. + * + * cover[x] = Σ dy · wind — signed vertical extent per cell + * area[x] = Σ (fx₀+fx₁)·dy·wind — twice the signed trapezoidal area + * + * Sweep: + * cover_accum += cover[x] + * α = min(|cover_accum·128 − area[x]|, 8192) · 255 / 8192 + */ + +/* Add one edge piece's area/cover into a single cell. */ +static HB_ALWAYS_INLINE void +cell_add (int32_t *area, int16_t *cover, unsigned width, int col, + int32_t fx0, int32_t fy0, int32_t fx1, int32_t fy1, int32_t wind, + unsigned &x_min, unsigned &x_max) +{ + if (unlikely ((unsigned) col >= width)) + { + if (unlikely (col < 0)) + { + /* Edge is to the left of the surface. The winding contribution + * still carries into the visible region, so add the cover delta + * to column 0. Area is not added since the edge doesn't cross + * column 0's cell. */ + int32_t dy = fy1 - fy0; + cover[0] += (int16_t) (dy * wind); + x_min = hb_min (x_min, 0u); + x_max = hb_max (x_max, 0u); + } + return; + } + int32_t dy = fy1 - fy0; + area[col] += (fx0 + fx1) * dy * wind; + cover[col] += (int16_t) (dy * wind); + x_min = hb_min (x_min, (unsigned) col); + x_max = hb_max (x_max, (unsigned) col); +} + +/* Walk one edge through the pixel cells of a single pixel row, + accumulating area/cover. py is the integer pixel-row index. */ +static HB_ALWAYS_INLINE void +edge_sweep_row (int32_t *area, + int16_t *cover, + unsigned width, + int x_org, + int32_t y_top, + const hb_raster_edge_t &edge, + unsigned &x_min, + unsigned &x_max) +{ + int32_t y_bot = y_top + HB_RASTER_ONE_PIXEL; + + int32_t ey0 = hb_max (edge.yL, y_top); + int32_t ey1 = hb_min (edge.yH, y_bot); + if (ey0 >= ey1) return; + + /* X at clipped endpoints (fixed-point) */ + int32_t x0 = edge.xL + (int32_t) ((int64_t) (ey0 - edge.yL) * edge.slope >> 16); + int32_t x1 = edge.xL + (int32_t) ((int64_t) (ey1 - edge.yL) * edge.slope >> 16); + + /* Fractional y within this pixel row [0, ONE_PIXEL] */ + int32_t fy0 = ey0 - y_top; + int32_t fy1 = ey1 - y_top; + + int32_t cx0 = x0 >> HB_RASTER_PIXEL_BITS; + int32_t fx0 = x0 & HB_RASTER_PIXEL_MASK; + int32_t cx1 = x1 >> HB_RASTER_PIXEL_BITS; + int32_t fx1 = x1 & HB_RASTER_PIXEL_MASK; + int32_t wind = edge.wind; + + /* Fast path: both endpoints in the same pixel column. */ + if (cx0 == cx1) + { + cell_add (area, cover, width, cx0 - x_org, fx0, fy0, fx1, fy1, wind, x_min, x_max); + return; + } + + int32_t total_dx = x1 - x0; + int32_t total_dy = fy1 - fy0; + + /* fy increment per pixel column (constant since x_b advances by ONE_PIXEL). */ + int32_t delta_fy = (int32_t) ((int64_t) HB_RASTER_ONE_PIXEL * total_dy / total_dx); + + if (total_dx > 0) + { + /* Left-to-right edge. */ + int32_t x_b = (cx0 + 1) << HB_RASTER_PIXEL_BITS; + int32_t fy_b = fy0 + (int32_t) ((int64_t) (x_b - x0) * total_dy / total_dx); + cell_add (area, cover, width, cx0 - x_org, fx0, fy0, HB_RASTER_ONE_PIXEL, fy_b, wind, x_min, x_max); + + int32_t fy_prev = fy_b; + for (int32_t cx = cx0 + 1; cx < cx1; cx++) + { + fy_b = fy_prev + delta_fy; + cell_add (area, cover, width, cx - x_org, 0, fy_prev, HB_RASTER_ONE_PIXEL, fy_b, wind, x_min, x_max); + fy_prev = fy_b; + } + + cell_add (area, cover, width, cx1 - x_org, 0, fy_prev, fx1, fy1, wind, x_min, x_max); + } + else + { + /* Right-to-left edge. */ + int32_t x_b = cx0 << HB_RASTER_PIXEL_BITS; + int32_t fy_b = fy0 + (int32_t) ((int64_t) (x_b - x0) * total_dy / total_dx); + cell_add (area, cover, width, cx0 - x_org, fx0, fy0, 0, fy_b, wind, x_min, x_max); + + int32_t fy_prev = fy_b; + for (int32_t cx = cx0 - 1; cx > cx1; cx--) + { + fy_b = fy_prev - delta_fy; + cell_add (area, cover, width, cx - x_org, HB_RASTER_ONE_PIXEL, fy_prev, 0, fy_b, wind, x_min, x_max); + fy_prev = fy_b; + } + + cell_add (area, cover, width, cx1 - x_org, HB_RASTER_ONE_PIXEL, fy_prev, fx1, fy1, wind, x_min, x_max); + } +} + +/* Convert cover-delta + area to alpha bytes, then clear. + Returns final cover accumulator over [x_min, x_max]. */ +static int32_t +sweep_row_to_alpha (uint8_t *__restrict row_buf, + int32_t *__restrict area, + int16_t *__restrict cover, + unsigned x_min, + unsigned x_max) +{ + const int32_t cover_scale = 2 * HB_RASTER_ONE_PIXEL; + int32_t cover_accum = 0; + unsigned x = x_min; + +#ifdef HB_RASTER_NEON + int32x4_t clamp_v = vdupq_n_s32 (HB_RASTER_FULL_COVERAGE); + int32x4_t bias_v = vdupq_n_s32 (HB_RASTER_FULL_COVERAGE / 2); + int32x4_t zero32 = vdupq_n_s32 (0); + int16x8_t zero16 = vdupq_n_s16 (0); + for (; x + 7 <= x_max; x += 8) + { + int32_t ctmp[8]; + for (unsigned i = 0; i < 8; i++) + { + cover_accum += cover[x + i]; + ctmp[i] = cover_accum * cover_scale; + } + + int32x4_t c0 = vld1q_s32 (ctmp + 0); + int32x4_t c1 = vld1q_s32 (ctmp + 4); + int32x4_t a0 = vld1q_s32 (area + x); + int32x4_t a1 = vld1q_s32 (area + x + 4); + + int32x4_t v0 = vabsq_s32 (vsubq_s32 (c0, a0)); + int32x4_t v1 = vabsq_s32 (vsubq_s32 (c1, a1)); + + v0 = vminq_s32 (v0, clamp_v); + v1 = vminq_s32 (v1, clamp_v); + + int32x4_t r0 = vshrq_n_s32 (vmlaq_n_s32 (bias_v, v0, 255), 2 * HB_RASTER_PIXEL_BITS + 1); + int32x4_t r1 = vshrq_n_s32 (vmlaq_n_s32 (bias_v, v1, 255), 2 * HB_RASTER_PIXEL_BITS + 1); + + int16x4_t h0 = vmovn_s32 (r0); + int16x4_t h1 = vmovn_s32 (r1); + int16x8_t h = vcombine_s16 (h0, h1); + uint8x8_t b = vqmovun_s16 (h); + vst1_u8 (row_buf + x, b); + + vst1q_s32 (area + x, zero32); + vst1q_s32 (area + x + 4, zero32); + vst1q_s16 (cover + x, zero16); + } +#elif defined(HB_RASTER_SSE2) + __m128i clamp_v = _mm_set1_epi32 (HB_RASTER_FULL_COVERAGE); + __m128i bias_v = _mm_set1_epi32 (HB_RASTER_FULL_COVERAGE / 2); + __m128i zero_v = _mm_setzero_si128 (); + for (; x + 7 <= x_max; x += 8) + { + int32_t ctmp[8]; + for (unsigned i = 0; i < 8; i++) + { + cover_accum += cover[x + i]; + ctmp[i] = cover_accum * cover_scale; + } + + __m128i c0 = _mm_loadu_si128 ((__m128i *) (void *) (ctmp + 0)); + __m128i c1 = _mm_loadu_si128 ((__m128i *) (void *) (ctmp + 4)); + __m128i a0 = _mm_loadu_si128 ((__m128i *) (void *) (area + x)); + __m128i a1 = _mm_loadu_si128 ((__m128i *) (void *) (area + x + 4)); + + __m128i v0 = _mm_sub_epi32 (c0, a0); + __m128i v1 = _mm_sub_epi32 (c1, a1); + + __m128i s0 = _mm_srai_epi32 (v0, 31); + __m128i s1 = _mm_srai_epi32 (v1, 31); + v0 = _mm_sub_epi32 (_mm_xor_si128 (v0, s0), s0); + v1 = _mm_sub_epi32 (_mm_xor_si128 (v1, s1), s1); + + __m128i lt0 = _mm_cmplt_epi32 (v0, clamp_v); + __m128i lt1 = _mm_cmplt_epi32 (v1, clamp_v); + v0 = _mm_or_si128 (_mm_and_si128 (lt0, v0), _mm_andnot_si128 (lt0, clamp_v)); + v1 = _mm_or_si128 (_mm_and_si128 (lt1, v1), _mm_andnot_si128 (lt1, clamp_v)); + + __m128i r0 = _mm_srai_epi32 (_mm_add_epi32 (_mm_sub_epi32 (_mm_slli_epi32 (v0, 8), v0), bias_v), 2 * HB_RASTER_PIXEL_BITS + 1); + __m128i r1 = _mm_srai_epi32 (_mm_add_epi32 (_mm_sub_epi32 (_mm_slli_epi32 (v1, 8), v1), bias_v), 2 * HB_RASTER_PIXEL_BITS + 1); + + __m128i h = _mm_packs_epi32 (r0, r1); + __m128i b = _mm_packus_epi16 (h, h); + _mm_storel_epi64 ((__m128i *) (void *) (row_buf + x), b); + + _mm_storeu_si128 ((__m128i *) (void *) (area + x), zero_v); + _mm_storeu_si128 ((__m128i *) (void *) (area + x + 4), zero_v); + _mm_storeu_si128 ((__m128i *) (void *) (cover + x), zero_v); + } +#endif + + for (; x <= x_max; x++) + { + cover_accum += cover[x]; + int32_t val = cover_accum * cover_scale - area[x]; + int32_t alpha = val < 0 ? -val : val; + if (alpha > HB_RASTER_FULL_COVERAGE) alpha = HB_RASTER_FULL_COVERAGE; + row_buf[x] = (uint8_t) (((unsigned) alpha * 255 + HB_RASTER_FULL_COVERAGE / 2) >> (2 * HB_RASTER_PIXEL_BITS + 1)); + area[x] = 0; + cover[x] = 0; + } + + return cover_accum; +} + + +/** + * hb_raster_draw_render: + * @draw: a rasterizer + * + * Rasterizes the accumulated outline geometry into a new + * #hb_raster_image_t. After rendering, the accumulated edges are + * cleared so the rasterizer can be reused. Output format is always + * @HB_RASTER_FORMAT_A8. + * + * Return value: (transfer full): + * A rendered #hb_raster_image_t. Returns `NULL` on allocation/configuration + * failure. If no geometry was accumulated, returns an empty image. + * + * Since: 13.0.0 + **/ +hb_raster_image_t * +hb_raster_draw_render (hb_raster_draw_t *draw) +{ + /* ── 1. Compute result extents ─────────────────────────────────── */ + hb_raster_extents_t ext; + + if (draw->has_extents) + { + ext = draw->fixed_extents; + } + else + { + /* Auto-size from edge bounding box */ + if (draw->edges.length == 0) + { + /* No edges: produce 0×0 image */ + ext = { 0, 0, 0, 0, 0 }; + } + else + { + int32_t xmin = draw->edges.arrayZ[0].xL, xmax = draw->edges.arrayZ[0].xL; + int32_t ymin = draw->edges.arrayZ[0].yL, ymax = draw->edges.arrayZ[0].yH; + + for (const auto &e : draw->edges) + { + xmin = hb_min (xmin, hb_min (e.xL, e.xH)); + xmax = hb_max (xmax, hb_max (e.xL, e.xH)); + ymin = hb_min (ymin, e.yL); + ymax = hb_max (ymax, e.yH); + } + + /* Convert fixed-point → pixels (floor for min, ceil for max) */ + int x0 = xmin >> HB_RASTER_PIXEL_BITS; + int y0 = ymin >> HB_RASTER_PIXEL_BITS; + int x1 = (xmax + HB_RASTER_PIXEL_MASK) >> HB_RASTER_PIXEL_BITS; + int y1 = (ymax + HB_RASTER_PIXEL_MASK) >> HB_RASTER_PIXEL_BITS; + + ext.x_origin = x0; + ext.y_origin = y0; + ext.width = (unsigned) hb_max (0, x1 - x0); + ext.height = (unsigned) hb_max (0, y1 - y0); + ext.stride = 0; /* filled below */ + } + } + + /* ── 2. Compute stride ─────────────────────────────────────────── */ + if (ext.stride == 0) + ext.stride = (ext.width + 3u) & ~3u; + + /* ── 3. Allocate or reuse image ─────────────────────────────────── */ + hb_raster_image_t *image; + if (draw->recycled_image) + { + image = draw->recycled_image; + draw->recycled_image = nullptr; + } + else + { + image = hb_raster_image_create_or_fail (); + if (unlikely (!image)) goto fail; + } + + if (unlikely (!image->configure (HB_RASTER_FORMAT_A8, ext))) + { + hb_raster_image_destroy (image); + image = nullptr; + goto fail; + } + image->clear (); + + /* ── 4. Bucket edges by starting row and rasterize scanlines ──── */ + if (draw->edges.length && ext.width && ext.height) + { + if (unlikely (!draw->row_area.resize_dirty (ext.width) || + !draw->row_cover.resize_dirty (ext.width))) + goto fail; + hb_memset (draw->row_area.arrayZ, 0, ext.width * sizeof (int32_t)); + hb_memset (draw->row_cover.arrayZ, 0, ext.width * sizeof (int16_t)); + + /* Bucket edges by their starting pixel row. + Only grow the outer vector; clear inner vectors without freeing. */ + unsigned old_buckets = draw->edge_buckets.length; + if (ext.height > old_buckets) + { + if (unlikely (!draw->edge_buckets.resize (ext.height))) + goto fail; + } + for (unsigned i = 0; i < hb_min (ext.height, old_buckets); i++) + draw->edge_buckets.arrayZ[i].clear (); + /* New buckets (if any) are already empty from resize's zero-init. */ + + for (unsigned i = 0; i < draw->edges.length; i++) + { + int row = (draw->edges.arrayZ[i].yL >> HB_RASTER_PIXEL_BITS) - ext.y_origin; + if (row < 0) row = 0; + if ((unsigned) row >= ext.height) continue; + draw->edge_buckets.arrayZ[row].push (i); + } + + /* Scanline loop with active edge list. */ + draw->active_edges.clear (); + + for (unsigned row = 0; row < ext.height; row++) + { + int32_t y_top = (ext.y_origin + (int) row) << HB_RASTER_PIXEL_BITS; + + /* Add new edges from this row's bucket. */ + draw->active_edges.extend (draw->edge_buckets.arrayZ[row]); + + /* Process active edges and compact live ones in one linear pass. */ + unsigned x_min = ext.width, x_max = 0; + unsigned write = 0; + unsigned active_len = draw->active_edges.length; + for (unsigned j = 0; j < active_len; j++) + { + unsigned edge_idx = draw->active_edges.arrayZ[j]; + const auto &e = draw->edges.arrayZ[edge_idx]; + if (e.yH <= y_top) + continue; + + edge_sweep_row (draw->row_area.arrayZ, draw->row_cover.arrayZ, + ext.width, ext.x_origin, y_top, e, x_min, x_max); + draw->active_edges.arrayZ[write++] = edge_idx; + } + draw->active_edges.resize (write); + + if (x_min <= x_max) + { + int32_t cover_accum = sweep_row_to_alpha (image->buffer.arrayZ + row * ext.stride, + draw->row_area.arrayZ, draw->row_cover.arrayZ, + x_min, x_max); + + /* If cover doesn't cancel, memset the constant-alpha tail. */ + if (cover_accum != 0) + { + int32_t alpha = cover_accum * (2 * HB_RASTER_ONE_PIXEL); + alpha = alpha < 0 ? -alpha : alpha; + if (alpha > HB_RASTER_FULL_COVERAGE) alpha = HB_RASTER_FULL_COVERAGE; + uint8_t byte = (uint8_t) (((unsigned) alpha * 255 + HB_RASTER_FULL_COVERAGE / 2) >> (2 * HB_RASTER_PIXEL_BITS + 1)); + + uint8_t *row_buf = image->buffer.arrayZ + row * ext.stride; + hb_memset (row_buf + x_max + 1, byte, ext.width - 1 - x_max); + } + } + } + } + +done: + /* ── 6. Reset one-shot state ────────────────────────────────────── */ + draw->edges.clear (); + draw->has_extents = false; + draw->fixed_extents = {}; + + return image; + +fail: + hb_raster_image_destroy (image); + image = nullptr; + goto done; +} diff --git a/thirdparty/harfbuzz/src/hb-raster-image.cc b/thirdparty/harfbuzz/src/hb-raster-image.cc new file mode 100644 index 0000000000..bfe78dea23 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-image.cc @@ -0,0 +1,967 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-raster-image.hh" + +#include + +#ifdef HAVE_PNG +#include +#endif + +#ifdef HAVE_PNG +struct hb_raster_png_read_blob_t +{ + const uint8_t *data = nullptr; + size_t size = 0; + size_t offset = 0; +}; + +static void +hb_raster_png_error (png_structp png, + png_const_charp msg HB_UNUSED) +{ + png_longjmp (png, 1); +} + +static void +hb_raster_png_warning (png_structp png HB_UNUSED, + png_const_charp msg HB_UNUSED) +{} + +static void +hb_raster_png_read_blob (png_structp png, png_bytep out, png_size_t length) +{ + hb_raster_png_read_blob_t *r = (hb_raster_png_read_blob_t *) png_get_io_ptr (png); + + if (!r || !r->data || length > r->size - r->offset) + png_error (png, "read error"); + + hb_memcpy (out, r->data + r->offset, length); + r->offset += length; +} + +struct hb_raster_png_write_blob_t +{ + hb_vector_t data; +}; + +static void +hb_raster_png_write_blob (png_structp png, png_bytep in, png_size_t length) +{ + hb_raster_png_write_blob_t *w = (hb_raster_png_write_blob_t *) png_get_io_ptr (png); + if (!w) + png_error (png, "write error"); + + unsigned old_length = w->data.length; + if (!w->data.resize_dirty ((int) (old_length + length))) + png_error (png, "write error"); + + hb_memcpy (w->data.arrayZ + old_length, in, length); +} + +static void +hb_raster_png_flush_blob (png_structp png HB_UNUSED) +{} +#endif + + +/* + * Image compositing + */ + +/* Unpack premultiplied pixel to float RGBA [0,1]. */ +static inline void +unpack_to_float (uint32_t px, float &r, float &g, float &b, float &a) +{ + b = (px & 0xFF) / 255.f; + g = ((px >> 8) & 0xFF) / 255.f; + r = ((px >> 16) & 0xFF) / 255.f; + a = (px >> 24) / 255.f; +} + +/* Pack float RGBA [0,1] premultiplied back to uint32_t. */ +static inline uint32_t +pack_from_float (float r, float g, float b, float a) +{ + return hb_raster_pack_pixel ((uint8_t) (hb_clamp (b, 0.f, 1.f) * 255.f + 0.5f), + (uint8_t) (hb_clamp (g, 0.f, 1.f) * 255.f + 0.5f), + (uint8_t) (hb_clamp (r, 0.f, 1.f) * 255.f + 0.5f), + (uint8_t) (hb_clamp (a, 0.f, 1.f) * 255.f + 0.5f)); +} + +/* Separable blend mode functions: operate on unpremultiplied [0,1] channels. */ +static inline float +blend_multiply (float sc, float dc) { return sc * dc; } +static inline float +blend_screen (float sc, float dc) { return sc + dc - sc * dc; } +static inline float +blend_overlay (float sc, float dc) +{ return dc <= 0.5f ? 2.f * sc * dc : 1.f - 2.f * (1.f - sc) * (1.f - dc); } +static inline float +blend_darken (float sc, float dc) { return hb_min (sc, dc); } +static inline float +blend_lighten (float sc, float dc) { return hb_max (sc, dc); } +static inline float +blend_color_dodge (float sc, float dc) +{ + if (dc <= 0.f) return 0.f; + if (sc >= 1.f) return 1.f; + return hb_min (1.f, dc / (1.f - sc)); +} +static inline float +blend_color_burn (float sc, float dc) +{ + if (dc >= 1.f) return 1.f; + if (sc <= 0.f) return 0.f; + return 1.f - hb_min (1.f, (1.f - dc) / sc); +} +static inline float +blend_hard_light (float sc, float dc) +{ return sc <= 0.5f ? 2.f * sc * dc : 1.f - 2.f * (1.f - sc) * (1.f - dc); } +static inline float +blend_soft_light (float sc, float dc) +{ + if (sc <= 0.5f) + return dc - (1.f - 2.f * sc) * dc * (1.f - dc); + float d = (dc <= 0.25f) ? ((16.f * dc - 12.f) * dc + 4.f) * dc + : sqrtf (dc); + return dc + (2.f * sc - 1.f) * (d - dc); +} +static inline float +blend_difference (float sc, float dc) { return fabsf (sc - dc); } +static inline float +blend_exclusion (float sc, float dc) { return sc + dc - 2.f * sc * dc; } + +/* Apply a separable blend mode per-pixel. + * Both src and dst are premultiplied BGRA32. */ +static inline uint32_t +apply_separable_blend (uint32_t src, uint32_t dst, + float (*blend_fn)(float, float)) +{ + float sr, sg, sb, sa; + float dr, dg, db, da; + unpack_to_float (src, sr, sg, sb, sa); + unpack_to_float (dst, dr, dg, db, da); + + float usr = sa > 0.f ? sr / sa : 0.f; + float usg = sa > 0.f ? sg / sa : 0.f; + float usb = sa > 0.f ? sb / sa : 0.f; + float udr = da > 0.f ? dr / da : 0.f; + float udg = da > 0.f ? dg / da : 0.f; + float udb = da > 0.f ? db / da : 0.f; + + float br = blend_fn (usr, udr); + float bg = blend_fn (usg, udg); + float bb = blend_fn (usb, udb); + + float ra = sa + da - sa * da; + float rr = sa * da * br + sa * (1.f - da) * usr + (1.f - sa) * da * udr; + float rg = sa * da * bg + sa * (1.f - da) * usg + (1.f - sa) * da * udg; + float rb = sa * da * bb + sa * (1.f - da) * usb + (1.f - sa) * da * udb; + + return pack_from_float (rr, rg, rb, ra); +} + +/* HSL helpers */ +static inline float +hsl_luminosity (float r, float g, float b) +{ return 0.299f * r + 0.587f * g + 0.114f * b; } + +static inline float +hsl_saturation (float r, float g, float b) +{ return hb_max (hb_max (r, g), b) - hb_min (hb_min (r, g), b); } + +static inline void +hsl_clip_color (float &r, float &g, float &b) +{ + float l = hsl_luminosity (r, g, b); + float mn = hb_min (hb_min (r, g), b); + float mx = hb_max (hb_max (r, g), b); + if (mn < 0.f) + { + float d = l - mn; + if (d > 0.f) { r = l + (r - l) * l / d; g = l + (g - l) * l / d; b = l + (b - l) * l / d; } + } + if (mx > 1.f) + { + float d = mx - l; + if (d > 0.f) { r = l + (r - l) * (1.f - l) / d; g = l + (g - l) * (1.f - l) / d; b = l + (b - l) * (1.f - l) / d; } + } +} + +static inline void +hsl_set_luminosity (float &r, float &g, float &b, float l) +{ + float d = l - hsl_luminosity (r, g, b); + r += d; g += d; b += d; + hsl_clip_color (r, g, b); +} + +static inline void +hsl_set_saturation_inner (float &mn, float &mid, float &mx, float s) +{ + if (mx > mn) + { + mid = (mid - mn) * s / (mx - mn); + mx = s; + } + else + mid = mx = 0.f; + mn = 0.f; +} + +static inline void +hsl_set_saturation (float &r, float &g, float &b, float s) +{ + if (r <= g) + { + if (g <= b) hsl_set_saturation_inner (r, g, b, s); + else if (r <= b) hsl_set_saturation_inner (r, b, g, s); + else hsl_set_saturation_inner (b, r, g, s); + } + else + { + if (r <= b) hsl_set_saturation_inner (g, r, b, s); + else if (g <= b) hsl_set_saturation_inner (g, b, r, s); + else hsl_set_saturation_inner (b, g, r, s); + } +} + +static inline uint32_t +apply_hsl_blend (uint32_t src, uint32_t dst, + hb_paint_composite_mode_t mode) +{ + float sr, sg, sb, sa; + float dr, dg, db, da; + unpack_to_float (src, sr, sg, sb, sa); + unpack_to_float (dst, dr, dg, db, da); + + float usr = sa > 0.f ? sr / sa : 0.f; + float usg = sa > 0.f ? sg / sa : 0.f; + float usb = sa > 0.f ? sb / sa : 0.f; + float udr = da > 0.f ? dr / da : 0.f; + float udg = da > 0.f ? dg / da : 0.f; + float udb = da > 0.f ? db / da : 0.f; + + float br = udr, bg = udg, bb = udb; + + if (mode == HB_PAINT_COMPOSITE_MODE_HSL_HUE) + { + br = usr; bg = usg; bb = usb; + hsl_set_saturation (br, bg, bb, hsl_saturation (udr, udg, udb)); + hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb)); + } + else if (mode == HB_PAINT_COMPOSITE_MODE_HSL_SATURATION) + { + br = udr; bg = udg; bb = udb; + hsl_set_saturation (br, bg, bb, hsl_saturation (usr, usg, usb)); + hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb)); + } + else if (mode == HB_PAINT_COMPOSITE_MODE_HSL_COLOR) + { + br = usr; bg = usg; bb = usb; + hsl_set_luminosity (br, bg, bb, hsl_luminosity (udr, udg, udb)); + } + else /* HSL_LUMINOSITY */ + { + br = udr; bg = udg; bb = udb; + hsl_set_luminosity (br, bg, bb, hsl_luminosity (usr, usg, usb)); + } + + float ra = sa + da - sa * da; + float rr = sa * da * br + sa * (1.f - da) * usr + (1.f - sa) * da * udr; + float rg = sa * da * bg + sa * (1.f - da) * usg + (1.f - sa) * da * udg; + float rb = sa * da * bb + sa * (1.f - da) * usb + (1.f - sa) * da * udb; + + return pack_from_float (rr, rg, rb, ra); +} + +/* Composite per-pixel with full blend mode support. */ +static inline uint32_t +composite_pixel (uint32_t src, uint32_t dst, + hb_paint_composite_mode_t mode) +{ + uint8_t sa = (uint8_t) (src >> 24); + uint8_t da = (uint8_t) (dst >> 24); + + switch (mode) + { + case HB_PAINT_COMPOSITE_MODE_CLEAR: + return 0; + case HB_PAINT_COMPOSITE_MODE_SRC: + return src; + case HB_PAINT_COMPOSITE_MODE_DEST: + return dst; + case HB_PAINT_COMPOSITE_MODE_SRC_OVER: + return hb_raster_src_over (src, dst); + case HB_PAINT_COMPOSITE_MODE_DEST_OVER: + return hb_raster_src_over (dst, src); + case HB_PAINT_COMPOSITE_MODE_SRC_IN: + return hb_raster_alpha_mul (src, da); + case HB_PAINT_COMPOSITE_MODE_DEST_IN: + return hb_raster_alpha_mul (dst, sa); + case HB_PAINT_COMPOSITE_MODE_SRC_OUT: + return hb_raster_alpha_mul (src, 255 - da); + case HB_PAINT_COMPOSITE_MODE_DEST_OUT: + return hb_raster_alpha_mul (dst, 255 - sa); + case HB_PAINT_COMPOSITE_MODE_SRC_ATOP: + { + /* Fa=Da, Fb=1-Sa */ + uint32_t a = hb_raster_alpha_mul (src, da); + uint32_t b = hb_raster_alpha_mul (dst, 255 - sa); + uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF)); + uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF)); + uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF)); + uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24)); + return hb_raster_pack_pixel (rb, rg, rr, ra); + } + case HB_PAINT_COMPOSITE_MODE_DEST_ATOP: + { + uint32_t a = hb_raster_alpha_mul (dst, sa); + uint32_t b = hb_raster_alpha_mul (src, 255 - da); + uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF)); + uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF)); + uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF)); + uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24)); + return hb_raster_pack_pixel (rb, rg, rr, ra); + } + case HB_PAINT_COMPOSITE_MODE_XOR: + { + uint32_t a = hb_raster_alpha_mul (src, 255 - da); + uint32_t b = hb_raster_alpha_mul (dst, 255 - sa); + uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (a & 0xFF) + (b & 0xFF)); + uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((a >> 8) & 0xFF) + ((b >> 8) & 0xFF)); + uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((a >> 16) & 0xFF) + ((b >> 16) & 0xFF)); + uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (a >> 24) + (b >> 24)); + return hb_raster_pack_pixel (rb, rg, rr, ra); + } + case HB_PAINT_COMPOSITE_MODE_PLUS: + { + uint8_t rb = (uint8_t) hb_min (255u, (unsigned) (src & 0xFF) + (dst & 0xFF)); + uint8_t rg = (uint8_t) hb_min (255u, (unsigned) ((src >> 8) & 0xFF) + ((dst >> 8) & 0xFF)); + uint8_t rr = (uint8_t) hb_min (255u, (unsigned) ((src >> 16) & 0xFF) + ((dst >> 16) & 0xFF)); + uint8_t ra = (uint8_t) hb_min (255u, (unsigned) (src >> 24) + (dst >> 24)); + return hb_raster_pack_pixel (rb, rg, rr, ra); + } + + case HB_PAINT_COMPOSITE_MODE_MULTIPLY: return apply_separable_blend (src, dst, blend_multiply); + case HB_PAINT_COMPOSITE_MODE_SCREEN: return apply_separable_blend (src, dst, blend_screen); + case HB_PAINT_COMPOSITE_MODE_OVERLAY: return apply_separable_blend (src, dst, blend_overlay); + case HB_PAINT_COMPOSITE_MODE_DARKEN: return apply_separable_blend (src, dst, blend_darken); + case HB_PAINT_COMPOSITE_MODE_LIGHTEN: return apply_separable_blend (src, dst, blend_lighten); + case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return apply_separable_blend (src, dst, blend_color_dodge); + case HB_PAINT_COMPOSITE_MODE_COLOR_BURN: return apply_separable_blend (src, dst, blend_color_burn); + case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: return apply_separable_blend (src, dst, blend_hard_light); + case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: return apply_separable_blend (src, dst, blend_soft_light); + case HB_PAINT_COMPOSITE_MODE_DIFFERENCE: return apply_separable_blend (src, dst, blend_difference); + case HB_PAINT_COMPOSITE_MODE_EXCLUSION: return apply_separable_blend (src, dst, blend_exclusion); + + case HB_PAINT_COMPOSITE_MODE_HSL_HUE: + case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION: + case HB_PAINT_COMPOSITE_MODE_HSL_COLOR: + case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY: + return apply_hsl_blend (src, dst, mode); + + default: + return hb_raster_src_over (src, dst); + } +} + +/* hb_raster_image_t */ + +unsigned +hb_raster_image_t::bytes_per_pixel (hb_raster_format_t format) +{ + return format == HB_RASTER_FORMAT_BGRA32 ? 4u : 1u; +} + +bool +hb_raster_image_t::configure (hb_raster_format_t format, + hb_raster_extents_t extents) +{ + if (format != HB_RASTER_FORMAT_A8 && + format != HB_RASTER_FORMAT_BGRA32) + format = HB_RASTER_FORMAT_A8; + + unsigned bpp = bytes_per_pixel (format); + if (extents.width > UINT_MAX / bpp) + return false; + + unsigned min_stride = extents.width * bpp; + if (extents.stride == 0 || extents.stride < min_stride) + extents.stride = min_stride; + + if (extents.height && extents.stride > (size_t) -1 / extents.height) + return false; + + size_t buf_size = (size_t) extents.stride * extents.height; + if (buf_size > HB_RASTER_MAX_BUFFER_SIZE) + return false; + if (unlikely (!buffer.resize_dirty (buf_size))) + return false; + + this->format = format; + this->extents = extents; + return true; +} + +bool +hb_raster_image_t::deserialize_from_png (hb_blob_t *blob) +{ +#ifndef HAVE_PNG + return false; +#else + if (!blob) + return false; + + unsigned blob_len = 0; + const uint8_t *blob_data = (const uint8_t *) hb_blob_get_data (blob, &blob_len); + if (!blob_data || !blob_len) + return false; + + png_structp png = png_create_read_struct (PNG_LIBPNG_VER_STRING, nullptr, + hb_raster_png_error, + hb_raster_png_warning); + if (!png) + return false; + + png_infop info = png_create_info_struct (png); + if (!info) + { + png_destroy_read_struct (&png, nullptr, nullptr); + return false; + } + + hb_raster_png_read_blob_t reader; + reader.data = blob_data; + reader.size = (size_t) blob_len; + reader.offset = 0; + hb_vector_t rgba; + hb_vector_t rows; + if (setjmp (png_jmpbuf (png))) + { + png_destroy_read_struct (&png, &info, nullptr); + rgba.fini (); + rows.fini (); + return false; + } + + png_set_read_fn (png, &reader, hb_raster_png_read_blob); + png_read_info (png, info); + + png_uint_32 w = 0, h = 0; + int bit_depth = 0, color_type = 0; + int interlace_type = 0, compression_type = 0, filter_method = 0; + png_get_IHDR (png, info, &w, &h, &bit_depth, &color_type, + &interlace_type, &compression_type, &filter_method); + + if (!w || !h || w > (png_uint_32) INT_MAX || h > (png_uint_32) INT_MAX) + { + png_destroy_read_struct (&png, &info, nullptr); + return false; + } + + bool has_trns = png_get_valid (png, info, PNG_INFO_tRNS); + + if (bit_depth == 16) + png_set_strip_16 (png); + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb (png); + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand_gray_1_2_4_to_8 (png); + if (has_trns) + png_set_tRNS_to_alpha (png); + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb (png); + if (!(color_type & PNG_COLOR_MASK_ALPHA) && !has_trns) + png_set_add_alpha (png, 0xff, PNG_FILLER_AFTER); + if (interlace_type != PNG_INTERLACE_NONE) + png_set_interlace_handling (png); + + png_read_update_info (png, info); + + if (png_get_bit_depth (png, info) != 8 || png_get_channels (png, info) != 4) + { + png_destroy_read_struct (&png, &info, nullptr); + return false; + } + + png_size_t rowbytes = png_get_rowbytes (png, info); + if (rowbytes < (png_size_t) w * 4u) + { + png_destroy_read_struct (&png, &info, nullptr); + return false; + } + + size_t rgba_size = (size_t) rowbytes * (size_t) h; + if (h && rgba_size / (size_t) h != (size_t) rowbytes) + { + png_destroy_read_struct (&png, &info, nullptr); + return false; + } + + if (!rgba.resize (rgba_size)) + { + png_destroy_read_struct (&png, &info, nullptr); + return false; + } + + if (!rows.resize ((unsigned) h)) + { + png_destroy_read_struct (&png, &info, nullptr); + return false; + } + + for (unsigned y = 0; y < (unsigned) h; y++) + rows[y] = (png_bytep) (rgba.arrayZ + (size_t) y * (size_t) rowbytes); + + png_read_image (png, rows.arrayZ); + png_read_end (png, nullptr); + png_destroy_read_struct (&png, &info, nullptr); + + hb_raster_image_t decoded; + hb_raster_extents_t decoded_extents = {0, 0, (unsigned) w, (unsigned) h, 0}; + if (!decoded.configure (HB_RASTER_FORMAT_BGRA32, decoded_extents)) + return false; + + for (unsigned y = 0; y < (unsigned) h; y++) + { + hb_packed_t *dst = (hb_packed_t *) (decoded.buffer.arrayZ + (size_t) ((unsigned) h - 1 - y) * decoded.extents.stride); + const uint8_t *src = rgba.arrayZ + (size_t) y * (size_t) rowbytes; + for (unsigned x = 0; x < (unsigned) w; x++) + { + uint8_t r = src[4 * x + 0]; + uint8_t g = src[4 * x + 1]; + uint8_t b = src[4 * x + 2]; + uint8_t a = src[4 * x + 3]; + dst[x] = hb_packed_t ((uint32_t) hb_raster_div255 (b * a) + | ((uint32_t) hb_raster_div255 (g * a) << 8) + | ((uint32_t) hb_raster_div255 (r * a) << 16) + | ((uint32_t) a << 24)); + } + } + + hb_swap (buffer, decoded.buffer); + hb_swap (this->extents, decoded.extents); + hb_swap (format, decoded.format); + return true; +#endif +} + +hb_blob_t * +hb_raster_image_t::serialize_to_png_or_fail () const +{ +#ifndef HAVE_PNG + return nullptr; +#else + if (format != HB_RASTER_FORMAT_BGRA32 || !extents.width || !extents.height) + return nullptr; + + png_structp png = png_create_write_struct (PNG_LIBPNG_VER_STRING, nullptr, + hb_raster_png_error, + hb_raster_png_warning); + if (!png) + return nullptr; + + png_infop info = png_create_info_struct (png); + if (!info) + { + png_destroy_write_struct (&png, nullptr); + return nullptr; + } + + hb_raster_png_write_blob_t writer; + hb_vector_t rgba; + hb_vector_t rows; + if (setjmp (png_jmpbuf (png))) + { + png_destroy_write_struct (&png, &info); + writer.data.fini (); + rgba.fini (); + rows.fini (); + return nullptr; + } + + png_set_write_fn (png, &writer, hb_raster_png_write_blob, hb_raster_png_flush_blob); + png_set_IHDR (png, info, + extents.width, extents.height, + 8, PNG_COLOR_TYPE_RGBA, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info (png, info); + + size_t rowbytes = (size_t) extents.width * 4u; + size_t rgba_size = rowbytes * (size_t) extents.height; + if (extents.height && rgba_size / (size_t) extents.height != rowbytes) + { + png_destroy_write_struct (&png, &info); + return nullptr; + } + + if (!rgba.resize (rgba_size)) + { + png_destroy_write_struct (&png, &info); + return nullptr; + } + + if (!rows.resize (extents.height)) + { + png_destroy_write_struct (&png, &info); + return nullptr; + } + + for (unsigned y = 0; y < extents.height; y++) + { + uint8_t *dst = rgba.arrayZ + (size_t) y * rowbytes; + const uint8_t *src = buffer.arrayZ + (size_t) (extents.height - 1 - y) * extents.stride; + + for (unsigned x = 0; x < extents.width; x++) + { + uint32_t px; + hb_memcpy (&px, src + x * 4, 4); + uint8_t b = (uint8_t) (px & 0xFF); + uint8_t g = (uint8_t) ((px >> 8) & 0xFF); + uint8_t r = (uint8_t) ((px >> 16) & 0xFF); + uint8_t a = (uint8_t) (px >> 24); + + dst[4 * x + 3] = a; + if (a) + { + dst[4 * x + 0] = (uint8_t) hb_min (255u, ((unsigned) r * 255u + a / 2u) / a); + dst[4 * x + 1] = (uint8_t) hb_min (255u, ((unsigned) g * 255u + a / 2u) / a); + dst[4 * x + 2] = (uint8_t) hb_min (255u, ((unsigned) b * 255u + a / 2u) / a); + } + else + dst[4 * x + 0] = dst[4 * x + 1] = dst[4 * x + 2] = 0; + } + + rows[y] = (png_bytep) dst; + } + + png_write_image (png, rows.arrayZ); + png_write_end (png, info); + png_destroy_write_struct (&png, &info); + + unsigned length = 0; + char *data = writer.data.steal (&length); + if (!data && length) + return nullptr; + + hb_blob_t *blob = hb_blob_create_or_fail (data, length, + HB_MEMORY_MODE_WRITABLE, + data, hb_free); + if (!blob) + hb_free (data); + return blob; +#endif +} + +void +hb_raster_image_t::clear () +{ + size_t buf_size = (size_t) extents.stride * extents.height; + hb_memset (buffer.arrayZ, 0, buf_size); +} + +const uint8_t * +hb_raster_image_t::get_buffer () const +{ + return buffer.arrayZ; +} + +void +hb_raster_image_t::composite_from (const hb_raster_image_t *src, + hb_paint_composite_mode_t mode) +{ + unsigned w = extents.width; + unsigned h = extents.height; + unsigned stride = extents.stride; + + for (unsigned y = 0; y < h; y++) + { + hb_packed_t *dp = (hb_packed_t *) (buffer.arrayZ + y * stride); + const hb_packed_t *sp = (const hb_packed_t *) (src->buffer.arrayZ + y * stride); + for (unsigned x = 0; x < w; x++) + dp[x] = hb_packed_t (composite_pixel ((uint32_t) sp[x], (uint32_t) dp[x], mode)); + } +} + +/* Composite src image onto dst image. + * Both images must have the same extents and BGRA32 format. */ +void +hb_raster_image_composite (hb_raster_image_t *dst, + const hb_raster_image_t *src, + hb_paint_composite_mode_t mode) +{ + dst->composite_from (src, mode); +} + +/** + * hb_raster_image_create_or_fail: + * + * Creates a new raster image object. + * + * Return value: (transfer full): + * A newly allocated #hb_raster_image_t with a reference count of 1, + * or `NULL` on allocation failure. + * + * The returned image can be released with hb_raster_image_destroy(), or + * transferred for reuse with hb_raster_draw_recycle_image() or + * hb_raster_paint_recycle_image(). + * + * Since: 13.0.0 + **/ +hb_raster_image_t * +hb_raster_image_create_or_fail (void) +{ + return hb_object_create (); +} + +/** + * hb_raster_image_reference: (skip) + * @image: a raster image + * + * Increases the reference count on @image by one. + * + * This prevents @image from being destroyed until a matching + * call to hb_raster_image_destroy() is made. + * + * Return value: (transfer full): + * The referenced #hb_raster_image_t. + * + * Since: 13.0.0 + **/ +hb_raster_image_t * +hb_raster_image_reference (hb_raster_image_t *image) +{ + return hb_object_reference (image); +} + +/** + * hb_raster_image_destroy: (skip) + * @image: a raster image + * + * Decreases the reference count on @image by one. When the + * reference count reaches zero, the image and its pixel buffer + * are freed. + * + * Since: 13.0.0 + **/ +void +hb_raster_image_destroy (hb_raster_image_t *image) +{ + if (!hb_object_destroy (image)) return; + hb_free (image); +} + +/** + * hb_raster_image_set_user_data: (skip) + * @image: a raster image + * @key: the user-data key + * @data: a pointer to the user data + * @destroy: (nullable): a callback to call when @data is not needed anymore + * @replace: whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified raster image. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_image_set_user_data (hb_raster_image_t *image, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (image, key, data, destroy, replace); +} + +/** + * hb_raster_image_get_user_data: (skip) + * @image: a raster image + * @key: the user-data key + * + * Fetches the user-data associated with the specified key, + * attached to the specified raster image. + * + * Return value: (transfer none): + * A pointer to the user data + * + * Since: 13.0.0 + **/ +void * +hb_raster_image_get_user_data (hb_raster_image_t *image, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (image, key); +} + +/** + * hb_raster_image_configure: + * @image: a raster image + * @format: the pixel format + * @extents: (nullable): desired image extents + * + * Configures @image format and extents together, resizing backing storage + * at most once. This function does not clear pixel contents. + * + * Passing `NULL` for @extents clears extents and releases the backing + * allocation. + * + * Return value: `true` if configuration succeeds, `false` on allocation + * failure + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_image_configure (hb_raster_image_t *image, + hb_raster_format_t format, + const hb_raster_extents_t *extents) +{ + if (unlikely (!extents)) + { + image->extents = {}; + image->buffer.resize_exact (0); + return true; + } + return image->configure (format, *extents); +} + +/** + * hb_raster_image_clear: + * @image: a raster image + * + * Clears @image pixels to zero while keeping current extents and format. + * + * Since: 13.0.0 + **/ +void +hb_raster_image_clear (hb_raster_image_t *image) +{ + image->clear (); +} + +/** + * hb_raster_image_get_buffer: + * @image: a raster image + * + * Fetches the raw pixel buffer of @image. The buffer layout is + * described by the extents obtained from hb_raster_image_get_extents() + * and the format from hb_raster_image_get_format(). Rows are stored + * bottom-to-top. + * + * Return value: (transfer none) (array): + * The pixel buffer, or `NULL` + * + * Since: 13.0.0 + **/ +const uint8_t * +hb_raster_image_get_buffer (hb_raster_image_t *image) +{ + return image->get_buffer (); +} + +/** + * hb_raster_image_get_extents: + * @image: a raster image + * @extents: (out) (nullable): the image extents + * + * Fetches the pixel-buffer extents of @image. + * + * Since: 13.0.0 + **/ +void +hb_raster_image_get_extents (hb_raster_image_t *image, + hb_raster_extents_t *extents) +{ + if (extents) + *extents = image->extents; +} + +/** + * hb_raster_image_get_format: + * @image: a raster image + * + * Fetches the pixel format of @image. + * + * Return value: + * The #hb_raster_format_t of the image + * + * Since: 13.0.0 + **/ +hb_raster_format_t +hb_raster_image_get_format (hb_raster_image_t *image) +{ + return image->format; +} + +/** + * hb_raster_image_deserialize_from_png_or_fail: + * @image: a raster image + * @png: PNG data + * + * Replaces @image contents by deserializing a PNG blob into a + * #HB_RASTER_FORMAT_BGRA32 raster image. + * + * On success, @image extents are reset to pixel extents with origin + * `(0, 0)`. Rows in the resulting image buffer are stored bottom-to-top. + * On failure, @image is left unchanged. + * + * Return value: `true` if deserialization succeeded, `false` otherwise + * + * Since: 13.1.0 + **/ +hb_bool_t +hb_raster_image_deserialize_from_png_or_fail (hb_raster_image_t *image, + hb_blob_t *png) +{ + return image->deserialize_from_png (png); +} + +/** + * hb_raster_image_serialize_to_png_or_fail: + * @image: a raster image + * + * Serializes @image to a PNG blob. + * + * Currently only #HB_RASTER_FORMAT_BGRA32 images are supported. + * + * Return value: (transfer full): + * A newly allocated PNG #hb_blob_t, or `NULL` on failure + * + * Since: 13.1.0 + **/ +hb_blob_t * +hb_raster_image_serialize_to_png_or_fail (hb_raster_image_t *image) +{ + return image->serialize_to_png_or_fail (); +} diff --git a/thirdparty/harfbuzz/src/hb-raster-image.hh b/thirdparty/harfbuzz/src/hb-raster-image.hh new file mode 100644 index 0000000000..d2e81ba106 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-image.hh @@ -0,0 +1,63 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_IMAGE_HH +#define HB_RASTER_IMAGE_HH + +#include "hb.hh" +#include "hb-raster-utils.hh" +#include "hb-raster.h" +#include "hb-object.hh" +#include "hb-vector.hh" + + +/* hb_raster_image_t — pixel artifact */ +struct hb_raster_image_t +{ + hb_object_header_t header; + + hb_vector_t buffer; + hb_raster_extents_t extents = {}; + hb_raster_format_t format = HB_RASTER_FORMAT_A8; + + HB_INTERNAL static unsigned bytes_per_pixel (hb_raster_format_t format); + HB_INTERNAL bool configure (hb_raster_format_t format, hb_raster_extents_t extents); + HB_INTERNAL bool deserialize_from_png (hb_blob_t *png); + HB_INTERNAL hb_blob_t *serialize_to_png_or_fail () const; + HB_INTERNAL void clear (); + HB_INTERNAL const uint8_t *get_buffer () const; + HB_INTERNAL void composite_from (const hb_raster_image_t *src, + hb_paint_composite_mode_t mode); +}; + +/* Composite src image onto dst. */ +HB_INTERNAL void +hb_raster_image_composite (hb_raster_image_t *dst, + const hb_raster_image_t *src, + hb_paint_composite_mode_t mode); + + +#endif /* HB_RASTER_IMAGE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-paint.cc b/thirdparty/harfbuzz/src/hb-raster-paint.cc new file mode 100644 index 0000000000..84657cdd60 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-paint.cc @@ -0,0 +1,2253 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-raster-paint.hh" +#include "hb-raster-svg.hh" +#include "hb-machinery.hh" + +#include + + +/* + * Pixel helpers (paint-specific) + */ + +/* Convert unpremultiplied hb_color_t (BGRA order) to premultiplied BGRA32 pixel. */ +static inline uint32_t +color_to_premul_pixel (hb_color_t color) +{ + uint8_t a = hb_color_get_alpha (color); + uint8_t r = hb_raster_div255 (hb_color_get_red (color) * a); + uint8_t g = hb_raster_div255 (hb_color_get_green (color) * a); + uint8_t b = hb_raster_div255 (hb_color_get_blue (color) * a); + return (uint32_t) b | ((uint32_t) g << 8) | ((uint32_t) r << 16) | ((uint32_t) a << 24); +} + +/* Bilinear sample from a premultiplied BGRA32 image. */ +static inline uint32_t +hb_raster_sample_bilinear_premul (const hb_packed_t *src, + unsigned width, + unsigned height, + float x, + float y) +{ + int x0 = (int) floorf (x); + int y0 = (int) floorf (y); + int x1 = x0 + 1; + int y1 = y0 + 1; + if (x1 >= (int) width) x1 = (int) width - 1; + if (y1 >= (int) height) y1 = (int) height - 1; + + float tx = x - x0; + float ty = y - y0; + float omtx = 1.f - tx; + float omty = 1.f - ty; + + uint32_t p00 = (uint32_t) src[(size_t) y0 * width + (size_t) x0]; + uint32_t p10 = (uint32_t) src[(size_t) y0 * width + (size_t) x1]; + uint32_t p01 = (uint32_t) src[(size_t) y1 * width + (size_t) x0]; + uint32_t p11 = (uint32_t) src[(size_t) y1 * width + (size_t) x1]; + + float w00 = omtx * omty; + float w10 = tx * omty; + float w01 = omtx * ty; + float w11 = tx * ty; + + float b = ((p00 >> 0) & 0xff) * w00 + ((p10 >> 0) & 0xff) * w10 + + ((p01 >> 0) & 0xff) * w01 + ((p11 >> 0) & 0xff) * w11; + float g = ((p00 >> 8) & 0xff) * w00 + ((p10 >> 8) & 0xff) * w10 + + ((p01 >> 8) & 0xff) * w01 + ((p11 >> 8) & 0xff) * w11; + float r = ((p00 >> 16) & 0xff) * w00 + ((p10 >> 16) & 0xff) * w10 + + ((p01 >> 16) & 0xff) * w01 + ((p11 >> 16) & 0xff) * w11; + float a = ((p00 >> 24) & 0xff) * w00 + ((p10 >> 24) & 0xff) * w10 + + ((p01 >> 24) & 0xff) * w01 + ((p11 >> 24) & 0xff) * w11; + + return (uint32_t) (b + 0.5f) + | ((uint32_t) (g + 0.5f) << 8) + | ((uint32_t) (r + 0.5f) << 16) + | ((uint32_t) (a + 0.5f) << 24); +} + + +/* + * Paint callbacks + */ + +/* Lazy initialization: set up root surface, initial clip and transform. + * Called from every paint callback that needs state. + * hb_font_paint_glyph() does NOT wrap with push/pop_transform, + * so the first callback could be push_clip_glyph or paint_color. */ +static void +ensure_initialized (hb_raster_paint_t *c) +{ + if (c->surface_stack.length) return; + + /* Root surface */ + hb_raster_image_t *root = c->acquire_surface (); + if (unlikely (!root)) return; + if (unlikely (!c->surface_stack.push_or_fail (root))) + { + c->release_surface (root); + return; + } + + /* Initial transform */ + if (unlikely (!c->transform_stack.push_or_fail (c->base_transform))) + { + c->release_surface (c->surface_stack.pop ()); + return; + } + + /* Initial clip: full coverage rectangle */ + hb_raster_clip_t clip; + clip.init_full (c->fixed_extents.width, c->fixed_extents.height); + if (unlikely (!c->clip_stack.push_or_fail (std::move (clip)))) + { + c->transform_stack.pop (); + c->release_surface (c->surface_stack.pop ()); + return; + } +} + +static void +hb_raster_paint_push_transform (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + + ensure_initialized (c); + if (unlikely (!c->transform_stack.length)) return; + + hb_transform_t<> t = c->current_transform (); + t.multiply ({xx, yx, xy, yy, dx, dy}); + (void) c->transform_stack.push (t); +} + +static void +hb_raster_paint_pop_transform (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + c->transform_stack.pop (); +} + +static hb_bool_t +hb_raster_paint_color_glyph (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_font_t *font HB_UNUSED, + void *user_data HB_UNUSED) +{ + return false; +} + +typedef void (*hb_raster_paint_clip_mask_emit_t) (hb_raster_draw_t *rdr, void *user_data); + +static void +hb_raster_paint_push_empty_clip (hb_raster_paint_t *c, unsigned w, unsigned h) +{ + hb_raster_clip_t new_clip = c->acquire_clip (w, h); + new_clip.init_full (w, h); + new_clip.is_rect = true; + new_clip.rect_x0 = new_clip.rect_y0 = 0; + new_clip.rect_x1 = new_clip.rect_y1 = 0; + new_clip.min_x = new_clip.min_y = new_clip.max_x = new_clip.max_y = 0; + (void) c->clip_stack.push (std::move (new_clip)); +} + +static void +hb_raster_paint_push_clip_from_emitter (hb_raster_paint_t *c, + hb_raster_paint_clip_mask_emit_t emit, + void *emit_data) +{ + ensure_initialized (c); + + hb_raster_image_t *surf = c->current_surface (); + if (unlikely (!surf)) return; + + unsigned w = surf->extents.width; + unsigned h = surf->extents.height; + + hb_raster_clip_t new_clip = c->acquire_clip (w, h); + + hb_raster_draw_t *rdr = c->clip_rdr; + hb_transform_t<> t = c->current_effective_transform (); + hb_raster_draw_set_transform (rdr, t.xx, t.yx, t.xy, t.yy, t.x0, t.y0); + emit (rdr, emit_data); + hb_raster_image_t *mask_img = hb_raster_draw_render (rdr); + + if (unlikely (!mask_img)) + { + hb_raster_paint_push_empty_clip (c, w, h); + return; + } + + /* Allocate alpha buffer and intersect with previous clip */ + size_t clip_size = (size_t) new_clip.stride * h; + if (unlikely (clip_size > HB_RASTER_MAX_BUFFER_SIZE || + !new_clip.alpha.resize ((unsigned) clip_size))) + { + hb_raster_draw_recycle_image (rdr, mask_img); + hb_raster_paint_push_empty_clip (c, w, h); + return; + } + + const uint8_t *mask_buf = hb_raster_image_get_buffer (mask_img); + hb_raster_extents_t mask_ext; + hb_raster_image_get_extents (mask_img, &mask_ext); + const hb_raster_clip_t &old_clip = c->current_clip (); + + /* Convert mask extents from surface coordinates to clip-buffer coordinates. */ + int mask_x0 = mask_ext.x_origin - surf->extents.x_origin; + int mask_y0 = mask_ext.y_origin - surf->extents.y_origin; + int mask_x1 = mask_x0 + (int) mask_ext.width; + int mask_y1 = mask_y0 + (int) mask_ext.height; + + int ix0_i = hb_max ((int) old_clip.min_x, hb_max (mask_x0, 0)); + int iy0_i = hb_max ((int) old_clip.min_y, hb_max (mask_y0, 0)); + int ix1_i = hb_min ((int) old_clip.max_x, hb_min (mask_x1, (int) w)); + int iy1_i = hb_min ((int) old_clip.max_y, hb_min (mask_y1, (int) h)); + + if (ix0_i >= ix1_i || iy0_i >= iy1_i) + { + hb_raster_draw_recycle_image (rdr, mask_img); + hb_raster_paint_push_empty_clip (c, w, h); + return; + } + + unsigned ix0 = (unsigned) ix0_i; + unsigned iy0 = (unsigned) iy0_i; + unsigned ix1 = (unsigned) ix1_i; + unsigned iy1 = (unsigned) iy1_i; + + new_clip.min_x = w; new_clip.min_y = h; + new_clip.max_x = 0; new_clip.max_y = 0; + + if (old_clip.is_rect) + { + for (unsigned y = iy0; y < iy1; y++) + { + const uint8_t *mask_row = mask_buf + (unsigned) ((int) y - mask_y0) * mask_ext.stride; + uint8_t *out_row = new_clip.alpha.arrayZ + y * new_clip.stride; + unsigned row_min = ix1; + unsigned row_max = ix0; + unsigned mx = (unsigned) ((int) ix0 - mask_x0); + for (unsigned x = ix0; x < ix1; x++) + { + uint8_t a = mask_row[mx++]; + out_row[x] = a; + if (a && row_min == ix1) + { + row_min = x; + row_max = x + 1; + } + else if (a) + row_max = x + 1; + } + if (row_min < row_max) + { + new_clip.min_x = hb_min (new_clip.min_x, row_min); + new_clip.min_y = hb_min (new_clip.min_y, y); + new_clip.max_x = hb_max (new_clip.max_x, row_max); + new_clip.max_y = hb_max (new_clip.max_y, y + 1); + } + } + } + else + { + for (unsigned y = iy0; y < iy1; y++) + { + const uint8_t *old_row = old_clip.alpha.arrayZ + y * old_clip.stride; + const uint8_t *mask_row = mask_buf + (unsigned) ((int) y - mask_y0) * mask_ext.stride; + uint8_t *out_row = new_clip.alpha.arrayZ + y * new_clip.stride; + unsigned row_min = ix1; + unsigned row_max = ix0; + for (unsigned x = ix0; x < ix1; x++) + { + unsigned mx = (unsigned) ((int) x - mask_x0); + uint8_t a = hb_raster_div255 (mask_row[mx] * old_row[x]); + out_row[x] = a; + if (a) + { + row_min = hb_min (row_min, x); + row_max = x + 1; + } + } + if (row_min < row_max) + { + new_clip.min_x = hb_min (new_clip.min_x, row_min); + new_clip.min_y = hb_min (new_clip.min_y, y); + new_clip.max_x = hb_max (new_clip.max_x, row_max); + new_clip.max_y = hb_max (new_clip.max_y, y + 1); + } + } + } + + hb_raster_draw_recycle_image (rdr, mask_img); + if (unlikely (!c->clip_stack.push_or_fail (std::move (new_clip)))) + hb_raster_paint_push_empty_clip (c, w, h); +} + +struct hb_raster_paint_glyph_clip_data_t +{ + hb_codepoint_t glyph; + hb_font_t *font; +}; + +static void +hb_raster_paint_emit_clip_glyph_mask (hb_raster_draw_t *rdr, void *user_data) +{ + hb_raster_paint_glyph_clip_data_t *data = (hb_raster_paint_glyph_clip_data_t *) user_data; + /* Let draw-render choose tight glyph extents; we map by mask origin below. */ + hb_font_draw_glyph (data->font, data->glyph, hb_raster_draw_get_funcs (), rdr); +} + +static void +hb_raster_paint_push_clip_glyph (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + hb_raster_paint_glyph_clip_data_t data = {glyph, font}; + hb_raster_paint_push_clip_from_emitter (c, hb_raster_paint_emit_clip_glyph_mask, &data); +} + +/* Push clip from arbitrary path emitter (used by SVG rasterizer). + * Identical to push_clip_glyph but calls user func instead of hb_font_draw_glyph. */ +struct hb_raster_paint_path_clip_data_t +{ + hb_raster_svg_path_func_t func; + void *user_data; +}; + +static void +hb_raster_paint_emit_clip_path_mask (hb_raster_draw_t *rdr, void *user_data) +{ + hb_raster_paint_path_clip_data_t *data = (hb_raster_paint_path_clip_data_t *) user_data; + data->func (hb_raster_draw_get_funcs (), rdr, data->user_data); +} + +void +hb_raster_paint_push_clip_path (hb_raster_paint_t *c, + hb_raster_svg_path_func_t func, + void *user_data) +{ + hb_raster_paint_path_clip_data_t data = {func, user_data}; + hb_raster_paint_push_clip_from_emitter (c, hb_raster_paint_emit_clip_path_mask, &data); +} + +static void +hb_raster_paint_push_clip_rectangle (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + float xmin, float ymin, + float xmax, float ymax, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + + ensure_initialized (c); + + if (!c->surface_stack.length) return; + + hb_transform_t<> t = c->current_effective_transform (); + + hb_raster_image_t *surf = c->current_surface (); + if (unlikely (!surf)) return; + + unsigned w = surf->extents.width; + unsigned h = surf->extents.height; + bool is_axis_aligned = (t.xy == 0.f && t.yx == 0.f); + + /* Transform the four corners to pixel space */ + float cx[4], cy[4]; + cx[0] = xmin; cy[0] = ymin; + cx[1] = xmax; cy[1] = ymin; + cx[2] = xmax; cy[2] = ymax; + cx[3] = xmin; cy[3] = ymax; + for (unsigned i = 0; i < 4; i++) + t.transform_point (cx[i], cy[i]); + + /* Compute bounding box in pixel coords */ + float fmin_x = cx[0], fmin_y = cy[0], fmax_x = cx[0], fmax_y = cy[0]; + for (unsigned i = 1; i < 4; i++) + { + fmin_x = hb_min (fmin_x, cx[i]); fmin_y = hb_min (fmin_y, cy[i]); + fmax_x = hb_max (fmax_x, cx[i]); fmax_y = hb_max (fmax_y, cy[i]); + } + + int px0 = (int) floorf (fmin_x) - surf->extents.x_origin; + int py0 = (int) floorf (fmin_y) - surf->extents.y_origin; + int px1 = (int) ceilf (fmax_x) - surf->extents.x_origin; + int py1 = (int) ceilf (fmax_y) - surf->extents.y_origin; + + /* Clamp to surface bounds */ + px0 = hb_max (px0, 0); + py0 = hb_max (py0, 0); + px1 = hb_min (px1, (int) w); + py1 = hb_min (py1, (int) h); + + const hb_raster_clip_t &old_clip = c->current_clip (); + + hb_raster_clip_t new_clip = c->acquire_clip (w, h); + + if (is_axis_aligned && old_clip.is_rect) + { + /* Fast path: axis-aligned rect-on-rect intersection */ + new_clip.is_rect = true; + new_clip.rect_x0 = hb_max (px0, old_clip.rect_x0); + new_clip.rect_y0 = hb_max (py0, old_clip.rect_y0); + new_clip.rect_x1 = hb_min (px1, old_clip.rect_x1); + new_clip.rect_y1 = hb_min (py1, old_clip.rect_y1); + new_clip.update_bounds_from_rect (); + } + else + { + /* General case: rasterize transformed quad as alpha mask */ + new_clip.is_rect = false; + size_t clip_size = (size_t) new_clip.stride * h; + if (unlikely (clip_size > HB_RASTER_MAX_BUFFER_SIZE || + !new_clip.alpha.resize ((unsigned) clip_size))) + { + hb_raster_paint_push_empty_clip (c, w, h); + return; + } + hb_memset (new_clip.alpha.arrayZ, 0, (unsigned) clip_size); + + /* Convert quad corners to pixel-relative coords */ + float qx[4], qy[4]; + int ox = surf->extents.x_origin; + int oy = surf->extents.y_origin; + for (unsigned i = 0; i < 4; i++) + { + qx[i] = cx[i] - ox; + qy[i] = cy[i] - oy; + } + + /* For each pixel in the bounding box, test if inside the quad + * using cross-product edge tests (winding order). */ + unsigned iy0 = (unsigned) hb_max (py0, (int) old_clip.min_y); + unsigned iy1 = (unsigned) hb_min (py1, (int) old_clip.max_y); + unsigned ix0 = (unsigned) hb_max (px0, (int) old_clip.min_x); + unsigned ix1 = (unsigned) hb_min (px1, (int) old_clip.max_x); + new_clip.min_x = w; new_clip.min_y = h; + new_clip.max_x = 0; new_clip.max_y = 0; + + /* Precompute edge normals for point-in-quad test. + * Edge i goes from corner i to corner (i+1)%4. + * Normal = (dy, -dx); inside test: dot(normal, p-corner) >= 0 */ + float enx[4], eny[4], ed[4]; + for (unsigned i = 0; i < 4; i++) + { + unsigned j = (i + 1) & 3; + float edx = qx[j] - qx[i], edy = qy[j] - qy[i]; + enx[i] = edy; /* normal x */ + eny[i] = -edx; /* normal y */ + ed[i] = enx[i] * qx[i] + eny[i] * qy[i]; /* distance threshold */ + } + float area2 = 0.f; + for (unsigned i = 0; i < 4; i++) + { + unsigned j = (i + 1) & 3; + area2 += qx[i] * qy[j] - qx[j] * qy[i]; + } + bool ccw = area2 >= 0.f; + + if (old_clip.is_rect) + { + for (unsigned y = iy0; y < iy1; y++) + for (unsigned x = ix0; x < ix1; x++) + { + float px_f = x + 0.5f, py_f = y + 0.5f; + /* Test if pixel center is inside the quad */ + bool inside = true; + for (unsigned i = 0; i < 4; i++) + { + float d = enx[i] * px_f + eny[i] * py_f; + if (ccw ? d < ed[i] : d > ed[i]) + { + inside = false; + break; + } + } + uint8_t a = inside ? 255 : 0; + new_clip.alpha[y * new_clip.stride + x] = a; + if (a) + { + new_clip.min_x = hb_min (new_clip.min_x, x); + new_clip.min_y = hb_min (new_clip.min_y, y); + new_clip.max_x = hb_max (new_clip.max_x, x + 1); + new_clip.max_y = hb_max (new_clip.max_y, y + 1); + } + } + } + else + { + for (unsigned y = iy0; y < iy1; y++) + { + const uint8_t *old_row = old_clip.alpha.arrayZ + y * old_clip.stride; + for (unsigned x = ix0; x < ix1; x++) + { + float px_f = x + 0.5f, py_f = y + 0.5f; + /* Test if pixel center is inside the quad */ + bool inside = true; + for (unsigned i = 0; i < 4; i++) + { + float d = enx[i] * px_f + eny[i] * py_f; + if (ccw ? d < ed[i] : d > ed[i]) + { + inside = false; + break; + } + } + uint8_t a = inside ? old_row[x] : 0; + new_clip.alpha[y * new_clip.stride + x] = a; + if (a) + { + new_clip.min_x = hb_min (new_clip.min_x, x); + new_clip.min_y = hb_min (new_clip.min_y, y); + new_clip.max_x = hb_max (new_clip.max_x, x + 1); + new_clip.max_y = hb_max (new_clip.max_y, y + 1); + } + } + } + } + } + + if (unlikely (!c->clip_stack.push_or_fail (std::move (new_clip)))) + hb_raster_paint_push_empty_clip (c, surf->extents.width, surf->extents.height); +} + +static void +hb_raster_paint_pop_clip (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + if (!c->clip_stack.length) return; + c->release_clip (c->clip_stack.pop ()); +} + +static void +hb_raster_paint_push_group (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + + ensure_initialized (c); + + hb_raster_image_t *new_surf = c->acquire_surface (); + if (unlikely (!new_surf)) return; + if (unlikely (!c->surface_stack.push_or_fail (new_surf))) + c->release_surface (new_surf); +} + +static void +hb_raster_paint_pop_group (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_paint_composite_mode_t mode, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + + if (c->surface_stack.length < 2) return; + + hb_raster_image_t *src = c->surface_stack.pop (); + hb_raster_image_t *dst = c->current_surface (); + + if (dst && src) + hb_raster_image_composite (dst, src, mode); + + c->release_surface (src); +} + +static void +hb_raster_paint_color (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_bool_t is_foreground, + hb_color_t color, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + + ensure_initialized (c); + + hb_raster_image_t *surf = c->current_surface (); + if (unlikely (!surf)) return; + + if (is_foreground) + { + /* Use foreground color, modulating alpha */ + color = HB_COLOR (hb_color_get_blue (c->foreground), + hb_color_get_green (c->foreground), + hb_color_get_red (c->foreground), + hb_raster_div255 (hb_color_get_alpha (c->foreground) * + hb_color_get_alpha (color))); + } + + uint32_t premul = color_to_premul_pixel (color); + uint8_t premul_a = (uint8_t) (premul >> 24); + const hb_raster_clip_t &clip = c->current_clip (); + + unsigned stride = surf->extents.stride; + if (clip.min_x >= clip.max_x || clip.min_y >= clip.max_y) return; + if (premul_a == 0) return; + + if (likely (!clip.is_rect)) + { + for (unsigned y = clip.min_y; y < clip.max_y; y++) + { + hb_packed_t *__restrict row = (hb_packed_t *) (surf->buffer.arrayZ + y * stride); + const uint8_t *__restrict clip_row = clip.alpha.arrayZ + y * clip.stride; + for (unsigned x = clip.min_x; x < clip.max_x; x++) + { + uint8_t clip_alpha = clip_row[x]; + if (clip_alpha == 0) continue; + if (clip_alpha == 255) + { + if (premul_a == 255) + row[x] = hb_packed_t (premul); + else + row[x] = hb_packed_t (hb_raster_src_over (premul, (uint32_t) row[x])); + } + else + { + uint32_t src = hb_raster_alpha_mul (premul, clip_alpha); + row[x] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[x])); + } + } + } + } + else + { + for (unsigned y = clip.min_y; y < clip.max_y; y++) + { + hb_packed_t *row = (hb_packed_t *) (surf->buffer.arrayZ + y * stride); + for (unsigned x = clip.min_x; x < clip.max_x; x++) + row[x] = hb_packed_t (hb_raster_src_over (premul, (uint32_t) row[x])); + } + } +} + +static hb_bool_t +hb_raster_paint_image (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_blob_t *blob, + unsigned width, + unsigned height, + hb_tag_t format, + float slant HB_UNUSED, + hb_glyph_extents_t *extents, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + + ensure_initialized (c); + + /* Handle SVG format */ + if (format == HB_PAINT_IMAGE_FORMAT_SVG) + return hb_raster_svg_render (c, blob, c->svg_glyph, c->svg_font, + c->svg_palette, c->foreground); + + unsigned src_width = width; + unsigned src_height = height; + const hb_packed_t *src_data = nullptr; + hb_raster_image_t decoded_png; + + if (format == HB_PAINT_IMAGE_FORMAT_BGRA) + { + if (src_width == 0 || src_height == 0) + return false; + if (src_width > (unsigned) INT_MAX || src_height > (unsigned) INT_MAX) + return false; + + unsigned data_len; + const uint8_t *data = (const uint8_t *) hb_blob_get_data (blob, &data_len); + size_t pixel_count = (size_t) src_width * (size_t) src_height; + if (src_width && pixel_count / src_width != src_height) + return false; + if (pixel_count > (size_t) -1 / 4u) + return false; + size_t required_size = pixel_count * 4u; + if (!data || (size_t) data_len < required_size) + return false; + + src_data = (const hb_packed_t *) data; + } + else if (format == HB_PAINT_IMAGE_FORMAT_PNG) + { +#ifdef HAVE_PNG + if (!decoded_png.deserialize_from_png (blob)) + return false; + src_width = decoded_png.extents.width; + src_height = decoded_png.extents.height; + src_data = (const hb_packed_t *) decoded_png.buffer.arrayZ; +#else + return false; +#endif + } + else + return false; + + hb_raster_image_t *surf = c->current_surface (); + if (unlikely (!surf)) return false; + if (!extents) return false; + + const hb_raster_clip_t &clip = c->current_clip (); + hb_transform_t<> t = c->current_effective_transform (); + + /* Compute inverse transform for sampling */ + float det = t.xx * t.yy - t.xy * t.yx; + if (fabsf (det) < 1e-10f) return false; + float inv_det = 1.f / det; + float inv_xx = t.yy * inv_det; + float inv_xy = -t.xy * inv_det; + float inv_yx = -t.yx * inv_det; + float inv_yy = t.xx * inv_det; + float inv_x0 = (t.xy * t.y0 - t.yy * t.x0) * inv_det; + float inv_y0 = (t.yx * t.x0 - t.xx * t.y0) * inv_det; + + unsigned surf_stride = surf->extents.stride; + int ox = surf->extents.x_origin; + int oy = surf->extents.y_origin; + + /* Image source rectangle in glyph space */ + float img_x = extents->x_bearing; + float img_y = extents->y_bearing; + float img_sx = (float) extents->width / src_width; + float img_sy = (float) extents->height / src_height; + if (fabsf (img_sx) < 1e-10f || fabsf (img_sy) < 1e-10f) + return false; + + if (clip.is_rect) + { + for (unsigned py = clip.min_y; py < clip.max_y; py++) + { + hb_packed_t *row = (hb_packed_t *) (surf->buffer.arrayZ + py * surf_stride); + float gx = inv_xx * (float) ((int) clip.min_x + ox) + inv_xy * (float) ((int) py + oy) + inv_x0; + float gy = inv_yx * (float) ((int) clip.min_x + ox) + inv_yy * (float) ((int) py + oy) + inv_y0; + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + /* Map glyph space to image texel; bilinear reconstruction. */ + float ix = (gx - img_x) / img_sx; + float iy = (float) (src_height - 1) - (gy - img_y) / img_sy; + + if (ix < 0.f || iy < 0.f || + ix > (float) (src_width - 1) || iy > (float) (src_height - 1)) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + + uint32_t src_px = hb_raster_sample_bilinear_premul (src_data, src_width, src_height, + ix, iy); + row[px] = hb_packed_t (hb_raster_src_over (src_px, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + } + else + { + for (unsigned py = clip.min_y; py < clip.max_y; py++) + { + hb_packed_t *row = (hb_packed_t *) (surf->buffer.arrayZ + py * surf_stride); + const uint8_t *clip_row = clip.alpha.arrayZ + py * clip.stride; + float gx = inv_xx * (float) ((int) clip.min_x + ox) + inv_xy * (float) ((int) py + oy) + inv_x0; + float gy = inv_yx * (float) ((int) clip.min_x + ox) + inv_yy * (float) ((int) py + oy) + inv_y0; + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + uint8_t clip_alpha = clip_row[px]; + if (clip_alpha == 0) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + + /* Map glyph space to image texel; bilinear reconstruction. */ + float ix = (gx - img_x) / img_sx; + float iy = (float) (src_height - 1) - (gy - img_y) / img_sy; + + if (ix < 0.f || iy < 0.f || + ix > (float) (src_width - 1) || iy > (float) (src_height - 1)) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + + uint32_t src_px = hb_raster_sample_bilinear_premul (src_data, src_width, src_height, + ix, iy); + src_px = hb_raster_alpha_mul (src_px, clip_alpha); + row[px] = hb_packed_t (hb_raster_src_over (src_px, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + } + + return true; +} + + +/* + * Gradient helpers + */ + +#define PREALLOCATED_COLOR_STOPS 16 +#define GRADIENT_LUT_SIZE 256 +#define GRADIENT_LUT_MIN_PIXELS (64u * 64u) + +static int +cmp_color_stop (const void *p1, const void *p2) +{ + const hb_color_stop_t *c1 = (const hb_color_stop_t *) p1; + const hb_color_stop_t *c2 = (const hb_color_stop_t *) p2; + if (c1->offset < c2->offset) return -1; + if (c1->offset > c2->offset) return 1; + return 0; +} + +static bool +get_color_stops (hb_raster_paint_t *c, + hb_color_line_t *color_line, + unsigned *count, + hb_color_stop_t **stops) +{ + unsigned len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr); + if (len > *count) + { + if (unlikely (!c->scratch_color_stops.resize (len))) + return false; + *stops = c->scratch_color_stops.arrayZ; + } + hb_color_line_get_color_stops (color_line, 0, &len, *stops); + for (unsigned i = 0; i < len; i++) + if ((*stops)[i].is_foreground) + (*stops)[i].color = HB_COLOR (hb_color_get_blue (c->foreground), + hb_color_get_green (c->foreground), + hb_color_get_red (c->foreground), + hb_raster_div255 (hb_color_get_alpha (c->foreground) * + hb_color_get_alpha ((*stops)[i].color))); + + *count = len; + return true; +} + +static void +normalize_color_line (hb_color_stop_t *stops, + unsigned len, + float *omin, float *omax) +{ + hb_qsort (stops, len, sizeof (hb_color_stop_t), cmp_color_stop); + + float mn = stops[0].offset, mx = stops[0].offset; + for (unsigned i = 1; i < len; i++) + { + mn = hb_min (mn, stops[i].offset); + mx = hb_max (mx, stops[i].offset); + } + if (mn != mx) + for (unsigned i = 0; i < len; i++) + stops[i].offset = (stops[i].offset - mn) / (mx - mn); + + *omin = mn; + *omax = mx; +} + +/* Evaluate color at normalized position t, interpolating in premultiplied space. */ +static uint32_t +evaluate_color_line (const hb_color_stop_t *stops, unsigned len, float t, + hb_paint_extend_t extend) +{ + /* Apply extend mode */ + if (extend == HB_PAINT_EXTEND_PAD) + { + t = hb_clamp (t, 0.f, 1.f); + } + else if (extend == HB_PAINT_EXTEND_REPEAT) + { + t = t - floorf (t); + } + else /* REFLECT */ + { + if (t < 0) t = -t; + int period = (int) floorf (t); + float frac = t - (float) period; + t = (period & 1) ? 1.f - frac : frac; + } + + /* Find bounding stops */ + if (t <= stops[0].offset) + return color_to_premul_pixel (stops[0].color); + if (t >= stops[len - 1].offset) + return color_to_premul_pixel (stops[len - 1].color); + + unsigned i; + for (i = 0; i < len - 1; i++) + if (t < stops[i + 1].offset) + break; + + float range = stops[i + 1].offset - stops[i].offset; + float k = range > 0.f ? (t - stops[i].offset) / range : 0.f; + + /* Interpolate in premultiplied [0,255] space */ + hb_color_t c0 = stops[i].color; + hb_color_t c1 = stops[i + 1].color; + + float a0 = hb_color_get_alpha (c0) / 255.f; + float r0 = hb_color_get_red (c0) / 255.f * a0; + float g0 = hb_color_get_green (c0) / 255.f * a0; + float b0 = hb_color_get_blue (c0) / 255.f * a0; + + float a1 = hb_color_get_alpha (c1) / 255.f; + float r1 = hb_color_get_red (c1) / 255.f * a1; + float g1 = hb_color_get_green (c1) / 255.f * a1; + float b1 = hb_color_get_blue (c1) / 255.f * a1; + + float a = a0 + k * (a1 - a0); + float r = r0 + k * (r1 - r0); + float g = g0 + k * (g1 - g0); + float b = b0 + k * (b1 - b0); + + uint8_t pa = (uint8_t) (a * 255.f + 0.5f); + uint8_t pr = (uint8_t) (r * 255.f + 0.5f); + uint8_t pg = (uint8_t) (g * 255.f + 0.5f); + uint8_t pb = (uint8_t) (b * 255.f + 0.5f); + + return (uint32_t) pb | ((uint32_t) pg << 8) | ((uint32_t) pr << 16) | ((uint32_t) pa << 24); +} + +static HB_ALWAYS_INLINE float +normalize_gradient_t (float t, hb_paint_extend_t extend) +{ + if (extend == HB_PAINT_EXTEND_PAD) + return hb_clamp (t, 0.f, 1.f); + if (extend == HB_PAINT_EXTEND_REPEAT) + { + t = t - floorf (t); + return t < 0.f ? t + 1.f : t; + } + + /* REFLECT */ + if (t < 0.f) t = -t; + int period = (int) floorf (t); + float frac = t - (float) period; + return (period & 1) ? 1.f - frac : frac; +} + +static void +build_gradient_lut (const hb_color_stop_t *stops, + unsigned len, + uint32_t *lut) +{ + for (unsigned i = 0; i < GRADIENT_LUT_SIZE; i++) + { + float t = (float) i / (GRADIENT_LUT_SIZE - 1); + lut[i] = evaluate_color_line (stops, len, t, HB_PAINT_EXTEND_PAD); + } +} + +static HB_ALWAYS_INLINE uint32_t +lookup_gradient_lut (const uint32_t *lut, + float t, + hb_paint_extend_t extend) +{ + float u = normalize_gradient_t (t, extend); + unsigned idx = (unsigned) (u * (GRADIENT_LUT_SIZE - 1) + 0.5f); + return lut[idx]; +} + +static void +reduce_anchors (float x0, float y0, + float x1, float y1, + float x2, float y2, + float *xx0, float *yy0, + float *xx1, float *yy1) +{ + float q2x = x2 - x0, q2y = y2 - y0; + float q1x = x1 - x0, q1y = y1 - y0; + float s = q2x * q2x + q2y * q2y; + if (s < 0.000001f) + { + *xx0 = x0; *yy0 = y0; + *xx1 = x1; *yy1 = y1; + return; + } + float k = (q2x * q1x + q2y * q1y) / s; + *xx0 = x0; + *yy0 = y0; + *xx1 = x1 - k * q2x; + *yy1 = y1 - k * q2y; +} + + +/* + * Gradient paint callbacks + */ + +static void +hb_raster_paint_linear_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + + ensure_initialized (c); + + hb_raster_image_t *surf = c->current_surface (); + if (unlikely (!surf)) return; + + unsigned len = PREALLOCATED_COLOR_STOPS; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + + if (unlikely (!get_color_stops (c, color_line, &len, &stops))) + return; + float mn, mx; + normalize_color_line (stops, len, &mn, &mx); + + hb_paint_extend_t extend = hb_color_line_get_extend (color_line); + const hb_raster_clip_t &clip = c->current_clip (); + unsigned clip_w = clip.max_x > clip.min_x ? clip.max_x - clip.min_x : 0; + unsigned clip_h = clip.max_y > clip.min_y ? clip.max_y - clip.min_y : 0; + bool use_lut = (uint64_t) clip_w * clip_h >= GRADIENT_LUT_MIN_PIXELS; + uint32_t lut[GRADIENT_LUT_SIZE]; + if (use_lut) + build_gradient_lut (stops, len, lut); + + /* Reduce 3-point anchor to 2-point gradient axis */ + float lx0, ly0, lx1, ly1; + reduce_anchors (x0, y0, x1, y1, x2, y2, &lx0, &ly0, &lx1, &ly1); + + /* Apply normalization to endpoints */ + float gx0 = lx0 + mn * (lx1 - lx0); + float gy0 = ly0 + mn * (ly1 - ly0); + float gx1 = lx0 + mx * (lx1 - lx0); + float gy1 = ly0 + mx * (ly1 - ly0); + + /* Inverse transform: pixel → glyph space */ + hb_transform_t<> t = c->current_effective_transform (); + float det = t.xx * t.yy - t.xy * t.yx; + if (fabsf (det) < 1e-10f) goto done; + + { + float inv_det = 1.f / det; + float inv_xx = t.yy * inv_det; + float inv_xy = -t.xy * inv_det; + float inv_yx = -t.yx * inv_det; + float inv_yy = t.xx * inv_det; + float inv_x0 = (t.xy * t.y0 - t.yy * t.x0) * inv_det; + float inv_y0 = (t.yx * t.x0 - t.xx * t.y0) * inv_det; + + /* Gradient direction vector and denominator for projection */ + float dx = gx1 - gx0, dy = gy1 - gy0; + float denom = dx * dx + dy * dy; + if (denom < 1e-10f) goto done; + float inv_denom = 1.f / denom; + + unsigned stride = surf->extents.stride; + int ox = surf->extents.x_origin; + int oy = surf->extents.y_origin; + + if (clip.is_rect) + { + for (unsigned py = clip.min_y; py < clip.max_y; py++) + { + hb_packed_t *row = (hb_packed_t *) (surf->buffer.arrayZ + py * stride); + float gx = inv_xx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_xy * ((float) ((int) py + oy) + 0.5f) + inv_x0; + float gy = inv_yx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_yy * ((float) ((int) py + oy) + 0.5f) + inv_y0; + if (use_lut) + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + float proj_t = ((gx - gx0) * dx + (gy - gy0) * dy) * inv_denom; + uint32_t src = lookup_gradient_lut (lut, proj_t, extend); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + else + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + float proj_t = ((gx - gx0) * dx + (gy - gy0) * dy) * inv_denom; + uint32_t src = evaluate_color_line (stops, len, proj_t, extend); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + } + } + else + { + for (unsigned py = clip.min_y; py < clip.max_y; py++) + { + hb_packed_t *row = (hb_packed_t *) (surf->buffer.arrayZ + py * stride); + const uint8_t *clip_row = clip.alpha.arrayZ + py * clip.stride; + float gx = inv_xx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_xy * ((float) ((int) py + oy) + 0.5f) + inv_x0; + float gy = inv_yx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_yy * ((float) ((int) py + oy) + 0.5f) + inv_y0; + if (use_lut) + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + uint8_t clip_alpha = clip_row[px]; + if (clip_alpha == 0) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float proj_t = ((gx - gx0) * dx + (gy - gy0) * dy) * inv_denom; + uint32_t src = lookup_gradient_lut (lut, proj_t, extend); + src = hb_raster_alpha_mul (src, clip_alpha); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + else + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + uint8_t clip_alpha = clip_row[px]; + if (clip_alpha == 0) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float proj_t = ((gx - gx0) * dx + (gy - gy0) * dy) * inv_denom; + uint32_t src = evaluate_color_line (stops, len, proj_t, extend); + src = hb_raster_alpha_mul (src, clip_alpha); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + } + } + } + +done: + (void) stops_; +} + +static void +hb_raster_paint_radial_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + + ensure_initialized (c); + + hb_raster_image_t *surf = c->current_surface (); + if (unlikely (!surf)) return; + + unsigned len = PREALLOCATED_COLOR_STOPS; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + + if (unlikely (!get_color_stops (c, color_line, &len, &stops))) + return; + float mn, mx; + normalize_color_line (stops, len, &mn, &mx); + + hb_paint_extend_t extend = hb_color_line_get_extend (color_line); + const hb_raster_clip_t &clip = c->current_clip (); + unsigned clip_w = clip.max_x > clip.min_x ? clip.max_x - clip.min_x : 0; + unsigned clip_h = clip.max_y > clip.min_y ? clip.max_y - clip.min_y : 0; + bool use_lut = (uint64_t) clip_w * clip_h >= GRADIENT_LUT_MIN_PIXELS; + uint32_t lut[GRADIENT_LUT_SIZE]; + if (use_lut) + build_gradient_lut (stops, len, lut); + + /* Apply normalization to circle parameters */ + float cx0 = x0 + mn * (x1 - x0); + float cy0 = y0 + mn * (y1 - y0); + float cr0 = r0 + mn * (r1 - r0); + float cx1 = x0 + mx * (x1 - x0); + float cy1 = y0 + mx * (y1 - y0); + float cr1 = r0 + mx * (r1 - r0); + + /* Inverse transform */ + hb_transform_t<> t = c->current_effective_transform (); + float det = t.xx * t.yy - t.xy * t.yx; + if (fabsf (det) < 1e-10f) goto done; + + { + float inv_det = 1.f / det; + float inv_xx = t.yy * inv_det; + float inv_xy = -t.xy * inv_det; + float inv_yx = -t.yx * inv_det; + float inv_yy = t.xx * inv_det; + float inv_x0 = (t.xy * t.y0 - t.yy * t.x0) * inv_det; + float inv_y0 = (t.yx * t.x0 - t.xx * t.y0) * inv_det; + + /* Precompute quadratic coefficients for radial gradient: + * |p - c0 - t*(c1-c0)|^2 = (r0 + t*(r1-r0))^2 + * + * Expanding gives At^2 + Bt + C = 0 where: + * cdx = c1.x - c0.x, cdy = c1.y - c0.y, dr = r1 - r0 + * A = cdx^2 + cdy^2 - dr^2 + * B = -2*(px-c0.x)*cdx - 2*(py-c0.y)*cdy - 2*r0*dr + * C = (px-c0.x)^2 + (py-c0.y)^2 - r0^2 + */ + float cdx = cx1 - cx0, cdy = cy1 - cy0; + float dr = cr1 - cr0; + float A = cdx * cdx + cdy * cdy - dr * dr; + + unsigned stride = surf->extents.stride; + int ox = surf->extents.x_origin; + int oy = surf->extents.y_origin; + + if (clip.is_rect) + { + for (unsigned py = clip.min_y; py < clip.max_y; py++) + { + hb_packed_t *row = (hb_packed_t *) (surf->buffer.arrayZ + py * stride); + float gx = inv_xx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_xy * ((float) ((int) py + oy) + 0.5f) + inv_x0; + float gy = inv_yx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_yy * ((float) ((int) py + oy) + 0.5f) + inv_y0; + if (use_lut) + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + float dpx = gx - cx0, dpy = gy - cy0; + float B = -2.f * (dpx * cdx + dpy * cdy + cr0 * dr); + float C = dpx * dpx + dpy * dpy - cr0 * cr0; + + float grad_t; + if (fabsf (A) > 1e-10f) + { + float disc = B * B - 4.f * A * C; + if (disc < 0.f) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float sq = sqrtf (disc); + /* Pick the larger root (t closer to 1 = outer circle) */ + float t1 = (-B + sq) / (2.f * A); + float t2 = (-B - sq) / (2.f * A); + /* Choose the root that gives a positive radius */ + if (cr0 + t1 * dr >= 0.f) + grad_t = t1; + else + grad_t = t2; + } + else + { + /* Linear case: Bt + C = 0 */ + if (fabsf (B) < 1e-10f) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + grad_t = -C / B; + } + + uint32_t src = lookup_gradient_lut (lut, grad_t, extend); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + else + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + float dpx = gx - cx0, dpy = gy - cy0; + float B = -2.f * (dpx * cdx + dpy * cdy + cr0 * dr); + float C = dpx * dpx + dpy * dpy - cr0 * cr0; + + float grad_t; + if (fabsf (A) > 1e-10f) + { + float disc = B * B - 4.f * A * C; + if (disc < 0.f) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float sq = sqrtf (disc); + float t1 = (-B + sq) / (2.f * A); + float t2 = (-B - sq) / (2.f * A); + grad_t = (cr0 + t1 * dr >= 0.f) ? t1 : t2; + } + else + { + if (fabsf (B) < 1e-10f) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + grad_t = -C / B; + } + + uint32_t src = evaluate_color_line (stops, len, grad_t, extend); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + } + } + else + { + for (unsigned py = clip.min_y; py < clip.max_y; py++) + { + hb_packed_t *row = (hb_packed_t *) (surf->buffer.arrayZ + py * stride); + const uint8_t *clip_row = clip.alpha.arrayZ + py * clip.stride; + float gx = inv_xx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_xy * ((float) ((int) py + oy) + 0.5f) + inv_x0; + float gy = inv_yx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_yy * ((float) ((int) py + oy) + 0.5f) + inv_y0; + if (use_lut) + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + uint8_t clip_alpha = clip_row[px]; + if (clip_alpha == 0) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float dpx = gx - cx0, dpy = gy - cy0; + float B = -2.f * (dpx * cdx + dpy * cdy + cr0 * dr); + float C = dpx * dpx + dpy * dpy - cr0 * cr0; + + float grad_t; + if (fabsf (A) > 1e-10f) + { + float disc = B * B - 4.f * A * C; + if (disc < 0.f) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float sq = sqrtf (disc); + float t1 = (-B + sq) / (2.f * A); + float t2 = (-B - sq) / (2.f * A); + grad_t = (cr0 + t1 * dr >= 0.f) ? t1 : t2; + } + else + { + if (fabsf (B) < 1e-10f) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + grad_t = -C / B; + } + + uint32_t src = lookup_gradient_lut (lut, grad_t, extend); + src = hb_raster_alpha_mul (src, clip_alpha); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + else + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + uint8_t clip_alpha = clip_row[px]; + if (clip_alpha == 0) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float dpx = gx - cx0, dpy = gy - cy0; + float B = -2.f * (dpx * cdx + dpy * cdy + cr0 * dr); + float C = dpx * dpx + dpy * dpy - cr0 * cr0; + + float grad_t; + if (fabsf (A) > 1e-10f) + { + float disc = B * B - 4.f * A * C; + if (disc < 0.f) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float sq = sqrtf (disc); + float t1 = (-B + sq) / (2.f * A); + float t2 = (-B - sq) / (2.f * A); + grad_t = (cr0 + t1 * dr >= 0.f) ? t1 : t2; + } + else + { + if (fabsf (B) < 1e-10f) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + grad_t = -C / B; + } + + uint32_t src = evaluate_color_line (stops, len, grad_t, extend); + src = hb_raster_alpha_mul (src, clip_alpha); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + } + } + } + +done: + (void) stops_; +} + +static void +hb_raster_paint_sweep_gradient (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + hb_color_line_t *color_line, + float cx, float cy, + float start_angle, + float end_angle, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + + ensure_initialized (c); + + hb_raster_image_t *surf = c->current_surface (); + if (unlikely (!surf)) return; + + unsigned len = PREALLOCATED_COLOR_STOPS; + hb_color_stop_t stops_[PREALLOCATED_COLOR_STOPS]; + hb_color_stop_t *stops = stops_; + + if (unlikely (!get_color_stops (c, color_line, &len, &stops))) + return; + float mn, mx; + normalize_color_line (stops, len, &mn, &mx); + + hb_paint_extend_t extend = hb_color_line_get_extend (color_line); + const hb_raster_clip_t &clip = c->current_clip (); + unsigned clip_w = clip.max_x > clip.min_x ? clip.max_x - clip.min_x : 0; + unsigned clip_h = clip.max_y > clip.min_y ? clip.max_y - clip.min_y : 0; + bool use_lut = (uint64_t) clip_w * clip_h >= GRADIENT_LUT_MIN_PIXELS; + uint32_t lut[GRADIENT_LUT_SIZE]; + if (use_lut) + build_gradient_lut (stops, len, lut); + + /* Apply normalization to angle range */ + float a0 = start_angle + mn * (end_angle - start_angle); + float a1 = start_angle + mx * (end_angle - start_angle); + float angle_range = a1 - a0; + + /* Inverse transform */ + hb_transform_t<> t = c->current_effective_transform (); + float det = t.xx * t.yy - t.xy * t.yx; + if (fabsf (det) < 1e-10f || fabsf (angle_range) < 1e-10f) goto done; + + { + float inv_det = 1.f / det; + float inv_xx = t.yy * inv_det; + float inv_xy = -t.xy * inv_det; + float inv_yx = -t.yx * inv_det; + float inv_yy = t.xx * inv_det; + float inv_x0 = (t.xy * t.y0 - t.yy * t.x0) * inv_det; + float inv_y0 = (t.yx * t.x0 - t.xx * t.y0) * inv_det; + + float inv_angle_range = 1.f / angle_range; + + unsigned stride = surf->extents.stride; + int ox = surf->extents.x_origin; + int oy = surf->extents.y_origin; + + if (clip.is_rect) + { + for (unsigned py = clip.min_y; py < clip.max_y; py++) + { + hb_packed_t *row = (hb_packed_t *) (surf->buffer.arrayZ + py * stride); + float gx = inv_xx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_xy * ((float) ((int) py + oy) + 0.5f) + inv_x0; + float gy = inv_yx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_yy * ((float) ((int) py + oy) + 0.5f) + inv_y0; + if (use_lut) + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + float angle = atan2f (gy - cy, gx - cx); + if (angle < 0) angle += (float) HB_2_PI; + float grad_t = (angle - a0) * inv_angle_range; + uint32_t src = lookup_gradient_lut (lut, grad_t, extend); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + else + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + float angle = atan2f (gy - cy, gx - cx); + if (angle < 0) angle += (float) HB_2_PI; + float grad_t = (angle - a0) * inv_angle_range; + uint32_t src = evaluate_color_line (stops, len, grad_t, extend); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + } + } + else + { + for (unsigned py = clip.min_y; py < clip.max_y; py++) + { + hb_packed_t *row = (hb_packed_t *) (surf->buffer.arrayZ + py * stride); + const uint8_t *clip_row = clip.alpha.arrayZ + py * clip.stride; + float gx = inv_xx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_xy * ((float) ((int) py + oy) + 0.5f) + inv_x0; + float gy = inv_yx * ((float) ((int) clip.min_x + ox) + 0.5f) + inv_yy * ((float) ((int) py + oy) + 0.5f) + inv_y0; + if (use_lut) + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + uint8_t clip_alpha = clip_row[px]; + if (clip_alpha == 0) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float angle = atan2f (gy - cy, gx - cx); + if (angle < 0) angle += (float) HB_2_PI; + float grad_t = (angle - a0) * inv_angle_range; + uint32_t src = lookup_gradient_lut (lut, grad_t, extend); + src = hb_raster_alpha_mul (src, clip_alpha); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + else + { + for (unsigned px = clip.min_x; px < clip.max_x; px++) + { + uint8_t clip_alpha = clip_row[px]; + if (clip_alpha == 0) + { + gx += inv_xx; + gy += inv_yx; + continue; + } + float angle = atan2f (gy - cy, gx - cx); + if (angle < 0) angle += (float) HB_2_PI; + float grad_t = (angle - a0) * inv_angle_range; + uint32_t src = evaluate_color_line (stops, len, grad_t, extend); + src = hb_raster_alpha_mul (src, clip_alpha); + row[px] = hb_packed_t (hb_raster_src_over (src, (uint32_t) row[px])); + gx += inv_xx; + gy += inv_yx; + } + } + } + } + } + +done: + (void) stops_; +} + +static hb_bool_t +hb_raster_paint_custom_palette_color (hb_paint_funcs_t *funcs HB_UNUSED, + void *paint_data, + unsigned int color_index, + hb_color_t *color, + void *user_data HB_UNUSED) +{ + hb_raster_paint_t *c = (hb_raster_paint_t *) paint_data; + if (likely (c->custom_palette && hb_map_has (c->custom_palette, color_index))) + { + *color = hb_map_get (c->custom_palette, color_index); + return true; + } + return false; +} + + +/* + * Lazy-loader singleton for paint funcs + */ + +static inline void free_static_raster_paint_funcs (); + +static struct hb_raster_paint_funcs_lazy_loader_t : hb_paint_funcs_lazy_loader_t +{ + static hb_paint_funcs_t *create () + { + hb_paint_funcs_t *funcs = hb_paint_funcs_create (); + + hb_paint_funcs_set_push_transform_func (funcs, hb_raster_paint_push_transform, nullptr, nullptr); + hb_paint_funcs_set_pop_transform_func (funcs, hb_raster_paint_pop_transform, nullptr, nullptr); + hb_paint_funcs_set_color_glyph_func (funcs, hb_raster_paint_color_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_glyph_func (funcs, hb_raster_paint_push_clip_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, hb_raster_paint_push_clip_rectangle, nullptr, nullptr); + hb_paint_funcs_set_pop_clip_func (funcs, hb_raster_paint_pop_clip, nullptr, nullptr); + hb_paint_funcs_set_push_group_func (funcs, hb_raster_paint_push_group, nullptr, nullptr); + hb_paint_funcs_set_pop_group_func (funcs, hb_raster_paint_pop_group, nullptr, nullptr); + hb_paint_funcs_set_color_func (funcs, hb_raster_paint_color, nullptr, nullptr); + hb_paint_funcs_set_image_func (funcs, hb_raster_paint_image, nullptr, nullptr); + hb_paint_funcs_set_linear_gradient_func (funcs, hb_raster_paint_linear_gradient, nullptr, nullptr); + hb_paint_funcs_set_radial_gradient_func (funcs, hb_raster_paint_radial_gradient, nullptr, nullptr); + hb_paint_funcs_set_sweep_gradient_func (funcs, hb_raster_paint_sweep_gradient, nullptr, nullptr); + hb_paint_funcs_set_custom_palette_color_func (funcs, hb_raster_paint_custom_palette_color, nullptr, nullptr); + + hb_paint_funcs_make_immutable (funcs); + + hb_atexit (free_static_raster_paint_funcs); + + return funcs; + } +} static_raster_paint_funcs; + +static inline void +free_static_raster_paint_funcs () +{ + static_raster_paint_funcs.free_instance (); +} + + +/* + * Public API + */ + +/** + * hb_raster_paint_create_or_fail: + * + * Creates a new color-glyph paint context. + * + * Return value: (transfer full): + * A newly allocated #hb_raster_paint_t, or `NULL` on allocation failure. + * + * Since: 13.0.0 + **/ +hb_raster_paint_t * +hb_raster_paint_create_or_fail (void) +{ + hb_raster_paint_t *paint = hb_object_create (); + if (unlikely (!paint)) + return nullptr; + + paint->clip_rdr = hb_raster_draw_create_or_fail (); + if (unlikely (!paint->clip_rdr)) + { + hb_free (paint); + return nullptr; + } + + return paint; +} + +/** + * hb_raster_paint_reference: (skip) + * @paint: a paint context + * + * Increases the reference count on @paint by one. + * + * Return value: (transfer full): + * The referenced #hb_raster_paint_t. + * + * Since: 13.0.0 + **/ +hb_raster_paint_t * +hb_raster_paint_reference (hb_raster_paint_t *paint) +{ + return hb_object_reference (paint); +} + +/** + * hb_raster_paint_destroy: (skip) + * @paint: a paint context + * + * Decreases the reference count on @paint by one. When the + * reference count reaches zero, the paint context is freed. + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_destroy (hb_raster_paint_t *paint) +{ + if (!hb_object_should_destroy (paint)) + return; + + hb_map_destroy (paint->custom_palette); + hb_raster_draw_destroy (paint->clip_rdr); + for (auto *s : paint->surface_stack) + hb_raster_image_destroy (s); + for (auto *s : paint->surface_cache) + hb_raster_image_destroy (s); + hb_object_actually_destroy (paint); + hb_free (paint); +} + +/** + * hb_raster_paint_set_user_data: (skip) + * @paint: a paint context + * @key: the user-data key + * @data: a pointer to the user data + * @destroy: (nullable): a callback to call when @data is not needed anymore + * @replace: whether to replace an existing data with the same key + * + * Attaches a user-data key/data pair to the specified paint context. + * + * Return value: `true` if success, `false` otherwise + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_paint_set_user_data (hb_raster_paint_t *paint, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (paint, key, data, destroy, replace); +} + +/** + * hb_raster_paint_get_user_data: (skip) + * @paint: a paint context + * @key: the user-data key + * + * Fetches the user-data associated with the specified key, + * attached to the specified paint context. + * + * Return value: (transfer none): + * A pointer to the user data + * + * Since: 13.0.0 + **/ +void * +hb_raster_paint_get_user_data (hb_raster_paint_t *paint, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (paint, key); +} + +/** + * hb_raster_paint_set_transform: + * @paint: a paint context + * @xx: xx component of the transform matrix + * @yx: yx component of the transform matrix + * @xy: xy component of the transform matrix + * @yy: yy component of the transform matrix + * @dx: x translation + * @dy: y translation + * + * Sets the base 2×3 affine transform that maps from glyph-space + * coordinates to pixel-space coordinates. + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_set_transform (hb_raster_paint_t *paint, + float xx, float yx, + float xy, float yy, + float dx, float dy) +{ + paint->base_transform = {xx, yx, xy, yy, dx, dy}; +} + +/** + * hb_raster_paint_get_transform: + * @paint: a paint context + * @xx: (out) (nullable): xx component of the transform matrix + * @yx: (out) (nullable): yx component of the transform matrix + * @xy: (out) (nullable): xy component of the transform matrix + * @yy: (out) (nullable): yy component of the transform matrix + * @dx: (out) (nullable): x translation + * @dy: (out) (nullable): y translation + * + * Gets the current base 2x3 affine transform. + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_get_transform (hb_raster_paint_t *paint, + float *xx, float *yx, + float *xy, float *yy, + float *dx, float *dy) +{ + if (xx) *xx = paint->base_transform.xx; + if (yx) *yx = paint->base_transform.yx; + if (xy) *xy = paint->base_transform.xy; + if (yy) *yy = paint->base_transform.yy; + if (dx) *dx = paint->base_transform.x0; + if (dy) *dy = paint->base_transform.y0; +} + +/** + * hb_raster_paint_set_scale_factor: + * @paint: a paint context + * @x_scale_factor: x-axis minification factor + * @y_scale_factor: y-axis minification factor + * + * Sets post-transform minification factors applied during painting. + * Factors larger than 1 shrink the output in pixels. The default is 1. + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_set_scale_factor (hb_raster_paint_t *paint, + float x_scale_factor, + float y_scale_factor) +{ + paint->x_scale_factor = x_scale_factor > 0.f ? x_scale_factor : 1.f; + paint->y_scale_factor = y_scale_factor > 0.f ? y_scale_factor : 1.f; +} + +/** + * hb_raster_paint_get_scale_factor: + * @paint: a paint context + * @x_scale_factor: (out) (nullable): x-axis minification factor + * @y_scale_factor: (out) (nullable): y-axis minification factor + * + * Fetches the current post-transform minification factors. + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_get_scale_factor (hb_raster_paint_t *paint, + float *x_scale_factor, + float *y_scale_factor) +{ + if (x_scale_factor) *x_scale_factor = paint->x_scale_factor; + if (y_scale_factor) *y_scale_factor = paint->y_scale_factor; +} + +/** + * hb_raster_paint_set_extents: + * @paint: a paint context + * @extents: the desired output extents + * + * Sets the output image extents (pixel rectangle). + * + * Call this before hb_font_paint_glyph() for each render. + * A common pattern is: + * |[ + * hb_glyph_extents_t gext; + * if (hb_font_get_glyph_extents (font, gid, &gext)) + * hb_raster_paint_set_glyph_extents (paint, &gext); + * ]| + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_set_extents (hb_raster_paint_t *paint, + const hb_raster_extents_t *extents) +{ + paint->fixed_extents = *extents; + paint->has_extents = true; + if (paint->fixed_extents.stride == 0) + paint->fixed_extents.stride = paint->fixed_extents.width * 4; +} + +/** + * hb_raster_paint_get_extents: + * @paint: a paint context + * @extents: (out) (nullable): where to write current extents + * + * Gets currently configured output extents. + * + * Return value: `true` if extents are set, `false` otherwise. + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_paint_get_extents (hb_raster_paint_t *paint, + hb_raster_extents_t *extents) +{ + if (!paint->has_extents) + return false; + + if (extents) + *extents = paint->fixed_extents; + return true; +} + +/** + * hb_raster_paint_set_glyph_extents: + * @paint: a paint context + * @glyph_extents: glyph extents from hb_font_get_glyph_extents() + * + * Transforms @glyph_extents with the paint context's base transform and + * sets the resulting output image extents. + * + * This is equivalent to computing a transformed bounding box in pixel + * space and calling hb_raster_paint_set_extents(). + * + * Return value: `true` if transformed extents are non-empty and set; + * `false` otherwise. + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_paint_set_glyph_extents (hb_raster_paint_t *paint, + const hb_glyph_extents_t *glyph_extents) +{ + float x0 = (float) glyph_extents->x_bearing; + float y0 = (float) glyph_extents->y_bearing; + float x1 = (float) glyph_extents->x_bearing + glyph_extents->width; + float y1 = (float) glyph_extents->y_bearing + glyph_extents->height; + + float xmin = hb_min (x0, x1); + float xmax = hb_max (x0, x1); + float ymin = hb_min (y0, y1); + float ymax = hb_max (y0, y1); + + float px[4] = {xmin, xmin, xmax, xmax}; + float py[4] = {ymin, ymax, ymin, ymax}; + + hb_transform_t<> t = paint->base_transform; + paint->apply_scale_factor (t); + + float tx, ty; + t.transform_point (px[0], py[0]); + tx = px[0]; ty = py[0]; + float tx_min = tx, tx_max = tx; + float ty_min = ty, ty_max = ty; + + for (unsigned i = 1; i < 4; i++) + { + t.transform_point (px[i], py[i]); + tx_min = hb_min (tx_min, px[i]); + tx_max = hb_max (tx_max, px[i]); + ty_min = hb_min (ty_min, py[i]); + ty_max = hb_max (ty_max, py[i]); + } + + int ex0 = (int) floorf (tx_min); + int ey0 = (int) floorf (ty_min); + int ex1 = (int) ceilf (tx_max); + int ey1 = (int) ceilf (ty_max); + + if (ex1 <= ex0 || ey1 <= ey0) + { + paint->fixed_extents = {}; + paint->has_extents = false; + return false; + } + + paint->fixed_extents = { + ex0, ey0, + (unsigned) (ex1 - ex0), + (unsigned) (ey1 - ey0), + 0 + }; + paint->has_extents = true; + if (paint->fixed_extents.stride == 0) + paint->fixed_extents.stride = paint->fixed_extents.width * 4; + return true; +} + +/** + * hb_raster_paint_set_foreground: + * @paint: a paint context + * @foreground: the foreground color + * + * Sets the foreground color used when paint callbacks request it + * (e.g. `is_foreground` in color stops or solid fills). + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_set_foreground (hb_raster_paint_t *paint, + hb_color_t foreground) +{ + paint->foreground = foreground; +} + +/** + * hb_raster_paint_clear_custom_palette_colors: + * @paint: a paint context. + * + * Clears all custom palette color overrides previously set on @paint. + * + * After this call, palette lookups use the selected font palette without + * custom override entries. + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_clear_custom_palette_colors (hb_raster_paint_t *paint) +{ + if (paint->custom_palette) + hb_map_clear (paint->custom_palette); +} + +/** + * hb_raster_paint_set_custom_palette_color: + * @paint: a paint context. + * @color_index: color index to override. + * @color: replacement color. + * + * Overrides one font palette color entry for subsequent paint operations. + * Overrides are keyed by @color_index and persist on @paint until cleared + * (or replaced for the same index). + * + * These overrides are consulted by paint operations that resolve CPAL + * entries. + * + * Return value: `true` if the override was set; `false` on allocation failure. + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_paint_set_custom_palette_color (hb_raster_paint_t *paint, + unsigned int color_index, + hb_color_t color) +{ + if (unlikely (!paint->custom_palette)) + { + paint->custom_palette = hb_map_create (); + if (unlikely (!paint->custom_palette)) + return false; + } + hb_map_set (paint->custom_palette, color_index, color); + return hb_map_allocation_successful (paint->custom_palette); +} + +/** + * hb_raster_paint_get_funcs: + * + * Fetches the singleton #hb_paint_funcs_t that renders color glyphs + * into an #hb_raster_paint_t. Pass the #hb_raster_paint_t as the + * @paint_data argument when calling hb_font_paint_glyph(). + * + * Return value: (transfer none): + * The rasterizer paint functions + * + * Since: 13.0.0 + **/ +hb_paint_funcs_t * +hb_raster_paint_get_funcs (void) +{ + return static_raster_paint_funcs.get_unconst (); +} + +/** + * hb_raster_paint_glyph: + * @paint: a paint context + * @font: font to paint from + * @glyph: glyph ID to paint + * @pen_x: glyph origin x in font coordinates (pre-transform) + * @pen_y: glyph origin y in font coordinates (pre-transform) + * @palette: palette index + * @foreground: foreground color + * + * Convenience wrapper to paint one color glyph at (@pen_x, @pen_y) using + * the paint context's current transform. The pen coordinates are applied + * before minification and transformed by the current affine transform. + * + * Return value: `true` if painting succeeded, `false` otherwise. + * + * Since: 13.0.0 + **/ +hb_bool_t +hb_raster_paint_glyph (hb_raster_paint_t *paint, + hb_font_t *font, + hb_codepoint_t glyph, + float pen_x, + float pen_y, + unsigned palette, + hb_color_t foreground) +{ + float xx = paint->base_transform.xx; + float yx = paint->base_transform.yx; + float xy = paint->base_transform.xy; + float yy = paint->base_transform.yy; + float dx = paint->base_transform.x0; + float dy = paint->base_transform.y0; + + float tx = dx + xx * pen_x + xy * pen_y; + float ty = dy + yx * pen_x + yy * pen_y; + + if (!paint->has_extents) + { + hb_glyph_extents_t ge; + if (hb_font_get_glyph_extents (font, glyph, &ge)) + { + hb_raster_paint_set_transform (paint, xx, yx, xy, yy, tx, ty); + hb_raster_paint_set_glyph_extents (paint, &ge); + } + } + + hb_raster_paint_set_transform (paint, xx, yx, xy, yy, tx, ty); + paint->svg_glyph = glyph; + paint->svg_font = font; + paint->svg_palette = palette; + hb_bool_t ret = hb_font_paint_glyph_or_fail (font, glyph, + hb_raster_paint_get_funcs (), paint, + palette, foreground); + paint->svg_glyph = 0; + paint->svg_font = nullptr; + paint->svg_palette = 0; + hb_raster_paint_set_transform (paint, xx, yx, xy, yy, dx, dy); + return ret; +} + +/** + * hb_raster_paint_render: + * @paint: a paint context + * + * Extracts the rendered image after hb_font_paint_glyph() has + * completed. The paint context's surface stack is consumed and + * the result returned as a new #hb_raster_image_t. Output format is + * always @HB_RASTER_FORMAT_BGRA32. + * + * Call hb_font_paint_glyph() before calling this function. + * hb_raster_paint_set_extents() or hb_raster_paint_set_glyph_extents() + * must be called before painting; otherwise this function returns `NULL`. + * Internal drawing state is cleared here so the same object can + * be reused without client-side clearing. + * + * Return value: (transfer full): + * A rendered #hb_raster_image_t. Returns `NULL` if extents were not set + * or if allocation/configuration fails. If extents were set but nothing + * was painted, returns an empty image. + * + * Since: 13.0.0 + **/ +hb_raster_image_t * +hb_raster_paint_render (hb_raster_paint_t *paint) +{ + hb_raster_image_t *result = nullptr; + + if (unlikely (!paint->has_extents)) + goto fail; + + if (paint->surface_stack.length) + { + result = paint->surface_stack[0]; + /* Release any remaining group surfaces (shouldn't happen with + * well-formed paint calls, but be safe). */ + for (unsigned i = 1; i < paint->surface_stack.length; i++) + paint->release_surface (paint->surface_stack[i]); + paint->surface_stack.clear (); + } + else + { + result = paint->acquire_surface (); + if (unlikely (!result)) + goto fail; + } + + /* Clean up stacks and reset auto-extents for next glyph. */ + paint->transform_stack.clear (); + paint->release_all_clips (); + hb_raster_draw_reset (paint->clip_rdr); + paint->has_extents = false; + paint->fixed_extents = {}; + + return result; + +fail: + paint->transform_stack.clear (); + paint->release_all_clips (); + for (auto *s : paint->surface_stack) + paint->release_surface (s); + paint->surface_stack.clear (); + hb_raster_draw_reset (paint->clip_rdr); + paint->has_extents = false; + paint->fixed_extents = {}; + return nullptr; +} + +/** + * hb_raster_paint_reset: + * @paint: a paint context + * + * Resets the paint context to its initial state, clearing all + * configuration while preserving internal image caches. + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_reset (hb_raster_paint_t *paint) +{ + paint->base_transform = {1, 0, 0, 1, 0, 0}; + paint->x_scale_factor = 1.f; + paint->y_scale_factor = 1.f; + paint->fixed_extents = {}; + paint->has_extents = false; + paint->foreground = HB_COLOR (0, 0, 0, 255); + hb_raster_paint_clear_custom_palette_colors (paint); + paint->transform_stack.clear (); + paint->release_all_clips (); + for (auto *s : paint->surface_stack) + paint->release_surface (s); + paint->surface_stack.clear (); +} + +/** + * hb_raster_paint_recycle_image: + * @paint: a paint context + * @image: a raster image to recycle + * + * Recycles @image for reuse by subsequent render calls. + * The caller transfers ownership of @image to @paint. + * + * Since: 13.0.0 + **/ +void +hb_raster_paint_recycle_image (hb_raster_paint_t *paint, + hb_raster_image_t *image) +{ + paint->release_surface (image); +} diff --git a/thirdparty/harfbuzz/src/hb-raster-paint.hh b/thirdparty/harfbuzz/src/hb-raster-paint.hh new file mode 100644 index 0000000000..1d6bb18a4c --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-paint.hh @@ -0,0 +1,205 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_PAINT_HH +#define HB_RASTER_PAINT_HH + +#include "hb.hh" + +#include "hb-raster-image.hh" +#include "hb-geometry.hh" + +/* hb_raster_clip_t — alpha mask for clipping */ +struct hb_raster_clip_t +{ + hb_vector_t alpha; /* A8 mask, same extents as root surface */ + unsigned width = 0; + unsigned height = 0; + unsigned stride = 0; + + /* Fast path: simple rectangle (no alpha buffer needed) */ + bool is_rect = true; + int rect_x0 = 0, rect_y0 = 0; + int rect_x1 = 0, rect_y1 = 0; + + /* Bounding box of non-zero alpha region (valid for both rect and mask) */ + unsigned min_x = 0, min_y = 0; + unsigned max_x = 0, max_y = 0; + + void init_full (unsigned w, unsigned h) + { + width = w; + height = h; + stride = (w + 3u) & ~3u; + is_rect = true; + rect_x0 = 0; + rect_y0 = 0; + rect_x1 = (int) w; + rect_y1 = (int) h; + min_x = 0; + min_y = 0; + max_x = w; + max_y = h; + } + + void update_bounds_from_rect () + { + min_x = (unsigned) hb_max (rect_x0, 0); + min_y = (unsigned) hb_max (rect_y0, 0); + max_x = (unsigned) hb_max (hb_min (rect_x1, (int) width), 0); + max_y = (unsigned) hb_max (hb_min (rect_y1, (int) height), 0); + } + + uint8_t get_alpha (unsigned x, unsigned y) const + { + if (is_rect) + return ((int) x >= rect_x0 && (int) x < rect_x1 && + (int) y >= rect_y0 && (int) y < rect_y1) ? 255 : 0; + if (x >= width || y >= height) return 0; + return alpha[y * stride + x]; + } +}; + + +/* hb_raster_paint_t — color glyph paint context */ +struct hb_raster_paint_t +{ + hb_object_header_t header; + + /* Configuration */ + hb_transform_t<> base_transform = {1, 0, 0, 1, 0, 0}; + float x_scale_factor = 1.f; + float y_scale_factor = 1.f; + hb_raster_extents_t fixed_extents = {}; + bool has_extents = false; + hb_color_t foreground = HB_COLOR (0, 0, 0, 255); + hb_map_t *custom_palette = nullptr; + + /* SVG rendering state */ + hb_codepoint_t svg_glyph = 0; + hb_font_t *svg_font = nullptr; + unsigned svg_palette = 0; + + /* Stacks */ + hb_vector_t> transform_stack; + hb_vector_t clip_stack; + hb_vector_t clip_cache; + hb_vector_t surface_stack; + + /* Cached surface pool (freelist for reuse across push/pop group) */ + hb_vector_t surface_cache; + hb_vector_t scratch_color_stops; + + /* Internal rasterizer for clip-to-glyph */ + hb_raster_draw_t *clip_rdr = nullptr; + + /* Helpers */ + + hb_raster_image_t *acquire_surface () + { + hb_raster_image_t *img; + if (surface_cache.length) + img = surface_cache.pop (); + else + { + img = hb_raster_image_create_or_fail (); + if (unlikely (!img)) return nullptr; + } + + if (unlikely (!img->configure (HB_RASTER_FORMAT_BGRA32, fixed_extents))) + { + hb_raster_image_destroy (img); + return nullptr; + } + img->clear (); + return img; + } + + void release_surface (hb_raster_image_t *img) + { + if (!surface_cache.push_or_fail (img)) + hb_raster_image_destroy (img); + } + + hb_raster_clip_t acquire_clip (unsigned w, unsigned h) + { + hb_raster_clip_t clip; + if (clip_cache.length) + clip = clip_cache.pop (); + clip.width = w; + clip.height = h; + clip.stride = (w + 3u) & ~3u; + clip.is_rect = false; + return clip; + } + + void release_clip (hb_raster_clip_t &&clip) + { + if (clip.alpha.arrayZ) + clip_cache.push (std::move (clip)); + } + + void release_all_clips () + { + while (clip_stack.length) + release_clip (clip_stack.pop ()); + } + + hb_raster_image_t *current_surface () + { + return surface_stack.length ? surface_stack.tail () : nullptr; + } + + hb_raster_clip_t ¤t_clip () + { + return clip_stack.tail (); + } + + hb_transform_t<> ¤t_transform () + { + return transform_stack.tail (); + } + + void apply_scale_factor (hb_transform_t<> &t) const + { + t.xx /= x_scale_factor; + t.xy /= x_scale_factor; + t.x0 /= x_scale_factor; + t.yx /= y_scale_factor; + t.yy /= y_scale_factor; + t.y0 /= y_scale_factor; + } + + hb_transform_t<> current_effective_transform () + { + hb_transform_t<> t = current_transform (); + apply_scale_factor (t); + return t; + } +}; + + +#endif /* HB_RASTER_PAINT_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-base.cc b/thirdparty/harfbuzz/src/hb-raster-svg-base.cc new file mode 100644 index 0000000000..760b9aea77 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-base.cc @@ -0,0 +1,501 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-base.hh" +#include "hb-raster-svg-parse.hh" + +void +svg_parse_style_props (hb_svg_str_t style, hb_svg_style_props_t *out) +{ + if (style.is_null ()) return; + const char *p = style.data; + const char *end = style.data + style.len; + while (p < end) + { + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ';')) + p++; + if (p >= end) break; + const char *name_start = p; + while (p < end && *p != ':' && *p != ';') + p++; + const char *name_end = p; + while (name_end > name_start && + (name_end[-1] == ' ' || name_end[-1] == '\t' || name_end[-1] == '\n' || name_end[-1] == '\r')) + name_end--; + if (p >= end || *p != ':') + { + while (p < end && *p != ';') p++; + continue; + } + p++; /* skip ':' */ + const char *value_start = p; + while (p < end && *p != ';') + p++; + const char *value_end = p; + while (value_start < value_end && + (*value_start == ' ' || *value_start == '\t' || *value_start == '\n' || *value_start == '\r')) + value_start++; + while (value_end > value_start && + (value_end[-1] == ' ' || value_end[-1] == '\t' || value_end[-1] == '\n' || value_end[-1] == '\r')) + value_end--; + + hb_svg_str_t prop_name = {name_start, (unsigned) (name_end - name_start)}; + hb_svg_str_t prop_value = {value_start, (unsigned) (value_end - value_start)}; + if (prop_name.len) + { + switch (hb_svg_ascii_lower (prop_name.data[0])) + { + case 'c': + if (prop_name.len == 9 && prop_name.eq_ascii_ci ("clip-path")) out->clip_path = prop_value; + else if (prop_name.len == 5 && prop_name.eq_ascii_ci ("color")) out->color = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("cx")) out->cx = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("cy")) out->cy = prop_value; + break; + case 'd': + if (prop_name.len == 7 && prop_name.eq_ascii_ci ("display")) out->display = prop_value; + else if (prop_name.len == 1 && prop_name.eq_ascii_ci ("d")) out->d = prop_value; + break; + case 'f': + if (prop_name.len == 4 && prop_name.eq_ascii_ci ("fill")) out->fill = prop_value; + else if (prop_name.len == 12 && prop_name.eq_ascii_ci ("fill-opacity")) out->fill_opacity = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("fx")) out->fx = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("fy")) out->fy = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("fr")) out->fr = prop_value; + break; + case 'g': + if ((prop_name.len == 13 && prop_name.eq_ascii_ci ("gradientunits")) || + (prop_name.len == 14 && prop_name.eq_ascii_ci ("gradient-units"))) out->gradient_units = prop_value; + else if ((prop_name.len == 17 && prop_name.eq_ascii_ci ("gradienttransform")) || + (prop_name.len == 18 && prop_name.eq_ascii_ci ("gradient-transform"))) out->gradient_transform = prop_value; + break; + case 'h': + if (prop_name.len == 6 && prop_name.eq_ascii_ci ("height")) out->height = prop_value; + break; + case 'o': + if (prop_name.len == 7 && prop_name.eq_ascii_ci ("opacity")) out->opacity = prop_value; + else if (prop_name.len == 6 && prop_name.eq_ascii_ci ("offset")) out->offset = prop_value; + break; + case 'p': + if (prop_name.len == 6 && prop_name.eq_ascii_ci ("points")) out->points = prop_value; + break; + case 'r': + if (prop_name.len == 1 && prop_name.eq_ascii_ci ("r")) out->r = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("rx")) out->rx = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("ry")) out->ry = prop_value; + break; + case 's': + if (prop_name.len == 10 && prop_name.eq_ascii_ci ("stop-color")) out->stop_color = prop_value; + else if (prop_name.len == 12 && prop_name.eq_ascii_ci ("stop-opacity")) out->stop_opacity = prop_value; + else if ((prop_name.len == 12 && prop_name.eq_ascii_ci ("spreadmethod")) || + (prop_name.len == 13 && prop_name.eq_ascii_ci ("spread-method"))) out->spread_method = prop_value; + break; + case 't': + if (prop_name.len == 9 && prop_name.eq_ascii_ci ("transform")) out->transform = prop_value; + break; + case 'v': + if (prop_name.len == 10 && prop_name.eq_ascii_ci ("visibility")) out->visibility = prop_value; + break; + case 'w': + if (prop_name.len == 5 && prop_name.eq_ascii_ci ("width")) out->width = prop_value; + break; + case 'x': + if (prop_name.len == 1 && prop_name.eq_ascii_ci ("x")) out->x = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("x1")) out->x1 = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("x2")) out->x2 = prop_value; + break; + case 'y': + if (prop_name.len == 1 && prop_name.eq_ascii_ci ("y")) out->y = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("y1")) out->y1 = prop_value; + else if (prop_name.len == 2 && prop_name.eq_ascii_ci ("y2")) out->y2 = prop_value; + break; + default: + break; + } + } + + if (p < end && *p == ';') p++; + } +} + +float +svg_parse_number_or_percent (hb_svg_str_t s, bool *is_percent) +{ + if (is_percent) *is_percent = false; + s = s.trim (); + if (!s.len) return 0.f; + if (s.data[s.len - 1] == '%') + { + if (is_percent) *is_percent = true; + hb_svg_str_t n = {s.data, s.len - 1}; + return n.to_float () / 100.f; + } + return s.to_float (); +} + +hb_svg_str_t +hb_raster_svg_find_href_attr (const hb_svg_xml_parser_t &parser) +{ + hb_svg_str_t href = parser.find_attr ("href"); + if (href.is_null ()) + href = parser.find_attr ("xlink:href"); + return href; +} + +bool +hb_raster_svg_parse_id_ref (hb_svg_str_t s, + hb_svg_str_t *out_id, + hb_svg_str_t *out_tail) +{ + if (out_id) *out_id = {}; + if (out_tail) *out_tail = {}; + s = s.trim (); + + if (s.len && s.data[0] == '#') + { + hb_svg_str_t id = {s.data + 1, s.len - 1}; + id = id.trim (); + if (!id.len) + return false; + if (out_id) *out_id = id; + return true; + } + + if (!s.starts_with_ascii_ci ("url(")) + return false; + + const char *p = s.data + 4; + const char *end = s.data + s.len; + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++; + + const char *q = p; + char quote = 0; + while (q < end) + { + char c = *q; + if (quote) + { + if (c == quote) quote = 0; + } + else + { + if (c == '"' || c == '\'') quote = c; + else if (c == ')') break; + } + q++; + } + if (q >= end || *q != ')') + return false; + + hb_svg_str_t id = {(const char *) p, (unsigned) (q - p)}; + id = id.trim (); + if (id.len >= 2 && + ((id.data[0] == '\'' && id.data[id.len - 1] == '\'') || + (id.data[0] == '"' && id.data[id.len - 1] == '"'))) + { + id.data++; + id.len -= 2; + } + id = id.trim (); + if (id.len && id.data[0] == '#') + { + id.data++; + id.len--; + } + id = id.trim (); + if (!id.len) + return false; + + if (out_id) *out_id = id; + if (out_tail) + { + const char *tail = q + 1; + while (tail < end && (*tail == ' ' || *tail == '\t' || *tail == '\n' || *tail == '\r')) tail++; + *out_tail = {(const char *) tail, (unsigned) (end - tail)}; + } + return true; +} + +bool +hb_raster_svg_parse_local_id_ref (hb_svg_str_t s, + hb_svg_str_t *out_id, + hb_svg_str_t *out_tail) +{ + if (out_id) *out_id = {}; + if (out_tail) *out_tail = {}; + s = s.trim (); + + if (s.len && s.data[0] == '#') + { + hb_svg_str_t id = {s.data + 1, s.len - 1}; + id = id.trim (); + if (!id.len) + return false; + if (out_id) *out_id = id; + return true; + } + + if (!s.starts_with_ascii_ci ("url(")) + return false; + + hb_svg_str_t id; + if (!hb_raster_svg_parse_id_ref (s, &id, out_tail)) + return false; + + const char *p = s.data + 4; + const char *end = s.data + s.len; + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++; + if (p < end && (*p == '\'' || *p == '"')) + { + p++; + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++; + if (p >= end || *p != '#') + return false; + } + else if (p >= end || *p != '#') + return false; + + if (out_id) *out_id = id; + return true; +} + +bool +hb_raster_svg_find_element_by_id (const char *doc_start, + unsigned doc_len, + const OT::SVG::accelerator_t *svg_accel, + const OT::SVG::svg_doc_cache_t *doc_cache, + hb_svg_str_t id, + const char **found) +{ + *found = nullptr; + if (!doc_start || !doc_len || !id.len) + return false; + + if (doc_cache && svg_accel) + { + unsigned start = 0, end = 0; + OT::SVG::svg_id_span_t key = {id.data, id.len}; + if (svg_accel->doc_cache_find_id_span (doc_cache, key, &start, &end)) + { + if (start < doc_len && end <= doc_len && start < end) + { + *found = doc_start + start; + return true; + } + } + } + + hb_svg_xml_parser_t search (doc_start, doc_len); + while (true) + { + hb_svg_token_type_t tok = search.next (); + if (tok == SVG_TOKEN_EOF) break; + if (tok != SVG_TOKEN_OPEN_TAG && tok != SVG_TOKEN_SELF_CLOSE_TAG) continue; + hb_svg_str_t attr_id = search.find_attr ("id"); + if (attr_id.len == id.len && 0 == memcmp (attr_id.data, id.data, id.len)) + { + *found = search.tag_start; + return true; + } + } + return false; +} + +bool +hb_raster_svg_parse_viewbox (hb_svg_str_t viewbox_str, + float *x, + float *y, + float *w, + float *h) +{ + if (!viewbox_str.len) + return false; + hb_svg_float_parser_t vb_fp (viewbox_str); + float vb_x = vb_fp.next_float (); + float vb_y = vb_fp.next_float (); + float vb_w = vb_fp.next_float (); + float vb_h = vb_fp.next_float (); + if (vb_w <= 0.f || vb_h <= 0.f) + return false; + if (x) *x = vb_x; + if (y) *y = vb_y; + if (w) *w = vb_w; + if (h) *h = vb_h; + return true; +} + +static inline float +svg_align_offset (hb_svg_str_t align, + float leftover, + char axis) +{ + if (leftover <= 0.f) return 0.f; + if ((axis == 'x' && align.starts_with_ascii_ci ("xMin")) || + (axis == 'y' && (align.eq_ascii_ci ("xMinYMin") || + align.eq_ascii_ci ("xMidYMin") || + align.eq_ascii_ci ("xMaxYMin")))) + return 0.f; + if ((axis == 'x' && align.starts_with_ascii_ci ("xMax")) || + (axis == 'y' && (align.eq_ascii_ci ("xMinYMax") || + align.eq_ascii_ci ("xMidYMax") || + align.eq_ascii_ci ("xMaxYMax")))) + return leftover; + return leftover * 0.5f; +} + +bool +hb_raster_svg_compute_viewbox_transform (float viewport_w, + float viewport_h, + float vb_x, + float vb_y, + float vb_w, + float vb_h, + hb_svg_str_t preserve_aspect_ratio, + hb_svg_transform_t *out) +{ + if (!(viewport_w > 0.f && viewport_h > 0.f && vb_w > 0.f && vb_h > 0.f)) + return false; + + hb_svg_str_t par = preserve_aspect_ratio.trim (); + if (par.starts_with_ascii_ci ("defer")) + { + const char *p = par.data + 5; + const char *end = par.data + par.len; + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++; + par = hb_svg_str_t (p, (unsigned) (end - p)); + } + if (!par.len) + par = hb_svg_str_t ("xMidYMid meet", 12); + + bool is_none = false; + bool is_slice = false; + hb_svg_str_t align = par; + const char *p = par.data; + const char *end = par.data + par.len; + while (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r') p++; + align = hb_svg_str_t (par.data, (unsigned) (p - par.data)).trim (); + + if (par.starts_with_ascii_ci ("none")) + is_none = true; + else if (align.starts_with_ascii_ci ("x")) + { + const char *mode = p; + while (mode < end && (*mode == ' ' || *mode == '\t' || *mode == '\n' || *mode == '\r')) mode++; + if (mode < end && hb_svg_str_t (mode, (unsigned) (end - mode)).starts_with_ascii_ci ("slice")) + is_slice = true; + } + + hb_svg_transform_t t; + if (is_none) + { + t.xx = viewport_w / vb_w; + t.yy = viewport_h / vb_h; + t.dx = -vb_x * t.xx; + t.dy = -vb_y * t.yy; + *out = t; + return true; + } + + float sx = viewport_w / vb_w; + float sy = viewport_h / vb_h; + float s = is_slice ? hb_max (sx, sy) : hb_min (sx, sy); + float scaled_w = vb_w * s; + float scaled_h = vb_h * s; + float leftover_x = viewport_w - scaled_w; + float leftover_y = viewport_h - scaled_h; + + if (!align.starts_with_ascii_ci ("x")) + align = hb_svg_str_t ("xMidYMid", 8); + + t.xx = s; + t.yy = s; + t.dx = svg_align_offset (align, leftover_x, 'x') - vb_x * s; + t.dy = svg_align_offset (align, leftover_y, 'y') - vb_y * s; + *out = t; + return true; +} + +bool +hb_raster_svg_compute_use_target_viewbox_transform (hb_svg_xml_parser_t &target_parser, + float use_w, + float use_h, + hb_svg_transform_t *out) +{ + if (!(target_parser.tag_name.eq ("svg") || target_parser.tag_name.eq ("symbol"))) + return false; + + float viewport_w = use_w; + float viewport_h = use_h; + hb_svg_style_props_t target_style_props; + svg_parse_style_props (target_parser.find_attr ("style"), &target_style_props); + if (viewport_w <= 0.f) + viewport_w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (target_parser, target_style_props.width, "width")); + if (viewport_h <= 0.f) + viewport_h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (target_parser, target_style_props.height, "height")); + + float vb_x = 0.f, vb_y = 0.f, vb_w = 0.f, vb_h = 0.f; + if (!hb_raster_svg_parse_viewbox (target_parser.find_attr ("viewBox"), &vb_x, &vb_y, &vb_w, &vb_h)) + return false; + + if (!(viewport_w > 0.f && viewport_h > 0.f)) + { + viewport_w = vb_w; + viewport_h = vb_h; + } + + return hb_raster_svg_compute_viewbox_transform (viewport_w, viewport_h, vb_x, vb_y, vb_w, vb_h, + target_parser.find_attr ("preserveAspectRatio"), + out); +} + +void +hb_raster_svg_parse_use_geometry (hb_svg_xml_parser_t &parser, + float *x, + float *y, + float *w, + float *h) +{ + hb_svg_style_props_t style_props; + svg_parse_style_props (parser.find_attr ("style"), &style_props); + + if (x) *x = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.x, "x")); + if (y) *y = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.y, "y")); + if (w) *w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.width, "width")); + if (h) *h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, style_props.height, "height")); +} + +float +hb_raster_svg_parse_non_percent_length (hb_svg_str_t s) +{ + bool is_percent = false; + float v = svg_parse_number_or_percent (s, &is_percent); + return is_percent ? 0.f : v; +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-base.hh b/thirdparty/harfbuzz/src/hb-raster-svg-base.hh new file mode 100644 index 0000000000..9570f9cb46 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-base.hh @@ -0,0 +1,228 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_BASE_HH +#define HB_RASTER_SVG_BASE_HH + +#include "hb.hh" +#include "OT/Color/svg/svg.hh" + +#include +#include +#include + +static inline char +hb_svg_ascii_lower (char c) +{ + if (c >= 'A' && c <= 'Z') + return c + ('a' - 'A'); + return c; +} + +struct hb_svg_str_t +{ + const char *data; + unsigned len; + + hb_svg_str_t () : data (nullptr), len (0) {} + hb_svg_str_t (const char *d, unsigned l) : data (d), len (l) {} + + bool is_null () const { return !data; } + + bool eq (const char *s) const + { + unsigned slen = (unsigned) strlen (s); + return len == slen && memcmp (data, s, len) == 0; + } + + bool starts_with (const char *prefix) const + { + unsigned plen = (unsigned) strlen (prefix); + return len >= plen && memcmp (data, prefix, plen) == 0; + } + + bool eq_ascii_ci (const char *lit) const + { + unsigned n = (unsigned) strlen (lit); + if (len != n) return false; + for (unsigned i = 0; i < n; i++) + if (hb_svg_ascii_lower (data[i]) != hb_svg_ascii_lower (lit[i])) + return false; + return true; + } + + bool starts_with_ascii_ci (const char *lit) const + { + unsigned n = (unsigned) strlen (lit); + if (len < n) return false; + for (unsigned i = 0; i < n; i++) + if (hb_svg_ascii_lower (data[i]) != hb_svg_ascii_lower (lit[i])) + return false; + return true; + } + + float to_float () const + { + if (!data || !len) return 0.f; + char buf[64]; + unsigned n = hb_min (len, (unsigned) sizeof (buf) - 1); + memcpy (buf, data, n); + buf[n] = '\0'; + float v = strtof (buf, nullptr); + return std::isfinite (v) ? v : 0.f; + } + + hb_svg_str_t trim_left () const + { + const char *p = data; + unsigned l = len; + while (l && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) + { + p++; + l--; + } + return {p, l}; + } + + hb_svg_str_t trim () const + { + hb_svg_str_t s = trim_left (); + while (s.len && (s.data[s.len - 1] == ' ' || s.data[s.len - 1] == '\t' || + s.data[s.len - 1] == '\n' || s.data[s.len - 1] == '\r')) + s.len--; + return s; + } +}; + +struct hb_svg_style_props_t +{ + hb_svg_str_t fill; + hb_svg_str_t fill_opacity; + hb_svg_str_t opacity; + hb_svg_str_t transform; + hb_svg_str_t clip_path; + hb_svg_str_t display; + hb_svg_str_t color; + hb_svg_str_t visibility; + hb_svg_str_t offset; + hb_svg_str_t stop_color; + hb_svg_str_t stop_opacity; + hb_svg_str_t spread_method; + hb_svg_str_t gradient_units; + hb_svg_str_t gradient_transform; + hb_svg_str_t x; + hb_svg_str_t y; + hb_svg_str_t width; + hb_svg_str_t height; + hb_svg_str_t cx; + hb_svg_str_t cy; + hb_svg_str_t r; + hb_svg_str_t fx; + hb_svg_str_t fy; + hb_svg_str_t fr; + hb_svg_str_t rx; + hb_svg_str_t ry; + hb_svg_str_t x1; + hb_svg_str_t y1; + hb_svg_str_t x2; + hb_svg_str_t y2; + hb_svg_str_t points; + hb_svg_str_t d; +}; + +struct hb_svg_xml_parser_t; +struct hb_svg_transform_t; + +HB_INTERNAL void svg_parse_style_props (hb_svg_str_t style, hb_svg_style_props_t *out); +HB_INTERNAL float svg_parse_number_or_percent (hb_svg_str_t s, bool *is_percent); +HB_INTERNAL hb_svg_str_t hb_raster_svg_find_href_attr (const hb_svg_xml_parser_t &parser); +HB_INTERNAL bool hb_raster_svg_parse_id_ref (hb_svg_str_t s, + hb_svg_str_t *out_id, + hb_svg_str_t *out_tail); +HB_INTERNAL bool hb_raster_svg_parse_local_id_ref (hb_svg_str_t s, + hb_svg_str_t *out_id, + hb_svg_str_t *out_tail); +HB_INTERNAL bool hb_raster_svg_find_element_by_id (const char *doc_start, + unsigned doc_len, + const OT::SVG::accelerator_t *svg_accel, + const OT::SVG::svg_doc_cache_t *doc_cache, + hb_svg_str_t id, + const char **found); +HB_INTERNAL bool hb_raster_svg_parse_viewbox (hb_svg_str_t viewbox_str, + float *x, + float *y, + float *w, + float *h); +HB_INTERNAL bool hb_raster_svg_compute_viewbox_transform (float viewport_w, + float viewport_h, + float vb_x, + float vb_y, + float vb_w, + float vb_h, + hb_svg_str_t preserve_aspect_ratio, + hb_svg_transform_t *out); +HB_INTERNAL bool hb_raster_svg_compute_use_target_viewbox_transform (hb_svg_xml_parser_t &target_parser, + float use_w, + float use_h, + hb_svg_transform_t *out); +HB_INTERNAL void hb_raster_svg_parse_use_geometry (hb_svg_xml_parser_t &parser, + float *x, + float *y, + float *w, + float *h); +HB_INTERNAL float hb_raster_svg_parse_non_percent_length (hb_svg_str_t s); +static inline float +svg_parse_float_clamped01 (hb_svg_str_t s) +{ + return hb_clamp (s.to_float (), 0.f, 1.f); +} + +static inline bool +svg_str_is_inherit (hb_svg_str_t s) +{ + return s.trim ().eq_ascii_ci ("inherit"); +} + +static inline bool +svg_str_is_none (hb_svg_str_t s) +{ + return s.trim ().eq_ascii_ci ("none"); +} + +static inline bool +hb_raster_svg_tag_is_container (hb_svg_str_t tag) +{ + return tag.eq ("g") || tag.eq ("a") || tag.eq ("svg") || tag.eq ("symbol"); +} + + +static inline bool +hb_raster_svg_tag_is_container_or_use (hb_svg_str_t tag) +{ + return hb_raster_svg_tag_is_container (tag) || tag.eq ("use"); +} + +#endif /* HB_RASTER_SVG_BASE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-bbox.cc b/thirdparty/harfbuzz/src/hb-raster-svg-bbox.cc new file mode 100644 index 0000000000..2bc39b63b9 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-bbox.cc @@ -0,0 +1,109 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-bbox.hh" + +static void +svg_bbox_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + ((hb_extents_t<> *) draw_data)->add_point (to_x, to_y); +} + +static void +svg_bbox_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + ((hb_extents_t<> *) draw_data)->add_point (to_x, to_y); +} + +static void +svg_bbox_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float control_x, float control_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t<> *ext = (hb_extents_t<> *) draw_data; + ext->add_point (control_x, control_y); + ext->add_point (to_x, to_y); +} + +static void +svg_bbox_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, + void *draw_data, + hb_draw_state_t *st HB_UNUSED, + float control1_x, float control1_y, + float control2_x, float control2_y, + float to_x, float to_y, + void *user_data HB_UNUSED) +{ + hb_extents_t<> *ext = (hb_extents_t<> *) draw_data; + ext->add_point (control1_x, control1_y); + ext->add_point (control2_x, control2_y); + ext->add_point (to_x, to_y); +} + +static hb_draw_funcs_t * +svg_bbox_draw_funcs () +{ + static hb_draw_funcs_t *funcs = nullptr; + if (unlikely (!funcs)) + { + funcs = hb_draw_funcs_create (); + hb_draw_funcs_set_move_to_func (funcs, svg_bbox_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, svg_bbox_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, svg_bbox_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, svg_bbox_cubic_to, nullptr, nullptr); + hb_draw_funcs_make_immutable (funcs); + } + return funcs; +} + +bool +hb_raster_svg_compute_shape_bbox (const hb_svg_shape_emit_data_t &shape, + hb_extents_t<> *bbox) +{ + hb_extents_t<> ext; + hb_svg_shape_emit_data_t tmp = shape; + hb_raster_svg_shape_path_emit (svg_bbox_draw_funcs (), &ext, &tmp); + if (ext.is_empty ()) return false; + *bbox = ext; + return true; +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-bbox.hh b/thirdparty/harfbuzz/src/hb-raster-svg-bbox.hh new file mode 100644 index 0000000000..46802e6d7a --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-bbox.hh @@ -0,0 +1,39 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_BBOX_HH +#define HB_RASTER_SVG_BBOX_HH + +#include "hb.hh" + +#include "hb-geometry.hh" +#include "hb-raster-svg-parse.hh" + +HB_INTERNAL bool +hb_raster_svg_compute_shape_bbox (const hb_svg_shape_emit_data_t &shape, + hb_extents_t<> *bbox); + +#endif /* HB_RASTER_SVG_BBOX_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-clip.cc b/thirdparty/harfbuzz/src/hb-raster-svg-clip.cc new file mode 100644 index 0000000000..19714eab42 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-clip.cc @@ -0,0 +1,528 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-clip.hh" + +#include "hb-raster.h" +#include "hb-raster-paint.hh" +#include "hb-raster-svg.hh" +#include "hb-raster-svg-base.hh" +#include "hb-decycler.hh" + +#include + +static inline bool +svg_transform_is_identity (const hb_svg_transform_t &t) +{ + return t.xx == 1.f && t.yx == 0.f && + t.xy == 0.f && t.yy == 1.f && + t.dx == 0.f && t.dy == 0.f; +} + +static bool +svg_parse_element_transform (hb_svg_xml_parser_t &parser, + hb_svg_transform_t *out) +{ + hb_svg_style_props_t style_props; + svg_parse_style_props (parser.find_attr ("style"), &style_props); + hb_svg_str_t transform = svg_pick_attr_or_style (parser, style_props.transform, "transform"); + if (!transform.len) + return false; + hb_raster_svg_parse_transform (transform, out); + return true; +} + +struct hb_svg_clip_collect_context_t +{ + hb_svg_defs_t *defs; + hb_svg_clip_path_def_t *clip; + const char *doc_start; + unsigned doc_len; + const OT::SVG::accelerator_t *svg_accel; + const OT::SVG::svg_doc_cache_t *doc_cache; + hb_decycler_t *use_decycler; + bool *had_alloc_failure; +}; + +static inline void +svg_clip_append_shape (hb_svg_clip_collect_context_t *ctx, + const hb_svg_shape_emit_data_t &shape, + const hb_svg_transform_t &transform) +{ + hb_svg_clip_shape_t clip_shape; + clip_shape.shape = shape; + if (!svg_transform_is_identity (transform)) + { + clip_shape.has_transform = true; + clip_shape.transform = transform; + } + if (likely (ctx->defs->clip_shapes.push_or_fail (clip_shape))) + ctx->clip->shape_count++; + else if (ctx->had_alloc_failure) + *ctx->had_alloc_failure = true; +} + +static inline bool +svg_resolve_element_visibility (hb_svg_xml_parser_t &parser, + bool parent_visible) +{ + hb_svg_style_props_t style_props; + svg_parse_style_props (parser.find_attr ("style"), &style_props); + hb_svg_str_t display_str = svg_pick_attr_or_style (parser, style_props.display, "display"); + hb_svg_str_t visibility_str = svg_pick_attr_or_style (parser, style_props.visibility, "visibility"); + if (display_str.trim ().eq_ascii_ci ("none")) + return false; + hb_svg_str_t vis_trim = visibility_str.trim (); + if (!vis_trim.len || vis_trim.eq_ascii_ci ("inherit")) + return parent_visible; + if (vis_trim.eq_ascii_ci ("hidden") || + vis_trim.eq_ascii_ci ("collapse")) + return false; + if (vis_trim.eq_ascii_ci ("visible")) + return true; + return parent_visible; +} + +static void +svg_clip_collect_ref_element (hb_svg_clip_collect_context_t *ctx, + hb_svg_xml_parser_t &parser, + const hb_svg_transform_t &base_transform, + unsigned depth, + bool suppress_viewbox_once = false, + bool parent_visible = true, + bool allow_symbol_once = false); + +static void +svg_clip_collect_use_target (hb_svg_clip_collect_context_t *ctx, + hb_svg_xml_parser_t &use_parser, + const hb_svg_transform_t &base_transform, + unsigned depth) +{ + const unsigned SVG_MAX_CLIP_USE_DEPTH = 64; + if (depth >= SVG_MAX_CLIP_USE_DEPTH) + return; + + hb_svg_str_t href = hb_raster_svg_find_href_attr (use_parser); + hb_svg_str_t ref_id; + if (!hb_raster_svg_parse_local_id_ref (href, &ref_id, nullptr)) + return; + + const char *found = nullptr; + if (!hb_raster_svg_find_element_by_id (ctx->doc_start, ctx->doc_len, + ctx->svg_accel, ctx->doc_cache, + ref_id, &found)) + return; + + hb_decycler_node_t node (*ctx->use_decycler); + if (unlikely (!node.visit ((uintptr_t) found))) + return; + + hb_svg_transform_t effective = base_transform; + float use_x = 0.f, use_y = 0.f, use_w = 0.f, use_h = 0.f; + hb_raster_svg_parse_use_geometry (use_parser, &use_x, &use_y, &use_w, &use_h); + if (use_x != 0.f || use_y != 0.f) + { + hb_svg_transform_t tr; + tr.dx = use_x; + tr.dy = use_y; + effective.multiply (tr); + } + + unsigned remaining = ctx->doc_len - (unsigned) (found - ctx->doc_start); + hb_svg_xml_parser_t ref_parser (found, remaining); + hb_svg_token_type_t rt = ref_parser.next (); + if (rt != SVG_TOKEN_OPEN_TAG && rt != SVG_TOKEN_SELF_CLOSE_TAG) + return; + + bool viewport_mapped = false; + hb_svg_transform_t vb_t; + if (hb_raster_svg_compute_use_target_viewbox_transform (ref_parser, use_w, use_h, &vb_t)) + { + effective.multiply (vb_t); + viewport_mapped = true; + } + + bool allow_symbol = ref_parser.tag_name.eq ("symbol"); + svg_clip_collect_ref_element (ctx, ref_parser, effective, depth + 1, + viewport_mapped, true, allow_symbol); +} + +static void +svg_clip_collect_ref_element (hb_svg_clip_collect_context_t *ctx, + hb_svg_xml_parser_t &parser, + const hb_svg_transform_t &base_transform, + unsigned depth, + bool suppress_viewbox_once, + bool parent_visible, + bool allow_symbol_once) +{ + const unsigned SVG_MAX_CLIP_REF_DEPTH = 64; + if (depth >= SVG_MAX_CLIP_REF_DEPTH) + { + if (!parser.self_closing) + svg_skip_subtree (parser); + return; + } + + bool is_visible = svg_resolve_element_visibility (parser, parent_visible); + if (!is_visible) + { + if (!parser.self_closing) + svg_skip_subtree (parser); + return; + } + + /* Definitions are not directly renderable clip geometry. */ + if (parser.tag_name.eq ("defs")) + { + if (!parser.self_closing) + svg_skip_subtree (parser); + return; + } + if (parser.tag_name.eq ("symbol") && !allow_symbol_once) + { + if (!parser.self_closing) + svg_skip_subtree (parser); + return; + } + if (parser.tag_name.eq ("symbol")) + allow_symbol_once = false; + + hb_svg_transform_t effective = base_transform; + hb_svg_style_props_t geom_style_props; + svg_parse_style_props (parser.find_attr ("style"), &geom_style_props); + hb_svg_transform_t local_t; + if (svg_parse_element_transform (parser, &local_t)) + effective.multiply (local_t); + if (parser.tag_name.eq ("svg")) + { + float svg_x = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.x, "x")); + float svg_y = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.y, "y")); + if (svg_x != 0.f || svg_y != 0.f) + { + hb_svg_transform_t tr; + tr.dx = svg_x; + tr.dy = svg_y; + effective.multiply (tr); + } + + if (!suppress_viewbox_once) + { + float vb_x = 0.f, vb_y = 0.f, vb_w = 0.f, vb_h = 0.f; + if (hb_raster_svg_parse_viewbox (parser.find_attr ("viewBox"), + &vb_x, &vb_y, &vb_w, &vb_h)) + { + float viewport_w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.width, "width")); + float viewport_h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.height, "height")); + if (!(viewport_w > 0.f && viewport_h > 0.f)) + { + viewport_w = vb_w; + viewport_h = vb_h; + } + hb_svg_transform_t vb_t; + if (hb_raster_svg_compute_viewbox_transform (viewport_w, viewport_h, + vb_x, vb_y, vb_w, vb_h, + parser.find_attr ("preserveAspectRatio"), + &vb_t)) + effective.multiply (vb_t); + } + suppress_viewbox_once = false; + } + } + + hb_svg_shape_emit_data_t shape; + if (hb_raster_svg_parse_shape_tag (parser, &shape)) + { + svg_clip_append_shape (ctx, shape, effective); + if (!parser.self_closing) + svg_skip_subtree (parser); + return; + } + + if (parser.tag_name.eq ("use")) + { + svg_clip_collect_use_target (ctx, parser, effective, depth + 1); + if (!parser.self_closing) + svg_skip_subtree (parser); + return; + } + + bool is_container = hb_raster_svg_tag_is_container (parser.tag_name); + if (!is_container || parser.self_closing) + { + if (!parser.self_closing) + svg_skip_subtree (parser); + return; + } + + int inner_depth = 1; + while (inner_depth > 0) + { + hb_svg_token_type_t tok = parser.next (); + if (tok == SVG_TOKEN_EOF) break; + if (tok == SVG_TOKEN_CLOSE_TAG) + { + inner_depth--; + continue; + } + if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG) + svg_clip_collect_ref_element (ctx, parser, effective, depth + 1, false, is_visible, false); + } +} + +void +hb_raster_svg_process_clip_path_def (hb_svg_defs_t *defs, + hb_svg_xml_parser_t &parser, + hb_svg_token_type_t tok, + const char *doc_start, + unsigned doc_len, + const OT::SVG::accelerator_t *svg_accel, + const OT::SVG::svg_doc_cache_t *doc_cache) +{ + hb_svg_clip_path_def_t clip; + hb_svg_str_t id = parser.find_attr ("id"); + hb_svg_str_t units = parser.find_attr ("clipPathUnits").trim (); + if (units.eq_ascii_ci ("objectBoundingBox")) + clip.units_user_space = false; + else if (units.eq_ascii_ci ("userSpaceOnUse")) + clip.units_user_space = true; + else + clip.units_user_space = true; + + hb_svg_transform_t cp_t; + if (svg_parse_element_transform (parser, &cp_t)) + { + clip.has_clip_transform = true; + clip.clip_transform = cp_t; + } + + clip.first_shape = defs->clip_shapes.length; + clip.shape_count = 0; + + if (tok == SVG_TOKEN_OPEN_TAG) + { + const unsigned SVG_MAX_CLIP_DEPTH = 64; + hb_svg_transform_t inherited[SVG_MAX_CLIP_DEPTH]; + bool inherited_visibility[SVG_MAX_CLIP_DEPTH]; + inherited[0] = hb_svg_transform_t (); + inherited[1] = hb_svg_transform_t (); + inherited_visibility[0] = true; + inherited_visibility[1] = true; + hb_decycler_t use_decycler; + + int cdepth = 1; + bool had_alloc_failure = false; + hb_svg_clip_collect_context_t collect_ctx = { + defs, &clip, + doc_start, doc_len, svg_accel, doc_cache, + &use_decycler, &had_alloc_failure + }; + while (cdepth > 0) + { + hb_svg_token_type_t ct = parser.next (); + if (ct == SVG_TOKEN_EOF) break; + if (ct == SVG_TOKEN_CLOSE_TAG) { cdepth--; continue; } + if (ct == SVG_TOKEN_OPEN_TAG || ct == SVG_TOKEN_SELF_CLOSE_TAG) + { + if (parser.tag_name.eq ("symbol")) + { + if (ct == SVG_TOKEN_OPEN_TAG) + { + int skip_depth = 1; + while (skip_depth > 0) + { + hb_svg_token_type_t st = parser.next (); + if (st == SVG_TOKEN_EOF) break; + if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--; + else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++; + } + } + continue; + } + + if (parser.tag_name.eq ("defs")) + { + if (ct == SVG_TOKEN_OPEN_TAG) + { + int skip_depth = 1; + while (skip_depth > 0) + { + hb_svg_token_type_t st = parser.next (); + if (st == SVG_TOKEN_EOF) break; + if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--; + else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++; + } + } + continue; + } + + bool parent_visible = (unsigned) cdepth < SVG_MAX_CLIP_DEPTH + ? inherited_visibility[cdepth] + : true; + bool is_visible = svg_resolve_element_visibility (parser, parent_visible); + bool is_hidden = !is_visible; + if (is_hidden) + { + if (ct == SVG_TOKEN_OPEN_TAG) + { + int skip_depth = 1; + while (skip_depth > 0) + { + hb_svg_token_type_t st = parser.next (); + if (st == SVG_TOKEN_EOF) break; + if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--; + else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++; + } + } + continue; + } + + hb_svg_transform_t effective = (unsigned) cdepth < SVG_MAX_CLIP_DEPTH + ? inherited[cdepth] + : hb_svg_transform_t (); + + hb_svg_transform_t local; + bool has_local_transform = svg_parse_element_transform (parser, &local); + if (has_local_transform) + { + effective.multiply (local); + } + + if (parser.tag_name.eq ("use")) + { + svg_clip_collect_use_target (&collect_ctx, parser, effective, 0); + } + else + { + hb_svg_shape_emit_data_t shape; + if (hb_raster_svg_parse_shape_tag (parser, &shape)) + svg_clip_append_shape (&collect_ctx, shape, effective); + } + + if (ct == SVG_TOKEN_OPEN_TAG) + { + if ((unsigned) (cdepth + 1) < SVG_MAX_CLIP_DEPTH) + { + inherited[cdepth + 1] = effective; + inherited_visibility[cdepth + 1] = is_visible; + } + cdepth++; + } + } + } + if (had_alloc_failure) + id = {}; + } + + if (id.len) + (void) defs->add_clip_path (hb_bytes_t (id.data, id.len), clip); +} + +struct hb_svg_clip_emit_data_t +{ + const hb_svg_defs_t *defs; + const hb_svg_clip_path_def_t *clip; + hb_transform_t<> base_transform; + hb_transform_t<> bbox_transform; + bool has_bbox_transform = false; +}; + +static inline hb_transform_t<> +svg_to_hb_transform (const hb_svg_transform_t &t) +{ + return hb_transform_t<> (t.xx, t.yx, t.xy, t.yy, t.dx, t.dy); +} + +static void +svg_clip_path_emit (hb_draw_funcs_t *dfuncs, + void *draw_data, + void *user_data) +{ + hb_raster_draw_t *rdr = (hb_raster_draw_t *) draw_data; + hb_svg_clip_emit_data_t *ed = (hb_svg_clip_emit_data_t *) user_data; + const hb_svg_clip_path_def_t *clip = ed->clip; + + for (unsigned i = 0; i < clip->shape_count; i++) + { + const hb_svg_clip_shape_t &s = ed->defs->clip_shapes[clip->first_shape + i]; + hb_transform_t<> t = ed->base_transform; + if (ed->has_bbox_transform) + t.multiply (ed->bbox_transform); + if (clip->has_clip_transform) + t.multiply (svg_to_hb_transform (clip->clip_transform)); + if (s.has_transform) + t.multiply (svg_to_hb_transform (s.transform)); + + hb_raster_draw_set_transform (rdr, t.xx, t.yx, t.xy, t.yy, t.x0, t.y0); + + hb_svg_shape_emit_data_t shape = s.shape; + hb_raster_svg_shape_path_emit (dfuncs, draw_data, &shape); + } +} + +bool +hb_raster_svg_push_clip_path_ref (hb_raster_paint_t *paint, + hb_svg_defs_t *defs, + hb_svg_str_t clip_path_str, + const hb_extents_t<> *object_bbox) +{ + if (clip_path_str.is_null ()) return false; + hb_svg_str_t trimmed = clip_path_str.trim (); + if (!trimmed.len || trimmed.eq_ascii_ci ("none")) return false; + + hb_svg_str_t clip_id; + if (!hb_raster_svg_parse_local_id_ref (trimmed, &clip_id, nullptr)) + return false; + + const hb_svg_clip_path_def_t *clip = defs->find_clip_path (hb_bytes_t (clip_id.data, clip_id.len)); + if (!clip) return false; + + hb_svg_clip_emit_data_t ed; + ed.defs = defs; + ed.clip = clip; + ed.base_transform = paint->current_effective_transform (); + + if (!clip->units_user_space) + { + if (!object_bbox || object_bbox->is_empty ()) + return false; + float w = object_bbox->xmax - object_bbox->xmin; + float h = object_bbox->ymax - object_bbox->ymin; + if (!(std::isfinite (w) && std::isfinite (h)) || w <= 0.f || h <= 0.f) + return false; + ed.has_bbox_transform = true; + ed.bbox_transform = hb_transform_t<> (w, 0, 0, h, object_bbox->xmin, object_bbox->ymin); + } + + hb_raster_paint_push_clip_path (paint, svg_clip_path_emit, &ed); + return true; +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-clip.hh b/thirdparty/harfbuzz/src/hb-raster-svg-clip.hh new file mode 100644 index 0000000000..0b89cc8f0d --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-clip.hh @@ -0,0 +1,53 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_CLIP_HH +#define HB_RASTER_SVG_CLIP_HH + +#include "hb.hh" + +#include "OT/Color/svg/svg.hh" +#include "hb-paint.hh" +#include "hb-raster-svg-defs.hh" + +struct hb_raster_paint_t; + +HB_INTERNAL void +hb_raster_svg_process_clip_path_def (hb_svg_defs_t *defs, + hb_svg_xml_parser_t &parser, + hb_svg_token_type_t tok, + const char *doc_start, + unsigned doc_len, + const OT::SVG::accelerator_t *svg_accel, + const OT::SVG::svg_doc_cache_t *doc_cache); + +HB_INTERNAL bool +hb_raster_svg_push_clip_path_ref (hb_raster_paint_t *paint, + hb_svg_defs_t *defs, + hb_svg_str_t clip_path_str, + const hb_extents_t<> *object_bbox); + +#endif /* HB_RASTER_SVG_CLIP_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-color.cc b/thirdparty/harfbuzz/src/hb-raster-svg-color.cc new file mode 100644 index 0000000000..a13634dd4b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-color.cc @@ -0,0 +1,340 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-color.hh" + +#include "hb-raster.h" +#include "hb-raster-svg-base.hh" +#include "hb-ot-color.h" + +#include +#include + +struct hb_svg_named_color_t +{ + const char *name; + uint32_t rgb; /* 0xRRGGBB */ +}; + +static const hb_svg_named_color_t svg_named_colors[] = { + {"aliceblue", 0xF0F8FF}, + {"antiquewhite", 0xFAEBD7}, + {"aqua", 0x00FFFF}, + {"aquamarine", 0x7FFFD4}, + {"azure", 0xF0FFFF}, + {"beige", 0xF5F5DC}, + {"bisque", 0xFFE4C4}, + {"black", 0x000000}, + {"blanchedalmond", 0xFFEBCD}, + {"blue", 0x0000FF}, + {"blueviolet", 0x8A2BE2}, + {"brown", 0xA52A2A}, + {"burlywood", 0xDEB887}, + {"cadetblue", 0x5F9EA0}, + {"chartreuse", 0x7FFF00}, + {"chocolate", 0xD2691E}, + {"coral", 0xFF7F50}, + {"cornflowerblue", 0x6495ED}, + {"cornsilk", 0xFFF8DC}, + {"crimson", 0xDC143C}, + {"cyan", 0x00FFFF}, + {"darkblue", 0x00008B}, + {"darkcyan", 0x008B8B}, + {"darkgoldenrod", 0xB8860B}, + {"darkgray", 0xA9A9A9}, + {"darkgreen", 0x006400}, + {"darkgrey", 0xA9A9A9}, + {"darkkhaki", 0xBDB76B}, + {"darkmagenta", 0x8B008B}, + {"darkolivegreen", 0x556B2F}, + {"darkorange", 0xFF8C00}, + {"darkorchid", 0x9932CC}, + {"darkred", 0x8B0000}, + {"darksalmon", 0xE9967A}, + {"darkseagreen", 0x8FBC8F}, + {"darkslateblue", 0x483D8B}, + {"darkslategray", 0x2F4F4F}, + {"darkslategrey", 0x2F4F4F}, + {"darkturquoise", 0x00CED1}, + {"darkviolet", 0x9400D3}, + {"deeppink", 0xFF1493}, + {"deepskyblue", 0x00BFFF}, + {"dimgray", 0x696969}, + {"dimgrey", 0x696969}, + {"dodgerblue", 0x1E90FF}, + {"firebrick", 0xB22222}, + {"floralwhite", 0xFFFAF0}, + {"forestgreen", 0x228B22}, + {"fuchsia", 0xFF00FF}, + {"gainsboro", 0xDCDCDC}, + {"ghostwhite", 0xF8F8FF}, + {"gold", 0xFFD700}, + {"goldenrod", 0xDAA520}, + {"gray", 0x808080}, + {"green", 0x008000}, + {"greenyellow", 0xADFF2F}, + {"grey", 0x808080}, + {"honeydew", 0xF0FFF0}, + {"hotpink", 0xFF69B4}, + {"indianred", 0xCD5C5C}, + {"indigo", 0x4B0082}, + {"ivory", 0xFFFFF0}, + {"khaki", 0xF0E68C}, + {"lavender", 0xE6E6FA}, + {"lavenderblush", 0xFFF0F5}, + {"lawngreen", 0x7CFC00}, + {"lemonchiffon", 0xFFFACD}, + {"lightblue", 0xADD8E6}, + {"lightcoral", 0xF08080}, + {"lightcyan", 0xE0FFFF}, + {"lightgoldenrodyellow", 0xFAFAD2}, + {"lightgray", 0xD3D3D3}, + {"lightgreen", 0x90EE90}, + {"lightgrey", 0xD3D3D3}, + {"lightpink", 0xFFB6C1}, + {"lightsalmon", 0xFFA07A}, + {"lightseagreen", 0x20B2AA}, + {"lightskyblue", 0x87CEFA}, + {"lightslategray", 0x778899}, + {"lightslategrey", 0x778899}, + {"lightsteelblue", 0xB0C4DE}, + {"lightyellow", 0xFFFFE0}, + {"lime", 0x00FF00}, + {"limegreen", 0x32CD32}, + {"linen", 0xFAF0E6}, + {"magenta", 0xFF00FF}, + {"maroon", 0x800000}, + {"mediumaquamarine", 0x66CDAA}, + {"mediumblue", 0x0000CD}, + {"mediumorchid", 0xBA55D3}, + {"mediumpurple", 0x9370DB}, + {"mediumseagreen", 0x3CB371}, + {"mediumslateblue", 0x7B68EE}, + {"mediumspringgreen", 0x00FA9A}, + {"mediumturquoise", 0x48D1CC}, + {"mediumvioletred", 0xC71585}, + {"midnightblue", 0x191970}, + {"mintcream", 0xF5FFFA}, + {"mistyrose", 0xFFE4E1}, + {"moccasin", 0xFFE4B5}, + {"navajowhite", 0xFFDEAD}, + {"navy", 0x000080}, + {"oldlace", 0xFDF5E6}, + {"olive", 0x808000}, + {"olivedrab", 0x6B8E23}, + {"orange", 0xFFA500}, + {"orangered", 0xFF4500}, + {"orchid", 0xDA70D6}, + {"palegoldenrod", 0xEEE8AA}, + {"palegreen", 0x98FB98}, + {"paleturquoise", 0xAFEEEE}, + {"palevioletred", 0xDB7093}, + {"papayawhip", 0xFFEFD5}, + {"peachpuff", 0xFFDAB9}, + {"peru", 0xCD853F}, + {"pink", 0xFFC0CB}, + {"plum", 0xDDA0DD}, + {"powderblue", 0xB0E0E6}, + {"purple", 0x800080}, + {"rebeccapurple", 0x663399}, + {"red", 0xFF0000}, + {"rosybrown", 0xBC8F8F}, + {"royalblue", 0x4169E1}, + {"saddlebrown", 0x8B4513}, + {"salmon", 0xFA8072}, + {"sandybrown", 0xF4A460}, + {"seagreen", 0x2E8B57}, + {"seashell", 0xFFF5EE}, + {"sienna", 0xA0522D}, + {"silver", 0xC0C0C0}, + {"skyblue", 0x87CEEB}, + {"slateblue", 0x6A5ACD}, + {"slategray", 0x708090}, + {"slategrey", 0x708090}, + {"snow", 0xFFFAFA}, + {"springgreen", 0x00FF7F}, + {"steelblue", 0x4682B4}, + {"tan", 0xD2B48C}, + {"teal", 0x008080}, + {"thistle", 0xD8BFD8}, + {"tomato", 0xFF6347}, + {"turquoise", 0x40E0D0}, + {"violet", 0xEE82EE}, + {"wheat", 0xF5DEB3}, + {"white", 0xFFFFFF}, + {"whitesmoke", 0xF5F5F5}, + {"yellow", 0xFFFF00}, + {"yellowgreen", 0x9ACD32}, +}; + +static int +hexval (char c) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'a' && c <= 'f') return c - 'a' + 10; + if (c >= 'A' && c <= 'F') return c - 'A' + 10; + return -1; +} + +/* Parse SVG color value; returns HB_COLOR with alpha = 255. + * Sets *is_none if "none". */ +hb_color_t +hb_raster_svg_parse_color (hb_svg_str_t s, + hb_paint_funcs_t *pfuncs, + void *paint_data, + hb_color_t foreground, + hb_face_t *face, + unsigned palette, + bool *is_none) +{ + *is_none = false; + s = s.trim (); + if (!s.len) { *is_none = true; return HB_COLOR (0, 0, 0, 0); } + + if (s.eq_ascii_ci ("none") || s.eq_ascii_ci ("transparent")) + { + *is_none = true; + return HB_COLOR (0, 0, 0, 0); + } + + if (s.eq_ascii_ci ("currentColor")) + return foreground; + + /* var(--colorN) → CPAL palette color */ + if (s.starts_with_ascii_ci ("var(")) + { + const char *p = s.data + 4; + const char *e = s.data + s.len; + while (p < e && *p == ' ') p++; + if (p + 7 < e && p[0] == '-' && p[1] == '-' && p[2] == 'c' && + p[3] == 'o' && p[4] == 'l' && p[5] == 'o' && p[6] == 'r') + { + p += 7; + unsigned color_index = 0; + while (p < e && *p >= '0' && *p <= '9') + color_index = color_index * 10 + (*p++ - '0'); + + hb_color_t palette_color; + if (hb_paint_custom_palette_color (pfuncs, paint_data, color_index, &palette_color)) + return palette_color; + + unsigned count = 1; + hb_ot_color_palette_get_colors (face, palette, color_index, &count, &palette_color); + if (count) + return palette_color; + } + + /* Fallback value after comma: var(--colorN, fallback) */ + p = s.data + 4; + while (p < e && *p != ',') p++; + if (p < e) + { + p++; + while (p < e && *p == ' ') p++; + const char *val_start = p; + /* Find closing paren */ + while (e > val_start && *(e - 1) != ')') e--; + if (e > val_start) e--; + hb_svg_str_t fallback = {val_start, (unsigned) (e - val_start)}; + return hb_raster_svg_parse_color (fallback, pfuncs, paint_data, foreground, face, palette, is_none); + } + + return foreground; + } + + /* #RGB or #RRGGBB */ + if (s.data[0] == '#') + { + if (s.len == 4) /* #RGB */ + { + int r = hexval (s.data[1]); + int g = hexval (s.data[2]); + int b = hexval (s.data[3]); + if (r < 0 || g < 0 || b < 0) return HB_COLOR (0, 0, 0, 255); + return HB_COLOR (b * 17, g * 17, r * 17, 255); + } + if (s.len == 7) /* #RRGGBB */ + { + int r = hexval (s.data[1]) * 16 + hexval (s.data[2]); + int g = hexval (s.data[3]) * 16 + hexval (s.data[4]); + int b = hexval (s.data[5]) * 16 + hexval (s.data[6]); + return HB_COLOR (b, g, r, 255); + } + return HB_COLOR (0, 0, 0, 255); + } + + /* rgb(r, g, b) or rgb(r%, g%, b%) */ + if (s.starts_with ("rgb")) + { + const char *p = s.data + 3; + const char *e = s.data + s.len; + while (p < e && *p != '(') p++; + if (p < e) p++; + + auto read_component = [&] () -> int { + while (p < e && (*p == ' ' || *p == ',')) p++; + char buf[32]; + unsigned n = 0; + while (p < e && n < sizeof (buf) - 1 && ((*p >= '0' && *p <= '9') || *p == '.' || *p == '-')) + buf[n++] = *p++; + bool is_pct = (p < e && *p == '%'); + if (is_pct) p++; + buf[n] = '\0'; + float val = strtof (buf, nullptr); + if (is_pct) val = val * 255.f / 100.f; + return hb_clamp ((int) (val + 0.5f), 0, 255); + }; + + int r = read_component (); + int g = read_component (); + int b = read_component (); + return HB_COLOR (b, g, r, 255); + } + + /* Named colors (case-insensitive comparison) */ + { + char lower[32]; + unsigned n = hb_min (s.len, (unsigned) sizeof (lower) - 1); + for (unsigned i = 0; i < n; i++) + lower[i] = (s.data[i] >= 'A' && s.data[i] <= 'Z') ? s.data[i] + 32 : s.data[i]; + lower[n] = '\0'; + + for (unsigned i = 0; i < ARRAY_LENGTH (svg_named_colors); i++) + if (strcmp (lower, svg_named_colors[i].name) == 0) + { + uint32_t rgb = svg_named_colors[i].rgb; + return HB_COLOR (rgb & 0xFF, (rgb >> 8) & 0xFF, (rgb >> 16) & 0xFF, 255); + } + } + + return HB_COLOR (0, 0, 0, 255); +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-color.hh b/thirdparty/harfbuzz/src/hb-raster-svg-color.hh new file mode 100644 index 0000000000..893072e140 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-color.hh @@ -0,0 +1,44 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_COLOR_HH +#define HB_RASTER_SVG_COLOR_HH + +#include "hb.hh" + +#include "hb-face.hh" +#include "hb-raster-svg-base.hh" + +HB_INTERNAL hb_color_t +hb_raster_svg_parse_color (hb_svg_str_t s, + hb_paint_funcs_t *pfuncs, + void *paint_data, + hb_color_t foreground, + hb_face_t *face, + unsigned palette, + bool *is_none); + +#endif /* HB_RASTER_SVG_COLOR_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-context.hh b/thirdparty/harfbuzz/src/hb-raster-svg-context.hh new file mode 100644 index 0000000000..5882cab177 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-context.hh @@ -0,0 +1,113 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_CONTEXT_HH +#define HB_RASTER_SVG_CONTEXT_HH + +#include "hb.hh" + +#include "OT/Color/svg/svg.hh" +#include "hb-decycler.hh" +#include "hb-raster-paint.hh" +#include "hb-raster-svg-base.hh" +#include "hb-raster-svg-defs.hh" + +struct hb_svg_fill_context_t +{ + hb_raster_paint_t *paint; + hb_paint_funcs_t *pfuncs; + hb_font_t *font; + unsigned palette; + hb_svg_defs_t *defs; +}; + +struct hb_svg_use_context_t +{ + hb_raster_paint_t *paint; + hb_paint_funcs_t *pfuncs; + + const char *doc_start; + unsigned doc_len; + const OT::SVG::accelerator_t *svg_accel; + const OT::SVG::svg_doc_cache_t *doc_cache; + hb_decycler_t *use_decycler; +}; + +struct hb_svg_render_context_t +{ + hb_raster_paint_t *paint; + hb_paint_funcs_t *pfuncs; + hb_font_t *font; + unsigned palette; + hb_color_t foreground; + hb_svg_defs_t defs; + int depth = 0; + + const char *doc_start; + unsigned doc_len; + const OT::SVG::accelerator_t *svg_accel = nullptr; + const OT::SVG::svg_doc_cache_t *doc_cache = nullptr; + hb_decycler_t use_decycler; + bool suppress_viewbox_once = false; + bool allow_symbol_render_once = false; + + void push_transform (float xx, float yx, float xy, float yy, float dx, float dy) + { + hb_paint_push_transform (pfuncs, paint, xx, yx, xy, yy, dx, dy); + } + void pop_transform () + { + hb_paint_pop_transform (pfuncs, paint); + } + void push_group () + { + hb_paint_push_group (pfuncs, paint); + } + void pop_group (hb_paint_composite_mode_t mode) + { + hb_paint_pop_group (pfuncs, paint, mode); + } + void paint_color (hb_color_t color) + { + hb_paint_color (pfuncs, paint, false, color); + } + void pop_clip () + { + hb_paint_pop_clip (pfuncs, paint); + } +}; + +struct hb_svg_cascade_t +{ + hb_svg_str_t fill; + float fill_opacity = 1.f; + hb_svg_str_t clip_path; + hb_color_t color = HB_COLOR (0, 0, 0, 255); + bool visibility = true; + float opacity = 1.f; +}; + +#endif /* HB_RASTER_SVG_CONTEXT_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-defs-scan.cc b/thirdparty/harfbuzz/src/hb-raster-svg-defs-scan.cc new file mode 100644 index 0000000000..3b90b457eb --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-defs-scan.cc @@ -0,0 +1,108 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-defs-scan.hh" + +#include "hb-raster-svg-clip.hh" +#include "hb-raster-svg-gradient.hh" + +static inline bool +hb_raster_svg_process_defs_child_tag (const hb_svg_defs_scan_context_t *ctx, + hb_svg_xml_parser_t &parser, + hb_svg_token_type_t tok) +{ + if (parser.tag_name.eq ("linearGradient")) + { + hb_raster_svg_process_gradient_def (ctx->defs, parser, tok, SVG_GRADIENT_LINEAR, + ctx->pfuncs, ctx->paint_data, + ctx->foreground, ctx->face, + ctx->palette); + return true; + } + if (parser.tag_name.eq ("radialGradient")) + { + hb_raster_svg_process_gradient_def (ctx->defs, parser, tok, SVG_GRADIENT_RADIAL, + ctx->pfuncs, ctx->paint_data, + ctx->foreground, ctx->face, + ctx->palette); + return true; + } + if (parser.tag_name.eq ("clipPath")) + { + hb_raster_svg_process_clip_path_def (ctx->defs, parser, tok, + ctx->doc_start, ctx->doc_len, + ctx->svg_accel, ctx->doc_cache); + return true; + } + return false; +} + +void +hb_raster_svg_process_defs_element (const hb_svg_defs_scan_context_t *ctx, + hb_svg_xml_parser_t &parser) +{ + int depth = 1; + + while (depth > 0) + { + hb_svg_token_type_t tok = parser.next (); + if (tok == SVG_TOKEN_EOF) break; + + if (tok == SVG_TOKEN_CLOSE_TAG) + { + depth--; + continue; + } + + if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG) + { + if (!hb_raster_svg_process_defs_child_tag (ctx, parser, tok) && + tok == SVG_TOKEN_OPEN_TAG) + depth++; + } + } +} + +void +hb_raster_svg_collect_defs (const hb_svg_defs_scan_context_t *ctx, + const char *data, + unsigned data_len) +{ + hb_svg_xml_parser_t parser (data, data_len); + while (true) + { + hb_svg_token_type_t tok = parser.next (); + if (tok == SVG_TOKEN_EOF) break; + if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG) + hb_raster_svg_process_defs_child_tag (ctx, parser, tok); + } +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-defs-scan.hh b/thirdparty/harfbuzz/src/hb-raster-svg-defs-scan.hh new file mode 100644 index 0000000000..bddad1f74c --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-defs-scan.hh @@ -0,0 +1,58 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_DEFS_SCAN_HH +#define HB_RASTER_SVG_DEFS_SCAN_HH + +#include "hb.hh" + +#include "OT/Color/svg/svg.hh" +#include "hb-raster-svg-defs.hh" + +struct hb_svg_defs_scan_context_t +{ + hb_svg_defs_t *defs; + hb_paint_funcs_t *pfuncs; + void *paint_data; + hb_color_t foreground; + hb_face_t *face; + unsigned palette; + const char *doc_start; + unsigned doc_len; + const OT::SVG::accelerator_t *svg_accel; + const OT::SVG::svg_doc_cache_t *doc_cache; +}; + +HB_INTERNAL void +hb_raster_svg_process_defs_element (const hb_svg_defs_scan_context_t *ctx, + hb_svg_xml_parser_t &parser); + +HB_INTERNAL void +hb_raster_svg_collect_defs (const hb_svg_defs_scan_context_t *ctx, + const char *data, + unsigned data_len); + +#endif /* HB_RASTER_SVG_DEFS_SCAN_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-defs.cc b/thirdparty/harfbuzz/src/hb-raster-svg-defs.cc new file mode 100644 index 0000000000..0341441671 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-defs.cc @@ -0,0 +1,121 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-defs.hh" + +#include + +hb_svg_defs_t::~hb_svg_defs_t () +{ + for (unsigned i = 0; i < owned_id_strings.length; i++) + hb_free (owned_id_strings.arrayZ[i]); +} + +bool +hb_svg_defs_t::add_id_mapping (hb_hashmap_t *map, + hb_bytes_t id, + unsigned idx) +{ + if (!id.length) + return false; + if (map->has (id)) + return true; + + unsigned n = (unsigned) id.length; + char *owned = (char *) hb_malloc (n + 1); + if (unlikely (!owned)) + return false; + hb_memcpy (owned, id.arrayZ, n); + owned[n] = '\0'; + if (unlikely (!owned_id_strings.push_or_fail (owned))) + { + hb_free (owned); + return false; + } + + hb_bytes_t owned_key = hb_bytes_t (owned, n); + if (unlikely (!map->set (owned_key, idx))) + return false; + return true; +} + +bool +hb_svg_defs_t::add_gradient (hb_bytes_t id, const hb_svg_gradient_t &grad) +{ + unsigned idx = gradients.length; + gradients.push (grad); + if (unlikely (gradients.in_error ())) + return false; + + if (unlikely (!add_id_mapping (&gradient_by_id, id, idx))) + { + gradients.pop (); + return false; + } + return true; +} + +const hb_svg_gradient_t * +hb_svg_defs_t::find_gradient (hb_bytes_t id) const +{ + if (!id.length) return nullptr; + unsigned *idx = nullptr; + if (gradient_by_id.has (id, &idx)) + return &gradients[*idx]; + return nullptr; +} + +bool +hb_svg_defs_t::add_clip_path (hb_bytes_t id, const hb_svg_clip_path_def_t &clip) +{ + unsigned idx = clip_paths.length; + clip_paths.push (clip); + if (unlikely (clip_paths.in_error ())) + return false; + + if (unlikely (!add_id_mapping (&clip_path_by_id, id, idx))) + { + clip_paths.pop (); + return false; + } + return true; +} + +const hb_svg_clip_path_def_t * +hb_svg_defs_t::find_clip_path (hb_bytes_t id) const +{ + if (!id.length) return nullptr; + unsigned *idx = nullptr; + if (clip_path_by_id.has (id, &idx)) + return &clip_paths[*idx]; + return nullptr; +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-defs.hh b/thirdparty/harfbuzz/src/hb-raster-svg-defs.hh new file mode 100644 index 0000000000..525d67519d --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-defs.hh @@ -0,0 +1,108 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_DEFS_HH +#define HB_RASTER_SVG_DEFS_HH + +#include "hb.hh" + +#include "hb-map.hh" +#include "hb-raster-svg-parse.hh" + +enum hb_svg_gradient_type_t +{ + SVG_GRADIENT_LINEAR, + SVG_GRADIENT_RADIAL +}; + +struct hb_svg_gradient_stop_t +{ + float offset; + hb_color_t color; + bool is_current_color = false; +}; + +struct hb_svg_gradient_t +{ + hb_svg_gradient_type_t type; + + hb_paint_extend_t spread = HB_PAINT_EXTEND_PAD; + bool has_spread = false; + hb_svg_transform_t gradient_transform; + bool has_gradient_transform = false; + bool units_user_space = false; + bool has_units_user_space = false; + + float x1 = 0, y1 = 0, x2 = 1, y2 = 0; + bool has_x1 = false, has_y1 = false, has_x2 = false, has_y2 = false; + + float cx = 0.5f, cy = 0.5f, r = 0.5f; + float fx = -1.f, fy = -1.f, fr = 0.f; + bool has_cx = false, has_cy = false, has_r = false; + bool has_fx = false, has_fy = false, has_fr = false; + + hb_vector_t stops; + + hb_bytes_t href_id = {}; +}; + +struct hb_svg_clip_path_def_t +{ + unsigned first_shape = 0; + unsigned shape_count = 0; + hb_svg_transform_t clip_transform; + bool has_clip_transform = false; + bool units_user_space = true; +}; + +struct hb_svg_clip_shape_t +{ + hb_svg_shape_emit_data_t shape; + hb_svg_transform_t transform; + bool has_transform = false; +}; + +struct hb_svg_defs_t +{ + hb_vector_t gradients; + hb_vector_t clip_shapes; + hb_vector_t clip_paths; + hb_hashmap_t gradient_by_id; + hb_hashmap_t clip_path_by_id; + hb_vector_t owned_id_strings; + + HB_INTERNAL ~hb_svg_defs_t (); + + HB_INTERNAL bool add_id_mapping (hb_hashmap_t *map, + hb_bytes_t id, + unsigned idx); + HB_INTERNAL bool add_gradient (hb_bytes_t id, const hb_svg_gradient_t &grad); + HB_INTERNAL const hb_svg_gradient_t *find_gradient (hb_bytes_t id) const; + HB_INTERNAL bool add_clip_path (hb_bytes_t id, const hb_svg_clip_path_def_t &clip); + HB_INTERNAL const hb_svg_clip_path_def_t *find_clip_path (hb_bytes_t id) const; +}; + +#endif /* HB_RASTER_SVG_DEFS_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-fill.cc b/thirdparty/harfbuzz/src/hb-raster-svg-fill.cc new file mode 100644 index 0000000000..98991ec501 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-fill.cc @@ -0,0 +1,300 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-fill.hh" + +#include "hb-raster-svg-base.hh" +#include "hb-raster-svg-color.hh" +#include "hb-decycler.hh" + +#include + +struct hb_svg_color_line_data_t +{ + const hb_svg_gradient_t *grad; + hb_paint_extend_t extend; + float alpha_scale; + hb_color_t current_color; +}; + +static unsigned +svg_color_line_get_stops (hb_color_line_t *color_line HB_UNUSED, + void *color_line_data, + unsigned int start, + unsigned int *count, + hb_color_stop_t *color_stops, + void *user_data HB_UNUSED) +{ + hb_svg_color_line_data_t *cl = (hb_svg_color_line_data_t *) color_line_data; + const hb_svg_gradient_t *grad = cl->grad; + unsigned total = grad->stops.length; + + if (count) + { + unsigned n = hb_min (*count, total > start ? total - start : 0u); + for (unsigned i = 0; i < n; i++) + { + const hb_svg_gradient_stop_t &stop = grad->stops[start + i]; + color_stops[i].offset = stop.offset; + color_stops[i].is_foreground = false; + hb_color_t c = stop.is_current_color ? cl->current_color : stop.color; + if (stop.is_current_color) + { + uint8_t a = (uint8_t) hb_raster_div255 (hb_color_get_alpha (c) * hb_color_get_alpha (stop.color)); + c = HB_COLOR (hb_color_get_blue (c), + hb_color_get_green (c), + hb_color_get_red (c), + a); + } + if (cl->alpha_scale < 1.f) + { + uint8_t a = (uint8_t) hb_clamp ((int) (hb_color_get_alpha (c) * cl->alpha_scale + 0.5f), 0, 255); + c = HB_COLOR (hb_color_get_blue (c), + hb_color_get_green (c), + hb_color_get_red (c), + a); + } + color_stops[i].color = c; + } + *count = n; + } + return total; +} + +static hb_paint_extend_t +svg_color_line_get_extend (hb_color_line_t *color_line HB_UNUSED, + void *color_line_data, + void *user_data HB_UNUSED) +{ + hb_svg_color_line_data_t *cl = (hb_svg_color_line_data_t *) color_line_data; + return cl->extend; +} + +static bool +svg_parse_paint_url_with_fallback (hb_svg_str_t s, + hb_svg_str_t *out_id, + hb_svg_str_t *fallback) +{ + if (out_id) *out_id = {}; + hb_svg_str_t id; + if (!hb_raster_svg_parse_local_id_ref (s, &id, fallback)) + return false; + if (!s.trim ().starts_with_ascii_ci ("url(")) + return false; + if (out_id) *out_id = id; + return id.len > 0; +} + +void +hb_raster_svg_emit_fill (const hb_svg_fill_context_t *ctx, + hb_svg_str_t fill_str, + float fill_opacity, + const hb_extents_t<> *object_bbox, + hb_color_t current_color) +{ + bool is_none = false; + + hb_svg_str_t url_id; + hb_svg_str_t fallback_paint; + bool has_url_paint = svg_parse_paint_url_with_fallback (fill_str, &url_id, &fallback_paint); + + const hb_svg_gradient_t *grad = has_url_paint ? ctx->defs->find_gradient (hb_bytes_t (url_id.data, url_id.len)) : nullptr; + if (has_url_paint && !grad) + { + if (fallback_paint.len) + hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color); + return; + } + if (grad) + { + const unsigned SVG_MAX_GRADIENT_REF_DEPTH = 1024; + hb_vector_t chain; + hb_decycler_t decycler; + const hb_svg_gradient_t *cur = grad; + while (cur) + { + if (chain.length >= SVG_MAX_GRADIENT_REF_DEPTH) + break; + + hb_decycler_node_t node (decycler); + if (unlikely (!node.visit ((uintptr_t) cur))) + break; + + if (unlikely (!chain.push_or_fail (cur))) + { + if (fallback_paint.len) + hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color); + return; + } + + if (!cur->href_id.length) break; + const hb_svg_gradient_t *next = ctx->defs->find_gradient (cur->href_id); + if (!next) break; + cur = next; + } + if (!chain.length) + { + if (fallback_paint.len) + hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color); + return; + } + + if (!chain.length) { + if (fallback_paint.len) + hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color); + return; + } + hb_svg_gradient_t effective = *chain.arrayZ[chain.length - 1]; + for (int i = (int) chain.length - 2; i >= 0; i--) + { + const hb_svg_gradient_t *g = chain.arrayZ[i]; + effective.type = g->type; + if (g->stops.length) effective.stops = g->stops; + if (g->has_spread) { effective.spread = g->spread; effective.has_spread = true; } + if (g->has_units_user_space) + { + effective.units_user_space = g->units_user_space; + effective.has_units_user_space = true; + } + if (g->has_gradient_transform) + { + effective.gradient_transform = g->gradient_transform; + effective.has_gradient_transform = true; + } + if (g->has_x1) { effective.x1 = g->x1; effective.has_x1 = true; } + if (g->has_y1) { effective.y1 = g->y1; effective.has_y1 = true; } + if (g->has_x2) { effective.x2 = g->x2; effective.has_x2 = true; } + if (g->has_y2) { effective.y2 = g->y2; effective.has_y2 = true; } + if (g->has_cx) { effective.cx = g->cx; effective.has_cx = true; } + if (g->has_cy) { effective.cy = g->cy; effective.has_cy = true; } + if (g->has_r) { effective.r = g->r; effective.has_r = true; } + if (g->has_fx) { effective.fx = g->fx; effective.has_fx = true; } + if (g->has_fy) { effective.fy = g->fy; effective.has_fy = true; } + if (g->has_fr) { effective.fr = g->fr; effective.has_fr = true; } + } + if (!effective.stops.length) + { + if (fallback_paint.len) + hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color); + return; + } + + hb_svg_color_line_data_t cl_data; + cl_data.grad = &effective; + cl_data.extend = effective.spread; + cl_data.alpha_scale = hb_clamp (fill_opacity, 0.f, 1.f); + cl_data.current_color = current_color; + + hb_color_line_t cl = { + &cl_data, + svg_color_line_get_stops, nullptr, + svg_color_line_get_extend, nullptr + }; + + bool has_bbox_transform = !effective.units_user_space && object_bbox && !object_bbox->is_empty (); + if (has_bbox_transform) + { + float w = object_bbox->xmax - object_bbox->xmin; + float h = object_bbox->ymax - object_bbox->ymin; + if (std::isfinite (w) && std::isfinite (h) && w > 0.f && h > 0.f) + hb_paint_push_transform (ctx->pfuncs, ctx->paint, w, 0, 0, h, object_bbox->xmin, object_bbox->ymin); + else + has_bbox_transform = false; + } + + if (effective.has_gradient_transform) + hb_paint_push_transform (ctx->pfuncs, ctx->paint, + effective.gradient_transform.xx, + effective.gradient_transform.yx, + effective.gradient_transform.xy, + effective.gradient_transform.yy, + effective.gradient_transform.dx, + effective.gradient_transform.dy); + + if (effective.type == SVG_GRADIENT_LINEAR) + { + hb_paint_linear_gradient (ctx->pfuncs, ctx->paint, &cl, + effective.x1, effective.y1, + effective.x2, effective.y2, + effective.x2 - (effective.y2 - effective.y1), + effective.y2 + (effective.x2 - effective.x1)); + } + else + { + if (!std::isfinite (effective.r) || effective.r < 0.f) + { + if (effective.has_gradient_transform) + hb_paint_pop_transform (ctx->pfuncs, ctx->paint); + if (has_bbox_transform) + hb_paint_pop_transform (ctx->pfuncs, ctx->paint); + if (fallback_paint.len) + hb_raster_svg_emit_fill (ctx, fallback_paint, fill_opacity, object_bbox, current_color); + return; + } + + float fx = effective.has_fx ? effective.fx : effective.cx; + float fy = effective.has_fy ? effective.fy : effective.cy; + float fr = effective.has_fr ? effective.fr : 0.f; + if (!std::isfinite (fr) || fr < 0.f) + fr = 0.f; + fr = hb_min (fr, effective.r); + + hb_paint_radial_gradient (ctx->pfuncs, ctx->paint, &cl, + fx, fy, fr, + effective.cx, effective.cy, effective.r); + } + + if (effective.has_gradient_transform) + hb_paint_pop_transform (ctx->pfuncs, ctx->paint); + if (has_bbox_transform) + hb_paint_pop_transform (ctx->pfuncs, ctx->paint); + + return; + } + + hb_color_t color = hb_raster_svg_parse_color (fill_str, + ctx->pfuncs, + ctx->paint, + current_color, + hb_font_get_face (ctx->font), + ctx->palette, + &is_none); + if (is_none) return; + + if (fill_opacity < 1.f) + color = HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + (uint8_t) (hb_color_get_alpha (color) * fill_opacity + 0.5f)); + + hb_paint_color (ctx->pfuncs, ctx->paint, false, color); +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-fill.hh b/thirdparty/harfbuzz/src/hb-raster-svg-fill.hh new file mode 100644 index 0000000000..ce4ceecd91 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-fill.hh @@ -0,0 +1,41 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_FILL_HH +#define HB_RASTER_SVG_FILL_HH + +#include "hb.hh" + +#include "hb-raster-svg-context.hh" + +HB_INTERNAL void +hb_raster_svg_emit_fill (const hb_svg_fill_context_t *ctx, + hb_svg_str_t fill_str, + float fill_opacity, + const hb_extents_t<> *object_bbox, + hb_color_t current_color); + +#endif /* HB_RASTER_SVG_FILL_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-gradient.cc b/thirdparty/harfbuzz/src/hb-raster-svg-gradient.cc new file mode 100644 index 0000000000..cf9e51df11 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-gradient.cc @@ -0,0 +1,245 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-gradient.hh" + +#include "hb-raster-svg-base.hh" + +static bool +svg_parse_gradient_stop (hb_svg_xml_parser_t &parser, + hb_svg_gradient_t &grad, + hb_paint_funcs_t *pfuncs, + void *paint_data, + hb_color_t foreground, + hb_face_t *face, + unsigned palette) +{ + const unsigned SVG_MAX_GRADIENT_STOPS = 1024; + if (grad.stops.length >= SVG_MAX_GRADIENT_STOPS) + return true; + + hb_svg_attr_view_t attrs (parser); + hb_svg_str_t style = attrs.get ("style"); + hb_svg_style_props_t style_props; + svg_parse_style_props (style, &style_props); + hb_svg_str_t offset_str = svg_pick_attr_or_style (parser, style_props.offset, "offset"); + hb_svg_str_t color_str = svg_pick_attr_or_style (parser, style_props.stop_color, "stop-color"); + hb_svg_str_t opacity_str = svg_pick_attr_or_style (parser, style_props.stop_opacity, "stop-opacity"); + hb_svg_str_t display_str = svg_pick_attr_or_style (parser, style_props.display, "display"); + hb_svg_str_t visibility_str = svg_pick_attr_or_style (parser, style_props.visibility, "visibility"); + + if (display_str.trim ().eq_ascii_ci ("none")) + return true; + hb_svg_str_t visibility_trim = visibility_str.trim (); + if (visibility_trim.eq_ascii_ci ("hidden") || + visibility_trim.eq_ascii_ci ("collapse")) + return true; + + float offset = 0; + if (offset_str.len) + offset = hb_clamp (svg_parse_number_or_percent (offset_str, nullptr), 0.f, 1.f); + if (grad.stops.length) + offset = hb_max (offset, grad.stops.arrayZ[grad.stops.length - 1].offset); + + bool is_none = false; + hb_color_t color = HB_COLOR (0, 0, 0, 255); + bool is_current_color = false; + if (color_str.len && !svg_str_is_inherit (color_str)) + { + is_current_color = color_str.trim ().eq_ascii_ci ("currentColor"); + color = hb_raster_svg_parse_color (color_str, pfuncs, paint_data, foreground, face, palette, &is_none); + } + + if (opacity_str.len && !svg_str_is_inherit (opacity_str)) + { + float opacity = svg_parse_float_clamped01 (opacity_str); + color = HB_COLOR (hb_color_get_blue (color), + hb_color_get_green (color), + hb_color_get_red (color), + (uint8_t) (hb_color_get_alpha (color) * opacity + 0.5f)); + } + + hb_svg_gradient_stop_t stop; + stop.offset = offset; + stop.color = color; + stop.is_current_color = is_current_color; + return grad.stops.push_or_fail (stop); +} + +static void +svg_parse_gradient_attrs (hb_svg_xml_parser_t &parser, + hb_svg_gradient_t &grad) +{ + hb_svg_style_props_t style_props; + svg_parse_style_props (parser.find_attr ("style"), &style_props); + + hb_svg_str_t spread_str = svg_pick_attr_or_style (parser, style_props.spread_method, "spreadMethod").trim (); + if (spread_str.eq_ascii_ci ("reflect")) + { + grad.spread = HB_PAINT_EXTEND_REFLECT; + grad.has_spread = true; + } + else if (spread_str.eq_ascii_ci ("repeat")) + { + grad.spread = HB_PAINT_EXTEND_REPEAT; + grad.has_spread = true; + } + else if (spread_str.eq_ascii_ci ("pad")) + { + grad.spread = HB_PAINT_EXTEND_PAD; + grad.has_spread = true; + } + + hb_svg_str_t units_str = svg_pick_attr_or_style (parser, style_props.gradient_units, "gradientUnits").trim (); + if (units_str.eq_ascii_ci ("userSpaceOnUse")) + { + grad.units_user_space = true; + grad.has_units_user_space = true; + } + else if (units_str.eq_ascii_ci ("objectBoundingBox")) + { + grad.units_user_space = false; + grad.has_units_user_space = true; + } + + hb_svg_str_t transform_str = svg_pick_attr_or_style (parser, style_props.gradient_transform, "gradientTransform"); + if (transform_str.len) + { + grad.has_gradient_transform = true; + hb_raster_svg_parse_transform (transform_str, &grad.gradient_transform); + } + + hb_svg_str_t href = hb_raster_svg_find_href_attr (parser); + if (href.len) + { + hb_svg_str_t href_id; + if (hb_raster_svg_parse_local_id_ref (href, &href_id, nullptr)) + grad.href_id = hb_bytes_t (href_id.data, href_id.len); + } +} + +static void +svg_parse_gradient_geometry_attrs (hb_svg_xml_parser_t &parser, + hb_svg_gradient_t &grad) +{ + hb_svg_style_props_t style_props; + svg_parse_style_props (parser.find_attr ("style"), &style_props); + if (grad.type == SVG_GRADIENT_LINEAR) + { + hb_svg_str_t x1_str = svg_pick_attr_or_style (parser, style_props.x1, "x1"); + hb_svg_str_t y1_str = svg_pick_attr_or_style (parser, style_props.y1, "y1"); + hb_svg_str_t x2_str = svg_pick_attr_or_style (parser, style_props.x2, "x2"); + hb_svg_str_t y2_str = svg_pick_attr_or_style (parser, style_props.y2, "y2"); + if (x1_str.len) { grad.x1 = svg_parse_number_or_percent (x1_str, nullptr); grad.has_x1 = true; } + if (y1_str.len) { grad.y1 = svg_parse_number_or_percent (y1_str, nullptr); grad.has_y1 = true; } + if (x2_str.len) { grad.x2 = svg_parse_number_or_percent (x2_str, nullptr); grad.has_x2 = true; } + if (y2_str.len) { grad.y2 = svg_parse_number_or_percent (y2_str, nullptr); grad.has_y2 = true; } + + if (!grad.has_x2) + grad.x2 = 1.f; + } + else + { + hb_svg_str_t cx_str = svg_pick_attr_or_style (parser, style_props.cx, "cx"); + hb_svg_str_t cy_str = svg_pick_attr_or_style (parser, style_props.cy, "cy"); + hb_svg_str_t r_str = svg_pick_attr_or_style (parser, style_props.r, "r"); + hb_svg_str_t fx_str = svg_pick_attr_or_style (parser, style_props.fx, "fx"); + hb_svg_str_t fy_str = svg_pick_attr_or_style (parser, style_props.fy, "fy"); + hb_svg_str_t fr_str = svg_pick_attr_or_style (parser, style_props.fr, "fr"); + + if (cx_str.len) { grad.cx = svg_parse_number_or_percent (cx_str, nullptr); grad.has_cx = true; } + if (cy_str.len) { grad.cy = svg_parse_number_or_percent (cy_str, nullptr); grad.has_cy = true; } + if (r_str.len) { grad.r = svg_parse_number_or_percent (r_str, nullptr); grad.has_r = true; } + if (fx_str.len) { grad.fx = svg_parse_number_or_percent (fx_str, nullptr); grad.has_fx = true; } + if (fy_str.len) { grad.fy = svg_parse_number_or_percent (fy_str, nullptr); grad.has_fy = true; } + if (fr_str.len) { grad.fr = svg_parse_number_or_percent (fr_str, nullptr); grad.has_fr = true; } + } +} + +static void +svg_parse_gradient_children (hb_svg_defs_t *defs, + hb_svg_xml_parser_t &parser, + hb_svg_gradient_t &grad, + hb_svg_str_t *id, + hb_paint_funcs_t *pfuncs, + void *paint_data, + hb_color_t foreground, + hb_face_t *face, + unsigned palette) +{ + int gdepth = 1; + bool had_alloc_failure = false; + while (gdepth > 0) + { + hb_svg_token_type_t gt = parser.next (); + if (gt == SVG_TOKEN_EOF) break; + if (gt == SVG_TOKEN_CLOSE_TAG) { gdepth--; continue; } + if ((gt == SVG_TOKEN_OPEN_TAG || gt == SVG_TOKEN_SELF_CLOSE_TAG) && + parser.tag_name.eq ("stop")) + if (unlikely (!svg_parse_gradient_stop (parser, grad, + pfuncs, paint_data, + foreground, face, + palette))) + had_alloc_failure = true; + if (gt == SVG_TOKEN_OPEN_TAG && !parser.tag_name.eq ("stop")) + gdepth++; + } + if (had_alloc_failure || defs->gradients.in_error ()) + *id = {}; +} + +void +hb_raster_svg_process_gradient_def (hb_svg_defs_t *defs, + hb_svg_xml_parser_t &parser, + hb_svg_token_type_t tok, + hb_svg_gradient_type_t type, + hb_paint_funcs_t *pfuncs, + void *paint_data, + hb_color_t foreground, + hb_face_t *face, + unsigned palette) +{ + hb_svg_gradient_t grad; + grad.type = type; + svg_parse_gradient_geometry_attrs (parser, grad); + svg_parse_gradient_attrs (parser, grad); + + hb_svg_str_t id = parser.find_attr ("id"); + if (tok == SVG_TOKEN_OPEN_TAG) + svg_parse_gradient_children (defs, parser, grad, &id, + pfuncs, paint_data, + foreground, face, + palette); + + if (id.len) + (void) defs->add_gradient (hb_bytes_t (id.data, id.len), grad); +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-gradient.hh b/thirdparty/harfbuzz/src/hb-raster-svg-gradient.hh new file mode 100644 index 0000000000..0b1e6fd0c4 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-gradient.hh @@ -0,0 +1,46 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_GRADIENT_HH +#define HB_RASTER_SVG_GRADIENT_HH + +#include "hb.hh" + +#include "hb-raster-svg-color.hh" +#include "hb-raster-svg-defs.hh" + +HB_INTERNAL void +hb_raster_svg_process_gradient_def (hb_svg_defs_t *defs, + hb_svg_xml_parser_t &parser, + hb_svg_token_type_t tok, + hb_svg_gradient_type_t type, + hb_paint_funcs_t *pfuncs, + void *paint_data, + hb_color_t foreground, + hb_face_t *face, + unsigned palette); + +#endif /* HB_RASTER_SVG_GRADIENT_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-parse.cc b/thirdparty/harfbuzz/src/hb-raster-svg-parse.cc new file mode 100644 index 0000000000..0bdb47106a --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-parse.cc @@ -0,0 +1,668 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-parse.hh" + +#include +bool +hb_raster_svg_parse_transform (hb_svg_str_t s, hb_svg_transform_t *out) +{ + hb_svg_float_parser_t fp (s); + + while (fp.p < fp.end) + { + fp.skip_ws_comma (); + if (fp.p >= fp.end) break; + + const char *start = fp.p; + while (fp.p < fp.end && *fp.p != '(') fp.p++; + hb_svg_str_t func_name = {start, (unsigned) (fp.p - start)}; + + while (func_name.len && (func_name.data[func_name.len - 1] == ' ' || + func_name.data[func_name.len - 1] == '\t')) + func_name.len--; + + if (fp.p >= fp.end) break; + fp.p++; + + hb_svg_transform_t t; + + if (func_name.eq_ascii_ci ("matrix")) + { + t.xx = fp.next_float (); + t.yx = fp.next_float (); + t.xy = fp.next_float (); + t.yy = fp.next_float (); + t.dx = fp.next_float (); + t.dy = fp.next_float (); + } + else if (func_name.eq_ascii_ci ("translate")) + { + t.dx = fp.next_float (); + fp.skip_ws_comma (); + if (fp.p < fp.end && *fp.p != ')') + t.dy = fp.next_float (); + } + else if (func_name.eq_ascii_ci ("scale")) + { + t.xx = fp.next_float (); + fp.skip_ws_comma (); + if (fp.p < fp.end && *fp.p != ')') + t.yy = fp.next_float (); + else + t.yy = t.xx; + } + else if (func_name.eq_ascii_ci ("rotate")) + { + float angle = fp.next_float () * (float) M_PI / 180.f; + float cs = cosf (angle), sn = sinf (angle); + fp.skip_ws_comma (); + if (fp.p < fp.end && *fp.p != ')') + { + float cx = fp.next_float (); + float cy = fp.next_float (); + t.xx = cs; t.yx = sn; + t.xy = -sn; t.yy = cs; + t.dx = cx - cs * cx + sn * cy; + t.dy = cy - sn * cx - cs * cy; + } + else + { + t.xx = cs; t.yx = sn; + t.xy = -sn; t.yy = cs; + } + } + else if (func_name.eq_ascii_ci ("skewX")) + { + float angle = fp.next_float () * (float) M_PI / 180.f; + t.xy = tanf (angle); + } + else if (func_name.eq_ascii_ci ("skewY")) + { + float angle = fp.next_float () * (float) M_PI / 180.f; + t.yx = tanf (angle); + } + + while (fp.p < fp.end && *fp.p != ')') fp.p++; + if (fp.p < fp.end) fp.p++; + + out->multiply (t); + } + return true; +} + +static void +svg_arc_to_cubics (hb_draw_funcs_t *dfuncs, void *draw_data, hb_draw_state_t *st, + float cx, float cy, + float rx, float ry, + float phi, float theta1, float dtheta) +{ + int n_segs = (int) ceilf (fabsf (dtheta) / ((float) M_PI / 2.f)); + if (n_segs < 1) n_segs = 1; + float seg_angle = dtheta / n_segs; + + float cos_phi = cosf (phi); + float sin_phi = sinf (phi); + + for (int i = 0; i < n_segs; i++) + { + float t1 = theta1 + i * seg_angle; + float t2 = t1 + seg_angle; + float alpha = sinf (seg_angle) * + (sqrtf (4.f + 3.f * tanf (seg_angle / 2.f) * tanf (seg_angle / 2.f)) - 1.f) / 3.f; + + float cos_t1 = cosf (t1), sin_t1 = sinf (t1); + float cos_t2 = cosf (t2), sin_t2 = sinf (t2); + + float e1x = rx * cos_t1, e1y = ry * sin_t1; + float e2x = rx * cos_t2, e2y = ry * sin_t2; + float d1x = -rx * sin_t1, d1y = ry * cos_t1; + float d2x = -rx * sin_t2, d2y = ry * cos_t2; + + float cp1x = e1x + alpha * d1x; + float cp1y = e1y + alpha * d1y; + float cp2x = e2x - alpha * d2x; + float cp2y = e2y - alpha * d2y; + + float r_cp1x = cos_phi * cp1x - sin_phi * cp1y + cx; + float r_cp1y = sin_phi * cp1x + cos_phi * cp1y + cy; + float r_cp2x = cos_phi * cp2x - sin_phi * cp2y + cx; + float r_cp2y = sin_phi * cp2x + cos_phi * cp2y + cy; + float r_e2x = cos_phi * e2x - sin_phi * e2y + cx; + float r_e2y = sin_phi * e2x + cos_phi * e2y + cy; + + hb_draw_cubic_to (dfuncs, draw_data, st, + r_cp1x, r_cp1y, + r_cp2x, r_cp2y, + r_e2x, r_e2y); + } +} + +static void +svg_arc_endpoint_to_center (float x1, float y1, float x2, float y2, + float rx, float ry, float phi_deg, + bool large_arc, bool sweep, + float *cx_out, float *cy_out, + float *theta1_out, float *dtheta_out, + float *rx_out, float *ry_out) +{ + float phi = phi_deg * (float) M_PI / 180.f; + float cos_phi = cosf (phi), sin_phi = sinf (phi); + + float mx = (x1 - x2) / 2.f; + float my = (y1 - y2) / 2.f; + float x1p = cos_phi * mx + sin_phi * my; + float y1p = -sin_phi * mx + cos_phi * my; + + rx = fabsf (rx); ry = fabsf (ry); + if (rx < 1e-10f || ry < 1e-10f) + { + *cx_out = (x1 + x2) / 2.f; + *cy_out = (y1 + y2) / 2.f; + *theta1_out = 0; + *dtheta_out = 0; + *rx_out = rx; *ry_out = ry; + return; + } + + float x1p2 = x1p * x1p, y1p2 = y1p * y1p; + float rx2 = rx * rx, ry2 = ry * ry; + float lambda = x1p2 / rx2 + y1p2 / ry2; + if (lambda > 1.f) + { + float sl = sqrtf (lambda); + rx *= sl; ry *= sl; + rx2 = rx * rx; ry2 = ry * ry; + } + + float num = rx2 * ry2 - rx2 * y1p2 - ry2 * x1p2; + float den = rx2 * y1p2 + ry2 * x1p2; + float sq = (den > 0.f) ? sqrtf (hb_max (num / den, 0.f)) : 0.f; + if (large_arc == sweep) sq = -sq; + + float cxp = sq * rx * y1p / ry; + float cyp = -sq * ry * x1p / rx; + + float cx = cos_phi * cxp - sin_phi * cyp + (x1 + x2) / 2.f; + float cy = sin_phi * cxp + cos_phi * cyp + (y1 + y2) / 2.f; + + auto angle = [] (float ux, float uy, float vx, float vy) -> float { + float dot = ux * vx + uy * vy; + float len = sqrtf ((ux * ux + uy * uy) * (vx * vx + vy * vy)); + if (!(len > 0.f) || !std::isfinite (len)) + return 0.f; + float a = acosf (hb_clamp (dot / len, -1.f, 1.f)); + if (ux * vy - uy * vx < 0.f) a = -a; + return a; + }; + + float theta1 = angle (1.f, 0.f, (x1p - cxp) / rx, (y1p - cyp) / ry); + float dtheta = angle ((x1p - cxp) / rx, (y1p - cyp) / ry, + (-x1p - cxp) / rx, (-y1p - cyp) / ry); + + if (!sweep && dtheta > 0.f) dtheta -= 2.f * (float) M_PI; + if (sweep && dtheta < 0.f) dtheta += 2.f * (float) M_PI; + + *cx_out = cx; *cy_out = cy; + *theta1_out = theta1; *dtheta_out = dtheta; + *rx_out = rx; *ry_out = ry; +} + +void +hb_raster_svg_parse_path_data (hb_svg_str_t d, hb_draw_funcs_t *dfuncs, void *draw_data) +{ + hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + hb_svg_float_parser_t fp (d); + + float cur_x = 0, cur_y = 0; + float start_x = 0, start_y = 0; + float last_cx = 0, last_cy = 0; + char last_cmd = 0; + char cmd = 0; + unsigned segments = 0; + + while (fp.p < fp.end) + { + if (unlikely (segments++ >= HB_SVG_MAX_PATH_SEGMENTS)) + break; + fp.skip_ws_comma (); + if (fp.p >= fp.end) break; + + char c = *fp.p; + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) + { + cmd = c; + fp.p++; + } + + switch (cmd) + { + case 'M': case 'm': + { + float x = fp.next_float (); + float y = fp.next_float (); + if (cmd == 'm') { x += cur_x; y += cur_y; } + hb_draw_move_to (dfuncs, draw_data, &st, x, y); + cur_x = start_x = x; + cur_y = start_y = y; + cmd = (cmd == 'M') ? 'L' : 'l'; + last_cmd = 'M'; + continue; + } + case 'L': case 'l': + { + float x = fp.next_float (); + float y = fp.next_float (); + if (cmd == 'l') { x += cur_x; y += cur_y; } + hb_draw_line_to (dfuncs, draw_data, &st, x, y); + cur_x = x; cur_y = y; + break; + } + case 'H': case 'h': + { + float x = fp.next_float (); + if (cmd == 'h') x += cur_x; + hb_draw_line_to (dfuncs, draw_data, &st, x, cur_y); + cur_x = x; + break; + } + case 'V': case 'v': + { + float y = fp.next_float (); + if (cmd == 'v') y += cur_y; + hb_draw_line_to (dfuncs, draw_data, &st, cur_x, y); + cur_y = y; + break; + } + case 'C': case 'c': + { + float x1 = fp.next_float (); + float y1 = fp.next_float (); + float x2 = fp.next_float (); + float y2 = fp.next_float (); + float x = fp.next_float (); + float y = fp.next_float (); + if (cmd == 'c') + { x1 += cur_x; y1 += cur_y; x2 += cur_x; y2 += cur_y; x += cur_x; y += cur_y; } + hb_draw_cubic_to (dfuncs, draw_data, &st, x1, y1, x2, y2, x, y); + last_cx = x2; last_cy = y2; + cur_x = x; cur_y = y; + break; + } + case 'S': case 's': + { + float cx1, cy1; + if (last_cmd == 'C' || last_cmd == 'c' || last_cmd == 'S' || last_cmd == 's') + { cx1 = 2 * cur_x - last_cx; cy1 = 2 * cur_y - last_cy; } + else + { cx1 = cur_x; cy1 = cur_y; } + float x2 = fp.next_float (); + float y2 = fp.next_float (); + float x = fp.next_float (); + float y = fp.next_float (); + if (cmd == 's') + { x2 += cur_x; y2 += cur_y; x += cur_x; y += cur_y; } + hb_draw_cubic_to (dfuncs, draw_data, &st, cx1, cy1, x2, y2, x, y); + last_cx = x2; last_cy = y2; + cur_x = x; cur_y = y; + break; + } + case 'Q': case 'q': + { + float x1 = fp.next_float (); + float y1 = fp.next_float (); + float x = fp.next_float (); + float y = fp.next_float (); + if (cmd == 'q') + { x1 += cur_x; y1 += cur_y; x += cur_x; y += cur_y; } + hb_draw_quadratic_to (dfuncs, draw_data, &st, x1, y1, x, y); + last_cx = x1; last_cy = y1; + cur_x = x; cur_y = y; + break; + } + case 'T': case 't': + { + float cx1, cy1; + if (last_cmd == 'Q' || last_cmd == 'q' || last_cmd == 'T' || last_cmd == 't') + { cx1 = 2 * cur_x - last_cx; cy1 = 2 * cur_y - last_cy; } + else + { cx1 = cur_x; cy1 = cur_y; } + float x = fp.next_float (); + float y = fp.next_float (); + if (cmd == 't') { x += cur_x; y += cur_y; } + hb_draw_quadratic_to (dfuncs, draw_data, &st, cx1, cy1, x, y); + last_cx = cx1; last_cy = cy1; + cur_x = x; cur_y = y; + break; + } + case 'A': case 'a': + { + float rx = fp.next_float (); + float ry = fp.next_float (); + float x_rot = fp.next_float (); + bool large_arc = fp.next_flag (); + bool sweep = fp.next_flag (); + float x = fp.next_float (); + float y = fp.next_float (); + if (cmd == 'a') { x += cur_x; y += cur_y; } + + if (fabsf (x - cur_x) < 1e-6f && fabsf (y - cur_y) < 1e-6f) + { + cur_x = x; cur_y = y; + break; + } + + float cx, cy, theta1, dtheta, adj_rx, adj_ry; + svg_arc_endpoint_to_center (cur_x, cur_y, x, y, + rx, ry, x_rot, + large_arc, sweep, + &cx, &cy, &theta1, &dtheta, + &adj_rx, &adj_ry); + + float phi = x_rot * (float) M_PI / 180.f; + svg_arc_to_cubics (dfuncs, draw_data, &st, + cx, cy, adj_rx, adj_ry, phi, theta1, dtheta); + cur_x = x; cur_y = y; + break; + } + case 'Z': case 'z': + hb_draw_close_path (dfuncs, draw_data, &st); + cur_x = start_x; + cur_y = start_y; + break; + + default: + fp.p++; + continue; + } + + last_cmd = cmd; + } +} + +static void +svg_rect_to_path (float x, float y, float w, float h, float rx, float ry, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + if (rx < 0.f || ry < 0.f) + return; + + if (rx <= 0 && ry <= 0) + { + hb_draw_move_to (dfuncs, draw_data, &st, x, y); + hb_draw_line_to (dfuncs, draw_data, &st, x + w, y); + hb_draw_line_to (dfuncs, draw_data, &st, x + w, y + h); + hb_draw_line_to (dfuncs, draw_data, &st, x, y + h); + hb_draw_close_path (dfuncs, draw_data, &st); + return; + } + + if (rx <= 0) rx = ry; + if (ry <= 0) ry = rx; + rx = hb_min (rx, w / 2); + ry = hb_min (ry, h / 2); + + float kx = rx * 0.5522847498f; + float ky = ry * 0.5522847498f; + + hb_draw_move_to (dfuncs, draw_data, &st, x + rx, y); + hb_draw_line_to (dfuncs, draw_data, &st, x + w - rx, y); + hb_draw_cubic_to (dfuncs, draw_data, &st, + x + w - rx + kx, y, + x + w, y + ry - ky, + x + w, y + ry); + hb_draw_line_to (dfuncs, draw_data, &st, x + w, y + h - ry); + hb_draw_cubic_to (dfuncs, draw_data, &st, + x + w, y + h - ry + ky, + x + w - rx + kx, y + h, + x + w - rx, y + h); + hb_draw_line_to (dfuncs, draw_data, &st, x + rx, y + h); + hb_draw_cubic_to (dfuncs, draw_data, &st, + x + rx - kx, y + h, + x, y + h - ry + ky, + x, y + h - ry); + hb_draw_line_to (dfuncs, draw_data, &st, x, y + ry); + hb_draw_cubic_to (dfuncs, draw_data, &st, + x, y + ry - ky, + x + rx - kx, y, + x + rx, y); + hb_draw_close_path (dfuncs, draw_data, &st); +} + +static void +svg_circle_to_path (float cx, float cy, float r, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + float k = r * 0.5522847498f; + + hb_draw_move_to (dfuncs, draw_data, &st, cx + r, cy); + hb_draw_cubic_to (dfuncs, draw_data, &st, + cx + r, cy + k, + cx + k, cy + r, + cx, cy + r); + hb_draw_cubic_to (dfuncs, draw_data, &st, + cx - k, cy + r, + cx - r, cy + k, + cx - r, cy); + hb_draw_cubic_to (dfuncs, draw_data, &st, + cx - r, cy - k, + cx - k, cy - r, + cx, cy - r); + hb_draw_cubic_to (dfuncs, draw_data, &st, + cx + k, cy - r, + cx + r, cy - k, + cx + r, cy); + hb_draw_close_path (dfuncs, draw_data, &st); +} + +static void +svg_ellipse_to_path (float cx, float cy, float rx, float ry, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + float kx = rx * 0.5522847498f; + float ky = ry * 0.5522847498f; + + hb_draw_move_to (dfuncs, draw_data, &st, cx + rx, cy); + hb_draw_cubic_to (dfuncs, draw_data, &st, + cx + rx, cy + ky, + cx + kx, cy + ry, + cx, cy + ry); + hb_draw_cubic_to (dfuncs, draw_data, &st, + cx - kx, cy + ry, + cx - rx, cy + ky, + cx - rx, cy); + hb_draw_cubic_to (dfuncs, draw_data, &st, + cx - rx, cy - ky, + cx - kx, cy - ry, + cx, cy - ry); + hb_draw_cubic_to (dfuncs, draw_data, &st, + cx + kx, cy - ry, + cx + rx, cy - ky, + cx + rx, cy); + hb_draw_close_path (dfuncs, draw_data, &st); +} + +static void +svg_line_to_path (float x1, float y1, float x2, float y2, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + hb_draw_move_to (dfuncs, draw_data, &st, x1, y1); + hb_draw_line_to (dfuncs, draw_data, &st, x2, y2); +} + +static void +svg_polygon_to_path (hb_svg_str_t points, bool close, + hb_draw_funcs_t *dfuncs, void *draw_data) +{ + hb_draw_state_t st = HB_DRAW_STATE_DEFAULT; + hb_svg_float_parser_t fp (points); + bool first = true; + while (fp.has_more ()) + { + float x = fp.next_float (); + float y = fp.next_float (); + if (first) + { + hb_draw_move_to (dfuncs, draw_data, &st, x, y); + first = false; + } + else + hb_draw_line_to (dfuncs, draw_data, &st, x, y); + } + if (close && !first) + hb_draw_close_path (dfuncs, draw_data, &st); +} + +void +hb_raster_svg_shape_path_emit (hb_draw_funcs_t *dfuncs, void *draw_data, void *user_data) +{ + hb_svg_shape_emit_data_t *shape = (hb_svg_shape_emit_data_t *) user_data; + switch (shape->type) + { + case hb_svg_shape_emit_data_t::SHAPE_PATH: + hb_raster_svg_parse_path_data (shape->str_data, dfuncs, draw_data); + break; + case hb_svg_shape_emit_data_t::SHAPE_RECT: + svg_rect_to_path (shape->params[0], shape->params[1], + shape->params[2], shape->params[3], + shape->params[4], shape->params[5], + dfuncs, draw_data); + break; + case hb_svg_shape_emit_data_t::SHAPE_CIRCLE: + svg_circle_to_path (shape->params[0], shape->params[1], shape->params[2], + dfuncs, draw_data); + break; + case hb_svg_shape_emit_data_t::SHAPE_ELLIPSE: + svg_ellipse_to_path (shape->params[0], shape->params[1], + shape->params[2], shape->params[3], + dfuncs, draw_data); + break; + case hb_svg_shape_emit_data_t::SHAPE_LINE: + svg_line_to_path (shape->params[0], shape->params[1], + shape->params[2], shape->params[3], + dfuncs, draw_data); + break; + case hb_svg_shape_emit_data_t::SHAPE_POLYLINE: + svg_polygon_to_path (shape->str_data, false, dfuncs, draw_data); + break; + case hb_svg_shape_emit_data_t::SHAPE_POLYGON: + svg_polygon_to_path (shape->str_data, true, dfuncs, draw_data); + break; + } +} + +bool +hb_raster_svg_parse_shape_tag (hb_svg_xml_parser_t &parser, + hb_svg_shape_emit_data_t *shape) +{ + hb_svg_attr_view_t attrs (parser); + hb_svg_style_props_t style_props; + svg_parse_style_props (attrs.get ("style"), &style_props); + hb_svg_str_t tag = parser.tag_name; + if (tag.eq ("path")) + { + hb_svg_str_t d = svg_pick_attr_or_style (parser, style_props.d, "d"); + if (!d.len) return false; + shape->type = hb_svg_shape_emit_data_t::SHAPE_PATH; + shape->str_data = d; + return true; + } + if (tag.eq ("rect")) + { + float w = svg_parse_float (svg_pick_attr_or_style (parser, style_props.width, "width")); + float h = svg_parse_float (svg_pick_attr_or_style (parser, style_props.height, "height")); + if (w <= 0 || h <= 0) return false; + float rx = svg_parse_float (svg_pick_attr_or_style (parser, style_props.rx, "rx")); + float ry = svg_parse_float (svg_pick_attr_or_style (parser, style_props.ry, "ry")); + if (rx < 0.f || ry < 0.f) return false; + shape->type = hb_svg_shape_emit_data_t::SHAPE_RECT; + shape->params[0] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.x, "x")); + shape->params[1] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.y, "y")); + shape->params[2] = w; + shape->params[3] = h; + shape->params[4] = rx; + shape->params[5] = ry; + return true; + } + if (tag.eq ("circle")) + { + float r = svg_parse_float (svg_pick_attr_or_style (parser, style_props.r, "r")); + if (r <= 0) return false; + shape->type = hb_svg_shape_emit_data_t::SHAPE_CIRCLE; + shape->params[0] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.cx, "cx")); + shape->params[1] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.cy, "cy")); + shape->params[2] = r; + return true; + } + if (tag.eq ("ellipse")) + { + float rx = svg_parse_float (svg_pick_attr_or_style (parser, style_props.rx, "rx")); + float ry = svg_parse_float (svg_pick_attr_or_style (parser, style_props.ry, "ry")); + if (rx <= 0 || ry <= 0) return false; + shape->type = hb_svg_shape_emit_data_t::SHAPE_ELLIPSE; + shape->params[0] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.cx, "cx")); + shape->params[1] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.cy, "cy")); + shape->params[2] = rx; + shape->params[3] = ry; + return true; + } + if (tag.eq ("line")) + { + shape->type = hb_svg_shape_emit_data_t::SHAPE_LINE; + shape->params[0] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.x1, "x1")); + shape->params[1] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.y1, "y1")); + shape->params[2] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.x2, "x2")); + shape->params[3] = svg_parse_float (svg_pick_attr_or_style (parser, style_props.y2, "y2")); + return true; + } + if (tag.eq ("polyline")) + { + hb_svg_str_t points = svg_pick_attr_or_style (parser, style_props.points, "points"); + if (!points.len) return false; + shape->type = hb_svg_shape_emit_data_t::SHAPE_POLYLINE; + shape->str_data = points; + return true; + } + if (tag.eq ("polygon")) + { + hb_svg_str_t points = svg_pick_attr_or_style (parser, style_props.points, "points"); + if (!points.len) return false; + shape->type = hb_svg_shape_emit_data_t::SHAPE_POLYGON; + shape->str_data = points; + return true; + } + return false; +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-parse.hh b/thirdparty/harfbuzz/src/hb-raster-svg-parse.hh new file mode 100644 index 0000000000..21c782c18a --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-parse.hh @@ -0,0 +1,368 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_PARSE_HH +#define HB_RASTER_SVG_PARSE_HH + +#include "hb.hh" + +#include "hb-raster-svg-base.hh" +#include "hb-draw.h" + +#include +#include + +enum hb_svg_token_type_t +{ + SVG_TOKEN_OPEN_TAG, + SVG_TOKEN_CLOSE_TAG, + SVG_TOKEN_SELF_CLOSE_TAG, + SVG_TOKEN_TEXT, + SVG_TOKEN_EOF +}; + +struct hb_svg_attr_t +{ + hb_svg_str_t name; + hb_svg_str_t value; +}; + +struct hb_svg_xml_parser_t +{ + enum { SVG_MAX_ATTRS_PER_TAG = 256 }; + + const char *p; + const char *end; + const char *tag_start = nullptr; + + hb_svg_str_t tag_name; + hb_vector_t attrs; + bool self_closing; + + hb_svg_xml_parser_t (const char *data, unsigned len) + : p (data), end (data + len), self_closing (false) {} + + void skip_ws () + { + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) + p++; + } + + hb_svg_str_t read_name () + { + const char *start = p; + while (p < end && *p != ' ' && *p != '\t' && *p != '\n' && *p != '\r' && + *p != '>' && *p != '/' && *p != '=') + p++; + return {start, (unsigned) (p - start)}; + } + + hb_svg_str_t read_attr_value () + { + if (p >= end) return {}; + char quote = *p; + if (quote != '"' && quote != '\'') return {}; + p++; + const char *start = p; + while (p < end && *p != quote) + p++; + hb_svg_str_t val = {start, (unsigned) (p - start)}; + if (p < end) p++; /* skip closing quote */ + return val; + } + + void parse_attrs () + { + attrs.clear (); + while (p < end) + { + skip_ws (); + if (p >= end || *p == '>' || *p == '/') break; + hb_svg_str_t name = read_name (); + if (!name.len) { p++; continue; } + skip_ws (); + if (p < end && *p == '=') + { + p++; + skip_ws (); + hb_svg_str_t val = read_attr_value (); + if (attrs.length < SVG_MAX_ATTRS_PER_TAG) + { + hb_svg_attr_t attr = {name, val}; + attrs.push (attr); + } + } + else + { + if (attrs.length < SVG_MAX_ATTRS_PER_TAG) + { + hb_svg_attr_t attr = {name, {}}; + attrs.push (attr); + } + } + } + } + + hb_svg_token_type_t next () + { + while (p < end) + { + if (*p != '<') + { + while (p < end && *p != '<') p++; + continue; + } + + tag_start = p; + p++; /* skip '<' */ + if (p >= end) return SVG_TOKEN_EOF; + + if (*p == '!') + { + if (p + 2 < end && p[1] == '-' && p[2] == '-') + { + p += 3; + while (p + 2 < end && !(p[0] == '-' && p[1] == '-' && p[2] == '>')) + p++; + if (p + 2 < end) p += 3; + } + else + { + while (p < end && *p != '>') p++; + if (p < end) p++; + } + continue; + } + if (*p == '?') + { + while (p + 1 < end && !(p[0] == '?' && p[1] == '>')) + p++; + if (p + 1 < end) p += 2; + continue; + } + + if (*p == '/') + { + p++; + tag_name = read_name (); + while (p < end && *p != '>') p++; + if (p < end) p++; + attrs.clear (); + self_closing = false; + return SVG_TOKEN_CLOSE_TAG; + } + + tag_name = read_name (); + parse_attrs (); + + self_closing = false; + skip_ws (); + if (p < end && *p == '/') + { + self_closing = true; + p++; + } + if (p < end && *p == '>') + p++; + + return self_closing ? SVG_TOKEN_SELF_CLOSE_TAG : SVG_TOKEN_OPEN_TAG; + } + return SVG_TOKEN_EOF; + } + + hb_svg_str_t find_attr (const char *name) const + { + for (unsigned i = 0; i < attrs.length; i++) + if (attrs[i].name.eq (name)) + return attrs[i].value; + return {}; + } +}; + +static inline void +svg_skip_subtree (hb_svg_xml_parser_t &parser) +{ + int depth = 1; + while (depth > 0) + { + hb_svg_token_type_t tok = parser.next (); + if (tok == SVG_TOKEN_EOF) break; + if (tok == SVG_TOKEN_CLOSE_TAG) depth--; + else if (tok == SVG_TOKEN_OPEN_TAG) depth++; + } +} + +static inline hb_svg_str_t +svg_pick_attr_or_style (const hb_svg_xml_parser_t &parser, + hb_svg_str_t style_value, + const char *attr_name) +{ + return style_value.is_null () ? parser.find_attr (attr_name) : style_value; +} + +struct hb_svg_attr_view_t +{ + const hb_svg_xml_parser_t &parser; + enum { CACHE_SIZE = 16 }; + struct entry_t + { + const char *name = nullptr; + hb_svg_str_t value; + }; + entry_t cache[CACHE_SIZE]; + unsigned cache_len = 0; + + hb_svg_attr_view_t (const hb_svg_xml_parser_t &p) : parser (p) {} + + hb_svg_str_t get (const char *name) + { + for (unsigned i = 0; i < cache_len; i++) + if (strcmp (cache[i].name, name) == 0) + return cache[i].value; + + hb_svg_str_t value = parser.find_attr (name); + if (cache_len < CACHE_SIZE) + { + cache[cache_len].name = name; + cache[cache_len].value = value; + cache_len++; + } + return value; + } +}; + +struct hb_svg_transform_t +{ + float xx = 1, yx = 0, xy = 0, yy = 1, dx = 0, dy = 0; + + void multiply (const hb_svg_transform_t &other) + { + float nxx = xx * other.xx + xy * other.yx; + float nyx = yx * other.xx + yy * other.yx; + float nxy = xx * other.xy + xy * other.yy; + float nyy = yx * other.xy + yy * other.yy; + float ndx = xx * other.dx + xy * other.dy + dx; + float ndy = yx * other.dx + yy * other.dy + dy; + xx = nxx; yx = nyx; xy = nxy; yy = nyy; dx = ndx; dy = ndy; + } +}; + +struct hb_svg_float_parser_t +{ + const char *p; + const char *end; + + hb_svg_float_parser_t (hb_svg_str_t s) : p (s.data), end (s.data + s.len) {} + + void skip_ws_comma () + { + while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == ',')) + p++; + } + + bool has_more () const { return p < end; } + + float next_float () + { + skip_ws_comma (); + if (p >= end) return 0.f; + const char *start = p; + char buf[64]; + unsigned n = 0; + bool has_digit = false; + if (p < end && (*p == '-' || *p == '+')) + buf[n++] = *p++; + while (p < end && n < sizeof (buf) - 1 && *p >= '0' && *p <= '9') + { + buf[n++] = *p++; + has_digit = true; + } + if (p < end && n < sizeof (buf) - 1 && *p == '.') + { + buf[n++] = *p++; + while (p < end && n < sizeof (buf) - 1 && *p >= '0' && *p <= '9') + { + buf[n++] = *p++; + has_digit = true; + } + } + if (p < end && n < sizeof (buf) - 1 && (*p == 'e' || *p == 'E')) + { + buf[n++] = *p++; + if (p < end && n < sizeof (buf) - 1 && (*p == '+' || *p == '-')) + buf[n++] = *p++; + bool has_exp_digit = false; + while (p < end && n < sizeof (buf) - 1 && *p >= '0' && *p <= '9') + { + buf[n++] = *p++; + has_exp_digit = true; + } + if (!has_exp_digit) + { + p = start; + has_digit = false; + } + } + if (!has_digit) + { + if (p < end) p++; + return 0.f; + } + buf[n] = '\0'; + float v = strtof (buf, nullptr); + return std::isfinite (v) ? v : 0.f; + } + + bool next_flag () + { + skip_ws_comma (); + if (p >= end) return false; + bool v = *p != '0'; + p++; + return v; + } +}; + +static inline float +svg_parse_float (hb_svg_str_t s) +{ + return s.to_float (); +} + +struct hb_svg_shape_emit_data_t +{ + enum { SHAPE_PATH, SHAPE_RECT, SHAPE_CIRCLE, SHAPE_ELLIPSE, + SHAPE_LINE, SHAPE_POLYLINE, SHAPE_POLYGON } type; + hb_svg_str_t str_data; + float params[6]; +}; + +HB_INTERNAL bool hb_raster_svg_parse_transform (hb_svg_str_t s, hb_svg_transform_t *out); +HB_INTERNAL void hb_raster_svg_parse_path_data (hb_svg_str_t d, hb_draw_funcs_t *dfuncs, void *draw_data); +HB_INTERNAL void hb_raster_svg_shape_path_emit (hb_draw_funcs_t *dfuncs, void *draw_data, void *user_data); +HB_INTERNAL bool hb_raster_svg_parse_shape_tag (hb_svg_xml_parser_t &parser, hb_svg_shape_emit_data_t *shape); + +#endif /* HB_RASTER_SVG_PARSE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-render.cc b/thirdparty/harfbuzz/src/hb-raster-svg-render.cc new file mode 100644 index 0000000000..d07ce040c4 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-render.cc @@ -0,0 +1,548 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-face.hh" +#include "hb-raster-svg.hh" +#include "hb-raster-svg-base.hh" +#include "hb-raster-svg-parse.hh" +#include "hb-raster-svg-context.hh" +#include "hb-raster-svg-defs-scan.hh" +#include "hb-raster-svg-gradient.hh" +#include "hb-raster-svg-clip.hh" +#include "hb-raster-svg-bbox.hh" +#include "hb-raster-svg-fill.hh" +#include "hb-raster-svg-use.hh" +#include "OT/Color/svg/svg.hh" + +#include +#include + +#ifndef HB_NO_RASTER_SVG + +#define SVG_MAX_DEPTH 32 + +/* + * 11. Element renderer — recursive SVG rendering + */ + +static void svg_render_element (hb_svg_render_context_t *ctx, + hb_svg_xml_parser_t &parser, + const struct hb_svg_cascade_t &inherited); + +/* Gradient def parsing lives in hb-raster-svg-gradient.* */ + +/* Clip-path defs and push helpers live in hb-raster-svg-clip.* */ + +/* Render a single shape element */ +static void +svg_render_shape (hb_svg_render_context_t *ctx, + hb_svg_shape_emit_data_t &shape, + const hb_svg_cascade_t &state, + hb_svg_str_t transform_str) +{ + bool has_transform = transform_str.len > 0; + bool has_opacity = state.opacity < 1.f; + bool has_clip_path = false; + + if (has_opacity) + ctx->push_group (); + + if (has_transform) + { + hb_svg_transform_t t; + hb_raster_svg_parse_transform (transform_str, &t); + ctx->push_transform (t.xx, t.yx, t.xy, t.yy, t.dx, t.dy); + } + + hb_extents_t<> bbox; + bool has_bbox = hb_raster_svg_compute_shape_bbox (shape, &bbox); + has_clip_path = hb_raster_svg_push_clip_path_ref (ctx->paint, &ctx->defs, state.clip_path, + has_bbox ? &bbox : nullptr); + + /* Clip with shape path, then fill */ + hb_raster_paint_push_clip_path (ctx->paint, hb_raster_svg_shape_path_emit, &shape); + + /* Default fill is black */ + if (state.fill.is_null ()) + { + hb_color_t black = HB_COLOR (0, 0, 0, 255); + if (state.fill_opacity < 1.f) + black = HB_COLOR (0, 0, 0, (uint8_t) (255 * state.fill_opacity + 0.5f)); + ctx->paint_color (black); + } + else + { + hb_svg_fill_context_t fill_ctx = {ctx->paint, ctx->pfuncs, ctx->font, ctx->palette, &ctx->defs}; + hb_raster_svg_emit_fill (&fill_ctx, state.fill, state.fill_opacity, has_bbox ? &bbox : nullptr, state.color); + } + + ctx->pop_clip (); + if (has_clip_path) + ctx->pop_clip (); + + if (has_transform) + ctx->pop_transform (); + + if (has_opacity) + ctx->pop_group (HB_PAINT_COMPOSITE_MODE_SRC_OVER); +} + +static void +svg_render_container_element (hb_svg_render_context_t *ctx, + hb_svg_xml_parser_t &parser, + hb_svg_str_t tag, + bool self_closing, + const hb_svg_cascade_t &state, + hb_svg_str_t transform_str, + hb_svg_str_t clip_path_str) +{ + bool has_transform = transform_str.len > 0; + bool has_opacity = state.opacity < 1.f; + bool has_clip = false; + bool has_viewbox = false; + bool has_viewbox_transform = false; + bool has_svg_translate = false; + float svg_x = 0.f, svg_y = 0.f; + float viewport_w = 0.f, viewport_h = 0.f; + hb_svg_transform_t viewbox_t; + float vb_x = 0, vb_y = 0, vb_w = 0, vb_h = 0; + + if (tag.eq ("svg") || tag.eq ("symbol")) + { + hb_svg_style_props_t geom_style_props; + svg_parse_style_props (parser.find_attr ("style"), &geom_style_props); + + if (tag.eq ("svg")) + { + svg_x = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.x, "x")); + svg_y = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.y, "y")); + has_svg_translate = (svg_x != 0.f || svg_y != 0.f); + } + + hb_svg_str_t viewbox_str = parser.find_attr ("viewBox"); + if (hb_raster_svg_parse_viewbox (viewbox_str, &vb_x, &vb_y, &vb_w, &vb_h)) + { + has_viewbox = true; + + if (tag.eq ("svg")) + { + viewport_w = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.width, "width")); + viewport_h = hb_raster_svg_parse_non_percent_length (svg_pick_attr_or_style (parser, geom_style_props.height, "height")); + if (!(viewport_w > 0.f && viewport_h > 0.f)) + { + if (ctx->depth == 1) + { + unsigned upem = hb_font_get_face (ctx->font)->get_upem (); + viewport_w = (float) upem; + viewport_h = (float) upem; + } + else + { + viewport_w = vb_w; + viewport_h = vb_h; + } + } + has_viewbox_transform = + hb_raster_svg_compute_viewbox_transform (viewport_w, viewport_h, + vb_x, vb_y, vb_w, vb_h, + parser.find_attr ("preserveAspectRatio"), + &viewbox_t); + } + } + } + + if (ctx->suppress_viewbox_once) + { + has_viewbox = false; + has_viewbox_transform = false; + ctx->suppress_viewbox_once = false; + } + + if (has_opacity) + ctx->push_group (); + + if (has_transform) + { + hb_svg_transform_t t; + hb_raster_svg_parse_transform (transform_str, &t); + ctx->push_transform (t.xx, t.yx, t.xy, t.yy, t.dx, t.dy); + } + + if (has_svg_translate) + ctx->push_transform (1, 0, 0, 1, svg_x, svg_y); + + if (has_viewbox_transform) + ctx->push_transform (viewbox_t.xx, viewbox_t.yx, viewbox_t.xy, viewbox_t.yy, + viewbox_t.dx, viewbox_t.dy); + else if (has_viewbox && vb_w > 0 && vb_h > 0) + ctx->push_transform (1, 0, 0, 1, -vb_x, -vb_y); + + has_clip = hb_raster_svg_push_clip_path_ref (ctx->paint, &ctx->defs, clip_path_str, nullptr); + + if (!self_closing) + { + int depth = 1; + while (depth > 0) + { + hb_svg_token_type_t tok = parser.next (); + if (tok == SVG_TOKEN_EOF) break; + + if (tok == SVG_TOKEN_CLOSE_TAG) + { + depth--; + continue; + } + + if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG) + { + hb_svg_str_t child_tag = parser.tag_name; + if (parser.tag_name.eq ("defs")) + { + if (tok != SVG_TOKEN_SELF_CLOSE_TAG) + { + hb_svg_defs_scan_context_t scan_ctx = { + &ctx->defs, ctx->pfuncs, ctx->paint, + ctx->foreground, hb_font_get_face (ctx->font), + ctx->palette, + ctx->doc_start, ctx->doc_len, + ctx->svg_accel, ctx->doc_cache + }; + hb_raster_svg_process_defs_element (&scan_ctx, parser); + } + continue; + } + svg_render_element (ctx, parser, state); + if (tok == SVG_TOKEN_OPEN_TAG && + !hb_raster_svg_tag_is_container_or_use (child_tag)) + { + /* Skip children of non-container elements we don't handle. */ + int skip_depth = 1; + while (skip_depth > 0) + { + hb_svg_token_type_t st = parser.next (); + if (st == SVG_TOKEN_EOF) break; + if (st == SVG_TOKEN_CLOSE_TAG) skip_depth--; + else if (st == SVG_TOKEN_OPEN_TAG) skip_depth++; + } + } + } + } + } + + if (has_viewbox_transform || (has_viewbox && vb_w > 0 && vb_h > 0)) + ctx->pop_transform (); + + if (has_svg_translate) + ctx->pop_transform (); + + if (has_clip) + ctx->pop_clip (); + + if (has_transform) + ctx->pop_transform (); + + if (has_opacity) + ctx->pop_group (HB_PAINT_COMPOSITE_MODE_SRC_OVER); +} + +static bool +svg_render_primitive_shape_element (hb_svg_render_context_t *ctx, + hb_svg_xml_parser_t &parser, + const hb_svg_cascade_t &state, + hb_svg_str_t transform_str) +{ + hb_svg_str_t tag = parser.tag_name; + if (!(tag.eq ("path") || tag.eq ("rect") || tag.eq ("circle") || + tag.eq ("ellipse") || tag.eq ("line") || tag.eq ("polyline") || + tag.eq ("polygon"))) + return false; + + hb_svg_shape_emit_data_t shape; + if (hb_raster_svg_parse_shape_tag (parser, &shape)) + svg_render_shape (ctx, shape, state, transform_str); + return true; +} + +static void +svg_render_use_callback (void *render_user, + hb_svg_xml_parser_t &parser, + const void *state, + bool viewport_mapped) +{ + hb_svg_render_context_t *ctx = (hb_svg_render_context_t *) render_user; + bool old_suppress = ctx->suppress_viewbox_once; + bool old_allow_symbol = ctx->allow_symbol_render_once; + if (viewport_mapped) + ctx->suppress_viewbox_once = true; + if (parser.tag_name.eq ("symbol")) + ctx->allow_symbol_render_once = true; + svg_render_element (ctx, parser, *(const hb_svg_cascade_t *) state); + ctx->suppress_viewbox_once = old_suppress; + ctx->allow_symbol_render_once = old_allow_symbol; +} + +/* Render one element (may be a container or shape) */ +static void +svg_render_element (hb_svg_render_context_t *ctx, + hb_svg_xml_parser_t &parser, + const hb_svg_cascade_t &inherited) +{ + if (ctx->depth >= SVG_MAX_DEPTH) return; + + const HB_UNUSED unsigned transform_depth = ctx->paint->transform_stack.length; + const HB_UNUSED unsigned clip_depth = ctx->paint->clip_stack.length; + const HB_UNUSED unsigned surface_depth = ctx->paint->surface_stack.length; + + ctx->depth++; + + hb_svg_str_t tag = parser.tag_name; + bool self_closing = parser.self_closing; + + /* Extract common attributes */ + hb_svg_str_t style = parser.find_attr ("style"); + hb_svg_style_props_t style_props; + svg_parse_style_props (style, &style_props); + hb_svg_str_t fill_attr = svg_pick_attr_or_style (parser, style_props.fill, "fill"); + hb_svg_str_t fill_opacity_str = svg_pick_attr_or_style (parser, style_props.fill_opacity, "fill-opacity"); + hb_svg_str_t opacity_str = svg_pick_attr_or_style (parser, style_props.opacity, "opacity"); + hb_svg_str_t transform_str = svg_pick_attr_or_style (parser, style_props.transform, "transform"); + hb_svg_str_t clip_path_attr = svg_pick_attr_or_style (parser, style_props.clip_path, "clip-path"); + hb_svg_str_t display_str = svg_pick_attr_or_style (parser, style_props.display, "display"); + hb_svg_str_t color_str = svg_pick_attr_or_style (parser, style_props.color, "color"); + hb_svg_str_t visibility_str = svg_pick_attr_or_style (parser, style_props.visibility, "visibility"); + + hb_svg_cascade_t state = inherited; + state.fill = (fill_attr.is_null () || svg_str_is_inherit (fill_attr)) ? inherited.fill : fill_attr; + state.fill_opacity = (fill_opacity_str.len && !svg_str_is_inherit (fill_opacity_str)) + ? svg_parse_float_clamped01 (fill_opacity_str) + : inherited.fill_opacity; + state.opacity = (opacity_str.len && !svg_str_is_inherit (opacity_str) && !svg_str_is_none (opacity_str)) + ? svg_parse_float_clamped01 (opacity_str) + : (svg_str_is_inherit (opacity_str) ? inherited.opacity : 1.f); + state.clip_path = (clip_path_attr.is_null () || svg_str_is_inherit (clip_path_attr)) + ? inherited.clip_path + : clip_path_attr; + if (svg_str_is_inherit (transform_str) || svg_str_is_none (transform_str)) + transform_str = {}; + state.color = inherited.color; + bool is_none = false; + if (color_str.len && !color_str.trim ().eq_ascii_ci ("inherit")) + state.color = hb_raster_svg_parse_color (color_str, ctx->pfuncs, ctx->paint, + inherited.color, hb_font_get_face (ctx->font), + ctx->palette, &is_none); + state.visibility = inherited.visibility; + hb_svg_str_t visibility_trim = visibility_str.trim (); + if (visibility_trim.len && !visibility_trim.eq_ascii_ci ("inherit")) + state.visibility = !(visibility_trim.eq_ascii_ci ("hidden") || + visibility_trim.eq_ascii_ci ("collapse")); + + if (display_str.trim ().eq_ascii_ci ("none")) + { + if (!self_closing) + svg_skip_subtree (parser); + ctx->depth--; + assert (ctx->paint->transform_stack.length == transform_depth); + assert (ctx->paint->clip_stack.length == clip_depth); + assert (ctx->paint->surface_stack.length == surface_depth); + return; + } + if (!state.visibility) + { + if (!self_closing) + svg_skip_subtree (parser); + ctx->depth--; + assert (ctx->paint->transform_stack.length == transform_depth); + assert (ctx->paint->clip_stack.length == clip_depth); + assert (ctx->paint->surface_stack.length == surface_depth); + return; + } + + if (tag.eq ("symbol") && !ctx->allow_symbol_render_once) + { + if (!self_closing) + svg_skip_subtree (parser); + ctx->depth--; + assert (ctx->paint->transform_stack.length == transform_depth); + assert (ctx->paint->clip_stack.length == clip_depth); + assert (ctx->paint->surface_stack.length == surface_depth); + return; + } + if (tag.eq ("symbol")) + ctx->allow_symbol_render_once = false; + + if (hb_raster_svg_tag_is_container (tag)) + svg_render_container_element (ctx, parser, tag, self_closing, + state, transform_str, state.clip_path); + else if (svg_render_primitive_shape_element (ctx, parser, state, transform_str)) + ; + else if (tag.eq ("use")) + { + hb_svg_use_context_t use_ctx = {ctx->paint, ctx->pfuncs, + ctx->doc_start, ctx->doc_len, + ctx->svg_accel, ctx->doc_cache, + &ctx->use_decycler}; + hb_raster_svg_render_use_element (&use_ctx, parser, &state, transform_str, + svg_render_use_callback, ctx); + } + + ctx->depth--; + + assert (ctx->paint->transform_stack.length == transform_depth); + assert (ctx->paint->clip_stack.length == clip_depth); + assert (ctx->paint->surface_stack.length == surface_depth); +} + + +/* + * 12. Entry point + */ + +hb_bool_t +hb_raster_svg_render (hb_raster_paint_t *paint, + hb_blob_t *blob, + hb_codepoint_t glyph, + hb_font_t *font, + unsigned palette, + hb_color_t foreground) +{ + bool ret = false; + hb_blob_t *render_blob = nullptr; + hb_face_t *face HB_UNUSED = hb_font_get_face (font); + const OT::SVG::svg_doc_cache_t *doc_cache = nullptr; +#ifndef HB_NO_SVG + unsigned doc_index = 0; + hb_codepoint_t start_glyph = HB_CODEPOINT_INVALID; + hb_codepoint_t end_glyph = HB_CODEPOINT_INVALID; +#endif + hb_paint_funcs_t *pfuncs = hb_raster_paint_get_funcs (); + hb_svg_render_context_t ctx; + hb_svg_cascade_t initial_state; + hb_svg_defs_scan_context_t scan_ctx; + bool found_glyph = false; +#ifndef HB_NO_SVG + unsigned glyph_start = 0, glyph_end = 0; +#endif + + unsigned data_len = 0; + const char *data = nullptr; + render_blob = OT::hb_ot_svg_reference_normalized_blob (blob, &data, &data_len); + if (!render_blob || !data_len) + goto done; + +#ifndef HB_NO_SVG + if (face && + hb_ot_color_glyph_get_svg_document_index (face, glyph, &doc_index) && + hb_ot_color_get_svg_document_glyph_range (face, doc_index, &start_glyph, &end_glyph)) + doc_cache = face->table.SVG->get_or_create_doc_cache (render_blob, data, data_len, + doc_index, start_glyph, end_glyph); + + if (doc_cache) + data = face->table.SVG->doc_cache_get_svg (doc_cache, &data_len); +#endif + + ctx.paint = paint; + ctx.pfuncs = pfuncs; + ctx.font = font; + ctx.palette = palette; + ctx.foreground = foreground; + ctx.doc_start = data; + ctx.doc_len = data_len; +#ifndef HB_NO_SVG + ctx.svg_accel = face ? face->table.SVG.get () : nullptr; +#endif + ctx.doc_cache = doc_cache; + + initial_state.color = foreground; + + scan_ctx = { + &ctx.defs, ctx.pfuncs, ctx.paint, + ctx.foreground, hb_font_get_face (ctx.font), + ctx.palette, + ctx.doc_start, ctx.doc_len, + ctx.svg_accel, ctx.doc_cache + }; + hb_raster_svg_collect_defs (&scan_ctx, data, data_len); + +#ifndef HB_NO_SVG + if (doc_cache && face->table.SVG->doc_cache_get_glyph_span (doc_cache, glyph, &glyph_start, &glyph_end)) + { + hb_svg_xml_parser_t parser (data + glyph_start, glyph_end - glyph_start); + hb_svg_token_type_t tok = parser.next (); + if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG) + { + hb_paint_push_font_transform (ctx.pfuncs, ctx.paint, font); + ctx.push_transform (1, 0, 0, -1, 0, 0); + svg_render_element (&ctx, parser, initial_state); + ctx.pop_transform (); + hb_paint_pop_transform (ctx.pfuncs, ctx.paint); + found_glyph = true; + } + } +#endif + if (!found_glyph) + { + /* Fallback for malformed/uncached docs: linear scan by glyph id. */ + char glyph_id_str[32]; + int glyph_id_len = snprintf (glyph_id_str, sizeof (glyph_id_str), "glyph%u", glyph); + if (glyph_id_len <= 0 || (unsigned) glyph_id_len >= sizeof (glyph_id_str)) + return false; + hb_svg_xml_parser_t parser (data, data_len); + while (true) + { + hb_svg_token_type_t tok = parser.next (); + if (tok == SVG_TOKEN_EOF) break; + + if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG) + { + hb_svg_str_t id = parser.find_attr ("id"); + if (id.len) + { + if (id.len == (unsigned) glyph_id_len && + 0 == hb_memcmp (id.data, glyph_id_str, (unsigned) glyph_id_len)) + { + hb_paint_push_font_transform (ctx.pfuncs, ctx.paint, font); + ctx.push_transform (1, 0, 0, -1, 0, 0); + svg_render_element (&ctx, parser, initial_state); + ctx.pop_transform (); + hb_paint_pop_transform (ctx.pfuncs, ctx.paint); + found_glyph = true; + break; + } + } + } + } + } + + ret = found_glyph; + +done: + hb_blob_destroy (render_blob); + return ret; +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-use.cc b/thirdparty/harfbuzz/src/hb-raster-svg-use.cc new file mode 100644 index 0000000000..b649612307 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-use.cc @@ -0,0 +1,104 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_NO_RASTER_SVG + +#include "hb.hh" + +#include "hb-raster-svg-use.hh" + +#include "hb-raster-svg-base.hh" + +void +hb_raster_svg_render_use_element (const hb_svg_use_context_t *ctx, + hb_svg_xml_parser_t &parser, + const void *state, + hb_svg_str_t transform_str, + hb_svg_use_render_cb_t render_cb, + void *render_user) +{ + hb_svg_str_t href = hb_raster_svg_find_href_attr (parser); + + hb_svg_str_t ref_id; + if (!hb_raster_svg_parse_local_id_ref (href, &ref_id, nullptr)) + return; + + float use_x = 0.f, use_y = 0.f, use_w = 0.f, use_h = 0.f; + hb_raster_svg_parse_use_geometry (parser, &use_x, &use_y, &use_w, &use_h); + + bool has_translate = (use_x != 0.f || use_y != 0.f); + bool has_use_transform = transform_str.len > 0; + + if (has_use_transform) + { + hb_svg_transform_t t; + hb_raster_svg_parse_transform (transform_str, &t); + hb_paint_push_transform (ctx->pfuncs, ctx->paint, t.xx, t.yx, t.xy, t.yy, t.dx, t.dy); + } + + if (has_translate) + hb_paint_push_transform (ctx->pfuncs, ctx->paint, 1, 0, 0, 1, use_x, use_y); + + const char *found = nullptr; + hb_raster_svg_find_element_by_id (ctx->doc_start, ctx->doc_len, ctx->svg_accel, ctx->doc_cache, + ref_id, &found); + + if (found) + { + bool can_render = true; + hb_decycler_node_t node (*ctx->use_decycler); + if (unlikely (!node.visit ((uintptr_t) found))) + can_render = false; + + if (can_render) + { + unsigned remaining = ctx->doc_start + ctx->doc_len - found; + hb_svg_xml_parser_t ref_parser (found, remaining); + hb_svg_token_type_t tok = ref_parser.next (); + if (tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG) + { + bool has_viewport_scale = false; + hb_svg_transform_t t; + if (hb_raster_svg_compute_use_target_viewbox_transform (ref_parser, use_w, use_h, &t)) + { + hb_paint_push_transform (ctx->pfuncs, ctx->paint, t.xx, t.yx, t.xy, t.yy, t.dx, t.dy); + has_viewport_scale = true; + } + render_cb (render_user, ref_parser, state, has_viewport_scale); + if (has_viewport_scale) + hb_paint_pop_transform (ctx->pfuncs, ctx->paint); + } + } + } + + if (has_translate) + hb_paint_pop_transform (ctx->pfuncs, ctx->paint); + + if (has_use_transform) + hb_paint_pop_transform (ctx->pfuncs, ctx->paint); +} + +#endif /* !HB_NO_RASTER_SVG */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg-use.hh b/thirdparty/harfbuzz/src/hb-raster-svg-use.hh new file mode 100644 index 0000000000..aa81f4a013 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg-use.hh @@ -0,0 +1,48 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_USE_HH +#define HB_RASTER_SVG_USE_HH + +#include "hb.hh" + +#include "hb-raster-svg-context.hh" +#include "hb-raster-svg-parse.hh" + +typedef void (*hb_svg_use_render_cb_t) (void *render_user, + hb_svg_xml_parser_t &parser, + const void *state, + bool viewport_mapped); + +HB_INTERNAL void +hb_raster_svg_render_use_element (const hb_svg_use_context_t *ctx, + hb_svg_xml_parser_t &parser, + const void *state, + hb_svg_str_t transform_str, + hb_svg_use_render_cb_t render_cb, + void *render_user); + +#endif /* HB_RASTER_SVG_USE_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-svg.hh b/thirdparty/harfbuzz/src/hb-raster-svg.hh new file mode 100644 index 0000000000..3b723f7c6d --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-svg.hh @@ -0,0 +1,68 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_SVG_HH +#define HB_RASTER_SVG_HH + +#include "hb.hh" + +struct hb_raster_paint_t; + +/* Callback type: emits SVG path draw commands to the given draw funcs */ +typedef void (*hb_raster_svg_path_func_t) (hb_draw_funcs_t *dfuncs, + void *draw_data, + void *user_data); + +/* Push clip from arbitrary path (reuses push_clip_glyph logic) */ +HB_INTERNAL void +hb_raster_paint_push_clip_path (hb_raster_paint_t *c, + hb_raster_svg_path_func_t func, + void *user_data); + +/* Render SVG document for a specific glyph */ +#ifndef HB_NO_RASTER_SVG +HB_INTERNAL hb_bool_t +hb_raster_svg_render (hb_raster_paint_t *paint, + hb_blob_t *blob, + hb_codepoint_t glyph, + hb_font_t *font, + unsigned palette, + hb_color_t foreground); +#else +static inline hb_bool_t +hb_raster_svg_render (hb_raster_paint_t *paint HB_UNUSED, + hb_blob_t *blob HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + hb_font_t *font HB_UNUSED, + unsigned palette HB_UNUSED, + hb_color_t foreground HB_UNUSED) +{ + return false; +} +#endif + + +#endif /* HB_RASTER_SVG_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster-utils.hh b/thirdparty/harfbuzz/src/hb-raster-utils.hh new file mode 100644 index 0000000000..852aee8445 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster-utils.hh @@ -0,0 +1,81 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_HH +#define HB_RASTER_HH + +#include "hb.hh" + +/* Shared pixel helpers (used by paint and image compositing). */ + +static HB_ALWAYS_INLINE uint8_t +hb_raster_div255 (unsigned a) +{ + if (true) + { + // An approximation. Slightly faster. + // https://github.com/linebender/vello/blob/ab58009c8289e83689cd0effc4e34d1c6e8b51f5/sparse_strips/vello_cpu/src/util.rs#L10-L63 + return (a + 255) >> 8; + } + + return (uint8_t) ((a + 128 + ((a + 128) >> 8)) >> 8); +} + +static HB_ALWAYS_INLINE uint32_t +hb_raster_pack_pixel (uint8_t b, uint8_t g, uint8_t r, uint8_t a) +{ + return (uint32_t) b | ((uint32_t) g << 8) | ((uint32_t) r << 16) | ((uint32_t) a << 24); +} + +/* SRC_OVER: premultiplied src over premultiplied dst. */ +static HB_ALWAYS_INLINE uint32_t +hb_raster_src_over (uint32_t src, uint32_t dst) +{ + uint8_t sa = (uint8_t) (src >> 24); + if (sa == 255) return src; + if (sa == 0) return dst; + unsigned inv_sa = 255 - sa; + uint8_t rb = hb_raster_div255 ((dst & 0xFF) * inv_sa) + (uint8_t) (src & 0xFF); + uint8_t rg = hb_raster_div255 (((dst >> 8) & 0xFF) * inv_sa) + (uint8_t) ((src >> 8) & 0xFF); + uint8_t rr = hb_raster_div255 (((dst >> 16) & 0xFF) * inv_sa) + (uint8_t) ((src >> 16) & 0xFF); + uint8_t ra = hb_raster_div255 (((dst >> 24) & 0xFF) * inv_sa) + sa; + return (uint32_t) rb | ((uint32_t) rg << 8) | ((uint32_t) rr << 16) | ((uint32_t) ra << 24); +} + +/* Scale a premultiplied pixel by an alpha [0,255]. */ +static HB_ALWAYS_INLINE uint32_t +hb_raster_alpha_mul (uint32_t px, unsigned a) +{ + if (a == 255) return px; + if (a == 0) return 0; + uint8_t rb = hb_raster_div255 ((px & 0xFF) * a); + uint8_t rg = hb_raster_div255 (((px >> 8) & 0xFF) * a); + uint8_t rr = hb_raster_div255 (((px >> 16) & 0xFF) * a); + uint8_t ra = hb_raster_div255 (((px >> 24) & 0xFF) * a); + return (uint32_t) rb | ((uint32_t) rg << 8) | ((uint32_t) rr << 16) | ((uint32_t) ra << 24); +} + +#endif /* HB_RASTER_HH */ diff --git a/thirdparty/harfbuzz/src/hb-raster.cc b/thirdparty/harfbuzz/src/hb-raster.cc new file mode 100644 index 0000000000..97ba6e6642 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster.cc @@ -0,0 +1,68 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-raster.h" + +/** + * SECTION:hb-raster + * @title: hb-raster + * @short_description: Glyph rasterization + * @include: hb-raster.h + * + * Functions for rasterizing glyph outlines into pixel buffers. + * + * #hb_raster_draw_t rasterizes outline geometry and always outputs + * @HB_RASTER_FORMAT_A8. Typical flow: + * + * |[ + * hb_raster_draw_t *draw = hb_raster_draw_create_or_fail (); + * hb_raster_draw_set_scale_factor (draw, 64.f, 64.f); + * hb_raster_draw_set_transform (draw, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f); + * hb_raster_draw_set_glyph_extents (draw, &glyph_extents); + * hb_raster_draw_glyph (draw, font, gid, pen_x, pen_y); + * hb_raster_image_t *mask = hb_raster_draw_render (draw); + * ]| + * + * #hb_raster_paint_t renders color paint graphs and always outputs + * @HB_RASTER_FORMAT_BGRA32. Typical flow: + * + * |[ + * hb_raster_paint_t *paint = hb_raster_paint_create_or_fail (); + * hb_raster_paint_set_scale_factor (paint, 64.f, 64.f); + * hb_raster_paint_set_transform (paint, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f); + * hb_raster_paint_set_foreground (paint, foreground); + * hb_glyph_extents_t glyph_extents; + * hb_font_get_glyph_extents (font, gid, &glyph_extents); + * hb_raster_paint_set_glyph_extents (paint, &glyph_extents); + * hb_raster_paint_glyph (paint, font, gid, pen_x, pen_y, 0, foreground); + * hb_raster_image_t *img = hb_raster_paint_render (paint); + * ]| + * + * In both modes, set extents explicitly (or via glyph extents) before + * rendering to avoid implicit allocations and to get deterministic bounds. + **/ diff --git a/thirdparty/harfbuzz/src/hb-raster.h b/thirdparty/harfbuzz/src/hb-raster.h new file mode 100644 index 0000000000..296e171b06 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-raster.h @@ -0,0 +1,321 @@ +/* + * Copyright (C) 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_RASTER_H +#define HB_RASTER_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/* Shared types */ + +/** + * hb_raster_format_t: + * @HB_RASTER_FORMAT_A8: 8-bit alpha-only coverage + * @HB_RASTER_FORMAT_BGRA32: 32-bit BGRA color + * + * Pixel format for raster images. + * + * Since: 13.0.0 + */ +typedef enum { + HB_RASTER_FORMAT_A8 = 0, + HB_RASTER_FORMAT_BGRA32 = 1, +} hb_raster_format_t; + +/** + * hb_raster_extents_t: + * @x_origin: X coordinate of the left edge of the image in glyph space + * @y_origin: Y coordinate of the bottom edge of the image in glyph space + * @width: Width in pixels + * @height: Height in pixels + * @stride: Bytes per row; 0 means auto-calculate on input, filled on output + * + * Pixel-buffer extents for raster operations. + * + * Since: 13.0.0 + */ +typedef struct hb_raster_extents_t { + int x_origin, y_origin; + unsigned int width, height; + unsigned int stride; +} hb_raster_extents_t; + + +/* hb_raster_image_t */ + +/** + * hb_raster_image_t: + * + * An opaque raster image object holding a pixel buffer produced by + * hb_raster_draw_render(). Use hb_raster_image_get_buffer() and + * hb_raster_image_get_extents() to access the pixels. + * + * Since: 13.0.0 + **/ +typedef struct hb_raster_image_t hb_raster_image_t; + +HB_EXTERN hb_raster_image_t * +hb_raster_image_create_or_fail (void); + +HB_EXTERN hb_raster_image_t * +hb_raster_image_reference (hb_raster_image_t *image); + +HB_EXTERN void +hb_raster_image_destroy (hb_raster_image_t *image); + +HB_EXTERN hb_bool_t +hb_raster_image_set_user_data (hb_raster_image_t *image, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_raster_image_get_user_data (hb_raster_image_t *image, + hb_user_data_key_t *key); + +HB_EXTERN hb_bool_t +hb_raster_image_configure (hb_raster_image_t *image, + hb_raster_format_t format, + const hb_raster_extents_t *extents); + +HB_EXTERN void +hb_raster_image_clear (hb_raster_image_t *image); + +HB_EXTERN const uint8_t * +hb_raster_image_get_buffer (hb_raster_image_t *image); + +HB_EXTERN void +hb_raster_image_get_extents (hb_raster_image_t *image, + hb_raster_extents_t *extents); + +HB_EXTERN hb_raster_format_t +hb_raster_image_get_format (hb_raster_image_t *image); + +HB_EXTERN hb_bool_t +hb_raster_image_deserialize_from_png_or_fail (hb_raster_image_t *image, + hb_blob_t *png); + +HB_EXTERN hb_blob_t * +hb_raster_image_serialize_to_png_or_fail (hb_raster_image_t *image); + + +/* hb_raster_draw_t */ + +/** + * hb_raster_draw_t: + * + * An opaque outline rasterizer object. Accumulates glyph outlines + * via #hb_draw_funcs_t callbacks obtained from hb_raster_draw_get_funcs(), + * then produces an #hb_raster_image_t with hb_raster_draw_render(). + * + * Since: 13.0.0 + **/ +typedef struct hb_raster_draw_t hb_raster_draw_t; + +HB_EXTERN hb_raster_draw_t * +hb_raster_draw_create_or_fail (void); + +HB_EXTERN hb_raster_draw_t * +hb_raster_draw_reference (hb_raster_draw_t *draw); + +HB_EXTERN void +hb_raster_draw_destroy (hb_raster_draw_t *draw); + +HB_EXTERN hb_bool_t +hb_raster_draw_set_user_data (hb_raster_draw_t *draw, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_raster_draw_get_user_data (hb_raster_draw_t *draw, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_raster_draw_set_transform (hb_raster_draw_t *draw, + float xx, float yx, + float xy, float yy, + float dx, float dy); + +HB_EXTERN void +hb_raster_draw_set_scale_factor (hb_raster_draw_t *draw, + float x_scale_factor, + float y_scale_factor); + +HB_EXTERN void +hb_raster_draw_get_scale_factor (hb_raster_draw_t *draw, + float *x_scale_factor, + float *y_scale_factor); + +HB_EXTERN void +hb_raster_draw_get_transform (hb_raster_draw_t *draw, + float *xx, float *yx, + float *xy, float *yy, + float *dx, float *dy); + +HB_EXTERN void +hb_raster_draw_set_extents (hb_raster_draw_t *draw, + const hb_raster_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_raster_draw_get_extents (hb_raster_draw_t *draw, + hb_raster_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_raster_draw_set_glyph_extents (hb_raster_draw_t *draw, + const hb_glyph_extents_t *glyph_extents); + +HB_EXTERN hb_draw_funcs_t * +hb_raster_draw_get_funcs (void); + +HB_EXTERN void +hb_raster_draw_glyph (hb_raster_draw_t *draw, + hb_font_t *font, + hb_codepoint_t glyph, + float pen_x, + float pen_y); + +HB_EXTERN hb_raster_image_t * +hb_raster_draw_render (hb_raster_draw_t *draw); + +HB_EXTERN void +hb_raster_draw_reset (hb_raster_draw_t *draw); + +HB_EXTERN void +hb_raster_draw_recycle_image (hb_raster_draw_t *draw, + hb_raster_image_t *image); + + + +/* hb_raster_paint_t */ + +/** + * hb_raster_paint_t: + * + * An opaque color-glyph paint context. Implements #hb_paint_funcs_t + * callbacks that render COLRv0/v1 color glyphs into a BGRA32 + * #hb_raster_image_t. + * + * Since: 13.0.0 + **/ +typedef struct hb_raster_paint_t hb_raster_paint_t; + +HB_EXTERN hb_raster_paint_t * +hb_raster_paint_create_or_fail (void); + +HB_EXTERN hb_raster_paint_t * +hb_raster_paint_reference (hb_raster_paint_t *paint); + +HB_EXTERN void +hb_raster_paint_destroy (hb_raster_paint_t *paint); + +HB_EXTERN hb_bool_t +hb_raster_paint_set_user_data (hb_raster_paint_t *paint, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_raster_paint_get_user_data (hb_raster_paint_t *paint, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_raster_paint_set_transform (hb_raster_paint_t *paint, + float xx, float yx, + float xy, float yy, + float dx, float dy); + +HB_EXTERN void +hb_raster_paint_get_transform (hb_raster_paint_t *paint, + float *xx, float *yx, + float *xy, float *yy, + float *dx, float *dy); + +HB_EXTERN void +hb_raster_paint_set_scale_factor (hb_raster_paint_t *paint, + float x_scale_factor, + float y_scale_factor); + +HB_EXTERN void +hb_raster_paint_get_scale_factor (hb_raster_paint_t *paint, + float *x_scale_factor, + float *y_scale_factor); + +HB_EXTERN void +hb_raster_paint_set_extents (hb_raster_paint_t *paint, + const hb_raster_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_raster_paint_get_extents (hb_raster_paint_t *paint, + hb_raster_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_raster_paint_set_glyph_extents (hb_raster_paint_t *paint, + const hb_glyph_extents_t *glyph_extents); + +HB_EXTERN void +hb_raster_paint_set_foreground (hb_raster_paint_t *paint, + hb_color_t foreground); + +HB_EXTERN void +hb_raster_paint_clear_custom_palette_colors (hb_raster_paint_t *paint); + +HB_EXTERN hb_bool_t +hb_raster_paint_set_custom_palette_color (hb_raster_paint_t *paint, + unsigned int color_index, + hb_color_t color); + +HB_EXTERN hb_paint_funcs_t * +hb_raster_paint_get_funcs (void); + +HB_EXTERN hb_bool_t +hb_raster_paint_glyph (hb_raster_paint_t *paint, + hb_font_t *font, + hb_codepoint_t glyph, + float pen_x, + float pen_y, + unsigned palette, + hb_color_t foreground); + +HB_EXTERN hb_raster_image_t * +hb_raster_paint_render (hb_raster_paint_t *paint); + +HB_EXTERN void +hb_raster_paint_reset (hb_raster_paint_t *paint); + +HB_EXTERN void +hb_raster_paint_recycle_image (hb_raster_paint_t *paint, + hb_raster_image_t *image); + + +HB_END_DECLS + +#endif /* HB_RASTER_H */ diff --git a/thirdparty/harfbuzz/src/hb-subset-cff-common.cc b/thirdparty/harfbuzz/src/hb-subset-cff-common.cc index 5e4ea5fe7c..96e148afc1 100644 --- a/thirdparty/harfbuzz/src/hb-subset-cff-common.cc +++ b/thirdparty/harfbuzz/src/hb-subset-cff-common.cc @@ -68,7 +68,6 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, /* use hb_set to determine the subset of font dicts */ hb_set_t set; hb_codepoint_t prev_fd = CFF_UNDEF_CODE; - hb_pair_t last_range {0, 0}; auto it = hb_iter (plan->new_to_old_gid_list); auto _ = *it; for (hb_codepoint_t gid = 0; gid < subset_num_glyphs; gid++) @@ -84,9 +83,8 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, /* fonttools retains FDSelect & font dicts for missing glyphs. do the same */ old_glyph = gid; } - if (old_glyph >= last_range.second) - last_range = src.get_fd_range (old_glyph); - unsigned fd = last_range.first; + auto fd_range = src.get_fd_range (old_glyph); + unsigned fd = fd_range.first; if (fd != prev_fd) { @@ -96,7 +94,7 @@ hb_plan_subset_cff_fdselect (const hb_subset_plan_t *plan, fdselect_ranges.push (code_pair_t { fd, gid }); if (gid == old_glyph) - gid = hb_min (_.first - 1, last_range.second - 1); + gid = hb_min (_.first - 1, fd_range.second - 1); } } diff --git a/thirdparty/harfbuzz/src/hb-subset-cff-common.hh b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh index 75e5e66241..3140b9ca36 100644 --- a/thirdparty/harfbuzz/src/hb-subset-cff-common.hh +++ b/thirdparty/harfbuzz/src/hb-subset-cff-common.hh @@ -321,11 +321,31 @@ struct cff_font_dict_op_serializer_t : op_serializer_t } }; +/* CharString command for specialization */ +struct cs_command_t +{ + hb_vector_t args; + hb_vector_t mask_bytes; /* For hintmask/cntrmask payload bytes. */ + op_code_t op; + + cs_command_t () : op (OpCode_Invalid) {} + cs_command_t (op_code_t op_) : op (op_) {} +}; + +typedef hb_vector_t *cs_command_vec_t; + struct flatten_param_t { + flatten_param_t (str_buff_t &flatStr_, + bool drop_hints_, + const hb_subset_plan_t *plan_, + cs_command_vec_t commands_ = nullptr) + : flatStr (flatStr_), drop_hints (drop_hints_), plan (plan_), commands (commands_) {} + str_buff_t &flatStr; bool drop_hints; const hb_subset_plan_t *plan; + cs_command_vec_t commands; /* Optional: capture parsed commands for specialization */ }; template @@ -335,7 +355,8 @@ struct subr_flattener_t const hb_subset_plan_t *plan_) : acc (acc_), plan (plan_) {} - bool flatten (str_buff_vec_t &flat_charstrings) + bool flatten (str_buff_vec_t &flat_charstrings, + hb_vector_t> *command_capture = nullptr) { unsigned count = plan->num_output_glyphs (); if (!flat_charstrings.resize_exact (count)) @@ -361,7 +382,8 @@ struct subr_flattener_t flatten_param_t param = { flat_charstrings.arrayZ[i], (bool) (plan->flags & HB_SUBSET_FLAGS_NO_HINTING), - plan + plan, + command_capture ? &(*command_capture)[i] : nullptr }; if (unlikely (!interp.interpret (param))) return false; diff --git a/thirdparty/harfbuzz/src/hb-subset-cff2-to-cff1.cc b/thirdparty/harfbuzz/src/hb-subset-cff2-to-cff1.cc new file mode 100644 index 0000000000..6a65bf5d8b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-subset-cff2-to-cff1.cc @@ -0,0 +1,40 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/* + * CFF2 to CFF1 Converter + * + * The actual implementation is in hb-subset-cff2.cc where it has access + * to the full cff2_subset_plan definition. + * + * This file just exists to keep the build system happy. + */ + +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + +#include "hb-subset-cff2-to-cff1.hh" + +#endif /* HB_NO_SUBSET_CFF */ diff --git a/thirdparty/harfbuzz/src/hb-subset-cff2-to-cff1.hh b/thirdparty/harfbuzz/src/hb-subset-cff2-to-cff1.hh new file mode 100644 index 0000000000..6022c00618 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-subset-cff2-to-cff1.hh @@ -0,0 +1,177 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef HB_SUBSET_CFF2_TO_CFF1_HH +#define HB_SUBSET_CFF2_TO_CFF1_HH + +#include "hb.hh" + +#ifndef HB_NO_SUBSET_CFF + +#include "hb-ot-cff1-table.hh" +#include "hb-ot-cff2-table.hh" +#include "hb-subset-cff-common.hh" + +namespace OT { + // Forward declarations - these are defined in hb-subset-cff2.cc + struct cff2_subset_plan; +} + +namespace CFF { + +// Forward declaration +struct cff2_top_dict_values_t; + +// Default font name for converted CFF1 fonts +static constexpr const char CFF1_DEFAULT_FONT_NAME[] = "CFF1Font"; + +/* + * CFF2 to CFF1 Converter + * + * Converts an instantiated (pinned) CFF2 variable font to CFF1 format. + * This is used when instantiating a variable font to a static instance. + * + * IMPLEMENTATION STATUS: + * ✓ CFF1 structure (Header, Name INDEX, String INDEX, Top DICT INDEX) + * ✓ ROS operator (makes font CID-keyed: "Adobe-Identity-0") + * ✓ FDArray and FDSelect in Top DICT (required for CID fonts) + * ✓ FDSelect3 format (compact range-based, 8 bytes for single-FD fonts) + * ✓ CID Charset with identity mapping (format 2) + * ✓ FontBBox from head table (xMin, yMin, xMax, yMax) + * ✓ Width optimization (defaultWidthX/nominalWidthX with O(n) algorithm) + * ✓ Width encoding in CharStrings (prepended if != defaultWidthX) + * ✓ CharString specialization (h/v operators, combined when possible) + * ✓ Stack depth control (generalize→specialize with maxstack=48) + * ✓ CharStrings with endchar operators (CFF1 requires, CFF2 doesn't) + * ✓ Private DICT instantiation (blend operators evaluated) + * ✓ Desubroutinized path (CharStrings are flattened, no subroutines) + * ✓ OTS validation passes + * ✓ HarfBuzz rendering works + * + * FUTURE ENHANCEMENTS: + * - Curve operator specialization (hhcurveto, vvcurveto, etc.) + * - Peephole optimization (minor additional size savings) + * + * Key conversions: + * - Version: 2 -> 1 + * - Add Name INDEX (required in CFF1) + * - Wrap Top DICT in an INDEX (inline in CFF2, indexed in CFF1) + * - Add String INDEX ("Adobe", "Identity" for ROS operator) + * - Add ROS operator to Top DICT (makes it CID-keyed) + * - Add FDSelect to Top DICT (required in CFF1 even with single FD) + * - Add endchar to CharStrings (required in CFF1, optional in CFF2) + */ + +struct cff1_subset_plan_from_cff2_t +{ + // Inherits most data from cff2_subset_plan + const OT::cff2_subset_plan *cff2_plan; + + // CFF1-specific additions + hb_vector_t fontName; // Single font name for Name INDEX + + bool create (const OT::cff2_subset_plan &cff2_plan_) + { + cff2_plan = &cff2_plan_; + + // Create a simple font name (CFF1 requires a Name INDEX) + fontName.resize (strlen (CFF1_DEFAULT_FONT_NAME)); + if (fontName.in_error ()) return false; + memcpy (fontName.arrayZ, CFF1_DEFAULT_FONT_NAME, strlen (CFF1_DEFAULT_FONT_NAME)); + + return true; + } +}; + +/* CFF1 Top DICT operator serializer that adds ROS and removes CFF2-specific ops */ +struct cff1_from_cff2_top_dict_op_serializer_t : cff_top_dict_op_serializer_t<> +{ + bool serialize (hb_serialize_context_t *c, + const op_str_t &opstr, + const cff_sub_table_info_t &info) const + { + TRACE_SERIALIZE (this); + + switch (opstr.op) + { + case OpCode_vstore: + // CFF2-only operator, skip it + return_trace (true); + + case OpCode_CharStrings: + return_trace (FontDict::serialize_link4_op(c, opstr.op, info.char_strings_link, whence_t::Absolute)); + + case OpCode_FDArray: + case OpCode_FDSelect: + // These are explicitly serialized in the main function to ensure they're present + // even if CFF2 doesn't have them. Skip them here to avoid duplication. + return_trace (true); + + default: + return_trace (copy_opstr (c, opstr)); + } + } + + // Serialize ROS operator to make this a CID-keyed font + bool serialize_ros (hb_serialize_context_t *c) const + { + TRACE_SERIALIZE (this); + + // ROS = Registry-Ordering-Supplement + // We use "Adobe", "Identity", 0 for maximum compatibility + + // Allocate space and encode directly + // Registry: SID for "Adobe" (custom string at index 0 = SID 391) + // Ordering: SID for "Identity" (custom string at index 1 = SID 392) + // Supplement: 0 + // Note: CFF standard strings end at SID 390, custom strings start at 391 + + str_buff_t buff; + str_encoder_t encoder (buff); + + encoder.encode_int (391); // Registry SID ("Adobe" in our String INDEX) + encoder.encode_int (392); // Ordering SID ("Identity" in our String INDEX) + encoder.encode_int (0); // Supplement + encoder.encode_op (OpCode_ROS); + + if (encoder.in_error ()) + return_trace (false); + + auto bytes = buff.as_bytes (); + return_trace (c->embed (bytes.arrayZ, bytes.length)); + } +}; + +/* Main serialization function */ +HB_INTERNAL bool +serialize_cff2_to_cff1 (hb_serialize_context_t *c, + OT::cff2_subset_plan &plan, + const cff2_top_dict_values_t &cff2_topDict, + const OT::cff2::accelerator_subset_t &acc); + +} /* namespace CFF */ + +#endif /* HB_NO_SUBSET_CFF */ + +#endif /* HB_SUBSET_CFF2_TO_CFF1_HH */ diff --git a/thirdparty/harfbuzz/src/hb-subset-cff2.cc b/thirdparty/harfbuzz/src/hb-subset-cff2.cc index eb5cb0c625..262eeb1aba 100644 --- a/thirdparty/harfbuzz/src/hb-subset-cff2.cc +++ b/thirdparty/harfbuzz/src/hb-subset-cff2.cc @@ -34,6 +34,7 @@ #include "hb-subset-plan.hh" #include "hb-subset-cff-common.hh" #include "hb-cff2-interp-cs.hh" +#include "hb-subset-cff2-to-cff1.hh" using namespace CFF; @@ -73,6 +74,56 @@ struct cff2_cs_opset_flatten_t : cff2_cs_opset_t &env, flatten_param_t& param) { + /* Optionally capture command for specialization (before flushing, to preserve args) */ + if (param.commands) + { + bool skip_command = false; + + switch (op) + { + case OpCode_return: + case OpCode_endchar: + skip_command = true; + break; + + case OpCode_hstem: + case OpCode_hstemhm: + case OpCode_vstem: + case OpCode_vstemhm: + case OpCode_hintmask: + case OpCode_cntrmask: + if (param.drop_hints) + skip_command = true; + break; + + default: + break; + } + + if (!skip_command) + { + cs_command_t cmd (op); + /* Capture resolved blend values */ + for (unsigned int i = 0; i < env.argStack.get_count ();) + { + const blend_arg_t &arg = env.argStack[i]; + if (arg.blending ()) + { + /* For blend args, capture only the resolved default value */ + cmd.args.push (arg); + /* Skip over the multiple blend values */ + i += arg.numValues; + } + else + { + cmd.args.push (arg); + i++; + } + } + param.commands->push (cmd); + } + } + switch (op) { case OpCode_return: @@ -167,6 +218,22 @@ struct cff2_cs_opset_flatten_t : cff2_cs_opset_t &env, flatten_param_t& param) { SUPER::flush_hintmask (op, env, param); + /* Preserve hintmask payload in captured commands for specializer re-encoding. */ + if (param.commands && !param.drop_hints && param.commands->length > 0) + { + auto &cmd = param.commands->tail (); + if (cmd.op == op) + { + cmd.mask_bytes.resize (env.hintmask_size); + if (unlikely (cmd.mask_bytes.in_error ())) + { + env.set_error (); + return; + } + for (unsigned int i = 0; i < env.hintmask_size; i++) + cmd.mask_bytes[i] = env.str_ref[i]; + } + } if (!param.drop_hints) { str_encoder_t encoder (param.flatStr); @@ -436,9 +503,15 @@ struct cff2_subset_plan drop_hints = plan->flags & HB_SUBSET_FLAGS_NO_HINTING; pinned = (bool) plan->normalized_coords; + normalized_coords = plan->normalized_coords; + head_maxp_info = plan->head_maxp_info; + hmtx_map = &plan->hmtx_map; desubroutinize = plan->flags & HB_SUBSET_FLAGS_DESUBROUTINIZE || pinned; // For instancing we need this path + /* Enable command capture for CFF2→CFF1 conversion (for specialization) */ + capture_commands = pinned; + #ifdef HB_EXPERIMENTAL_API min_charstrings_off_size = (plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS) ? 4 : 0; #else @@ -450,8 +523,21 @@ struct cff2_subset_plan /* Flatten global & local subrs */ subr_flattener_t, cff2_cs_opset_flatten_t> flattener(acc, plan); - if (!flattener.flatten (subset_charstrings)) - return false; + + /* Enable command capture if requested (for specialization) */ + if (capture_commands) + { + if (!charstring_commands.resize_exact (num_glyphs)) + return false; + + if (!flattener.flatten (subset_charstrings, &charstring_commands)) + return false; + } + else + { + if (!flattener.flatten (subset_charstrings)) + return false; + } } else { @@ -518,9 +604,483 @@ struct cff2_subset_plan bool desubroutinize = false; unsigned min_charstrings_off_size = 0; + + hb_array_t normalized_coords; // For instantiation + head_maxp_info_t head_maxp_info; // For FontBBox + const hb_hashmap_t> *hmtx_map; // For widths + + // Width optimization results (for CFF1 conversion) + unsigned default_width = 0; + unsigned nominal_width = 0; + + // Command capture for specialization (CFF2→CFF1 conversion) + bool capture_commands = false; + hb_vector_t> charstring_commands; }; } // namespace OT +/* + * CFF2 to CFF1 Converter Implementation + */ + +#include "hb-cff-width-optimizer.hh" +#include "hb-cff-specializer.hh" + +/* Serialize charstrings using CFF1 format with widths */ +static bool +_serialize_cff1_charstrings (hb_serialize_context_t *c, + OT::cff2_subset_plan &plan, + unsigned default_width, + unsigned nominal_width) +{ + c->push (); + + // CFF1 requires: + // 1. Width at the beginning (if != defaultWidthX) + // 2. endchar at the end + str_buff_vec_t cff1_charstrings; + if (unlikely (!cff1_charstrings.resize (plan.subset_charstrings.length))) + { + c->pop_discard (); + return false; + } + + for (unsigned i = 0; i < plan.subset_charstrings.length; i++) + { + // Get width for this glyph from hmtx_map + unsigned width = 0; + if (plan.hmtx_map->has (i)) + width = plan.hmtx_map->get (i).first; + + // Encode width if different from default + str_encoder_t encoder (cff1_charstrings[i]); + if (width != default_width) + { + int delta = (int) width - (int) nominal_width; + encoder.encode_int (delta); + } + + // Use specialized commands if available, otherwise use binary + if (plan.capture_commands && i < plan.charstring_commands.length && + plan.charstring_commands[i].length > 0) + { + // Specialize and encode commands + auto &commands = plan.charstring_commands[i]; + CFF::specialize_commands (commands, 48); /* maxstack=48 for CFF1 */ + if (unlikely (!CFF::encode_commands (commands, cff1_charstrings[i]))) + { + c->pop_discard (); + return false; + } + } + else + { + // Use binary CharString + const str_buff_t &cs = plan.subset_charstrings[i]; + for (unsigned j = 0; j < cs.length; j++) + cff1_charstrings[i].push (cs[j]); + } + + // Check if it already ends with endchar (0x0e) or return (0x0b) + if (cff1_charstrings[i].length == 0 || + (cff1_charstrings[i].tail () != 0x0e && cff1_charstrings[i].tail () != 0x0b)) + { + // Append endchar operator + if (unlikely (!cff1_charstrings[i].push_or_fail (0x0e))) + { + c->pop_discard (); + return false; + } + } + } + + unsigned data_size = 0; + unsigned total_size = CFF1CharStrings::total_size (cff1_charstrings, &data_size); + if (unlikely (!c->start_zerocopy (total_size))) + { + c->pop_discard (); + return false; + } + + auto *cs = c->start_embed (); + if (unlikely (!cs->serialize (c, cff1_charstrings))) + { + c->pop_discard (); + return false; + } + + plan.info.char_strings_link = c->pop_pack (false); + return true; +} + +/* Serialize CID Charset (format 2 range: gid 0-N -> cid 0-N) */ +static bool +_serialize_cff1_charset (hb_serialize_context_t *c, + unsigned int num_glyphs, + objidx_t &charset_link) +{ + // For CID fonts, create a simple identity charset + // Format 2: one range covering all glyphs (except .notdef) + c->push (); + + auto *charset = c->start_embed (); + if (unlikely (!charset)) + { + c->pop_discard (); + return false; + } + + // Create a single range for CID 1 to num_glyphs-1 + hb_vector_t ranges; + if (num_glyphs > 1) + { + code_pair_t range; + range.code = 1; // first CID + range.glyph = num_glyphs - 2; // nLeft (covers glyphs 1 to num_glyphs-1) + ranges.push (range); + } + + if (unlikely (!charset->serialize (c, 2, num_glyphs, ranges))) + { + c->pop_discard (); + return false; + } + + charset_link = c->pop_pack (); + return true; +} + +/* CFF2 to CFF1 serialization */ +namespace CFF { + +bool +serialize_cff2_to_cff1 (hb_serialize_context_t *c, + OT::cff2_subset_plan &plan, + const cff2_top_dict_values_t &cff2_topDict, + const OT::cff2::accelerator_subset_t &acc) +{ + TRACE_SERIALIZE (this); + + /* + * CFF1 Serialization Order (reverse, as HarfBuzz packs from end): + * 1. CharStrings + * 2. Private DICs & Local Subrs + * 3. FDArray + * 4. FDSelect + * 5. Charset + * 6. Global Subrs + * 7. String INDEX + * 8. Top DICT INDEX + * 9. Name INDEX + * 10. Header + */ + + // 0. Optimize width encoding (for all FDs) + { + // Collect widths from hmtx_map + hb_vector_t widths; + widths.alloc (plan.num_glyphs); + + for (unsigned gid = 0; gid < plan.num_glyphs; gid++) + { + unsigned width = 0; + if (plan.hmtx_map->has (gid)) + width = plan.hmtx_map->get (gid).first; + widths.push (width); + } + + // Optimize defaultWidthX and nominalWidthX + CFF::optimize_widths (widths, plan.default_width, plan.nominal_width); + } + + // 1. CharStrings (with widths prepended) + if (!_serialize_cff1_charstrings (c, plan, plan.default_width, plan.nominal_width)) + return_trace (false); + + // 2. Private DICs & Local Subrs (same as CFF2) + hb_vector_t private_dict_infos; + if (unlikely (!private_dict_infos.resize (plan.subset_fdcount))) + return_trace (false); + + for (int i = (int)acc.privateDicts.length; --i >= 0;) + { + if (plan.fdmap.has (i)) + { + objidx_t subrs_link = 0; + + if (plan.subset_localsubrs[i].length > 0) + { + auto *dest = c->push (); + if (likely (dest->serialize (c, plan.subset_localsubrs[i]))) + subrs_link = c->pop_pack (false); + else + { + c->pop_discard (); + return_trace (false); + } + } + + auto *pd = c->push (); + // Use the CFF2 Private DICT serializer which instantiates blends when pinned=true + cff2_private_dict_op_serializer_t privSzr (plan.desubroutinize, plan.drop_hints, plan.pinned, + acc.varStore, plan.normalized_coords); + if (likely (pd->serialize (c, acc.privateDicts[i], privSzr, subrs_link))) + { + // Add defaultWidthX and nominalWidthX for CFF1 + str_buff_t width_ops; + str_encoder_t encoder (width_ops); + encoder.encode_int (plan.default_width); + encoder.encode_op (OpCode_defaultWidthX); + encoder.encode_int (plan.nominal_width); + encoder.encode_op (OpCode_nominalWidthX); + + if (!encoder.in_error () && c->embed (width_ops.as_bytes ().arrayZ, width_ops.length)) + { + unsigned fd = plan.fdmap[i]; + private_dict_infos[fd].size = c->length (); + private_dict_infos[fd].link = c->pop_pack (); + } + else + { + c->pop_discard (); + return_trace (false); + } + } + else + { + c->pop_discard (); + return_trace (false); + } + } + } + + // 3. FDArray - serialize CFF2 font dicts as CFF1 + { + auto *fda = c->push> (); + cff_font_dict_op_serializer_t fontSzr; + auto it = + + hb_zip (+ hb_iter (acc.fontDicts) + | hb_filter ([&] (const cff2_font_dict_values_t &_) + { return plan.fdmap.has (&_ - &acc.fontDicts[0]); }), + hb_iter (private_dict_infos)) + ; + // Explicitly specify template parameters: DICTVAL, INFO + bool success = fda->serialize (c, it, fontSzr); + if (success) + plan.info.fd_array_link = c->pop_pack (false); + else + { + c->pop_discard (); + return_trace (false); + } + } + + // 4. FDSelect (required in CFF1 CID-keyed fonts) + // CFF1 requires FDSelect for all CID-keyed fonts, even with just one FD + // CFF2 makes it optional when there's only one FD + if (acc.fdSelect != &Null (CFF2FDSelect)) + { + c->push (); + if (likely (hb_serialize_cff_fdselect (c, plan.num_glyphs, + *(const FDSelect *)acc.fdSelect, + plan.orig_fdcount, + plan.subset_fdselect_format, + plan.subset_fdselect_size, + plan.subset_fdselect_ranges))) + plan.info.fd_select.link = c->pop_pack (); + else + { + c->pop_discard (); + return_trace (false); + } + } + else + { + // Create a range-based FDSelect3 mapping all glyphs to FD 0 + // Format: format(1) + nRanges(2) + range(3) + sentinel(2) = 8 bytes + c->push (); + + // Format byte + HBUINT8 format; + format = 3; + if (unlikely (!c->embed (format))) + { + c->pop_discard (); + return_trace (false); + } + + // nRanges + HBUINT16 nRanges; + nRanges = 1; + if (unlikely (!c->embed (nRanges))) + { + c->pop_discard (); + return_trace (false); + } + + // Single range: {first: 0, fd: 0} + FDSelect3_Range range; + range.first = 0; + range.fd = 0; + if (unlikely (!c->embed (range))) + { + c->pop_discard (); + return_trace (false); + } + + // Sentinel (number of glyphs) + HBUINT16 sentinel; + sentinel = plan.num_glyphs; + if (unlikely (!c->embed (sentinel))) + { + c->pop_discard (); + return_trace (false); + } + + plan.info.fd_select.link = c->pop_pack (); + } + + // 5. Charset (CID charset for identity mapping) + objidx_t charset_link; + if (!_serialize_cff1_charset (c, plan.num_glyphs, charset_link)) + return_trace (false); + + // 6. Global Subrs + { + auto *dest = c->push (); + if (likely (dest->serialize (c, plan.subset_globalsubrs))) + c->pop_pack (false); + else + { + c->pop_discard (); + return_trace (false); + } + } + + // 7. String INDEX - Add "Adobe" and "Identity" for ROS operator + { + const char *adobe_str = "Adobe"; + const char *identity_str = "Identity"; + unsigned adobe_len = 5; // strlen("Adobe") + unsigned identity_len = 8; // strlen("Identity") + + // Build strings array + hb_vector_t strings; + strings.alloc (2); + strings.push (hb_ubytes_t ((const unsigned char *) adobe_str, adobe_len)); + strings.push (hb_ubytes_t ((const unsigned char *) identity_str, identity_len)); + + // Serialize as CFF INDEX + auto *dest = c->push (); + if (likely (dest->serialize (c, strings))) + c->pop_pack (false); + else + { + c->pop_discard (); + return_trace (false); + } + } + + // 8. CFF Header + OT::cff1 *cff = c->allocate_min (); + if (unlikely (!cff)) return_trace (false); + + /* header */ + cff->version.major = 0x01; + cff->version.minor = 0x00; + cff->nameIndex = cff->min_size; + cff->offSize = 4; /* unused? */ + + // 9. Name INDEX (single entry) + { + unsigned name_len = strlen (CFF1_DEFAULT_FONT_NAME); + + CFF1Index *idx = c->start_embed (); + if (unlikely (!idx)) return_trace (false); + + if (unlikely (!idx->serialize_header (c, hb_iter (&name_len, 1), name_len))) + return_trace (false); + + if (unlikely (!c->embed (CFF1_DEFAULT_FONT_NAME, name_len))) + return_trace (false); + } + + // 10. Top DICT INDEX + { + // Serialize the Top DICT data first + c->push (); + cff1_from_cff2_top_dict_op_serializer_t topSzr; + + // Serialize ROS first + if (unlikely (!topSzr.serialize_ros (c))) + { + c->pop_discard (); + return_trace (false); + } + + // Serialize FontBBox from head table + { + str_buff_t bbox_buff; + str_encoder_t encoder (bbox_buff); + + encoder.encode_int (plan.head_maxp_info.xMin); + encoder.encode_int (plan.head_maxp_info.yMin); + encoder.encode_int (plan.head_maxp_info.xMax); + encoder.encode_int (plan.head_maxp_info.yMax); + encoder.encode_op (OpCode_FontBBox); + + if (encoder.in_error () || !c->embed (bbox_buff.as_bytes ().arrayZ, bbox_buff.length)) + { + c->pop_discard (); + return_trace (false); + } + } + + // Serialize charset operator + if (charset_link && unlikely (!FontDict::serialize_link4_op (c, OpCode_charset, charset_link, whence_t::Absolute))) + { + c->pop_discard (); + return_trace (false); + } + + // Serialize FDSelect operator (required for CID-keyed CFF1 fonts) + if (plan.info.fd_select.link && unlikely (!FontDict::serialize_link4_op (c, OpCode_FDSelect, plan.info.fd_select.link, whence_t::Absolute))) + { + c->pop_discard (); + return_trace (false); + } + + // Serialize FDArray operator (required for CID-keyed CFF1 fonts) + if (plan.info.fd_array_link && unlikely (!FontDict::serialize_link4_op (c, OpCode_FDArray, plan.info.fd_array_link, whence_t::Absolute))) + { + c->pop_discard (); + return_trace (false); + } + + // Serialize other operators from CFF2 TopDict + for (const auto &opstr : cff2_topDict.values) + { + if (unlikely (!topSzr.serialize (c, opstr, plan.info))) + { + c->pop_discard (); + return_trace (false); + } + } + + unsigned top_size = c->length (); + c->pop_pack (false); + + // Serialize INDEX header + auto *dest = c->start_embed (); + if (unlikely (!dest->serialize_header (c, hb_iter (&top_size, 1), top_size))) + return_trace (false); + } + + return_trace (true); +} + +} /* namespace CFF */ + static bool _serialize_cff2_charstrings (hb_serialize_context_t *c, cff2_subset_plan &plan, const OT::cff2::accelerator_subset_t &acc) @@ -669,6 +1229,38 @@ OT::cff2::accelerator_subset_t::subset (hb_subset_context_t *c) const cff2_subset_plan cff2_plan; if (unlikely (!cff2_plan.create (*this, c->plan))) return false; + + // If instantiating (pinned) and downgrade flag is set, convert to CFF1 + if (cff2_plan.pinned && (c->plan->flags & HB_SUBSET_FLAGS_DOWNGRADE_CFF2)) + { + // Serialize CFF1 to the subsetter's serializer + // If we run out of room, returning true will cause subsetter to retry with larger buffer + bool result = CFF::serialize_cff2_to_cff1 (c->serializer, cff2_plan, topDict, *this); + + if (c->serializer->ran_out_of_room ()) + return true; // Subsetter will retry with larger buffer + + if (result && !c->serializer->in_error ()) + { + // Success - end serialization to resolve links + c->serializer->end_serialize (); + + // Copy the serialized CFF1 data and add as CFF table + hb_blob_t *cff_blob = c->serializer->copy_blob (); + if (cff_blob) + { + c->plan->add_table (HB_TAG('C','F','F',' '), cff_blob); + hb_blob_destroy (cff_blob); + + // Return false to signal CFF2 table is not needed + return false; + } + } + + // Conversion failed - don't fall back, fail hard for debugging + return false; + } + return serialize (c->serializer, cff2_plan, c->plan->normalized_coords.as_array ()); } diff --git a/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc b/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc index 11aefded2c..03c59dc80a 100644 --- a/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc +++ b/thirdparty/harfbuzz/src/hb-subset-instancer-solver.cc @@ -407,9 +407,19 @@ rebase_tent (Triple tent, Triple axisLimit, TripleDistances axis_triple_distance rebase_tent_result_t &out, rebase_tent_result_t &scratch) { - assert (-1.0 <= axisLimit.minimum && axisLimit.minimum <= axisLimit.middle && axisLimit.middle <= axisLimit.maximum && axisLimit.maximum <= +1.0); - assert (-2.0 <= tent.minimum && tent.minimum <= tent.middle && tent.middle <= tent.maximum && tent.maximum <= +2.0); - assert (tent.middle != 0.0); + if (unlikely (!(-1.0 <= axisLimit.minimum && + axisLimit.minimum <= axisLimit.middle && + axisLimit.middle <= axisLimit.maximum && + axisLimit.maximum <= +1.0) || + !(-2.0 <= tent.minimum && + tent.minimum <= tent.middle && + tent.middle <= tent.maximum && + tent.maximum <= +2.0) || + tent.middle == 0.0)) + { + out.reset (); + return; + } rebase_tent_result_t &sols = scratch; _solve (tent, axisLimit, sols); diff --git a/thirdparty/harfbuzz/src/hb-subset.h b/thirdparty/harfbuzz/src/hb-subset.h index bc55b6d0b8..2b9b2a2263 100644 --- a/thirdparty/harfbuzz/src/hb-subset.h +++ b/thirdparty/harfbuzz/src/hb-subset.h @@ -84,6 +84,9 @@ typedef struct hb_subset_plan_t hb_subset_plan_t; * HB_SUBSET_FLAGS_RETAIN_GIDS then the number of glyphs in the font won't * be reduced as a result of subsetting. If necessary empty glyphs will be * included at the end of the font to keep the number of glyphs unchanged. + * @HB_SUBSET_FLAGS_DOWNGRADE_CFF2: If set and instantiating a variable font, + * convert the output CFF2 table to CFF1. This enables compatibility with older + * renderers that don't support CFF2. Since: REPLACEME * * List of boolean properties that can be configured on the subset input. * @@ -107,6 +110,7 @@ typedef enum { /*< flags >*/ HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00001000u, HB_SUBSET_FLAGS_RETAIN_NUM_GLYPHS = 0x00002000u, #endif + HB_SUBSET_FLAGS_DOWNGRADE_CFF2 = 0x00004000u, } hb_subset_flags_t; /** diff --git a/thirdparty/harfbuzz/src/hb-ucd-table.hh b/thirdparty/harfbuzz/src/hb-ucd-table.hh index 868428ab1e..d2d23cf59b 100644 --- a/thirdparty/harfbuzz/src/hb-ucd-table.hh +++ b/thirdparty/harfbuzz/src/hb-ucd-table.hh @@ -763,1716 +763,1712 @@ static const uint64_t _hb_ucd_dm2_u64_map[408]= #include -static const uint8_t _hb_ucd_u8[19868]= +static const uint8_t _hb_ucd_u8[19845]= { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 27, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 28, 26, 29, 30, 31, 32, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 33, 34, 34, 34, 34, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 26, 56, 57, 58, 58, 58, 58, 59, 26, 26, 60, 26, 26, 26, 26, 26, - 26, 61, 26, 62, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 63, 58, 58, 58, 26, 64, 65, 66, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 67, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 68, 69, 70, 58, 58, 58, 58, 71, 58, - 58, 58, 58, 58, 58, 58, 72, 73, 74, 75, 76, 77, 78, 79, 58, 80, - 81, 82, 83, 84, 85, 58, 86, 87, 88, 89, 78, 90, 91, 92, 58, 58, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 93, 26, 26, 26, 26, 26, 26, 26, 26, 94, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 95, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 96, 26, 97, 58, 58, 58, 58, 26, 98, 58, 58, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 99, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,100, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 101, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,102, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,103, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, - 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, - 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, - 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, - 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, - 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, - 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, - 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, - 16, 16, 36, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 40, 40, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, - 39, 39, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, - 42, 42, 42, 42, 42, 42, 42, 42, 32, 32, 41, 32, 43, 44, 16, 10, - 43, 43, 40, 45, 11, 46, 46, 11, 34, 11, 11, 11, 11, 11, 11, 11, - 11, 47, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, - 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 48, 34, 32, 34, 11, - 32, 49, 42, 42, 50, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, - 47, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 46, 51, 2, 2, 2, - 16, 16, 16, 16, 52, 53, 54, 55, 56, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 57, 58, 59, 42, 58, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 61, - 36, 62, 63, 43, 43, 43, 43, 43, 64, 64, 64, 8, 9, 65, 2, 66, - 42, 42, 42, 42, 42, 59, 67, 2, 68, 36, 36, 36, 36, 69, 42, 42, - 7, 7, 7, 7, 7, 2, 2, 36, 70, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 71, 42, 42, 42, 72, 49, 42, 42, 73, 74, 75, 42, 42, 36, - 7, 7, 7, 7, 7, 36, 76, 77, 2, 2, 2, 2, 2, 2, 2, 78, - 69, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 79, 61, 36, - 36, 36, 36, 42, 42, 42, 42, 42, 70, 43, 43, 43, 43, 43, 43, 43, - 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, - 42, 42, 39, 21, 2, 80, 56, 20, 36, 36, 36, 42, 42, 74, 42, 42, - 42, 42, 74, 42, 74, 42, 42, 43, 2, 2, 2, 2, 2, 2, 2, 63, - 36, 36, 36, 36, 69, 42, 43, 63, 36, 36, 36, 36, 36, 60, 43, 43, - 36, 36, 36, 36, 81, 36, 36, 36, 64, 43, 43, 56, 42, 42, 42, 42, - 36, 36, 36, 36, 82, 42, 42, 42, 42, 83, 42, 42, 42, 42, 42, 42, - 42, 84, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 84, 70, 85, - 86, 42, 42, 42, 84, 85, 86, 85, 69, 42, 42, 42, 36, 36, 36, 36, - 36, 42, 2, 7, 7, 7, 7, 7, 87, 36, 36, 36, 36, 36, 36, 36, - 69, 85, 61, 36, 36, 36, 60, 61, 60, 61, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 60, 36, 36, 36, 60, 60, 43, 36, 36, 43, 70, 85, - 86, 42, 79, 88, 89, 88, 86, 60, 43, 43, 43, 88, 43, 43, 36, 61, - 36, 42, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 55, 62, 79, - 56, 84, 61, 36, 36, 60, 43, 61, 60, 36, 61, 60, 36, 43, 79, 85, - 86, 79, 43, 56, 79, 56, 42, 43, 56, 43, 43, 43, 61, 36, 60, 60, - 43, 43, 43, 7, 7, 7, 7, 7, 42, 36, 69, 63, 43, 43, 43, 43, - 56, 84, 61, 36, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, - 60, 36, 61, 36, 36, 43, 70, 85, 86, 42, 42, 56, 84, 88, 86, 43, - 60, 43, 43, 43, 43, 43, 43, 43, 65, 43, 43, 43, 61, 42, 42, 42, - 56, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 43, 70, 86, - 86, 42, 79, 88, 89, 88, 86, 43, 43, 43, 56, 84, 43, 43, 36, 61, - 77, 27, 27, 27, 43, 43, 43, 43, 43, 70, 61, 36, 36, 60, 43, 36, - 60, 36, 36, 43, 61, 60, 60, 36, 43, 61, 60, 43, 36, 60, 43, 36, - 36, 36, 36, 36, 36, 43, 43, 85, 84, 89, 43, 85, 89, 85, 86, 43, - 60, 43, 43, 88, 43, 43, 43, 43, 27, 90, 66, 66, 55, 91, 43, 43, - 84, 85, 70, 36, 36, 36, 60, 36, 60, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 43, 70, 42, 84, 85, 89, 42, 79, 42, 42, 43, - 43, 43, 56, 79, 36, 60, 36, 43, 43, 43, 43, 92, 27, 27, 27, 90, - 69, 85, 71, 36, 36, 36, 60, 36, 36, 36, 61, 36, 36, 43, 70, 86, - 85, 85, 89, 84, 89, 85, 42, 43, 43, 43, 88, 89, 43, 43, 36, 60, - 61, 93, 43, 43, 43, 43, 43, 43, 42, 85, 36, 36, 36, 36, 60, 36, - 36, 36, 36, 36, 36, 69, 70, 85, 86, 42, 79, 85, 89, 85, 86, 76, - 43, 43, 36, 93, 27, 27, 27, 94, 27, 27, 27, 27, 90, 36, 36, 36, - 56, 85, 61, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 36, 36, 36, - 36, 61, 36, 36, 36, 36, 61, 43, 36, 36, 36, 60, 43, 79, 43, 88, - 85, 42, 79, 79, 85, 85, 85, 85, 43, 85, 63, 43, 43, 43, 43, 43, - 61, 36, 36, 36, 36, 36, 36, 36, 69, 36, 42, 42, 42, 79, 43, 95, - 36, 36, 36, 74, 42, 42, 42, 59, 7, 7, 7, 7, 7, 2, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 61, 60, 60, 36, 36, 60, 36, 36, - 36, 36, 61, 61, 36, 36, 36, 36, 69, 36, 42, 42, 42, 42, 70, 43, - 36, 36, 60, 80, 42, 42, 42, 79, 7, 7, 7, 7, 7, 43, 36, 36, - 76, 66, 2, 2, 2, 2, 2, 2, 2, 96, 96, 66, 42, 66, 66, 66, - 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 49, 49, 49, 4, 4, 85, - 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, - 56, 42, 42, 42, 42, 42, 42, 84, 42, 42, 59, 42, 36, 36, 69, 42, - 42, 42, 42, 42, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 79, 66, - 66, 66, 66, 75, 66, 66, 91, 66, 2, 2, 96, 66, 21, 63, 43, 43, - 36, 36, 36, 36, 36, 93, 86, 42, 84, 42, 42, 42, 86, 84, 86, 70, - 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 85, 42, 36, 36, 42, - 70, 85, 97, 93, 85, 85, 85, 36, 69, 42, 70, 36, 36, 36, 36, 36, - 36, 84, 86, 84, 85, 85, 86, 93, 7, 7, 7, 7, 7, 85, 86, 66, - 11, 11, 11, 47, 43, 43, 47, 43, 16, 16, 16, 16, 16, 52, 44, 16, - 36, 36, 36, 36, 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, - 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, 36, 36, 36, 36, - 36, 36, 36, 60, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 56, 42, - 2, 2, 2, 2, 98, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, - 66, 66, 66, 66, 66, 43, 43, 43, 11, 11, 11, 43, 16, 16, 16, 43, - 100, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 71, - 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,102,103, 43, - 36, 36, 36, 36, 36, 62, 2,104,105, 36, 36, 36, 60, 43, 43, 43, - 36, 42, 84, 43, 43, 43, 43, 61, 36, 42,106, 63, 43, 43, 43, 43, - 36, 42, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 60, 36, - 60, 42, 43, 43, 43, 43, 43, 43, 36, 36, 42, 86, 42, 42, 42, 85, - 85, 85, 85, 84, 86, 42, 42, 42, 42, 42, 2, 87, 2, 65, 69, 43, - 7, 7, 7, 7, 7, 43, 43, 43, 27, 27, 27, 27, 27, 43, 43, 43, - 2, 2, 2,107, 2, 58, 42, 83, 36, 82, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 60, 43, 43, 43, 36, 36, 69, 70, 36, 36, 36, 36, - 36, 36, 36, 36, 69, 60, 43, 43, 36, 36, 36, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 60, 42, 84, 85, 86, 84, 85, 43, 43, - 85, 84, 85, 85, 86, 42, 43, 43, 91, 43, 2, 7, 7, 7, 7, 7, - 36, 36, 36, 36, 36, 36, 36, 43, 36, 36, 60, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 43, 43, 36, 36, 36, 36, 36, 43, 43, 43, - 7, 7, 7, 7, 7, 99, 43, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 36, 36, 36, 69, 84, 86, 43, 2, 36, 36, 93, 84, 42, 42, 42, 79, - 84, 84, 86, 42, 42, 42, 84, 85, 85, 86, 42, 42, 42, 42, 79, 56, - 2, 2, 2, 87, 2, 2, 2, 43, 42, 42, 42, 42, 42, 42, 42,108, - 42, 42, 42, 42, 42, 42, 42, 43, 42, 42, 42, 42, 42, 42, 43, 43, - 42, 42, 97, 36, 36, 36, 36, 36, 36, 36, 84, 42, 42, 84, 84, 85, - 85, 84, 97, 36, 36, 36, 60, 2, 96, 66, 66, 66, 66, 49, 42, 42, - 42, 42, 66, 66, 66, 66, 21, 2, 42, 97, 36, 36, 36, 36, 36, 36, - 93, 42, 42, 85, 42, 86, 42, 36, 36, 36, 36, 84, 42, 85, 86, 86, - 42, 85, 43, 43, 43, 43, 2, 2, 36, 36, 85, 85, 85, 85, 42, 42, - 42, 42, 85, 42, 43, 92, 2, 2, 7, 7, 7, 7, 7, 43, 61, 36, - 36, 36, 36, 36, 39, 39, 39, 2, 16, 16, 16, 16, 34,109, 43, 43, - 11, 11, 11, 11, 11, 46, 47, 11, 2, 2, 2, 2, 43, 43, 43, 43, - 42, 59, 42, 42, 42, 42, 42, 42, 84, 42, 42, 42, 70, 36, 69, 36, - 36, 36, 70, 93, 42, 60, 43, 43, 16, 16, 16, 16, 16, 16, 39, 39, - 39, 39, 39, 39, 39, 44, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, - 16, 16, 16, 16, 16,110, 39, 39, 32, 32, 32, 16, 16, 16, 16, 32, - 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 43, 11, 11, 11, 43, - 16, 16, 16, 16, 47, 47, 47, 47, 16, 16, 16, 16, 16, 16, 16, 43, - 16, 16, 16, 16,111,111,111,111, 16, 16,109, 16, 11, 11,112,113, - 40, 16,109, 16, 11, 11,112, 40, 16, 16, 43, 16, 11, 11,114, 40, - 16, 16, 16, 16, 11, 11,115, 40, 43, 16,109, 16, 11, 11,112,116, - 117,117,117,117,117,118, 64, 64,119,119,119, 2,120,121,120,121, - 2, 2, 2, 2,122, 64, 64,123, 2, 2, 2, 2,124,125, 2,126, - 127, 2,128,129, 2, 2, 2, 2, 2, 9,127, 2, 2, 2, 2,130, - 64, 64,131, 64, 64, 64, 64, 64,132, 43, 27, 27, 27, 8,128,133, - 27, 27, 27, 27, 27, 8,128,103, 39, 39, 39, 39, 39, 39, 80, 43, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43, 43, - 42, 42, 42, 42, 42, 42,134, 50,108, 50,108, 42, 42, 42, 42, 42, - 79, 43, 43, 43, 43, 43, 43, 43, 66,135, 66,136, 66, 34, 11, 16, - 11, 32,136, 66, 48, 11, 11, 66, 66, 66,135,135,135, 11, 11,137, - 11, 11, 35, 36,138, 66, 16, 11, 8, 8, 48, 16, 16, 26, 66,139, - 27, 27, 27, 27, 27, 27, 27, 27,104,104,104,104,104,104,104,104, - 104,140,141,104,142, 66, 43, 43, 8, 8,143, 66, 66, 8, 66, 66, - 143, 26, 66,143, 66, 66, 66,143, 66, 66, 66, 66, 66, 66, 66, 8, - 66,143,143, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 66, 66, 66, 66, 4, 4, 66, 66, - 8, 66, 66, 66,144,145, 66, 66, 66, 66, 66, 66, 66, 66,143, 66, - 66, 66, 66, 66, 66, 26, 8, 8, 8, 8, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 8, 8, 8, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 91, 43, 43, 27, 27, 27, 27, 27, 27, 66, 66, - 66, 66, 66, 66, 66, 27, 27, 27, 66, 66, 66, 26, 66, 66, 66, 66, - 26, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, - 66, 66, 66, 66, 66, 66, 66, 26, 66, 66, 66, 66, 4, 4, 4, 4, - 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, - 8, 8,128,146, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, - 8,128,147,147,147,147,147,147,147,147,147,147,146, 8, 8, 8, - 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, - 8, 8,143, 26, 8, 8,143, 66, 66, 66, 43, 66, 66, 66, 66, 66, - 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 39, 11, - 32, 32,139, 66, 66,136, 34,148, 42, 32, 43, 43, 92, 2, 98, 2, - 16, 16, 16,149, 43, 43,149, 43, 36, 36, 36, 36, 43, 43, 43, 51, - 63, 43, 43, 43, 43, 43, 43, 56, 36, 36, 36, 60, 43, 43, 43, 43, - 36, 36, 36, 60, 36, 36, 36, 60, 2,120,120, 2,124,125,120, 2, - 2, 2, 2, 6, 2,107,120, 2,120, 4, 4, 4, 4, 2, 2, 87, - 2, 2, 2, 2, 2,119, 2, 2,107,150, 2, 2, 2, 2, 2, 2, - 66, 2,151,147,147,147,152, 43, 66, 66, 66, 66, 66, 54, 66, 66, - 66, 66, 43, 43, 43, 43, 43, 43, 66, 66, 66, 43, 43, 43, 43, 43, - 1, 2,153,154, 4, 4, 4, 4, 4, 66, 4, 4, 4, 4,155,156, - 157,104,104,104,104, 42, 42, 85,158, 39, 39, 66,104,159, 62, 66, - 36, 36, 36, 60, 56,160,161, 68, 36, 36, 36, 36, 36, 62, 39, 68, - 43, 43, 61, 36, 36, 36, 36, 36, 66, 27, 27, 66, 66, 66, 66, 66, - 66, 66, 66, 43, 43, 43, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, - 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, - 162, 27, 27, 27, 27, 27, 27, 27, 36, 36, 82, 36, 36, 36, 36, 36, - 66, 66, 66, 91, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,163, 2, - 7, 7, 7, 7, 7, 36, 43, 43, 32, 32, 32, 32, 32, 32, 32, 69, - 50,164, 42, 42, 42, 42, 42, 87, 32, 32, 32, 32, 32, 32, 39, 42, - 36, 36, 36,104,104,104,104,104, 42, 2, 2, 2, 43, 43, 43, 43, - 40, 40, 40,161, 39, 39, 39, 39, 40, 32, 32, 32, 32, 32, 32, 32, - 16, 32, 32, 32, 32, 32, 32, 32, 44, 16, 16, 16, 34, 34, 34, 32, - 32, 32, 32, 32, 41,165, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 46, 43, - 51, 39,166, 35, 39, 35, 36, 36, 36, 70, 36, 70, 36, 69, 36, 36, - 36, 93, 86, 84, 66, 66, 79, 43, 27, 27, 27, 66,167, 43, 43, 43, - 36, 36, 2, 2, 43, 43, 43, 43, 85, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 85, 85, 85, 85, 85, 85, 85, 85, 42, 43, 43, 43, 43, 2, - 42, 36, 36, 36, 2, 71, 71, 69, 36, 36, 36, 42, 42, 42, 42, 2, - 36, 36, 36, 69, 42, 42, 42, 42, 42, 85, 43, 43, 43, 43, 43, 92, - 36, 69, 85, 42, 42, 85, 42, 85,106, 2, 2, 2, 2, 2, 2, 51, - 7, 7, 7, 7, 7, 43, 43, 2, 36, 36, 69, 68, 36, 36, 36, 36, - 7, 7, 7, 7, 7, 36, 36, 60, 36, 36, 36, 36, 69, 42, 42, 84, - 86, 84, 86, 79, 43, 43, 43, 43, 36, 69, 36, 36, 36, 36, 84, 43, - 7, 7, 7, 7, 7, 43, 2, 2, 68, 36, 36, 76, 66, 93, 84, 36, - 70, 42, 70, 69, 70, 36, 36, 42, 69, 60, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 61, 82, 2, 36, 36, 36, 36, 36, 93, 42, 85, - 2, 82,168, 79, 43, 43, 43, 43, 61, 36, 36, 60, 61, 36, 36, 60, - 61, 36, 36, 60, 43, 43, 43, 43, 16, 16, 16, 16, 16,113, 39, 39, - 16, 16, 16, 16,110, 40, 43, 43, 36, 93, 86, 85, 84,106, 86, 43, - 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 43, 61, 36, 36, - 169,169,169,169,169,169,169,169,170,170,170,170,170,170,170,170, - 16, 16, 16,109, 43, 43, 43, 43, 43,149, 16, 16, 43, 43, 61, 70, - 36, 36, 36, 36,171, 36, 36, 36, 36, 36, 36, 60, 36, 36, 60, 60, - 36, 61, 60, 36, 36, 36, 36, 36, 36, 40, 40, 40, 40, 40, 40, 40, - 40, 22, 66, 66, 66, 66, 66, 66, 66, 77, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36,147, 66, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 66, 66, 66, 66, 36, 36, 36, 36, 36, 36,167, 66, - 2, 2, 2,151,129, 43, 43, 43, 6,172,173,147,147,147,147,147, - 147,147,129,151,129, 2,126,174, 2, 63, 2, 2,155,147,147,129, - 2,175, 8,176, 65, 2, 43, 43, 36, 36, 60, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 60, 78, 92, 2, 3, 2, 4, 5, 6, 2, - 16, 16, 16, 16, 16, 17, 18,128,129, 4, 2, 36, 36, 36, 36, 36, - 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, - 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 60, 43, - 20,177, 55,178, 26, 8,143, 91, 43, 43, 43, 43, 78, 64, 66, 43, - 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 60, 36, 61, - 2, 63, 43,179, 27, 27, 27, 27, 27, 27, 43, 54, 66, 66, 66, 66, - 104,104,142, 27, 90, 66, 66, 66, 66, 66, 66, 66, 66, 27, 66, 91, - 66, 66, 66, 66, 66, 66, 91, 43, 91, 43, 43, 43, 43, 43, 43, 43, - 66, 66, 66, 66, 66, 66, 49, 43,180, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 43, 43, 27, 27, 43, 43, 43, 43, 61, 36, - 154, 36, 36, 36, 36,181, 43, 43, 36, 36, 36, 42, 42, 79, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 92, 36, 36, 43, 43, 36, 36, 36, 36, - 182,104,104, 43, 43, 43, 43, 43, 11, 11, 11, 11, 16, 16, 16, 16, - 11, 11, 43, 43, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 43, 43, - 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, 43, 92, - 11, 11, 11, 11, 11, 46, 11, 11, 11, 46, 11,149, 16, 16, 16, 16, - 16,149, 16, 16, 16, 16, 16, 16, 16,149, 16, 16, 16,149,109, 43, - 39, 39, 39, 51, 39, 39, 39, 39, 80, 39, 39, 39, 39, 80, 43, 43, - 36, 36, 36, 43, 60, 36, 36, 36, 36, 36, 36, 61, 60, 43, 60, 61, - 36, 36, 36, 92, 27, 27, 27, 27, 36, 36, 36, 76,162, 27, 27, 27, - 43, 43, 43,179, 27, 27, 27, 27, 36, 60, 36, 43, 43,179, 27, 27, - 36, 36, 36, 27, 27, 27, 43, 92, 36, 36, 36, 36, 36, 43, 43, 92, - 36, 36, 36, 36, 43, 43, 27, 36, 43, 27, 27, 27, 27, 27, 27, 27, - 69, 42, 56, 79, 43, 43, 42, 42, 36, 36, 61, 36, 61, 36, 36, 36, - 36, 36, 36, 43, 42, 79, 43, 56, 27, 27, 27, 27, 99, 43, 43, 43, - 2, 2, 2, 2, 63, 43, 43, 43, 36, 36, 36, 36, 36, 36,183, 30, - 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 77, 36, 36, 36, - 36, 36, 69, 79, 43,179, 27, 27, 2, 2, 2, 63, 43, 43, 43, 43, - 36, 36, 36, 43, 92, 2, 2, 2, 36, 36, 36, 43, 27, 27, 27, 27, - 36, 60, 43, 43, 27, 27, 27, 27, 36, 43, 43, 43, 92, 2, 63, 43, - 43, 43, 43, 43,179, 27, 27, 27, 11, 46, 43, 43, 43, 43, 43, 43, - 16,109, 43, 43, 43, 27, 27, 27, 36, 36, 42, 42, 43, 43, 43, 43, - 7, 7, 7, 7, 7, 36, 36, 68, 11, 11, 11, 43, 56, 42, 42,158, - 16, 16, 16, 43, 43, 43, 43, 8, 27, 27, 27, 27, 27, 27, 27, 99, - 36, 36, 36, 36, 36, 56,184, 43, 36, 43, 43, 43, 43, 43, 43, 43, - 43, 36, 82, 36, 43, 43, 43, 43, 96, 66, 66, 66, 91, 43, 43, 43, - 43, 43, 43, 43, 43, 42, 42, 42, 27, 27, 27, 94, 43, 43, 43, 43, - 180, 27, 30, 2, 2, 43, 43, 43, 36, 42, 42, 2, 2, 43, 43, 43, - 36, 36,183, 27, 27, 27, 43, 43, 86, 97, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 42, 59, 2, 2, 2, 43, - 27, 27, 27, 7, 7, 7, 7, 7, 70, 69, 70, 43, 43, 43, 43, 56, - 85, 86, 42, 84, 86, 59,185, 2, 2, 79, 43, 43, 43, 43, 78, 43, - 42, 70, 36, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, 86, 42, - 42, 42, 79, 7, 7, 7, 7, 7, 2, 2, 93, 97, 43, 43, 43, 43, - 36, 69, 2, 60, 43, 43, 43, 43, 36, 93, 85, 42, 42, 42, 42, 84, - 97, 36, 62, 2, 58, 42, 59, 86, 7, 7, 7, 7, 7, 62, 62, 2, - 179, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 85, 86, 42, 85, 84, 42, 2, 2, 2, 70, - 69, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 60, 36, 36, 61, - 36, 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 62, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 69, 85, 86, 42, 42, 42, 79, 43, 43, - 42, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 56, 70, 85, - 84, 85, 89, 88, 89, 88, 85, 43, 60, 43, 43, 88, 43, 43, 61, 36, - 36, 85, 43, 42, 42, 42, 79, 43, 42, 42, 79, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 61, 43, 60, 36, 36, 36, 61, 85, 86, 42, 42, - 79, 89, 88, 88, 85, 89, 85, 84, 70, 70, 2, 92, 63, 43, 43, 43, - 56, 79, 43, 43, 43, 43, 43, 43, 36, 36, 93, 85, 42, 42, 42, 42, - 85, 42, 84, 70, 36, 62, 2, 2, 7, 7, 7, 7, 7, 2, 92, 70, - 85, 86, 42, 42, 84, 84, 85, 86, 84, 42, 36, 71, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 93, 85, 42, 42, 43, 85, 85, 42, 86, - 59, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 42, 43, - 85, 86, 42, 42, 42, 84, 86, 86, 59, 2, 60, 43, 43, 43, 43, 43, - 2, 2, 2, 2, 2, 2, 63, 43, 36, 36, 36, 36, 36, 69, 86, 85, - 42, 42, 42, 86, 62, 43, 43, 43, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 56, 86, - 85, 42, 42, 86, 42, 42, 43, 43, 7, 7, 7, 7, 7, 27, 2, 96, - 42, 42, 42, 42, 86, 59, 43, 43, 27, 99, 43, 43, 43, 43, 43, 61, - 36, 36, 36, 60, 61, 43, 36, 36, 36, 36, 61, 60, 36, 36, 36, 36, - 85, 85, 85, 88, 89, 56, 84, 70, 97, 86, 2, 63, 43, 43, 43, 43, - 36, 36, 36, 36, 43, 36, 36, 36, 93, 85, 42, 42, 43, 42, 85, 85, - 70, 71, 89, 43, 43, 43, 43, 43, 69, 42, 42, 42, 42, 70, 36, 36, - 36, 69, 42, 42, 84, 69, 42, 59, 2, 2, 2, 58, 43, 43, 43, 43, - 69, 42, 42, 84, 86, 42, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, - 42, 42, 42, 84, 42, 2, 71, 2, 2, 63, 43, 43, 43, 43, 43, 43, - 2, 2, 2, 2, 2, 43, 43, 43, 84, 42, 84, 84, 43, 43, 43, 43, - 62, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 42, 42, 42, 86, - 62, 2, 2, 43, 43, 43, 43, 43, 2, 36, 36, 36, 36, 36, 36, 36, - 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 88, 42, 42, 42, - 84, 42, 86, 79, 43, 43, 43, 43, 36, 36, 36, 60, 36, 61, 36, 36, - 69, 42, 42, 79, 43, 79, 42, 56, 42, 42, 42, 69, 43, 43, 43, 43, - 36, 36, 36, 61, 60, 36, 36, 36, 36, 36, 36, 36, 36, 85, 85, 89, - 42, 88, 86, 86, 60, 43, 43, 43, 36, 36, 36, 36, 82, 36, 43, 43, - 36, 69, 84,106, 63, 43, 43, 43, 42, 93, 36, 36, 36, 36, 36, 36, - 36, 36, 85, 42, 42, 79, 43, 85, 84, 59, 2, 2, 2, 2, 2, 2, - 7, 7, 7, 7, 7, 79, 43, 43, 27, 27, 90, 66, 66, 66, 55, 20, - 167, 66, 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 43, 43, 92, - 104,104,104,104,104,104,104,181, 2, 2, 63, 43, 43, 43, 43, 43, - 62, 63, 43, 43, 43, 43, 43, 43, 64, 64, 64, 64, 64, 64, 64, 64, - 70, 36, 36, 69, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 85, 86, 42, - 42, 42, 59, 43, 43, 43, 43, 43, 42, 42, 42, 59, 2, 2, 66, 66, - 39, 39, 96, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7,179, 27, 27, - 27, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 61, 36, - 39, 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82,163, 2, - 27, 27, 27, 30, 2, 63, 43, 43, 11, 11, 11, 11, 46,149, 16, 16, - 16, 16, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 43, 56, - 93, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 43, 43, 43, 56, 42, 73, 39, 39, 39, 39, 39, 39, - 39, 87, 79, 43, 43, 43, 43, 43, 85, 39,104,181, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 61, 36, 60, 43, 43, 43, 43, 43, 43, - 39, 39, 51, 39, 39, 39, 51, 80, 43, 60, 43, 43, 43, 43, 43, 43, - 36, 60, 61, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 43, 49, 59, 64, 64, 43, 43, 43, 43, 43, 43, - 7, 7, 7, 7, 7, 66, 91, 43, 66, 66, 43, 43, 43, 66, 66, 66, - 176, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 43, 43, 43, 43, - 66, 66, 66, 91, 54, 66, 66, 66, 66, 66,186, 86, 42, 66,186, 85, - 85,187, 64, 64, 64, 83, 42, 42, 42, 75, 49, 42, 42, 42, 66, 66, - 66, 66, 66, 66, 66, 42, 42, 66, 66, 42, 75, 43, 43, 43, 43, 43, - 27, 27, 43, 43, 43, 43, 43, 43, 11, 11, 11, 11, 11, 16, 16, 16, - 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, - 16, 16,109, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 46, 11, 43, 46, 47, 46, 47, 11, 46, 11, - 11, 11, 11, 16, 16,149,149, 16, 16, 16,149, 16, 16, 16, 16, 16, - 16, 16, 11, 47, 11, 46, 47, 11, 11, 11, 46, 11, 11, 11, 46, 16, - 16, 16, 16, 16, 11, 47, 11, 46, 11, 11, 46, 46, 43, 11, 11, 11, - 46, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, - 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 43, 11, 11, 11, 11, - 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, - 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, - 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, - 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 43, 7, - 42, 42, 42, 75, 66, 49, 42, 42, 42, 42, 42, 42, 42, 42, 75, 66, - 66, 66, 49, 66, 66, 66, 66, 66, 66, 66, 75, 21, 2, 2, 43, 43, - 43, 43, 43, 43, 43, 56, 42, 42, 16, 16, 16, 16, 16,138, 16, 16, - 16, 16, 16, 16, 16, 16, 16,109, 43, 43,149, 16, 16,109, 43, 43, - 42, 42, 42, 79, 42, 42, 42, 42, 42, 42, 42, 42, 79, 56, 42, 42, - 42, 56, 79, 42, 42, 79, 43, 43, 39, 39, 39, 39, 39, 39, 39, 43, - 43, 43, 43, 43, 43, 43, 43, 56, 42, 42, 42, 73, 39, 39, 39, 43, - 7, 7, 7, 7, 7, 43, 43, 76, 36, 36, 36, 36, 36, 36, 36, 79, - 36, 36, 36, 36, 36, 36, 42, 42, 7, 7, 7, 7, 7, 43, 43, 95, - 36, 36, 36, 36, 36, 82, 42, 42,188, 7, 7, 7, 7,189, 43, 92, - 36, 69, 36, 70, 36, 36, 36, 42, 36, 36, 69, 43, 43, 43, 43, 82, - 36, 36, 36, 60, 36, 36, 61, 60, 36, 36, 60,179, 27, 27, 27, 27, - 16, 16, 42, 42, 42, 73, 43, 43, 27, 27, 27, 27, 27, 27,162, 27, - 190, 27, 99, 43, 43, 43, 43, 43, 27, 27, 27, 27, 27, 27, 27,162, - 27, 27, 27, 27, 27, 27, 27, 43, 36, 36, 61, 36, 36, 36, 36, 36, - 61, 60, 60, 61, 61, 36, 36, 36, 36, 60, 36, 36, 61, 61, 43, 43, - 43, 60, 43, 61, 61, 61, 61, 36, 61, 60, 60, 61, 61, 61, 61, 61, - 61, 60, 60, 61, 36, 60, 36, 36, 36, 60, 36, 36, 61, 36, 60, 60, - 36, 36, 36, 36, 36, 61, 36, 36, 61, 36, 61, 36, 36, 61, 36, 36, - 8, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 66, 43, 43, - 54, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, 27, 27, 90, 66, - 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 66, 66, 66, 66, 66, - 66, 91, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 91, 43, 43, 43, - 66, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 25, 40, 40, - 66, 66, 66, 66, 91, 43, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, - 8, 8, 8, 8,176, 43, 43, 43, 66, 66, 66, 66, 66, 91, 43, 66, - 66, 66, 66, 91, 91, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, 54, - 66, 66, 66, 66, 66, 91, 43, 54, 66, 91, 66, 66, 66, 66, 66, 66, - 7, 7, 7, 7, 7, 91, 43, 43, 78, 43, 43, 43, 43, 43, 43, 43, - 170,170,170,170,170,170,170, 43,170,170,170,170,170,170,170, 0, - 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, - 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, - 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, - 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, - 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 6, 6, - 24, 24, 6, 24, 12, 12, 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, - 25, 9, 26, 12, 11, 11, 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, - 2, 12, 17, 12, 21, 12, 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, - 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, - 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, - 6, 2, 24, 7, 7, 6, 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, - 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, - 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, - 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, - 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, - 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, - 25, 22, 18, 21, 21, 29, 1, 2, 15, 6, 18, 6, 12, 11, 9, 26, - 26, 9, 26, 5, 7, 5, 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, - 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, - 18, 17, 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, - 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, - 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, - 25, 2, 25, 24, 23, 2, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, - 12, 17, 21, 1, 26, 10, 10, 1, 7, 13, 13, 2, 23, 15, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, - 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 31, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 32, 2, 33, 34, 35, 36, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 37, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 38, 39, 40, 41, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 2, 58, 59, 0, 0, 0, 0, 60, 2, 2, 61, 2, 2, 2, 2, 2, + 2, 62, 2, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 2, 65, 66, 67, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 68, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 69, 70, 71, 0, 0, 0, 0, 72, 0, + 0, 0, 0, 0, 0, 0, 73, 74, 75, 76, 77, 78, 4, 79, 0, 80, + 81, 82, 83, 84, 85, 0, 86, 87, 88, 89, 4, 90, 91, 92, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 93, 2, 2, 2, 2, 2, 2, 2, 2, 94, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 95, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 96, 2, 97, 0, 0, 0, 0, 2, 98, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, - 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, - 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, - 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, - 6, 7, 8, 0, 9, 0, 10, 11, 0, 0, 12, 13, 14, 15, 16, 0, - 0, 0, 0, 17, 18, 19, 20, 0, 21, 0, 22, 23, 0, 24, 25, 0, - 0, 24, 26, 27, 0, 24, 26, 0, 0, 24, 26, 0, 0, 24, 26, 0, - 0, 0, 26, 0, 0, 24, 28, 0, 0, 24, 26, 0, 0, 29, 26, 0, - 0, 0, 30, 0, 0, 31, 32, 0, 0, 33, 34, 0, 35, 36, 0, 37, - 38, 0, 39, 0, 0, 40, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 0, - 0, 0, 45, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 47, 0, 0, - 0, 0, 0, 0, 48, 0, 0, 49, 0, 50, 51, 52, 0, 53, 54, 55, - 0, 56, 0, 57, 0, 58, 0, 0, 0, 0, 59, 60, 0, 0, 0, 0, - 0, 0, 61, 62, 0, 0, 0, 0, 0, 0, 63, 64, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 65, 0, 0, 0, 66, - 0, 0, 0, 67, 0, 68, 0, 0, 69, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 70, 71, 0, 0, 72, 0, 0, 0, 0, - 0, 0, 0, 0, 73, 74, 0, 0, 0, 0, 54, 75, 0, 76, 77, 0, - 0, 78, 79, 0, 0, 0, 0, 0, 0, 80, 81, 82, 0, 0, 0, 0, - 0, 0, 0, 26, 0, 0, 0, 0, 0, 0, 0, 0, 83, 0, 0, 0, - 0, 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, 0, 86, 0, 0, 0, 87, - 0, 0, 0, 0, 88, 89, 0, 0, 0, 0, 0, 90, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 91, 0, 92, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 93, 0, 94, 0, 0, 95, 0, - 96, 0, 0, 0, 0, 0, 73, 97, 0, 98, 0, 0, 99,100, 0, 78, - 0, 0,101, 0, 0,102, 0, 0, 0, 0, 0,103, 0,104, 26,105, - 0, 0,106, 0, 0, 0,107, 0, 0, 0,108, 0, 0, 0, 0, 0, - 0, 66,109, 0, 0, 66, 0, 0, 0,110, 0, 0, 0,111, 0, 0, - 0, 0, 0, 0, 0, 98, 0, 0, 0, 0, 0, 0, 0,112,113, 0, - 0, 0, 0, 79, 0, 44,114, 0,115, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, - 117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,119, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,120, 0,121, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,122, 0, 0, 0, 0,123, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,124, - 125,126, 0, 0, 0, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,128,129, 0, 0,130, 0, 0, 0, 0,121, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,131, 0,132, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,133, 0, 0, 0, 0, - 0, 0, 0,134, 0, 0, 0, 0, 0, 0, 0,135, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,136, 0, 0, 0,137, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, - 1, 2, 3, 4, 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, - 14, 15, 16, 17, 18, 1, 1, 1, 0, 0, 0, 0, 19, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, - 25, 26, 27, 28, 29, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, 1, 36, 0, 0, 0, 0, - 37, 0, 0, 0, 0, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, - 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, - 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 0, 0, 0, 0, 19, 1, - 21, 0, 0, 47, 0, 0, 0, 0, 0, 38, 48, 1, 1, 49, 49, 50, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, - 0, 19, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, - 54, 21, 35, 1, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 56, - 57, 58, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 59, 0, 0, 0, 56, 0, 60, 0, 0, 0, 0, 0, 0, - 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 68, 0, 0, 0, 0, 0, 0, 69, 70, 0, 0, 0, 0, 0, - 71, 72, 73, 74, 75, 76, 0, 0, 0, 0, 0, 0, 0, 77, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, - 0, 80, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, - 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 83, 0, 0, 0, 0, 0, 0, 19, 84, 0, 62, 0, 0, 0, - 0, 49, 1, 85, 0, 0, 0, 0, 1, 52, 15, 86, 36, 10, 21, 1, - 1, 1, 1, 41, 1, 21, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 55, 0, 0, 0, 62, 0, 0, 0, 0, 0, 0, 0, 0, 19, 10, - 1, 0, 0, 0, 0, 0, 88, 0, 0, 0, 0, 0, 0, 89, 0, 0, - 88, 0, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 0, 0, 0, - 90, 9, 12, 4, 91, 8, 92, 47, 0, 58, 50, 0, 21, 1, 21, 93, - 94, 1, 1, 1, 1, 1, 1, 1, 1, 95, 96, 97, 0, 0, 0, 0, - 98, 1, 99, 58, 81,100,101, 4, 58, 0, 0, 0, 0, 0, 0, 19, - 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, 1, 1, 1, 1, - 1, 1, 1, 1, 0, 0,102,103, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,104, 0, 0, 0, 0, 19, 0, 1, 1, 50, 0, 0, 0, 0, - 0, 0, 0, 38, 0, 0, 0, 0, 50, 0, 0, 0, 0, 63, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 0, 1, 1, 1, 1, - 50, 0, 0, 0, 0, 0,105, 68, 0, 0, 0, 0, 0, 0, 0, 0, - 61, 0, 0, 0, 0, 0, 0, 0, 78, 0, 0, 0, 62, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,106,107, 58, 38, 81, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 0, 0, 0, 0, - 0, 0, 0,108, 1, 14, 4, 12, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 47, 84, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 38, 90, 0, 0, 0, 0,109, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,110, 61, 0,111, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 49, 50, 0, 0, 0, 0, 0, 0, 19, 58, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,112, 51, 0,112, 14, 52, - 84, 0, 0, 0,113, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 62, 0, 0, 61, 0, 0, 0, 0, 0, 0,114, 0, 90, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,114, 0, 0, 0, 0,115, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 78, 55, 0, 38, 1, 58, - 1, 58, 0, 0, 0, 0, 0, 88, 62, 0, 0, 0, 63, 89, 0, 0, - 0, 0, 0, 59,116, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, 0, 0, 0, 61, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 79, 78, 0, 0, 0, - 0, 0, 0, 0, 0, 61, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 56, 0, 89, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 61, 0, 0, 79, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 61, - 0, 0, 0, 0, 0, 0, 0, 0, 8, 92, 0, 0, 0, 0, 0, 0, - 1, 90, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,118, 0,119,120,121,122, 0,105, 4,123, 49, 23, 0, - 0, 0, 0, 0, 0, 0, 38, 50, 0, 0, 0, 0, 38, 58, 0, 0, - 0, 0, 0, 0, 1, 90, 1, 1, 1, 1, 39, 1, 48,106, 90, 0, - 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 59, - 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,124, - 0, 0, 0, 0, 0, 0, 0,113, 0, 0, 0, 0, 19, 59, 0, 38, - 0, 81, 0, 0, 0, 0, 0, 0, 4,123, 0, 0, 0, 1,125, 0, - 0, 0, 0, 0, 0, 0, 0, 0,230,230,230,230,230,232,220,220, - 220,220,232,216,220,220,220,220,220,202,202,220,220,220,220,202, - 202,220,220,220, 1, 1, 1, 1, 1,220,220,220,220,230,230,230, - 230,240,230,220,220,220,230,230,230,220,220, 0,230,230,230,220, - 220,220,220,230,232,220,220,230,233,234,234,233,234,234,233,230, - 0, 0, 0,230, 0,220,230,230,230,230,220,230,230,230,222,220, - 230,230,220,220,230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, - 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230, - 230,220,220,230,220,230,230,220, 35, 0, 0, 0, 0, 0,230,230, - 230, 0, 0,230,230, 0,220,230,230,220, 0, 0, 0, 36, 0, 0, - 230,220,230,230,220,220,230,220,220,230,220,230,220,230,230, 0, - 0,220, 0, 0,230,230, 0,230, 0,230,230,230,230,230, 0, 0, - 0,220,220,220,230,220,220,220,230,230, 0,220, 27, 28, 29,230, - 7, 0, 0, 0, 0, 9, 0, 0, 0,230,220,230,230, 0, 0, 0, - 0, 0,230, 0, 0, 84, 91, 0, 0, 0, 0, 9, 9, 0, 0, 0, - 0, 0, 9, 0,103,103, 9, 0,107,107,107,107,118,118, 9, 0, - 122,122,122,122,220,220, 0, 0, 0,220, 0,220, 0,216, 0, 0, - 0,129,130, 0,132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0, - 130, 0,230,230, 9, 0,230,230, 0, 0,220, 0, 0, 0, 0, 7, - 0, 9, 9, 0, 9, 9, 0, 0, 0,230, 0, 0, 0,228, 0, 0, - 0,222,230,220,220, 0, 0, 0,230, 0, 0,220,230,220, 0,220, - 230,230,230,234, 0, 0, 9, 9, 0, 0, 7, 0,230,230,230, 0, - 230, 0, 1, 1, 1, 0, 0, 0,230,234,214,220,202,230,230,230, - 230,230,232,228,228,220,218,230,233,220,230,220,230,230, 1, 1, - 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, 0,218,228, - 232,222,224,224, 0, 8, 8, 0, 0, 0, 0,220,230, 0,230,230, - 220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, - 0,230,220, 0, 0, 0,220,220, 0, 0,230,220, 0, 9, 7, 0, - 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, 0, 0, 0, 0, 1, 0, - 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0, - 220,220,220, 0,232,232,220,230,230,230, 7, 0, 16, 17, 17, 17, - 17, 17, 17, 33, 17, 17, 17, 19, 17, 17, 17, 17, 20,101, 17,113, - 129,169, 17, 27, 28, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,237, 0, 1, 2, 2, - 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 7, 8, - 9, 0, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 21, 22, 0, 0, 0, 0, - 23, 24, 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 0, 0, 0, 0, - 0, 0, 0, 33, 34, 35, 36, 0, 0, 0, 0, 0, 37, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 1, 2, 40, 41, - 0, 1, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 3, 4, 0, 0, 5, 0, - 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, - 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 10, - 0, 0, 0, 0, 0, 0, 11, 12, 0, 13, 0, 14, 15, 16, 0, 0, - 0, 0, 0, 1, 17, 18, 0, 19, 7, 1, 0, 0, 0, 20, 20, 7, - 20, 20, 20, 20, 20, 20, 20, 8, 21, 0, 22, 0, 7, 23, 24, 0, - 20, 20, 25, 0, 0, 0, 26, 27, 1, 7, 20, 20, 20, 20, 20, 1, - 28, 29, 30, 31, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 10, 0, - 0, 0, 0, 0, 0, 0, 20, 20, 20, 1, 0, 0, 8, 21, 32, 4, - 0, 10, 0, 33, 7, 20, 20, 20, 0, 0, 0, 0, 8, 34, 34, 35, - 36, 34, 37, 0, 38, 1, 20, 20, 0, 0, 39, 0, 1, 1, 0, 8, - 21, 1, 20, 0, 0, 0, 1, 0, 0, 40, 1, 1, 0, 0, 8, 21, - 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 26, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 21, 7, 20, 41, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 21, 0, 42, 43, 44, 0, 45, 0, 8, 21, 0, 0, 0, 0, 0, - 0, 0, 0, 46, 7, 1, 10, 1, 0, 0, 0, 1, 20, 20, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 34, 9, 0, 0, 20, 20, - 1, 20, 20, 0, 0, 0, 0, 0, 0, 0, 26, 21, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 47, 48, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, - 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 13, 13, 13, - 24, 25, 26, 26, 26, 27, 13, 13, 13, 28, 29, 30, 13, 31, 32, 33, - 34, 35, 36, 37, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 38, 7, 7, 39, 7, 40, 7, 7, - 7, 41, 13, 42, 7, 7, 43, 7, 7, 7, 44, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, - 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, - 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 59, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 59, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 78, 69, 69, 69, 69, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, - 81, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 69, 69, 96, 97, 98, 99, 99, 99, - 100,101,102,103,104,105,106,107,108,109, 95,110,111,112,113,114, - 115,116,117,117,118,119,120,121,122,123,124,125,126,127,128,129, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,102, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, + 11, 11, 11, 11, 11, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, + 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 14, + 1, 1, 1, 1, 14, 1, 1, 1, 18, 18, 18, 18, 18, 18, 18, 18, + 1, 5, 0, 11, 11, 11, 11, 11, 1, 1, 1, 1, 1, 1, 14, 0, + 1, 1, 1, 1, 14, 0, 0, 0, 6, 6, 6, 6, 7, 7, 7, 7, + 1, 1, 1, 1, 1, 14, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 14, 0, 0, 0, 0, + 1, 1, 1, 14, 1, 1, 1, 14, 3, 3, 3, 3, 3, 3, 0, 0, + 28, 28, 28, 28, 28, 28, 28, 28, 11, 11, 11, 11, 11, 1, 1, 1, + 14, 17, 1, 1, 1, 1, 1, 1, 14, 0, 0, 0, 0, 0, 0, 0, + 14, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 0, 0, 0, + 3, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 29, 0, + 11, 11, 11, 11, 11, 11, 11, 11, 7, 7, 7, 7, 7, 6, 6, 6, + 0, 0, 0, 11, 11, 11, 11, 11, 17, 1, 1, 1, 1, 1, 1, 1, + 11, 11, 11, 11, 11, 10, 10, 10, 1, 1, 1, 0, 0, 0, 0, 0, + 5, 46, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 41, 41, 41, 41, + 3, 3, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 0, 0, 12, + 1, 1, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, + 49, 10, 10, 10, 10, 10, 10, 10, 7, 6, 6, 6, 6, 6, 6, 6, + 44, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, + 3, 3, 3, 3, 0, 0, 0, 0, 11, 11, 11, 11, 11, 12, 9, 77, + 88, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,102,103,104, + 89, 6, 6, 6, 6, 6, 6, 6, 22, 22, 22, 22, 22, 22, 22, 22, + 26, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 5, 5, 5, 5, 5, + 5, 20, 1, 1, 1, 1, 1, 1, 1, 17, 1, 1, 1, 1, 1, 1, + 11, 11, 11, 11, 11, 12, 12, 12, 1, 1, 1, 1, 14, 1, 1, 0, + 14, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 14, 1, 1, 1, 1, + 10, 10, 10, 10, 10, 10, 51, 0,115, 1, 1, 1, 1, 1, 1, 1, + 5, 5, 5, 5, 5, 5, 5, 0, 1, 1, 1, 1, 13, 13, 13, 12, + 6, 6, 6, 0, 7, 7, 7, 0, 30, 30, 30, 30, 30, 30, 30, 30, + 3, 3, 3, 3, 3, 29, 0, 0, 3, 3, 3, 3, 3, 3, 3, 29, + 10, 10, 10, 10, 10, 3, 3, 3, 75, 10, 10, 10, 10, 10, 10, 10, + 3, 3, 3, 29, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 32, 7, 7, 0, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 51, 0, 0, 0, 1, 1, 1, 1, 1, 1, 86, 10, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 15, 19, + 1, 1, 1, 1, 1, 1, 1, 34, 1, 1, 1, 1, 1, 1, 1, 5, + 15, 15, 15, 15, 15, 15, 15, 15, 1, 14, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 23, 0, 0, 0, 0, 10, 10, 0, 0, 0, 0, 0, 0, + 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, + 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, + 3, 3, 3, 3, 29, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 87, 12,100, 12, 27,101, 76, 12, 6, 6, 6, 6, 6,105,106,135, + 87, 30, 30, 66,107,136,137,108, 50, 10, 89, 12,138,139, 10, 78, + 7, 7, 7, 59, 7, 7, 7, 8, 6, 6, 6, 60, 6, 6, 6, 6, + 8, 8, 8, 8, 21, 21, 21, 21, 21, 21, 21, 21, 6, 8, 8, 8, + 8, 8, 8, 8, 7, 21, 21, 6, 21, 8, 8, 7, 21, 7, 6, 7, + 7, 21, 8, 7, 8, 6, 7, 21, 8, 8, 8, 7, 21, 6, 8, 7, + 21, 7, 21, 21, 8, 67, 8, 6, 1, 1,109, 21,110,109, 21, 21, + 21, 21, 21, 21, 21, 21, 6, 8, 21,110, 8, 7, 8, 8, 8, 8, + 8, 8, 6, 6, 6, 7, 21, 8, 21, 21, 7, 8, 8, 8, 8, 8, + 6, 6, 1, 6, 6, 6, 6, 6, 13, 22, 22, 13, 13, 13, 13, 13, + 13, 22, 22, 22, 22, 22, 22, 22, 13, 13, 68, 22, 22, 22, 68, 68, + 8, 8, 68, 8, 0, 69, 6, 88, 0, 0, 22,140, 7, 32, 32, 7, + 21, 7, 7, 7, 7, 7, 7, 7, 7, 39, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 21, 6, 7, 8, 6, 8, 8, 8, 8, + 6, 6, 8, 90, 21, 8, 21, 7, 8, 43, 5, 5, 79, 8, 8, 8, + 7, 21, 21, 21, 21, 21, 21, 6, 39, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 32, 54, 12, 12, 12, 6, 6, 6, 6,111,141, 44, 70, + 5, 5, 5, 5, 5, 5, 5,142, 71, 36, 5, 71, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 14, 0, 17, 1, 40, 33, 0, 0, 0, 0, 0, + 18, 18, 18, 9, 77, 80, 12, 3, 5, 5, 5, 5, 5, 36,143, 12, + 52, 1, 1, 1, 1, 24, 5, 5, 11, 11, 11, 11, 11, 12, 12, 1, + 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 53, 5, 5, 5,144, 43, + 5, 5, 81, 72, 55, 5, 5, 1, 11, 11, 11, 11, 11, 1, 56, 82, + 12, 12, 12, 12, 12, 12, 12, 73, 24, 1, 1, 1, 1, 1, 1, 1, + 5, 5, 5, 5, 5, 23, 17, 1, 25, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 24, 5, 5, 5, 5, 13, 66, 12, 61, 26, 30, + 1, 1, 1, 5, 5, 72, 5, 5, 5, 5, 72, 5, 72, 5, 5, 0, + 12, 12, 12, 12, 12, 12, 12, 33, 1, 1, 1, 1, 24, 5, 0, 33, + 1, 1, 1, 1,145, 1, 1, 1, 18, 0, 0, 26, 5, 5, 5, 5, + 1, 1, 1, 1, 45, 5, 5, 5, 5, 91, 5, 5, 5, 5, 5, 5, + 1, 1, 1, 1, 1, 20, 25, 15, 19, 5, 5, 5, 20, 15, 19, 15, + 24, 5, 5, 5, 1, 1, 1, 1, 1, 5, 12, 11, 11, 11, 11, 11, + 62, 1, 1, 1, 1, 1, 1, 1, 24, 15, 17, 1, 1, 1, 14, 17, + 14, 14, 0, 1, 1, 0, 25, 15, 19, 5, 23, 35, 37, 35, 19, 14, + 0, 0, 0, 35, 0, 0, 1, 17, 1, 30, 10, 10, 10, 70, 40, 23, + 26, 20, 17, 1, 1, 14, 0, 17, 14, 1, 17, 14, 1, 0, 23, 15, + 19, 23, 0, 26, 23, 26, 5, 0, 26, 0, 0, 0, 17, 1, 14, 14, + 5, 1, 24, 33, 0, 0, 0, 0, 26, 20, 17, 1, 1, 1, 1, 17, + 14, 1, 17, 1, 1, 0, 25, 15, 19, 5, 5, 26, 20, 35, 19, 0, + 80, 0, 0, 0, 17, 5, 5, 5, 26, 15, 17, 1, 1, 1, 14, 17, + 14, 1, 17, 1, 1, 0, 25, 19, 19, 5, 23, 35, 37, 35, 19, 0, + 0, 0, 26, 20, 0, 0, 1, 17, 82, 10, 10, 10, 0, 0, 0, 0, + 0, 25, 17, 1, 1, 14, 0, 1, 14, 1, 1, 0, 17, 14, 14, 1, + 0, 17, 14, 0, 1, 14, 0, 1, 1, 1, 1, 1, 1, 0, 0, 15, + 20, 37, 0, 15, 37, 15, 19, 0, 14, 0, 0, 35, 0, 0, 0, 0, + 10, 63, 3, 3, 70, 29, 0, 0, 20, 15, 25, 1, 1, 1, 14, 1, + 1, 1, 1, 1, 1, 0, 25, 5, 20, 15, 37, 5, 23, 5, 5, 0, + 0, 0, 26, 23, 1, 14, 1, 0, 0, 0, 0, 38, 10, 10, 10, 63, + 24, 15, 53, 1, 1, 1, 14, 1, 1, 1, 17, 1, 1, 0, 25, 19, + 15, 15, 37, 20, 37, 15, 5, 0, 0, 0, 35, 37, 0, 0, 1, 14, + 17, 34, 0, 0, 0, 0, 0, 0, 5, 15, 1, 1, 1, 1, 14, 1, + 1, 1, 1, 1, 1, 24, 25, 15, 19, 5, 23, 15, 37, 15, 19, 56, + 0, 0, 1, 34, 10, 10, 10,112, 10, 10, 10, 10, 63, 1, 1, 1, + 26, 15, 17, 1, 1, 1, 1, 1, 1, 1, 1, 14, 0, 1, 1, 1, + 1, 17, 1, 1, 1, 1, 17, 0, 1, 1, 1, 14, 0, 23, 0, 35, + 15, 5, 23, 23, 15, 15, 15, 15, 0, 15, 33, 0, 0, 0, 0, 0, + 24, 1, 5, 5, 5, 23, 0,113, 1, 1, 1, 72, 5, 5, 5, 36, + 11, 11, 11, 11, 11, 12, 0, 0, 17, 14, 14, 1, 1, 14, 1, 1, + 1, 1, 17, 17, 1, 1, 1, 1, 24, 1, 5, 5, 5, 5, 25, 0, + 1, 1, 14, 61, 5, 5, 5, 23, 11, 11, 11, 11, 11, 0, 1, 1, + 56, 3, 12, 12, 12, 12, 12, 12, 12, 57, 57, 3, 5, 3, 3, 3, + 10, 10, 43, 43, 43, 27, 27, 15, 1, 1, 1, 1, 17, 1, 1, 1, + 26, 5, 5, 5, 5, 5, 5, 20, 5, 5, 36, 5, 1, 1, 24, 5, + 5, 5, 5, 5, 26, 5, 5, 5, 5, 5, 5, 5, 5, 5, 23, 3, + 3, 3, 3, 55, 3, 3, 29, 3, 12, 12, 57, 3, 66, 33, 0, 0, + 1, 1, 1, 1, 1, 34, 19, 5, 20, 5, 5, 5, 19, 20, 19, 25, + 1, 1, 1, 15, 5, 1, 1, 5, 25, 15, 46, 34, 15, 15, 15, 1, + 24, 5, 25, 1, 1, 1, 1, 1, 1, 20, 19, 20, 15, 15, 19, 34, + 11, 11, 11, 11, 11, 15, 19, 3, 7, 7, 7, 39, 0, 0, 39, 0, + 6, 6, 6, 6, 6,111, 69, 6, 1, 1, 1, 14, 14, 1, 1, 0, + 14, 1, 1, 0, 1, 1, 1, 14, 1, 1, 1, 1, 1, 14, 26, 5, + 12, 12, 12, 12,114, 10, 10, 10, 7, 7, 7, 0, 6, 6, 6, 0, + 1, 1, 1, 1, 1, 1, 56, 53,146, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1,147,116, 0, 1, 1, 1, 1, 1, 40, 12, 16, + 148, 1, 1, 1, 14, 0, 0, 0, 1, 5, 20, 0, 0, 0, 0, 17, + 1, 5, 83, 33, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 14, 1, 14, 5, 0, 0, 0, 0, 0, 0, + 1, 1, 5, 19, 5, 5, 5, 15, 15, 15, 15, 20, 19, 5, 5, 5, + 5, 5, 12, 62, 12, 80, 24, 0, 10, 10, 10, 10, 10, 0, 0, 0, + 12, 12, 12, 92, 12, 71, 5, 91, 1, 45, 1, 1, 1, 1, 1, 1, + 1, 1, 24, 25, 1, 1, 1, 1, 1, 1, 1, 1, 24, 14, 0, 0, + 5, 20, 15, 19, 20, 15, 0, 0, 15, 20, 15, 15, 19, 5, 0, 0, + 29, 0, 12, 11, 11, 11, 11, 11, 1, 1, 14, 0, 0, 0, 0, 0, + 11, 11, 11, 11, 11, 51, 0, 3, 1, 1, 1, 24, 20, 19, 0, 12, + 1, 1, 34, 20, 5, 5, 5, 23, 20, 20, 19, 5, 5, 5, 20, 15, + 15, 19, 5, 5, 5, 5, 23, 26, 12, 12, 12, 62, 12, 12, 12, 0, + 5, 5, 5, 5, 5, 5, 5, 93, 5, 5, 5, 5, 5, 5, 0, 0, + 5, 5, 46, 1, 1, 1, 1, 1, 1, 1, 20, 5, 5, 20, 20, 15, + 15, 20, 46, 1, 1, 1, 14, 12, 57, 3, 3, 3, 3, 43, 5, 5, + 5, 5, 3, 3, 3, 3, 66, 12, 34, 5, 5, 15, 5, 19, 5, 1, + 1, 1, 1, 20, 5, 15, 19, 19, 5, 15, 0, 0, 0, 0, 12, 12, + 1, 1, 15, 15, 15, 15, 5, 5, 5, 5, 15, 5, 0, 38, 12, 12, + 11, 11, 11, 11, 11, 0, 17, 1, 6, 6, 6, 6, 21, 47, 0, 0, + 7, 7, 7, 7, 7, 32, 39, 7, 12, 12, 12, 12, 0, 0, 0, 0, + 5, 36, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 25, 1, 24, 1, + 1, 1, 25, 34, 5, 14, 0, 0, 6, 6, 6, 6, 6, 6, 13, 13, + 13, 13, 13, 13, 13, 69, 6, 6, 6, 6, 6, 6, 69, 6, 6, 6, + 6, 6, 6, 6, 6,117, 13, 13, 8, 8, 8, 6, 6, 6, 6, 8, + 6, 6, 6, 6, 39, 39, 39, 39, 6, 6, 6, 6, 6, 6, 6, 0, + 6, 6, 47, 6, 7, 7, 94,118, 22, 6, 47, 6, 7, 7, 94, 22, + 6, 6, 0, 6, 7, 7,149, 22, 6, 6, 6, 6, 7, 7,150, 22, + 0, 6, 47, 6, 7, 7, 94,151, 74, 74, 74, 74, 74,152, 18, 18, + 84, 84, 84, 12, 58,119, 58,119, 12, 12, 12, 12,153, 18, 18,154, + 12, 12, 12, 12,120,121, 12,122,123, 12, 64, 65, 12, 12, 12, 12, + 12, 77,123, 12, 12, 12, 12,155, 18, 18,156, 18, 18, 18, 18, 18, + 157, 0, 10, 10, 10, 9, 64,158, 10, 10, 10, 10, 10, 9, 64,116, + 13, 13, 13, 13, 13, 13, 61, 0, 30, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5,159, 79, 93, 79, 93, 5, 5, 5, 5, 5, + 23, 0, 0, 0, 0, 0, 0, 0, 3, 85, 3, 95, 3, 21, 7, 6, + 7, 8, 95, 3, 90, 7, 7, 3, 3, 3, 85, 85, 85, 7, 7,160, + 7, 7, 67, 1,124, 3, 6, 7, 9, 9, 90, 6, 6, 50, 3,125, + 16,161,162, 16,126, 3, 0, 0, 9, 9, 48, 3, 3, 9, 3, 3, + 48, 50, 3, 48, 3, 3, 3, 48, 3, 3, 3, 3, 3, 3, 3, 9, + 3, 48, 48, 3, 3, 3, 3, 3, 3, 3, 9, 9, 9, 9, 9, 9, + 3, 3, 3, 3, 27, 27, 3, 3, 9, 3, 3, 3,163,164, 3, 3, + 3, 3, 3, 3, 3, 3, 48, 3, 3, 3, 3, 3, 3, 50, 9, 9, + 9, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 9, 9, + 9, 3, 3, 3, 3, 3, 3, 3, 10, 10, 10, 10, 10, 10, 3, 3, + 3, 3, 3, 3, 3, 10, 10, 10, 3, 3, 3, 50, 3, 3, 3, 3, + 50, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 9, 9, 9, 9, + 3, 3, 3, 3, 3, 3, 3, 50, 3, 3, 3, 3, 27, 27, 27, 27, + 27, 27, 27, 10, 10, 10, 10, 10, 10, 10, 3, 3, 3, 3, 3, 3, + 9, 9, 64,127, 9, 9, 9, 9, 9, 9, 9, 27, 27, 27, 27, 27, + 9, 64, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,127, 9, 9, 9, + 9, 9, 9, 9, 27, 27, 9, 9, 9, 9, 9, 9, 9, 9, 27, 9, + 9, 9, 48, 50, 9, 9, 48, 3, 3, 3, 0, 3, 3, 3, 3, 3, + 8, 7, 8, 21, 21, 21, 21, 7, 8, 8, 21, 6, 6, 6, 13, 7, + 8, 8,125, 3, 3, 95, 21,165, 5, 8, 0, 0, 38, 12,114, 12, + 6, 6, 6, 42, 0, 0, 42, 0, 1, 1, 1, 1, 0, 0, 0, 54, + 33, 0, 0, 0, 0, 0, 0, 26, 12, 58, 58, 12,120,121, 58, 12, + 12, 12, 12, 76, 12, 92, 58, 12, 58, 27, 27, 27, 27, 12, 12, 62, + 12, 12, 12, 12, 12, 84, 12, 12, 92,166, 12, 12, 12, 12, 12, 12, + 3, 12, 96, 31, 31, 31,167, 0, 3, 3, 3, 3, 3, 44, 3, 3, + 87, 12,168,128, 27, 27, 27, 27, 27, 3, 27, 27, 27, 27,129,169, + 170, 16, 16, 16, 16, 5, 5, 15,130, 13, 13, 3, 16,171, 40, 3, + 1, 1, 1, 14, 26,172,131, 52, 1, 1, 1, 1, 1, 40, 13, 52, + 0, 0, 17, 1, 1, 1, 1, 1, 3, 10, 10, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 0, 0, 0, 44, 3, 3, 3, 3, 10, 10, 10, 10, + 1, 1, 45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,132, 12, + 11, 11, 11, 11, 11, 1, 0, 0, 8, 8, 8, 8, 8, 8, 8, 24, + 79,173, 5, 5, 5, 5, 5, 62, 8, 8, 8, 8, 8, 8, 13, 5, + 1, 1, 1, 16, 16, 16, 16, 16, 5, 12, 12, 12, 0, 0, 0, 0, + 22, 22, 22,131, 13, 13, 13, 13, 22, 8, 8, 8, 8, 8, 8, 8, + 6, 8, 8, 8, 8, 8, 8, 8, 69, 6, 6, 6, 21, 21, 21, 8, + 8, 8, 8, 8, 68,174, 21, 67, 8, 8, 6, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 7, 7, 8, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 7, 7, 21, 21, 8, 8, 8, 8, 8, 8, 8, 8, 32, 0, + 54, 13,175, 67, 13, 67, 1, 1, 1, 25, 1, 25, 1, 24, 1, 1, + 1, 34, 19, 20, 3, 3, 23, 0, 10, 10, 10, 3, 97, 0, 0, 0, + 1, 1, 12, 12, 0, 0, 0, 0, 15, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 15, 15, 15, 15, 15, 15, 15, 5, 0, 0, 0, 0, 12, + 5, 1, 1, 1, 12, 53, 53, 24, 1, 1, 1, 5, 5, 5, 5, 12, + 1, 1, 1, 24, 5, 5, 5, 5, 5, 15, 0, 0, 0, 0, 0, 38, + 1, 24, 15, 5, 5, 15, 5, 15, 83, 12, 12, 12, 12, 12, 12, 54, + 1, 1, 24, 52, 1, 1, 1, 1, 11, 11, 11, 11, 11, 1, 1, 14, + 1, 1, 1, 1, 24, 5, 5, 20, 19, 20, 19, 23, 0, 0, 0, 0, + 1, 24, 1, 1, 1, 1, 20, 0, 11, 11, 11, 11, 11, 0, 12, 12, + 52, 1, 1, 56, 3, 34, 20, 1, 25, 5, 25, 24, 25, 1, 1, 5, + 24, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 45, 12, + 1, 1, 1, 1, 1, 34, 5, 15, 12, 45,176, 23, 0, 0, 0, 0, + 17, 1, 1, 14, 17, 1, 1, 14, 17, 1, 1, 14, 0, 0, 0, 0, + 6, 6, 6, 6, 6,118, 13, 13, 6, 6, 6, 6,117, 22, 0, 0, + 1, 34, 19, 15, 20, 83, 19, 0, 1, 1, 1, 14, 0, 17, 1, 1, + 6, 6, 6, 47, 0, 0, 0, 0, 0, 42, 6, 6, 0, 0, 17, 25, + 1, 1, 1, 1,177, 1, 1, 1, 1, 1, 1, 14, 1, 1, 14, 14, + 1, 17, 14, 1, 1, 1, 1, 1, 1, 22, 22, 22, 22, 22, 22, 22, + 22,107, 3, 3, 3, 3, 3, 3, 3, 82, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 31, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 97, 3, + 12, 12, 12, 96, 65, 0, 0, 0, 76,178,179, 31, 31, 31, 31, 31, + 31, 31, 65, 96, 65, 12,122,180, 12, 33, 12, 12,129, 31, 31, 65, + 12,181, 9, 98, 80, 12, 0, 0, 1, 1, 14, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 14, 73, 38, 12,100, 12, 27,101, 76, 12, + 6, 6, 6, 6, 6,105,106, 64, 65, 27, 12, 1, 1, 1, 1, 1, + 52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 13, + 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 14, 0, + 30,182, 70,183, 50, 9, 48, 29, 0, 0, 0, 0, 73, 18, 3, 0, + 1, 1, 1, 1, 1, 1, 17, 1, 1, 1, 1, 1, 1, 14, 1, 17, + 12, 33, 0, 49, 10, 10, 10, 10, 10, 10, 0, 44, 3, 3, 3, 3, + 16, 16,126, 10, 63, 3, 3, 3, 3, 3, 3, 3, 3, 10, 3, 29, + 3, 3, 3, 3, 3, 3, 43, 0,133, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 0, 0, 10, 10, 0, 0, 0, 0, 17, 1, + 128, 1, 1, 1, 1, 99, 0, 0, 1, 1, 1, 5, 5, 23, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 38, 1, 1, 0, 0, 1, 1, 1, 1, + 184, 16, 16, 0, 0, 0, 0, 0, 7, 7, 7, 7, 6, 6, 6, 6, + 7, 7, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 38, 7, 32, 7, 42, 6, 6, 6, 6, + 6, 42, 6, 6, 6, 6, 6, 6, 6, 42, 6, 6, 6, 42, 47, 0, + 13, 13, 13, 54, 13, 13, 13, 13, 61, 13, 13, 13, 13, 61, 0, 0, + 1, 1, 1, 0, 14, 1, 1, 1, 1, 1, 1, 17, 14, 0, 14, 17, + 1, 1, 1, 38, 10, 10, 10, 10, 1, 1, 1, 56, 75, 10, 10, 10, + 0, 0, 0, 49, 10, 10, 10, 10, 1, 14, 1, 0, 0, 49, 10, 10, + 1, 1, 1, 10, 10, 10, 0, 38, 1, 1, 1, 1, 1, 0, 0, 38, + 1, 1, 1, 1, 0, 0, 10, 1, 24, 5, 26, 23, 0, 0, 5, 5, + 1, 1, 17, 1, 17, 1, 1, 1, 1, 1, 1, 0, 5, 23, 0, 26, + 12, 12, 12, 12, 33, 0, 0, 0, 1, 1, 1, 1, 1, 1, 86, 78, + 1, 1, 1, 1, 82, 1, 1, 1, 1, 1, 24, 23, 0, 49, 10, 10, + 12, 12, 12, 33, 0, 0, 0, 0, 1, 1, 1, 0, 38, 12, 12, 12, + 1, 1, 1, 0, 10, 10, 10, 10, 1, 14, 0, 0, 10, 10, 10, 10, + 1, 0, 0, 0, 38, 12, 33, 0, 0, 0, 0, 0, 49, 10, 10, 10, + 7, 32, 0, 0, 0, 0, 0, 0, 6, 47, 0, 0, 0, 10, 10, 10, + 1, 1, 5, 5, 0, 0, 0, 0, 11, 11, 11, 11, 11, 1, 1, 52, + 7, 7, 7, 0, 26, 5, 5,130, 6, 6, 6, 0, 0, 0, 0, 9, + 10, 10, 10, 10, 10, 10, 10, 51, 1, 1, 1, 1, 1, 26,185, 0, + 0, 1, 45, 1, 0, 0, 0, 0, 57, 3, 3, 3, 29, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 5, 5, 10, 10, 10,112, 0, 0, 0, 0, + 133, 10, 78, 12, 12, 0, 0, 0, 1, 5, 5, 12, 12, 0, 0, 0, + 1, 1, 86, 10, 10, 10, 0, 0, 19, 46, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 36, 12, 12, 12, 0, + 10, 10, 10, 11, 11, 11, 11, 11, 25, 24, 25, 0, 0, 0, 0, 26, + 15, 19, 5, 20, 19, 36,186, 12, 12, 23, 0, 0, 0, 0, 73, 0, + 5, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 24, 5, 5, 19, 5, + 5, 5, 23, 11, 11, 11, 11, 11, 12, 12, 34, 46, 0, 0, 0, 0, + 1, 24, 12, 14, 0, 0, 0, 0, 1, 34, 15, 5, 5, 5, 5, 20, + 46, 1, 40, 12, 71, 5, 36, 19, 11, 11, 11, 11, 11, 40, 40, 12, + 10, 10, 51, 0, 0, 0, 0, 0, 5, 15, 20, 5, 12, 12, 12, 25, + 24, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 14, 14, 1, 1, 17, + 1, 1, 1, 1, 1, 1, 1, 17, 1, 1, 1, 1, 40, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 24, 15, 19, 5, 5, 5, 23, 0, 0, + 5, 15, 17, 1, 1, 1, 14, 17, 14, 1, 17, 1, 1, 26, 25, 15, + 20, 15, 37, 35, 37, 35, 15, 0, 14, 0, 0, 35, 0, 0, 17, 1, + 1, 15, 0, 5, 5, 5, 23, 0, 5, 5, 23, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 17, 0, 14, 1, 1, 1, 17, 15, 19, 5, 5, + 23, 37, 35, 35, 15, 37, 15, 20, 25, 25, 12, 38, 33, 0, 0, 0, + 26, 23, 0, 0, 0, 0, 0, 0, 1, 1, 34, 15, 5, 5, 5, 5, + 15, 5, 20, 25, 1, 40, 12, 12, 11, 11, 11, 11, 11, 12, 38, 25, + 15, 19, 5, 5, 20, 20, 15, 19, 20, 5, 1, 53, 0, 0, 0, 0, + 15, 5, 5, 0, 15, 15, 5, 19, 36, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 1, 1, 5, 0, 15, 19, 5, 5, 5, 20, 19, 19, + 36, 12, 14, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 33, 0, + 1, 1, 1, 1, 1, 24, 19, 15, 5, 5, 5, 19, 40, 0, 0, 0, + 11, 11, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 14, 26, 19, + 15, 5, 5, 19, 5, 5, 0, 0, 11, 11, 11, 11, 11, 10, 12, 57, + 5, 5, 5, 5, 19, 36, 0, 0, 10, 51, 0, 0, 0, 0, 0, 17, + 1, 1, 1, 14, 17, 0, 1, 1, 1, 1, 17, 14, 1, 1, 1, 1, + 15, 15, 15, 35, 37, 26, 20, 25, 46, 19, 12, 33, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 34, 15, 5, 5, 0, 5, 15, 15, + 25, 53, 37, 0, 0, 0, 0, 0, 24, 5, 5, 5, 5, 25, 1, 1, + 1, 24, 5, 5, 20, 24, 5, 36, 12, 12, 12, 71, 0, 0, 0, 0, + 24, 5, 5, 20, 19, 5, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, + 5, 5, 5, 20, 5, 12, 53, 12, 12, 33, 0, 0, 0, 0, 0, 0, + 12, 12, 12, 12, 12, 0, 0, 0, 20, 5, 20, 20, 0, 0, 0, 0, + 40, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 23, 5, 5, 5, 19, + 40, 12, 12, 0, 0, 0, 0, 0, 12, 1, 1, 1, 1, 1, 1, 1, + 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 35, 5, 5, 5, + 20, 5, 19, 23, 0, 0, 0, 0, 1, 1, 1, 14, 1, 17, 1, 1, + 24, 5, 5, 23, 0, 23, 5, 26, 5, 5, 5, 24, 0, 0, 0, 0, + 1, 1, 1, 17, 14, 1, 1, 1, 1, 1, 1, 1, 1, 15, 15, 37, + 5, 35, 19, 19, 14, 0, 0, 0, 1, 1, 1, 1, 45, 1, 0, 0, + 1, 24, 20, 83, 33, 0, 0, 0, 5, 34, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 5, 5, 23, 0, 15, 20, 36, 12, 12, 12, 12, 12, 12, + 11, 11, 11, 11, 11, 23, 0, 0, 10, 10, 63, 3, 3, 3, 70, 30, + 97, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 38, + 16, 16, 16, 16, 16, 16, 16, 99, 12, 12, 33, 0, 0, 0, 0, 0, + 40, 33, 0, 0, 0, 0, 0, 0, 25, 1, 1, 24, 5, 5, 5, 5, + 5, 5, 5, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 15, 19, 5, + 5, 5, 36, 0, 0, 0, 0, 0, 5, 5, 5, 36, 12, 12, 3, 3, + 13, 13, 57, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 49, 10, 10, + 10, 17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 17, 1, + 13, 52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 45,132, 12, + 10, 10, 10, 78, 12, 33, 0, 0, 7, 7, 7, 7, 32, 42, 6, 6, + 6, 6, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 14, 0, 26, + 34, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 26, + 5, 81, 13, 13, 13, 13, 13, 13, 13, 62, 23, 0, 0, 0, 0, 0, + 15, 13, 16, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, + 13, 13, 54, 13, 13, 13, 54, 61, 0, 14, 0, 0, 0, 0, 0, 0, + 1, 14, 17, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 43, 36, 18, 18, 0, 0, 0, 0, 0, 0, + 11, 11, 11, 11, 11, 3, 29, 0, 3, 3, 0, 0, 0, 3, 3, 3, + 98, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 29, 44, 3, 3, 3, + 3, 3,134, 19, 5, 3,134, 15, 15,187, 18, 18, 18, 91, 5, 5, + 5, 55, 43, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, + 3, 5, 55, 0, 0, 0, 0, 0, 6, 6, 47, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 32, 7, 0, 32, 39, 32, 39, 7, 32, 7, + 7, 7, 7, 6, 6, 42, 42, 6, 6, 6, 42, 6, 6, 6, 6, 6, + 6, 6, 7, 39, 7, 32, 39, 7, 7, 7, 32, 7, 7, 7, 32, 6, + 6, 6, 6, 6, 7, 39, 7, 32, 7, 7, 32, 32, 0, 7, 7, 7, + 32, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 7, 7, 7, 7, + 59, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 60, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 59, 6, 6, + 6, 6, 60, 6, 6, 6, 7, 7, 7, 7, 59, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 60, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 59, 6, 6, 6, 6, 60, 6, 6, 6, + 7, 7, 7, 7, 59, 6, 6, 6, 6, 60, 6, 6, 6, 8, 0, 11, + 5, 5, 5, 55, 3, 43, 5, 5, 5, 5, 5, 5, 5, 5, 55, 3, + 3, 3, 43, 3, 3, 3, 3, 3, 3, 3, 55, 66, 12, 12, 0, 0, + 0, 0, 0, 0, 0, 26, 5, 5, 6, 6, 6, 6, 6,124, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 47, 0, 0, 42, 6, 6, 47, 0, 0, + 5, 5, 5, 23, 5, 5, 5, 5, 5, 5, 5, 5, 23, 26, 5, 5, + 5, 26, 23, 5, 5, 23, 0, 0, 13, 13, 13, 13, 13, 13, 13, 0, + 0, 0, 0, 0, 0, 0, 0, 26, 5, 5, 5, 81, 13, 13, 13, 0, + 11, 11, 11, 11, 11, 0, 0, 56, 1, 1, 1, 1, 1, 1, 1, 23, + 1, 1, 1, 1, 1, 1, 5, 5, 11, 11, 11, 11, 11, 0, 0,113, + 1, 1, 1, 1, 1, 45, 5, 5,188, 11, 11, 11, 11,189, 0, 38, + 1, 24, 1, 25, 1, 1, 1, 5, 1, 1, 24, 0, 0, 0, 0, 45, + 1, 1, 1, 14, 1, 1, 17, 14, 1, 1, 14, 49, 10, 10, 10, 10, + 6, 6, 5, 5, 5, 81, 0, 0, 10, 10, 10, 10, 10, 10, 75, 10, + 190, 10, 51, 0, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 75, + 10, 10, 10, 10, 10, 10, 10, 0, 1, 1, 17, 1, 1, 1, 1, 1, + 17, 14, 14, 17, 17, 1, 1, 1, 1, 14, 1, 1, 17, 17, 0, 0, + 0, 14, 0, 17, 17, 17, 17, 1, 17, 14, 14, 17, 17, 17, 17, 17, + 17, 14, 14, 17, 1, 14, 1, 1, 1, 14, 1, 1, 17, 1, 14, 14, + 1, 1, 1, 1, 1, 17, 1, 1, 17, 1, 17, 1, 1, 17, 1, 1, + 9, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 63, 3, + 0, 0, 0, 3, 3, 3, 3, 3, 3, 29, 0, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3,108, 22, 22, 3, 3, 3, 3, 29, 0, 3, 3, + 9, 9, 9, 9, 98, 0, 0, 0, 3, 3, 3, 3, 3, 29, 0, 3, + 3, 3, 3, 29, 29, 0, 44, 3, 3, 3, 3, 3, 3, 3, 29, 44, + 3, 3, 3, 3, 3, 29, 0, 44, 3, 29, 3, 3, 3, 3, 3, 3, + 11, 11, 11, 11, 11, 29, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 7, 7, 3, 3, 26, 26, + 4, 4, 12, 12, 5, 5, 9, 9, 9, 5, 25, 25, 15, 15, 13, 13, + 21, 21, 6, 6, 7, 2, 10, 10, 14, 14, 2, 7, 1, 1, 10, 12, + 12, 10, 5, 9, 24, 24, 12, 2, 7, 12, 12, 7, 2, 12, 22, 18, + 0, 0, 26, 2, 23, 23, 18, 22, 9, 2, 21, 2, 7, 10, 2, 10, + 12, 21, 10, 2, 2, 21, 2, 9, 7, 21, 8, 8, 2, 5, 26, 12, + 2, 26, 7, 6, 10, 7, 5, 2, 25, 26, 2, 15, 26, 25, 15, 2, + 6, 7, 21, 7, 2, 6, 12, 26, 7, 26, 21, 26, 20, 19, 9, 25, + 5, 25, 6, 2, 21, 6, 15, 26, 25, 22, 18, 21, 26, 21, 5, 7, + 6, 24, 6, 5, 26, 23, 21, 12, 6, 12, 2, 1, 29, 29, 26, 15, + 21, 17, 25, 21, 15, 21, 11, 11, 21, 23, 12, 6, 26, 7, 10, 21, + 17, 17, 9, 26, 7, 15, 29, 21, 21, 9, 24, 5, 25, 9, 1, 12, + 17, 21, 11, 12, 8, 24, 26, 9, 21, 22, 23, 26, 25, 2, 14, 2, + 23, 21, 21, 25, 9, 22, 21, 18, 24, 16, 5, 22, 25, 18, 24, 26, + 26, 24, 9, 8, 8, 5, 5, 21, 15, 7, 2, 23, 21, 15, 17, 7, + 18, 2, 5, 6, 5, 24, 22, 20, 21, 20, 19, 21, 21, 16, 16, 21, + 7, 5, 5, 26, 14, 15, 18, 25, 7, 14, 17, 22, 17, 6, 24, 6, + 6, 21, 12, 15, 26, 10, 25, 0, 7, 20, 25, 1, 24, 15, 7, 19, + 9, 21, 17, 2, 17, 12, 1, 21, 12, 1, 24, 7, 29, 7, 7, 22, + 14, 7, 2, 24, 9, 24, 24, 2, 29, 1, 27, 28, 1, 29, 21, 29, + 1, 2, 15, 6, 18, 6, 12, 11, 26, 5, 14, 9, 5, 14, 26, 22, + 18, 26, 5, 12, 22, 21, 18, 17, 26, 6, 18, 18, 26, 14, 14, 6, + 12, 24, 11, 21, 24, 9, 6, 9, 6, 10, 7, 25, 17, 16, 16, 22, + 16, 16, 25, 17, 25, 24, 23, 2, 21, 14, 12, 17, 21, 1, 10, 1, + 7, 13, 13, 2, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, + 0, 0, 0, 0, 0, 0, 41, 42, 43, 44, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 11, 12, 13, 14, 0, 0, 0, 0, 0, 0, 0, 0, 15, + 0, 0, 0, 0, 0, 0, 0, 16, 17, 18, 0, 19, 0, 20, 21, 0, + 0, 22, 23, 24, 25, 26, 0, 0, 0, 0, 27, 28, 29, 30, 0, 31, + 0, 32, 33, 0, 2, 34, 0, 0, 2, 1, 35, 0, 2, 1, 0, 0, + 2, 1, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 2, 36, 0, 0, + 2, 1, 0, 0, 37, 1, 0, 0, 0, 38, 0, 0, 39, 40, 0, 0, + 41, 42, 0, 43, 44, 0, 45, 46, 0, 47, 0, 0, 48, 0, 0, 49, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 51, 4, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, + 53, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 55, 0, 0, 56, 0, + 57, 58, 59, 0, 60, 5, 61, 0, 62, 0, 63, 0, 64, 0, 0, 0, + 0, 65, 66, 0, 0, 0, 0, 0, 0, 67, 68, 0, 0, 0, 0, 0, + 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 71, 0, 0, 0, 3, 0, 0, 0, 72, 0, 73, 0, 0, 74, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 75, 76, + 0, 0, 77, 0, 0, 0, 0, 0, 0, 0, 0, 6, 78, 0, 0, 0, + 0, 5, 79, 0, 80, 81, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, + 82, 83, 84, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 85, 0, 0, 0, 0, 0, 0, 0, 0, 86, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, 0, + 0, 0, 88, 0, 0, 0, 89, 0, 0, 0, 0, 90, 91, 0, 0, 0, + 0, 0, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 93, 0, 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 95, 0, 96, 0, 0, 97, 0, 98, 0, 0, 0, 0, 0, 6, 99, 0, + 9, 0, 0,100,101, 0, 7, 0, 0,102, 0, 0,103, 0, 0, 0, + 0, 0,104, 0,105, 1,106, 0, 0,107, 0, 0, 0,108, 0, 0, + 0,109, 0, 0, 0, 0, 0, 0, 3,110, 0, 0, 3, 0, 0, 0, + 111, 0, 0, 0,112, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, + 0, 0, 0, 0,113,114, 0, 0, 0, 0, 8, 0, 4,115, 0,116, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, + 0, 0, 0, 0, 0,117, 0,118, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,119, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,121, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,122, 0, 0, 0, 0,123, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,124,125,126, 0, 0, 0, 0,127, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,129, 0, 0,130, + 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 131, 0,132, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,133, 0, 0, 0, 0, 0, 0, 0,134, 0, 0, 0, 0, 0, + 0, 0,135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,136, 0, 0, 0,137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, + 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 15, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 1, 12, 0, 0, 1, 1, + 1, 1, 1, 49, 50, 5, 51, 52, 53, 5, 5, 22, 32, 23, 1, 54, + 24, 55, 16, 33, 56, 57, 58, 1, 1, 1, 0, 0, 0, 0, 6, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 7, 60, 1, 34, 5, + 7, 61, 62, 63, 64, 65, 66, 67, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 68, 0, 0, 0, 69, 70, 71, 35, 1, 25, 0, 0, + 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 8, 1, 26, 16, 26, + 73, 27, 0, 0, 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, 0, 0, + 0, 0, 36, 25, 75, 37, 7, 37, 76, 0, 0, 0, 0, 0, 0, 0, + 6, 1, 7, 0, 0, 17, 0, 0, 0, 0, 0, 8, 38, 1, 1, 13, + 13, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 0, 0, 0, + 0, 0, 0, 6, 18, 1, 0, 0, 8, 16, 5, 1, 1, 1, 77, 7, + 36, 18, 78, 7, 35, 1, 0, 0, 0, 4, 79, 10, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 4, 0, 80, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 3, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 83, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 85, 86, 0, 0, 0, + 0, 0, 87, 88, 89, 90, 91, 92, 0, 0, 0, 0, 0, 0, 0, 93, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 28, 0, 0, 0, + 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, + 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 14, 0, 0, 20, 0, 0, + 94, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95, 0, 0, 0, + 0, 0, 0, 6, 29, 0, 3, 0, 0, 0, 0, 13, 1, 96, 0, 0, + 0, 0, 1, 18, 33, 97, 25, 23, 7, 1, 1, 1, 1, 27, 1, 7, + 98, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, + 6, 23, 1, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 21, + 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, + 0, 0, 12, 32, 24, 5, 99, 22, 42, 17, 0, 10, 11, 0, 7, 1, + 7,100,101, 1, 1, 1, 1, 1, 1, 1, 1,102,103,104, 0, 0, + 0, 0,105, 1,106, 10, 20,107,108, 5, 10, 0, 0, 0, 0, 0, + 0, 6, 11, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, + 109,110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,111, 0, 0, 0, + 0, 6, 0, 1, 1, 11, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, + 0, 0, 11, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 1, + 1, 1, 11, 0, 0, 0, 0, 0, 43, 40, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 44,112, 10, 8, 20, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, + 0, 0, 0, 0, 0,113, 1, 16, 5, 24, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 17, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 8, 12, 0, 0, 0, 0,114, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,115, 2, 0,116, 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 13, 11, 0, 0, 0, 0, 0, 0, + 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 39, 0, 45, + 16, 18, 29, 0, 0, 0, 46, 27, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 3, 0, 0, 2, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 3, 0, 0, 3, 0, 21, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 31, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 15, 9, 0, 8, 1, 10, 1, 10, 0, 0, 0, 0, + 0, 30, 3, 0, 0, 0, 14, 21, 0, 0, 0, 0, 0, 19, 47, 0, + 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 47, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 28, 15, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 21, 41, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 28, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 22, 42, 0, 0, 0, 0, 0, 0,118, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,119, 0,120,121,122,123, 0, 43, 5, 48, 13, + 34, 0, 0, 0, 0, 0, 0, 0, 8, 11, 0, 0, 0, 0, 8, 10, + 0, 0, 0, 0, 0, 0, 1, 12, 1, 1, 1, 1, 26, 1, 38, 44, + 12, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, + 0, 19, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0,124, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 0, 6, 19, + 0, 8, 0, 20, 0, 0, 0, 0, 0, 0, 5, 48, 0, 0, 0, 1, + 125, 0, 0, 0, 0, 0,230,230,230,230, 0, 0, 0, 9, 9, 0, + 0, 0, 0, 9, 0, 0,220,220,220,220, 0, 0, 0,230,230,230, + 220,230, 0, 0,230,230, 7, 0, 0, 0,230, 0, 0, 0,230,230, + 0, 0,230,230,230, 0, 0,230,230,230, 0, 0, 9, 0, 0, 0, + 0, 7,230,230,230,220, 0,220, 0, 0,230,220,220,220, 0, 0, + 230, 0, 0,230, 0, 0, 0, 0, 7, 0, 1, 1, 1, 1,220,230, + 230,230,220,220,230,230,220,230,230,220,230, 0, 0,230,230,220, + 0, 0, 0, 9, 9, 0,220, 0, 0, 0, 0, 0, 9, 9, 0, 9, + 7, 0, 1,220,220,220,220,220,220,230,230,230,220,220,230,220, + 220,230,230,220,230,230,220,230,220,230,230,230, 0,230, 0,220, + 220,220,220,220, 0, 0, 9, 9, 0, 0, 1, 0, 0, 0, 0, 0, + 0,220,230, 0,230,230, 0, 0,220,220, 0, 0,230,220, 0, 0, + 9, 7,220,220,220, 0,230,232,220,220,220,220,232,216,220,202, + 202,220,220,220,220,202,202,220,220,220,230,240,230,220,230,220, + 220, 0,232,220,220,230,233,234,234,233,234,234,233,230, 0,220, + 230,230,230,230,222,220,230,222,228,230, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, 25, 0,230,220, + 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, + 34,230, 35, 0, 0, 0,230, 0,220,230, 0, 36, 0, 0,220,220, + 230,220,220,230,230, 0,230,230, 0,220, 27, 28, 29,230, 0,230, + 220,230, 0, 84, 91, 0,103,103, 9, 0,107,107,107,107,118,118, + 9, 0,122,122,122,122, 0,220, 0,220, 0,216, 0, 0, 0,129, + 130, 0,132, 0, 0, 0, 0, 0,130,130,130,130, 0, 0,130, 0, + 230,230, 9, 0,230,230, 0, 0,220, 0, 0,228, 0, 0, 0,222, + 230,220,230, 0, 0,220,230,220, 0,220,230,230,230,234,230, 0, + 1, 1,230,234,214,220,202,230,230,230,230,230,232,228,228,220, + 218,230,233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, + 1,230,220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, + 8, 0,220, 0, 0,230, 0, 0, 26, 0, 0,220, 0,230,230, 1, + 220, 0, 0,230,220, 0, 0, 7, 9, 0, 6, 6, 0, 0, 0, 0, + 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226,216,216,216,216, + 216, 0,232,232,220,230,230,230, 7, 0, 1, 0, 0, 0, 0, 0, + 0, 32, 0, 0, 0, 3, 0, 0, 0, 0, 4,101, 0,112,128,169, + 0, 11, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,237, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 1, 1, 0, 3, 0, 0, 0, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 6, 7, 8, 9, 0, + 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 20, 0, 0, 21, 22, 0, 0, 0, 0, 23, 24, + 25, 26, 0, 27, 0, 28, 29, 30, 31, 32, 0, 0, 0, 0, 0, 0, + 0, 33, 34, 35, 36, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 38, 39, 0, 0, 0, 0, 2, 1, 40, 41, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 36, 0, 0, + 8, 0, 0, 0, 0, 44, 0, 0, 52, 0, 0, 0, 56, 0, 0, 0, + 0, 0, 0, 0, 16, 8, 0, 0, 0, 0, 0, 0, 24, 48, 0, 0, + 0, 0, 0, 0, 28, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, + 60, 64, 0, 68, 0, 72, 76, 80, 0, 0, 0, 0, 0, 8, 84, 88, + 0, 92, 16, 8, 0, 0, 0, 4, 4, 16, 4, 4, 4, 4, 4, 4, + 4, 24, 20, 0, 96, 0, 16,100,104, 0, 4, 4,108, 0, 0, 0, + 40,112, 8, 16, 4, 4, 4, 4, 4, 8,116,120,124,128, 0, 0, + 4, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 4, 8, 0, 0, 24, 20,132, 36, 0, 28, 0,136, 16, 4, + 4, 4, 0, 0, 0, 0, 24, 12, 12,140,144, 12,148, 0,152, 8, + 4, 4, 0, 0,156, 0, 8, 8, 0, 24, 20, 8, 4, 0, 0, 0, + 8, 0, 0,160, 8, 8, 0, 0, 24, 20, 0, 8, 0, 8, 0, 8, + 0, 0, 0, 0, 40, 12, 12, 12, 12, 12, 12, 12, 12, 12, 20, 16, + 4,164, 12, 12, 12, 12, 12, 12, 12, 12, 12, 20, 0,168,172,176, + 0,180, 0, 24, 20, 0, 0, 0, 0, 0, 0, 0, 0,184, 16, 8, + 28, 8, 0, 0, 0, 8, 4, 4, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 40, 12, 48, 0, 0, 4, 4, 8, 4, 4, 0, 0, 0, + 0, 0, 0, 0, 40, 20, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 32,188,192, 0, 0, 0, 0, 0, 0, 0, 4, 5, + 6, 7, 8, 9, 10, 1, 1, 11, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 12, 13, 2, 2, 2, 2, 14, 0, 0, 0, 0, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 25, 26, 3, 3, 3, 27, + 0, 0, 0, 28, 29, 30, 0, 31, 32, 33, 34, 35, 36, 37, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 38, 1, 1, 39, 1, 40, 1, 1, 1, 41, 0, 42, 1, 1, + 43, 1, 1, 1, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 20, + 21, 8, 8, 8, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 2, 2, 51, 52, 53, 54, 9, 9, 9, 9, 9, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 8, 8, 70, 71, 72, + 73, 74, 75, 5, 5, 5, 5, 76, 5, 5, 5, 5, 5, 5, 5, 15, + 15, 5, 5, 5, 5, 77, 5, 78, 79, 80, 81, 82, 83, 1, 84, 85, + 86, 87, 88, 89, 90, 91, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 92, 1, 1, 1, 1, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 93, 16, 16, 94, 95, 96, 97, 98, + 99,100,101,102,103,104,105, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,106, 0, + 0, 1, 1,107,108,109, 13, 13, 13,110,111,112,113,114,115,116, + 117,118,119, 0,120,121,122,123,124,125,126, 17, 17,127,128,129, 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, - 95,146,147,148,149, 95,150,151,152,153,154,155,156,157,158,159, - 160,161, 95,162,163,164,165,165,165,165,165,165,165,166,167,165, - 168, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95,169,170,170,170,170,170,170,170,170,171,170, - 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, - 170,170,170,170,170,170,170,170,170,170,170,170,170,172,173,173, - 173,173,174, 95, 95, 95, 95, 95,175, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95,176,176,176,176,177,178,179,180, 95, 95, - 181, 95,182,183,184,185,186,186,186,186,186,186,186,186,186,186, - 186,186,186,186,186,186,186,186,186,186,186,186,187,187,187,188, - 189,190, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95,191,192,193,194,195,195,196, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,197,198, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 59,199, - 59, 59, 59,200,201,202, 59,203,204,205,206,207,208, 95,209,210, - 211, 59, 59,212, 59,213,214,214,214,214,214,215, 95, 95, 95, 95, - 95, 95, 95, 95,216, 95,217,218,219, 95, 95,220, 95, 95, 95,221, - 95,222, 95,223, 95,224,225,226,227, 95, 95, 95, 95, 95,228,229, - 230, 95,231,232, 95, 95,233,234, 59,235,236, 95, 59, 59, 59, 59, - 59, 59, 59,237, 59,238,239,240, 59, 59,241,242, 59,243, 95, 95, - 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69,244, 69, 69,245, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69,246, 69, 69, 69, 69, 69, 69, 69, 69, 69,247, 69, 69, - 69, 69,248, 95, 95, 95, 69, 69, 69, 69,249, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69,250, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,251, 95, - 95, 95, 95, 95, 95, 95,252, 95,253,254, 0, 1, 2, 2, 0, 1, - 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, - 19, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 19, 19, - 19, 19, 19, 19, 19, 0, 19, 0, 0, 0, 0, 0, 0, 0, 19, 19, - 19, 19, 19, 0, 0, 0, 0, 0, 26, 26, 0, 0, 0, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, - 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, - 9, 2, 9, 2, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 2, 9, 9, 9, 9, 9, 9, 9, 55, 55, 55, 55, 55, 55, 55, 55, - 55, 55, 55, 55, 55, 55, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, - 4, 2, 2, 4, 4, 4, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 2, 2, 2, 2, 14, 14, - 14, 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 2, 2, 2, 3, 3, - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 0, 0, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 37, 37, - 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, - 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, - 2, 2, 2, 2, 2, 2, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, - 90, 90, 90, 90, 2, 2, 90, 90, 90, 90, 90, 90, 90, 2, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 2, 2, 95, 2, 37, 37, - 37, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, - 0, 3, 3, 3, 3, 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, - 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 0, 0, 7, 7, 5, 5, - 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 5, 5, 2, - 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, - 5, 5, 5, 5, 5, 5, 5, 2, 5, 2, 2, 2, 5, 5, 5, 5, - 2, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 2, 2, 2, - 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 5, 5, 2, 5, 5, 5, - 5, 5, 2, 2, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 11, - 11, 11, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, - 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, - 11, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, - 2, 2, 11, 2, 11, 11, 11, 2, 2, 11, 11, 11, 2, 2, 2, 11, - 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, 11, 2, 11, 2, 2, 2, - 2, 2, 2, 2, 11, 11, 11, 11, 11, 11, 11, 11, 11, 2, 2, 10, - 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, - 2, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 2, - 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, 10, - 2, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 2, 2, 10, 2, - 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, - 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 2, 21, - 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, - 2, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 2, - 21, 21, 21, 21, 21, 21, 21, 2, 21, 21, 2, 21, 21, 21, 21, 21, - 2, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 2, 2, 2, 2, - 2, 2, 2, 21, 21, 21, 2, 2, 2, 2, 21, 21, 2, 21, 21, 21, - 21, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, - 22, 2, 2, 2, 22, 22, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, - 22, 2, 22, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 2, 2, 2, 2, 22, 22, 22, 2, - 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, 22, - 22, 2, 2, 2, 2, 2, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, - 23, 23, 23, 2, 23, 23, 23, 2, 23, 23, 23, 23, 23, 23, 23, 23, - 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, 23, 2, 2, 2, 2, - 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 23, 23, 2, 2, 23, 23, - 23, 23, 2, 2, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 2, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 16, - 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, 2, - 2, 2, 2, 16, 16, 2, 2, 2, 2, 2, 16, 16, 16, 2, 16, 16, - 16, 16, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, 2, 20, 20, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 2, 20, 20, 20, 2, - 20, 20, 20, 20, 20, 20, 2, 2, 2, 2, 20, 20, 20, 20, 20, 20, - 20, 20, 2, 2, 20, 20, 2, 36, 36, 36, 2, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, - 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 36, - 36, 36, 36, 2, 36, 2, 2, 2, 2, 2, 2, 2, 36, 36, 2, 2, - 36, 36, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 24, 24, 24, 24, 24, 24, 24, 24, 2, 2, 2, 2, 0, 24, 24, - 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, - 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 2, 18, 18, - 18, 18, 18, 2, 18, 2, 18, 18, 18, 18, 18, 18, 18, 2, 18, 18, - 2, 2, 18, 18, 18, 18, 25, 25, 25, 25, 25, 25, 25, 25, 2, 25, - 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 2, 2, 2, 25, 25, - 25, 25, 25, 2, 25, 25, 25, 25, 25, 25, 25, 0, 0, 0, 0, 25, - 25, 2, 2, 2, 2, 2, 33, 33, 33, 33, 33, 33, 33, 33, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 8, 2, 2, - 2, 2, 2, 8, 2, 2, 8, 8, 8, 0, 8, 8, 8, 8, 12, 12, - 12, 12, 12, 12, 12, 12, 30, 30, 30, 30, 30, 30, 30, 30, 30, 2, - 30, 30, 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 2, 30, 30, - 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 2, 2, 2, 30, 30, - 2, 2, 2, 2, 2, 2, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 2, 2, 28, 28, 28, 28, 28, 28, 28, 28, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, - 35, 35, 35, 35, 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, - 2, 2, 2, 2, 2, 2, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, - 45, 45, 45, 45, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 44, 44, - 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 0, 0, 2, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 2, 2, 2, 2, 46, 46, - 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 2, 46, 46, 46, 2, - 46, 46, 2, 2, 2, 2, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 2, 2, 31, 31, 2, 2, 2, 2, 2, 2, 32, 32, - 0, 0, 32, 0, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 2, 2, 2, 2, 2, 2, 32, 2, 2, 2, 2, 2, 2, 2, 32, 32, - 32, 2, 2, 2, 2, 2, 28, 28, 28, 28, 28, 28, 2, 2, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 2, 48, 48, - 48, 48, 2, 2, 2, 2, 48, 2, 2, 2, 48, 48, 48, 48, 52, 52, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 2, 2, 52, 52, - 52, 52, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, 58, 58, 58, 58, - 58, 58, 2, 2, 2, 2, 58, 58, 2, 2, 2, 2, 2, 2, 58, 58, - 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, - 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 91, 91, 91, - 91, 91, 91, 91, 91, 2, 91, 91, 91, 91, 91, 2, 2, 91, 91, 91, - 2, 2, 2, 2, 2, 2, 91, 91, 91, 91, 91, 91, 2, 2, 1, 1, - 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 2, 62, 62, 76, 76, - 76, 76, 76, 76, 76, 76, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, - 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 70, 70, - 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 70, 70, 70, 70, - 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 73, 73, 73, 73, 6, 6, - 6, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, 1, - 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, - 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, - 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, 9, - 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, - 9, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, 2, 9, 9, 9, - 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, 9, - 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 2, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, - 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 2, 19, 19, - 19, 19, 19, 2, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 1, 2, - 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, - 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 19, 0, - 0, 0, 2, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 2, 27, 27, - 27, 27, 27, 27, 27, 27, 0, 0, 0, 0, 2, 2, 0, 0, 56, 56, - 56, 56, 56, 56, 56, 56, 55, 55, 55, 55, 2, 2, 2, 2, 2, 55, - 55, 55, 55, 55, 55, 55, 61, 61, 61, 61, 61, 61, 61, 61, 2, 2, - 2, 2, 2, 2, 2, 61, 61, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 0, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 2, 13, 13, 13, 13, 13, 13, 13, 13, 13, 2, 2, 2, 2, 13, 13, - 13, 13, 13, 13, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, 13, - 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, - 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 2, 2, - 2, 2, 2, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 2, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, - 2, 2, 2, 2, 2, 0, 12, 12, 12, 12, 12, 12, 12, 0, 17, 17, - 17, 17, 17, 17, 17, 0, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 86, 86, - 86, 86, 86, 86, 86, 86, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 77, 77, 2, 2, 2, 2, 79, 79, 79, 79, 79, 79, 79, 79, 0, 0, - 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 19, 19, 19, 19, 2, 19, - 19, 19, 19, 19, 19, 19, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 65, 65, 65, 65, 75, 75, - 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 75, 2, 2, 2, 2, - 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 0, 69, 74, 74, - 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, 84, - 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 2, 0, 84, 84, - 2, 2, 2, 2, 84, 84, 33, 33, 33, 33, 33, 33, 33, 2, 68, 68, - 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, - 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, 92, - 92, 92, 92, 92, 92, 92, 92, 92, 92, 2, 2, 2, 2, 2, 2, 2, - 2, 92, 92, 92, 92, 92, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 2, 2, 30, 30, 30, 30, 30, 30, 2, 19, 19, - 19, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 19, 19, 19, 19, - 0, 0, 2, 2, 2, 2, 87, 87, 87, 87, 87, 87, 2, 2, 87, 87, - 2, 2, 2, 2, 2, 2, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, - 2, 12, 12, 12, 12, 12, 13, 13, 2, 2, 2, 2, 2, 2, 19, 19, - 19, 19, 19, 19, 19, 2, 2, 2, 2, 4, 4, 4, 4, 4, 2, 2, - 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, 14, - 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, 3, - 3, 3, 3, 3, 0, 0, 1, 1, 1, 1, 1, 1, 6, 6, 0, 0, - 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, - 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 2, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, - 17, 17, 17, 17, 0, 0, 2, 2, 12, 12, 12, 12, 12, 12, 2, 2, - 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, - 49, 49, 49, 49, 49, 2, 49, 49, 49, 2, 49, 49, 2, 49, 49, 49, - 49, 49, 49, 49, 2, 2, 49, 49, 49, 2, 2, 2, 2, 2, 0, 0, - 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, - 0, 0, 0, 2, 2, 2, 9, 2, 2, 2, 2, 2, 2, 2, 0, 0, - 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 71, 2, 2, 2, 67, 67, 67, 67, 67, 67, 67, 67, 67, 2, - 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 42, 42, 42, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, - 41, 2, 2, 2, 2, 2,118,118,118,118,118,118,118,118,118,118, - 118, 2, 2, 2, 2, 2, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, - 59, 59, 2, 2, 2, 2, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, - 40, 40, 40, 40, 40, 40, 51, 51, 51, 51, 51, 51, 51, 51, 50, 50, - 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 2, 2, 50, 50, - 2, 2, 2, 2, 2, 2,135,135,135,135,135,135,135,135,135,135, - 135,135, 2, 2, 2, 2,106,106,106,106,106,106,106,106,104,104, - 104,104,104,104,104,104,104,104,104,104, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2,104,161,161,161,161,161,161,161,161,161,161, - 161, 2,161,161,161,161,161,161,161, 2,161,161, 2,161,161,161, - 2,161,161,161,161,161,161,161, 2,161,161, 2, 2, 2,170,170, - 170,170,170,170,170,170,170,170,170,170, 2, 2, 2, 2,110,110, - 110,110,110,110,110,110,110,110,110,110,110,110,110, 2,110,110, - 110,110,110,110, 2, 2, 19, 19, 19, 19, 19, 19, 2, 19, 19, 2, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 2, 2, 2, 2, 2, 47, 47, - 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 2, 47, 47, 2, - 2, 2, 47, 2, 2, 47, 81, 81, 81, 81, 81, 81, 81, 81, 81, 81, - 81, 81, 81, 81, 2, 81,120,120,120,120,120,120,120,120,116,116, - 116,116,116,116,116,116,116,116,116,116,116,116,116, 2, 2, 2, - 2, 2, 2, 2, 2,116,128,128,128,128,128,128,128,128,128,128, - 128, 2,128,128, 2, 2, 2, 2, 2,128,128,128,128,128, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, - 72, 72, 72, 72, 72, 72, 72, 72, 2, 2, 2, 2, 2, 72,173,173, - 173,173,173,173,173,173,173,173, 2, 2, 2, 2, 2, 2, 98, 98, - 98, 98, 98, 98, 98, 98, 97, 97, 97, 97, 97, 97, 97, 97, 2, 2, - 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 57, 57, - 57, 57, 2, 57, 57, 2, 2, 2, 2, 2, 57, 57, 57, 57, 57, 57, - 57, 57, 2, 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, 57, - 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 57, 2, 2, 57, 57, - 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 2, 2, 2, 2, 88, 88, - 88, 88, 88, 88, 88, 88,117,117,117,117,117,117,117,117,112,112, - 112,112,112,112,112,112,112,112,112,112,112,112,112, 2, 2, 2, - 2,112,112,112,112,112, 78, 78, 78, 78, 78, 78, 78, 78, 78, 78, - 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 78, 78, 78, 78, 83, 83, - 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 83, 2, 2, 82, 82, - 82, 82, 82, 82, 82, 82, 82, 82, 82, 2, 2, 2, 2, 2,122,122, - 122,122,122,122,122,122,122,122, 2, 2, 2, 2, 2, 2, 2,122, - 122,122,122, 2, 2, 2, 2,122,122,122,122,122,122,122, 89, 89, - 89, 89, 89, 89, 89, 89, 89, 2, 2, 2, 2, 2, 2, 2,130,130, - 130,130,130,130,130,130,130,130,130, 2, 2, 2, 2, 2, 2, 2, - 130,130,130,130,130,130,144,144,144,144,144,144,144,144,144,144, - 2, 2, 2, 2, 2, 2,165,165,165,165,165,165,165,165,165,165, - 165,165,165,165, 2, 2, 2,165,165,165,165,165,165,165, 2, 2, - 2, 2, 2, 2,165,165, 3, 3, 3, 3, 3, 3, 3, 2,156,156, - 156,156,156,156,156,156,156,156, 2,156,156,156, 2, 2,156,156, - 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, - 2, 2, 2, 2, 2, 2,147,147,147,147,147,147,147,147,148,148, - 148,148,148,148,148,148,148,148, 2, 2, 2, 2, 2, 2,158,158, - 158,158,158,158,158,158,158,158, 2, 2, 2, 2, 2, 2,153,153, - 153,153,153,153,153,153,153,153,153,153, 2, 2, 2, 2,149,149, - 149,149,149,149,149,149,149,149,149,149,149,149,149, 2, 94, 94, - 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, - 94, 94, 94, 94, 94, 94, 2, 2, 2, 2, 2, 2, 2, 94, 85, 85, - 85, 85, 85, 85, 85, 85, 85, 85, 85, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 85, 2, 2,101,101,101,101,101,101,101,101,101, 2, - 2, 2, 2, 2, 2, 2,101,101, 2, 2, 2, 2, 2, 2, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 2, 96, 96,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111, 2,100,100, - 100,100,100,100,100,100, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 2, 2, 2,108,108,108,108,108,108,108,108,108,108, - 2,108,108,108,108,108,108,108, 2, 2, 2, 2, 2, 2,129,129, - 129,129,129,129,129, 2,129, 2,129,129,129,129, 2,129,129,129, - 129,129,129,129,129,129,129,129,129,129,129,129, 2,129,129,129, - 2, 2, 2, 2, 2, 2,109,109,109,109,109,109,109,109,109,109, - 109, 2, 2, 2, 2, 2,109,109, 2, 2, 2, 2, 2, 2,107,107, - 107,107, 2,107,107,107,107,107,107,107,107, 2, 2,107,107, 2, - 2,107,107,107,107,107,107,107,107,107,107,107,107,107,107, 2, - 107,107,107,107,107,107,107, 2,107,107, 2,107,107,107,107,107, - 2, 1,107,107,107,107,107, 2, 2,107,107,107, 2, 2,107, 2, - 2, 2, 2, 2, 2,107, 2, 2, 2, 2, 2,107,107,107,107,107, - 107,107, 2, 2,107,107,107,107,107,107,107, 2, 2, 2,171,171, - 171,171,171,171,171,171,171,171, 2,171, 2, 2,171, 2,171,171, - 171,171,171,171, 2,171,171, 2,171, 2, 2,171, 2,171,171,171, - 171, 2,171,171,171,171,171, 2, 2, 2, 2, 2, 2, 2, 2,171, - 171, 2, 2, 2, 2, 2,137,137,137,137,137,137,137,137,137,137, - 137,137, 2,137,137,137,137,137, 2, 2, 2, 2, 2, 2,124,124, - 124,124,124,124,124,124,124,124, 2, 2, 2, 2, 2, 2,123,123, - 123,123,123,123,123,123,123,123,123,123,123,123, 2, 2,114,114, - 114,114,114,114,114,114,114,114,114,114,114, 2, 2, 2,114,114, - 2, 2, 2, 2, 2, 2, 32, 32, 32, 32, 32, 2, 2, 2,102,102, - 102,102,102,102,102,102,102,102, 2, 2, 2, 2, 2, 2, 33, 33, - 33, 33, 2, 2, 2, 2,126,126,126,126,126,126,126,126,126,126, - 126, 2, 2,126,126,126,126,126,126,126, 2, 2, 2, 2,126,126, - 126,126,126,126,126, 2,142,142,142,142,142,142,142,142,142,142, - 142,142, 2, 2, 2, 2,125,125,125,125,125,125,125,125,125,125, - 125, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,125,154,154, - 154,154,154,154,154, 2, 2,154, 2, 2,154,154,154,154,154,154, - 154,154, 2,154,154, 2,154,154,154,154,154,154,154,154,154,154, - 154,154,154,154, 2,154,154, 2, 2,154,154,154,154,154,154,154, - 2, 2, 2, 2, 2, 2,150,150,150,150,150,150,150,150, 2, 2, - 150,150,150,150,150,150,150,150,150,150,150, 2, 2, 2,141,141, - 141,141,141,141,141,141,140,140,140,140,140,140,140,140,140,140, - 140, 2, 2, 2, 2, 2,121,121,121,121,121,121,121,121,121, 2, - 2, 2, 2, 2, 2, 2, 7, 7, 2, 2, 2, 2, 2, 2,169,169, - 169,169,169,169,169,169,169,169, 2, 2, 2, 2, 2, 2,133,133, - 133,133,133,133,133,133,133, 2,133,133,133,133,133,133,133,133, - 133,133,133,133,133, 2,133,133,133,133,133,133, 2, 2,133,133, - 133,133,133, 2, 2, 2,134,134,134,134,134,134,134,134, 2, 2, - 134,134,134,134,134,134, 2,134,134,134,134,134,134,134,134,134, - 134,134,134,134,134, 2,138,138,138,138,138,138,138, 2,138,138, - 2,138,138,138,138,138,138,138,138,138,138,138,138,138, 2, 2, - 138, 2,138,138, 2,138,138,138, 2, 2, 2, 2, 2, 2,143,143, - 143,143,143,143, 2,143,143, 2,143,143,143,143,143,143,143,143, - 143,143,143,143,143,143,143,143,143,143,143,143,143, 2,143,143, - 2,143,143,143,143,143,143, 2, 2, 2, 2, 2, 2, 2,143,143, - 2, 2, 2, 2, 2, 2,175,175,175,175,175,175,175,175,175,175, - 175,175, 2, 2, 2, 2,175,175, 2, 2, 2, 2, 2, 2,145,145, - 145,145,145,145,145,145,145, 2, 2, 2, 2, 2, 2, 2,163,163, - 163,163,163,163,163,163,163, 2,163,163,163,163,163,163,163,163, - 163, 2, 2, 2,163,163,163,163,163, 2, 2, 2, 2, 2, 86, 2, - 2, 2, 2, 2, 2, 2, 22, 22, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 22, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, - 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, 63, - 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157,157, - 157,157,157,157,157,157,157,157,157, 2, 2, 2, 2, 2, 80, 80, - 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 2, 2, 80, 80, - 80, 2, 2, 2, 2, 2,127,127,127,127,127,127,127,127,127,127, - 127,127,127,127,127, 2,166,166,166,166,166,166,166,166,166,166, - 2, 2, 2, 2, 2, 2, 79, 2, 2, 2, 2, 2, 2, 2,115,115, - 115,115,115,115,115,115,115,115,115,115,115,115,115, 2,115,115, - 2, 2, 2, 2,115,115,159,159,159,159,159,159,159,159,159,159, - 159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,103,103, - 103,103,103,103,103,103,103,103,103,103,103,103, 2, 2,119,119, - 119,119,119,119,119,119,119,119,119,119,119,119, 2, 2,119,119, - 2,119,119,119,119,119, 2, 2, 2, 2, 2,119,119,119,167,167, - 167,167,167,167,167,167,167,167, 2, 2, 2, 2, 2, 2,146,146, - 146,146,146,146,146,146,146,146,146, 2, 2, 2, 2, 2,172,172, - 172,172,172,172,172,172,172, 2, 2,172,172,172,172,172,172,172, - 172,172, 2, 2, 2, 2, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 99,136,139, - 13, 13,155, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 2,136,136, - 136,136,136,136,136,136,155,155,155,155,155,155,155,155,155,155, - 155,155,155,155, 2, 2, 2, 2, 2, 2, 2, 2, 2,155,136,136, - 136,136,136,136,136, 2,136,136,136, 2, 2, 2, 2, 2, 17, 17, - 17, 17, 2, 17, 17, 17, 17, 17, 17, 17, 2, 17, 17, 2, 17, 15, - 15, 15, 15, 15, 15, 15, 17, 17, 17, 2, 2, 2, 2, 2, 2, 2, - 15, 2, 2, 2, 2, 2, 15, 15, 15, 2, 2, 17, 2, 2, 2, 2, - 2, 2, 17, 17, 17, 17,139,139,139,139,139,139,139,139,139,139, - 139,139, 2, 2, 2, 2,105,105,105,105,105,105,105,105,105,105, - 105, 2, 2, 2, 2, 2,105,105,105,105,105, 2, 2, 2,105, 2, - 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 1, 1, - 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, 0, - 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, - 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, - 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0,131,131, - 131,131,131,131,131,131,131,131,131,131, 2, 2, 2, 2, 2, 2, - 2,131,131,131,131,131, 2,131,131,131,131,131,131,131, 2, 2, - 2, 2, 2, 19, 19, 19, 56, 56, 56, 56, 56, 56, 56, 2, 56, 2, - 2, 56, 56, 56, 56, 56, 56, 56, 2, 56, 56, 2, 56, 56, 56, 56, - 56, 2, 2, 2, 2, 2, 6, 6, 6, 6, 6, 6, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 6,151,151,151,151,151,151,151,151,151,151, - 151,151,151, 2, 2, 2,151,151,151,151,151,151, 2, 2,151,151, - 2, 2, 2, 2,151,151,160,160,160,160,160,160,160,160,160,160, - 160,160,160,160,160, 2,152,152,152,152,152,152,152,152,152,152, - 2, 2, 2, 2, 2,152,164,164,164,164,164,164,164,164,164,164, - 2, 2, 2, 2, 2, 2,168,168,168,168,168,168,168,168,168,168, - 168, 2, 2, 2, 2,168,174,174,174,174,174,174,174,174,174,174, - 174,174,174,174,174, 2,174,174,174,174,174,174, 2, 2, 2, 2, - 2, 2, 2, 2,174,174, 30, 30, 30, 30, 2, 30, 30, 2,113,113, - 113,113,113,113,113,113,113,113,113,113,113, 2, 2,113,113,113, - 113,113,113,113,113, 2,132,132,132,132,132,132,132,132,132,132, - 132,132, 2, 2, 2, 2,132,132, 2, 2, 2, 2,132,132, 3, 3, - 3, 3, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, 2, 3, 2, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 2, 3, - 2, 3, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 3, 2, 3, - 2, 3, 2, 3, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, - 3, 3, 3, 2, 3, 2, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 2, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 15, 0, - 0, 2, 2, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, - 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, - 0, 0, 0, 2, 2, 0, 13, 2, 2, 2, 2, 2, 2, 2, 13, 13, - 13, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 9, 11, 12, 13, - 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, 9, 9, 9, 9, - 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, 22, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 23, 9, - 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 26, 27, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, - 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, - 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, - 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, - 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, - 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 0, + 146,147,148,149,150,151,152,153,154, 0,155,156,157,158, 0,159, + 160,161,162,163,164,165,166,167,168,169,170, 0,171,172,173, 7, + 7, 7, 7, 7, 7, 7,174,175, 7,176, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,177, 4, + 4, 4, 4, 4, 4, 4, 4,178, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4,179, 11, 11, 11, 11,180, 0, 0, 0, 0, + 0,181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, + 12, 12, 12,182,183,184,185, 0, 0,186, 0,187,188,189,190, 3, + 3, 3, 3, 3, 3, 14, 14, 14,191,192,193, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,194,195, + 196,197, 18, 18,198, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,199,200, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 5,201, 5, 5, 5,202,203,204, 5, + 205,206,207,208,209,210, 0,211,212,213, 5, 5,214, 5,215, 10, + 10, 10, 10, 10,216, 0, 0, 0, 0, 0, 0, 0, 0,217, 0,218, + 219,220, 0, 0,221, 0, 0, 0,222, 0,223, 0,224, 0,225,226, + 227,228, 0, 0, 0, 0, 0,229,230,231, 0,232,233, 0, 0,234, + 235, 5,236,237, 0, 5, 5, 5, 5, 5, 5, 5,238, 5,239,240, + 241, 5, 5,242,243, 5,244, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,245, 1, 1,246, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,247, 1, 1, 1, + 1, 1, 1, 1, 1, 1,248, 1, 1, 1, 1,249, 0, 0, 0, 1, + 1, 1, 1,250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + 1, 1, 1, 1, 1,251, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1,252, 0, 0, 0, 0, 0, 0, 0,253, + 0, 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, + 2, 2, 2, 2, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 13, 12, + 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0,136, + 136,136,136,136,136,136,136, 80, 80, 80, 80, 80, 80, 80, 80, 19, + 19, 19, 19, 19, 19, 19, 19, 3, 3, 3, 3, 3, 3, 3, 3, 63, + 63, 63, 63, 63, 63, 63, 63, 39, 39, 39, 39, 39, 39, 39, 39, 28, + 28, 28, 28, 28, 28, 28, 28, 79, 79, 79, 79, 79, 79, 79, 79,131, + 131,131,131,131,131,131,131, 1, 1, 1, 1, 1, 1, 1, 1,127, + 127,127,127,127,127,127,127, 6, 6, 6, 6, 6, 6, 6, 6,155, + 155,155,155,155,155,155,155,139,139,139,139,139,139,139,139, 9, + 9, 9, 9, 9, 9, 9, 9, 15, 15, 15, 15, 15, 15, 15, 15, 30, + 30, 30, 30, 30, 30, 30, 30,110,110,110,110,110,110,110,110, 77, + 77, 77, 77, 77, 77, 77, 77, 17, 17, 17, 17, 17, 17, 17, 17, 27, + 27, 27, 27, 27, 27, 27, 27, 33, 33, 33, 33, 33, 33, 33, 33,113, + 113,113,113,113,113,113,113, 49, 49, 49, 49, 49, 49, 49, 49, 25, + 25, 25, 25, 25, 25, 25, 25, 29, 29, 29, 29, 29, 29, 29, 29, 7, + 7, 7, 7, 7, 7, 7, 7, 32, 32, 32, 32, 32, 32, 32, 32, 8, + 8, 8, 8, 8, 8, 8, 8, 99, 99, 99, 99, 99, 99, 99, 99, 31, + 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 2,105, + 105,105,105,105,105,105,105, 55, 55, 55, 55, 55, 55, 55, 55, 62, + 62, 62, 62, 62, 62, 62, 62, 56, 56, 56, 56, 56, 56, 56, 56, 91, + 91, 91, 91, 91, 91, 91, 91, 0, 0, 0, 0, 2, 2, 2, 2,100, + 100,100,100,100,100,100,100,119,119,119,119,119,119,119,119, 14, + 14, 14, 14, 14, 14, 14, 14,130,130,130,130,130,130,130,130, 94, + 94, 94, 94, 94, 94, 94, 94,157,157,157,157,157,157,157,157,137, + 137,137,137,137,137,137,137,146,146,146,146,146,146,146,146, 20, + 20, 20, 20, 20, 20, 20, 20, 35, 35, 35, 35, 35, 35, 35, 35, 0, + 0, 0, 0, 0, 0, 2, 2, 84, 84, 84, 84, 84, 84, 84, 84, 40, + 40, 40, 40, 40, 40, 40, 40, 97, 97, 97, 97, 97, 97, 97, 97,124, + 124,124,124,124,124,124,124,123,123,123,123,123,123,123,123,125, + 125,125,125,125,125,125,125,140,140,140,140,140,140,140,140,159, + 159,159,159,159,159,159,159,132,132,132,132,132,132,132,132, 37, + 37, 37, 37, 37, 37, 37, 37, 22, 22, 22, 22, 22, 22, 22, 22, 24, + 24, 24, 24, 24, 24, 24, 24, 58, 58, 58, 58, 58, 58, 58, 58, 76, + 76, 76, 76, 76, 76, 76, 76, 26, 26, 26, 26, 26, 26, 26, 26, 75, + 75, 75, 75, 75, 75, 75, 75, 89, 89, 89, 89, 89, 89, 89, 89,114, + 114,114,114,114,114,114,114,141,141,141,141,141,141,141,141,133, + 133,133,133,133,133,133,133,163,163,163,163,163,163,163,163, 4, + 4, 4, 4, 4, 4, 4, 4, 70, 70, 70, 70, 70, 70, 70, 70, 0, + 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 2, 2, 2, 2, 2, 68, + 68, 68, 68, 68, 68, 68, 68, 92, 92, 92, 92, 92, 92, 92, 92, 87, + 87, 87, 87, 87, 87, 87, 87,135,135,135,135,135,135,135,135, 85, + 85, 85, 85, 85, 85, 85, 85, 96, 96, 96, 96, 96, 96, 96, 96,109, + 109,109,109,109,109,109,109,102,102,102,102,102,102,102,102, 64, + 64, 64, 64, 64, 64, 64, 64, 61, 61, 61, 61, 61, 61, 61, 61, 65, + 65, 65, 65, 65, 65, 65, 65, 2, 0, 0, 0, 0, 0, 0, 0,108, + 108,108,108,108,108,108,108,142,142,142,142,142,142,142,142,121, + 121,121,121,121,121,121,121,166,166,166,166,166,166,166,166,167, + 167,167,167,167,167,167,167, 0, 2, 2, 2, 2, 2, 2, 2,151, + 151,151,151,151,151,151,151,152,152,152,152,152,152,152,152, 38, + 38, 38, 38, 38, 38, 38, 38, 90, 90, 90, 90, 90, 90, 90, 90, 23, + 23, 23, 23, 23, 23, 23, 23, 36, 36, 36, 36, 36, 36, 36, 36, 30, + 2, 30, 30, 30, 30, 2, 2, 48, 48, 48, 48, 48, 48, 48, 48, 93, + 93, 93, 93, 93, 93, 93, 93, 73, 73, 73, 73, 73, 73, 73, 73, 9, + 9, 9, 9, 9, 9, 2, 2, 13, 13, 13, 13, 13, 13, 2, 2, 86, + 86, 86, 86, 86, 86, 86, 86, 0, 0, 0, 0, 0, 2, 2, 2, 67, + 67, 67, 67, 67, 67, 67, 67, 51, 51, 51, 51, 51, 51, 51, 51,104, + 104,104,104,104,104,104,104,170,170,170,170,170,170,170,170, 78, + 78, 78, 78, 78, 78, 78, 78,144,144,144,144,144,144,144,144,165, + 165,165,165,165,165,165,165,171,171,171,171,171,171,171,171,126, + 126,126,126,126,126,126,126,150,150,150,150,150,150,150,150,134, + 134,134,134,134,134,134,134,138,138,138,138,138,138,138,138,175, + 175,175,175,175,175,175,175, 18, 18, 18, 18, 18, 18, 18, 18, 0, + 0, 0, 0, 0, 2, 0, 0, 60, 60, 60, 60, 60, 60, 60, 60, 69, + 69, 69, 69, 69, 69, 69, 69,118,118,118,118,118,118,118,118, 59, + 59, 59, 59, 59, 59, 59, 59,106,106,106,106,106,106,106,106,156, + 156,156,156,156,156,156,156,147,147,147,147,147,147,147,147,148, + 148,148,148,148,148,148,148,169,169,169,169,169,169,169,169,172, + 172,172,172,172,172,172,172,164,164,164,164,164,164,164,164,168, + 168,168,168,168,168,168,168,174,174,174,174,174,174,174,174, 0, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 0, 0, 0, 0, 0, 5, + 5, 5, 5, 5, 5, 5, 5, 21, 21, 21, 21, 21, 21, 21, 21, 16, + 16, 16, 16, 16, 16, 16, 16, 12, 12, 12, 12, 12, 12, 12, 2, 74, + 74, 74, 74, 74, 74, 74, 74, 42, 42, 42, 42, 42, 42, 42, 42, 50, + 50, 50, 50, 50, 50, 50, 50,161,161,161,161,161,161,161,161, 47, + 47, 47, 47, 47, 47, 47, 47,120,120,120,120,120,120,120,120,116, + 116,116,116,116,116,116,116, 98, 98, 98, 98, 98, 98, 98, 98, 57, + 57, 57, 57, 57, 57, 57, 57, 88, 88, 88, 88, 88, 88, 88, 88,117, + 117,117,117,117,117,117,117,112,112,112,112,112,112,112,112,101, + 101,101,101,101,101,101,101,111,111,111,111,111,111,111,111,154, + 154,154,154,154,154,154,154,143,143,143,143,143,143,143,143,115, + 115,115,115,115,115,115,115, 0, 0, 19, 0, 0, 0, 0, 0, 95, + 95, 95, 95, 95, 95, 95, 95, 11, 11, 11, 11, 11, 11, 11, 11, 10, + 10, 10, 10, 10, 10, 10, 10, 22, 22, 22, 2, 2, 2, 22, 22, 2, + 25, 25, 25, 25, 25, 25, 25, 34, 34, 34, 34, 34, 34, 34, 34, 52, + 52, 52, 52, 52, 52, 52, 52, 54, 54, 54, 54, 54, 54, 54, 54, 0, + 0, 0, 2, 2, 2, 2, 2, 2, 30, 30, 30, 30, 30, 30, 2, 2, + 2, 12, 12, 12, 12, 12, 12, 71, 71, 71, 71, 71, 71, 71, 71, 41, + 41, 41, 41, 41, 41, 41, 41, 53, 53, 53, 53, 53, 53, 53, 53, 81, + 81, 81, 81, 81, 81, 81, 81, 66, 66, 66, 66, 66, 66, 66, 66, 72, + 72, 72, 72, 72, 72, 72, 72,173,173,173,173,173,173,173,173, 83, + 83, 83, 83, 83, 83, 83, 83, 82, 82, 82, 82, 82, 82, 82, 82,158, + 158,158,158,158,158,158,158,153,153,153,153,153,153,153,153,145, + 145,145,145,145,145,145,145,103,103,103,103,103,103,103,103,160, + 160,160,160,160,160,160,160, 2, 3, 3, 2, 3, 2, 2, 3, 3, + 3, 3, 2, 3, 3, 3, 3, 19, 19, 19, 19, 19, 19, 19, 0, 2, + 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 0, 3, 3, 5, + 5, 5, 5, 5, 2, 2, 5, 11, 11, 11, 2, 2, 2, 2, 11, 10, + 10, 10, 10, 10, 10, 2, 10, 21, 21, 21, 21, 21, 2, 2, 21, 22, + 2, 22, 22, 22, 22, 2, 2, 23, 23, 23, 23, 23, 2, 23, 23, 23, + 2, 23, 23, 23, 23, 23, 23, 16, 16, 16, 16, 16, 2, 16, 16, 16, + 2, 16, 16, 16, 16, 16, 16, 20, 20, 20, 20, 20, 2, 20, 20, 20, + 2, 20, 20, 20, 20, 20, 20, 36, 36, 36, 36, 36, 36, 36, 2, 25, + 25, 25, 25, 25, 2, 25, 25, 8, 8, 8, 8, 8, 8, 2, 8, 2, + 2, 2, 2, 2, 8, 2, 2, 29, 29, 29, 29, 29, 29, 2, 2, 45, + 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 44, 44, 44, 44, 43, + 43, 43, 43, 43, 43, 43, 43, 31, 31, 2, 2, 2, 2, 2, 2, 48, + 48, 48, 48, 2, 2, 2, 2, 91, 91, 2, 2, 2, 2, 2, 2, 1, + 1, 1, 1, 1, 1, 2, 2, 9, 9, 9, 9, 9, 2, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 2, 19, 19, 19, 19, 19, 2, 2, 2, 0, + 0, 0, 0, 2, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 61, 0, + 17, 17, 17, 17, 17, 17, 17, 12, 12, 12, 12, 2, 2, 2, 2, 13, + 13, 2, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 19, 2, 0, + 0, 0, 2, 0, 0, 0, 0, 49, 49, 49, 49, 49, 49, 2, 2, 0, + 0, 0, 2, 2, 2, 2, 0,135,135,135,135, 2, 2, 2, 2,161, + 161,161, 2,161,161,161,161,161,161, 2,161,161,161,161,161, 19, + 19, 19, 2, 2, 2, 2, 2,128,128,128,128,128,128,128,128, 57, + 2, 2, 2, 2, 2, 2, 2,112,112,112,112,112,112,112, 2,122, + 122,122,122,122,122,122,122,130,130,130, 2, 2, 2, 2, 2,165, + 165,165,165,165,165, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3,149, + 149,149,149,149,149,149,149, 94, 94, 94, 94, 94, 94, 2, 2,129, + 129,129,129,129,129,129,129,107,107,107,107,107, 2, 2,107,107, + 107,107,107,107,107,107,107,107,107,107,107,107, 2, 2, 2,171, + 171,171,171,171,171, 2,171,123,123,123,123,123,123, 2, 2,102, + 102, 2, 2, 2, 2, 2, 2,154,154,154,154,154,154,154, 2, 2, + 2,150,150,150,150,150,150,169,169, 2, 2, 2, 2, 2, 2,138, + 138,138,138,138,138,138, 2,103,103,103,103,103,103, 2, 2,119, + 119, 2,119,119,119,119,119, 2, 2, 0, 0, 0, 0, 0, 0, 3, + 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 19, + 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, 0, 0, 0, 0, + 0, 26, 26, 0, 0, 0, 0, 9, 9, 9, 9, 0, 9, 9, 9, 2, + 2, 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, + 9, 9, 2, 9, 2, 9, 9, 9, 9, 2, 9, 9, 9, 9, 9, 9, + 9, 55, 55, 55, 55, 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 4, + 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 2, 4, 4, 4, 2, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 2, 2, 2, 14, 14, + 14, 14, 14, 14, 2, 2, 2, 3, 3, 3, 3, 0, 3, 3, 3, 3, + 3, 3, 0, 3, 3, 3, 0, 0, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, + 3, 3, 3, 3, 3, 3, 3, 37, 37, 37, 37, 37, 37, 2, 37, 37, + 37, 37, 2, 2, 37, 37, 37, 38, 38, 2, 2, 2, 2, 2, 2, 64, + 64, 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, 2, 2, 90, + 90, 90, 90, 90, 90, 90, 2, 95, 95, 95, 95, 2, 2, 95, 2, 37, + 37, 37, 2, 2, 2, 2, 2, 3, 3, 2, 2, 2, 2, 2, 3, 3, + 3, 0, 3, 3, 3, 3, 3, 7, 1, 1, 1, 1, 7, 7, 7, 7, + 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 5, 5, 2, 5, 5, 5, 5, 5, 5, 5, + 2, 5, 2, 2, 2, 5, 5, 5, 5, 2, 2, 5, 5, 5, 5, 5, + 2, 2, 5, 5, 5, 5, 2, 2, 2, 2, 2, 2, 2, 2, 5, 2, + 2, 2, 2, 5, 5, 2, 5, 5, 5, 5, 5, 2, 2, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 2, 2, 11, 11, 11, 2, 11, 11, 11, 11, + 2, 2, 11, 11, 11, 11, 11, 11, 2, 11, 11, 11, 11, 11, 11, 11, + 2, 11, 11, 2, 11, 11, 2, 11, 11, 2, 2, 11, 2, 11, 11, 11, + 2, 2, 11, 11, 11, 2, 2, 2, 11, 2, 2, 2, 2, 2, 2, 2, + 11, 11, 11, 11, 2, 11, 2, 2, 2, 2, 2, 2, 2, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 2, 2, 10, 10, 10, 2, 10, 10, 10, 10, + 10, 2, 10, 10, 10, 10, 10, 10, 2, 10, 10, 10, 10, 10, 10, 10, + 2, 10, 10, 2, 10, 10, 10, 10, 10, 2, 2, 10, 10, 10, 10, 10, + 10, 2, 10, 10, 10, 2, 2, 10, 2, 2, 2, 2, 2, 2, 2, 10, + 10, 10, 10, 2, 2, 10, 10, 10, 10, 2, 2, 2, 2, 2, 2, 2, + 10, 10, 10, 10, 10, 10, 10, 2, 21, 21, 21, 2, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 21, 21, 21, 2, 21, 21, 21, 21, 21, 21, 21, + 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 21, 21, 21, + 2, 2, 21, 21, 21, 2, 2, 2, 2, 2, 2, 2, 21, 21, 21, 2, + 2, 2, 2, 21, 21, 2, 21, 21, 21, 21, 21, 2, 2, 21, 21, 2, + 2, 22, 22, 2, 22, 22, 22, 2, 22, 22, 2, 22, 2, 22, 22, 2, + 2, 2, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, 2, 22, 22, 22, + 2, 2, 2, 2, 2, 2, 22, 2, 2, 2, 2, 2, 2, 22, 22, 22, + 22, 22, 2, 2, 2, 2, 2, 23, 23, 2, 2, 23, 23, 23, 23, 23, + 2, 23, 23, 23, 23, 2, 2, 2, 2, 2, 2, 2, 23, 23, 2, 23, + 23, 23, 2, 23, 23, 2, 2, 23, 23, 23, 23, 2, 2, 23, 23, 2, + 2, 2, 2, 2, 2, 2, 23, 16, 16, 16, 16, 2, 16, 16, 16, 16, + 16, 2, 2, 16, 16, 16, 16, 16, 2, 16, 16, 16, 16, 2, 2, 2, + 2, 2, 2, 2, 16, 16, 2, 2, 2, 2, 2, 16, 16, 16, 2, 16, + 16, 16, 16, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, 2, 2, + 2, 2, 2, 20, 20, 20, 20, 20, 20, 20, 20, 2, 2, 20, 20, 2, + 36, 36, 36, 2, 36, 36, 36, 2, 2, 36, 36, 36, 36, 36, 36, 36, + 36, 2, 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, 2, + 2, 36, 2, 2, 2, 2, 36, 36, 36, 36, 36, 36, 2, 36, 2, 2, + 2, 2, 2, 2, 2, 36, 36, 2, 2, 36, 36, 36, 2, 2, 2, 2, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 2, 2, 2, 2, 0, 24, + 24, 24, 24, 2, 2, 2, 2, 2, 18, 18, 2, 18, 2, 18, 18, 18, + 18, 18, 2, 18, 18, 18, 18, 18, 18, 18, 18, 2, 18, 2, 18, 18, + 18, 18, 18, 18, 18, 2, 2, 18, 18, 18, 18, 18, 2, 18, 2, 18, + 18, 18, 18, 18, 18, 18, 2, 18, 18, 2, 2, 18, 18, 18, 18, 25, + 25, 25, 25, 25, 2, 2, 2, 25, 25, 25, 25, 25, 0, 0, 0, 0, + 25, 25, 2, 2, 2, 2, 2, 8, 8, 8, 0, 8, 8, 8, 8, 30, + 30, 30, 2, 2, 30, 30, 30, 30, 30, 30, 30, 30, 2, 2, 2, 30, + 30, 2, 2, 2, 2, 2, 2, 34, 34, 34, 34, 34, 2, 2, 2, 35, + 35, 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 2, 2, 2, 2, 45, + 45, 45, 45, 45, 45, 2, 2, 2, 2, 2, 2, 2, 2, 2, 45, 44, + 44, 44, 44, 44, 0, 0, 2, 43, 43, 43, 43, 2, 2, 2, 2, 46, + 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 2, 46, 46, 46, + 2, 46, 46, 2, 2, 2, 2, 31, 31, 31, 31, 31, 31, 2, 2, 32, + 32, 0, 0, 32, 0, 32, 32, 32, 32, 2, 2, 2, 2, 2, 2, 32, + 2, 2, 2, 2, 2, 2, 2, 32, 32, 32, 2, 2, 2, 2, 2, 28, + 28, 28, 28, 28, 28, 2, 2, 48, 48, 48, 48, 48, 48, 48, 2, 48, + 2, 2, 2, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 2, 2, 52, + 52, 52, 52, 52, 2, 2, 2, 58, 58, 58, 58, 2, 2, 2, 2, 58, + 58, 2, 2, 2, 2, 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, + 54, 54, 54, 2, 2, 54, 54, 91, 91, 91, 91, 91, 91, 91, 2, 91, + 91, 91, 91, 91, 2, 2, 91, 91, 91, 91, 91, 91, 91, 2, 2, 1, + 1, 1, 1, 2, 2, 2, 2, 62, 62, 62, 62, 62, 2, 62, 62, 93, + 93, 93, 93, 2, 2, 2, 2, 2, 2, 2, 2, 93, 93, 93, 93, 2, + 2, 2, 70, 70, 70, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, 6, + 6, 6, 2, 2, 2, 2, 2, 8, 8, 8, 2, 2, 8, 8, 8, 1, + 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, + 1, 0, 2, 2, 2, 2, 2, 19, 19, 19, 19, 19, 19, 9, 9, 9, + 9, 9, 6, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 9, 9, 9, + 9, 19, 19, 19, 19, 9, 9, 9, 9, 9, 19, 19, 19, 19, 19, 6, + 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 9, 2, + 9, 2, 9, 2, 9, 2, 9, 9, 9, 9, 9, 2, 2, 9, 9, 9, + 9, 9, 9, 2, 9, 9, 9, 2, 2, 9, 9, 9, 2, 9, 9, 0, + 0, 0, 0, 1, 1, 0, 0, 0, 19, 2, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 19, 1, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 0, 9, 0, 0, 0, 19, 19, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 19, 0, 19, 0, 0, 0, 2, 2, 2, 2, 55, + 55, 55, 55, 2, 2, 2, 2, 2, 55, 55, 55, 55, 55, 55, 55, 61, + 2, 2, 2, 2, 2, 2, 2, 13, 13, 2, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 2, 2, 2, 2, 0, 0, 0, 0, 0, 13, 0, 13, 0, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 13, + 13, 13, 13, 0, 0, 0, 0, 2, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 2, 2, 1, 1, 0, 0, 15, 15, 15, 17, + 17, 17, 0, 0, 17, 17, 17, 2, 2, 2, 2, 2, 26, 26, 26, 2, + 12, 12, 12, 12, 12, 12, 12, 2, 2, 2, 2, 2, 2, 2, 0, 12, + 12, 12, 12, 12, 12, 12, 0, 17, 17, 17, 17, 17, 17, 17, 0, 39, + 39, 39, 39, 39, 2, 2, 2, 39, 39, 39, 39, 39, 39, 39, 2, 77, + 77, 77, 77, 2, 2, 2, 2, 0, 0, 19, 19, 19, 19, 19, 19, 0, + 0, 0, 19, 19, 19, 19, 19, 2, 19, 19, 19, 19, 19, 19, 19, 60, + 60, 60, 60, 60, 2, 2, 2, 75, 75, 75, 75, 75, 75, 2, 2, 2, + 2, 2, 2, 2, 2, 75, 75, 75, 75, 2, 2, 2, 2, 2, 2, 69, + 69, 69, 69, 69, 69, 0, 69, 74, 74, 74, 74, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 74, 12, 12, 12, 12, 12, 2, 2, 2, 84, + 84, 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, + 33, 33, 33, 33, 33, 33, 2, 68, 68, 68, 68, 68, 68, 68, 2, 68, + 68, 68, 68, 68, 68, 2, 2, 68, 68, 2, 2, 68, 68, 68, 68, 92, + 92, 92, 2, 2, 2, 2, 2, 2, 2, 2, 92, 92, 92, 92, 92, 87, + 87, 87, 87, 87, 87, 87, 2, 19, 19, 19, 0, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, 2, 87, + 87, 87, 87, 87, 87, 2, 2, 87, 87, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 12, 12, 12, 12, 12, 2, 2, 2, 4, 4, 4, 4, 4, 2, + 2, 2, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 2, 14, + 14, 14, 14, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, 14, 3, + 3, 3, 3, 3, 3, 0, 0, 1, 1, 1, 1, 1, 1, 6, 6, 3, + 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 0, 0, + 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 2, + 2, 12, 12, 12, 2, 2, 2, 2, 0, 0, 0, 0, 0, 2, 2, 49, + 49, 49, 49, 2, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 2, 49, + 49, 49, 2, 49, 49, 2, 49, 49, 49, 49, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 2, 2, 2, 0, 9, 2, 2, 2, 2, 2, 2, 2, 0, + 0, 0, 0, 0, 1, 2, 2, 71, 71, 71, 71, 71, 2, 2, 2, 67, + 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 42, + 42, 42, 42, 2, 2, 2, 2, 2, 2, 2, 2, 2, 42, 42, 42, 41, + 41, 41, 2, 2, 2, 2, 2,118,118,118, 2, 2, 2, 2, 2, 53, + 53, 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 2, 2, 2, 2, 59, + 59, 59, 59, 59, 59, 2, 2, 50, 50, 50, 50, 50, 50, 2, 2, 50, + 50, 2, 2, 2, 2, 2, 2,104,104,104,104, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2,104,161,161,161, 2,161,161, 2,161,161, + 161, 2,161,161, 2, 2, 2,170,170,170,170, 2, 2, 2, 2,110, + 110,110,110,110,110,110, 2,110,110,110,110,110,110, 2, 2, 19, + 19, 19, 19, 19, 19, 2, 19, 19, 2, 19, 19, 19, 19, 19, 19, 47, + 47, 47, 47, 47, 47, 2, 2, 47, 2, 47, 47, 47, 47, 47, 47, 47, + 47, 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, + 81, 81, 81, 81, 81, 2, 81,116,116,116,116,116,116,116, 2, 2, + 2, 2, 2, 2, 2, 2,116,128,128,128, 2,128,128, 2, 2, 2, + 2, 2,128,128,128,128,128, 66, 66, 66, 66, 2, 2, 2, 66, 72, + 72, 2, 2, 2, 2, 2, 72,173,173, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 97, 97, 97, 97, 2, 2, 97, 97, 97, 97, 97, 97, 57, + 57, 57, 57, 2, 57, 57, 2, 2, 2, 2, 2, 57, 57, 57, 57, 57, + 57, 57, 57, 2, 57, 57, 57, 2, 57, 57, 57, 57, 57, 57, 57, 57, + 57, 57, 57, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 2, + 2, 2,112,112,112,112,112, 78, 78, 78, 78, 78, 78, 2, 2, 2, + 78, 78, 78, 78, 78, 78, 78, 83, 83, 83, 83, 83, 83, 2, 2, 82, + 82, 82, 2, 2, 2, 2, 2,122,122, 2, 2, 2, 2, 2, 2, 2, + 122,122,122,122, 2, 2, 2, 2,122,122,122,122,122,122,122, 89, + 2, 2, 2, 2, 2, 2, 2, 2, 2,130,130,130,130,130,130,144, + 144, 2, 2, 2, 2, 2, 2, 2,165,165,165,165,165,165,165, 2, + 2, 2, 2, 2, 2,165,165, 3, 3, 3, 3, 3, 3, 3, 2,156, + 156, 2,156,156,156, 2, 2,156,156, 2, 2, 2, 2, 2, 2, 3, + 2, 2, 2, 2, 2, 2, 2,148,148, 2, 2, 2, 2, 2, 2,158, + 158, 2, 2, 2, 2, 2, 2,153,153,153,153, 2, 2, 2, 2,149, + 149,149,149,149,149,149, 2, 2, 2, 94, 94, 94, 94, 94, 94, 2, + 2, 2, 2, 2, 2, 2, 94, 85, 85, 85, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 85, 2, 2,101, 2, 2, 2, 2, 2, 2, 2,101, + 101, 2, 2, 2, 2, 2, 2, 96, 96, 96, 96, 96, 2, 96, 96,111, + 111,111,111,111,111,111, 2, 2, 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 2, 2, 2,108,108, 2,108,108,108,108,108,108, + 108, 2, 2, 2, 2, 2, 2,129,129,129,129,129,129,129, 2,129, + 2,129,129,129,129, 2,129,129,129,129,129,129,129, 2,129,129, + 129, 2, 2, 2, 2, 2, 2,109,109,109, 2, 2, 2, 2, 2,109, + 109, 2, 2, 2, 2, 2, 2,107,107,107,107, 2,107,107,107,107, + 2, 2,107,107,107,107,107,107, 2,107,107,107,107,107,107,107, + 2,107,107, 2,107,107,107,107,107, 2, 1,107,107,107,107,107, + 2, 2,107,107,107, 2, 2,107, 2, 2, 2, 2, 2, 2,107, 2, + 2, 2, 2, 2,107,107,107,107,107,107,107, 2, 2,107,107,171, + 171, 2,171, 2, 2,171, 2,171, 2,171, 2, 2,171, 2,171,171, + 171,171, 2,171,171,171,171,171, 2, 2, 2, 2, 2, 2, 2, 2, + 171,171, 2, 2, 2, 2, 2,137,137,137,137, 2,137,137,137,137, + 137, 2, 2, 2, 2, 2, 2,124,124, 2, 2, 2, 2, 2, 2,114, + 114,114,114,114, 2, 2, 2,114,114, 2, 2, 2, 2, 2, 2, 32, + 32, 32, 32, 32, 2, 2, 2, 33, 33, 33, 33, 2, 2, 2, 2,126, + 126,126, 2, 2,126,126,126,126,126,126,126, 2, 2, 2, 2,126, + 126,126,126,126,126,126, 2,142,142,142,142, 2, 2, 2, 2,125, + 125,125, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,125, 2, + 154, 2, 2,154,154,154,154,154,154,154,154, 2,154,154, 2,154, + 154,154,154,154,154, 2,154,154, 2, 2,154,154,154,154,154,154, + 154, 2, 2, 2, 2, 2, 2,150,150,150,150,150, 2, 2, 2,140, + 140,140, 2, 2, 2, 2, 2,121, 2, 2, 2, 2, 2, 2, 2, 7, + 7, 2, 2, 2, 2, 2, 2,133, 2,133,133,133,133,133,133,133, + 133,133,133,133,133,133, 2,133,133,133,133,133,133, 2, 2,133, + 133,133,133,133, 2, 2, 2, 2, 2,134,134,134,134,134,134, 2, + 134,134,134,134,134,134,134,134,134,134,134,134,134,134, 2,138, + 138, 2,138,138,138,138,138, 2, 2,138, 2,138,138, 2,138,138, + 138, 2, 2, 2, 2, 2, 2,143,143,143,143,143,143, 2,143,143, + 2,143,143,143,143,143,143,143,143,143,143,143,143,143, 2,143, + 143, 2,143,143,143,143,143,143, 2, 2, 2, 2, 2, 2, 2,143, + 143, 2, 2, 2, 2, 2, 2,175,175,175,175, 2, 2, 2, 2,175, + 175, 2, 2, 2, 2, 2, 2,145, 2, 2, 2, 2, 2, 2, 2,163, + 2,163,163,163,163,163,163,163,163,163, 2, 2, 2,163,163,163, + 163,163, 2, 2, 2, 2, 2, 86, 2, 2, 2, 2, 2, 2, 2, 22, + 22, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 22, 63, + 63, 2, 2, 2, 2, 2, 2, 63, 63, 63, 63, 63, 63, 63, 2, 63, + 63, 63, 63, 63, 2, 2, 2, 63, 63, 63, 63, 2, 2, 2, 2,157, + 157,157, 2, 2, 2, 2, 2, 80, 80, 80, 80, 80, 80, 2, 2, 80, + 80, 80, 2, 2, 2, 2, 2,127,127,127,127,127,127,127, 2,166, + 166, 2, 2, 2, 2, 2, 2, 79, 2, 2, 2, 2, 2, 2, 2,115, + 115,115,115,115,115,115, 2,115,115, 2, 2, 2, 2,115,115,159, + 159,159,159,159,159,159, 2,159,159, 2, 2, 2, 2, 2, 2,119, + 119,119,119,119,119, 2, 2, 2, 2, 2, 2, 2,119,119,119,167, + 167, 2, 2, 2, 2, 2, 2,146,146,146, 2, 2, 2, 2, 2,172, + 2, 2,172,172,172,172,172,172,172,172,172, 2, 2, 2, 2, 99, + 99, 99, 2, 2, 2, 2, 99, 2, 2, 2, 2, 2, 2, 2, 99,136, + 139, 13, 13,155, 2, 2, 2, 13, 13, 13, 13, 13, 13, 13, 2,155, + 155,155,155,155,155, 2, 2, 2, 2, 2, 2, 2, 2, 2,155,136, + 136,136,136,136,136,136, 2,136,136,136, 2, 2, 2, 2, 2, 17, + 17, 17, 17, 2, 17, 17, 17, 17, 17, 17, 17, 2, 17, 17, 2, 17, + 15, 15, 15, 15, 15, 15, 15, 17, 17, 17, 2, 2, 2, 2, 2, 2, + 2, 15, 2, 2, 2, 2, 2, 15, 15, 15, 2, 2, 17, 2, 2, 2, + 2, 2, 2, 17, 17, 17, 17,139,139,139,139, 2, 2, 2, 2,105, + 105,105, 2, 2, 2, 2, 2,105,105,105,105,105, 2, 2, 2,105, + 2, 2, 2, 2, 2, 2, 2,105,105, 2, 2,105,105,105,105, 1, + 1, 1, 1, 1, 1, 1, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 0, 0, 2, 2, 0, 2, 2, 0, 0, 2, 2, + 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, + 0, 0, 2, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0,131, + 131,131,131, 2, 2, 2, 2, 2, 2, 2,131,131,131,131,131, 2, + 131,131,131,131,131,131,131, 2, 2, 2, 2, 2, 19, 19, 19, 56, + 56, 56, 56, 56, 56, 56, 2, 56, 2, 2, 56, 56, 56, 56, 56, 56, + 56, 2, 56, 56, 2, 56, 56, 56, 56, 56, 2, 2, 2, 2, 2, 6, + 6, 6, 6, 6, 6, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6,151, + 151,151,151,151, 2, 2, 2,151,151,151,151,151,151, 2, 2,151, + 151, 2, 2, 2, 2,151,151,160,160,160,160,160,160,160, 2,152, + 152, 2, 2, 2, 2, 2,152,164,164, 2, 2, 2, 2, 2, 2,168, + 168,168, 2, 2, 2, 2,168,174,174,174,174,174,174,174, 2,174, + 174,174,174,174,174, 2, 2, 2, 2, 2, 2, 2, 2,174,174, 30, + 30, 30, 30, 2, 30, 30, 2,113,113,113,113,113, 2, 2,113,113, + 113,113,113,113,113,113, 2,132,132,132,132, 2, 2, 2, 2,132, + 132, 2, 2, 2, 2,132,132, 3, 3, 3, 3, 2, 3, 3, 3, 2, + 3, 3, 3, 3, 3, 3, 3, 2, 3, 2, 3, 2, 2, 2, 2, 2, + 2, 3, 2, 2, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 2, + 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 2, 3, 2, 2, + 3, 3, 3, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 0, 0, 15, 0, 0, 2, 2, 2, 2, 2, 0, + 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, + 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 13, + 2, 2, 2, 2, 2, 2, 2, 13, 13, 13, 2, 2, 2, 2, 2, 2, + 0, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 0, 0, 0, 10, 0, 11, 12, 13, 0, 0, 0, 14, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 69, - 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 72, 73, 74, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, - 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0, - 110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 118,119,120,121, 0,122,123,124,125,126, 0,127, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,129,130,131, - 132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147, - 148,149,150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160, - 161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,162, 0,163, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,164,165, 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 167, 0, 0, 0,168,169, 0, 0,170, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,171, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,172, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,173, + 0, 0, 0, 16, 17, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 19, + 20, 21, 0, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,174, 0, + 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,176,177, 0, 0, 0, 0,178,179, 0, 0, 0,180,181,182,183, - 184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199, - 200,201,202,203,204,205,206,207,208,209,210,211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, + 28, 29, 30, 0, 0, 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, + 0, 36, 37, 38, 39, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 41, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, + 0, 46, 47, 0, 0, 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, + 0, 53, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, + 0, 55, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, + 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, + 65, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99,100,101,102,103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, + 0,107, 0, 0, 0,108, 0,109, 0,110, 0,111,112,113, 0,114, + 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,117, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,118,119,120,121, 0,122,123, + 124,125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,128,129,130,131,132,133,134,135,136,137,138, + 139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154, + 155,156,157, 0, 0, 0,158,159,160,161, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,162, 0,163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, + 0, 0, 0, 0, 0, 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, + 0,170, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0, + 178,179, 0, 0, 0,180,181,182,183,184,185,186,187,188,189,190, + 191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206, + 207,208,209,210,211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, }; -static const uint16_t _hb_ucd_u16[10832]= +static const uint16_t _hb_ucd_u16[10784]= { - 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, - 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, - 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, - 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, - 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, - 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, - 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, - 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, - 48, 67, 68, 69, 48, 70, 71, 48, 72, 73, 48, 48, 74, 32, 75, 32, - 76, 48, 48, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 83, 84, 91, 92, 93, 94, 95, 96, 97, 84, 98, 99, 100, 88, 101, - 102, 83, 84, 103, 104, 105, 88, 106, 107, 108, 109, 110, 111, 112, 94, 113, - 114, 115, 84, 116, 117, 118, 88, 119, 120, 115, 84, 121, 122, 123, 88, 124, - 125, 115, 48, 126, 127, 128, 88, 129, 130, 131, 48, 132, 133, 134, 94, 135, - 136, 48, 48, 137, 138, 139, 140, 140, 141, 48, 142, 143, 144, 145, 140, 140, - 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 140, 140, - 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 168, 169, 48, 48, 168, 48, 48, 170, 171, 172, 48, 48, - 48, 171, 48, 48, 48, 173, 174, 175, 48, 176, 9, 9, 9, 9, 9, 177, - 178, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, - 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, - 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, - 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, - 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 32, 216, 217, 140, - 218, 48, 48, 219, 220, 160, 221, 222, 223, 48, 224, 64, 48, 48, 225, 226, - 48, 48, 227, 228, 229, 64, 48, 230, 231, 9, 9, 232, 233, 234, 235, 236, - 11, 11, 237, 27, 27, 27, 238, 239, 11, 240, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, - 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, - 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 263, 264, 265, 266, - 267, 268, 269, 270, 271, 272, 273, 273, 274, 275, 276, 209, 277, 278, 209, 279, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 281, 209, 282, 209, 209, 209, 209, 283, 209, 284, 280, 285, 209, 286, 287, 209, - 209, 209, 176, 140, 288, 140, 272, 272, 272, 289, 209, 209, 209, 209, 290, 272, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 291, 292, 209, 209, 293, - 209, 209, 209, 209, 209, 209, 294, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 295, 296, 272, 297, 209, 209, 298, 280, 299, 280, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 280, 280, 280, 280, 280, 280, 280, 280, 300, 301, 280, 280, 280, 302, 280, 303, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 209, 209, 209, 280, 304, 209, 209, 305, 209, 209, 209, 209, 209, 209, 209, 209, - 9, 9, 9, 11, 11, 11, 306, 307, 13, 13, 13, 13, 13, 13, 308, 309, - 11, 11, 310, 48, 48, 48, 311, 312, 48, 313, 314, 314, 314, 314, 32, 32, - 315, 316, 317, 318, 319, 320, 140, 140, 209, 321, 209, 209, 209, 209, 209, 322, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, 140, 209, - 324, 325, 326, 327, 136, 48, 48, 48, 48, 328, 178, 48, 48, 48, 48, 329, - 330, 48, 48, 136, 48, 48, 48, 48, 200, 331, 48, 48, 209, 209, 332, 48, - 209, 333, 334, 209, 335, 336, 209, 209, 334, 209, 209, 336, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, - 48, 337, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 230, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 339, 48, 340, 140, 13, 13, 341, 342, 13, 343, 48, 48, 48, 48, 344, 345, - 31, 346, 347, 348, 13, 13, 13, 349, 350, 351, 352, 353, 354, 355, 140, 356, - 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, - 64, 48, 365, 48, 366, 367, 48, 151, 76, 48, 48, 368, 369, 370, 371, 372, - 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, - 383, 384, 314, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 48, 389, 48, 48, 206, - 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, - 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, - 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, - 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, - 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, - 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, - 422, 272, 272, 423, 273, 273, 273, 424, 425, 426, 427, 140, 140, 209, 209, 428, - 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, - 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, - 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, - 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 48, 48, 48, 388, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 313, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, - 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, - 48, 454, 48, 455, 48, 207, 140, 140, 48, 48, 48, 456, 272, 457, 272, 272, - 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, - 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, - 48, 48, 475, 192, 476, 9, 477, 11, 478, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 272, 479, 48, 48, 480, 481, 482, 483, 140, 484, - 48, 464, 485, 48, 62, 486, 140, 48, 487, 140, 140, 48, 488, 140, 48, 313, - 489, 48, 48, 490, 491, 457, 492, 493, 223, 48, 48, 494, 495, 48, 196, 192, - 496, 48, 497, 498, 499, 48, 48, 500, 223, 48, 48, 501, 502, 503, 504, 505, - 48, 97, 506, 507, 508, 140, 140, 140, 509, 510, 511, 48, 48, 512, 513, 192, - 514, 83, 84, 515, 516, 517, 518, 519, 520, 48, 48, 521, 522, 523, 524, 140, - 48, 48, 48, 525, 526, 527, 481, 140, 48, 48, 48, 528, 529, 192, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 530, 531, 532, 533, 140, 140, - 48, 48, 48, 534, 535, 192, 536, 140, 48, 48, 537, 538, 192, 539, 540, 140, - 48, 541, 542, 543, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 506, 544, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 545, - 546, 547, 48, 548, 549, 192, 140, 140, 140, 140, 550, 48, 48, 551, 552, 140, - 553, 48, 48, 554, 555, 556, 48, 48, 557, 558, 559, 48, 48, 48, 48, 196, - 560, 140, 140, 140, 140, 140, 561, 140, 140, 140, 140, 140, 48, 48, 562, 192, - 84, 48, 530, 563, 564, 148, 175, 565, 48, 566, 567, 568, 140, 140, 140, 140, - 569, 48, 48, 570, 571, 192, 572, 48, 573, 574, 192, 48, 48, 575, 192, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 576, - 577, 115, 48, 578, 579, 580, 140, 140, 140, 140, 140, 100, 272, 581, 582, 583, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, - 273, 273, 273, 273, 273, 273, 584, 585, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 388, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 586, - 48, 48, 48, 587, 588, 589, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 71, - 48, 48, 48, 48, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 590, 591, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 592, - 48, 48, 48, 593, 594, 595, 596, 597, 48, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 598, 48, 599, 192, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 9, 9, 11, 11, 272, 600, 9, 601, 11, 602, 140, 140, - 48, 48, 48, 48, 603, 604, 605, 605, 606, 607, 140, 140, 140, 140, 608, 609, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 610, - 48, 200, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 48, 611, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 612, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 611, 613, 140, 614, 615, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 48, 48, 71, 151, 196, 616, 617, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 618, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 619, 209, 427, 209, 620, - 32, 32, 216, 32, 621, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, - 209, 209, 622, 209, 209, 209, 623, 624, 625, 209, 626, 209, 209, 209, 288, 140, - 209, 209, 209, 209, 627, 140, 140, 140, 140, 140, 140, 140, 272, 628, 272, 628, - 209, 209, 209, 209, 209, 338, 272, 461, 140, 140, 140, 140, 140, 140, 140, 140, - 9, 629, 11, 630, 631, 632, 242, 9, 633, 634, 635, 636, 637, 9, 629, 11, - 638, 639, 11, 640, 641, 642, 643, 9, 644, 11, 9, 629, 11, 630, 631, 11, - 242, 9, 633, 643, 9, 644, 11, 9, 629, 11, 645, 9, 646, 647, 648, 649, - 11, 650, 9, 651, 652, 653, 654, 11, 655, 9, 656, 11, 657, 539, 539, 539, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 32, 32, 32, 658, 32, 32, 659, 660, 661, 662, 45, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 663, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 666, 667, 668, 27, 27, 27, 669, 140, 670, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 151, 671, 672, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 673, 140, 48, 48, 674, 675, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 676, 192, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 590, 677, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 200, 678, 679, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 680, 200, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 681, 621, 140, 140, - 9, 9, 633, 11, 682, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 504, 272, 272, 683, 684, 140, 140, 140, 140, - 504, 272, 685, 686, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 687, 48, 688, 689, 690, 691, 692, 693, 694, 206, 695, 206, 140, 140, 140, 696, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 209, 209, 697, 209, 209, 209, 209, 209, 209, 322, 333, 698, 698, 698, 209, 323, - 699, 209, 209, 209, 209, 209, 209, 209, 209, 209, 700, 140, 140, 140, 701, 209, - 702, 209, 209, 697, 703, 704, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 705, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 706, 426, 426, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 176, 697, 427, - 697, 209, 209, 209, 707, 176, 209, 209, 707, 209, 700, 697, 704, 708, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 707, 700, 426, 709, 209, 209, 209, 710, 711, 712, 703, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 713, 209, 209, 209, 209, 209, 714, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 100, 48, - 48, 48, 48, 48, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 715, 140, 587, 587, 587, 587, 587, 587, 140, 140, 140, 140, 140, 140, 140, 140, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 716, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 717, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, - 0, 0, 0, 0, 0, 4, 0, 4, 2, 2, 5, 2, 2, 2, 5, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 6, 0, 0, 0, 0, 7, 8, 0, 0, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 11, - 12, 13, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, 16, 17, 14, 14, - 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 19, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 18, 20, 21, 21, 21, 22, 20, 21, 21, 21, 21, - 21, 23, 24, 25, 25, 25, 25, 25, 25, 26, 25, 25, 25, 27, 28, 26, - 29, 30, 31, 32, 31, 31, 31, 31, 33, 34, 35, 31, 31, 31, 36, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 29, 31, 31, 31, 31, - 37, 38, 37, 37, 37, 37, 37, 37, 37, 39, 31, 31, 31, 31, 31, 31, - 40, 40, 40, 40, 40, 40, 41, 26, 42, 42, 42, 42, 42, 42, 42, 43, - 44, 44, 44, 44, 44, 45, 44, 46, 47, 47, 47, 48, 37, 49, 31, 31, - 31, 31, 50, 31, 31, 31, 31, 31, 31, 31, 31, 31, 51, 31, 31, 31, - 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 52, 54, 52, 52, 52, - 55, 56, 57, 58, 58, 59, 60, 61, 56, 62, 63, 64, 65, 58, 58, 66, - 67, 68, 69, 70, 70, 71, 72, 73, 68, 74, 75, 76, 77, 70, 78, 26, - 79, 80, 81, 82, 82, 83, 84, 85, 80, 86, 87, 26, 88, 82, 89, 90, - 91, 92, 93, 94, 94, 95, 96, 97, 92, 98, 99, 100, 101, 94, 94, 26, - 102, 103, 104, 105, 106, 103, 107, 108, 103, 104, 109, 26, 110, 107, 107, 111, - 112, 113, 114, 112, 112, 114, 112, 115, 113, 116, 117, 118, 119, 112, 120, 112, - 121, 122, 123, 121, 121, 123, 124, 125, 122, 126, 127, 128, 129, 121, 130, 26, - 131, 132, 133, 131, 131, 131, 131, 131, 132, 133, 134, 131, 135, 131, 131, 131, - 136, 137, 138, 139, 137, 137, 140, 141, 138, 142, 143, 137, 144, 137, 145, 26, - 146, 147, 147, 147, 147, 147, 147, 148, 147, 147, 147, 149, 26, 26, 26, 26, - 150, 151, 152, 152, 153, 152, 152, 154, 155, 156, 152, 157, 26, 26, 26, 26, - 158, 158, 158, 158, 158, 158, 158, 158, 158, 159, 158, 158, 158, 160, 159, 158, - 158, 158, 158, 159, 158, 158, 158, 161, 158, 161, 162, 163, 26, 26, 26, 26, - 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, 164, - 164, 164, 164, 164, 165, 165, 165, 165, 166, 167, 165, 165, 165, 165, 165, 168, - 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, 169, - 170, 170, 170, 170, 170, 170, 170, 170, 170, 171, 172, 171, 170, 170, 170, 170, - 170, 171, 170, 170, 170, 170, 171, 172, 171, 170, 172, 170, 170, 170, 170, 170, - 170, 170, 171, 170, 170, 170, 170, 170, 170, 170, 170, 173, 170, 170, 170, 174, - 170, 170, 170, 175, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 177, 177, - 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, 178, - 179, 179, 179, 180, 181, 181, 181, 181, 181, 181, 181, 181, 181, 182, 181, 183, - 184, 184, 185, 186, 187, 187, 188, 26, 189, 189, 190, 26, 191, 192, 193, 26, - 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 194, 195, 194, 196, 194, 196, - 197, 198, 198, 199, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 198, 200, - 198, 198, 198, 198, 198, 201, 178, 178, 178, 178, 178, 178, 178, 178, 202, 26, - 203, 203, 203, 204, 203, 205, 203, 205, 206, 203, 207, 207, 207, 208, 209, 26, - 210, 210, 210, 210, 210, 211, 210, 210, 210, 212, 210, 213, 194, 194, 194, 194, - 214, 214, 214, 215, 216, 216, 216, 216, 216, 216, 216, 217, 216, 216, 216, 218, - 216, 219, 216, 219, 216, 220, 9, 9, 9, 9, 9, 221, 9, 222, 26, 26, - 223, 223, 223, 223, 223, 223, 223, 223, 223, 224, 223, 223, 223, 223, 223, 223, - 225, 225, 225, 225, 225, 225, 225, 225, 226, 226, 226, 226, 226, 226, 227, 228, - 229, 229, 229, 229, 229, 229, 229, 230, 229, 231, 232, 232, 232, 232, 232, 232, - 18, 233, 165, 165, 165, 165, 165, 234, 225, 26, 235, 9, 236, 237, 238, 239, - 2, 2, 2, 2, 240, 241, 2, 2, 2, 2, 2, 242, 243, 244, 2, 245, - 2, 2, 2, 2, 2, 2, 2, 246, 9, 9, 9, 9, 9, 9, 9, 9, - 14, 14, 247, 247, 14, 14, 14, 14, 247, 247, 14, 248, 14, 14, 14, 247, - 14, 14, 14, 14, 14, 14, 249, 14, 249, 14, 250, 251, 14, 14, 252, 253, - 0, 254, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 256, 257, - 0, 258, 2, 259, 0, 0, 0, 0, 260, 26, 9, 9, 9, 9, 261, 26, - 0, 0, 0, 0, 262, 263, 4, 0, 0, 264, 0, 0, 2, 2, 2, 2, - 2, 265, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 260, 26, 26, 0, 266, 26, 26, 0, 0, 0, 0, - 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, 267, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, - 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 269, 2, 2, 2, 2, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 270, 271, - 165, 165, 165, 165, 166, 167, 272, 272, 272, 272, 272, 272, 272, 273, 274, 273, - 170, 170, 172, 26, 172, 172, 172, 172, 172, 172, 172, 172, 18, 18, 18, 18, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, 26, 26, 26, 26, - 276, 276, 276, 277, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 278, 26, - 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, - 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 279, 26, 26, 26, 0, 0, - 280, 0, 0, 0, 281, 282, 0, 283, 284, 285, 285, 285, 285, 285, 285, 285, - 285, 285, 286, 287, 288, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 290, - 291, 292, 292, 292, 292, 292, 293, 169, 169, 169, 169, 169, 169, 169, 169, 169, - 169, 294, 0, 0, 292, 292, 292, 292, 0, 0, 0, 0, 275, 295, 289, 289, - 169, 169, 169, 294, 0, 0, 0, 0, 0, 0, 0, 0, 169, 169, 169, 296, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 289, 289, 289, 289, 289, 297, - 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 289, 0, 0, 0, 0, 0, - 276, 276, 276, 276, 276, 276, 276, 276, 0, 0, 0, 0, 0, 0, 0, 0, - 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, 298, - 298, 299, 298, 298, 298, 298, 298, 298, 300, 26, 301, 301, 301, 301, 301, 301, - 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, 302, - 302, 302, 302, 302, 302, 303, 26, 26, 18, 18, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 26, - 0, 0, 0, 0, 305, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 306, 2, 2, 2, 2, 2, 2, 2, 2, 2, 259, 26, 26, 307, 2, - 308, 308, 308, 308, 308, 309, 0, 260, 310, 310, 310, 310, 310, 310, 310, 26, - 311, 311, 311, 311, 311, 311, 311, 311, 312, 313, 311, 314, 52, 52, 52, 52, - 315, 315, 315, 315, 315, 316, 317, 317, 317, 317, 318, 319, 169, 169, 169, 320, - 321, 321, 321, 321, 321, 321, 321, 321, 321, 322, 321, 323, 164, 164, 164, 324, - 325, 325, 325, 325, 325, 325, 326, 26, 325, 327, 325, 328, 164, 164, 164, 164, - 329, 329, 329, 329, 329, 329, 329, 329, 330, 26, 26, 331, 332, 332, 333, 26, - 334, 334, 334, 26, 172, 172, 2, 2, 2, 2, 2, 335, 336, 337, 176, 176, - 176, 176, 176, 176, 176, 176, 176, 176, 332, 332, 332, 332, 332, 338, 332, 339, - 169, 169, 169, 169, 340, 26, 169, 169, 294, 341, 169, 169, 169, 169, 169, 340, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 279, 276, 276, - 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 342, 26, 26, 26, 26, - 343, 26, 344, 345, 25, 25, 346, 347, 348, 25, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 349, 31, 31, 31, 31, 31, 31, 31, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 26, 26, 26, 26, 31, 31, - 9, 9, 0, 260, 9, 350, 0, 0, 0, 0, 351, 0, 258, 352, 353, 31, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 354, - 355, 0, 0, 0, 1, 2, 2, 3, 1, 2, 2, 3, 356, 289, 288, 289, - 289, 289, 289, 357, 169, 169, 169, 294, 358, 358, 358, 359, 258, 258, 26, 360, - 361, 362, 361, 361, 363, 361, 361, 364, 361, 365, 361, 365, 26, 26, 26, 26, - 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 361, 366, - 367, 0, 0, 0, 0, 0, 368, 0, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 253, 0, 369, 370, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 371, - 372, 372, 372, 373, 374, 374, 374, 374, 374, 374, 375, 26, 376, 0, 0, 352, - 377, 377, 377, 377, 378, 379, 380, 380, 380, 381, 382, 382, 382, 382, 382, 383, - 384, 384, 384, 385, 386, 386, 386, 386, 387, 386, 388, 26, 26, 26, 26, 26, - 389, 389, 389, 389, 389, 389, 389, 389, 389, 389, 390, 390, 390, 390, 390, 390, - 391, 391, 391, 392, 391, 393, 394, 394, 394, 394, 395, 394, 394, 394, 394, 395, - 396, 396, 396, 396, 396, 26, 397, 397, 397, 397, 397, 397, 398, 399, 400, 401, - 400, 401, 402, 400, 403, 400, 403, 404, 405, 405, 405, 405, 405, 405, 406, 26, - 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, 407, - 407, 407, 407, 407, 407, 407, 408, 26, 407, 407, 409, 26, 407, 26, 26, 26, - 410, 2, 2, 2, 2, 2, 411, 412, 26, 26, 26, 26, 26, 26, 26, 26, - 413, 414, 415, 415, 415, 415, 416, 417, 418, 418, 419, 418, 420, 420, 420, 420, - 421, 421, 421, 422, 423, 421, 26, 26, 26, 26, 26, 26, 424, 424, 425, 426, - 427, 427, 427, 428, 429, 429, 429, 430, 431, 431, 431, 432, 26, 26, 26, 26, - 433, 433, 433, 433, 434, 434, 434, 435, 434, 434, 436, 434, 434, 434, 434, 434, - 437, 438, 439, 440, 441, 441, 442, 443, 441, 444, 441, 444, 445, 445, 445, 445, - 446, 446, 446, 446, 26, 26, 26, 26, 447, 447, 447, 447, 448, 449, 448, 26, - 450, 450, 450, 450, 450, 450, 451, 452, 453, 453, 454, 453, 455, 455, 456, 455, - 457, 457, 458, 459, 26, 460, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 461, 461, 461, 461, 461, 461, 461, 461, 461, 462, 26, 26, 26, 26, 26, 26, - 463, 463, 463, 463, 463, 463, 464, 26, 463, 463, 463, 463, 463, 463, 464, 465, - 466, 466, 466, 466, 466, 26, 466, 467, 468, 468, 468, 468, 469, 470, 468, 468, - 469, 471, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 31, 31, 31, 472, - 473, 473, 473, 473, 473, 474, 475, 26, 476, 26, 31, 477, 26, 26, 26, 476, - 478, 478, 478, 478, 478, 26, 479, 479, 479, 479, 479, 480, 26, 26, 481, 481, - 481, 482, 26, 26, 26, 26, 483, 483, 483, 484, 26, 26, 485, 485, 486, 26, - 487, 487, 487, 487, 487, 487, 487, 487, 487, 488, 489, 487, 487, 487, 488, 490, - 491, 491, 491, 491, 491, 491, 491, 491, 492, 493, 494, 494, 494, 495, 494, 496, - 497, 497, 497, 497, 497, 497, 498, 497, 497, 26, 499, 499, 499, 499, 500, 26, - 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 502, 137, 503, 26, - 504, 504, 505, 504, 504, 504, 504, 504, 506, 26, 26, 26, 26, 26, 26, 26, - 507, 508, 509, 510, 509, 511, 512, 512, 512, 512, 512, 512, 512, 513, 512, 514, - 515, 516, 517, 518, 518, 519, 520, 521, 516, 522, 523, 524, 525, 526, 526, 26, - 527, 528, 527, 527, 527, 527, 529, 527, 530, 531, 529, 532, 533, 26, 26, 26, - 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 534, 535, 536, 26, 26, 26, - 537, 537, 537, 537, 537, 537, 537, 537, 537, 26, 537, 538, 26, 26, 26, 26, - 539, 539, 539, 539, 539, 539, 540, 539, 539, 539, 539, 540, 26, 26, 26, 26, - 541, 541, 541, 541, 541, 541, 541, 541, 542, 26, 541, 543, 198, 544, 26, 26, - 545, 545, 545, 545, 545, 545, 545, 546, 545, 546, 164, 164, 547, 26, 26, 26, - 548, 548, 548, 549, 548, 550, 548, 548, 551, 26, 26, 26, 26, 26, 26, 26, - 552, 552, 552, 552, 552, 552, 552, 553, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 555, 556, - 557, 558, 559, 560, 560, 560, 561, 562, 557, 26, 560, 563, 26, 26, 26, 26, - 26, 26, 26, 26, 564, 565, 564, 564, 564, 564, 564, 565, 566, 26, 26, 26, - 567, 567, 567, 567, 567, 567, 567, 567, 567, 26, 568, 568, 568, 568, 568, 568, - 568, 568, 568, 568, 569, 26, 178, 178, 570, 570, 570, 570, 570, 570, 570, 571, - 52, 572, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 501, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 573, 573, 573, 573, 574, 26, 573, 574, - 575, 576, 575, 575, 575, 575, 577, 575, 578, 26, 575, 575, 575, 579, 580, 580, - 580, 580, 581, 580, 580, 582, 583, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 584, 585, 586, 586, 586, 586, 584, 587, 586, 26, 586, 588, 589, 590, 591, 591, - 591, 592, 593, 594, 591, 595, 596, 596, 596, 596, 596, 597, 596, 598, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 599, 599, 599, 600, - 601, 601, 602, 601, 601, 601, 601, 603, 601, 601, 601, 604, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 605, 26, 107, 107, 107, 107, 107, 107, 606, 607, - 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, - 608, 608, 608, 609, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 608, 610, 611, 26, - 608, 608, 608, 608, 608, 608, 608, 608, 612, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 613, 614, 26, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 616, 26, 615, 615, 615, 615, - 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 615, 617, - 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, 618, - 618, 618, 618, 618, 618, 618, 618, 618, 619, 26, 26, 26, 26, 26, 26, 26, - 620, 620, 620, 620, 620, 620, 620, 621, 26, 26, 26, 26, 26, 26, 26, 26, - 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, 304, - 304, 304, 304, 304, 304, 304, 304, 622, 623, 623, 623, 624, 623, 625, 626, 626, - 626, 626, 626, 626, 626, 626, 626, 627, 626, 628, 629, 629, 629, 630, 630, 26, - 631, 631, 631, 631, 631, 631, 631, 631, 632, 26, 631, 633, 633, 631, 631, 634, - 631, 631, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 635, 635, 635, 635, 635, 635, 635, 636, - 26, 26, 26, 26, 26, 26, 26, 26, 637, 637, 637, 637, 637, 637, 637, 637, - 637, 637, 637, 638, 639, 639, 639, 640, 639, 639, 641, 26, 26, 26, 26, 26, - 642, 642, 642, 642, 642, 642, 642, 642, 642, 643, 642, 642, 642, 642, 642, 642, - 642, 644, 642, 642, 26, 26, 26, 26, 26, 26, 26, 26, 645, 26, 646, 26, - 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, - 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, - 648, 648, 648, 648, 648, 648, 648, 648, 648, 648, 649, 26, 26, 26, 26, 650, - 647, 647, 647, 651, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 647, 652, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 653, 654, - 655, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, - 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, 285, - 285, 285, 285, 285, 656, 26, 657, 26, 26, 26, 658, 26, 659, 26, 660, 660, - 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, - 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 660, 661, - 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 662, 663, 662, 664, - 662, 665, 662, 666, 352, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 369, - 0, 0, 0, 0, 0, 0, 352, 667, 0, 0, 668, 26, 0, 0, 668, 26, - 9, 9, 9, 9, 9, 221, 9, 9, 669, 26, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 352, 26, 26, 26, 26, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 275, 26, - 0, 0, 0, 0, 258, 355, 0, 0, 0, 0, 0, 0, 670, 671, 0, 672, - 673, 674, 0, 0, 0, 675, 0, 0, 0, 0, 0, 0, 0, 266, 26, 26, - 14, 14, 14, 14, 14, 14, 14, 14, 247, 26, 26, 26, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 352, 26, 0, 0, 352, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 258, 26, 0, 0, 0, 668, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, - 0, 0, 0, 255, 676, 677, 0, 678, 679, 0, 0, 0, 0, 0, 0, 0, - 680, 681, 255, 255, 0, 0, 0, 682, 683, 667, 684, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 275, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 268, 0, 0, 0, 0, 0, 0, - 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, 685, - 685, 686, 26, 687, 688, 685, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 2, 2, 2, 343, 689, 412, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 690, 269, 269, 691, 692, 693, 18, 18, 18, 18, 18, 18, 18, 694, 26, 26, - 26, 695, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 696, 696, 696, 696, 696, 697, 696, 698, 696, 699, 26, 26, 26, 26, 26, 26, - 26, 26, 700, 700, 700, 701, 26, 26, 702, 702, 702, 702, 702, 702, 702, 703, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 704, 704, 704, 704, 704, 705, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 706, 706, 706, 706, 706, 707, - 26, 26, 26, 26, 26, 26, 26, 26, 708, 708, 708, 709, 708, 708, 710, 711, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 172, 712, 170, 172, - 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, - 713, 713, 713, 713, 713, 713, 713, 713, 714, 713, 715, 26, 26, 26, 26, 26, - 716, 716, 716, 716, 716, 716, 716, 716, 716, 717, 716, 718, 26, 26, 26, 26, - 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 355, 0, - 0, 0, 0, 0, 0, 0, 369, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 355, 0, 0, 0, 0, 0, 0, 275, 26, 26, 26, 26, 26, 26, 26, 26, - 719, 31, 31, 31, 720, 721, 722, 723, 724, 725, 720, 726, 720, 722, 722, 727, - 31, 728, 31, 729, 730, 728, 31, 729, 26, 26, 26, 26, 26, 26, 731, 26, - 0, 0, 0, 0, 0, 352, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 352, 26, 0, 258, 355, 0, 355, 0, 355, 0, 0, 0, 275, 26, - 0, 0, 0, 0, 0, 275, 26, 26, 26, 26, 26, 26, 732, 0, 0, 0, - 733, 26, 0, 0, 0, 0, 0, 352, 0, 668, 260, 26, 275, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 734, 0, 369, 0, 369, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 260, 0, 352, 668, 26, - 0, 352, 0, 0, 0, 0, 0, 0, 0, 26, 0, 260, 0, 0, 0, 0, - 0, 26, 0, 0, 0, 275, 0, 352, 260, 26, 0, 668, 26, 26, 26, 26, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 275, 0, 369, - 0, 735, 0, 0, 0, 0, 0, 0, 258, 736, 0, 737, 0, 367, 0, 668, - 0, 0, 351, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 266, - 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 26, 26, 26, 26, - 276, 276, 276, 279, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, - 276, 276, 276, 276, 276, 279, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, - 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 738, 26, 276, 276, - 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 279, 26, 26, 26, 26, - 276, 276, 276, 279, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, - 276, 276, 276, 276, 276, 276, 276, 276, 276, 739, 276, 276, 276, 276, 276, 276, - 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 276, 342, - 740, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 224, 224, 768, 424, 432, 440, 448, 776, 224, 224, 784, 792, 72, 800, 40, 808, + 48, 48, 48, 816, 824, 48, 48, 832, 840, 848, 856, 864, 872, 880, 48, 888, + 48, 48, 48, 896, 904, 40, 40, 40, 40, 912, 40, 96, 920, 928, 936, 456, + 64, 64, 64, 64, 64, 64, 64, 944, 952, 960, 968, 40, 976, 984, 48, 992, + 72, 72, 72, 40, 40, 40, 48, 48,1000, 48, 48, 48,1008, 48, 48, 48, + 48, 48, 48,1016, 72,1024, 40, 40,1032, 464, 64,1040,1048, 8,1056,1064, + 1072,1080, 8, 8,1088, 64,1096,1104, 8, 8, 8, 8, 8,1112,1120,1128, + 1136,1144, 8, 64,1152, 8, 8, 8, 8, 8, 472,1160, 232, 8,1168,1176, + 8,1184,1192,1200, 8,1208, 176, 8,1216,1224, 8, 8,1232, 64,1240, 64, + 480, 8, 8,1248,1256,1264,1272,1280,1288, 240, 128,1296,1304,1312, 144,1320, + 1328, 240, 128,1336,1344,1352, 304,1360,1368, 488, 128,1376,1384, 248, 144,1392, + 1400, 240, 128,1408,1416,1424, 144,1432,1440,1448,1456,1464,1472,1480, 304,1488, + 1496, 256, 128,1504,1512,1520, 144,1528,1536, 256, 128,1544,1552,1560, 144,1568, + 1576, 256, 8,1584,1592,1600, 144,1608,1616,1624, 8,1632,1640,1648, 304,1656, + 312, 8, 8,1664,1672,1680, 0, 0,1688, 8,1696,1704,1712,1720, 0, 0, + 1728,1736, 320,1744,1752, 8, 152,1760,1768,1776, 64,1784,1792,1800, 0, 0, + 8, 8,1808,1816, 496,1824,1832,1840,1848,1856, 72, 72,1864, 40, 40,1872, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 504,1880, 8, 8, 504, 8, 8,1888, 512, 520, 8, 8, + 8, 512, 8, 8, 8,1896,1904, 528, 8, 264, 72, 72, 72, 72, 72,1912, + 536, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8,1920, 8,1928,1936, 8, 8, 8, 8,1944,1952, + 8,1960, 8,1968, 8,1976,1984,1992, 8, 8, 8,2000,2008,2016, 80,2024, + 2032, 80, 8, 8,2040, 8, 8, 160,2048, 8,2056, 8, 8, 8, 8, 328, + 8, 120,2064,2072,2080, 8, 112,2088, 8, 8, 184, 8, 192,2096, 24, 24, + 8,2104, 8, 8, 8,2112,2120,2128, 80, 80,2136,2144, 64, 544,2152, 0, + 2160, 8, 8,2168,2176, 496,2184,2192, 336, 8,2200, 232, 8, 8,2208,2216, + 8, 8,2224,2232,2240, 232, 8, 552,2248, 72, 72,2256,2264,2272,2280,2288, + 40, 40,2296, 96, 96, 96,2304,2312, 40,2320, 96, 96, 64, 64, 64, 64, + 48, 48, 48, 48, 48, 48, 48, 48, 48,2328, 48, 48, 48, 48, 48, 48, + 168, 560, 168, 168, 560,2336, 168,2344, 344, 344, 344,2352,2360,2368,2376,2384, + 2392,2400,2408,2416,2424,2432,2440,2448,2456,2464, 568, 568,2472,2480,2488,2496, + 2504,2512,2520,2528,2536, 88, 104, 104,2544,2552,2560, 24,2568,2576, 24,2584, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 2592, 24,2600, 24, 24, 24, 24,2608, 24,2616, 56,2624, 24,2632,2640, 24, + 24, 24, 264, 0, 576, 0, 88, 88, 88,2648, 24, 24, 24, 24,2656, 88, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,2664,2672, 24, 24,2680, + 24, 24, 24, 24, 24, 24,2688, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24,2696,2704, 88,2712, 24, 24,2720, 56,2728, 56, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 56, 56, 56, 56, 56, 56, 56, 56,2736,2744, 56, 56, 56,2752, 56,2760, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 24, 24, 24, 56,2768, 24, 24,2776, 24, 24, 24, 24, 24, 24, 24, 24, + 72, 72, 72, 40, 40, 40,2784,2792, 48, 48, 48, 48, 48, 48,2800,2808, + 40, 40,2816, 8, 8, 8,2824,2832, 8, 200, 208, 208, 208, 208, 64, 64, + 2840,2848,2856,2864,2872,2880, 0, 0, 24,2888, 24, 24, 24, 24, 24, 352, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 272, 0, 24, + 2896,2904,2912,2920, 312, 8, 8, 8, 8,2928, 536, 8, 8, 8, 8,2936, + 2944, 8, 8, 312, 8, 8, 8, 8, 120,2952, 8, 8, 24, 24,2960, 8, + 24, 584, 592, 24,2968, 600, 24, 24, 592, 24, 24, 600, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 24, 24, 24, 24, + 8,2976, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 152, 24, 24, 24, 608, 8, 8, 552, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 2984, 8,2992, 0, 48, 48,3000,3008, 48,3016, 8, 8, 8, 8,3024,3032, + 456,3040,3048,3056, 48, 48, 48,3064,3072,3080,3088,3096,3104,3112, 0,3120, + 3128, 8,3136,3144, 8, 8, 8,3152,3160, 8, 8,3168,3176, 80, 64,3184, + 232, 8,3192, 8,3200,3208, 8, 152, 480, 8, 8,3216,3224, 360,3232,3240, + 8, 8,3248,3256,3264,3272, 8,3280, 8, 8, 8,3288,3296,3304,3312,3320, + 3328,3336, 208, 40, 40,3344,3352, 40, 40, 40, 40, 40, 8, 8,3360, 80, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 368, 8,3368, 8, 8, 184, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 112, 8, 8, 8, 8, 8, 8, 192, 0, 0, + 3376,3384,3392,3400,3408, 8, 8, 8, 8, 8, 8,3416,3424,3432, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8,3440, 24, 8, 8, 8, 8,3448, 8, 8,3456, 0, 0,3464, + 64,3472, 64,3480,3488,3496,3504,3512, 8, 8, 8, 8, 8, 8, 8,3520, + 3528, 424, 432, 440, 448,3536,3544,3552, 8,3560, 8, 120,3568,3576,3584,3592, + 3600, 8, 520,3608, 112, 112, 0, 0, 8, 8, 8, 8, 8, 8, 8, 176, + 3616, 88, 88,3624, 104, 104, 104,3632,3640, 280, 376, 0, 0, 24, 24,3648, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 152, 8, 8, 8, 248,3656,3664, + 8, 8,3672, 8,3680, 8, 8,3688, 8,3696, 8, 8,3704,3712, 0, 0, + 72, 72,3720, 40, 40, 8, 8, 8, 8, 112, 80, 72, 72,3728, 40,3736, + 8, 8, 616, 8, 8, 8,3744, 624, 624,3752,3760,3768, 8, 8, 8, 368, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 200, 8, 328, 616, 0,3776, 96, 96,3784, 0, 0, 0, 0, + 3792, 8, 8,3800, 8,3808, 8,3816, 8, 120,3824, 0, 0, 0, 8,3832, + 8,3840, 8,3848, 8, 192, 0, 0, 8, 8, 8,3856, 88, 632, 88, 88, + 3864,3872, 8,3880, 640,3888, 8,3896, 8, 648, 0, 0,3904, 8,3912,3920, + 8, 8, 8,3928, 8,3936, 8,3944, 8,3952,3960, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 160, 0, 0, 0, 72, 72, 72,3968, 40, 40, 40,3976, + 8, 8,3984, 80,3992, 72,4000, 40,4008, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 88,4016, 8, 8,4024, 656,4032,4040, 0,4048, + 8, 648,4056, 8, 472,4064, 0, 8,4072, 0, 0, 8,4080, 0, 8, 200, + 4088, 8, 8,4096,4104, 632,4112,4120, 336, 8, 8,4128,4136, 8, 160, 80, + 4144, 8,4152,4160,4168, 8, 8,4176, 336, 8, 8,4184,4192,4200, 384,4208, + 8, 488, 664,4216,4224, 0, 0, 0,4232,4240,4248, 8, 8,4256,4264, 80, + 4272, 240, 128,4280,4288,4296,4304,4312,4320, 8, 8,4328,4336,4344,4352, 0, + 8, 8, 8,4360,4368,4376, 656, 0, 8, 8, 8,4384,4392, 80, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 672,4400,4408,4416, 0, 0, + 8, 8, 8,4424,4432, 80,4440, 0, 8, 8,4448,4456, 80, 288,4464, 0, + 8,4472,4480,4488, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 664,4496, 0, 0, 0, 0, 0, 0, 72, 72, 40, 40, 320,4504, + 4512,4520, 8,4528,4536, 80, 0, 0, 0, 0,4544, 8, 8,4552,4560, 0, + 4568, 8, 8,4576,4584,4592, 8, 8,4600,4608,4616, 8, 8, 8, 8, 160, + 4624, 0, 0, 0, 0, 0,4632, 0, 0, 0, 0, 0, 8, 8,4640, 80, + 128, 8, 672,4648,4656, 320, 528,4664, 8,4672,4680,4688, 0, 0, 0, 0, + 4696, 8, 8,4704,4712, 80,4720, 8,4728,4736, 80, 8, 8,4744, 80, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,4752, + 4760, 256, 8,4768,4776,4784, 0, 0, 0, 0, 0, 248, 88,4792,4800,4808, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 192, 0, 0, 0, 0, 0, 0, + 104, 104, 104, 104, 104, 104,4816,4824, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 368, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8,4832, + 8, 8, 8, 136,4840,4848, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 176, + 8, 8, 8, 8, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 680,4856, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 160, 8, 120, 360, 8, 8, 8, 8, 120, 80, 8, 112,4864, + 8, 8, 8,4872,4880,4888,4896,4904, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,4912, 8,4920, 80, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 72, 72, 40, 40, 88,4928, 72,4936, 40,4944, 0, 0, + 8, 8, 8, 8,4952,4960, 688, 688,4968,4976, 0, 0, 0, 0,4984,4992, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 328, 0,5000, + 8, 120, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 696, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,5008, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 696,5016, 0,5024,5032, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 184, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 176, 152, 160,5040,5048, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,5056, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,5064, 24, 376, 24,5072, + 64, 64, 544, 64, 704, 24, 24, 24, 24, 24, 24, 24, 352, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 272, + 24, 24,5080, 24, 24, 24,5088,5096,5104, 24,5112, 24, 24, 24, 576, 0, + 24, 24, 24, 24,5120, 0, 0, 0, 0, 0, 0, 0, 88, 712, 88, 712, + 24, 24, 24, 24, 24, 608, 88, 640, 0, 0, 0, 0, 0, 0, 0, 0, + 72, 296, 40, 720, 728,5128, 168, 72, 392,5136,5144,5152,5160, 72, 296, 40, + 5168,5176, 40,5184,5192,5200, 736, 72, 744, 40, 72, 296, 40, 720, 728, 40, + 168, 72, 392, 736, 72, 744, 40, 72, 296, 40,5208, 72,5216,5224,5232,5240, + 40,5248, 72,5256,5264,5272,5280, 40,5288, 72,5296, 40,5304, 288, 288, 288, + 64, 64, 64,5312, 64, 64,5320,5328,5336,5344, 464, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5352,5360,5368, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5376,5384,5392, 96, 96, 96,5400, 0,5408, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 152,5416,5424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,5432, 0, 8, 8,5440,5448, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,5456, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 680,5464, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120,5472,5480, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,5488, 120, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,5496, 704, 0, 0, + 72, 72, 392, 40,5504, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 384, 88, 88,5512,5520, 0, 0, 0, 0, + 384, 88,5528,5536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5544, 8,5552,5560,5568,5576,5584,5592,5600, 184,5608, 184, 0, 0, 0,5616, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 24, 24, 216, 24, 24, 24, 24, 24, 24, 352, 584, 400, 400, 400, 24, 272, + 5624, 24, 24, 24, 24, 24, 24, 24, 24, 24, 408, 0, 0, 0,5632, 24, + 5640, 24, 24, 216, 752, 760, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,5648, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,5656, 280, 280, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 264, 216, 376, + 216, 24, 24, 24, 416, 264, 24, 24, 416, 24, 408, 216, 760,5664, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 416, 408, 280,5672, 24, 24, 24,5680,5688,5696, 752, + 24, 24, 24, 24, 24, 24, 24, 24, 24,5704, 24, 24, 24, 24, 24,5712, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 112, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 112, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 248, 8, + 8, 8, 8, 8, 8, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 176, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 192, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5720, 0, 136, 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,5728, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, + 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, 176, + 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, 168, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, + 24, 24, 24, 24, 24, 24, 24, 24,1104, 48, 48,1112,1104, 48, 48,1112, + 24, 24, 24, 24, 24,1288, 24,1288, 48, 48,1512, 48, 48, 48,1512, 48, + 48, 48, 48, 48, 48, 48, 48,2048, 24, 24, 24, 24,2056,2064, 24, 24, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,2072,2080, + 2088,2096, 144, 144,2104, 144, 144, 144, 144, 144, 144, 144,2112, 296, 144, 144, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 2120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120,1520, 592, 592, 592,2128,1520, 592, 592, 592, 592, + 592,2136,2144, 352, 352, 352, 352, 352, 352, 0, 352, 352, 352,2152,2160, 0, + 1528,2168, 56,2176, 56, 56, 56, 56,2184,2192,2200, 56, 56, 56,2208, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,1528, 56, 56, 56, 56, + 496,2216, 496, 496, 496, 496, 496, 496, 496,2224, 56, 56, 56, 56, 56, 56, + 784, 784, 784, 784, 784, 784,2232, 0, 688, 688, 688, 688, 688, 688, 688,2240, + 792, 792, 792, 792, 792,2248, 792,2256,1296,1296,1296,2264, 496,2272, 56, 56, + 56, 56,2280, 56, 56, 56, 56, 56, 56, 56, 56, 56,2288, 56, 56, 56, + 240, 240, 240, 240, 240, 240, 240, 240, 240, 240,2296, 240,2304, 240, 240, 240, + 2312,1536,2320,1120,1120,2328,2336,2344,1536,2352,2360,2368,2376,1120,1120,2384, + 2392,1544,2400,1304,1304,2408,2416,2424,1544,2432,2440,2448,2456,1304,2464, 0, + 2472,1552,2480,1312,1312,2488,2496,2504,1552,2512,2520, 0,2528,1312,2536,2544, + 2552,1560,2560,1128,1128,2568,2576,2584,1560,2592,2600,2608,2616,1128,1128, 0, + 2624,1320,1568,2632,2640,1320, 504,2648,1320,1568,2656, 0,2664, 504, 504,2672, + 800,1576,1584, 800, 800,1584, 800,2680,1576,2688,2696,2704,2712, 800,2720, 800, + 1136,1592,1600,1136,1136,1600,2728,2736,1592,2744,2752,2760,2768,1136,2776, 0, + 400,1608,1616, 400, 400, 400, 400, 400,1608,1616,2784, 400,2792, 400, 400, 400, + 2800, 808,1624,2808, 808, 808,2816,2824,1624,2832,2840, 808,2848, 808,2856, 0, + 2864, 512, 512, 512, 512, 512, 512,2872, 512, 512, 512,2880, 0, 0, 0, 0, + 2888,2896, 984, 984,2904, 984, 984,2912,2920,2928, 984,2936, 0, 0, 0, 0, + 224, 224, 224, 224, 224, 224, 224, 224, 224,1328, 224, 224, 224,2944,1328, 224, + 224, 224, 224,1328, 224, 224, 224,1632, 224,1632,2952,2960, 0, 0, 0, 0, + 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, 200, + 200, 200, 200, 200, 256, 256, 256, 256,1640,1648, 256, 256, 256, 256, 256,2968, + 160, 160, 160, 160, 160, 160, 160, 160, 160, 816, 280, 816, 160, 160, 160, 160, + 160, 816, 160, 160, 160, 160, 816, 280, 816, 160, 280, 160, 160, 160, 160, 160, + 160, 160, 816, 160, 160, 160, 160, 160, 160, 160, 160,2976, 160, 160, 160,2984, + 160, 160, 160,2992, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232,1656,1656, + 1336,1336,1336,3000, 408, 408, 408, 408, 408, 408, 408, 408, 408,3008, 408,3016, + 1664,1664,3024,3032,1672,1672,3040, 0,1680,1680,3048, 0,3056,3064,3072, 0, + 272, 272, 272, 272, 272, 272, 272, 272, 272, 272, 272,3080, 272,1688, 272,1688, + 3088, 248, 248,3096, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248, 248,3104, + 248, 248, 248, 248, 248,3112, 80, 80, 80, 80, 80, 80, 80, 80,3120, 0, + 824, 824, 824,3128, 824,1696, 824,1696,3136, 824,1344,1344,1344,3144,3152, 0, + 520, 520, 520, 520, 520,3160, 520, 520, 520,3168, 520,3176, 272, 272, 272, 272, + 1352,1352,1352,3184, 320, 320, 320, 320, 320, 320, 320,3192, 320, 320, 320,3200, + 320,1704, 320,1704, 320,3208, 104, 104, 104, 104, 104,1712, 104,3216, 0, 0, + 304, 304, 304, 304, 304, 304, 304, 304, 304,3224, 304, 304, 304, 304, 304, 304, + 528, 528, 528, 528, 528, 528, 528, 528, 832, 832, 832, 832, 832, 832,3232,3240, + 600, 600, 600, 600, 600, 600, 600,3248, 600,3256, 840, 840, 840, 840, 840, 840, + 120,3264, 256, 256, 256, 256, 256,3272, 528, 0,3280, 104,3288,3296,3304,3312, + 48, 48, 48, 48,3320,3328, 48, 48, 48, 48, 48,3336,3344,3352, 48,3360, + 48, 48, 48, 48, 48, 48, 48,3368, 104, 104, 104, 104, 104, 104, 104, 104, + 144, 144, 848, 848, 144, 144, 144, 144, 848, 848, 144,3376, 144, 144, 144, 848, + 144, 144, 144, 144, 144, 144,1720, 144,1720, 144,3384,3392, 144, 144,3400,1728, + 24,3408, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 992, 24,3416,3424, + 24, 608, 48,1736, 24, 24, 24, 24, 616, 0, 104, 104, 104, 104,3432, 0, + 24, 24, 24, 24,3440,3448,1288, 24, 24,3456, 24, 24, 48, 48, 48, 48, + 48,3464, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 616, 0, 0, 24,1360, 0, 0, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,1744, 24, + 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 312, 48, 48, 48, 48, + 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296, 296,3472,3480, + 256, 256, 256, 256,1640,1648, 696, 696, 696, 696, 696, 696, 696,1752,3488,1752, + 160, 160, 280, 0, 280, 280, 280, 280, 280, 280, 280, 280, 120, 120, 120, 120, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 416, 0, 0, 0, 0, + 8, 8, 8,3496, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,3504, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 856, 0, 0, 0, 24, 24, + 3512, 24, 24, 24,3520,3528, 24,3536,3544, 152, 152, 152, 152, 152, 152, 152, + 152, 152,3552,3560,1760, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184,3568, + 3576, 536, 536, 536, 536, 536,3584, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16,1144, 24, 24, 536, 536, 536, 536, 24, 24, 24, 24, 416,3592, 184, 184, + 16, 16, 16,1144, 24, 24, 24, 24, 24, 24, 24, 24, 16, 16, 16,3600, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 184, 184, 184, 184, 184,3608, + 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 184, 24, 24, 24, 24, 24, + 8, 8, 8, 8, 8, 8, 8, 8, 24, 24, 24, 24, 24, 24, 24, 24, + 72,3616, 72, 72, 72, 72, 72, 72,3624, 0, 864, 864, 864, 864, 864, 864, + 176, 176, 176, 176, 176,3632, 0, 0, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, 0, + 24, 24, 24, 24,3640, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 48,3648, 48, 48, 48, 48, 48, 48, 48, 48, 48,1736, 0, 0,3656, 48, + 1000,1000,1000,1000,1000,3664, 24, 616, 704, 704, 704, 704, 704, 704, 704, 0, + 544, 544, 544, 544, 544, 544, 544, 544,3672,3680, 544,3688, 240, 240, 240, 240, + 1008,1008,1008,1008,1008,3696,1152,1152,1152,1152,3704,3712, 16, 16, 16,3720, + 424, 424, 424, 424, 424, 424, 424, 424, 424,3728, 424,3736, 200, 200, 200,3744, + 624, 624, 624, 624, 624, 624,3752, 0, 624,3760, 624,3768, 200, 200, 200, 200, + 632, 632, 632, 632, 632, 632, 632, 632,3776, 0, 0,3784, 640, 640,3792, 0, + 1368,1368,1368, 0, 280, 280, 48, 48, 48, 48, 48,3800,3808,3816, 232, 232, + 232, 232, 232, 232, 232, 232, 232, 232, 640, 640, 640, 640, 640,3824, 640,3832, + 16, 16, 16, 16,1768, 0, 16, 16,1144,3840, 16, 16, 16, 16, 16,1768, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 856, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,1776, 0, 0, 0, 0, + 1784, 0,3848,3856, 352, 352,3864,3872,3880, 352, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56,3888, 56, 56, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 0, 0, 0, 0, 56, 56, + 104, 104, 24, 616, 104,3896, 24, 24, 24, 24,1792, 24, 608, 328,3904, 56, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,3912, + 712, 24, 24, 24,1104, 48, 48,1112,1104, 48, 48,1112,3920, 184,1760, 184, + 184, 184, 184,3928, 16, 16, 16,1144,1376,1376,1376,3936, 608, 608, 0,3944, + 216,3952, 216, 216,3960, 216, 216,3968, 216,1800, 216,1800, 0, 0, 0, 0, + 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216, 216,3976, + 1808, 24, 24, 24, 24, 24,3984, 24, 144, 144, 144, 144, 144, 144, 144, 144, + 144,1728, 24, 872,3992, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24,4000, + 1384,1384,1384,4008, 880, 880, 880, 880, 880, 880,4016, 0,4024, 24, 24, 328, + 1160,1160,1160,1160,4032,4040,1392,1392,1392,4048,1016,1016,1016,1016,1016,4056, + 1400,1400,1400,4064,1024,1024,1024,1024,4072,1024,4080, 0, 0, 0, 0, 0, + 432, 432, 432, 432, 432, 432, 432, 432, 432, 432, 888, 888, 888, 888, 888, 888, + 1168,1168,1168,4088,1168,4096, 648, 648, 648, 648,1816, 648, 648, 648, 648,1816, + 1032,1032,1032,1032,1032, 0, 896, 896, 896, 896, 896, 896,4104,4112,1176,1824, + 1176,1824,4120,1176,1832,1176,1832,4128, 904, 904, 904, 904, 904, 904,4136, 0, + 168, 168, 168, 168, 168, 168,4144, 0, 168, 168,4152, 0, 168, 0, 0, 0, + 4160, 48, 48, 48, 48, 48,4168,1840, 0, 0, 0, 0, 0, 0, 0, 0, + 4176,4184,1184,1184,1184,1184,4192,4200,1408,1408,4208,1408,1192,1192,1192,1192, + 1200,1200,1200,4216,4224,1200, 0, 0, 0, 0, 0, 0,1848,1848,4232,4240, + 1416,1416,1416,4248,1424,1424,1424,4256,1432,1432,1432,4264, 0, 0, 0, 0, + 1208,1208,1208,1208, 440, 440, 440,4272, 440, 440,4280, 440, 440, 440, 440, 440, + 4288,4296,4304,4312,1216,1216,4320,4328,1216,1856,1216,1856,1224,1224,1224,1224, + 1232,1232,1232,1232, 0, 0, 0, 0,1240,1240,1240,1240,1864,4336,1864, 0, + 912, 912, 912, 912, 912, 912,4344,4352,1440,1440,4360,1440,1448,1448,4368,1448, + 1872,1872,4376,4384, 0,4392, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 552, 552, 552, 552, 552, 552, 552, 552, 552,4400, 0, 0, 0, 0, 0, 0, + 360, 360, 360, 360, 360, 360,1880, 0, 360, 360, 360, 360, 360, 360,1880,4408, + 920, 920, 920, 920, 920, 0, 920,4416, 928, 928, 928, 928,1888,4424, 928, 928, + 1888,4432, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 56, 56,4440, + 1040,1040,1040,1040,1040,4448,4456, 0,1896, 0, 56,4464, 0, 0, 0,1896, + 1048,1048,1048,1048,1048, 0,1056,1056,1056,1056,1056,4472, 0, 0,1456,1456, + 1456,4480, 0, 0, 0, 0,1464,1464,1464,4488, 0, 0,1904,1904,4496, 0, + 368, 368, 368, 368, 368, 368, 368, 368, 368,1912,4504, 368, 368, 368,1912,4512, + 656, 656, 656, 656, 656, 656, 656, 656,4520,4528,1248,1248,1248,4536,1248,4544, + 664, 664, 664, 664, 664, 664,4552, 664, 664, 0,1256,1256,1256,1256,4560, 0, + 336, 336, 336, 336, 336, 336, 336, 336, 336, 336, 336, 336,4568, 808,4576, 0, + 720, 720,4584, 720, 720, 720, 720, 720,4592, 0, 0, 0, 0, 0, 0, 0, + 4600,4608,1920,4616,1920,4624, 672, 672, 672, 672, 672, 672, 672,4632, 672,4640, + 4648,1928,4656,1936,1936,4664,4672,4680,1928,4688,4696,4704,4712,1944,1944, 0, + 936,4720, 936, 936, 936, 936,1952, 936,4728,4736,1952,4744,4752, 0, 0, 0, + 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384,4760,4768, 0, 0, 0, + 448, 448, 448, 448, 448, 448, 448, 448, 448, 0, 448,4776, 0, 0, 0, 0, + 456, 456, 456, 456, 456, 456,1960, 456, 456, 456, 456,1960, 0, 0, 0, 0, + 560, 560, 560, 560, 560, 560, 560, 560,4784, 0, 560,4792, 248,4800, 0, 0, + 680, 680, 680, 680, 680, 680, 680,1968, 680,1968, 200, 200,4808, 0, 0, 0, + 944, 944, 944,4816, 944,4824, 944, 944,4832, 0, 0, 0, 0, 0, 0, 0, + 728, 728, 728, 728, 728, 728, 728,4840, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 464, 464, 464, 464, 464, 464, 464, 464, 464, 464,4848,4856, + 1976,4864,4872,1264,1264,1264,4880,4888,1976, 0,1264,4896, 0, 0, 0, 0, + 0, 0, 0, 0, 952,1984, 952, 952, 952, 952, 952,1984,4904, 0, 0, 0, + 568, 568, 568, 568, 568, 568, 568, 568, 568, 0, 472, 472, 472, 472, 472, 472, + 472, 472, 472, 472,4912, 0, 80, 80, 736, 736, 736, 736, 736, 736, 736,4920, + 240,4928, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 336, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1064,1064,1064,1064,1992, 0,1064,1992, + 576,4936, 576, 576, 576, 576,4944, 576,4952, 0, 576, 576, 576,4960, 960, 960, + 960, 960,4968, 960, 960,4976,4984, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2000,4992, 968, 968, 968, 968,2000,5000, 968, 0, 968,5008,5016,5024,1272,1272, + 1272,5032,5040,5048,1272,5056, 976, 976, 976, 976, 976,5064, 976,5072, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1472,1472,1472,5080, + 584, 584,5088, 584, 584, 584, 584,5096, 584, 584, 584,5104, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,5112, 0, 504, 504, 504, 504, 504, 504,5120,5128, + 64, 64, 64,5136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,5144,5152, 0, + 64, 64, 64, 64, 64, 64, 64, 64,5160, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376, 376,5168, 0, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,5176, 0, 40, 40, 40, 40, + 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,5184, + 112, 112, 112, 112, 112, 112, 112, 112,5192, 0, 0, 0, 0, 0, 0, 0, + 744, 744, 744, 744, 744, 744, 744,5200, 0, 0, 0, 0, 0, 0, 0, 0, + 88, 88, 88, 88, 88, 88, 88,5208,1280,1280,1280,5216,1280,5224, 480, 480, + 480, 480, 480, 480, 480, 480, 480,5232, 480,5240,1480,1480,1480,2008,2008, 0, + 344, 344, 344, 344, 344, 344, 344, 344,5248, 0, 344,2016,2016, 344, 344,5256, + 344, 344, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 752, 752, 752, 752, 752, 752, 752,5264, + 0, 0, 0, 0, 0, 0, 0, 0, 392, 392, 392, 392, 392, 392, 392, 392, + 392, 392, 392,5272,1072,1072,1072,5280,1072,1072,5288, 0, 0, 0, 0, 0, + 264, 264, 264, 264, 264, 264, 264, 264, 264,5296, 264, 264, 264, 264, 264, 264, + 264,5304, 264, 264, 0, 0, 0, 0, 0, 0, 0, 0,5312, 0,5320, 0, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,5328, 0, 0, 0, 0,5336, + 32, 32, 32,5344, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,5352, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,5360,5368, + 5376, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, 152, + 152, 152, 152, 152,5384, 0,5392, 0, 0, 0,5400, 0,5408, 0, 136, 136, + 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,5416, + 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288, 288,5424, 288,5432, + 288,5440, 288,5448, 328, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 872, + 24, 24, 24, 24, 24, 24, 328,2024, 24, 24, 760, 0, 24, 24, 760, 0, + 104, 104, 104, 104, 104,1712, 104, 104,5456, 0, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 328, 0, 0, 0, 0, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 416, 0, + 24, 24, 24, 24, 608, 712, 24, 24, 24, 24, 24, 24,5464,5472, 24,5480, + 5488,5496, 24, 24, 24,5504, 24, 24, 24, 24, 24, 24, 24,1360, 0, 0, + 144, 144, 144, 144, 144, 144, 144, 144, 848, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 328, 0, 24, 24, 328, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 608, 0, 24, 24, 24, 760, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 992, 24, 24, 24, 24, 24, + 24, 24, 24, 992,5512,5520, 24,5528,5536, 24, 24, 24, 24, 24, 24, 24, + 5544,5552, 992, 992, 24, 24, 24,5560,5568,2024,5576, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 416, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24,1744, 24, 24, 24, 24, 24, 24, + 96,5584, 0,5592,5600, 96, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 48, 48, 48,1784,5608,1840, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5616, 312, 312,5624,5632,5640, 120, 120, 120, 120, 120, 120, 120,5648, 0, 0, + 0,5656, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 768, 768, 768, 768, 768,5664, 768,5672, 768,5680, 0, 0, 0, 0, 0, 0, + 0, 0,1488,1488,1488,5688, 0, 0, 776, 776, 776, 776, 776, 776, 776,5696, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1080,1080,1080,1080,1080,5704, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1088,1088,1088,1088,1088,5712, + 0, 0, 0, 0, 0, 0, 0, 0,1096,1096,1096,5720,1096,1096,5728,5736, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 280,5744, 160, 280, + 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208,5752, 208,5760, 0, 0, 0, 0, 0, + 488, 488, 488, 488, 488, 488, 488, 488, 488,5768, 488,5776, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 712, 24, + 24, 24, 24, 24, 24, 24, 872, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 712, 24, 24, 24, 24, 24, 24, 416, 0, 0, 0, 0, 0, 0, 0, 0, + 5784, 56, 56, 56,1496,5792,1504,5800,5808,5816,1496,5824,1496,1504,1504,5832, + 56,2032, 56,2040,5840,2032, 56,2040, 0, 0, 0, 0, 0, 0,5848, 0, + 24, 24, 24, 24, 24, 328, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 328, 0, 24, 608, 712, 24, 712, 24, 712, 24, 24, 24, 416, 0, + 24, 24, 24, 24, 24, 416, 0, 0, 0, 0, 0, 0,5856, 24, 24, 24, + 5864, 0, 24, 24, 24, 24, 24, 328, 24, 760, 616, 0, 416, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,5872, 24, 872, 24, 872, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 616, 24, 328, 760, 0, + 24, 328, 24, 24, 24, 24, 24, 24, 24, 0, 24, 616, 24, 24, 24, 24, + 24, 0, 24, 24, 24, 416, 24, 328, 616, 0, 24, 760, 0, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 24, 416, 24, 872, + 24,5880, 24, 24, 24, 24, 24, 24, 608,5888, 24,5896, 24,1808, 24, 760, + 24, 24,1792, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,1360, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, + 8, 8, 8, 856, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 856, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,5904, 0, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 856, 0, 0, 0, 0, + 8, 8, 8, 856, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 8, 8, 8, 8, 8,5912, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,1776, + 5920, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, @@ -2690,15 +2686,15 @@ static const uint16_t _hb_ucd_u16[10832]= }; static const int16_t _hb_ucd_i16[196]= { - 0, 0, 0, 0, 1, -1, 0, 0, 2, 0, -2, 0, 0, 0, 0, 2, - 0, -2, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 0, 0, 1, -1, - 0, 0, 0, 1, -1, 0, 0, 0, 0, 1, -1, 0, 3, 3, 3, -3, + 0, 0, 0, 0, 1, -1, 1, -1, 1, -1, 0, 0, -1, 1, -1, 1, + 0, 0, 1, -1, -1, 1, -1, 0, 0, 0, 0, 1, 0, 1, -1, 0, + 0, 0, 0, 2, 0, -2, 0, 0, 0, 1, -1, 1, 2, 0, -2, 0, + -1, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, -16, 3, 3, 3, -3, -3, -3, 0, 0, 0, 2016, 0, 0, 0, 0, 0, 2527, 1923, 1914, 1918, 0, 2250, 0, 0, 0, 0, 0, 0, 138, 0, 7, 0, 0, -7, 0, 0, 0, - 1, -1, 1, -1, -1, 1, -1, 0, 1824, 0, 0, 0, 0, 0, 2104, 0, - 2108, 2106, 0, 2106, 1316, 0, 0, 0, 0, 1, -1, 1, -1, -138, 0, 0, - 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, -8, -7, -7, 0, - 1, -1, 0, 2,-1316, 1, -1, 0, -1, 1, -1, 1, -1, 3, 1, -1, + 1824, 0, 0, 0, 0, 0, 2104, 0, 2108, 2106, 0, 2106, 1316, 0, 0, 0, + -1, -138, 0, 0, 1, -1, 8, 8, 8, 0, 7, 7, 0, 0, -8, -8, + -8, -7, -7, 0, 1, -1, 0, 2,-1316, 1, -1, 0, -1, 3, 1, -1, -3, 1, -1, 1, -1, 0, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0, 0, 0,-2016, 0, 0, 1, -1, 0, 1, 0, 0,-2104, 0, 0, 0, 0,-2106, -2108,-2106, 0, 0, 1, -1,-2250, 0, 0, 0,-2527, 0, 0, -2, 0, 1, @@ -2707,11 +2703,13 @@ static const int16_t _hb_ucd_i16[196]= static inline uint8_t _hb_ucd_gc (unsigned u) { - return u<1114110 ? _hb_ucd_u8[7920u+((_hb_ucd_u8[2176u+((_hb_ucd_u16[((_hb_ucd_u8[((((((u)>>1))>>3))>>5)])<<5)+((((((u)>>1))>>3))&31)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : 2; + /* packtab: [2^8,2^5,2^3,2^1] */ + return u<1114110u ? (uint8_t)(_hb_ucd_u8[7912u+((_hb_ucd_u8[2176u+_hb_ucd_u16[((_hb_ucd_u8[((((((u)>>1))>>3))>>5)])<<5)+((((((u)>>1))>>3))&31)]+((((u)>>1))&7)])<<1)+((u)&1)]) : 2; } static inline uint8_t _hb_ucd_ccc (unsigned u) { - return u<125259 ? _hb_ucd_u8[10388u+((_hb_ucd_u8[9284u+((_hb_ucd_u8[8548u+((_hb_ucd_u8[8302u+((((((u)>>2))>>3))>>4)])<<4)+((((((u)>>2))>>3))&15)])<<3)+((((u)>>2))&7)])<<2)+((u)&3)] : 0; + /* packtab: [2^8,2^4,2^3,2^2] */ + return u<125259u ? (uint8_t)(_hb_ucd_u8[10370u+((_hb_ucd_u8[9270u+((_hb_ucd_u8[8539u+((_hb_ucd_u8[8294u+((((((u)>>2))>>3))>>4)])<<4)+((((((u)>>2))>>3))&15)])<<3)+((((u)>>2))&7)])<<2)+((u)&3)]) : 0; } static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) { @@ -2719,15 +2717,18 @@ static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) } static inline int16_t _hb_ucd_bmg (unsigned u) { - return u<65380 ? _hb_ucd_i16[((_hb_ucd_u8[11140u+((_hb_ucd_u8[11020u+((_hb_ucd_b4(_hb_ucd_u8+10892u,((((((u)>>2))>>3))>>3)))<<3)+((((((u)>>2))>>3))&7)])<<3)+((((u)>>2))&7)])<<2)+((u)&3)] : 0; + /* packtab: [2^4,2^3,2^3,2^2] */ + return u<65380u ? (int16_t)(_hb_ucd_i16[_hb_ucd_u8[11118u+((_hb_ucd_u8[11002u+((_hb_ucd_b4(_hb_ucd_u8+10874u,((((((u)>>2))>>3))>>3)))<<3)+((((((u)>>2))>>3))&7)])<<3)+((((u)>>2))&7)]+((u)&3)]) : 0; } static inline uint8_t _hb_ucd_sc (unsigned u) { - return u<918000 ? _hb_ucd_u8[12662u+((_hb_ucd_u16[3328u+((_hb_ucd_u8[11926u+((_hb_ucd_u8[11476u+((((((u)>>3))>>4))>>4)])<<4)+((((((u)>>3))>>4))&15)])<<4)+((((u)>>3))&15)])<<3)+((u)&7)] : 2; + /* packtab: [2^8,2^4,2^4,2^3] */ + return u<918000u ? (uint8_t)(_hb_ucd_u8[12639u+_hb_ucd_u16[3296u+((_hb_ucd_u8[11903u+((_hb_ucd_u8[11454u+((((((u)>>3))>>4))>>4)])<<4)+((((((u)>>3))>>4))&15)])<<4)+((((u)>>3))&15)]+((u)&7)]) : 2; } static inline uint16_t _hb_ucd_dm (unsigned u) { - return u<195102 ? _hb_ucd_u16[7408u+((_hb_ucd_u8[18972u+((_hb_ucd_u8[18590u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)] : 0; + /* packtab: [2^8,2^5,2^4] */ + return u<195102u ? (uint16_t)(_hb_ucd_u16[7360u+((_hb_ucd_u8[18949u+((_hb_ucd_u8[18567u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)]) : 0; } @@ -2735,1624 +2736,1620 @@ static inline uint16_t _hb_ucd_dm (unsigned u) #include -static const uint8_t _hb_ucd_u8[14800]= +static const uint8_t _hb_ucd_u8[14763]= { - 0, 1, 2, 3, 4, 5, 5, 5, 5, 5, 6, 5, 5, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 5, 17, 15, 18, 19, 20, 21, 22, 23, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 24, 25, 26, 5, 27, 28, - 5, 29, 5, 30, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 31, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 32, - 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 33, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 17, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 33, 41, 42, 43, 44, 45, - 46, 47, 48, 39, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 49, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 50, 17, 17, 17, 51, 17, 52, 53, 54, 55, 56, 57, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 58, 59, 59, 59, 59, 59, 59, 59, 59, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 17, 61, 62, 17, 63, 64, 65, - 66, 67, 68, 69, 70, 71, 17, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, - 17, 17, 17, 97, 98, 99,100,100,100,100,100,100,100,100,100,101, - 17, 17, 17, 17,102, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,103, 17, 17,104,100,100,100,100,100,100,100,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 100,105,100,100,100,100,100,100, 17, 17,106,107,100,108,109,110, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,111,112,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,113, - 17,114,115,100,100,100,100,100,100,100,100,100,116,100,100,100, - 100,100,100,100,100,100,100,100,100,100,100,100,117, 39,118,119, - 120,121,122,123,124,125,126,127, 39, 39,128,100,100,100,100,129, - 130,131,132,100,133,134,135,136,137,138,100,100,139,140,141,100, - 142,143,144,145, 39, 39,146,147,148, 39,149,150,100,100,100,100, - 17, 17, 17, 17, 17, 17,151, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17,152, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,153, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,154, 17, 17,155,100, - 100,100,100,100,100,100,100,100, 17, 17,156,100,100,100,100,100, - 17, 17, 17,157, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17,158,100,100,100,100,100,100,100,100,100,100,100, - 159,160,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,161, - 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60,162, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, - 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, - 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, - 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, - 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, - 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, - 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, - 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, - 16, 16, 36, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 40, 40, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, - 39, 39, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, - 42, 42, 42, 42, 42, 42, 42, 42, 32, 32, 41, 32, 43, 44, 16, 10, - 43, 43, 40, 45, 11, 46, 46, 11, 34, 11, 11, 11, 11, 11, 11, 11, - 11, 47, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, - 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 48, 34, 32, 34, 11, - 32, 49, 42, 42, 50, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, - 47, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 46, 51, 2, 2, 2, - 16, 16, 16, 16, 52, 53, 54, 55, 56, 42, 42, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 42, 42, 42, 57, 58, 59, 42, 58, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 61, - 36, 62, 63, 43, 43, 43, 43, 43, 64, 64, 64, 8, 9, 65, 2, 66, - 42, 42, 42, 42, 42, 59, 67, 2, 68, 36, 36, 36, 36, 69, 42, 42, - 7, 7, 7, 7, 7, 2, 2, 36, 70, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 71, 42, 42, 42, 72, 49, 42, 42, 73, 74, 75, 42, 42, 36, - 7, 7, 7, 7, 7, 36, 76, 77, 2, 2, 2, 2, 2, 2, 2, 78, - 69, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 79, 61, 36, - 36, 36, 36, 42, 42, 42, 42, 42, 70, 43, 43, 43, 43, 43, 43, 43, - 7, 7, 7, 7, 7, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, - 42, 42, 39, 21, 2, 80, 56, 20, 36, 36, 36, 42, 42, 74, 42, 42, - 42, 42, 74, 42, 74, 42, 42, 43, 2, 2, 2, 2, 2, 2, 2, 63, - 36, 36, 36, 36, 69, 42, 43, 63, 36, 36, 36, 36, 36, 60, 43, 43, - 36, 36, 36, 36, 81, 36, 36, 36, 64, 43, 43, 56, 42, 42, 42, 42, - 36, 36, 36, 36, 82, 42, 42, 42, 42, 83, 42, 42, 42, 42, 42, 42, - 42, 84, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 84, 70, 85, - 86, 42, 42, 42, 84, 85, 86, 85, 69, 42, 42, 42, 36, 36, 36, 36, - 36, 42, 2, 7, 7, 7, 7, 7, 87, 36, 36, 36, 36, 36, 36, 36, - 69, 85, 61, 36, 36, 36, 60, 61, 60, 61, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 60, 36, 36, 36, 60, 60, 43, 36, 36, 43, 70, 85, - 86, 42, 79, 88, 89, 88, 86, 60, 43, 43, 43, 88, 43, 43, 36, 61, - 36, 42, 43, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 55, 62, 79, - 56, 84, 61, 36, 36, 60, 43, 61, 60, 36, 61, 60, 36, 43, 79, 85, - 86, 79, 43, 56, 79, 56, 42, 43, 56, 43, 43, 43, 61, 36, 60, 60, - 43, 43, 43, 7, 7, 7, 7, 7, 42, 36, 69, 63, 43, 43, 43, 43, - 56, 84, 61, 36, 36, 36, 36, 61, 36, 61, 36, 36, 36, 36, 36, 36, - 60, 36, 61, 36, 36, 43, 70, 85, 86, 42, 42, 56, 84, 88, 86, 43, - 60, 43, 43, 43, 43, 43, 43, 43, 65, 43, 43, 43, 61, 42, 42, 42, - 56, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 43, 70, 86, - 86, 42, 79, 88, 89, 88, 86, 43, 43, 43, 56, 84, 43, 43, 36, 61, - 77, 27, 27, 27, 43, 43, 43, 43, 43, 70, 61, 36, 36, 60, 43, 36, - 60, 36, 36, 43, 61, 60, 60, 36, 43, 61, 60, 43, 36, 60, 43, 36, - 36, 36, 36, 36, 36, 43, 43, 85, 84, 89, 43, 85, 89, 85, 86, 43, - 60, 43, 43, 88, 43, 43, 43, 43, 27, 90, 66, 66, 55, 91, 43, 43, - 84, 85, 70, 36, 36, 36, 60, 36, 60, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 43, 70, 42, 84, 85, 89, 42, 79, 42, 42, 43, - 43, 43, 56, 79, 36, 60, 36, 43, 43, 43, 43, 92, 27, 27, 27, 90, - 69, 85, 71, 36, 36, 36, 60, 36, 36, 36, 61, 36, 36, 43, 70, 86, - 85, 85, 89, 84, 89, 85, 42, 43, 43, 43, 88, 89, 43, 43, 36, 60, - 61, 93, 43, 43, 43, 43, 43, 43, 42, 85, 36, 36, 36, 36, 60, 36, - 36, 36, 36, 36, 36, 69, 70, 85, 86, 42, 79, 85, 89, 85, 86, 76, - 43, 43, 36, 93, 27, 27, 27, 94, 27, 27, 27, 27, 90, 36, 36, 36, - 56, 85, 61, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, 36, 36, 36, - 36, 61, 36, 36, 36, 36, 61, 43, 36, 36, 36, 60, 43, 79, 43, 88, - 85, 42, 79, 79, 85, 85, 85, 85, 43, 85, 63, 43, 43, 43, 43, 43, - 61, 36, 36, 36, 36, 36, 36, 36, 69, 36, 42, 42, 42, 79, 43, 95, - 36, 36, 36, 74, 42, 42, 42, 59, 7, 7, 7, 7, 7, 2, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 43, 61, 60, 60, 36, 36, 60, 36, 36, - 36, 36, 61, 61, 36, 36, 36, 36, 69, 36, 42, 42, 42, 42, 70, 43, - 36, 36, 60, 80, 42, 42, 42, 79, 7, 7, 7, 7, 7, 43, 36, 36, - 76, 66, 2, 2, 2, 2, 2, 2, 2, 96, 96, 66, 42, 66, 66, 66, - 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 49, 49, 49, 4, 4, 85, - 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 43, - 56, 42, 42, 42, 42, 42, 42, 84, 42, 42, 59, 42, 36, 36, 69, 42, - 42, 42, 42, 42, 56, 42, 42, 42, 42, 42, 42, 42, 42, 42, 79, 66, - 66, 66, 66, 75, 66, 66, 91, 66, 2, 2, 96, 66, 21, 63, 43, 43, - 36, 36, 36, 36, 36, 93, 86, 42, 84, 42, 42, 42, 86, 84, 86, 70, - 7, 7, 7, 7, 7, 2, 2, 2, 36, 36, 36, 85, 42, 36, 36, 42, - 70, 85, 97, 93, 85, 85, 85, 36, 69, 42, 70, 36, 36, 36, 36, 36, - 36, 84, 86, 84, 85, 85, 86, 93, 7, 7, 7, 7, 7, 85, 86, 66, - 11, 11, 11, 47, 43, 43, 47, 43, 16, 16, 16, 16, 16, 52, 44, 16, - 36, 36, 36, 36, 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, - 60, 36, 36, 43, 36, 36, 36, 60, 60, 36, 36, 43, 36, 36, 36, 36, - 36, 36, 36, 60, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 56, 42, - 2, 2, 2, 2, 98, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, - 66, 66, 66, 66, 66, 43, 43, 43, 11, 11, 11, 43, 16, 16, 16, 43, - 100, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 71, - 101, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,102,103, 43, - 36, 36, 36, 36, 36, 62, 2,104,105, 36, 36, 36, 60, 43, 43, 43, - 36, 42, 84, 43, 43, 43, 43, 61, 36, 42,106, 63, 43, 43, 43, 43, - 36, 42, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36, 60, 36, - 60, 42, 43, 43, 43, 43, 43, 43, 36, 36, 42, 86, 42, 42, 42, 85, - 85, 85, 85, 84, 86, 42, 42, 42, 42, 42, 2, 87, 2, 65, 69, 43, - 7, 7, 7, 7, 7, 43, 43, 43, 27, 27, 27, 27, 27, 43, 43, 43, - 2, 2, 2,107, 2, 58, 42, 83, 36, 82, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 60, 43, 43, 43, 36, 36, 69, 70, 36, 36, 36, 36, - 36, 36, 36, 36, 69, 60, 43, 43, 36, 36, 36, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 60, 42, 84, 85, 86, 84, 85, 43, 43, - 85, 84, 85, 85, 86, 42, 43, 43, 91, 43, 2, 7, 7, 7, 7, 7, - 36, 36, 36, 36, 36, 36, 36, 43, 36, 36, 60, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 43, 43, 36, 36, 36, 36, 36, 43, 43, 43, - 7, 7, 7, 7, 7, 99, 43, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 36, 36, 36, 69, 84, 86, 43, 2, 36, 36, 93, 84, 42, 42, 42, 79, - 84, 84, 86, 42, 42, 42, 84, 85, 85, 86, 42, 42, 42, 42, 79, 56, - 2, 2, 2, 87, 2, 2, 2, 43, 42, 42, 42, 42, 42, 42, 42,108, - 42, 42, 42, 42, 42, 42, 42, 43, 42, 42, 42, 42, 42, 42, 43, 43, - 42, 42, 97, 36, 36, 36, 36, 36, 36, 36, 84, 42, 42, 84, 84, 85, - 85, 84, 97, 36, 36, 36, 60, 2, 96, 66, 66, 66, 66, 49, 42, 42, - 42, 42, 66, 66, 66, 66, 21, 2, 42, 97, 36, 36, 36, 36, 36, 36, - 93, 42, 42, 85, 42, 86, 42, 36, 36, 36, 36, 84, 42, 85, 86, 86, - 42, 85, 43, 43, 43, 43, 2, 2, 36, 36, 85, 85, 85, 85, 42, 42, - 42, 42, 85, 42, 43, 92, 2, 2, 7, 7, 7, 7, 7, 43, 61, 36, - 36, 36, 36, 36, 39, 39, 39, 2, 16, 16, 16, 16, 34,109, 43, 43, - 11, 11, 11, 11, 11, 46, 47, 11, 2, 2, 2, 2, 43, 43, 43, 43, - 42, 59, 42, 42, 42, 42, 42, 42, 84, 42, 42, 42, 70, 36, 69, 36, - 36, 36, 70, 93, 42, 60, 43, 43, 16, 16, 16, 16, 16, 16, 39, 39, - 39, 39, 39, 39, 39, 44, 16, 16, 16, 16, 16, 16, 44, 16, 16, 16, - 16, 16, 16, 16, 16,110, 39, 39, 32, 32, 32, 16, 16, 16, 16, 32, - 16, 16, 16, 16, 11, 11, 11, 11, 16, 16, 16, 43, 11, 11, 11, 43, - 16, 16, 16, 16, 47, 47, 47, 47, 16, 16, 16, 16, 16, 16, 16, 43, - 16, 16, 16, 16,111,111,111,111, 16, 16,109, 16, 11, 11,112,113, - 40, 16,109, 16, 11, 11,112, 40, 16, 16, 43, 16, 11, 11,114, 40, - 16, 16, 16, 16, 11, 11,115, 40, 43, 16,109, 16, 11, 11,112,116, - 117,117,117,117,117,118, 64, 64,119,119,119, 2,120,121,120,121, - 2, 2, 2, 2,122, 64, 64,123, 2, 2, 2, 2,124,125, 2,126, - 127, 2,128,129, 2, 2, 2, 2, 2, 9,127, 2, 2, 2, 2,130, - 64, 64,131, 64, 64, 64, 64, 64,132, 43, 27, 27, 27, 8,128,133, - 27, 27, 27, 27, 27, 8,128,103, 39, 39, 39, 39, 39, 39, 80, 43, - 20, 20, 20, 20, 20, 20, 20, 20, 20, 43, 43, 43, 43, 43, 43, 43, - 42, 42, 42, 42, 42, 42,134, 50,108, 50,108, 42, 42, 42, 42, 42, - 79, 43, 43, 43, 43, 43, 43, 43, 66,135, 66,136, 66, 34, 11, 16, - 11, 32,136, 66, 48, 11, 11, 66, 66, 66,135,135,135, 11, 11,137, - 11, 11, 35, 36,138, 66, 16, 11, 8, 8, 48, 16, 16, 26, 66,139, - 27, 27, 27, 27, 27, 27, 27, 27,104,104,104,104,104,104,104,104, - 104,140,141,104,142, 66, 43, 43, 8, 8,143, 66, 66, 8, 66, 66, - 143, 26, 66,143, 66, 66, 66,143, 66, 66, 66, 66, 66, 66, 66, 8, - 66,143,143, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, 66, 66, 66, 66, 4, 4, 66, 66, - 8, 66, 66, 66,144,145, 66, 66, 66, 66, 66, 66, 66, 66,143, 66, - 66, 66, 66, 66, 66, 26, 8, 8, 8, 8, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 8, 8, 8, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 91, 43, 43, 27, 27, 27, 27, 27, 27, 66, 66, - 66, 66, 66, 66, 66, 27, 27, 27, 66, 66, 66, 26, 66, 66, 66, 66, - 26, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 8, 8, 8, 8, - 66, 66, 66, 66, 66, 66, 66, 26, 66, 66, 66, 66, 4, 4, 4, 4, - 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, - 8, 8,128,146, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, - 8,128,147,147,147,147,147,147,147,147,147,147,146, 8, 8, 8, - 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, - 8, 8,143, 26, 8, 8,143, 66, 66, 66, 43, 66, 66, 66, 66, 66, - 32, 11, 32, 34, 34, 34, 34, 11, 32, 32, 34, 16, 16, 16, 39, 11, - 32, 32,139, 66, 66,136, 34,148, 42, 32, 43, 43, 92, 2, 98, 2, - 16, 16, 16,149, 43, 43,149, 43, 36, 36, 36, 36, 43, 43, 43, 51, - 63, 43, 43, 43, 43, 43, 43, 56, 36, 36, 36, 60, 43, 43, 43, 43, - 36, 36, 36, 60, 36, 36, 36, 60, 2,120,120, 2,124,125,120, 2, - 2, 2, 2, 6, 2,107,120, 2,120, 4, 4, 4, 4, 2, 2, 87, - 2, 2, 2, 2, 2,119, 2, 2,107,150, 2, 2, 2, 2, 2, 2, - 66, 2,151,147,147,147,152, 43, 66, 66, 66, 66, 66, 54, 66, 66, - 66, 66, 43, 43, 43, 43, 43, 43, 66, 66, 66, 43, 43, 43, 43, 43, - 1, 2,153,154, 4, 4, 4, 4, 4, 66, 4, 4, 4, 4,155,156, - 157,104,104,104,104, 42, 42, 85,158, 39, 39, 66,104,159, 62, 66, - 36, 36, 36, 60, 56,160,161, 68, 36, 36, 36, 36, 36, 62, 39, 68, - 43, 43, 61, 36, 36, 36, 36, 36, 66, 27, 27, 66, 66, 66, 66, 66, - 66, 66, 66, 43, 43, 43, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, - 27, 27, 27, 27, 27, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, - 162, 27, 27, 27, 27, 27, 27, 27, 36, 36, 82, 36, 36, 36, 36, 36, - 66, 66, 66, 91, 43, 43, 43, 43, 36, 36, 36, 36, 36, 36,163, 2, - 7, 7, 7, 7, 7, 36, 43, 43, 32, 32, 32, 32, 32, 32, 32, 69, - 50,164, 42, 42, 42, 42, 42, 87, 32, 32, 32, 32, 32, 32, 39, 42, - 36, 36, 36,104,104,104,104,104, 42, 2, 2, 2, 43, 43, 43, 43, - 40, 40, 40,161, 39, 39, 39, 39, 40, 32, 32, 32, 32, 32, 32, 32, - 16, 32, 32, 32, 32, 32, 32, 32, 44, 16, 16, 16, 34, 34, 34, 32, - 32, 32, 32, 32, 41,165, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 46, 43, - 51, 39,166, 35, 39, 35, 36, 36, 36, 70, 36, 70, 36, 69, 36, 36, - 36, 93, 86, 84, 66, 66, 79, 43, 27, 27, 27, 66,167, 43, 43, 43, - 36, 36, 2, 2, 43, 43, 43, 43, 85, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 85, 85, 85, 85, 85, 85, 85, 85, 42, 43, 43, 43, 43, 2, - 42, 36, 36, 36, 2, 71, 71, 69, 36, 36, 36, 42, 42, 42, 42, 2, - 36, 36, 36, 69, 42, 42, 42, 42, 42, 85, 43, 43, 43, 43, 43, 92, - 36, 69, 85, 42, 42, 85, 42, 85,106, 2, 2, 2, 2, 2, 2, 51, - 7, 7, 7, 7, 7, 43, 43, 2, 36, 36, 69, 68, 36, 36, 36, 36, - 7, 7, 7, 7, 7, 36, 36, 60, 36, 36, 36, 36, 69, 42, 42, 84, - 86, 84, 86, 79, 43, 43, 43, 43, 36, 69, 36, 36, 36, 36, 84, 43, - 7, 7, 7, 7, 7, 43, 2, 2, 68, 36, 36, 76, 66, 93, 84, 36, - 70, 42, 70, 69, 70, 36, 36, 42, 69, 60, 43, 43, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 61, 82, 2, 36, 36, 36, 36, 36, 93, 42, 85, - 2, 82,168, 79, 43, 43, 43, 43, 61, 36, 36, 60, 61, 36, 36, 60, - 61, 36, 36, 60, 43, 43, 43, 43, 16, 16, 16, 16, 16,113, 39, 39, - 16, 16, 16, 16,110, 40, 43, 43, 36, 93, 86, 85, 84,106, 86, 43, - 36, 36, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 43, 61, 36, 36, - 169,169,169,169,169,169,169,169,170,170,170,170,170,170,170,170, - 16, 16, 16,109, 43, 43, 43, 43, 43,149, 16, 16, 43, 43, 61, 70, - 36, 36, 36, 36,171, 36, 36, 36, 36, 36, 36, 60, 36, 36, 60, 60, - 36, 61, 60, 36, 36, 36, 36, 36, 36, 40, 40, 40, 40, 40, 40, 40, - 40, 22, 66, 66, 66, 66, 66, 66, 66, 77, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36,147, 66, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 66, 66, 66, 66, 36, 36, 36, 36, 36, 36,167, 66, - 2, 2, 2,151,129, 43, 43, 43, 6,172,173,147,147,147,147,147, - 147,147,129,151,129, 2,126,174, 2, 63, 2, 2,155,147,147,129, - 2,175, 8,176, 65, 2, 43, 43, 36, 36, 60, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 60, 78, 92, 2, 3, 2, 4, 5, 6, 2, - 16, 16, 16, 16, 16, 17, 18,128,129, 4, 2, 36, 36, 36, 36, 36, - 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, - 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 36, 36, 43, 36, 60, 43, - 20,177, 55,178, 26, 8,143, 91, 43, 43, 43, 43, 78, 64, 66, 43, - 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 36, 36, 60, 36, 61, - 2, 63, 43,179, 27, 27, 27, 27, 27, 27, 43, 54, 66, 66, 66, 66, - 104,104,142, 27, 90, 66, 66, 66, 66, 66, 66, 66, 66, 27, 66, 91, - 66, 66, 66, 66, 66, 66, 91, 43, 91, 43, 43, 43, 43, 43, 43, 43, - 66, 66, 66, 66, 66, 66, 49, 43,180, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 27, 27, 27, 43, 43, 27, 27, 43, 43, 43, 43, 61, 36, - 154, 36, 36, 36, 36,181, 43, 43, 36, 36, 36, 42, 42, 79, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 92, 36, 36, 43, 43, 36, 36, 36, 36, - 182,104,104, 43, 43, 43, 43, 43, 11, 11, 11, 11, 16, 16, 16, 16, - 11, 11, 43, 43, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 43, 43, - 36, 36, 36, 36, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, 43, 92, - 11, 11, 11, 11, 11, 46, 11, 11, 11, 46, 11,149, 16, 16, 16, 16, - 16,149, 16, 16, 16, 16, 16, 16, 16,149, 16, 16, 16,149,109, 43, - 39, 39, 39, 51, 39, 39, 39, 39, 80, 39, 39, 39, 39, 80, 43, 43, - 36, 36, 36, 43, 60, 36, 36, 36, 36, 36, 36, 61, 60, 43, 60, 61, - 36, 36, 36, 92, 27, 27, 27, 27, 36, 36, 36, 76,162, 27, 27, 27, - 43, 43, 43,179, 27, 27, 27, 27, 36, 60, 36, 43, 43,179, 27, 27, - 36, 36, 36, 27, 27, 27, 43, 92, 36, 36, 36, 36, 36, 43, 43, 92, - 36, 36, 36, 36, 43, 43, 27, 36, 43, 27, 27, 27, 27, 27, 27, 27, - 69, 42, 56, 79, 43, 43, 42, 42, 36, 36, 61, 36, 61, 36, 36, 36, - 36, 36, 36, 43, 42, 79, 43, 56, 27, 27, 27, 27, 99, 43, 43, 43, - 2, 2, 2, 2, 63, 43, 43, 43, 36, 36, 36, 36, 36, 36,183, 30, - 36, 36, 36, 36, 36, 36,183, 27, 36, 36, 36, 36, 77, 36, 36, 36, - 36, 36, 69, 79, 43,179, 27, 27, 2, 2, 2, 63, 43, 43, 43, 43, - 36, 36, 36, 43, 92, 2, 2, 2, 36, 36, 36, 43, 27, 27, 27, 27, - 36, 60, 43, 43, 27, 27, 27, 27, 36, 43, 43, 43, 92, 2, 63, 43, - 43, 43, 43, 43,179, 27, 27, 27, 11, 46, 43, 43, 43, 43, 43, 43, - 16,109, 43, 43, 43, 27, 27, 27, 36, 36, 42, 42, 43, 43, 43, 43, - 7, 7, 7, 7, 7, 36, 36, 68, 11, 11, 11, 43, 56, 42, 42,158, - 16, 16, 16, 43, 43, 43, 43, 8, 27, 27, 27, 27, 27, 27, 27, 99, - 36, 36, 36, 36, 36, 56,184, 43, 36, 43, 43, 43, 43, 43, 43, 43, - 43, 36, 82, 36, 43, 43, 43, 43, 96, 66, 66, 66, 91, 43, 43, 43, - 43, 43, 43, 43, 43, 42, 42, 42, 27, 27, 27, 94, 43, 43, 43, 43, - 180, 27, 30, 2, 2, 43, 43, 43, 36, 42, 42, 2, 2, 43, 43, 43, - 36, 36,183, 27, 27, 27, 43, 43, 86, 97, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 42, 59, 2, 2, 2, 43, - 27, 27, 27, 7, 7, 7, 7, 7, 70, 69, 70, 43, 43, 43, 43, 56, - 85, 86, 42, 84, 86, 59,185, 2, 2, 79, 43, 43, 43, 43, 78, 43, - 42, 70, 36, 36, 36, 36, 36, 36, 36, 36, 36, 69, 42, 42, 86, 42, - 42, 42, 79, 7, 7, 7, 7, 7, 2, 2, 93, 97, 43, 43, 43, 43, - 36, 69, 2, 60, 43, 43, 43, 43, 36, 93, 85, 42, 42, 42, 42, 84, - 97, 36, 62, 2, 58, 42, 59, 86, 7, 7, 7, 7, 7, 62, 62, 2, - 179, 27, 27, 27, 27, 27, 27, 27, 27, 27, 99, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 85, 86, 42, 85, 84, 42, 2, 2, 2, 70, - 69, 43, 43, 43, 43, 43, 43, 43, 36, 36, 36, 60, 60, 36, 36, 61, - 36, 36, 36, 36, 36, 36, 36, 61, 36, 36, 36, 36, 62, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 69, 85, 86, 42, 42, 42, 79, 43, 43, - 42, 85, 61, 36, 36, 36, 60, 61, 60, 36, 61, 36, 36, 56, 70, 85, - 84, 85, 89, 88, 89, 88, 85, 43, 60, 43, 43, 88, 43, 43, 61, 36, - 36, 85, 43, 42, 42, 42, 79, 43, 42, 42, 79, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 61, 43, 60, 36, 36, 36, 61, 85, 86, 42, 42, - 79, 89, 88, 88, 85, 89, 85, 84, 70, 70, 2, 92, 63, 43, 43, 43, - 56, 79, 43, 43, 43, 43, 43, 43, 36, 36, 93, 85, 42, 42, 42, 42, - 85, 42, 84, 70, 36, 62, 2, 2, 7, 7, 7, 7, 7, 2, 92, 70, - 85, 86, 42, 42, 84, 84, 85, 86, 84, 42, 36, 71, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 93, 85, 42, 42, 43, 85, 85, 42, 86, - 59, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 42, 43, - 85, 86, 42, 42, 42, 84, 86, 86, 59, 2, 60, 43, 43, 43, 43, 43, - 2, 2, 2, 2, 2, 2, 63, 43, 36, 36, 36, 36, 36, 69, 86, 85, - 42, 42, 42, 86, 62, 43, 43, 43, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 56, 86, - 85, 42, 42, 86, 42, 42, 43, 43, 7, 7, 7, 7, 7, 27, 2, 96, - 42, 42, 42, 42, 86, 59, 43, 43, 27, 99, 43, 43, 43, 43, 43, 61, - 36, 36, 36, 60, 61, 43, 36, 36, 36, 36, 61, 60, 36, 36, 36, 36, - 85, 85, 85, 88, 89, 56, 84, 70, 97, 86, 2, 63, 43, 43, 43, 43, - 36, 36, 36, 36, 43, 36, 36, 36, 93, 85, 42, 42, 43, 42, 85, 85, - 70, 71, 89, 43, 43, 43, 43, 43, 69, 42, 42, 42, 42, 70, 36, 36, - 36, 69, 42, 42, 84, 69, 42, 59, 2, 2, 2, 58, 43, 43, 43, 43, - 69, 42, 42, 84, 86, 42, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, - 42, 42, 42, 84, 42, 2, 71, 2, 2, 63, 43, 43, 43, 43, 43, 43, - 2, 2, 2, 2, 2, 43, 43, 43, 84, 42, 84, 84, 43, 43, 43, 43, - 62, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 42, 42, 42, 86, - 62, 2, 2, 43, 43, 43, 43, 43, 2, 36, 36, 36, 36, 36, 36, 36, - 43, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 88, 42, 42, 42, - 84, 42, 86, 79, 43, 43, 43, 43, 36, 36, 36, 60, 36, 61, 36, 36, - 69, 42, 42, 79, 43, 79, 42, 56, 42, 42, 42, 69, 43, 43, 43, 43, - 36, 36, 36, 61, 60, 36, 36, 36, 36, 36, 36, 36, 36, 85, 85, 89, - 42, 88, 86, 86, 60, 43, 43, 43, 36, 36, 36, 36, 82, 36, 43, 43, - 36, 69, 84,106, 63, 43, 43, 43, 42, 93, 36, 36, 36, 36, 36, 36, - 36, 36, 85, 42, 42, 79, 43, 85, 84, 59, 2, 2, 2, 2, 2, 2, - 7, 7, 7, 7, 7, 79, 43, 43, 27, 27, 90, 66, 66, 66, 55, 20, - 167, 66, 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 43, 43, 92, - 104,104,104,104,104,104,104,181, 2, 2, 63, 43, 43, 43, 43, 43, - 62, 63, 43, 43, 43, 43, 43, 43, 64, 64, 64, 64, 64, 64, 64, 64, - 70, 36, 36, 69, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 85, 86, 42, - 42, 42, 59, 43, 43, 43, 43, 43, 42, 42, 42, 59, 2, 2, 66, 66, - 39, 39, 96, 43, 43, 43, 43, 43, 7, 7, 7, 7, 7,179, 27, 27, - 27, 61, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 43, 43, 61, 36, - 39, 68, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 82,163, 2, - 27, 27, 27, 30, 2, 63, 43, 43, 11, 11, 11, 11, 46,149, 16, 16, - 16, 16, 43, 43, 43, 43, 43, 43, 36, 36, 36, 36, 36, 60, 43, 56, - 93, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, 85, - 85, 85, 85, 85, 43, 43, 43, 56, 42, 73, 39, 39, 39, 39, 39, 39, - 39, 87, 79, 43, 43, 43, 43, 43, 85, 39,104,181, 43, 43, 43, 43, - 43, 43, 43, 43, 43, 43, 43, 61, 36, 60, 43, 43, 43, 43, 43, 43, - 39, 39, 51, 39, 39, 39, 51, 80, 43, 60, 43, 43, 43, 43, 43, 43, - 36, 60, 61, 43, 43, 43, 43, 43, 43, 43, 36, 36, 43, 43, 43, 43, - 36, 36, 36, 36, 36, 43, 49, 59, 64, 64, 43, 43, 43, 43, 43, 43, - 7, 7, 7, 7, 7, 66, 91, 43, 66, 66, 43, 43, 43, 66, 66, 66, - 176, 43, 43, 43, 43, 43, 43, 43, 42, 42, 42, 79, 43, 43, 43, 43, - 66, 66, 66, 91, 54, 66, 66, 66, 66, 66,186, 86, 42, 66,186, 85, - 85,187, 64, 64, 64, 83, 42, 42, 42, 75, 49, 42, 42, 42, 66, 66, - 66, 66, 66, 66, 66, 42, 42, 66, 66, 42, 75, 43, 43, 43, 43, 43, - 27, 27, 43, 43, 43, 43, 43, 43, 11, 11, 11, 11, 11, 16, 16, 16, - 16, 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 16, - 16, 16,109, 16, 16, 16, 16, 16, 11, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 46, 11, 43, 46, 47, 46, 47, 11, 46, 11, - 11, 11, 11, 16, 16,149,149, 16, 16, 16,149, 16, 16, 16, 16, 16, - 16, 16, 11, 47, 11, 46, 47, 11, 11, 11, 46, 11, 11, 11, 46, 16, - 16, 16, 16, 16, 11, 47, 11, 46, 11, 11, 46, 46, 43, 11, 11, 11, - 46, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 11, 11, - 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 43, 11, 11, 11, 11, - 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, - 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, - 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, - 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 43, 7, - 42, 42, 42, 75, 66, 49, 42, 42, 42, 42, 42, 42, 42, 42, 75, 66, - 66, 66, 49, 66, 66, 66, 66, 66, 66, 66, 75, 21, 2, 2, 43, 43, - 43, 43, 43, 43, 43, 56, 42, 42, 16, 16, 16, 16, 16,138, 16, 16, - 16, 16, 16, 16, 16, 16, 16,109, 43, 43,149, 16, 16,109, 43, 43, - 42, 42, 42, 79, 42, 42, 42, 42, 42, 42, 42, 42, 79, 56, 42, 42, - 42, 56, 79, 42, 42, 79, 43, 43, 39, 39, 39, 39, 39, 39, 39, 43, - 43, 43, 43, 43, 43, 43, 43, 56, 42, 42, 42, 73, 39, 39, 39, 43, - 7, 7, 7, 7, 7, 43, 43, 76, 36, 36, 36, 36, 36, 36, 36, 79, - 36, 36, 36, 36, 36, 36, 42, 42, 7, 7, 7, 7, 7, 43, 43, 95, - 36, 36, 36, 36, 36, 82, 42, 42,188, 7, 7, 7, 7,189, 43, 92, - 36, 69, 36, 70, 36, 36, 36, 42, 36, 36, 69, 43, 43, 43, 43, 82, - 36, 36, 36, 60, 36, 36, 61, 60, 36, 36, 60,179, 27, 27, 27, 27, - 16, 16, 42, 42, 42, 73, 43, 43, 27, 27, 27, 27, 27, 27,162, 27, - 190, 27, 99, 43, 43, 43, 43, 43, 27, 27, 27, 27, 27, 27, 27,162, - 27, 27, 27, 27, 27, 27, 27, 43, 36, 36, 61, 36, 36, 36, 36, 36, - 61, 60, 60, 61, 61, 36, 36, 36, 36, 60, 36, 36, 61, 61, 43, 43, - 43, 60, 43, 61, 61, 61, 61, 36, 61, 60, 60, 61, 61, 61, 61, 61, - 61, 60, 60, 61, 36, 60, 36, 36, 36, 60, 36, 36, 61, 36, 60, 60, - 36, 36, 36, 36, 36, 61, 36, 36, 61, 36, 61, 36, 36, 61, 36, 36, - 8, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 66, 43, 43, - 54, 66, 66, 66, 66, 66, 66, 66, 27, 27, 27, 27, 27, 27, 90, 66, - 66, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, 66, 66, 66, 66, 66, - 66, 91, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 91, 43, 43, 43, - 66, 43, 43, 43, 43, 43, 43, 43, 66, 66, 66, 66, 66, 25, 40, 40, - 66, 66, 66, 66, 91, 43, 66, 66, 66, 66, 66, 66, 43, 43, 43, 43, - 8, 8, 8, 8,176, 43, 43, 43, 66, 66, 66, 66, 66, 91, 43, 66, - 66, 66, 66, 91, 91, 43, 54, 66, 66, 66, 66, 66, 66, 66, 91, 54, - 66, 66, 66, 66, 66, 91, 43, 54, 66, 91, 66, 66, 66, 66, 66, 66, - 7, 7, 7, 7, 7, 91, 43, 43, 78, 43, 43, 43, 43, 43, 43, 43, - 170,170,170,170,170,170,170, 43,170,170,170,170,170,170,170, 0, - 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, - 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, - 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, - 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, - 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 6, 6, - 24, 24, 6, 24, 12, 12, 2, 2, 6, 5, 9, 21, 9, 2, 2, 9, - 25, 9, 26, 12, 11, 11, 2, 6, 5, 21, 17, 2, 2, 26, 26, 23, - 2, 12, 17, 12, 21, 12, 12, 21, 7, 2, 2, 7, 7, 21, 21, 2, - 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, 12, 7, 21, 7, - 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, 2, 1, 12, 2, - 6, 2, 24, 7, 7, 6, 1, 12, 12, 10, 10, 10, 10, 12, 21, 6, - 2, 10, 10, 2, 15, 26, 26, 2, 2, 21, 7, 10, 15, 7, 2, 23, - 21, 26, 10, 7, 21, 15, 15, 2, 17, 7, 29, 7, 7, 22, 18, 2, - 14, 14, 14, 7, 10, 21, 17, 21, 11, 12, 5, 2, 5, 6, 8, 8, - 8, 24, 5, 24, 2, 24, 9, 24, 24, 2, 29, 29, 29, 1, 17, 17, - 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, 21, 16, 16, 21, - 25, 22, 18, 21, 21, 29, 1, 2, 15, 6, 18, 6, 12, 11, 9, 26, - 26, 9, 26, 5, 7, 5, 5, 26, 14, 9, 5, 14, 14, 15, 25, 26, - 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 2, 5, 22, 21, 21, 22, - 18, 17, 26, 6, 7, 14, 17, 22, 18, 18, 26, 14, 17, 6, 14, 6, - 12, 24, 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, - 6, 10, 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, - 25, 2, 25, 24, 23, 2, 2, 15, 12, 15, 14, 2, 21, 14, 7, 15, - 12, 17, 21, 1, 26, 10, 10, 1, 7, 13, 13, 2, 23, 15, 0, 1, - 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, - 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, - 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, - 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, - 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 2, 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, - 9, 10, 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, 16, 19, - 16, 19, 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, 36, 0, - 37, 0, 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, 0, 47, - 0, 0, 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 50, - 0, 51, 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, 56, 57, - 0, 0, 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, 64, 65, - 0, 0, 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 70, - 0, 71, 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, - 74, 75, 0, 0, 0, 0, 76, 77, 0, 78, 79, 0, 0, 80, 81, 0, - 82, 62, 0, 83, 84, 0, 0, 85, 86, 87, 0, 88, 0, 89, 0, 90, - 0, 0, 51, 91, 51, 0, 92, 0, 93, 0, 0, 0, 81, 0, 0, 0, - 94, 95, 0, 96, 97, 98, 99, 0, 0, 0, 0, 0, 51, 0, 0, 0, - 0,100,101, 0, 0, 0, 0, 0, 0,102, 0, 0, 0, 0, 0, 0, - 103, 0, 0, 0, 0, 0, 0,104,105, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,108,109, 0, 0,110, 0, 0, 0, 0, 0, 0,111, 0,112, 0, - 105, 0, 0, 0, 0, 0,113,114, 0, 0, 0, 0, 0, 0, 0,115, - 0, 0, 0,116, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0,118, - 0,119, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, - 8, 0, 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, - 14, 15, 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, - 0, 0, 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, - 28, 29, 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, - 33, 0, 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, - 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, - 0, 43, 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, - 0, 0, 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, - 0, 53, 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, - 0, 0, 57, 58, 0, 0, 0, 59, 60, 61, 62, 0, 0, 0, 0, 63, - 52, 0, 64, 65, 0, 0, 66, 0, 0, 0, 67, 68, 0, 0, 0, 69, - 0, 70, 71, 72, 73, 74, 1, 75, 0, 76, 77, 78, 0, 0, 79, 80, - 0, 0, 0, 81, 0, 0, 1, 1, 0, 0, 82, 0, 0, 83, 0, 0, - 0, 0, 79, 84, 0, 85, 0, 0, 0, 0, 0, 80, 86, 0, 87, 0, - 52, 0, 1, 80, 0, 0, 88, 0, 0, 89, 0, 0, 0, 0, 0, 90, - 57, 0, 0, 0, 0, 0, 0, 91, 92, 0, 0, 86, 0, 0, 33, 0, - 0, 93, 0, 0, 0, 0, 94, 0, 0, 0, 0, 49, 0, 0, 95, 0, - 0, 0, 0, 96, 97, 0, 0, 98, 0, 0, 99, 0, 0, 0,100, 0, - 0, 0,101, 0, 0, 0,102, 0, 0, 0, 0,103,104, 95, 0, 0, - 105, 0, 0, 0, 86, 0, 0,106, 0, 0, 0,107,108, 0, 0,109, - 110, 0, 0, 0, 0, 0, 0,111, 0, 0,112, 0, 0, 0, 0,113, - 33, 0,114,115,116, 57, 0, 0,117, 35, 0, 0,118, 0, 0, 0, - 119, 0, 0, 0, 0, 0, 0,120, 0, 0,121, 0, 0, 0, 0,122, - 90, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,123, 0, 0, 0, - 0,124, 0, 0,125, 0, 0, 0, 0,123, 0, 0,126, 0, 0, 0, - 0, 0, 81, 0, 0, 0, 0,127, 0, 0, 0,128, 0, 0, 0,129, - 0,130, 0, 0, 0, 0,131,132,133, 0,134, 0,135, 0, 0, 0, - 136,137,138, 0, 79, 0, 0, 0, 0, 0, 35, 0, 0, 0,139, 0, - 0, 0,140, 0, 0, 0,141, 0, 0, 0,142,143, 0,144, 0, 0, - 145, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, - 5, 6, 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, - 18, 1, 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, - 25, 26, 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, - 34, 35, 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, - 42, 0, 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, - 21, 0, 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, - 0, 19, 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, - 54, 21, 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, - 0, 0, 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, - 0, 0, 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, - 0, 0, 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, - 0, 77, 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, - 0, 80, 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, - 0, 0, 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, - 1, 52, 15, 86, 36, 10, 21, 1, 1, 1, 1, 41, 1, 21, 87, 0, - 0, 55, 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, - 0, 89, 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 90, 9, 12, 4, - 91, 8, 92, 47, 0, 58, 50, 0, 21, 1, 21, 93, 94, 1, 1, 1, - 1, 95, 96, 97, 98, 1, 99, 58, 81,100,101, 4, 58, 0, 0, 0, - 0, 0, 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0,102,103, - 0, 0,104, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, 0, 0, - 0, 0, 0, 62, 0, 0,105, 68, 61, 0, 0, 0, 78, 0, 0, 0, - 106,107, 58, 38, 81, 0, 0, 0, 0, 0, 0,108, 1, 14, 4, 12, - 84, 0, 0, 0, 0, 38, 90, 0, 0, 0, 0,109, 0, 0,110, 61, - 0,111, 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, 19, 58, - 0, 0,112, 51, 0,112, 14, 52,113, 41, 0, 0, 62, 0, 0, 61, - 0, 0,114, 0, 90, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, - 0,114, 0, 0, 0, 0,115, 0, 0, 0, 78, 55, 0, 38, 1, 58, - 1, 58, 0, 0, 0, 0, 0, 88, 63, 89, 0, 0,116, 0, 0, 0, - 55, 0, 0, 0, 0,116, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, - 0, 61, 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, - 8, 92, 0, 0, 1, 90, 0, 0,117, 0, 0, 0, 0, 0, 0,118, - 0,119,120,121,122, 0,105, 4,123, 49, 23, 0, 0, 0, 38, 50, - 38, 58, 0, 0, 1, 90, 1, 1, 1, 1, 39, 1, 48,106, 90, 0, - 0, 0, 0, 1, 0, 0, 0,124, 0, 0, 0,113, 19, 59, 0, 38, - 0, 81, 0, 0, 4,123, 0, 0, 0, 1,125, 0, 0, 0, 0, 0, - 230,230,230,230,230,232,220,220,220,220,232,216,220,220,220,220, - 220,202,202,220,220,220,220,202,202,220,220,220, 1, 1, 1, 1, - 1,220,220,220,220,230,230,230,230,240,230,220,220,220,230,230, - 230,220,220, 0,230,230,230,220,220,220,220,230,232,220,220,230, - 233,234,234,233,234,234,233,230, 0, 0, 0,230, 0,220,230,230, - 230,230,220,230,230,230,222,220,230,230,220,220,230,222,228,230, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, - 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, - 28, 29, 30, 31, 32, 33, 34,230,230,220,220,230,220,230,230,220, - 35, 0, 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0,220,230, - 230,220, 0, 0, 0, 36, 0, 0,230,220,230,230,220,220,230,220, - 220,230,220,230,220,230,230, 0, 0,220, 0, 0,230,230, 0,230, - 0,230,230,230,230,230, 0, 0, 0,220,220,220,230,220,220,220, - 230,230, 0,220, 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, - 0,230,220,230,230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, - 0, 0, 0, 9, 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0, - 107,107,107,107,118,118, 9, 0,122,122,122,122,220,220, 0, 0, - 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, - 0, 0,130,130,130,130, 0, 0,130, 0,230,230, 9, 0,230,230, - 0, 0,220, 0, 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, - 0,230, 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0, - 230, 0, 0,220,230,220, 0,220,230,230,230,234, 0, 0, 9, 9, - 0, 0, 7, 0,230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0, - 230,234,214,220,202,230,230,230,230,230,232,228,228,220,218,230, - 233,220,230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230, - 220,230, 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, - 0, 0, 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, - 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, - 0, 0,230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, - 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, - 0,226,216,216,216,216,216, 0,220,220,220, 0,232,232,220,230, - 230,230, 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, - 26, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, - 3, 3, 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, - 13, 3, 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, - 3, 3, 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, - 3, 3, 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, - 27, 28, 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, - 0, 0, 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, - 0, 0, 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, - 13, 0, 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, - 23, 24, 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, - 0, 8, 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, - 35, 19, 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, - 0, 1, 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, - 14, 0, 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, - 52, 53, 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, - 19, 1, 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, - 0, 0, 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, - 0, 0, 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, - 0, 7, 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, - 0, 0, 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, - 15, 16, 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, - 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, - 21, 9, 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, - 26, 0, 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, - 1, 28, 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, - 1, 4, 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, - 21, 21, 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, - 39, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, - 0, 0, 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, - 1, 1, 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, - 0, 0, 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, - 21, 21, 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, - 9, 1, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 9, 10, 11, 11, 11, 11, 12, 13, - 13, 13, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 13, 13, 13, - 24, 25, 26, 26, 26, 27, 13, 13, 13, 28, 29, 30, 13, 31, 32, 33, - 34, 35, 36, 37, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 38, 7, 7, 39, 7, 40, 7, 7, - 7, 41, 13, 42, 7, 7, 43, 7, 7, 7, 44, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 45, 0, 0, 1, 2, 2, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 35, 36, 37, 37, - 37, 37, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, - 51, 52, 2, 2, 53, 54, 55, 56, 57, 58, 59, 59, 59, 59, 60, 59, - 59, 59, 59, 59, 59, 59, 61, 61, 59, 59, 59, 59, 62, 59, 63, 64, - 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 59, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 78, 69, 69, 69, 69, 79, 79, 79, 79, 79, 79, 79, 79, 79, 80, - 81, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 94, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 69, 69, 96, 97, 98, 99, 99, 99, - 100,101,102,103,104,105,106,107,108,109, 95,110,111,112,113,114, - 115,116,117,117,118,119,120,121,122,123,124,125,126,127,128,129, - 130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145, - 95,146,147,148,149, 95,150,151,152,153,154,155,156,157,158,159, - 160,161, 95,162,163,164,165,165,165,165,165,165,165,166,167,165, - 168, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95,169,170,170,170,170,170,170,170,170,171,170, - 170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170, - 170,170,170,170,170,170,170,170,170,170,170,170,170,172,173,173, - 173,173,174, 95, 95, 95, 95, 95,175, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95,176,176,176,176,177,178,179,180, 95, 95, - 181, 95,182,183,184,185,186,186,186,186,186,186,186,186,186,186, - 186,186,186,186,186,186,186,186,186,186,186,186,187,187,187,188, - 189,190, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95,191,192,193,194,195,195,196, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95,197,198, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 59,199, - 59, 59, 59,200,201,202, 59,203,204,205,206,207,208, 95,209,210, - 211, 59, 59,212, 59,213,214,214,214,214,214,215, 95, 95, 95, 95, - 95, 95, 95, 95,216, 95,217,218,219, 95, 95,220, 95, 95, 95,221, - 95,222, 95,223, 95,224,225,226,227, 95, 95, 95, 95, 95,228,229, - 230, 95,231,232, 95, 95,233,234, 59,235,236, 95, 59, 59, 59, 59, - 59, 59, 59,237, 59,238,239,240, 59, 59,241,242, 59,243, 95, 95, - 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69,244, 69, 69,245, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, - 69, 69, 69,246, 69, 69, 69, 69, 69, 69, 69, 69, 69,247, 69, 69, - 69, 69,248, 95, 95, 95, 69, 69, 69, 69,249, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 69, 69, 69, 69, 69, 69,250, 69, 69, 69, - 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69,251, 95, - 95, 95, 95, 95, 95, 95,252, 95,253,254, 0, 1, 2, 2, 0, 1, - 2, 2, 2, 3, 4, 5, 0, 0, 0, 0, 0, 19, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, 0, - 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 2, 2, - 9, 9, 9, 9, 0, 9, 2, 2, 2, 2, 9, 0, 9, 0, 9, 9, - 9, 2, 9, 2, 9, 9, 9, 9, 2, 9, 9, 9, 55, 55, 55, 55, - 55, 55, 6, 6, 6, 6, 6, 1, 1, 6, 2, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 2, 2, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 2, 2, 2, 2, 14, 14, 2, 2, 2, 3, 3, 3, 3, 3, 0, - 3, 3, 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, - 3, 3, 1, 3, 3, 3, 37, 37, 37, 37, 37, 37, 2, 37, 37, 37, - 37, 2, 2, 37, 37, 37, 38, 38, 38, 38, 38, 38, 2, 2, 64, 64, - 64, 64, 64, 64, 64, 2, 2, 64, 64, 64, 90, 90, 90, 90, 90, 90, - 2, 2, 90, 90, 90, 2, 95, 95, 95, 95, 2, 2, 95, 2, 3, 3, - 2, 2, 2, 2, 2, 3, 3, 3, 0, 3, 7, 7, 7, 7, 7, 1, - 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 2, 5, - 5, 5, 5, 2, 2, 5, 5, 2, 5, 5, 5, 2, 5, 2, 2, 2, - 5, 5, 5, 5, 2, 2, 5, 5, 5, 2, 2, 2, 2, 5, 5, 5, - 2, 5, 2, 11, 11, 11, 11, 11, 11, 2, 2, 2, 2, 11, 11, 2, - 2, 11, 11, 11, 11, 11, 11, 2, 11, 11, 2, 11, 11, 2, 11, 11, - 2, 2, 2, 11, 2, 2, 11, 2, 11, 2, 2, 2, 11, 11, 2, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 2, 10, 10, 2, 10, 10, 10, 10, - 2, 2, 10, 2, 2, 2, 2, 2, 10, 10, 2, 21, 21, 21, 21, 21, - 21, 21, 21, 2, 2, 21, 21, 2, 21, 21, 21, 21, 2, 2, 21, 21, - 2, 21, 2, 2, 21, 21, 2, 2, 22, 22, 2, 22, 22, 22, 22, 22, - 22, 2, 22, 2, 22, 22, 22, 22, 2, 2, 2, 22, 22, 2, 2, 2, - 2, 22, 22, 2, 2, 2, 22, 22, 22, 22, 23, 23, 23, 23, 23, 2, - 23, 23, 23, 23, 2, 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 2, - 23, 23, 2, 2, 2, 23, 16, 16, 16, 16, 16, 2, 16, 16, 2, 16, - 16, 16, 16, 16, 2, 2, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, - 16, 16, 20, 20, 20, 20, 20, 2, 20, 20, 2, 2, 20, 20, 2, 36, - 36, 36, 36, 36, 36, 36, 36, 36, 36, 2, 2, 2, 36, 36, 36, 36, - 2, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, 36, 36, 2, - 36, 2, 36, 2, 2, 2, 2, 24, 24, 24, 24, 24, 24, 24, 24, 24, - 24, 2, 2, 2, 2, 0, 2, 18, 18, 2, 18, 2, 18, 18, 18, 18, - 18, 2, 18, 18, 18, 18, 2, 18, 2, 18, 18, 18, 2, 2, 18, 2, - 18, 2, 25, 25, 25, 25, 2, 25, 25, 25, 25, 2, 2, 2, 25, 2, - 25, 25, 25, 0, 0, 0, 0, 25, 25, 2, 33, 33, 33, 33, 8, 8, - 8, 8, 8, 8, 2, 8, 2, 8, 2, 2, 8, 8, 8, 0, 12, 12, - 12, 12, 30, 30, 30, 30, 30, 2, 30, 30, 30, 30, 2, 2, 30, 30, - 30, 2, 2, 30, 30, 30, 30, 2, 2, 2, 29, 29, 29, 29, 29, 29, - 2, 2, 28, 28, 28, 28, 34, 34, 34, 34, 34, 2, 2, 2, 35, 35, - 35, 35, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, 2, 2, 45, 45, - 45, 45, 45, 45, 2, 2, 2, 2, 2, 45, 44, 44, 44, 44, 44, 0, - 0, 2, 43, 43, 43, 43, 46, 46, 46, 46, 46, 2, 46, 46, 31, 31, - 31, 31, 31, 31, 2, 2, 32, 32, 0, 0, 32, 0, 32, 32, 32, 32, - 32, 32, 32, 32, 2, 2, 32, 2, 2, 2, 32, 32, 32, 2, 28, 28, - 2, 2, 48, 48, 48, 48, 48, 48, 48, 2, 48, 2, 2, 2, 52, 52, - 52, 52, 52, 52, 2, 2, 52, 2, 2, 2, 58, 58, 58, 58, 58, 58, - 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 54, 54, 54, 54, 2, 2, - 54, 54, 91, 91, 91, 91, 91, 91, 91, 2, 91, 2, 2, 91, 91, 91, - 2, 2, 1, 1, 2, 2, 62, 62, 62, 62, 62, 2, 62, 62, 76, 76, - 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 2, 2, 2, 70, 70, 70, - 2, 2, 2, 70, 70, 70, 73, 73, 73, 73, 6, 6, 6, 2, 8, 8, - 8, 2, 2, 8, 8, 8, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, - 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 19, 19, 9, 9, 9, 9, - 9, 6, 19, 9, 9, 9, 9, 9, 19, 19, 9, 9, 9, 19, 6, 19, - 19, 19, 19, 19, 19, 9, 9, 9, 2, 2, 2, 9, 2, 9, 2, 9, - 9, 9, 1, 1, 0, 0, 0, 2, 0, 0, 0, 19, 2, 2, 0, 0, - 0, 19, 0, 0, 0, 2, 19, 2, 2, 2, 0, 0, 2, 2, 1, 2, - 2, 2, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, 2, 2, - 0, 0, 56, 56, 56, 56, 2, 55, 55, 55, 61, 61, 61, 61, 2, 2, - 2, 61, 61, 2, 2, 2, 13, 13, 13, 13, 13, 13, 2, 13, 13, 13, - 2, 2, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, - 12, 12, 2, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 2, 2, 1, - 1, 0, 0, 15, 15, 15, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 0, 2, 26, 26, 26, 26, 26, 26, 26, 2, 12, 12, 12, 12, 12, - 12, 2, 12, 12, 12, 0, 39, 39, 39, 39, 39, 2, 2, 2, 39, 39, - 39, 2, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, 2, 19, - 19, 19, 60, 60, 60, 60, 60, 2, 2, 2, 65, 65, 65, 65, 75, 75, - 75, 75, 75, 75, 2, 2, 2, 2, 75, 75, 69, 69, 69, 69, 69, 69, - 0, 69, 74, 74, 74, 74, 2, 2, 2, 74, 12, 2, 2, 2, 84, 84, - 84, 84, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, 84, 33, 33, - 33, 2, 68, 68, 68, 68, 68, 68, 68, 2, 68, 68, 2, 2, 92, 92, - 92, 92, 92, 92, 92, 2, 2, 2, 2, 92, 87, 87, 87, 87, 87, 87, - 87, 2, 19, 9, 19, 19, 19, 19, 0, 0, 87, 87, 2, 2, 2, 2, - 2, 12, 19, 19, 19, 2, 2, 2, 2, 4, 14, 2, 14, 2, 14, 14, - 2, 14, 14, 2, 14, 14, 3, 3, 0, 0, 1, 1, 6, 6, 3, 2, - 3, 3, 3, 2, 2, 0, 2, 0, 0, 0, 0, 0, 17, 17, 17, 17, - 0, 0, 2, 2, 12, 12, 49, 49, 49, 49, 2, 49, 49, 49, 49, 49, - 49, 2, 49, 49, 2, 49, 49, 49, 2, 2, 0, 2, 2, 2, 9, 2, - 2, 2, 0, 1, 2, 2, 71, 71, 71, 71, 71, 2, 2, 2, 67, 67, - 67, 67, 67, 2, 2, 2, 42, 42, 42, 42, 2, 42, 42, 42, 41, 41, - 41, 41, 41, 41, 41, 2,118,118,118,118,118,118,118, 2, 53, 53, - 53, 53, 53, 53, 2, 53, 59, 59, 59, 59, 59, 59, 2, 2, 40, 40, - 40, 40, 51, 51, 51, 51, 50, 50, 50, 50, 50, 50, 2, 2,135,135, - 135,135,106,106,106,106,104,104,104,104, 2, 2, 2,104,161,161, - 161,161,161,161,161, 2,161,161, 2,161,161, 2, 2, 2,170,170, - 170,170,110,110,110,110,110,110,110, 2,110,110, 2, 2, 19, 19, - 2, 19, 19, 2, 19, 19, 47, 47, 47, 47, 47, 47, 2, 2, 47, 2, - 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, 47, 81, 81, - 81, 81, 81, 81, 2, 81,120,120,120,120,116,116,116,116,116,116, - 116, 2, 2, 2, 2,116,128,128,128,128,128,128,128, 2,128,128, - 2, 2, 2, 2, 2,128, 66, 66, 66, 66, 2, 2, 2, 66, 72, 72, - 72, 72, 72, 72, 2, 2, 2, 2, 2, 72,173,173,173,173,173,173, - 2, 2, 98, 98, 98, 98, 97, 97, 97, 97, 2, 2, 97, 97, 57, 57, - 57, 57, 2, 57, 57, 2, 2, 57, 57, 57, 57, 57, 2, 2, 57, 57, - 57, 2, 2, 2, 2, 57, 57, 2, 2, 2, 88, 88, 88, 88,117,117, - 117,117,112,112,112,112,112,112,112, 2, 2, 2, 2,112, 78, 78, - 78, 78, 78, 78, 2, 2, 2, 78, 78, 78, 83, 83, 83, 83, 83, 83, - 2, 2, 82, 82, 82, 82, 82, 82, 82, 2,122,122,122,122,122,122, - 2, 2, 2,122,122,122,122, 2, 2, 2, 89, 89, 89, 89, 89, 2, - 2, 2,130,130,130,130,130,130,130, 2, 2, 2,130,130,144,144, - 144,144,144,144, 2, 2,165,165,165,165,165,165, 2, 2, 2,165, - 165,165, 2, 2,165,165, 3, 3, 3, 2,156,156,156,156,156,156, - 2,156,156,156, 2, 2, 2, 2, 3, 3, 3, 2, 2, 2,147,147, - 147,147,148,148,148,148,148,148, 2, 2,158,158,158,158,158,158, - 2, 2,153,153,153,153,149,149,149,149,149,149,149, 2, 94, 94, - 94, 94, 94, 94, 2, 2, 2, 2, 94, 94, 2, 2, 2, 94, 85, 85, - 85, 85, 85, 85, 85, 2, 2, 85, 2, 2,101,101,101,101,101, 2, - 2, 2,101,101, 2, 2, 96, 96, 96, 96, 96, 2, 96, 96,111,111, - 111,111,111,111,111, 2,100,100,100,100,108,108,108,108,108,108, - 2,108,108,108, 2, 2,129,129,129,129,129,129,129, 2,129, 2, - 129,129,129,129, 2,129,129,129, 2, 2,109,109,109,109,109,109, - 109, 2,109,109, 2, 2,107,107,107,107, 2,107,107,107,107, 2, - 2,107,107, 2,107,107,107,107, 2, 1,107,107, 2, 2,107, 2, - 2, 2, 2, 2, 2,107, 2, 2,107,107,171,171,171,171,171,171, - 2,171, 2, 2,171, 2,171, 2,171, 2, 2,171, 2,171,171,171, - 171, 2,171, 2, 2, 2, 2,171,171, 2,137,137,137,137, 2,137, - 137,137,137,137, 2, 2,124,124,124,124,124,124, 2, 2,123,123, - 123,123,123,123, 2, 2,114,114,114,114,114, 2, 2, 2,114,114, - 2, 2,102,102,102,102,102,102, 2, 2,126,126,126,126,126,126, - 126, 2, 2,126,126,126,142,142,142,142,125,125,125,125,125,125, - 125, 2, 2, 2, 2,125,154,154,154,154,154,154,154, 2, 2,154, - 2, 2, 2,154,154, 2,154,154, 2,154,154, 2, 2,154,154,154, - 2, 2,150,150,150,150, 2, 2,150,150,150, 2, 2, 2,141,141, - 141,141,140,140,140,140,140,140,140, 2,121,121,121,121,121, 2, - 2, 2, 7, 7, 2, 2,169,169,169,169,169,169, 2, 2,133,133, - 133,133,133, 2,133,133,133,133,133, 2,133,133, 2, 2,133, 2, - 2, 2,134,134,134,134, 2, 2,134,134, 2,134,134,134,134,134, - 134, 2,138,138,138,138,138,138,138, 2,138,138, 2,138, 2, 2, - 138, 2,138,138, 2, 2,143,143,143,143,143,143, 2,143,143, 2, - 143,143,143,143,143, 2,143, 2, 2, 2,143,143, 2, 2,175,175, - 175,175,175,175, 2, 2,145,145,145,145,145, 2, 2, 2,163,163, - 163,163,163, 2,163,163,163,163,163, 2, 2, 2,163,163, 86, 2, - 2, 2, 63, 63, 63, 63, 63, 63, 2, 2, 63, 63, 63, 2, 63, 2, - 2, 2,157,157,157,157,157,157,157, 2, 80, 80, 80, 80, 80, 80, - 2, 2, 80, 80, 80, 2,127,127,127,127,127,127,127, 2,166,166, - 166,166,166,166, 2, 2, 79, 2, 2, 2,115,115,115,115,115,115, - 115, 2,115,115, 2, 2, 2, 2,115,115,159,159,159,159,159,159, - 159, 2,159,159, 2, 2,103,103,103,103,103,103, 2, 2,119,119, - 119,119,119,119, 2, 2,119,119, 2,119, 2,119,119,119,167,167, - 167,167,167,167, 2, 2,146,146,146,146,146,146,146, 2,172,172, - 172,172,172, 2, 2,172, 99, 99, 99, 99, 99, 99, 99, 2, 2, 2, - 2, 99,136,139, 13, 13,155, 2, 2, 2, 13, 13, 13, 2,136,136, - 136,136,155,155,155,155,155,155, 2, 2, 2, 2, 2,155,136,136, - 136, 2, 2, 17, 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 17, 17, - 17, 2, 2, 2, 15, 2, 2, 17, 2, 2,139,139,139,139,105,105, - 105,105,105,105,105, 2,105, 2, 2, 2,105,105, 2, 2, 1, 1, - 1, 2, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 2, 2, - 0, 2, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 2,131,131, - 131,131, 2, 2, 2,131, 2,131,131,131, 56, 56, 56, 2, 56, 2, - 2, 56, 56, 56, 2, 56, 56, 2, 56, 56, 6, 6, 2, 2, 2, 2, - 2, 6,151,151,151,151,151, 2, 2, 2,151,151, 2, 2, 2, 2, - 151,151,160,160,160,160,160,160,160, 2,152,152,152,152,152,152, - 2, 2, 2, 2, 2,152,164,164,164,164,164,164, 2, 2,168,168, - 168,168,168,168,168, 2, 2, 2, 2,168,174,174,174,174,174,174, - 174, 2,174,174, 2, 2, 2, 2,174,174, 2, 30, 30, 2,113,113, - 113,113,113, 2, 2,113,113,113,113, 2,132,132,132,132,132,132, - 2, 2, 2, 2,132,132, 2, 3, 3, 3, 2, 3, 3, 2, 3, 2, - 2, 3, 2, 3, 2, 3, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, - 2, 3, 15, 0, 0, 2, 0, 2, 2, 0, 13, 2, 2, 2, 2, 0, - 2, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, - 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, 17, - 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, 22, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 26, 27, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, - 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, - 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, - 31, 32, 0, 33, 0, 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 43, 44, 0, 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, - 0, 0, 0, 48, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 50, 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, - 0, 0, 0, 0, 54, 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, - 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 58, 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, - 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 4, 5, 6, 7, 2, 2, 2, 2, 2, 8, 2, 2, 9, 1, 10, + 11, 12, 13, 14, 15, 0, 16, 2, 17, 0, 18, 19, 20, 21, 22, 23, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 24, 25, 26, 2, 27, 28, + 2, 29, 2, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, - 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102, - 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,104, 0, 0, 0, 0, 0, 0,105,106, 0,107, 0, 0, 0, - 108, 0,109, 0,110, 0,111,112,113, 0,114, 0, 0, 0,115, 0, - 0, 0,116, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,118,119,120,121, 0,122,123,124,125,126, 0,127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 32, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 2, 23, 24, 25, 2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 5, 38, 39, 40, 41, 42, 3, 43, 5, 44, 45, 46, 47, 48, + 49, 50, 51, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 52, 2, 2, + 53, 2, 2, 2, 54, 2, 55, 56, 57, 58, 59, 60, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 61, 4, 4, 4, 4, 4, 4, 4, 4, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 62, 63, 2, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 2, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 2, 2, 2, 98, 99,100, 0, 0, 0, 0, 0, 0, 0, 0, 0,101, + 2, 2, 2, 2,102, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2,103, 2, 2,104, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,105, 0, 0, 0, 0, 0, 0, 2, 2,106,107, 0,108,109,110, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,111,112, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,113, + 2,114,115, 0, 0, 0, 0, 0, 0, 0, 0, 0,116, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,117, 3,118,119, + 120,121,122,123,124,125,126,127, 3, 3,128, 0, 0, 0, 0,129, + 130,131,132, 0,133,134,135,136,137,138, 0, 0,139,140,141, 0, + 142,143,144,145, 3, 3,146,147,148, 3,149,150, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2,151, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2,152, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,153, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,154, 2, 2,155, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2, 2,156, 0, 0, 0, 0, 0, + 2, 2, 2,157, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2,158, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 159,160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,161, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, + 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, + 5, 5, 5, 5, 5, 5, 5, 5, 7, 7, 7, 7, 7, 7, 7, 7, + 11, 11, 11, 11, 11, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 10, + 13, 13, 13, 13, 13, 13, 13, 13, 16, 16, 16, 16, 16, 16, 16, 16, + 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 14, + 1, 1, 1, 1, 14, 1, 1, 1, 18, 18, 18, 18, 18, 18, 18, 18, + 1, 5, 0, 11, 11, 11, 11, 11, 1, 1, 1, 1, 1, 1, 14, 0, + 1, 1, 1, 1, 14, 0, 0, 0, 6, 6, 6, 6, 7, 7, 7, 7, + 1, 1, 1, 1, 1, 14, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 14, 0, 0, 0, 0, + 1, 1, 1, 14, 1, 1, 1, 14, 3, 3, 3, 3, 3, 3, 0, 0, + 28, 28, 28, 28, 28, 28, 28, 28, 11, 11, 11, 11, 11, 1, 1, 1, + 14, 17, 1, 1, 1, 1, 1, 1, 14, 0, 0, 0, 0, 0, 0, 0, + 14, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 0, 0, 0, + 3, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 29, 0, + 11, 11, 11, 11, 11, 11, 11, 11, 7, 7, 7, 7, 7, 6, 6, 6, + 0, 0, 0, 11, 11, 11, 11, 11, 17, 1, 1, 1, 1, 1, 1, 1, + 11, 11, 11, 11, 11, 10, 10, 10, 1, 1, 1, 0, 0, 0, 0, 0, + 5, 46, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 41, 41, 41, 41, + 3, 3, 0, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 0, 0, 12, + 1, 1, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, + 49, 10, 10, 10, 10, 10, 10, 10, 7, 6, 6, 6, 6, 6, 6, 6, + 44, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, + 3, 3, 3, 3, 0, 0, 0, 0, 11, 11, 11, 11, 11, 12, 9, 77, + 88, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,102,103,104, + 89, 6, 6, 6, 6, 6, 6, 6, 22, 22, 22, 22, 22, 22, 22, 22, + 26, 5, 5, 5, 5, 5, 5, 5, 1, 1, 1, 5, 5, 5, 5, 5, + 5, 20, 1, 1, 1, 1, 1, 1, 1, 17, 1, 1, 1, 1, 1, 1, + 11, 11, 11, 11, 11, 12, 12, 12, 1, 1, 1, 1, 14, 1, 1, 0, + 14, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 14, 1, 1, 1, 1, + 10, 10, 10, 10, 10, 10, 51, 0,115, 1, 1, 1, 1, 1, 1, 1, + 5, 5, 5, 5, 5, 5, 5, 0, 1, 1, 1, 1, 13, 13, 13, 12, + 6, 6, 6, 0, 7, 7, 7, 0, 30, 30, 30, 30, 30, 30, 30, 30, + 3, 3, 3, 3, 3, 29, 0, 0, 3, 3, 3, 3, 3, 3, 3, 29, + 10, 10, 10, 10, 10, 3, 3, 3, 75, 10, 10, 10, 10, 10, 10, 10, + 3, 3, 3, 29, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, + 7, 7, 7, 7, 7, 32, 7, 7, 0, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 51, 0, 0, 0, 1, 1, 1, 1, 1, 1, 86, 10, + 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 15, 19, + 1, 1, 1, 1, 1, 1, 1, 34, 1, 1, 1, 1, 1, 1, 1, 5, + 15, 15, 15, 15, 15, 15, 15, 15, 1, 14, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 23, 0, 0, 0, 0, 10, 10, 0, 0, 0, 0, 0, 0, + 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, + 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, + 3, 3, 3, 3, 29, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 87, 12,100, 12, 27,101, 76, 12, 6, 6, 6, 6, 6,105,106,135, + 87, 30, 30, 66,107,136,137,108, 50, 10, 89, 12,138,139, 10, 78, + 7, 7, 7, 59, 7, 7, 7, 8, 6, 6, 6, 60, 6, 6, 6, 6, + 8, 8, 8, 8, 21, 21, 21, 21, 21, 21, 21, 21, 6, 8, 8, 8, + 8, 8, 8, 8, 7, 21, 21, 6, 21, 8, 8, 7, 21, 7, 6, 7, + 7, 21, 8, 7, 8, 6, 7, 21, 8, 8, 8, 7, 21, 6, 8, 7, + 21, 7, 21, 21, 8, 67, 8, 6, 1, 1,109, 21,110,109, 21, 21, + 21, 21, 21, 21, 21, 21, 6, 8, 21,110, 8, 7, 8, 8, 8, 8, + 8, 8, 6, 6, 6, 7, 21, 8, 21, 21, 7, 8, 8, 8, 8, 8, + 6, 6, 1, 6, 6, 6, 6, 6, 13, 22, 22, 13, 13, 13, 13, 13, + 13, 22, 22, 22, 22, 22, 22, 22, 13, 13, 68, 22, 22, 22, 68, 68, + 8, 8, 68, 8, 0, 69, 6, 88, 0, 0, 22,140, 7, 32, 32, 7, + 21, 7, 7, 7, 7, 7, 7, 7, 7, 39, 7, 7, 7, 7, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 21, 6, 7, 8, 6, 8, 8, 8, 8, + 6, 6, 8, 90, 21, 8, 21, 7, 8, 43, 5, 5, 79, 8, 8, 8, + 7, 21, 21, 21, 21, 21, 21, 6, 39, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 32, 54, 12, 12, 12, 6, 6, 6, 6,111,141, 44, 70, + 5, 5, 5, 5, 5, 5, 5,142, 71, 36, 5, 71, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 14, 0, 17, 1, 40, 33, 0, 0, 0, 0, 0, + 18, 18, 18, 9, 77, 80, 12, 3, 5, 5, 5, 5, 5, 36,143, 12, + 52, 1, 1, 1, 1, 24, 5, 5, 11, 11, 11, 11, 11, 12, 12, 1, + 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 53, 5, 5, 5,144, 43, + 5, 5, 81, 72, 55, 5, 5, 1, 11, 11, 11, 11, 11, 1, 56, 82, + 12, 12, 12, 12, 12, 12, 12, 73, 24, 1, 1, 1, 1, 1, 1, 1, + 5, 5, 5, 5, 5, 23, 17, 1, 25, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 24, 5, 5, 5, 5, 13, 66, 12, 61, 26, 30, + 1, 1, 1, 5, 5, 72, 5, 5, 5, 5, 72, 5, 72, 5, 5, 0, + 12, 12, 12, 12, 12, 12, 12, 33, 1, 1, 1, 1, 24, 5, 0, 33, + 1, 1, 1, 1,145, 1, 1, 1, 18, 0, 0, 26, 5, 5, 5, 5, + 1, 1, 1, 1, 45, 5, 5, 5, 5, 91, 5, 5, 5, 5, 5, 5, + 1, 1, 1, 1, 1, 20, 25, 15, 19, 5, 5, 5, 20, 15, 19, 15, + 24, 5, 5, 5, 1, 1, 1, 1, 1, 5, 12, 11, 11, 11, 11, 11, + 62, 1, 1, 1, 1, 1, 1, 1, 24, 15, 17, 1, 1, 1, 14, 17, + 14, 14, 0, 1, 1, 0, 25, 15, 19, 5, 23, 35, 37, 35, 19, 14, + 0, 0, 0, 35, 0, 0, 1, 17, 1, 30, 10, 10, 10, 70, 40, 23, + 26, 20, 17, 1, 1, 14, 0, 17, 14, 1, 17, 14, 1, 0, 23, 15, + 19, 23, 0, 26, 23, 26, 5, 0, 26, 0, 0, 0, 17, 1, 14, 14, + 5, 1, 24, 33, 0, 0, 0, 0, 26, 20, 17, 1, 1, 1, 1, 17, + 14, 1, 17, 1, 1, 0, 25, 15, 19, 5, 5, 26, 20, 35, 19, 0, + 80, 0, 0, 0, 17, 5, 5, 5, 26, 15, 17, 1, 1, 1, 14, 17, + 14, 1, 17, 1, 1, 0, 25, 19, 19, 5, 23, 35, 37, 35, 19, 0, + 0, 0, 26, 20, 0, 0, 1, 17, 82, 10, 10, 10, 0, 0, 0, 0, + 0, 25, 17, 1, 1, 14, 0, 1, 14, 1, 1, 0, 17, 14, 14, 1, + 0, 17, 14, 0, 1, 14, 0, 1, 1, 1, 1, 1, 1, 0, 0, 15, + 20, 37, 0, 15, 37, 15, 19, 0, 14, 0, 0, 35, 0, 0, 0, 0, + 10, 63, 3, 3, 70, 29, 0, 0, 20, 15, 25, 1, 1, 1, 14, 1, + 1, 1, 1, 1, 1, 0, 25, 5, 20, 15, 37, 5, 23, 5, 5, 0, + 0, 0, 26, 23, 1, 14, 1, 0, 0, 0, 0, 38, 10, 10, 10, 63, + 24, 15, 53, 1, 1, 1, 14, 1, 1, 1, 17, 1, 1, 0, 25, 19, + 15, 15, 37, 20, 37, 15, 5, 0, 0, 0, 35, 37, 0, 0, 1, 14, + 17, 34, 0, 0, 0, 0, 0, 0, 5, 15, 1, 1, 1, 1, 14, 1, + 1, 1, 1, 1, 1, 24, 25, 15, 19, 5, 23, 15, 37, 15, 19, 56, + 0, 0, 1, 34, 10, 10, 10,112, 10, 10, 10, 10, 63, 1, 1, 1, + 26, 15, 17, 1, 1, 1, 1, 1, 1, 1, 1, 14, 0, 1, 1, 1, + 1, 17, 1, 1, 1, 1, 17, 0, 1, 1, 1, 14, 0, 23, 0, 35, + 15, 5, 23, 23, 15, 15, 15, 15, 0, 15, 33, 0, 0, 0, 0, 0, + 24, 1, 5, 5, 5, 23, 0,113, 1, 1, 1, 72, 5, 5, 5, 36, + 11, 11, 11, 11, 11, 12, 0, 0, 17, 14, 14, 1, 1, 14, 1, 1, + 1, 1, 17, 17, 1, 1, 1, 1, 24, 1, 5, 5, 5, 5, 25, 0, + 1, 1, 14, 61, 5, 5, 5, 23, 11, 11, 11, 11, 11, 0, 1, 1, + 56, 3, 12, 12, 12, 12, 12, 12, 12, 57, 57, 3, 5, 3, 3, 3, + 10, 10, 43, 43, 43, 27, 27, 15, 1, 1, 1, 1, 17, 1, 1, 1, + 26, 5, 5, 5, 5, 5, 5, 20, 5, 5, 36, 5, 1, 1, 24, 5, + 5, 5, 5, 5, 26, 5, 5, 5, 5, 5, 5, 5, 5, 5, 23, 3, + 3, 3, 3, 55, 3, 3, 29, 3, 12, 12, 57, 3, 66, 33, 0, 0, + 1, 1, 1, 1, 1, 34, 19, 5, 20, 5, 5, 5, 19, 20, 19, 25, + 1, 1, 1, 15, 5, 1, 1, 5, 25, 15, 46, 34, 15, 15, 15, 1, + 24, 5, 25, 1, 1, 1, 1, 1, 1, 20, 19, 20, 15, 15, 19, 34, + 11, 11, 11, 11, 11, 15, 19, 3, 7, 7, 7, 39, 0, 0, 39, 0, + 6, 6, 6, 6, 6,111, 69, 6, 1, 1, 1, 14, 14, 1, 1, 0, + 14, 1, 1, 0, 1, 1, 1, 14, 1, 1, 1, 1, 1, 14, 26, 5, + 12, 12, 12, 12,114, 10, 10, 10, 7, 7, 7, 0, 6, 6, 6, 0, + 1, 1, 1, 1, 1, 1, 56, 53,146, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1,147,116, 0, 1, 1, 1, 1, 1, 40, 12, 16, + 148, 1, 1, 1, 14, 0, 0, 0, 1, 5, 20, 0, 0, 0, 0, 17, + 1, 5, 83, 33, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 14, 1, 14, 5, 0, 0, 0, 0, 0, 0, + 1, 1, 5, 19, 5, 5, 5, 15, 15, 15, 15, 20, 19, 5, 5, 5, + 5, 5, 12, 62, 12, 80, 24, 0, 10, 10, 10, 10, 10, 0, 0, 0, + 12, 12, 12, 92, 12, 71, 5, 91, 1, 45, 1, 1, 1, 1, 1, 1, + 1, 1, 24, 25, 1, 1, 1, 1, 1, 1, 1, 1, 24, 14, 0, 0, + 5, 20, 15, 19, 20, 15, 0, 0, 15, 20, 15, 15, 19, 5, 0, 0, + 29, 0, 12, 11, 11, 11, 11, 11, 1, 1, 14, 0, 0, 0, 0, 0, + 11, 11, 11, 11, 11, 51, 0, 3, 1, 1, 1, 24, 20, 19, 0, 12, + 1, 1, 34, 20, 5, 5, 5, 23, 20, 20, 19, 5, 5, 5, 20, 15, + 15, 19, 5, 5, 5, 5, 23, 26, 12, 12, 12, 62, 12, 12, 12, 0, + 5, 5, 5, 5, 5, 5, 5, 93, 5, 5, 5, 5, 5, 5, 0, 0, + 5, 5, 46, 1, 1, 1, 1, 1, 1, 1, 20, 5, 5, 20, 20, 15, + 15, 20, 46, 1, 1, 1, 14, 12, 57, 3, 3, 3, 3, 43, 5, 5, + 5, 5, 3, 3, 3, 3, 66, 12, 34, 5, 5, 15, 5, 19, 5, 1, + 1, 1, 1, 20, 5, 15, 19, 19, 5, 15, 0, 0, 0, 0, 12, 12, + 1, 1, 15, 15, 15, 15, 5, 5, 5, 5, 15, 5, 0, 38, 12, 12, + 11, 11, 11, 11, 11, 0, 17, 1, 6, 6, 6, 6, 21, 47, 0, 0, + 7, 7, 7, 7, 7, 32, 39, 7, 12, 12, 12, 12, 0, 0, 0, 0, + 5, 36, 5, 5, 5, 5, 5, 5, 20, 5, 5, 5, 25, 1, 24, 1, + 1, 1, 25, 34, 5, 14, 0, 0, 6, 6, 6, 6, 6, 6, 13, 13, + 13, 13, 13, 13, 13, 69, 6, 6, 6, 6, 6, 6, 69, 6, 6, 6, + 6, 6, 6, 6, 6,117, 13, 13, 8, 8, 8, 6, 6, 6, 6, 8, + 6, 6, 6, 6, 39, 39, 39, 39, 6, 6, 6, 6, 6, 6, 6, 0, + 6, 6, 47, 6, 7, 7, 94,118, 22, 6, 47, 6, 7, 7, 94, 22, + 6, 6, 0, 6, 7, 7,149, 22, 6, 6, 6, 6, 7, 7,150, 22, + 0, 6, 47, 6, 7, 7, 94,151, 74, 74, 74, 74, 74,152, 18, 18, + 84, 84, 84, 12, 58,119, 58,119, 12, 12, 12, 12,153, 18, 18,154, + 12, 12, 12, 12,120,121, 12,122,123, 12, 64, 65, 12, 12, 12, 12, + 12, 77,123, 12, 12, 12, 12,155, 18, 18,156, 18, 18, 18, 18, 18, + 157, 0, 10, 10, 10, 9, 64,158, 10, 10, 10, 10, 10, 9, 64,116, + 13, 13, 13, 13, 13, 13, 61, 0, 30, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 5, 5,159, 79, 93, 79, 93, 5, 5, 5, 5, 5, + 23, 0, 0, 0, 0, 0, 0, 0, 3, 85, 3, 95, 3, 21, 7, 6, + 7, 8, 95, 3, 90, 7, 7, 3, 3, 3, 85, 85, 85, 7, 7,160, + 7, 7, 67, 1,124, 3, 6, 7, 9, 9, 90, 6, 6, 50, 3,125, + 16,161,162, 16,126, 3, 0, 0, 9, 9, 48, 3, 3, 9, 3, 3, + 48, 50, 3, 48, 3, 3, 3, 48, 3, 3, 3, 3, 3, 3, 3, 9, + 3, 48, 48, 3, 3, 3, 3, 3, 3, 3, 9, 9, 9, 9, 9, 9, + 3, 3, 3, 3, 27, 27, 3, 3, 9, 3, 3, 3,163,164, 3, 3, + 3, 3, 3, 3, 3, 3, 48, 3, 3, 3, 3, 3, 3, 50, 9, 9, + 9, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 9, 9, + 9, 3, 3, 3, 3, 3, 3, 3, 10, 10, 10, 10, 10, 10, 3, 3, + 3, 3, 3, 3, 3, 10, 10, 10, 3, 3, 3, 50, 3, 3, 3, 3, + 50, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 9, 9, 9, 9, + 3, 3, 3, 3, 3, 3, 3, 50, 3, 3, 3, 3, 27, 27, 27, 27, + 27, 27, 27, 10, 10, 10, 10, 10, 10, 10, 3, 3, 3, 3, 3, 3, + 9, 9, 64,127, 9, 9, 9, 9, 9, 9, 9, 27, 27, 27, 27, 27, + 9, 64, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,127, 9, 9, 9, + 9, 9, 9, 9, 27, 27, 9, 9, 9, 9, 9, 9, 9, 9, 27, 9, + 9, 9, 48, 50, 9, 9, 48, 3, 3, 3, 0, 3, 3, 3, 3, 3, + 8, 7, 8, 21, 21, 21, 21, 7, 8, 8, 21, 6, 6, 6, 13, 7, + 8, 8,125, 3, 3, 95, 21,165, 5, 8, 0, 0, 38, 12,114, 12, + 6, 6, 6, 42, 0, 0, 42, 0, 1, 1, 1, 1, 0, 0, 0, 54, + 33, 0, 0, 0, 0, 0, 0, 26, 12, 58, 58, 12,120,121, 58, 12, + 12, 12, 12, 76, 12, 92, 58, 12, 58, 27, 27, 27, 27, 12, 12, 62, + 12, 12, 12, 12, 12, 84, 12, 12, 92,166, 12, 12, 12, 12, 12, 12, + 3, 12, 96, 31, 31, 31,167, 0, 3, 3, 3, 3, 3, 44, 3, 3, + 87, 12,168,128, 27, 27, 27, 27, 27, 3, 27, 27, 27, 27,129,169, + 170, 16, 16, 16, 16, 5, 5, 15,130, 13, 13, 3, 16,171, 40, 3, + 1, 1, 1, 14, 26,172,131, 52, 1, 1, 1, 1, 1, 40, 13, 52, + 0, 0, 17, 1, 1, 1, 1, 1, 3, 10, 10, 3, 3, 3, 3, 3, + 3, 3, 3, 0, 0, 0, 0, 44, 3, 3, 3, 3, 10, 10, 10, 10, + 1, 1, 45, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,132, 12, + 11, 11, 11, 11, 11, 1, 0, 0, 8, 8, 8, 8, 8, 8, 8, 24, + 79,173, 5, 5, 5, 5, 5, 62, 8, 8, 8, 8, 8, 8, 13, 5, + 1, 1, 1, 16, 16, 16, 16, 16, 5, 12, 12, 12, 0, 0, 0, 0, + 22, 22, 22,131, 13, 13, 13, 13, 22, 8, 8, 8, 8, 8, 8, 8, + 6, 8, 8, 8, 8, 8, 8, 8, 69, 6, 6, 6, 21, 21, 21, 8, + 8, 8, 8, 8, 68,174, 21, 67, 8, 8, 6, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 7, 7, 8, 7, 7, 8, 8, 8, 8, 8, 8, + 8, 8, 7, 7, 21, 21, 8, 8, 8, 8, 8, 8, 8, 8, 32, 0, + 54, 13,175, 67, 13, 67, 1, 1, 1, 25, 1, 25, 1, 24, 1, 1, + 1, 34, 19, 20, 3, 3, 23, 0, 10, 10, 10, 3, 97, 0, 0, 0, + 1, 1, 12, 12, 0, 0, 0, 0, 15, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 15, 15, 15, 15, 15, 15, 15, 5, 0, 0, 0, 0, 12, + 5, 1, 1, 1, 12, 53, 53, 24, 1, 1, 1, 5, 5, 5, 5, 12, + 1, 1, 1, 24, 5, 5, 5, 5, 5, 15, 0, 0, 0, 0, 0, 38, + 1, 24, 15, 5, 5, 15, 5, 15, 83, 12, 12, 12, 12, 12, 12, 54, + 1, 1, 24, 52, 1, 1, 1, 1, 11, 11, 11, 11, 11, 1, 1, 14, + 1, 1, 1, 1, 24, 5, 5, 20, 19, 20, 19, 23, 0, 0, 0, 0, + 1, 24, 1, 1, 1, 1, 20, 0, 11, 11, 11, 11, 11, 0, 12, 12, + 52, 1, 1, 56, 3, 34, 20, 1, 25, 5, 25, 24, 25, 1, 1, 5, + 24, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 45, 12, + 1, 1, 1, 1, 1, 34, 5, 15, 12, 45,176, 23, 0, 0, 0, 0, + 17, 1, 1, 14, 17, 1, 1, 14, 17, 1, 1, 14, 0, 0, 0, 0, + 6, 6, 6, 6, 6,118, 13, 13, 6, 6, 6, 6,117, 22, 0, 0, + 1, 34, 19, 15, 20, 83, 19, 0, 1, 1, 1, 14, 0, 17, 1, 1, + 6, 6, 6, 47, 0, 0, 0, 0, 0, 42, 6, 6, 0, 0, 17, 25, + 1, 1, 1, 1,177, 1, 1, 1, 1, 1, 1, 14, 1, 1, 14, 14, + 1, 17, 14, 1, 1, 1, 1, 1, 1, 22, 22, 22, 22, 22, 22, 22, + 22,107, 3, 3, 3, 3, 3, 3, 3, 82, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 31, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 97, 3, + 12, 12, 12, 96, 65, 0, 0, 0, 76,178,179, 31, 31, 31, 31, 31, + 31, 31, 65, 96, 65, 12,122,180, 12, 33, 12, 12,129, 31, 31, 65, + 12,181, 9, 98, 80, 12, 0, 0, 1, 1, 14, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 14, 73, 38, 12,100, 12, 27,101, 76, 12, + 6, 6, 6, 6, 6,105,106, 64, 65, 27, 12, 1, 1, 1, 1, 1, + 52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 13, + 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 14, 0, + 30,182, 70,183, 50, 9, 48, 29, 0, 0, 0, 0, 73, 18, 3, 0, + 1, 1, 1, 1, 1, 1, 17, 1, 1, 1, 1, 1, 1, 14, 1, 17, + 12, 33, 0, 49, 10, 10, 10, 10, 10, 10, 0, 44, 3, 3, 3, 3, + 16, 16,126, 10, 63, 3, 3, 3, 3, 3, 3, 3, 3, 10, 3, 29, + 3, 3, 3, 3, 3, 3, 43, 0,133, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 0, 0, 10, 10, 0, 0, 0, 0, 17, 1, + 128, 1, 1, 1, 1, 99, 0, 0, 1, 1, 1, 5, 5, 23, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 38, 1, 1, 0, 0, 1, 1, 1, 1, + 184, 16, 16, 0, 0, 0, 0, 0, 7, 7, 7, 7, 6, 6, 6, 6, + 7, 7, 0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 38, 7, 32, 7, 42, 6, 6, 6, 6, + 6, 42, 6, 6, 6, 6, 6, 6, 6, 42, 6, 6, 6, 42, 47, 0, + 13, 13, 13, 54, 13, 13, 13, 13, 61, 13, 13, 13, 13, 61, 0, 0, + 1, 1, 1, 0, 14, 1, 1, 1, 1, 1, 1, 17, 14, 0, 14, 17, + 1, 1, 1, 38, 10, 10, 10, 10, 1, 1, 1, 56, 75, 10, 10, 10, + 0, 0, 0, 49, 10, 10, 10, 10, 1, 14, 1, 0, 0, 49, 10, 10, + 1, 1, 1, 10, 10, 10, 0, 38, 1, 1, 1, 1, 1, 0, 0, 38, + 1, 1, 1, 1, 0, 0, 10, 1, 24, 5, 26, 23, 0, 0, 5, 5, + 1, 1, 17, 1, 17, 1, 1, 1, 1, 1, 1, 0, 5, 23, 0, 26, + 12, 12, 12, 12, 33, 0, 0, 0, 1, 1, 1, 1, 1, 1, 86, 78, + 1, 1, 1, 1, 82, 1, 1, 1, 1, 1, 24, 23, 0, 49, 10, 10, + 12, 12, 12, 33, 0, 0, 0, 0, 1, 1, 1, 0, 38, 12, 12, 12, + 1, 1, 1, 0, 10, 10, 10, 10, 1, 14, 0, 0, 10, 10, 10, 10, + 1, 0, 0, 0, 38, 12, 33, 0, 0, 0, 0, 0, 49, 10, 10, 10, + 7, 32, 0, 0, 0, 0, 0, 0, 6, 47, 0, 0, 0, 10, 10, 10, + 1, 1, 5, 5, 0, 0, 0, 0, 11, 11, 11, 11, 11, 1, 1, 52, + 7, 7, 7, 0, 26, 5, 5,130, 6, 6, 6, 0, 0, 0, 0, 9, + 10, 10, 10, 10, 10, 10, 10, 51, 1, 1, 1, 1, 1, 26,185, 0, + 0, 1, 45, 1, 0, 0, 0, 0, 57, 3, 3, 3, 29, 0, 0, 0, + 0, 0, 0, 0, 0, 5, 5, 5, 10, 10, 10,112, 0, 0, 0, 0, + 133, 10, 78, 12, 12, 0, 0, 0, 1, 5, 5, 12, 12, 0, 0, 0, + 1, 1, 86, 10, 10, 10, 0, 0, 19, 46, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 5, 5, 5, 5, 5, 5, 5, 36, 12, 12, 12, 0, + 10, 10, 10, 11, 11, 11, 11, 11, 25, 24, 25, 0, 0, 0, 0, 26, + 15, 19, 5, 20, 19, 36,186, 12, 12, 23, 0, 0, 0, 0, 73, 0, + 5, 25, 1, 1, 1, 1, 1, 1, 1, 1, 1, 24, 5, 5, 19, 5, + 5, 5, 23, 11, 11, 11, 11, 11, 12, 12, 34, 46, 0, 0, 0, 0, + 1, 24, 12, 14, 0, 0, 0, 0, 1, 34, 15, 5, 5, 5, 5, 20, + 46, 1, 40, 12, 71, 5, 36, 19, 11, 11, 11, 11, 11, 40, 40, 12, + 10, 10, 51, 0, 0, 0, 0, 0, 5, 15, 20, 5, 12, 12, 12, 25, + 24, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 14, 14, 1, 1, 17, + 1, 1, 1, 1, 1, 1, 1, 17, 1, 1, 1, 1, 40, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 24, 15, 19, 5, 5, 5, 23, 0, 0, + 5, 15, 17, 1, 1, 1, 14, 17, 14, 1, 17, 1, 1, 26, 25, 15, + 20, 15, 37, 35, 37, 35, 15, 0, 14, 0, 0, 35, 0, 0, 17, 1, + 1, 15, 0, 5, 5, 5, 23, 0, 5, 5, 23, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 17, 0, 14, 1, 1, 1, 17, 15, 19, 5, 5, + 23, 37, 35, 35, 15, 37, 15, 20, 25, 25, 12, 38, 33, 0, 0, 0, + 26, 23, 0, 0, 0, 0, 0, 0, 1, 1, 34, 15, 5, 5, 5, 5, + 15, 5, 20, 25, 1, 40, 12, 12, 11, 11, 11, 11, 11, 12, 38, 25, + 15, 19, 5, 5, 20, 20, 15, 19, 20, 5, 1, 53, 0, 0, 0, 0, + 15, 5, 5, 0, 15, 15, 5, 19, 36, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 1, 1, 5, 0, 15, 19, 5, 5, 5, 20, 19, 19, + 36, 12, 14, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 33, 0, + 1, 1, 1, 1, 1, 24, 19, 15, 5, 5, 5, 19, 40, 0, 0, 0, + 11, 11, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 14, 26, 19, + 15, 5, 5, 19, 5, 5, 0, 0, 11, 11, 11, 11, 11, 10, 12, 57, + 5, 5, 5, 5, 19, 36, 0, 0, 10, 51, 0, 0, 0, 0, 0, 17, + 1, 1, 1, 14, 17, 0, 1, 1, 1, 1, 17, 14, 1, 1, 1, 1, + 15, 15, 15, 35, 37, 26, 20, 25, 46, 19, 12, 33, 0, 0, 0, 0, + 1, 1, 1, 1, 0, 1, 1, 1, 34, 15, 5, 5, 0, 5, 15, 15, + 25, 53, 37, 0, 0, 0, 0, 0, 24, 5, 5, 5, 5, 25, 1, 1, + 1, 24, 5, 5, 20, 24, 5, 36, 12, 12, 12, 71, 0, 0, 0, 0, + 24, 5, 5, 20, 19, 5, 1, 1, 1, 1, 1, 1, 1, 5, 5, 5, + 5, 5, 5, 20, 5, 12, 53, 12, 12, 33, 0, 0, 0, 0, 0, 0, + 12, 12, 12, 12, 12, 0, 0, 0, 20, 5, 20, 20, 0, 0, 0, 0, + 40, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 23, 5, 5, 5, 19, + 40, 12, 12, 0, 0, 0, 0, 0, 12, 1, 1, 1, 1, 1, 1, 1, + 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 35, 5, 5, 5, + 20, 5, 19, 23, 0, 0, 0, 0, 1, 1, 1, 14, 1, 17, 1, 1, + 24, 5, 5, 23, 0, 23, 5, 26, 5, 5, 5, 24, 0, 0, 0, 0, + 1, 1, 1, 17, 14, 1, 1, 1, 1, 1, 1, 1, 1, 15, 15, 37, + 5, 35, 19, 19, 14, 0, 0, 0, 1, 1, 1, 1, 45, 1, 0, 0, + 1, 24, 20, 83, 33, 0, 0, 0, 5, 34, 1, 1, 1, 1, 1, 1, + 1, 1, 15, 5, 5, 23, 0, 15, 20, 36, 12, 12, 12, 12, 12, 12, + 11, 11, 11, 11, 11, 23, 0, 0, 10, 10, 63, 3, 3, 3, 70, 30, + 97, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0, 38, + 16, 16, 16, 16, 16, 16, 16, 99, 12, 12, 33, 0, 0, 0, 0, 0, + 40, 33, 0, 0, 0, 0, 0, 0, 25, 1, 1, 24, 5, 5, 5, 5, + 5, 5, 5, 0, 0, 0, 0, 0, 5, 5, 5, 5, 5, 15, 19, 5, + 5, 5, 36, 0, 0, 0, 0, 0, 5, 5, 5, 36, 12, 12, 3, 3, + 13, 13, 57, 0, 0, 0, 0, 0, 11, 11, 11, 11, 11, 49, 10, 10, + 10, 17, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 17, 1, + 13, 52, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 45,132, 12, + 10, 10, 10, 78, 12, 33, 0, 0, 7, 7, 7, 7, 32, 42, 6, 6, + 6, 6, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 14, 0, 26, + 34, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 26, + 5, 81, 13, 13, 13, 13, 13, 13, 13, 62, 23, 0, 0, 0, 0, 0, + 15, 13, 16, 99, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, + 13, 13, 54, 13, 13, 13, 54, 61, 0, 14, 0, 0, 0, 0, 0, 0, + 1, 14, 17, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 0, 43, 36, 18, 18, 0, 0, 0, 0, 0, 0, + 11, 11, 11, 11, 11, 3, 29, 0, 3, 3, 0, 0, 0, 3, 3, 3, + 98, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 29, 44, 3, 3, 3, + 3, 3,134, 19, 5, 3,134, 15, 15,187, 18, 18, 18, 91, 5, 5, + 5, 55, 43, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, + 3, 5, 55, 0, 0, 0, 0, 0, 6, 6, 47, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 32, 7, 0, 32, 39, 32, 39, 7, 32, 7, + 7, 7, 7, 6, 6, 42, 42, 6, 6, 6, 42, 6, 6, 6, 6, 6, + 6, 6, 7, 39, 7, 32, 39, 7, 7, 7, 32, 7, 7, 7, 32, 6, + 6, 6, 6, 6, 7, 39, 7, 32, 7, 7, 32, 32, 0, 7, 7, 7, + 32, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 0, 7, 7, 7, 7, + 59, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 60, 6, 6, + 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 59, 6, 6, + 6, 6, 60, 6, 6, 6, 7, 7, 7, 7, 59, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 60, 6, 6, 6, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 59, 6, 6, 6, 6, 60, 6, 6, 6, + 7, 7, 7, 7, 59, 6, 6, 6, 6, 60, 6, 6, 6, 8, 0, 11, + 5, 5, 5, 55, 3, 43, 5, 5, 5, 5, 5, 5, 5, 5, 55, 3, + 3, 3, 43, 3, 3, 3, 3, 3, 3, 3, 55, 66, 12, 12, 0, 0, + 0, 0, 0, 0, 0, 26, 5, 5, 6, 6, 6, 6, 6,124, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 47, 0, 0, 42, 6, 6, 47, 0, 0, + 5, 5, 5, 23, 5, 5, 5, 5, 5, 5, 5, 5, 23, 26, 5, 5, + 5, 26, 23, 5, 5, 23, 0, 0, 13, 13, 13, 13, 13, 13, 13, 0, + 0, 0, 0, 0, 0, 0, 0, 26, 5, 5, 5, 81, 13, 13, 13, 0, + 11, 11, 11, 11, 11, 0, 0, 56, 1, 1, 1, 1, 1, 1, 1, 23, + 1, 1, 1, 1, 1, 1, 5, 5, 11, 11, 11, 11, 11, 0, 0,113, + 1, 1, 1, 1, 1, 45, 5, 5,188, 11, 11, 11, 11,189, 0, 38, + 1, 24, 1, 25, 1, 1, 1, 5, 1, 1, 24, 0, 0, 0, 0, 45, + 1, 1, 1, 14, 1, 1, 17, 14, 1, 1, 14, 49, 10, 10, 10, 10, + 6, 6, 5, 5, 5, 81, 0, 0, 10, 10, 10, 10, 10, 10, 75, 10, + 190, 10, 51, 0, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 75, + 10, 10, 10, 10, 10, 10, 10, 0, 1, 1, 17, 1, 1, 1, 1, 1, + 17, 14, 14, 17, 17, 1, 1, 1, 1, 14, 1, 1, 17, 17, 0, 0, + 0, 14, 0, 17, 17, 17, 17, 1, 17, 14, 14, 17, 17, 17, 17, 17, + 17, 14, 14, 17, 1, 14, 1, 1, 1, 14, 1, 1, 17, 1, 14, 14, + 1, 1, 1, 1, 1, 17, 1, 1, 17, 1, 17, 1, 1, 17, 1, 1, + 9, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 63, 3, + 0, 0, 0, 3, 3, 3, 3, 3, 3, 29, 0, 0, 0, 0, 0, 0, + 3, 3, 3, 3, 3,108, 22, 22, 3, 3, 3, 3, 29, 0, 3, 3, + 9, 9, 9, 9, 98, 0, 0, 0, 3, 3, 3, 3, 3, 29, 0, 3, + 3, 3, 3, 29, 29, 0, 44, 3, 3, 3, 3, 3, 3, 3, 29, 44, + 3, 3, 3, 3, 3, 29, 0, 44, 3, 29, 3, 3, 3, 3, 3, 3, + 11, 11, 11, 11, 11, 29, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 7, 7, 3, 3, 26, 26, + 4, 4, 12, 12, 5, 5, 9, 9, 9, 5, 25, 25, 15, 15, 13, 13, + 21, 21, 6, 6, 7, 2, 10, 10, 14, 14, 2, 7, 1, 1, 10, 12, + 12, 10, 5, 9, 24, 24, 12, 2, 7, 12, 12, 7, 2, 12, 22, 18, + 0, 0, 26, 2, 23, 23, 18, 22, 9, 2, 21, 2, 7, 10, 2, 10, + 12, 21, 10, 2, 2, 21, 2, 9, 7, 21, 8, 8, 2, 5, 26, 12, + 2, 26, 7, 6, 10, 7, 5, 2, 25, 26, 2, 15, 26, 25, 15, 2, + 6, 7, 21, 7, 2, 6, 12, 26, 7, 26, 21, 26, 20, 19, 9, 25, + 5, 25, 6, 2, 21, 6, 15, 26, 25, 22, 18, 21, 26, 21, 5, 7, + 6, 24, 6, 5, 26, 23, 21, 12, 6, 12, 2, 1, 29, 29, 26, 15, + 21, 17, 25, 21, 15, 21, 11, 11, 21, 23, 12, 6, 26, 7, 10, 21, + 17, 17, 9, 26, 7, 15, 29, 21, 21, 9, 24, 5, 25, 9, 1, 12, + 17, 21, 11, 12, 8, 24, 26, 9, 21, 22, 23, 26, 25, 2, 14, 2, + 23, 21, 21, 25, 9, 22, 21, 18, 24, 16, 5, 22, 25, 18, 24, 26, + 26, 24, 9, 8, 8, 5, 5, 21, 15, 7, 2, 23, 21, 15, 17, 7, + 18, 2, 5, 6, 5, 24, 22, 20, 21, 20, 19, 21, 21, 16, 16, 21, + 7, 5, 5, 26, 14, 15, 18, 25, 7, 14, 17, 22, 17, 6, 24, 6, + 6, 21, 12, 15, 26, 10, 25, 0, 7, 20, 25, 1, 24, 15, 7, 19, + 9, 21, 17, 2, 17, 12, 1, 21, 12, 1, 24, 7, 29, 7, 7, 22, + 14, 7, 2, 24, 9, 24, 24, 2, 29, 1, 27, 28, 1, 29, 21, 29, + 1, 2, 15, 6, 18, 6, 12, 11, 26, 5, 14, 9, 5, 14, 26, 22, + 18, 26, 5, 12, 22, 21, 18, 17, 26, 6, 18, 18, 26, 14, 14, 6, + 12, 24, 11, 21, 24, 9, 6, 9, 6, 10, 7, 25, 17, 16, 16, 22, + 16, 16, 25, 17, 25, 24, 23, 2, 21, 14, 12, 17, 21, 1, 10, 1, + 7, 13, 13, 2, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, + 0, 0, 0, 0, 0, 0, 41, 42, 43, 44, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 9, 0, 0, + 0, 10, 11, 12, 13, 0, 14, 15, 16, 0, 17, 18, 19, 20, 21, 1, + 22, 1, 23, 1, 2, 1, 2, 1, 2, 0, 2, 1, 24, 1, 2, 25, + 2, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 36, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 38, + 0, 0, 39, 0, 0, 40, 0, 41, 0, 0, 0, 42, 43, 44, 45, 46, + 47, 48, 49, 50, 0, 0, 51, 0, 0, 0, 52, 0, 0, 0, 53, 0, + 0, 0, 0, 0, 0, 0, 54, 0, 3, 0, 55, 56, 0, 57, 0, 0, + 0, 0, 0, 0, 58, 59, 60, 0, 0, 0, 0, 61, 0, 0, 62, 63, + 64, 4, 65, 0, 0, 66, 67, 0, 0, 0, 68, 0, 0, 0, 0, 69, + 0, 0, 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 71, 0, 0, 0, 72, 0, 73, 0, 0, 74, 0, 0, 75, 0, + 0, 0, 0, 0, 0, 0, 0, 76, 77, 0, 0, 0, 0, 78, 79, 0, + 80, 81, 0, 0, 82, 5, 0, 83, 4, 0, 84, 85, 0, 0, 86, 87, + 88, 0, 89, 0, 90, 0, 91, 0, 0, 3, 92, 3, 0, 93, 0, 94, + 0, 0, 0, 5, 0, 0, 0, 95, 96, 0, 97, 98, 99,100, 0, 0, + 0, 0, 0, 3, 0, 0, 0, 0,101,102, 0, 0, 0, 0, 0, 0, + 103, 0, 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105, 6, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, 0, + 0, 0, 0,111, 0,112, 0, 6, 0, 0, 0, 0, 0,113,114, 0, + 0, 0, 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, 0, + 0, 0, 0, 0, 0, 0,118, 0,119, 0, 0, 0, 0, 0, 0, 0, + 2, 1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 11, 0, 0, 0, + 14, 0, 0, 0, 16, 3, 17, 18, 19, 20, 21, 22, 0, 23, 0, 0, + 0, 0, 24, 25, 26, 27, 0, 0, 0, 0, 28, 0, 0, 29, 30, 0, + 31, 0, 32, 33, 0, 0, 34, 0, 35, 36, 0, 0, 0, 0, 0, 37, + 38, 0, 39, 40, 0, 0, 41, 0, 0, 0, 42, 0, 0, 43, 44, 45, + 46, 1, 47, 0, 0, 1, 0, 0, 7, 1, 48, 0, 0, 0, 0, 0, + 49, 50, 0, 0, 0, 0, 0, 0, 51, 52, 0, 0, 0, 0, 0, 0, + 53, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 57, 58, 0, 0, + 0, 59, 0, 0, 0, 0, 0, 0, 60, 12, 0, 0, 0, 0, 61, 0, + 0, 0, 62, 0, 5, 0, 63, 0, 0, 0, 0, 64, 0, 0, 0, 0, + 65, 0, 66, 0, 0, 0, 0, 6, 67, 0, 0, 0, 68, 69, 70, 71, + 0, 0, 0, 0, 72, 5, 0, 73, 74, 0, 0, 75, 0, 0, 0, 76, + 77, 0, 0, 0, 78, 0, 79, 80, 81, 82, 83, 3, 84, 0, 85, 86, + 87, 0, 0, 8, 9, 0, 0, 3, 3, 0, 0, 88, 0, 0, 89, 0, + 0, 0, 0, 8, 90, 0, 91, 0, 0, 0, 0, 0, 9, 10, 0, 92, + 0, 5, 0, 3, 9, 0, 0, 93, 0, 0, 94, 0, 0, 6, 0, 0, + 0, 0, 0, 0, 95, 96, 0, 0, 10, 0, 0, 1, 0, 0, 97, 0, + 0, 0, 0, 98, 0, 0, 0, 0, 12, 0, 0, 13, 0, 0, 0, 0, + 99,100, 0, 0,101, 0, 0,102, 0, 0, 0,103, 0, 0, 0,104, + 0, 0, 0,105, 0, 0, 0, 0,106,107, 13, 0, 0,108, 0, 0, + 0, 10, 0, 0,109,110, 0, 0,111,112, 0, 0, 0, 0, 0, 0, + 113, 0, 0,114, 0, 0, 0, 0,115, 1, 0,116,117,118, 6, 0, + 0,119, 7, 0, 0,120, 0, 0, 0,121, 0, 0, 0, 0, 0, 0, + 122, 0, 0,123, 0, 0, 0, 0,124, 11, 0, 0, 0, 0, 0, 6, + 0, 0, 0, 0, 5, 15, 0, 0, 0, 0,125, 0, 0,126, 0, 0, + 0, 0, 15, 0, 0,127, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, + 128, 0, 0, 0,129, 0,130, 0, 0, 0, 0,131,132,133, 0,134, + 0,135, 0, 0, 0,136,137,138, 0, 8, 0, 0, 0, 0, 0, 7, + 0, 0, 0,139, 0, 0, 0,140, 0, 0, 0,141, 0, 0, 0,142, + 143, 0,144, 0, 0,145, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, + 0, 9, 1, 1, 1, 1, 0, 0, 0, 2, 0, 3, 0, 0, 3, 0, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 6, 11, 0, 0, 0, 0, 14, + 0, 0, 15, 0, 0, 0, 0, 0, 0, 17, 29, 0, 0, 0, 0, 0, + 31, 0, 0, 2, 0, 0, 1, 12, 0, 0, 1, 49, 50, 5, 51, 52, + 53, 5, 5, 22, 32, 23, 1, 54, 24, 55, 16, 33, 56, 57, 58, 1, + 1, 1, 6, 1, 0, 0, 59, 7, 60, 1, 34, 5, 7, 61, 62, 63, + 64, 65, 66, 67, 0, 0, 1, 1, 68, 0, 0, 0, 69, 70, 71, 35, + 1, 25, 72, 0, 0, 0, 0, 8, 1, 26, 16, 26, 73, 27, 74, 0, + 0, 0, 36, 25, 75, 37, 7, 37, 76, 0, 0, 0, 6, 1, 7, 0, + 0, 17, 0, 8, 38, 1, 1, 13, 13, 11, 0, 0, 39, 0, 0, 6, + 18, 1, 0, 0, 8, 16, 5, 1, 1, 1, 77, 7, 36, 18, 78, 7, + 35, 1, 79, 10, 0, 0, 0, 80, 0, 0, 0, 0, 2, 3, 0, 0, + 14, 0, 0, 0, 81, 0, 0, 0, 82, 0, 0, 0, 83, 0, 0, 0, + 84, 0, 0, 0, 40, 0, 0, 85, 86, 0, 87, 88, 89, 90, 91, 92, + 0, 0, 0, 93, 0, 0, 0, 15, 28, 0, 0, 0, 0, 13, 0, 41, + 0, 0, 14, 0, 0, 20, 0, 0, 94, 0, 0, 0, 95, 0, 0, 6, + 29, 0, 0, 13, 1, 96, 1, 18, 33, 97, 25, 23, 7, 1, 1, 1, + 1, 27, 1, 7, 98, 0, 0, 9, 0, 0, 0, 0, 6, 23, 1, 0, + 0, 0, 0, 0, 30, 0, 0, 21, 0, 0, 30, 0, 0, 0, 0, 15, + 0, 0, 12, 32, 24, 5, 99, 22, 42, 17, 0, 10, 11, 0, 7, 1, + 7,100,101, 1, 1, 1, 1,102,103,104,105, 1,106, 10, 20,107, + 108, 5, 10, 0, 0, 0, 0, 0,109,110, 0, 0,111, 0, 0, 1, + 1, 11, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 43, 40, 2, 0, + 0, 0, 44,112, 10, 8, 20, 0, 0, 0, 0, 0, 0,113, 1, 16, + 5, 24, 0, 8, 12, 0, 0, 0, 0,114, 0, 0,115, 2, 0,116, + 0, 0, 0, 1, 0, 0, 0, 0, 13, 11, 0, 0, 6, 10, 0, 0, + 45, 39, 0, 45, 16, 18, 46, 27, 0, 0, 3, 0, 0, 2, 12, 0, + 0, 0, 2, 3, 0, 0, 3, 0, 21, 0, 0, 31, 0, 0, 0, 0, + 117, 0, 0, 0, 15, 9, 0, 8, 1, 10, 1, 10, 0, 0, 0, 0, + 0, 30, 14, 21, 0, 0, 47, 0, 0, 0, 9, 0, 0, 0, 0, 47, + 0, 0, 0, 0, 2, 0, 0, 0, 0, 28, 0, 0, 4, 0, 21, 41, + 0, 0, 28, 0, 0, 0, 22, 42, 0, 0,118, 0, 0, 0, 0, 0, + 0,119, 0,120,121,122,123, 0, 43, 5, 48, 13, 34, 0, 0, 0, + 8, 11, 8, 10, 0, 0, 1, 12, 1, 1, 1, 1, 26, 1, 38, 44, + 12, 0, 0, 0, 0, 1, 0, 0, 0,124, 0, 0, 0, 46, 6, 19, + 0, 8, 0, 20, 0, 0, 5, 48, 0, 0, 0, 1,125, 0, 0, 0, + 0,230,230,230,230, 0, 0, 0, 9, 9, 0, 0, 0, 0, 9, 0, + 0,220,220,220,220, 0, 0, 0,230,230,230,220,230, 0, 0,230, + 230, 7, 0, 0, 0,230, 0, 0, 0,230,230, 0, 0,230,230,230, + 0, 0,230,230,230, 0, 0, 9, 0, 0, 0, 0, 7,230,230,230, + 220, 0,220, 0, 0,230,220,220,220, 0, 0,230, 0, 0,230, 0, + 0, 0, 0, 7, 0, 1, 1, 1, 1,220,230,230,230,220,220,230, + 230,220,230,230,220,230, 0, 0,230,230,220, 0, 0, 0, 9, 9, + 0,220, 0, 0, 0, 0, 0, 9, 9, 0, 9, 7, 0, 1,220,220, + 220,220,220,220,230,230,230,220,220,230,220,220,230,230,220,230, + 230,220,230,220,230,230,230, 0,230, 0,220,220,220,220,220, 0, + 0, 9, 9, 0, 0, 1, 0, 0, 0, 0, 0, 0,220,230, 0,230, + 230, 0, 0,220,220, 0, 0,230,220, 0, 0, 9, 7,220,220,220, + 0,230,232,220,220,220,220,232,216,220,202,202,220,220,220,220, + 202,202,220,220,220,230,240,230,220,230,220,220, 0,232,220,220, + 230,233,234,234,233,234,234,233,230, 0,220,230,230,230,230,222, + 220,230,222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, + 20, 21, 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, + 0, 0, 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230, 35, 0, 0, + 0,230, 0,220,230, 0, 36, 0, 0,220,220,230,220,220,230,230, + 0,230,230, 0,220, 27, 28, 29,230, 0,230,220,230, 0, 84, 91, + 0,103,103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122, + 122, 0,220, 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, 0, + 0, 0, 0,130,130,130,130, 0, 0,130, 0,230,230, 9, 0,230, + 230, 0, 0,220, 0, 0,228, 0, 0, 0,222,230,220,230, 0, 0, + 220,230,220, 0,220,230,230,230,234,230, 0, 1, 1,230,234,214, + 220,202,230,230,230,230,230,232,228,228,220,218,230,233,220,230, + 220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, 1, + 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0,220, 0, 0, + 230, 0, 0, 26, 0, 0,220, 0,230,230, 1,220, 0, 0,230,220, + 0, 0, 7, 9, 0, 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, + 1, 1, 1, 0, 0, 0,226,216,216,216,216,216, 0,232,232,220, + 230,230,230, 7, 0, 1, 0, 0, 32, 0, 48, 0, 0, 84, 96,135, + 144, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,176, 0, 0, 0, 0, 0, 0, 0, 0, 8, 16, 24, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 40, + 0, 0, 0, 0, 0, 48, 56, 64, 0, 0, 0, 0, 0, 72, 80, 88, + 96,104, 0, 0, 0, 0, 0, 0, 0, 0,112, 0,120, 0, 0, 0, + 0, 0, 0,128,136,144,152,160,168, 0, 0, 0,176,184,192, 0, + 0, 0, 0, 0, 0,200, 0, 0, 0, 0, 0, 0, 0, 0,208, 0, + 0,216,224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16, 0, 36, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 0, 0, + 64, 0, 68, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 72, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 20, 0, 0, 0, 0, + 0, 0, 20, 0, 20, 0, 0, 0, 0, 0, 0, 0, 76, 80, 84, 88, + 0, 0, 12, 92, 96, 28, 0,100,104, 4, 4, 4,108, 44,112,116, + 120, 4,124, 0,128,132, 4, 4, 48,136,140, 0, 32, 0, 0, 0, + 40, 0, 0, 0, 0, 0, 0, 0, 4, 48, 0, 52,144, 20,148,152, + 4, 0, 0,156,160,164,168,172, 4, 0,176, 56, 60,180, 32, 0, + 16,184, 56, 0, 0, 0, 0, 0, 52, 12, 12, 0, 0, 0, 0, 12, + 0, 0,188, 8, 8, 8, 8,192,196, 8, 8, 8, 8,200,204,208, + 212, 60, 44, 0, 0, 0, 0, 0, 0, 0,216, 28,220, 0, 12, 4, + 16, 0, 0, 0, 0,224,228, 0, 0, 0, 0, 0, 4,232, 32, 0, + 0, 0, 0, 0, 0, 0,236, 12, 0, 0, 0, 0, 16, 0, 36, 0, + 0, 0, 24, 0, 0, 0,240,244, 0, 0, 0, 0, 2, 2, 2, 2, + 4, 4, 4, 4, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 6, 8, + 0, 10, 12, 0, 0, 2, 2, 0, 2, 2, 0, 0, 0, 0, 14, 16, + 6, 8, 0, 0, 4, 8, 0, 0, 2, 2, 2, 0, 0, 6, 4, 8, + 2, 0, 2, 0, 0, 0, 0, 6, 0, 20, 0, 0, 0, 22, 0, 0, + 0, 6, 8, 0, 24, 26, 28, 0, 0, 0, 30, 0, 0, 0, 0, 32, + 34, 36, 38, 0, 0, 40, 42, 0, 0, 0, 18, 0, 0, 0, 2, 2, + 2, 2, 0, 2, 2, 2, 0, 6, 44, 0, 0, 0, 0, 2, 0, 46, + 48, 50, 0, 0, 52, 0, 0, 0, 6, 4, 54, 0, 2, 0, 0, 2, + 2, 56, 58, 60, 0, 62, 64, 18, 2, 10, 12, 0, 0, 0, 66, 8, + 0, 2, 2, 2, 0, 6, 4, 4, 4, 4, 68, 2, 70, 4, 4, 4, + 8, 72, 0, 0, 74, 76, 2, 0, 78, 0, 0, 0, 4, 8, 2, 0, + 0, 0, 80, 0, 6, 4, 4, 4, 4, 8, 0, 2, 2, 2, 2, 6, + 4, 4, 4, 8, 0, 0, 0, 82, 0, 84, 86, 0, 0, 0, 2, 88, + 0, 0, 0, 90, 6, 8, 2, 0, 0, 0, 6, 4, 4, 4, 8, 0, + 2, 0, 2, 2, 6, 4, 4, 8, 0, 10, 12, 6, 8, 2, 0, 0, + 4, 5, 6, 7, 8, 9, 10, 1, 1, 11, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 12, 13, 2, 2, 2, 2, 14, 0, 0, 0, 0, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 0, 0, 0, 25, 26, 3, 3, + 3, 27, 0, 0, 0, 28, 29, 30, 0, 31, 32, 33, 34, 35, 36, 37, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 38, 1, 1, 39, 1, 40, 1, 1, 1, 41, 0, 42, + 1, 1, 43, 1, 1, 1, 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 20, 21, 8, 8, 8, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 2, 2, 51, 52, 53, 54, 9, 9, 9, 9, 9, 55, 56, + 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 8, 8, 70, + 71, 72, 73, 74, 75, 5, 5, 5, 5, 76, 5, 5, 5, 5, 5, 5, + 5, 15, 15, 5, 5, 5, 5, 77, 5, 78, 79, 80, 81, 82, 83, 1, + 84, 85, 86, 87, 88, 89, 90, 91, 5, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 92, 1, 1, 1, + 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 93, 16, 16, 94, 95, 96, + 97, 98, 99,100,101,102,103,104,105, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 106, 0, 0, 1, 1,107,108,109, 13, 13, 13,110,111,112,113,114, + 115,116,117,118,119, 0,120,121,122,123,124,125,126, 17, 17,127, 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, - 144,145,146,147,148,149,150,151,152,153,154,155,156,157, 0, 0, - 0,158,159,160,161, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 144,145,146,147,148,149,150,151,152,153,154, 0,155,156,157,158, + 0,159,160,161,162,163,164,165,166,167,168,169,170, 0,171,172, + 173, 7, 7, 7, 7, 7, 7, 7,174,175, 7,176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,162, 0,163, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,164,165, 0, 0, 0, 0, 0, - 0, 0,166, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 177, 4, 4, 4, 4, 4, 4, 4, 4,178, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4,179, 11, 11, 11, 11,180, 0, 0, + 0, 0, 0,181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 12, 12, 12, 12,182,183,184,185, 0, 0,186, 0,187,188,189, + 190, 3, 3, 3, 3, 3, 3, 14, 14, 14,191,192,193, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,167, 0, 0, 0,168,169, 0, 0,170, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,171, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,172, 0, 0, 0, 0, + 194,195,196,197, 18, 18,198, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,199,200, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 5,201, 5, 5, 5,202,203, + 204, 5,205,206,207,208,209,210, 0,211,212,213, 5, 5,214, 5, + 215, 10, 10, 10, 10, 10,216, 0, 0, 0, 0, 0, 0, 0, 0,217, + 0,218,219,220, 0, 0,221, 0, 0, 0,222, 0,223, 0,224, 0, + 225,226,227,228, 0, 0, 0, 0, 0,229,230,231, 0,232,233, 0, + 0,234,235, 5,236,237, 0, 5, 5, 5, 5, 5, 5, 5,238, 5, + 239,240,241, 5, 5,242,243, 5,244, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,245, 1, + 1,246, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,247, 1, + 1, 1, 1, 1, 1, 1, 1, 1,248, 1, 1, 1, 1,249, 0, 0, + 0, 1, 1, 1, 1,250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1,251, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1,252, 0, 0, 0, 0, 0, 0, + 0,253, 0, 19, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 2, 2, 13, 13, 13, 13, 12, 12, 12, 12, 0, 0, 0, + 0,136,136,136,136, 80, 80, 80, 80, 19, 19, 19, 19, 3, 3, 3, + 3, 63, 63, 63, 63, 39, 39, 39, 39, 28, 28, 28, 28,131,131,131, + 131, 79, 79, 79, 79, 1, 1, 1, 1,127,127,127,127, 6, 6, 6, + 6,155,155,155,155, 9, 9, 9, 9, 30, 30, 30, 30,139,139,139, + 139, 15, 15, 15, 15,110,110,110,110, 77, 77, 77, 77, 17, 17, 17, + 17, 27, 27, 27, 27, 33, 33, 33, 33,113,113,113,113, 49, 49, 49, + 49, 25, 25, 25, 25, 29, 29, 29, 29, 7, 7, 7, 7, 8, 8, 8, + 8, 32, 32, 32, 32, 99, 99, 99, 99, 31, 31, 31, 31,105,105,105, + 105, 55, 55, 55, 55, 62, 62, 62, 62, 56, 56, 56, 56, 91, 91, 91, + 91,119,119,119,119, 14, 14, 14, 14, 94, 94, 94, 94, 20, 20, 20, + 20,100,100,100,100,130,130,130,130,157,157,157,157,137,137,137, + 137, 97, 97, 97, 97,123,123,123,123,133,133,133,133,146,146,146, + 146, 84, 84, 84, 84,159,159,159,159,132,132,132,132, 4, 4, 4, + 4, 36, 36, 36, 36, 24, 24, 24, 24, 30, 30, 30, 2, 35, 35, 35, + 35, 40, 40, 40, 40,124,124,124,124,125,125,125,125,140,140,140, + 140, 37, 37, 37, 37, 58, 58, 58, 58, 0, 0, 2, 2, 75, 75, 75, + 75, 68, 68, 68, 68,114,114,114,114,163,163,163,163, 22, 22, 22, + 22, 23, 23, 23, 23, 76, 76, 76, 76, 0, 0, 0, 2, 26, 26, 26, + 26, 87, 87, 87, 87,135,135,135,135, 89, 89, 89, 89,141,141,141, + 141, 70, 70, 70, 70, 92, 92, 92, 92, 96, 96, 96, 96, 5, 5, 5, + 5, 18, 18, 18, 18, 48, 48, 48, 48, 85, 85, 85, 85,109,109,109, + 109,102,102,102,102,151,151,151,151, 16, 16, 16, 16, 0, 2, 2, + 2,165,165,165,165,108,108,108,108,171,171,171,171,142,142,142, + 142,150,150,150,150,134,134,134,134,138,138,138,138, 64, 64, 64, + 64, 90, 90, 90, 90, 21, 21, 21, 21, 93, 93, 93, 93, 61, 61, 61, + 61, 65, 65, 65, 65, 78, 78, 78, 78,126,126,126,126,154,154,154, + 154,121,121,121,121,166,166,166,166,167,167,167,167,152,152,152, + 152, 10, 10, 10, 10, 2, 0, 0, 0,104,104,104,104,170,170,170, + 170, 57, 57, 57, 57,107,107,107,107,175,175,175,175, 38, 38, 38, + 38, 73, 73, 73, 73, 86, 86, 86, 86, 67, 67, 67, 67, 59, 59, 59, + 59, 51, 51, 51, 51,161,161,161,161,144,144,144,144,143,143,143, + 143,172,172,172,172,174,174,174,174, 60, 60, 60, 60, 69, 69, 69, + 69, 47, 47, 47, 47,112,112,112,112,118,118,118,118,106,106,106, + 106,156,156,156,156,147,147,147,147,148,148,148,148,169,169,169, + 169,164,164,164,164,168,168,168,168, 11, 11, 11, 11, 74, 74, 74, + 74, 42, 42, 42, 42, 50, 50, 50, 50,116,116,116,116,111,111,111, + 111,115,115,115,115, 52, 52, 52, 52, 13, 13, 2, 2,120,120,120, + 120, 98, 98, 98, 98, 88, 88, 88, 88,117,117,117,117,101,101,101, + 101,103,103,103,103, 19, 19, 19, 0, 95, 95, 95, 95, 30, 30, 2, + 2, 34, 34, 34, 34, 54, 54, 54, 54, 0, 2, 0, 0, 71, 71, 71, + 71, 53, 53, 53, 53, 81, 81, 81, 81, 66, 66, 66, 66, 83, 83, 83, + 83,153,153,153,153,160,160,160,160, 2, 2, 22, 22, 30, 2, 30, + 30, 9, 9, 2, 2, 2, 2, 0, 0, 41, 41, 41, 41, 72, 72, 72, + 72,173,173,173,173, 82, 82, 82, 82,158,158,158,158,129,129,129, + 129,145,145,145,145, 2, 3, 3, 3, 23, 2, 23, 23, 16, 2, 16, + 16, 2, 2, 2, 0, 45, 45, 45, 45, 44, 44, 44, 44, 43, 43, 43, + 43,128,128,128,128,122,122,122,122,149,149,149,149, 0, 19, 19, + 19, 0, 0, 19, 0, 9, 2, 9, 9, 5, 2, 2, 5, 2, 10, 10, + 10, 10, 10, 2, 10, 2, 21, 21, 21, 21, 2, 2, 21, 22, 22, 22, + 2, 22, 22, 2, 2, 20, 2, 20, 20, 2, 30, 30, 30, 12, 12, 12, + 2, 19, 19, 19, 2, 2, 2, 12, 12,161,161, 2,161, 3, 3, 3, + 2,107, 2, 2,107, 2, 3, 2, 3, 19, 0, 0, 0, 2, 2, 9, + 9, 9, 9, 9, 2, 2, 4, 4, 4, 2, 11, 11, 11, 11, 11, 11, + 2, 11, 2, 11, 11, 10, 10, 2, 2, 22, 2, 22, 22, 2, 2, 2, + 22, 23, 23, 2, 2, 2, 36, 36, 36, 2, 2, 36, 36, 2, 25, 25, + 25, 46, 46, 46, 46, 31, 31, 2, 2, 91, 91, 2, 2, 1, 0, 0, + 0, 1, 1, 0, 0, 0, 17, 17, 17, 49, 49, 49, 2,161,161,161, + 2, 2,107,107,107,107, 2, 2, 2,171,171, 2,171, 0, 0, 2, + 0, 2, 3, 3, 2, 3, 2, 2, 3, 4, 4, 4, 2, 2, 14, 14, + 14, 14, 14, 14, 2, 3, 0, 3, 3, 0, 3, 3, 3, 3, 3, 3, + 0, 37, 37, 37, 2, 3, 3, 2, 2, 2, 2, 2, 3, 2, 2, 5, + 5, 5, 5, 5, 2, 2, 2, 2, 11, 11, 2, 2, 11, 11, 11, 2, + 2, 10, 2, 10, 10, 21, 2, 21, 21, 21, 21, 2, 2, 22, 2, 2, + 2, 2, 16, 16, 16, 16, 16, 2, 2, 36, 36, 36, 2, 36, 2, 2, + 2, 18, 18, 18, 2, 18, 18, 2, 2, 25, 2, 25, 25, 8, 8, 2, + 8, 2, 8, 2, 2, 29, 29, 2, 2, 46, 2, 46, 46, 32, 2, 2, + 2, 1, 1, 2, 2, 1, 1, 1, 0, 19, 19, 9, 9, 2, 9, 2, + 9, 0, 0, 0, 19, 19, 2, 2, 2, 0, 0, 19, 19, 2, 2, 2, + 61, 15, 15, 15, 2, 17, 17, 17, 0, 2, 19, 19, 19, 75, 75, 2, + 2, 12, 2, 2, 2, 68, 68, 2, 2, 87, 87, 2, 2, 49, 49, 2, + 2, 50, 50, 2, 2, 2, 57, 57, 57, 57, 2, 2, 2,112,112,112, + 2, 2,122,122,122,130,130,130, 2,165,165, 2, 2,156,156, 2, + 2, 2, 2, 3, 3, 94, 94, 2, 2,129,129, 2,129,107, 2,107, + 107,123,123, 2, 2,102,102, 2, 2,126,126,126, 2,154,154,154, + 2, 2, 2,150,150,169,169, 2, 2,138,138,138, 2,138,138, 2, + 138,143,143, 2,143,163,163,163, 2,103,103, 2, 2,119,119, 2, + 119, 2, 2, 2, 99, 13, 13, 13, 2,136,136,136, 2,105, 2, 2, + 2, 0, 0, 0, 1, 56, 56, 56, 2,151,151, 2, 2, 3, 3, 2, + 3, 0, 0, 26, 26, 0, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, + 0, 9, 9, 2, 9, 9, 9, 55, 55, 6, 1, 1, 6, 2, 2, 2, + 14, 14, 2, 2, 2, 3, 3, 3, 1, 1, 1, 3, 3, 1, 3, 3, + 3, 37, 37, 2, 37, 2, 37, 37, 37, 38, 38, 2, 2, 64, 64, 64, + 2, 2, 64, 64, 64, 90, 90, 2, 2, 90, 90, 90, 2, 2, 2, 95, + 2, 3, 3, 0, 3, 7, 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, + 7, 2, 5, 5, 5, 5, 2, 5, 5, 5, 2, 5, 2, 5, 5, 2, + 2, 2, 2, 2, 5, 5, 5, 2, 5, 2, 11, 11, 2, 2, 11, 2, + 2, 11, 2, 11, 2, 2, 2, 11, 11, 10, 2, 2, 2, 2, 2, 10, + 10, 21, 21, 2, 21, 2, 2, 21, 21, 2, 22, 22, 22, 2, 22, 22, + 2, 2, 23, 23, 2, 23, 23, 23, 2, 2, 2, 23, 23, 2, 2, 2, + 23, 2, 16, 16, 2, 16, 16, 16, 2, 2, 2, 16, 16, 2, 2, 20, + 20, 36, 36, 2, 36, 2, 36, 2, 2, 2, 2, 36, 2, 2, 2, 2, + 36, 36, 2, 36, 2, 2, 24, 24, 24, 24, 24, 24, 2, 2, 18, 18, + 2, 18, 2, 18, 18, 2, 18, 2, 18, 18, 2, 18, 2, 25, 2, 2, + 2, 25, 0, 0, 0, 0, 25, 25, 2, 8, 8, 8, 0, 30, 2, 2, + 2, 34, 2, 2, 2, 35, 35, 35, 0, 0, 0, 35, 35, 35, 2, 2, + 2, 45, 45, 2, 2, 2, 2, 2, 45, 44, 0, 0, 2, 32, 32, 0, + 0, 32, 0, 32, 32, 32, 32, 2, 2, 32, 32, 32, 2, 28, 28, 2, + 2, 48, 48, 48, 2, 48, 2, 2, 2, 52, 52, 2, 2, 52, 2, 2, + 2, 58, 58, 2, 2, 58, 58, 58, 2, 2, 2, 58, 58, 2, 2, 54, + 54, 91, 91, 91, 2, 91, 2, 2, 91, 62, 2, 62, 62, 2, 2, 2, + 70, 70, 70, 2, 2, 2, 70, 70, 70, 6, 6, 6, 2, 8, 8, 8, + 2, 2, 8, 8, 8, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, + 2, 9, 9, 9, 6, 19, 9, 9, 9, 9, 9, 19, 19, 9, 9, 9, + 19, 6, 19, 19, 19, 19, 19, 19, 9, 2, 9, 9, 9, 0, 19, 2, + 2, 1, 2, 2, 2, 0, 0, 9, 0, 2, 55, 55, 55, 61, 2, 2, + 2, 13, 13, 2, 13, 0, 13, 0, 13, 0, 13, 13, 13, 13, 13, 1, + 1, 1, 1, 12, 12, 2, 15, 15, 15, 2, 1, 1, 0, 0, 15, 15, + 15, 2, 26, 26, 26, 2, 12, 12, 12, 12, 12, 12, 0, 39, 2, 2, + 2, 39, 39, 39, 2, 60, 2, 2, 2, 2, 2, 75, 75, 69, 69, 0, + 69, 2, 2, 2, 74, 84, 84, 2, 0, 84, 84, 2, 2, 2, 2, 84, + 84, 33, 33, 33, 2, 68, 68, 68, 2, 92, 92, 92, 2, 2, 2, 2, + 92, 87, 87, 87, 2, 19, 9, 19, 19, 19, 19, 0, 0, 2, 2, 2, + 12, 2, 2, 2, 4, 14, 2, 14, 2, 14, 14, 2, 14, 14, 2, 14, + 14, 3, 3, 0, 0, 1, 1, 6, 6, 3, 2, 3, 3, 3, 2, 2, + 0, 0, 0, 17, 17, 17, 17, 0, 0, 2, 49, 49, 49, 49, 49, 2, + 49, 9, 2, 2, 2, 0, 1, 2, 2, 71, 2, 2, 2, 67, 2, 2, + 2, 2, 42, 42, 42, 41, 41, 41, 2,118,118,118, 2, 53, 53, 2, + 53, 59, 59, 2, 2, 2, 2, 2,104,161, 2, 2, 2,110,110,110, + 2,110,110, 2, 2, 19, 19, 2, 19, 19, 2, 19, 19, 47, 47, 2, + 2, 47, 2, 47, 47, 47, 47, 2, 47, 47, 2, 2, 2, 47, 2, 2, + 47, 81, 81, 2, 81,116,116,116, 2, 2, 2, 2,116,128,128,128, + 2,128,128, 2, 2, 2, 2, 2,128, 2, 2, 2, 66, 72, 72, 2, + 2, 2, 2, 2, 72,173,173, 2, 2, 2, 2, 97, 97, 2, 57, 57, + 2, 57, 57, 2, 2, 57, 57, 57, 2, 2, 2, 2, 57, 2, 2, 2, + 112, 78, 78, 2, 2, 2, 78, 78, 78, 83, 83, 2, 2, 82, 82, 82, + 2,122,122, 2, 2,122, 2, 2, 2, 89, 2, 2, 2, 2, 2,130, + 130,144,144, 2, 2, 2,165,165,165, 2, 2,165,165,156,156, 2, + 156, 3, 2, 2, 2,148,148, 2, 2,158,158, 2, 2,149,149,149, + 2, 2, 2, 94, 94, 2, 2, 2, 94, 85, 85, 85, 2, 2, 85, 2, + 2,101, 2, 2, 2,101,101, 2, 2, 96, 2, 96, 96,111,111,111, + 2,108,108, 2,108,108,108, 2, 2,129,129,129, 2,129, 2,129, + 129,129,129, 2, 2,109,109,109, 2,109,109, 2, 2,107,107, 2, + 1,107,107, 2, 2, 2, 2, 2,107, 2, 2,107,107, 2, 2,171, + 2,171, 2,171, 2, 2,171, 2,171,171,171,171, 2,171, 2, 2, + 2, 2,171,171, 2, 2,137,137,137,137,137, 2, 2,124,124, 2, + 2,114, 2, 2, 2,114,114, 2, 2, 2,126,126,126,125,125,125, + 2, 2, 2, 2,125, 2,154, 2, 2, 2,154,154, 2,154,154, 2, + 154,154, 2, 2,154,154,154, 2, 2,150, 2, 2, 2,140,140,140, + 2,121, 2, 2, 2, 7, 7, 2, 2,133, 2,133,133,133,133,133, + 2,133,133, 2, 2,133, 2, 2, 2, 2, 2,134,134, 2,134,134, + 134,134,134,134, 2, 2, 2,138, 2,138,138, 2, 2,143, 2,143, + 143,143,143,143, 2,143, 2, 2, 2,143,143, 2, 2,175,175, 2, + 2,145, 2, 2, 2,163, 2,163,163, 2, 2,163,163, 86, 2, 2, + 2, 63, 63, 2, 2, 63, 63, 63, 2, 63, 2, 2, 2,157,157,157, + 2, 80, 80, 2, 2, 80, 80, 80, 2,127,127,127, 2,166,166, 2, + 2, 79, 2, 2, 2,115,115,115, 2,115,115, 2, 2, 2, 2,115, + 115,159,159,159, 2,159,159, 2, 2,119,119, 2, 2, 2,119,119, + 119,167,167, 2, 2,146,146,146, 2,172, 2, 2,172, 99, 99, 99, + 2,136,139, 13, 13,155, 2, 2, 2,155,155, 2, 2, 2, 2, 2, + 155, 2, 17, 17, 17, 2, 17, 17, 2, 17, 15, 15, 15, 17, 17, 17, + 2, 2, 2, 15, 2, 2, 17, 2, 2,105,105,105, 2,105,105, 2, + 2, 1, 1, 1, 2, 0, 1, 1, 1, 0, 0, 1, 1, 2, 2, 0, + 2, 2, 0, 0, 2, 0, 2, 0, 2, 2, 2, 2,131, 2,131,131, + 131, 56, 2, 2, 56, 56, 56, 2, 56, 56, 2, 56, 56, 6, 6, 2, + 2, 2, 2, 2, 6,151, 2, 2, 2, 2, 2,151,151,160,160,160, + 2,152,152, 2, 2, 2, 2, 2,152,164,164, 2, 2,168,168,168, + 2, 2, 2, 2,168,174,174,174, 2,174,174, 2, 2, 2, 2,174, + 174, 2, 30, 30, 2,113, 2, 2,113,113,113,113, 2,132,132, 2, + 2, 2, 2,132,132, 2, 2, 3, 2, 3, 2, 3, 2, 15, 0, 0, + 2, 0, 2, 2, 0, 13, 2, 2, 2, 2, 0, 2, 2, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 10, 0, 11, 12, 13, 0, + 0, 0, 14, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,174, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,176,177, 0, 0, 0, 0,178,179, 0, 0, 0, - 180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195, - 196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211, - 212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 17, 0, 0, 0, 0, 18, + 0, 0, 0, 0, 0, 19, 20, 21, 0, 22, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, + 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 27, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, + 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, 0, 31, 32, 0, 33, 0, + 34, 0, 35, 0, 0, 0, 0, 36, 37, 38, 39, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 44, 0, + 45, 0, 0, 0, 0, 0, 0, 46, 47, 0, 0, 0, 0, 0, 48, 0, + 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, + 51, 0, 0, 0, 52, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 54, + 0, 0, 0, 0, 0, 0, 0, 55, 0, 0, 0, 0, 0, 0, 0, 56, + 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 58, + 59, 60, 61, 62, 63, 64, 65, 0, 0, 0, 0, 0, 0, 66, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 69, 70, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, + 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,104, 0, 0, + 0, 0, 0, 0,105,106, 0,107, 0, 0, 0,108, 0,109, 0,110, + 0,111,112,113, 0,114, 0, 0, 0,115, 0, 0, 0,116, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118, + 119,120,121, 0,122,123,124,125,126, 0,127, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,128,129,130,131,132, + 133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148, + 149,150,151,152,153,154,155,156,157, 0, 0, 0,158,159,160,161, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,162, 0,163, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,164,165, 0, 0, 0, 0, 0, 0, 0,166, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, + 0, 0, 0,168,169, 0, 0,170, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,171, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,172, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,173, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,174, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 176,177, 0, 0, 0, 0,178,179, 0, 0, 0,180,181,182,183,184, + 185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, + 201,202,203,204,205,206,207,208,209,210,211,212,213, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -static const uint16_t _hb_ucd_u16[10904]= +static const uint16_t _hb_ucd_u16[10877]= { - 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, - 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, - 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, - 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, - 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, - 13, 13, 13, 42, 9, 43, 11, 11, 44, 45, 32, 46, 47, 48, 49, 50, - 51, 52, 48, 48, 53, 32, 54, 55, 48, 48, 48, 48, 48, 56, 57, 58, - 59, 60, 48, 32, 61, 48, 48, 48, 48, 48, 62, 63, 64, 48, 65, 66, - 48, 67, 68, 69, 48, 70, 71, 48, 72, 73, 48, 48, 74, 32, 75, 32, - 76, 48, 48, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 83, 84, 91, 92, 93, 94, 95, 96, 97, 84, 98, 99, 100, 88, 101, - 102, 83, 84, 103, 104, 105, 88, 106, 107, 108, 109, 110, 111, 112, 94, 113, - 114, 115, 84, 116, 117, 118, 88, 119, 120, 115, 84, 121, 122, 123, 88, 124, - 125, 115, 48, 126, 127, 128, 88, 129, 130, 131, 48, 132, 133, 134, 94, 135, - 136, 48, 48, 137, 138, 139, 140, 140, 141, 48, 142, 143, 144, 145, 140, 140, - 146, 147, 148, 149, 150, 48, 151, 152, 153, 154, 32, 155, 156, 157, 140, 140, - 48, 48, 158, 159, 160, 161, 162, 163, 164, 165, 9, 9, 166, 11, 11, 167, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 168, 169, 48, 48, 168, 48, 48, 170, 171, 172, 48, 48, - 48, 171, 48, 48, 48, 173, 174, 175, 48, 176, 9, 9, 9, 9, 9, 177, - 178, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 179, 48, 180, 181, 48, 48, 48, 48, 182, 183, - 48, 184, 48, 185, 48, 186, 187, 188, 48, 48, 48, 189, 190, 191, 192, 193, - 194, 192, 48, 48, 195, 48, 48, 196, 197, 48, 198, 48, 48, 48, 48, 199, - 48, 200, 201, 202, 203, 48, 204, 205, 48, 48, 206, 48, 207, 208, 209, 209, - 48, 210, 48, 48, 48, 211, 212, 213, 192, 192, 214, 215, 32, 216, 217, 140, - 218, 48, 48, 219, 220, 160, 221, 222, 223, 48, 224, 64, 48, 48, 225, 226, - 48, 48, 227, 228, 229, 64, 48, 230, 231, 9, 9, 232, 233, 234, 235, 236, - 11, 11, 237, 27, 27, 27, 238, 239, 11, 240, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 241, 13, 13, 13, 13, 13, 13, - 242, 243, 242, 242, 243, 244, 242, 245, 246, 246, 246, 247, 248, 249, 250, 251, - 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 262, 263, 264, 265, 266, - 267, 268, 269, 270, 271, 272, 273, 273, 274, 275, 276, 209, 277, 278, 209, 279, - 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, - 281, 209, 282, 209, 209, 209, 209, 283, 209, 284, 280, 285, 209, 286, 287, 209, - 209, 209, 176, 140, 288, 140, 272, 272, 272, 289, 209, 209, 209, 209, 290, 272, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 291, 292, 209, 209, 293, - 209, 209, 209, 209, 209, 209, 294, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 209, 209, 209, 209, 209, 209, 295, 296, 272, 297, 209, 209, 298, 280, 299, 280, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, - 280, 280, 280, 280, 280, 280, 280, 280, 300, 301, 280, 280, 280, 302, 280, 303, - 209, 209, 209, 280, 304, 209, 209, 305, 209, 209, 209, 209, 209, 209, 209, 209, - 9, 9, 9, 11, 11, 11, 306, 307, 13, 13, 13, 13, 13, 13, 308, 309, - 11, 11, 310, 48, 48, 48, 311, 312, 48, 313, 314, 314, 314, 314, 32, 32, - 315, 316, 317, 318, 319, 320, 140, 140, 209, 321, 209, 209, 209, 209, 209, 322, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, 140, 209, - 324, 325, 326, 327, 136, 48, 48, 48, 48, 328, 178, 48, 48, 48, 48, 329, - 330, 48, 48, 136, 48, 48, 48, 48, 200, 331, 48, 48, 209, 209, 332, 48, - 209, 333, 334, 209, 335, 336, 209, 209, 334, 209, 209, 336, 209, 209, 209, 209, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 209, 209, 209, 209, - 48, 337, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 151, 209, 209, 209, 338, 48, 48, 230, - 339, 48, 340, 140, 13, 13, 341, 342, 13, 343, 48, 48, 48, 48, 344, 345, - 31, 346, 347, 348, 13, 13, 13, 349, 350, 351, 352, 353, 354, 355, 140, 356, - 357, 48, 358, 359, 48, 48, 48, 360, 361, 48, 48, 362, 363, 192, 32, 364, - 64, 48, 365, 48, 366, 367, 48, 151, 76, 48, 48, 368, 369, 370, 371, 372, - 48, 48, 373, 374, 375, 376, 48, 377, 48, 48, 48, 378, 379, 380, 381, 382, - 383, 384, 314, 11, 11, 385, 386, 11, 11, 11, 11, 11, 48, 48, 387, 192, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 388, 48, 389, 48, 48, 206, - 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, 390, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, - 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, 48, 207, 140, 140, - 392, 393, 394, 395, 396, 48, 48, 48, 48, 48, 48, 397, 398, 399, 48, 48, - 48, 48, 48, 400, 209, 48, 48, 48, 48, 401, 48, 48, 402, 140, 140, 403, - 32, 404, 32, 405, 406, 407, 408, 409, 48, 48, 48, 48, 48, 48, 48, 410, - 411, 2, 3, 4, 5, 412, 413, 414, 48, 415, 48, 200, 416, 417, 418, 419, - 420, 48, 172, 421, 204, 204, 140, 140, 48, 48, 48, 48, 48, 48, 48, 71, - 422, 272, 272, 423, 273, 273, 273, 424, 425, 426, 427, 140, 140, 209, 209, 428, - 140, 140, 140, 140, 140, 140, 140, 140, 48, 151, 48, 48, 48, 100, 429, 430, - 48, 48, 431, 48, 432, 48, 48, 433, 48, 434, 48, 48, 435, 436, 140, 140, - 9, 9, 437, 11, 11, 48, 48, 48, 48, 204, 192, 9, 9, 438, 11, 439, - 48, 48, 440, 48, 48, 48, 441, 442, 442, 443, 444, 445, 48, 48, 48, 388, - 48, 48, 48, 313, 48, 199, 440, 140, 446, 27, 27, 447, 140, 140, 140, 140, - 448, 48, 48, 449, 48, 450, 48, 451, 48, 200, 452, 140, 140, 140, 48, 453, - 48, 454, 48, 455, 48, 207, 140, 140, 48, 48, 48, 456, 272, 457, 272, 272, - 458, 459, 48, 460, 461, 462, 48, 463, 48, 464, 140, 140, 465, 48, 466, 467, - 48, 48, 48, 468, 48, 469, 48, 470, 48, 471, 472, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 196, 140, 140, 140, 9, 9, 9, 473, 11, 11, 11, 474, - 48, 48, 475, 192, 476, 9, 477, 11, 478, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 272, 479, 48, 48, 480, 481, 482, 483, 140, 484, - 48, 464, 485, 48, 62, 486, 140, 48, 487, 140, 140, 48, 488, 140, 48, 313, - 489, 48, 48, 490, 491, 457, 492, 493, 223, 48, 48, 494, 495, 48, 196, 192, - 496, 48, 497, 498, 499, 48, 48, 500, 223, 48, 48, 501, 502, 503, 504, 505, - 48, 97, 506, 507, 508, 140, 140, 140, 509, 510, 511, 48, 48, 512, 513, 192, - 514, 83, 84, 515, 516, 517, 518, 519, 520, 48, 48, 521, 522, 523, 524, 140, - 48, 48, 48, 525, 526, 527, 481, 140, 48, 48, 48, 528, 529, 192, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 530, 531, 532, 533, 140, 140, - 48, 48, 48, 534, 535, 192, 536, 140, 48, 48, 537, 538, 192, 539, 540, 140, - 48, 541, 542, 543, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 506, 544, 140, 140, 140, 140, 140, 140, 9, 9, 11, 11, 148, 545, - 546, 547, 48, 548, 549, 192, 140, 140, 140, 140, 550, 48, 48, 551, 552, 140, - 553, 48, 48, 554, 555, 556, 48, 48, 557, 558, 559, 48, 48, 48, 48, 196, - 560, 140, 140, 140, 140, 140, 561, 140, 140, 140, 140, 140, 48, 48, 562, 192, - 84, 48, 530, 563, 564, 148, 175, 565, 48, 566, 567, 568, 140, 140, 140, 140, - 569, 48, 48, 570, 571, 192, 572, 48, 573, 574, 192, 48, 48, 575, 192, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 576, - 577, 115, 48, 578, 579, 580, 140, 140, 140, 140, 140, 100, 272, 581, 582, 583, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, - 273, 273, 273, 273, 273, 273, 584, 585, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 388, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 586, - 48, 48, 48, 587, 588, 589, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 71, - 48, 48, 48, 48, 313, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 590, 591, 192, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 196, 48, 200, 370, 48, 48, 48, 48, 200, 192, 48, 204, 592, - 48, 48, 48, 593, 594, 595, 596, 597, 48, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 598, 48, 599, 192, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 9, 9, 11, 11, 272, 600, 9, 601, 11, 602, 140, 140, - 48, 48, 48, 48, 603, 604, 605, 605, 606, 607, 140, 140, 140, 140, 608, 609, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 199, 140, 610, - 48, 200, 140, 140, 140, 140, 140, 140, 48, 48, 48, 48, 48, 48, 48, 611, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 612, - 48, 48, 611, 613, 140, 614, 615, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 206, - 48, 48, 48, 48, 48, 48, 71, 151, 196, 616, 617, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 618, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 619, 209, 427, 209, 620, - 32, 32, 216, 32, 621, 209, 209, 209, 209, 209, 209, 209, 322, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 323, - 209, 209, 622, 209, 209, 209, 623, 624, 625, 209, 626, 209, 209, 209, 288, 140, - 209, 209, 209, 209, 627, 140, 140, 140, 140, 140, 140, 140, 272, 628, 272, 628, - 209, 209, 209, 209, 209, 338, 272, 461, 140, 140, 140, 140, 140, 140, 140, 140, - 9, 629, 11, 630, 631, 632, 242, 9, 633, 634, 635, 636, 637, 9, 629, 11, - 638, 639, 11, 640, 641, 642, 643, 9, 644, 11, 9, 629, 11, 630, 631, 11, - 242, 9, 633, 643, 9, 644, 11, 9, 629, 11, 645, 9, 646, 647, 648, 649, - 11, 650, 9, 651, 652, 653, 654, 11, 655, 9, 656, 11, 657, 539, 539, 539, - 32, 32, 32, 658, 32, 32, 659, 660, 661, 662, 45, 140, 140, 140, 140, 140, - 663, 664, 665, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 666, 667, 668, 27, 27, 27, 669, 140, 670, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 151, 671, 672, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 673, 140, 48, 48, 674, 675, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 676, 192, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 590, 677, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 48, 200, 678, 679, - 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 680, 200, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 681, 621, 140, 140, - 9, 9, 633, 11, 682, 370, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 140, 140, 140, 140, 140, 140, 140, 504, 272, 272, 683, 684, 140, 140, 140, 140, - 504, 272, 685, 686, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 687, 48, 688, 689, 690, 691, 692, 693, 694, 206, 695, 206, 140, 140, 140, 696, - 209, 209, 697, 209, 209, 209, 209, 209, 209, 322, 333, 698, 698, 698, 209, 323, - 699, 209, 209, 209, 209, 209, 209, 209, 209, 209, 700, 140, 140, 140, 701, 209, - 702, 209, 209, 697, 703, 704, 323, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 705, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 706, 426, 426, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 209, 176, 697, 427, - 697, 209, 209, 209, 707, 176, 209, 209, 707, 209, 700, 697, 704, 708, 140, 140, - 209, 209, 209, 209, 209, 707, 700, 426, 709, 209, 209, 209, 710, 711, 712, 703, - 209, 209, 209, 209, 209, 209, 209, 209, 209, 713, 209, 209, 209, 209, 209, 714, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 140, 140, - 48, 204, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 204, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 100, 48, - 48, 48, 48, 48, 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 204, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, - 48, 48, 48, 48, 71, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 207, 140, 140, 140, 140, 140, 140, 140, 140, - 715, 140, 587, 587, 587, 587, 587, 587, 140, 140, 140, 140, 140, 140, 140, 140, - 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 140, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 716, - 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 391, 717, - 0, 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, - 8, 8, 8, 8, 8, 8, 8, 9, 10, 11, 12, 11, 11, 11, 13, 11, - 14, 14, 14, 14, 14, 14, 14, 14, 15, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 16, 17, 18, 17, 17, 19, 20, 21, 21, 22, 21, 23, 24, - 25, 26, 27, 27, 28, 29, 27, 30, 27, 27, 27, 27, 27, 31, 27, 27, - 32, 33, 33, 33, 34, 27, 27, 27, 35, 35, 35, 36, 37, 37, 37, 38, - 39, 39, 40, 41, 42, 43, 44, 27, 27, 45, 27, 27, 27, 27, 46, 27, - 47, 47, 47, 47, 47, 48, 49, 47, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, - 106, 107, 108, 108, 109, 110, 111, 108, 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 121, 122, 121, 123, 124, 124, 125, 126, 127, 128, 129, 130, 124, 124, - 131, 131, 131, 131, 132, 131, 133, 134, 131, 132, 131, 135, 135, 136, 124, 124, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 137, 138, 138, 139, 138, 138, 140, - 141, 141, 141, 141, 141, 141, 141, 141, 142, 142, 142, 142, 143, 144, 142, 142, - 143, 142, 142, 145, 146, 147, 142, 142, 142, 146, 142, 142, 142, 148, 142, 149, - 142, 150, 151, 151, 151, 151, 151, 152, 153, 153, 153, 153, 153, 153, 153, 153, - 154, 155, 156, 156, 156, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, - 167, 167, 167, 167, 167, 168, 169, 169, 170, 171, 172, 172, 172, 172, 172, 173, - 172, 172, 174, 153, 153, 153, 153, 175, 176, 177, 178, 178, 179, 180, 181, 182, - 183, 183, 184, 183, 185, 186, 167, 167, 187, 188, 189, 189, 189, 190, 189, 191, - 192, 192, 193, 8, 8, 194, 195, 124, 196, 196, 196, 196, 197, 196, 196, 196, - 198, 198, 198, 198, 199, 199, 199, 200, 201, 201, 201, 202, 203, 204, 204, 204, - 205, 138, 138, 206, 207, 208, 209, 210, 4, 4, 211, 4, 4, 212, 213, 214, - 4, 4, 4, 215, 8, 8, 8, 8, 11, 216, 11, 11, 216, 217, 11, 218, - 11, 11, 11, 219, 219, 220, 11, 221, 222, 0, 0, 0, 0, 0, 223, 224, - 225, 226, 0, 0, 227, 8, 8, 228, 0, 0, 229, 230, 231, 0, 4, 4, - 232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 233, 124, 234, 124, 0, 0, 235, 235, 235, 235, 235, 235, 235, 235, - 0, 0, 0, 0, 0, 0, 0, 236, 237, 237, 237, 237, 237, 237, 4, 4, - 238, 238, 238, 238, 238, 238, 238, 239, 138, 138, 139, 240, 240, 240, 241, 242, - 142, 243, 244, 244, 244, 244, 14, 14, 0, 0, 0, 0, 0, 245, 124, 124, - 246, 247, 246, 246, 246, 246, 246, 248, 246, 246, 246, 246, 246, 246, 246, 246, - 246, 246, 246, 246, 246, 249, 124, 0, 250, 0, 251, 252, 253, 254, 254, 254, - 254, 255, 256, 257, 257, 257, 257, 258, 259, 260, 260, 261, 141, 141, 141, 141, - 262, 0, 260, 260, 0, 0, 263, 257, 141, 262, 0, 0, 0, 0, 141, 264, - 0, 0, 0, 0, 0, 257, 257, 265, 257, 257, 257, 257, 257, 266, 0, 0, - 246, 246, 246, 246, 0, 0, 0, 0, 267, 267, 267, 267, 267, 267, 267, 267, - 268, 267, 267, 267, 269, 270, 270, 270, 271, 271, 271, 271, 271, 271, 271, 271, - 271, 271, 272, 124, 14, 14, 14, 14, 14, 14, 273, 273, 273, 273, 273, 274, - 0, 0, 275, 4, 4, 4, 4, 4, 276, 4, 4, 4, 4, 226, 124, 277, - 278, 278, 279, 233, 280, 280, 280, 281, 282, 282, 282, 282, 283, 284, 47, 47, - 285, 285, 286, 287, 287, 288, 141, 289, 290, 290, 290, 290, 291, 292, 137, 293, - 294, 294, 294, 295, 296, 297, 137, 137, 298, 298, 298, 298, 299, 300, 301, 302, - 303, 304, 244, 4, 4, 305, 306, 151, 151, 151, 151, 151, 301, 301, 307, 308, - 141, 141, 309, 141, 310, 141, 141, 311, 124, 124, 124, 124, 124, 124, 124, 124, - 246, 246, 246, 246, 246, 246, 312, 246, 246, 246, 246, 246, 246, 313, 124, 124, - 314, 315, 21, 316, 317, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 27, 27, 27, 318, 27, 27, 27, 27, 27, 27, 27, 27, 27, 124, 124, 27, - 8, 233, 319, 0, 0, 320, 321, 322, 27, 27, 27, 27, 27, 27, 27, 323, - 324, 0, 1, 2, 1, 2, 325, 256, 257, 326, 141, 262, 327, 328, 329, 330, - 331, 332, 333, 334, 335, 335, 124, 124, 332, 332, 332, 332, 332, 332, 332, 336, - 337, 0, 0, 338, 11, 11, 11, 11, 339, 340, 341, 124, 124, 0, 0, 342, - 343, 344, 345, 345, 345, 346, 347, 348, 349, 349, 350, 351, 352, 353, 353, 354, - 355, 356, 357, 357, 358, 359, 124, 124, 360, 360, 360, 360, 360, 361, 361, 361, - 362, 363, 364, 365, 365, 366, 365, 367, 368, 368, 369, 370, 370, 370, 371, 372, - 372, 373, 374, 375, 376, 376, 376, 377, 378, 378, 378, 378, 378, 378, 378, 378, - 378, 378, 378, 379, 378, 380, 381, 124, 382, 4, 4, 383, 124, 124, 124, 124, - 384, 385, 385, 386, 387, 388, 389, 389, 390, 391, 392, 124, 124, 124, 393, 394, - 395, 396, 397, 398, 399, 400, 124, 124, 401, 401, 402, 403, 402, 404, 402, 402, - 405, 406, 407, 408, 409, 409, 410, 410, 411, 411, 124, 124, 412, 412, 413, 414, - 415, 415, 415, 416, 417, 418, 419, 420, 421, 422, 423, 124, 124, 124, 124, 124, - 424, 424, 424, 424, 425, 124, 124, 124, 426, 426, 426, 427, 426, 426, 426, 428, - 429, 429, 430, 431, 432, 432, 433, 432, 434, 124, 124, 124, 124, 124, 124, 124, - 124, 124, 124, 124, 124, 124, 27, 435, 436, 436, 437, 438, 439, 440, 124, 441, - 442, 442, 443, 444, 444, 445, 124, 446, 447, 124, 124, 448, 449, 124, 450, 451, - 452, 452, 452, 452, 453, 454, 452, 455, 456, 456, 456, 456, 457, 458, 459, 460, - 461, 461, 461, 462, 463, 464, 464, 465, 466, 466, 466, 466, 466, 466, 467, 468, - 469, 470, 469, 469, 471, 124, 124, 124, 472, 473, 474, 475, 475, 475, 476, 477, - 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 487, 488, 489, 490, 491, 124, - 492, 492, 492, 492, 492, 493, 494, 124, 495, 495, 495, 495, 496, 497, 124, 124, - 498, 498, 498, 499, 498, 500, 124, 124, 501, 501, 501, 501, 502, 503, 504, 124, - 505, 505, 505, 506, 506, 137, 507, 124, 508, 509, 510, 508, 511, 124, 124, 124, - 512, 512, 512, 513, 124, 124, 124, 124, 124, 124, 514, 514, 514, 514, 514, 515, - 516, 517, 518, 519, 520, 521, 124, 124, 124, 124, 522, 523, 523, 522, 524, 124, - 525, 525, 525, 525, 526, 527, 527, 527, 527, 527, 528, 153, 529, 529, 529, 530, - 531, 124, 124, 124, 124, 124, 532, 124, 124, 124, 124, 124, 533, 533, 534, 535, - 536, 537, 537, 538, 539, 537, 540, 541, 541, 542, 543, 544, 124, 124, 124, 124, - 545, 546, 546, 547, 548, 549, 550, 551, 552, 553, 554, 555, 555, 556, 557, 124, - 124, 124, 124, 124, 124, 124, 558, 559, 560, 561, 560, 562, 560, 563, 124, 124, - 124, 124, 124, 564, 565, 565, 565, 566, 567, 567, 567, 567, 567, 567, 567, 567, - 567, 568, 124, 124, 124, 124, 124, 124, 567, 567, 567, 567, 567, 567, 569, 570, - 567, 567, 567, 567, 571, 124, 124, 124, 124, 572, 572, 572, 572, 572, 572, 573, - 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 574, 575, 574, 574, - 574, 574, 574, 574, 574, 574, 574, 576, 577, 577, 577, 577, 577, 577, 577, 577, - 577, 577, 577, 577, 578, 124, 124, 124, 579, 579, 579, 580, 124, 124, 124, 124, - 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 273, 581, 582, 583, 584, 585, - 585, 585, 585, 586, 587, 588, 589, 590, 591, 591, 591, 591, 592, 593, 594, 595, - 591, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 596, 596, 596, 597, - 124, 124, 124, 124, 598, 598, 598, 598, 598, 599, 600, 601, 600, 602, 124, 124, - 603, 603, 603, 603, 604, 603, 603, 603, 605, 603, 124, 124, 124, 124, 606, 607, - 608, 608, 608, 608, 608, 608, 608, 608, 609, 609, 609, 609, 609, 609, 609, 609, - 609, 609, 609, 609, 609, 610, 124, 611, 608, 612, 124, 124, 124, 124, 124, 124, - 608, 608, 608, 608, 608, 608, 608, 613, 124, 124, 124, 124, 124, 124, 124, 614, - 615, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, 254, - 254, 254, 616, 617, 124, 618, 619, 620, 620, 620, 620, 620, 620, 620, 620, 620, - 620, 620, 620, 620, 620, 620, 620, 621, 622, 622, 622, 622, 622, 622, 623, 624, - 625, 626, 627, 124, 124, 124, 124, 124, 0, 0, 0, 0, 0, 0, 0, 340, - 0, 0, 0, 628, 0, 629, 0, 629, 8, 8, 194, 8, 630, 0, 0, 0, - 0, 0, 0, 0, 627, 124, 124, 124, 0, 0, 0, 0, 0, 0, 0, 631, - 0, 0, 632, 0, 0, 0, 633, 634, 635, 0, 636, 0, 0, 0, 234, 124, - 11, 11, 11, 11, 637, 124, 124, 124, 124, 124, 124, 124, 0, 627, 0, 627, - 0, 0, 0, 0, 0, 638, 0, 639, 0, 0, 0, 0, 0, 223, 0, 0, - 0, 640, 641, 642, 643, 0, 0, 0, 644, 645, 0, 646, 647, 648, 0, 0, - 0, 0, 649, 0, 0, 0, 0, 0, 0, 0, 0, 0, 650, 0, 0, 0, - 651, 651, 651, 651, 651, 651, 651, 651, 652, 653, 654, 124, 124, 124, 124, 124, - 4, 655, 656, 124, 124, 124, 124, 124, 657, 658, 659, 14, 14, 14, 660, 124, - 661, 124, 124, 124, 124, 124, 124, 124, 662, 662, 663, 664, 665, 124, 124, 124, - 124, 666, 667, 124, 668, 668, 668, 669, 124, 124, 124, 124, 124, 670, 670, 671, - 124, 124, 124, 124, 124, 672, 672, 673, 124, 124, 124, 124, 674, 675, 674, 676, - 124, 124, 124, 124, 124, 124, 677, 678, 679, 679, 679, 679, 679, 679, 679, 679, - 679, 679, 679, 679, 680, 681, 124, 124, 682, 682, 682, 682, 683, 684, 124, 124, - 124, 124, 124, 124, 124, 124, 124, 324, 0, 0, 0, 685, 124, 124, 124, 124, - 324, 0, 0, 245, 124, 124, 124, 124, 686, 27, 687, 688, 689, 690, 691, 692, - 693, 694, 695, 694, 124, 124, 124, 696, 0, 0, 348, 0, 0, 0, 0, 0, - 0, 627, 225, 324, 324, 324, 0, 631, 0, 0, 245, 124, 124, 124, 697, 0, - 698, 0, 0, 348, 639, 227, 631, 124, 0, 0, 0, 0, 0, 699, 340, 340, - 0, 0, 0, 0, 0, 233, 348, 629, 348, 0, 0, 0, 700, 233, 0, 0, - 700, 0, 245, 348, 227, 639, 124, 124, 0, 0, 0, 0, 0, 700, 245, 340, - 701, 0, 0, 0, 702, 703, 704, 639, 0, 320, 0, 0, 0, 0, 0, 234, - 246, 246, 246, 246, 246, 246, 124, 124, 246, 312, 246, 246, 246, 246, 246, 246, - 246, 246, 312, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 705, 246, - 246, 246, 246, 246, 246, 312, 124, 124, 246, 312, 124, 124, 124, 124, 124, 124, - 246, 246, 246, 246, 706, 246, 246, 246, 246, 246, 246, 246, 246, 246, 246, 313, - 707, 124, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 0, 1, 2, 2, 2, - 2, 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, - 2, 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 8, 8, 8, 8, 16, 8, 8, 8, 17, 18, 18, 18, - 19, 19, 19, 19, 19, 20, 19, 19, 21, 22, 22, 22, 22, 22, 22, 22, - 22, 23, 21, 22, 22, 22, 23, 21, 24, 25, 25, 25, 25, 25, 25, 25, - 25, 25, 12, 12, 25, 25, 26, 27, 25, 28, 12, 12, 29, 30, 29, 31, - 29, 29, 32, 32, 29, 29, 29, 29, 31, 29, 33, 7, 7, 34, 29, 29, - 35, 29, 29, 29, 29, 29, 29, 30, 36, 36, 36, 37, 36, 36, 36, 36, - 36, 36, 38, 39, 40, 40, 40, 40, 41, 12, 12, 12, 42, 42, 42, 42, - 42, 42, 43, 44, 45, 45, 45, 45, 45, 45, 45, 46, 45, 45, 45, 47, - 48, 48, 48, 48, 48, 48, 48, 49, 36, 36, 38, 12, 50, 51, 29, 29, - 52, 29, 29, 29, 53, 53, 53, 53, 54, 55, 53, 53, 53, 56, 53, 53, - 57, 58, 57, 59, 59, 57, 57, 57, 57, 57, 60, 57, 61, 62, 63, 57, - 57, 59, 59, 64, 12, 65, 12, 66, 57, 62, 57, 57, 57, 57, 57, 64, - 67, 67, 68, 69, 70, 71, 71, 71, 71, 71, 72, 71, 72, 73, 74, 72, - 68, 69, 70, 74, 75, 12, 67, 76, 12, 77, 71, 71, 71, 68, 12, 12, - 78, 78, 79, 80, 80, 79, 79, 79, 79, 79, 81, 79, 81, 78, 82, 79, - 79, 80, 80, 82, 83, 12, 12, 12, 79, 84, 79, 79, 82, 12, 78, 79, - 85, 85, 86, 87, 87, 86, 86, 86, 86, 86, 88, 86, 88, 85, 89, 86, - 86, 87, 87, 89, 12, 85, 12, 90, 86, 91, 86, 86, 86, 86, 12, 12, - 92, 93, 94, 92, 95, 96, 97, 95, 98, 99, 94, 92, 100, 100, 96, 92, - 94, 92, 95, 96, 99, 98, 12, 12, 12, 92, 100, 100, 100, 100, 94, 12, - 101, 101, 101, 102, 102, 101, 101, 101, 101, 101, 102, 101, 101, 101, 103, 101, - 101, 102, 102, 103, 12, 104, 105, 103, 101, 106, 101, 101, 12, 107, 101, 101, - 108, 108, 108, 109, 109, 108, 108, 108, 108, 108, 109, 108, 108, 110, 111, 108, - 108, 109, 109, 111, 12, 112, 12, 113, 108, 114, 108, 108, 110, 12, 12, 12, - 115, 115, 115, 116, 116, 115, 115, 115, 115, 115, 115, 115, 115, 116, 116, 115, - 12, 115, 115, 115, 115, 117, 115, 115, 118, 118, 119, 119, 119, 120, 121, 119, - 119, 119, 119, 119, 122, 119, 119, 123, 119, 120, 124, 125, 119, 126, 119, 119, - 12, 121, 119, 119, 121, 127, 12, 12, 128, 129, 129, 129, 129, 129, 129, 129, - 129, 129, 130, 131, 129, 129, 129, 12, 12, 12, 12, 12, 132, 133, 134, 135, - 135, 135, 135, 135, 135, 136, 135, 135, 135, 135, 135, 137, 135, 138, 135, 134, - 135, 135, 137, 135, 139, 139, 139, 139, 139, 139, 140, 139, 139, 139, 139, 141, - 140, 139, 139, 139, 139, 139, 139, 142, 139, 143, 144, 12, 145, 145, 145, 145, - 146, 146, 146, 146, 146, 147, 12, 148, 146, 146, 149, 146, 150, 150, 150, 150, - 151, 151, 151, 151, 151, 151, 152, 153, 151, 154, 152, 153, 152, 153, 151, 154, - 152, 153, 151, 151, 151, 154, 151, 151, 151, 151, 154, 155, 151, 151, 151, 156, - 151, 151, 153, 12, 157, 157, 157, 157, 157, 158, 157, 158, 159, 159, 159, 159, - 160, 160, 160, 160, 160, 160, 160, 161, 162, 162, 162, 162, 162, 162, 163, 164, - 162, 162, 165, 12, 166, 166, 166, 166, 166, 167, 12, 168, 169, 169, 169, 169, - 169, 170, 12, 12, 171, 171, 171, 171, 171, 12, 12, 12, 172, 172, 172, 173, - 173, 12, 12, 12, 174, 174, 174, 174, 174, 174, 174, 175, 174, 174, 175, 12, - 176, 177, 178, 178, 178, 178, 179, 12, 178, 178, 178, 178, 178, 178, 180, 12, - 178, 178, 181, 12, 159, 182, 12, 12, 183, 183, 183, 183, 183, 183, 183, 184, - 183, 183, 183, 12, 185, 183, 183, 183, 186, 186, 186, 186, 186, 186, 186, 187, - 186, 188, 12, 12, 189, 189, 189, 189, 189, 189, 189, 12, 189, 189, 190, 12, - 189, 189, 191, 192, 193, 193, 193, 193, 193, 193, 193, 194, 195, 195, 195, 195, - 195, 195, 195, 196, 195, 195, 195, 197, 195, 195, 198, 12, 195, 195, 195, 198, - 7, 7, 7, 199, 7, 7, 7, 12, 200, 200, 200, 200, 200, 200, 200, 201, - 202, 202, 202, 202, 203, 203, 203, 203, 203, 12, 12, 203, 204, 204, 204, 204, - 204, 204, 205, 204, 204, 204, 206, 207, 208, 208, 208, 208, 19, 19, 209, 12, - 146, 146, 210, 211, 202, 202, 12, 12, 212, 7, 7, 7, 213, 7, 214, 215, - 0, 214, 216, 12, 2, 217, 218, 2, 2, 2, 2, 219, 220, 217, 221, 2, - 2, 2, 222, 2, 2, 2, 2, 223, 8, 224, 8, 224, 8, 8, 225, 225, - 8, 8, 8, 224, 8, 15, 8, 8, 8, 10, 8, 226, 10, 15, 8, 14, - 0, 0, 0, 227, 0, 228, 0, 0, 229, 0, 0, 230, 0, 0, 0, 231, - 2, 2, 2, 232, 233, 12, 12, 12, 234, 12, 12, 12, 0, 235, 236, 0, - 4, 0, 0, 0, 0, 0, 0, 4, 2, 2, 5, 12, 0, 0, 233, 12, - 0, 0, 231, 12, 237, 237, 237, 237, 0, 238, 0, 0, 239, 239, 239, 239, - 18, 18, 18, 18, 18, 12, 240, 18, 241, 241, 241, 241, 241, 241, 12, 242, - 243, 12, 12, 242, 151, 154, 12, 12, 151, 154, 151, 154, 0, 0, 0, 233, - 244, 244, 244, 244, 244, 244, 245, 244, 244, 12, 12, 12, 244, 246, 12, 12, - 0, 247, 0, 0, 248, 244, 249, 250, 0, 0, 244, 0, 251, 252, 252, 252, - 252, 252, 252, 252, 252, 253, 254, 255, 256, 257, 257, 257, 257, 257, 257, 257, - 257, 257, 258, 256, 12, 259, 260, 260, 260, 260, 260, 260, 261, 150, 150, 150, - 150, 150, 150, 262, 0, 233, 12, 131, 150, 150, 150, 263, 257, 257, 257, 258, - 257, 257, 0, 0, 264, 264, 264, 264, 264, 264, 264, 265, 264, 266, 12, 12, - 267, 267, 267, 267, 268, 268, 268, 268, 268, 268, 268, 12, 269, 269, 269, 269, - 269, 269, 12, 12, 236, 2, 2, 2, 2, 2, 230, 2, 270, 2, 2, 2, - 271, 271, 271, 271, 271, 271, 271, 272, 273, 273, 273, 273, 273, 273, 12, 12, - 274, 274, 274, 274, 274, 275, 12, 276, 274, 274, 275, 12, 277, 277, 277, 277, - 277, 277, 277, 278, 279, 279, 279, 279, 279, 12, 12, 280, 150, 150, 150, 281, - 282, 282, 282, 282, 282, 282, 282, 283, 282, 282, 284, 285, 145, 145, 145, 286, - 287, 287, 287, 287, 287, 288, 12, 12, 287, 287, 287, 289, 287, 287, 289, 287, - 290, 290, 290, 290, 291, 12, 12, 12, 12, 12, 292, 290, 293, 293, 293, 293, - 293, 294, 12, 12, 155, 154, 155, 154, 155, 154, 12, 12, 2, 2, 3, 2, - 2, 295, 296, 12, 293, 293, 293, 297, 293, 293, 297, 12, 150, 12, 12, 12, - 150, 262, 298, 150, 150, 150, 150, 12, 244, 244, 244, 246, 244, 244, 246, 12, - 2, 299, 12, 12, 300, 22, 12, 24, 25, 26, 25, 301, 302, 303, 25, 25, - 29, 29, 29, 304, 7, 7, 7, 305, 231, 0, 0, 0, 0, 231, 0, 12, - 29, 306, 29, 29, 29, 29, 29, 307, 308, 0, 0, 0, 0, 309, 257, 257, - 257, 257, 257, 310, 311, 150, 311, 150, 311, 150, 311, 281, 0, 231, 0, 231, - 12, 12, 308, 233, 312, 312, 312, 313, 312, 312, 312, 312, 312, 314, 312, 312, - 312, 312, 314, 315, 312, 312, 312, 316, 312, 312, 314, 12, 231, 131, 0, 0, - 0, 131, 0, 0, 8, 8, 8, 14, 0, 0, 0, 317, 318, 12, 12, 12, - 0, 0, 0, 319, 320, 320, 320, 320, 320, 320, 320, 321, 322, 322, 322, 322, - 323, 12, 12, 12, 214, 0, 0, 0, 0, 0, 0, 12, 324, 324, 324, 324, - 324, 12, 12, 325, 326, 326, 326, 326, 326, 326, 327, 12, 328, 328, 328, 328, - 328, 328, 329, 12, 330, 330, 330, 330, 330, 330, 330, 331, 332, 332, 332, 332, - 332, 12, 332, 332, 332, 333, 12, 12, 334, 334, 334, 334, 335, 335, 335, 335, - 336, 336, 336, 336, 336, 336, 336, 337, 336, 336, 337, 12, 338, 338, 338, 338, - 338, 12, 338, 338, 338, 338, 338, 12, 339, 339, 339, 339, 339, 339, 12, 12, - 340, 340, 340, 340, 340, 12, 12, 341, 342, 342, 343, 342, 343, 344, 342, 342, - 344, 342, 342, 342, 344, 342, 344, 345, 346, 346, 346, 346, 346, 12, 12, 12, - 347, 347, 347, 347, 347, 348, 12, 12, 347, 349, 12, 12, 347, 347, 12, 12, - 2, 350, 2, 2, 351, 2, 299, 12, 352, 353, 354, 352, 352, 352, 352, 352, - 352, 355, 356, 357, 358, 358, 358, 358, 358, 359, 358, 358, 360, 360, 360, 360, - 361, 361, 361, 361, 361, 361, 361, 362, 12, 363, 361, 361, 364, 364, 364, 364, - 365, 366, 367, 364, 368, 368, 368, 368, 368, 368, 368, 369, 370, 370, 370, 370, - 370, 370, 371, 372, 373, 373, 373, 373, 373, 373, 374, 12, 375, 375, 375, 375, - 376, 376, 376, 376, 376, 376, 12, 376, 377, 376, 376, 376, 378, 379, 12, 378, - 378, 380, 380, 378, 378, 378, 378, 378, 378, 381, 382, 383, 378, 378, 384, 12, - 385, 385, 385, 385, 386, 386, 386, 386, 387, 387, 387, 387, 387, 388, 389, 387, - 387, 388, 12, 12, 390, 390, 390, 390, 390, 391, 392, 390, 393, 393, 393, 393, - 393, 394, 393, 393, 395, 395, 395, 395, 396, 12, 395, 395, 397, 397, 397, 397, - 398, 12, 399, 400, 12, 12, 399, 397, 401, 401, 401, 401, 401, 401, 402, 12, - 403, 403, 403, 403, 404, 12, 12, 12, 404, 12, 405, 403, 406, 406, 406, 406, - 406, 406, 12, 12, 406, 406, 407, 12, 408, 408, 408, 408, 408, 409, 410, 408, - 408, 409, 12, 411, 29, 29, 29, 412, 413, 413, 413, 413, 413, 413, 414, 415, - 415, 12, 12, 12, 416, 29, 12, 12, 29, 29, 417, 12, 12, 12, 416, 29, - 418, 418, 418, 418, 418, 418, 12, 12, 419, 419, 419, 419, 419, 419, 420, 12, - 421, 421, 421, 421, 421, 421, 422, 12, 423, 423, 423, 423, 423, 423, 423, 12, - 424, 424, 424, 424, 424, 425, 12, 12, 426, 426, 426, 426, 426, 426, 426, 427, - 428, 426, 426, 426, 426, 427, 12, 429, 430, 430, 430, 430, 431, 12, 12, 432, - 433, 433, 433, 433, 433, 433, 434, 12, 433, 433, 435, 12, 436, 436, 436, 436, - 436, 437, 436, 436, 436, 436, 12, 12, 438, 438, 438, 438, 438, 439, 12, 12, - 440, 440, 440, 440, 118, 119, 119, 119, 119, 127, 12, 12, 441, 441, 441, 441, - 442, 441, 441, 441, 443, 12, 12, 12, 444, 445, 446, 447, 444, 444, 444, 447, - 444, 444, 448, 12, 449, 449, 449, 449, 449, 449, 450, 12, 449, 449, 451, 12, - 452, 453, 452, 454, 454, 452, 452, 452, 452, 452, 455, 452, 455, 453, 456, 452, - 452, 454, 454, 457, 458, 459, 12, 453, 452, 460, 452, 458, 452, 458, 12, 12, - 461, 461, 462, 463, 461, 461, 461, 461, 461, 462, 461, 461, 464, 465, 466, 461, - 461, 462, 467, 12, 468, 12, 12, 12, 469, 469, 469, 469, 469, 469, 469, 470, - 471, 12, 12, 12, 472, 472, 472, 472, 472, 472, 12, 12, 472, 472, 473, 12, - 474, 474, 474, 474, 474, 475, 474, 474, 474, 474, 474, 475, 476, 476, 476, 476, - 476, 477, 12, 12, 476, 476, 478, 12, 178, 178, 178, 180, 479, 479, 479, 479, - 479, 479, 480, 12, 145, 12, 12, 12, 481, 481, 481, 481, 481, 481, 482, 483, - 481, 481, 481, 12, 481, 482, 12, 12, 484, 484, 484, 484, 484, 484, 484, 12, - 485, 485, 485, 485, 486, 12, 12, 487, 488, 489, 490, 488, 488, 491, 488, 488, - 488, 488, 488, 488, 488, 492, 493, 488, 488, 489, 12, 12, 488, 488, 494, 12, - 495, 495, 496, 495, 495, 495, 495, 495, 495, 497, 12, 12, 498, 498, 498, 498, - 498, 498, 12, 12, 499, 499, 499, 499, 500, 12, 12, 12, 501, 501, 501, 501, - 501, 501, 502, 12, 53, 53, 503, 12, 440, 440, 12, 12, 504, 504, 504, 504, - 505, 12, 12, 12, 504, 504, 505, 12, 506, 506, 507, 506, 506, 506, 506, 506, - 506, 508, 506, 506, 506, 509, 12, 12, 506, 506, 506, 510, 511, 511, 511, 511, - 512, 511, 511, 511, 511, 511, 513, 511, 511, 514, 12, 12, 515, 516, 517, 515, - 515, 515, 515, 515, 515, 516, 518, 517, 515, 515, 12, 12, 515, 515, 519, 12, - 520, 521, 522, 520, 520, 520, 520, 520, 520, 520, 520, 523, 521, 520, 524, 12, - 520, 520, 525, 12, 526, 526, 526, 526, 526, 526, 526, 12, 526, 526, 527, 12, - 528, 528, 528, 528, 528, 528, 529, 12, 530, 530, 530, 530, 531, 530, 530, 530, - 530, 530, 532, 533, 530, 530, 532, 12, 534, 12, 12, 12, 100, 100, 100, 100, - 96, 12, 12, 98, 535, 535, 535, 535, 535, 535, 536, 12, 535, 535, 535, 537, - 535, 538, 12, 12, 535, 12, 12, 12, 539, 539, 539, 539, 540, 12, 12, 12, - 541, 541, 541, 541, 541, 542, 12, 12, 541, 541, 543, 12, 544, 544, 544, 544, - 544, 545, 12, 12, 546, 546, 546, 546, 546, 546, 547, 12, 269, 269, 548, 12, - 549, 549, 549, 549, 549, 549, 549, 550, 549, 549, 551, 552, 553, 553, 553, 553, - 553, 553, 553, 554, 553, 553, 555, 12, 556, 556, 556, 556, 556, 556, 556, 557, - 556, 557, 12, 12, 558, 558, 558, 558, 558, 559, 12, 12, 558, 558, 560, 558, - 560, 558, 558, 558, 558, 558, 12, 561, 562, 562, 562, 562, 562, 562, 563, 12, - 564, 564, 564, 564, 564, 564, 565, 12, 566, 566, 566, 566, 566, 566, 567, 566, - 566, 12, 12, 12, 568, 568, 568, 568, 568, 568, 569, 570, 568, 568, 12, 570, - 571, 572, 12, 12, 244, 573, 12, 12, 574, 574, 574, 574, 575, 575, 575, 575, - 575, 576, 12, 12, 12, 12, 12, 577, 574, 574, 574, 578, 578, 12, 12, 12, - 257, 579, 257, 580, 581, 252, 252, 252, 582, 12, 12, 12, 583, 12, 12, 12, - 253, 584, 12, 12, 12, 257, 12, 12, 585, 585, 585, 585, 585, 585, 585, 12, - 586, 586, 586, 586, 586, 586, 587, 12, 586, 586, 586, 588, 586, 586, 588, 12, - 586, 586, 589, 586, 0, 12, 12, 12, 0, 12, 238, 0, 317, 12, 12, 12, - 7, 590, 12, 12, 0, 233, 12, 12, 0, 231, 308, 0, 0, 591, 227, 0, - 0, 0, 591, 7, 212, 592, 7, 0, 0, 0, 593, 227, 8, 224, 12, 12, - 0, 231, 12, 12, 0, 0, 317, 12, 0, 0, 0, 228, 594, 595, 308, 228, - 0, 0, 596, 308, 0, 308, 0, 0, 0, 596, 231, 308, 0, 228, 0, 228, - 0, 0, 596, 231, 0, 597, 238, 0, 228, 0, 0, 0, 0, 233, 0, 0, - 0, 0, 0, 238, 598, 598, 598, 598, 598, 598, 598, 12, 12, 12, 599, 598, - 600, 598, 598, 598, 2, 2, 2, 299, 12, 270, 299, 12, 239, 601, 239, 239, - 239, 239, 602, 239, 603, 604, 601, 12, 19, 19, 19, 605, 12, 12, 12, 606, - 607, 607, 607, 607, 607, 607, 607, 608, 607, 607, 607, 609, 607, 607, 609, 610, - 611, 611, 611, 611, 611, 611, 611, 612, 613, 613, 613, 613, 613, 613, 614, 615, - 616, 616, 616, 616, 616, 616, 617, 12, 618, 618, 618, 618, 618, 618, 619, 620, - 621, 621, 621, 621, 621, 621, 621, 622, 621, 623, 12, 624, 151, 154, 151, 625, - 151, 151, 151, 154, 626, 626, 626, 626, 626, 627, 626, 626, 626, 628, 12, 12, - 629, 629, 629, 629, 629, 629, 629, 12, 629, 629, 630, 631, 0, 317, 12, 12, - 29, 632, 29, 29, 633, 634, 632, 29, 412, 29, 635, 12, 636, 51, 635, 632, - 633, 634, 635, 635, 633, 634, 412, 29, 412, 29, 632, 637, 29, 29, 638, 29, - 29, 29, 29, 12, 632, 632, 638, 29, 50, 12, 12, 12, 12, 238, 0, 0, - 639, 12, 12, 12, 0, 0, 317, 0, 0, 0, 12, 12, 0, 0, 231, 238, - 0, 231, 317, 308, 0, 0, 0, 640, 0, 0, 231, 131, 641, 12, 12, 12, - 244, 244, 573, 12, 642, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, - 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, - 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126,1127,1131,1133, 0,1147, - 1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226,1227,1228,1229,1233, 0, - 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944,1129, 954,1139, 958,1143, - 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973,1158, 974,1159, 975,1160, - 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993,1178, 994,1179, 0, 0, - 1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, 0,1016,1201,1020,1206, - 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0,1032,1218,1037,1223,1035, - 1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, 0,1058,1244,1064,1250, - 1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077,1264,1074,1261, 0, 0, - 1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096,1283,1103,1290,1111,1299, - 1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0,1093,1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 949,1134,1010,1195,1050,1236,1090,1277,1341,1368,1340, - 1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323,1350, 0, 0, 992,1177, - 1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, 0, 987,1172, 0, 0, - 1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951,1136, 979,1164, 980,1165, - 1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062,1248,1091,1278,1092,1279, - 1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, 0, 0, 0, 945,1130, - 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113,1301, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 10,1425, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,1314,1427, 5, - 1434,1438,1443, 0,1450, 0,1455,1461,1514, 0, 0, 0, 0, 0, 0, 0, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, + 224, 224, 768, 424, 432, 440, 448, 776, 224, 224, 784, 792, 72, 800, 40, 808, + 48, 48, 48, 816, 824, 48, 48, 832, 840, 848, 856, 864, 872, 880, 48, 888, + 48, 48, 48, 896, 904, 40, 40, 40, 40, 912, 40, 96, 920, 928, 936, 456, + 64, 64, 64, 64, 64, 64, 64, 944, 952, 960, 968, 40, 976, 984, 48, 992, + 72, 72, 72, 40, 40, 40, 48, 48,1000, 48, 48, 48,1008, 48, 48, 48, + 48, 48, 48,1016, 72,1024, 40, 40,1032, 464, 64,1040,1048, 8,1056,1064, + 1072,1080, 8, 8,1088, 64,1096,1104, 8, 8, 8, 8, 8,1112,1120,1128, + 1136,1144, 8, 64,1152, 8, 8, 8, 8, 8, 472,1160, 232, 8,1168,1176, + 8,1184,1192,1200, 8,1208, 176, 8,1216,1224, 8, 8,1232, 64,1240, 64, + 480, 8, 8,1248,1256,1264,1272,1280,1288, 240, 128,1296,1304,1312, 144,1320, + 1328, 240, 128,1336,1344,1352, 304,1360,1368, 488, 128,1376,1384, 248, 144,1392, + 1400, 240, 128,1408,1416,1424, 144,1432,1440,1448,1456,1464,1472,1480, 304,1488, + 1496, 256, 128,1504,1512,1520, 144,1528,1536, 256, 128,1544,1552,1560, 144,1568, + 1576, 256, 8,1584,1592,1600, 144,1608,1616,1624, 8,1632,1640,1648, 304,1656, + 312, 8, 8,1664,1672,1680, 0, 0,1688, 8,1696,1704,1712,1720, 0, 0, + 1728,1736, 320,1744,1752, 8, 152,1760,1768,1776, 64,1784,1792,1800, 0, 0, + 8, 8,1808,1816, 496,1824,1832,1840,1848,1856, 72, 72,1864, 40, 40,1872, + 8, 8, 8, 8, 504,1880, 8, 8, 504, 8, 8,1888, 512, 520, 8, 8, + 8, 512, 8, 8, 8,1896,1904, 528, 8, 264, 72, 72, 72, 72, 72,1912, + 536, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8,1920, 8,1928,1936, 8, 8, 8, 8,1944,1952, + 8,1960, 8,1968, 8,1976,1984,1992, 8, 8, 8,2000,2008,2016, 80,2024, + 2032, 80, 8, 8,2040, 8, 8, 160,2048, 8,2056, 8, 8, 8, 8, 328, + 8, 120,2064,2072,2080, 8, 112,2088, 8, 8, 184, 8, 192,2096, 24, 24, + 8,2104, 8, 8, 8,2112,2120,2128, 80, 80,2136,2144, 64, 544,2152, 0, + 2160, 8, 8,2168,2176, 496,2184,2192, 336, 8,2200, 232, 8, 8,2208,2216, + 8, 8,2224,2232,2240, 232, 8, 552,2248, 72, 72,2256,2264,2272,2280,2288, + 40, 40,2296, 96, 96, 96,2304,2312, 40,2320, 96, 96, 64, 64, 64, 64, + 48, 48, 48, 48, 48, 48, 48, 48, 48,2328, 48, 48, 48, 48, 48, 48, + 168, 560, 168, 168, 560,2336, 168,2344, 344, 344, 344,2352,2360,2368,2376,2384, + 2392,2400,2408,2416,2424,2432,2440,2448,2456,2464, 568, 568,2472,2480,2488,2496, + 2504,2512,2520,2528,2536, 88, 104, 104,2544,2552,2560, 24,2568,2576, 24,2584, + 2592, 24,2600, 24, 24, 24, 24,2608, 24,2616, 56,2624, 24,2632,2640, 24, + 24, 24, 264, 0, 576, 0, 88, 88, 88,2648, 24, 24, 24, 24,2656, 88, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,2664,2672, 24, 24,2680, + 24, 24, 24, 24, 24, 24,2688, 24, 24, 24, 24, 24, 24, 24, 24, 24, + 24, 24, 24, 24, 24, 24,2696,2704, 88,2712, 24, 24,2720, 56,2728, 56, + 56, 56, 56, 56, 56, 56, 56, 56,2736,2744, 56, 56, 56,2752, 56,2760, + 24, 24, 24, 56,2768, 24, 24,2776, 24, 24, 24, 24, 24, 24, 24, 24, + 72, 72, 72, 40, 40, 40,2784,2792, 48, 48, 48, 48, 48, 48,2800,2808, + 40, 40,2816, 8, 8, 8,2824,2832, 8, 200, 208, 208, 208, 208, 64, 64, + 2840,2848,2856,2864,2872,2880, 0, 0, 24,2888, 24, 24, 24, 24, 24, 352, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 272, 0, 24, + 2896,2904,2912,2920, 312, 8, 8, 8, 8,2928, 536, 8, 8, 8, 8,2936, + 2944, 8, 8, 312, 8, 8, 8, 8, 120,2952, 8, 8, 24, 24,2960, 8, + 24, 584, 592, 24,2968, 600, 24, 24, 592, 24, 24, 600, 24, 24, 24, 24, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 24, 24, 24, 24, + 8,2976, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 152, 24, 24, 24, 608, 8, 8, 552, + 2984, 8,2992, 0, 48, 48,3000,3008, 48,3016, 8, 8, 8, 8,3024,3032, + 456,3040,3048,3056, 48, 48, 48,3064,3072,3080,3088,3096,3104,3112, 0,3120, + 3128, 8,3136,3144, 8, 8, 8,3152,3160, 8, 8,3168,3176, 80, 64,3184, + 232, 8,3192, 8,3200,3208, 8, 152, 480, 8, 8,3216,3224, 360,3232,3240, + 8, 8,3248,3256,3264,3272, 8,3280, 8, 8, 8,3288,3296,3304,3312,3320, + 3328,3336, 208, 40, 40,3344,3352, 40, 40, 40, 40, 40, 8, 8,3360, 80, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 368, 8,3368, 8, 8, 184, + 8, 8, 8, 8, 8, 8, 112, 8, 8, 8, 8, 8, 8, 192, 0, 0, + 3376,3384,3392,3400,3408, 8, 8, 8, 8, 8, 8,3416,3424,3432, 8, 8, + 8, 8, 8,3440, 24, 8, 8, 8, 8,3448, 8, 8,3456, 0, 0,3464, + 64,3472, 64,3480,3488,3496,3504,3512, 8, 8, 8, 8, 8, 8, 8,3520, + 3528, 424, 432, 440, 448,3536,3544,3552, 8,3560, 8, 120,3568,3576,3584,3592, + 3600, 8, 520,3608, 112, 112, 0, 0, 8, 8, 8, 8, 8, 8, 8, 176, + 3616, 88, 88,3624, 104, 104, 104,3632,3640, 280, 376, 0, 0, 24, 24,3648, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 152, 8, 8, 8, 248,3656,3664, + 8, 8,3672, 8,3680, 8, 8,3688, 8,3696, 8, 8,3704,3712, 0, 0, + 72, 72,3720, 40, 40, 8, 8, 8, 8, 112, 80, 72, 72,3728, 40,3736, + 8, 8, 616, 8, 8, 8,3744, 624, 624,3752,3760,3768, 8, 8, 8, 368, + 8, 8, 8, 200, 8, 328, 616, 0,3776, 96, 96,3784, 0, 0, 0, 0, + 3792, 8, 8,3800, 8,3808, 8,3816, 8, 120,3824, 0, 0, 0, 8,3832, + 8,3840, 8,3848, 8, 192, 0, 0, 8, 8, 8,3856, 88, 632, 88, 88, + 3864,3872, 8,3880, 640,3888, 8,3896, 8, 648, 0, 0,3904, 8,3912,3920, + 8, 8, 8,3928, 8,3936, 8,3944, 8,3952,3960, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 160, 0, 0, 0, 72, 72, 72,3968, 40, 40, 40,3976, + 8, 8,3984, 80,3992, 72,4000, 40,4008, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 88,4016, 8, 8,4024, 656,4032,4040, 0,4048, + 8, 648,4056, 8, 472,4064, 0, 8,4072, 0, 0, 8,4080, 0, 8, 200, + 4088, 8, 8,4096,4104, 632,4112,4120, 336, 8, 8,4128,4136, 8, 160, 80, + 4144, 8,4152,4160,4168, 8, 8,4176, 336, 8, 8,4184,4192,4200, 384,4208, + 8, 488, 664,4216,4224, 0, 0, 0,4232,4240,4248, 8, 8,4256,4264, 80, + 4272, 240, 128,4280,4288,4296,4304,4312,4320, 8, 8,4328,4336,4344,4352, 0, + 8, 8, 8,4360,4368,4376, 656, 0, 8, 8, 8,4384,4392, 80, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 672,4400,4408,4416, 0, 0, + 8, 8, 8,4424,4432, 80,4440, 0, 8, 8,4448,4456, 80, 288,4464, 0, + 8,4472,4480,4488, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 664,4496, 0, 0, 0, 0, 0, 0, 72, 72, 40, 40, 320,4504, + 4512,4520, 8,4528,4536, 80, 0, 0, 0, 0,4544, 8, 8,4552,4560, 0, + 4568, 8, 8,4576,4584,4592, 8, 8,4600,4608,4616, 8, 8, 8, 8, 160, + 4624, 0, 0, 0, 0, 0,4632, 0, 0, 0, 0, 0, 8, 8,4640, 80, + 128, 8, 672,4648,4656, 320, 528,4664, 8,4672,4680,4688, 0, 0, 0, 0, + 4696, 8, 8,4704,4712, 80,4720, 8,4728,4736, 80, 8, 8,4744, 80, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,4752, + 4760, 256, 8,4768,4776,4784, 0, 0, 0, 0, 0, 248, 88,4792,4800,4808, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 192, 0, 0, 0, 0, 0, 0, + 104, 104, 104, 104, 104, 104,4816,4824, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 368, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8,4832, + 8, 8, 8, 136,4840,4848, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 176, + 8, 8, 8, 8, 200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 680,4856, 80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 160, 8, 120, 360, 8, 8, 8, 8, 120, 80, 8, 112,4864, + 8, 8, 8,4872,4880,4888,4896,4904, 8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0,4912, 8,4920, 80, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 72, 72, 40, 40, 88,4928, 72,4936, 40,4944, 0, 0, + 8, 8, 8, 8,4952,4960, 688, 688,4968,4976, 0, 0, 0, 0,4984,4992, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 328, 0,5000, + 8, 120, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 696, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,5008, + 8, 8, 696,5016, 0,5024,5032, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 184, + 8, 8, 8, 8, 8, 8, 176, 152, 160,5040,5048, 0, 0, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,5056, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,5064, 24, 376, 24,5072, + 64, 64, 544, 64, 704, 24, 24, 24, 24, 24, 24, 24, 352, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 272, + 24, 24,5080, 24, 24, 24,5088,5096,5104, 24,5112, 24, 24, 24, 576, 0, + 24, 24, 24, 24,5120, 0, 0, 0, 0, 0, 0, 0, 88, 712, 88, 712, + 24, 24, 24, 24, 24, 608, 88, 640, 0, 0, 0, 0, 0, 0, 0, 0, + 72, 296, 40, 720, 728,5128, 168, 72, 392,5136,5144,5152,5160, 72, 296, 40, + 5168,5176, 40,5184,5192,5200, 736, 72, 744, 40, 72, 296, 40, 720, 728, 40, + 168, 72, 392, 736, 72, 744, 40, 72, 296, 40,5208, 72,5216,5224,5232,5240, + 40,5248, 72,5256,5264,5272,5280, 40,5288, 72,5296, 40,5304, 288, 288, 288, + 64, 64, 64,5312, 64, 64,5320,5328,5336,5344, 464, 0, 0, 0, 0, 0, + 5352,5360,5368, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5376,5384,5392, 96, 96, 96,5400, 0,5408, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 152,5416,5424, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,5432, 0, 8, 8,5440,5448, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8,5456, 80, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 680,5464, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 120,5472,5480, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,5488, 120, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,5496, 704, 0, 0, + 72, 72, 392, 40,5504, 360, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 384, 88, 88,5512,5520, 0, 0, 0, 0, + 384, 88,5528,5536, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5544, 8,5552,5560,5568,5576,5584,5592,5600, 184,5608, 184, 0, 0, 0,5616, + 24, 24, 216, 24, 24, 24, 24, 24, 24, 352, 584, 400, 400, 400, 24, 272, + 5624, 24, 24, 24, 24, 24, 24, 24, 24, 24, 408, 0, 0, 0,5632, 24, + 5640, 24, 24, 216, 752, 760, 272, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,5648, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,5656, 280, 280, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 264, 216, 376, + 216, 24, 24, 24, 416, 264, 24, 24, 416, 24, 408, 216, 760,5664, 0, 0, + 24, 24, 24, 24, 24, 416, 408, 280,5672, 24, 24, 24,5680,5688,5696, 752, + 24, 24, 24, 24, 24, 24, 24, 24, 24,5704, 24, 24, 24, 24, 24,5712, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0, 0, + 8, 112, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 112, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 248, 8, + 8, 8, 8, 8, 8, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 8, 8, 8, 8, 176, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 192, 0, 0, 0, 0, 0, 0, 0, 0, + 5720, 0, 136, 136, 136, 136, 136, 136, 0, 0, 0, 0, 0, 0, 0, 0, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 0, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,5728, + 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, + 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, + 20, 20, 20, 20, 20, 20, 20, 20, 12, 12, 12, 12, 12, 12, 12, 12, + 24, 24, 24, 24, 24, 24, 24, 24, 28, 28, 28, 28, 28, 28, 28, 28, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 52, 52, 52, 52, 52, 52, 52, 52, + 44, 44, 44, 44, 44, 44, 44, 44, 36, 36, 36, 36, 36, 36, 36, 36, + 64, 64, 64, 64, 64, 64, 64, 64, 92, 92, 92, 92, 92, 92, 92, 92, + 84, 84, 84, 84, 84, 84, 84, 84, 80, 80, 80, 80, 80, 80, 80, 80, + 68, 68, 68, 68, 68, 68, 68, 68, 56, 56, 56, 56, 56, 56, 56, 56, + 12, 12, 12, 12, 228, 232, 228, 232, 12, 12, 468, 468, 32, 472, 32, 472, + 32, 32, 32, 692, 12, 12, 696, 12, 56, 56, 56, 56, 56, 56, 56, 700, + 704, 72, 708, 72, 72, 72, 712, 72, 60, 60, 60, 60, 60, 60, 60, 60, + 716, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 720, 308, 724, 308, 308, + 728, 732, 236, 236, 736, 236, 740, 744, 748, 752, 36, 36, 756, 760, 36, 764, + 36, 36, 36, 36, 36, 768, 36, 36, 772, 312, 312, 312, 776, 36, 36, 36, + 316, 316, 316, 780, 320, 320, 320, 784, 476, 476, 788, 792, 796, 800, 804, 36, + 36, 808, 36, 36, 36, 36, 812, 36, 112, 112, 112, 112, 112, 816, 820, 112, + 824, 828, 832, 836, 840, 844, 848, 852, 856, 860, 864, 868, 872, 876, 880, 884, + 888, 892, 896, 900, 904, 908, 912, 916, 920, 924, 928, 932, 936, 940, 944, 948, + 952, 956, 960, 964, 968, 972, 976, 980, 984, 988, 992, 996,1000,1004,1008,1012, + 1016,1020,1024,1028,1032,1036,1040,1044,1048,1052, 324, 324,1056,1060,1064, 324, + 1068,1072,1076,1080,1084,1088,1092,1096,1100, 328, 328,1104, 328,1108, 0, 0, + 1112,1116,1120,1124,1128,1132, 0, 0, 128, 128, 128, 128, 480, 128,1136,1140, + 128, 480, 128, 484, 484,1144, 0, 0, 96, 96, 96, 96, 96, 96, 96, 96, + 96, 96, 116, 116, 488, 116, 116,1148, 88, 88, 88, 88, 492,1152, 88, 88, + 492, 88, 88,1156, 496,1160, 88, 88, 88, 496, 88, 88, 88,1164, 88,1168, + 88,1172, 108, 108, 108, 108, 108,1176,1180,1184, 240, 240, 240, 240,1188,1192, + 1196,1200,1204,1208,1212,1216,1220,1224, 132, 132, 132, 132, 132,1228, 500, 500, + 1232,1236, 136, 136, 136, 136, 136,1240, 136, 136,1244, 40, 40, 40, 40,1248, + 1252,1256, 504, 504,1260,1264,1268,1272, 332, 332,1276, 332,1280,1284, 132, 132, + 1288,1292, 244, 244, 244,1296, 244,1300, 508, 508,1304, 56, 56, 512,1308, 0, + 140, 140, 140, 140,1312, 140, 140, 140, 248, 248, 248, 248, 336, 336, 336,1316, + 340, 340, 340,1320,1324, 344, 344, 344,1328, 116, 116,1332,1336,1340,1344,1348, + 32, 32,1352, 32, 32,1356,1360,1364, 32, 32, 32,1368, 56, 56, 56, 56, + 72, 516, 72, 72, 516,1372, 72,1376, 72, 72, 72, 520, 520,1380, 72,1384, + 1388, 12, 12, 12, 12, 12, 524,1392, 528, 532, 12, 12, 348, 56, 56,1396, + 12, 12,1400,1404,1408, 12, 32, 32,1412, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 176, 0, 352, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12,1416, + 148, 148, 148, 148, 148, 148, 32, 32, 144, 144, 144, 144, 144, 144, 144,1420, + 116, 116, 488, 356, 356, 356,1424,1428, 88,1432, 180, 180, 180, 180, 60, 60, + 12, 12, 12, 12, 12, 184, 0, 0, 4,1436, 4, 4, 4, 4, 4,1440, + 4, 4, 4, 4, 4,1444, 0, 12,1448, 12,1452,1456,1460, 76, 76, 76, + 76,1464, 536, 100, 100, 100, 100,1468,1472, 252, 252,1476, 8, 8, 8, 8, + 360, 12, 252, 252, 12, 12,1480, 100, 8, 360, 12, 12, 12, 12, 8,1484, + 12, 12, 12, 12, 12, 100, 100,1488, 100, 100, 100, 100, 100,1492, 12, 12, + 4, 4, 4, 4, 12, 12, 12, 12,1496, 24, 24, 24,1500, 364, 364, 364, + 84, 84,1504, 0, 60, 60, 60, 60, 60, 60, 44, 44, 44, 44, 44,1508, + 12, 12,1512, 32, 32, 32, 32, 32,1516, 32, 32, 32, 32, 532, 0,1520, + 540, 540,1524, 176, 368, 368, 368,1528, 256, 256, 256, 256,1532,1536, 112, 112, + 544, 544,1540, 548, 548,1544, 8,1548, 260, 260, 260, 260,1552,1556, 96,1560, + 372, 372, 372,1564,1568,1572, 96, 96, 264, 264, 264, 264,1576,1580, 376,1584, + 1588,1592, 180, 32, 32,1596,1600, 108, 108, 108, 108, 108, 376, 376,1604,1608, + 8, 8,1612, 8,1616, 8, 8,1620, 4, 4, 4, 4, 4, 4, 188, 4, + 4, 4, 4, 4, 4, 552, 0, 0,1624,1628, 236,1632,1636, 36, 36, 36, + 36, 36, 36,1640, 36, 36, 36, 36, 36, 36, 36, 36, 36, 0, 0, 36, + 56, 176,1644, 12, 12, 556,1648,1652, 36, 36, 36, 36, 36, 36, 36,1656, + 152, 12, 228, 232, 228, 232,1660, 536, 100,1664, 8, 360,1668,1672,1676,1680, + 1684, 120,1688,1692, 560, 560, 0, 0, 120, 120, 120, 120, 120, 120, 120,1696, + 1700, 12, 12,1704, 72, 72, 72, 72,1708, 192,1712, 0, 0, 12, 12,1716, + 1720,1724, 380, 380, 380,1728,1732, 156, 564, 564,1736,1740,1744, 568, 568,1748, + 1752,1756, 572, 572,1760,1764, 0, 0, 196, 196, 196, 196, 196, 384, 384, 384, + 1768,1772,1776, 388, 388,1780, 388,1784, 576, 576,1788, 392, 392, 392,1792, 580, + 580,1796,1800,1804, 396, 396, 396,1808, 80, 80, 80,1812, 80,1816,1820, 0, + 1824, 32, 32,1828, 0, 0, 0, 0,1832, 584, 584,1836,1840,1844, 588, 588, + 1848,1852,1856, 0, 0, 0,1860,1864,1868,1872,1876,1880,1884,1888, 0, 0, + 592, 592, 268,1892, 268,1896, 268, 268,1900,1904,1908,1912, 596, 596, 600, 600, + 604, 604, 0, 0, 608, 608,1916,1920, 400, 400, 400,1924,1928,1932,1936,1940, + 1944,1948,1952, 0, 0, 0, 0, 0, 272, 272, 272, 272,1956, 0, 0, 0, + 160, 160, 160,1960, 160, 160, 160,1964, 612, 612,1968,1972, 404, 404,1976, 404, + 1980, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36,1984, + 616, 616,1988,1992,1996,2000, 0,2004, 620, 620,2008, 624, 624,2012, 0,2016, + 2020, 0, 0,2024,2028, 0,2032,2036, 200, 200, 200, 200,2040,2044, 200,2048, + 276, 276, 276, 276,2052,2056,2060,2064, 408, 408, 408,2068,2072, 628, 628,2076, + 164, 164, 164, 164, 164, 164,2080,2084, 412,2088, 412, 412,2092, 0, 0, 0, + 2096,2100,2104, 416, 416, 416,2108,2112,2116,2120,2124,2128,2132,2136,2140,2144, + 2148, 632, 632,2152,2156,2160,2164, 0, 204, 204, 204, 204, 204,2168,2172, 0, + 280, 280, 280, 280,2176,2180, 0, 0, 284, 284, 284,2184, 284,2188, 0, 0, + 288, 288, 288, 288,2192,2196,2200, 0, 420, 420, 420, 636, 636, 96,2204, 0, + 640,2208,2212, 640,2216, 0, 0, 0, 424, 424, 424,2220, 0, 0, 0, 0, + 0, 0, 208, 208, 208, 208, 208,2224,2228,2232,2236,2240,2244,2248, 0, 0, + 0, 0, 644, 648, 648, 644,2252, 0, 292, 292, 292, 292,2256, 212, 212, 212, + 212, 212,2260, 40, 428, 428, 428,2264,2268, 0, 0, 0, 0, 0,2272, 0, + 0, 0, 0, 0, 652, 652,2276,2280,2284, 432, 432,2288,2292, 432,2296, 656, + 656,2300,2304,2308, 0, 0, 0, 0,2312, 660, 660,2316,2320,2324,2328,2332, + 2336,2340,2344, 664, 664,2348,2352, 0, 0, 0, 0, 0, 0, 0,2356,2360, + 436,2364, 436,2368, 436,2372, 0, 0, 0, 0, 0,2376, 440, 440, 440,2380, + 28,2384, 0, 0, 0, 0, 0, 0, 28, 28, 28, 28, 28, 28,2388,2392, + 28, 28, 28, 28,2396, 0, 0, 0, 0, 168, 168, 168, 168, 168, 168,2400, + 20, 20, 20, 20, 20,2404, 20, 20, 20, 20, 20, 20, 20, 20, 20,2408, + 52, 52, 52, 52,2412, 0, 0, 0, 444, 444, 444,2416, 0, 0, 0, 0, + 44, 44, 44,2420,2424,2428,2432, 296, 296, 296, 296,2436,2440,2444,2448,2452, + 216, 216, 216, 216,2456,2460,2464,2468, 216, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 448, 448, 448,2472, 0, 0, 0, 0, 220, 220, 220, 220, + 220,2476, 668,2480, 668,2484, 0, 0, 124, 124, 124, 124,2488, 124, 124, 124, + 2492, 124, 0, 0, 0, 0,2496,2500, 64, 64, 64, 64, 64,2504, 0,2508, + 16,2512, 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16,2516, + 0, 0, 0, 0, 0, 0, 0,2520,2524, 76, 76, 76, 76, 76, 76, 76, + 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,2528,2532, 0,2536,2540, 68, + 68, 68, 68, 68, 68, 68, 68,2544, 172, 172, 172, 172, 172, 172,2548,2552, + 2556,2560, 224, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 192, + 12, 12, 12,2564, 12, 452, 12, 452, 56, 56, 512, 56,2568, 12, 12, 12, + 12, 12, 12, 12, 224, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 456, + 12, 12,2572, 12, 12, 12,2576,2580,2584, 12,2588, 12, 12, 12, 352, 0, + 72, 72, 72, 72,2592, 0, 0, 0, 0, 0, 0, 0, 12, 224, 12, 224, + 12, 12, 12, 12, 12,2596, 12, 300, 12, 12, 12, 12, 12, 524, 12, 12, + 12,2600,2604,2608,2612, 12, 12, 12,2616,2620, 12,2624,2628,2632, 12, 12, + 12, 12,2636, 12, 12, 12, 12, 12, 12, 12, 12, 12,2640, 12, 12, 12, + 2644,2648,2652, 0, 0, 0, 0, 0, 32,2656,2660, 0, 0, 0, 0, 0, + 2664,2668,2672, 60, 60, 60,2676, 0,2680, 0, 0, 0, 0, 0, 0, 0, + 672, 672,2684,2688,2692, 0, 0, 0, 0,2696,2700, 0, 460, 460, 460,2704, + 0, 0, 0, 0, 0, 676, 676,2708, 0, 0, 0, 0, 0, 680, 680,2712, + 0, 0, 0, 0, 684,2716, 684,2720, 0, 0, 0, 0, 0, 0,2724,2728, + 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104, 104,2732,2736, 0, 0, + 304, 304, 304, 304,2740,2744, 0, 0, 0, 0, 0, 0, 0, 0, 0, 152, + 12, 12, 12,2748, 0, 0, 0, 0, 152, 12, 12, 184, 0, 0, 0, 0, + 2752, 36,2756,2760,2764,2768,2772,2776,2780, 688,2784, 688, 0, 0, 0,2788, + 12, 12, 156, 12, 12, 12, 12, 12, 12, 224, 528, 152, 152, 152, 12, 456, + 12, 12, 184, 0, 0, 0,2792, 12,2796, 12, 12, 156, 300, 348, 456, 0, + 12, 12, 12, 12, 12,2800, 192, 192, 12, 12, 12, 12, 12, 176, 156, 452, + 156, 12, 12, 12, 464, 176, 12, 12, 464, 12, 184, 156, 348, 300, 0, 0, + 12, 12, 12, 12, 12, 464, 184, 192,2804, 12, 12, 12,2808,2812,2816, 300, + 12, 556, 12, 12, 12, 12, 12, 352, 4, 4, 4, 4, 4, 4, 0, 0, + 4, 188, 4, 4, 4, 4, 4, 4, 4, 4, 188, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4,2820, 4, 4, 4, 4, 4, 4, 188, 0, 0, + 4, 188, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,2824, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 552,2828, 0, 12, 12, 12, 12, 12, 12, + 0, 0, 0, 0, 4, 4, 4, 4, 8, 8, 8, 8, 12, 12, 12, 12, + 16, 16, 16, 16, 20, 20, 20, 20, 36, 36, 36, 36, 32, 32, 32, 32, + 24, 24, 24, 24, 28, 28, 28, 28, 40, 40, 40, 40, 48, 48, 48, 48, + 44, 44, 44, 44, 56, 56, 56, 56, 52, 52, 52, 52, 60, 60, 60, 60, + 64, 64, 64, 64, 76, 76, 76, 76, 68, 68, 68, 68, 80, 80, 80, 80, + 84, 84, 84, 84, 88, 88, 88, 88, 72, 72, 72, 72, 96, 96, 96, 96, + 100, 100, 100, 100, 92, 92, 92, 92, 104, 104, 104, 104, 116, 116, 116, 116, + 120, 120, 120, 120, 124, 124, 124, 124, 108, 108, 108, 108, 132, 132, 132, 132, + 112, 112, 112, 112, 136, 136, 136, 136, 128, 128, 128, 128, 148, 148, 148, 148, + 144, 144, 144, 144, 152, 152, 152, 152, 452, 12, 12, 12, 12, 12, 12, 0, + 180, 180, 180, 180, 176, 176, 176, 176, 184, 184, 184, 184, 140, 140, 140, 140, + 12, 12, 264, 0, 72, 232, 72, 232, 12, 12, 12, 264, 4, 4, 4, 600, + 12, 12, 12, 364, 240, 240, 240, 240, 168, 168, 168, 168, 188, 188, 188, 188, + 248, 248, 248, 248, 252, 252, 252, 252, 160, 160, 160, 160, 204, 204, 204, 204, + 12, 0, 0, 0, 764, 24, 24, 24, 24, 24, 628, 12, 164, 164, 164, 164, + 236, 236, 236, 236, 156, 156, 156, 156, 292, 292, 292, 292, 300, 300, 300, 300, + 268, 268, 268, 268, 208, 208, 208, 208, 324, 324, 324, 324, 192, 192, 192, 192, + 312, 312, 312, 312, 344, 344, 344, 344, 244, 244, 244, 244, 196, 196, 196, 196, + 276, 276, 276, 276, 316, 316, 316, 316, 212, 212, 212, 212, 12, 12, 364, 0, + 216, 216, 216, 216, 220, 220, 220, 220, 256, 256, 256, 256, 476, 476, 476, 476, + 396, 396, 396, 396, 172, 172, 172, 172, 228, 228, 228, 228, 260, 260, 260, 260, + 408, 408, 408, 408, 320, 320, 320, 320, 480, 480, 480, 480, 264, 0, 0, 0, + 12, 12, 296, 0, 412, 412, 412, 412, 8, 8, 8, 812, 484, 484, 484, 484, + 416, 416, 416, 416, 272, 272, 272, 272, 304, 304, 304, 304, 488, 488, 488, 488, + 496, 496, 496, 496, 308, 308, 308, 308, 456, 456, 456, 456, 460, 460, 460, 460, + 420, 420, 420, 420, 368, 368, 368, 368, 328, 328, 328, 328, 372, 372, 372, 372, + 348, 348, 348, 348, 352, 352, 352, 352, 380, 380, 380, 380, 432, 432, 432, 432, + 200, 200, 200, 200, 280, 280, 280, 280, 284, 284, 284, 284, 436, 436, 436, 436, + 440, 440, 440, 440, 364, 0, 0, 0, 12, 264, 0, 0, 444, 444, 444, 444, + 12, 12, 0, 0, 12, 12, 768, 12, 24, 628, 24, 24, 400, 400, 400, 400, + 112, 112, 892, 112, 112, 112, 112,1048, 124,1052, 0,1056, 72, 72, 684, 636, + 684, 636, 72, 72, 136, 136, 900, 0, 340, 340, 340, 0, 156, 156, 904, 0, + 52, 52, 52,1072, 68, 688, 68, 688, 68, 772, 68, 68, 12, 648, 12, 12, + 12, 12, 12, 296, 24, 24, 24,1092, 916, 92, 92, 92, 520, 520, 520, 520, + 524, 524, 524, 524, 572, 572, 572, 572, 4, 4, 600, 0, 296, 12, 12, 12, + 108, 108, 108,1132, 576, 576, 576, 576, 536, 536, 536, 536, 492, 492, 492, 492, + 540, 540, 540, 540, 500, 500, 924, 500, 528, 528, 528, 528, 604, 604, 604, 604, + 608, 608, 608, 608, 464, 464,1144, 0, 612, 612, 612, 612, 616, 616, 616, 616, + 532, 532, 532, 532, 504, 504, 504, 504, 544, 544, 544, 544, 548, 548, 548, 548, + 552, 552, 552, 552, 588, 588, 588, 588, 376, 376, 376, 376, 352, 352,1188, 0, + 424, 424, 424, 424, 384, 384,1200, 384, 384, 384, 384, 384, 556, 556, 556, 556, + 388, 388, 388, 388, 392, 392, 392, 392, 472, 472, 472, 472, 512, 512, 512, 512, + 356, 356, 356, 356, 560, 560, 560, 560, 564, 564, 564, 564, 516, 516, 516, 516, + 28, 28, 28, 0, 24, 24, 840, 12, 24, 840,1264, 12, 68,1268, 844,1272, + 0,1276, 848, 772,1280, 68, 68, 68,1284, 144, 144, 144, 60,1288, 60, 60, + 852, 220, 220, 220, 220, 952, 852, 220, 220, 220, 952, 852, 956, 164, 164, 164, + 164, 164, 0, 0, 164, 164, 960,1292, 164,1296, 0, 0, 28, 964, 28, 968, + 28, 28, 972, 972, 968, 28,1300, 52, 52,1304, 28, 28,1308, 28, 28, 28, + 28, 28, 28, 964, 256, 256, 256,1312, 256, 256, 976,1316,1320, 0, 0, 0, + 396, 396,1324,1328, 400, 400, 400,1332, 400, 400, 400,1336, 632, 632, 632, 632, + 632, 632, 632,1340, 256, 256, 976, 0, 980, 984, 28, 28,1344, 28, 28, 28, + 1348,1352, 120, 120, 120,1356, 120, 120, 332,1360, 332, 776, 776, 332, 332, 332, + 332, 332,1364, 332,1368, 988,1372, 332, 332, 776, 776, 992, 0,1376, 0,1380, + 332, 988, 332, 332, 332, 332, 332, 992, 856, 856, 860, 996,1000, 568, 568, 568, + 568, 568, 864, 568, 864,1384,1004, 864, 860, 996,1000,1004,1388, 0, 856,1392, + 0,1396, 568, 568, 568, 860, 0, 0, 780, 780, 448, 784, 784, 448, 448, 448, + 448, 448,1008, 448,1008, 780, 868, 448, 448, 784, 784, 868,1400, 0, 0, 0, + 448,1404, 448, 448, 868, 0, 780, 448, 788, 788, 404, 792, 792, 404, 404, 404, + 404, 404,1012, 404,1012, 788,1016, 404, 404, 792, 792,1016, 0, 788, 0,1408, + 404,1412, 404, 404, 404, 404, 0, 0, 680,1416, 796, 680, 872, 800,1420, 872, + 876,1020, 796, 680, 284, 284, 800, 680, 796, 680, 872, 800,1020, 876, 0, 0, + 0, 680, 284, 284, 284, 284, 796, 0, 288, 288, 288, 728, 728, 288, 288, 288, + 288, 288, 728, 288, 288, 288, 880, 288, 288, 728, 728, 880, 0,1424,1428, 880, + 288,1432, 288, 288, 0,1436, 288, 288, 360, 360, 360, 732, 732, 360, 360, 360, + 360, 360, 732, 360, 360,1024,1028, 360, 360, 732, 732,1028, 0,1440, 0,1444, + 360,1448, 360, 360,1024, 0, 0, 0, 172, 172, 172, 804, 804, 172, 172, 172, + 172, 804, 804, 172, 0, 172, 172, 172, 172,1452, 172, 172, 884, 884, 224, 224, + 224,1032, 888, 224, 224, 224, 224, 224,1456, 224, 224,1460, 224,1032,1464,1468, + 224,1472, 224, 224, 0, 888, 224, 224, 888,1036, 0, 0,1476, 228, 228, 228, + 228, 228,1480, 736, 228, 228, 228, 0,1484,1488,1040, 336, 336, 336, 336, 336, + 336,1492, 336, 336, 336, 336, 336,1044, 336,1496, 336,1040, 336, 336,1044, 336, + 112, 112, 112,1500, 892, 112, 112, 112, 112,1504,1508, 0, 124, 124,1512, 124, + 72, 232, 684, 636, 684, 636, 72, 232, 72, 232, 72, 72, 72, 72, 232, 808, + 72, 72, 72,1516, 72, 72, 636, 0, 116,1060, 116,1060, 640, 640, 640, 640, + 640, 640, 640,1520, 236, 236,1524,1528, 236, 236,1532, 0, 740, 740, 740, 740, + 740,1536, 0,1540, 744, 744, 744, 744, 744,1544, 0, 0, 748, 748, 748, 748, + 748, 0, 0, 0, 896, 896, 896,1064,1064, 0, 0, 0, 136, 136, 136, 900, + 1548,1552, 128, 128, 128, 128,1556, 0, 128, 128,1068, 0, 128, 128,1560, 0, + 40,1564, 0, 0, 340, 340, 340, 340, 340, 340, 340,1568,1572, 340, 340, 340, + 596, 596, 596, 596, 596, 596, 596,1576, 596,1580, 0, 0, 260, 260, 260, 0, + 260, 260,1584, 0, 260, 260,1588,1592, 644, 644, 644, 644, 644, 644, 644,1596, + 156, 156, 156,1600, 156, 156, 156,1604, 156, 156, 156, 904, 52, 52, 52, 0, + 148, 148, 148,1608, 408, 0, 0, 408, 320, 320,1612, 320, 320, 320,1616,1620, + 60, 60,1624, 0, 124, 124,1628,1632, 292, 292, 0, 0,1076, 52, 52, 52, + 1636, 52, 908,1640, 12, 908,1644, 0, 24,1080,1648, 24, 24, 24, 24,1652, + 1656,1080,1660, 24, 24, 24,1664, 24, 24, 24, 24,1668, 68, 68,1084,1084, + 68, 68, 68, 688, 68, 844, 68,1672, 844, 772, 68, 848, 12, 12, 12, 912, + 1676, 12, 12,1088,1680, 0, 0, 0, 12,1684,1096, 12, 768, 12, 12, 12, + 12, 12, 12, 768, 24, 24, 840, 0, 12, 692, 12, 12, 144, 0,1688, 144, + 412, 412, 0,1100,1692, 0, 0,1100, 72, 232, 0, 0, 4, 4,1696, 4, + 4, 0, 0, 0, 4, 600, 0, 0, 12,1700, 12, 12,1704, 4,1708,1712, + 12, 12, 4, 12,1716, 80, 80, 80, 80,1104,1720,1724, 92, 92,1108, 916, + 0,1728, 300, 300,1732, 8, 8, 8, 12, 264, 0, 736, 8, 8, 8,1736, + 92, 92, 92,1108, 92, 92, 12, 12, 36, 36, 36,1740, 36,1744, 0, 0, + 88, 88, 88, 0, 48, 48, 0, 0,1096, 24, 24, 24, 24, 24,1088, 24, + 1112, 24, 24, 24, 520, 520, 520,1748, 416, 416, 0, 0, 268,1116, 0,1752, + 268, 268,1116, 0, 524, 524, 524,1756, 572, 0, 0,1760, 8, 8, 8,1120, + 208, 208, 208,1764, 208, 208,1768,1772, 100, 100, 100,1776, 272,1780, 0, 0, + 272, 272, 272,1124, 272, 272,1124, 272,1784, 0, 0, 0, 0, 0,1788, 324, + 304,1792, 0, 0, 808, 232, 808, 232, 808, 232, 0, 0, 24, 24, 628, 24, + 24,1796,1800, 0, 304, 304, 304,1128, 304, 304,1128, 0, 8, 0, 0, 0, + 8, 812,1804, 8, 8, 8, 8, 0, 24, 816, 0, 0,1808, 220, 0, 956, + 164, 960, 164,1812,1816,1820, 164, 164, 28, 28, 28,1824, 52, 52, 52,1828, + 12, 296, 12, 0, 28,1832, 28, 28, 28, 28, 28,1836, 12,1840, 92, 92, + 92, 92, 92,1844, 820, 8, 820, 8, 820, 8, 820,1120, 12, 296, 12, 296, + 0, 0, 452, 264, 108, 108, 108,1848, 108, 920, 108, 108, 108, 108, 920,1852, + 108, 108, 920, 0, 296, 736, 12, 12, 12, 736, 12, 12, 68, 68, 68, 848, + 1856, 0, 0, 0, 12, 12, 12,1860, 652, 652, 652, 652, 652, 652, 652,1864, + 1868, 0, 0, 0, 908, 12, 12, 12, 576, 0, 0,1872, 696, 696, 696, 696, + 696, 696,1876, 0, 536, 536,1880, 0, 656, 656, 656, 656, 656, 656, 656,1884, + 492, 0, 492, 492, 492,1888, 0, 0, 580, 580, 580, 580, 580, 580, 580,1136, + 580, 580,1136, 0, 308, 0, 308, 308, 308, 308, 308, 0, 540, 540, 0, 0, + 456, 0, 0,1892, 924, 824, 500, 500, 824, 500, 500, 500, 824, 500, 824,1896, + 460, 0, 0, 0, 84,1900, 0, 0, 84,1904, 0, 0, 84, 84, 0, 0, + 24,1908, 24, 24,1912, 24, 816, 0, 528,1916,1920, 528, 528,1924,1928,1932, + 660, 660, 660, 660, 660,1936, 660, 660, 584, 584, 584, 584, 584, 584, 584,1940, + 0,1944, 584, 584, 752, 752, 752, 752,1948,1952,1956, 752, 664, 664, 664, 664, + 664, 664, 664,1960, 700, 700, 700, 700, 700, 700,1964,1968, 704, 704, 704, 704, + 704, 704,1972, 0, 192, 192, 0, 192,1976, 192, 192, 192, 464,1980, 0, 464, + 464,1140,1140, 464, 464, 464, 464, 464, 464,1984,1988,1992, 532,1148,1996, 532, + 532,1148, 0, 0, 420,2000,2004, 420, 668, 668, 668, 668, 668,2008, 668, 668, + 708, 708, 708, 708,2012, 0, 708, 708, 756, 756, 756, 756,2016, 0,1152,2020, + 0, 0,1152, 756, 312, 312,2024, 0,1156, 0, 0, 0,1156, 0,2028, 180, + 504, 504, 0, 0, 504, 504,2032, 0, 368,1160,2036, 368, 368,1160, 0,2040, + 28, 28, 28, 828, 544, 544,2044,1164,1164, 0, 0, 0,1168, 28, 0, 0, + 28, 28,2048, 0, 0, 0,1168, 28, 548, 548, 0, 0, 552, 552,2052, 0, + 712, 712, 712, 712, 712, 712,2056, 0, 672, 672, 672, 672, 672, 672, 672, 0, + 760, 760, 760, 760, 760,2060, 0, 0, 168, 168, 168,1172,2064, 168, 168, 168, + 168,1172, 0,2068,2072, 0, 0,2076, 620, 620, 620, 620, 620, 620,2080, 0, + 620, 620,2084, 0, 328,2088, 328, 328, 328, 328, 0, 0, 588,2092, 0, 0, + 884, 224, 224, 224, 224,1036, 0, 0,2096, 372, 372, 372,2100, 0, 0, 0, + 716,2104,2108,1176, 716, 716, 716,1176, 716, 716,2112, 0, 348, 348,2116, 0, + 348, 348,2120, 0, 468, 928, 468, 832, 832, 468, 468, 468, 468, 468,1180, 468, + 1180, 928,2124, 468, 468, 832, 832,2128, 932,2132, 0, 928, 468,2136, 468, 932, + 468, 932, 0, 0, 376, 376, 936,2140, 376, 936, 376, 376,2144,2148,2152, 376, + 376, 936,2156, 0,2160, 0, 0, 0, 188, 188, 188,2164,2168, 0, 0, 0, + 244, 244, 0, 0, 244, 244,2172, 0, 196,1184, 196, 196, 196, 196, 196,1184, + 276,2176, 0, 0, 276, 276,2180, 0, 128, 128, 128,1068, 100, 0, 0, 0, + 424, 424,1192,2184, 424, 424, 424, 0, 424,1192, 0, 0, 380, 380, 380, 0, + 2188, 0, 0,2192, 428,1196,2196, 428, 428,2200, 428, 428, 428, 428, 428, 428, + 428,2204,2208, 428, 428,1196, 0, 0, 428, 428,2212, 0, 384,2216, 0, 0, + 316, 316, 0, 0,2220, 0, 0, 0, 432, 432,2224, 0, 120, 120,2228, 0, + 176, 176, 0, 0,1204, 0, 0, 0, 556, 556,1204, 0, 200, 200,2232, 200, + 200,2236, 200, 200, 200,2240, 0, 0, 200, 200, 200,2244,2248, 388, 388, 388, + 388, 388,2252, 388, 388,2256, 0, 0, 392,1208,1212, 392, 392,1208,2260,1212, + 392, 392, 0, 0, 392, 392,2264, 0, 508,1216,2268, 508, 508, 508, 508, 508, + 508, 508, 508,2272,1216, 508,2276, 0, 508, 508,2280, 0, 472, 472, 472, 0, + 472, 472,2284, 0, 720, 720, 720, 720, 720, 720,2288, 0,2292, 280, 280, 280, + 280, 280,1220,2296, 280, 280,1220, 0,2300, 0, 0, 0, 800, 0, 0, 876, + 32, 32,2304, 0, 32, 32, 32,2308, 32,2312, 0, 0, 32, 0, 0, 0, + 2316, 0, 0, 0, 20,2320, 0, 0, 20, 20,2324, 0, 56,2328, 0, 0, + 436, 436,2332, 0, 48, 48,2336, 0, 592, 592, 592, 592, 592, 592, 592,2340, + 592, 592,2344,2348, 212, 212, 212,2352, 212, 212,2356, 0, 624, 624, 624, 624, + 624, 624, 624,1224, 624,1224, 0, 0, 160,2360, 0, 0, 160, 160,1228, 160, + 1228, 160, 160, 160, 160, 160, 0,2364, 440, 440,2368, 0, 204, 204,2372, 0, + 512, 512,2376, 512, 512, 0, 0, 0, 132, 132,2380,1232, 132, 132, 0,1232, + 2384,2388, 0, 0, 4,1236, 0, 0, 64,2392, 0, 0, 0, 0, 0,2396, + 16, 16, 16,1240,1240, 0, 0, 0, 92,2400, 92,2404,2408, 80, 80, 80, + 2412, 0, 0, 0,2416, 0, 0, 0,1104,2420, 0, 0, 0, 92, 0, 0, + 76, 76, 76, 0, 140, 140,2424, 0, 140, 140, 140,1244, 140, 140,1244, 0, + 140, 140,2428, 140, 12, 0, 692, 12, 52,2432, 0, 0, 12, 296, 452, 12, + 12,1248, 912, 12, 12, 12,1248, 52,1076,2436, 52, 12, 12, 12,2440, 912, + 68, 688, 0, 0, 12, 296, 0, 0, 12, 12, 12, 648,2444,2448, 452, 648, + 12, 12, 940, 452, 12, 452, 12, 12, 12, 940, 296, 452, 12, 648, 12, 648, + 12, 12, 940, 296, 12,2452, 692, 12, 648, 12, 12, 12, 12, 264, 12, 12, + 12, 12, 12, 692, 44, 44, 44, 0, 0, 0,2456, 44,2460, 44, 44, 44, + 24, 24, 24, 816, 0,1112, 816, 0, 152,1252, 152, 152, 152, 152,2464, 152, + 2468,2472,1252, 0, 60, 60, 60,2476, 0, 0, 0,2480, 356, 356, 356,2484, + 356, 356, 356,1256, 356, 356,1256,2488, 676, 676, 676, 676, 676, 676, 676,2492, + 444, 444,2496,2500, 560, 560,2504, 0, 564, 564,2508,2512, 516, 516, 516,2516, + 516,2520, 0,2524, 72, 232, 72,2528, 72, 72, 72, 232, 104,2532, 104, 104, + 104,2536, 0, 0, 216, 216, 216, 0, 216, 216,2540,2544, 12, 364, 0, 0, + 28, 724, 28, 28, 944, 948, 724, 28, 828, 28, 836, 0,2548, 984, 836, 724, + 944, 948, 836, 836, 944, 948, 828, 28, 828, 28, 724,2552, 28, 28,1260, 28, + 724, 724,1260, 28, 980, 0, 0, 0, 0, 692, 12, 12,2556, 0, 0, 0, + 12, 12, 364, 12, 12, 12, 296, 692, 12, 296, 364, 452, 12, 12, 12,2560, + 12, 12, 296, 736,2564, 0, 0, 0, 4, 4,1236, 0,2568, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, + 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040, + 1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0,1124,1125,1126, + 1127,1131,1133, 0,1147,1154,1155,1156,1161,1187,1188,1189,1193, 0,1219,1226, + 1227,1228,1229,1233, 0, 0,1267,1268,1269,1273,1298, 0,1303, 943,1128, 944, + 1129, 954,1139, 958,1143, 959,1144, 960,1145, 961,1146, 964,1149, 0, 0, 973, + 1158, 974,1159, 975,1160, 983,1168, 978,1163, 988,1173, 990,1175, 991,1176, 993, + 1178, 994,1179, 0, 0,1004,1190,1005,1191,1006,1192,1014,1199,1007, 0, 0, + 0,1016,1201,1020,1206, 0,1022,1208,1025,1211,1023,1209, 0, 0, 0, 0, + 1032,1218,1037,1223,1035,1221, 0, 0, 0,1044,1230,1045,1231,1049,1235, 0, + 0,1058,1244,1064,1250,1060,1246,1066,1252,1067,1253,1072,1258,1069,1255,1077, + 1264,1074,1261, 0, 0,1083,1270,1084,1271,1085,1272,1088,1275,1089,1276,1096, + 1283,1103,1290,1111,1299,1115,1118,1307,1120,1309,1121,1310, 0,1053,1239, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1093,1280, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1446,1458,1468,1476,1480,1486,1517, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 949,1134,1010,1195,1050,1236, + 1090,1277,1341,1368,1340,1367,1342,1369,1339,1366, 0,1320,1347,1418,1419,1323, + 1350, 0, 0, 992,1177,1018,1204,1055,1241,1416,1417,1415,1424,1202, 0, 0, + 0, 987,1172, 0, 0,1031,1217,1321,1348,1322,1349,1338,1365, 950,1135, 951, + 1136, 979,1164, 980,1165,1011,1196,1012,1197,1051,1237,1052,1238,1061,1247,1062, + 1248,1091,1278,1092,1279,1071,1257,1076,1263, 0, 0, 997,1182, 0, 0, 0, + 0, 0, 0, 945,1130, 982,1167,1337,1364,1335,1362,1046,1232,1422,1423,1113, + 1301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, + 10,1425, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, + 0, 0,1314,1427, 5,1434,1438,1443, 0,1450, 0,1455,1461,1514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1489,1503,1494,1500,1508, 0, 0, 0, 0,1520,1521, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0,1525, 0, 0, 0,1522, - 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0,1547, 0, 0, 0,1567, - 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, 0, 0, 0,1568,1569, - 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524,1546, 0, 0,1527,1549, - 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533,1555,1535,1557,1537,1559, - 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541,1563,1542,1564, 0, 0, - 1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606,1607,1609,1608,1610, 0, - 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1612, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1446,1458,1468,1476,1480,1486,1517, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, 0,1624, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1489,1503,1494,1500,1508, 0, 0, 0, 0, + 1520,1521, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1526,1528, 0, + 1525, 0, 0, 0,1522, 0, 0, 0, 0,1536,1532,1539, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1534, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1556, 0, 0, 0, 0, 0, 0,1548,1550, 0, + 1547, 0, 0, 0,1567, 0, 0, 0, 0,1558,1554,1561, 0, 0, 0, 0, + 0, 0, 0,1568,1569, 0, 0, 0, 0, 0, 0, 0, 0, 0,1529,1551, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1523,1545,1524, + 1546, 0, 0,1527,1549, 0, 0,1570,1571,1530,1552,1531,1553, 0, 0,1533, + 1555,1535,1557,1537,1559, 0, 0,1572,1573,1544,1566,1538,1560,1540,1562,1541, + 1563,1542,1564, 0, 0,1543,1565, 0, 0, 0, 0, 0, 0, 0, 0,1606, + 1607,1609,1608,1610, 0, 0, 0, 0, 0, 0, 0, 0, 0,1613, 0,1611, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0,1634, 0, 0,1635, 0, + 1612, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1620, 0, 0, 0, 0, 0, 0, 0,1623, 0, + 0,1624, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1614,1615,1616,1617,1618,1619,1621,1622, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1628,1629, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1625,1626, 0,1627, 0, 0, 0, + 1634, 0, 0,1635, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, + 0,1641, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, + 0, 0, 0, 0,1648,1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0, + 1659, 0, 0, 0, 0, 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, + 0, 0, 0, 0,1662, 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1664, 0,1665,1673, 0,1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1630,1631,1632, 0, 0,1633, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1639, 0, 0,1638,1640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1636,1637, 0, 0, 0, 0, 0, 0,1641, 0, 0, 0, + 1668, 0, 0, 0, 0, 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, + 0, 0, 0, 0,1671, 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1675, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0,1676, 0,1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1642,1644,1643, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1645, 0, 0, 0, 0, 0, 0, 0,1646, 0, 0, 0, 0, 0, 0,1648, - 1649, 0,1647,1650, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1651,1653,1652, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1654, 0,1655,1657,1656, 0, 0, 0, 0,1659, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1660, 0, 0, 0, 0,1661, 0, 0, 0, 0,1662, - 0, 0, 0, 0,1663, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1658, 0, 0, 0, 0, 0, 0, 0, 0, 0,1664, 0,1665,1673, 0, - 1674, 0, 0, 0, 0, 0, 0, 0, 0,1666, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1668, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1669, 0, 0, 0, 0,1670, 0, 0, 0, 0,1671, - 0, 0, 0, 0,1672, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1675, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1676, 0, - 1677, 0,1678, 0,1679, 0,1680, 0, 0, 0,1681, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0, + 1686, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955, + 1140, 956,1141, 957,1142,1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967, + 1152,1378,1380,1379,1381, 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995, + 1180, 998,1183, 996,1181, 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019, + 1205,1021,1207,1024,1210,1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030, + 1216,1034,1220,1036,1222,1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383, + 1385,1056,1242,1057,1243,1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070, + 1256,1386,1387,1388,1389,1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095, + 1282,1098,1285,1097,1284,1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102, + 1289,1105,1292,1104,1291,1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122, + 1311,1123,1312,1186,1260,1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947, + 1132,1317,1344,1316,1343,1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373, + 1377,1372,1376,1694,1696, 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328, + 1355,1327,1354,1697,1698,1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330, + 1357,1333,1360,1332,1359,1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399, + 1404,1094,1281,1087,1274,1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109, + 1297,1117,1306,1116,1304,1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701, + 1705,1702,1706,1703,1707,1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729, + 1731,1730,1732, 0, 0,1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737, + 1741,1738,1742,1739,1743,1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765, + 1768,1766,1769,1767,1770,1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777, + 1779,1778,1780, 0, 0,1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785, + 1788,1786,1789,1787,1790, 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794, + 1798,1795,1799,1796,1800,1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, + 22,1479, 23,1485, 24,1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709, + 1710,1711,1712,1713,1714,1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745, + 1746,1747,1748,1749,1750,1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802, + 1803,1804,1805,1806,1807,1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822, + 1474,1465, 0,1473,1825,1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823, + 1484,1466, 0,1483,1829,1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, + 19, 0, 0,1492,1515,1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, + 25,1497,1498,1506,1518,1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824, + 1512,1519, 0,1511,1830,1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1682, 0,1683, 0, 0,1684,1685, 0,1686, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 953,1138, 955,1140, 956,1141, 957,1142, - 1324,1351, 963,1148, 965,1150, 968,1153, 966,1151, 967,1152,1378,1380,1379,1381, - 984,1169, 985,1170,1420,1421, 986,1171, 989,1174, 995,1180, 998,1183, 996,1181, - 999,1184,1000,1185,1015,1200,1329,1356,1017,1203,1019,1205,1021,1207,1024,1210, - 1687,1688,1027,1213,1026,1212,1028,1214,1029,1215,1030,1216,1034,1220,1036,1222, - 1039,1225,1038,1224,1334,1361,1336,1363,1382,1384,1383,1385,1056,1242,1057,1243, - 1059,1245,1063,1249,1689,1690,1065,1251,1068,1254,1070,1256,1386,1387,1388,1389, - 1691,1692,1073,1259,1075,1262,1079,1266,1078,1265,1095,1282,1098,1285,1097,1284, - 1390,1391,1392,1393,1099,1286,1100,1287,1101,1288,1102,1289,1105,1292,1104,1291, - 1106,1294,1107,1295,1108,1296,1114,1302,1119,1308,1122,1311,1123,1312,1186,1260, - 1293,1305, 0,1394, 0, 0, 0, 0, 952,1137, 947,1132,1317,1344,1316,1343, - 1319,1346,1318,1345,1693,1695,1371,1375,1370,1374,1373,1377,1372,1376,1694,1696, - 981,1166, 977,1162, 972,1157,1326,1353,1325,1352,1328,1355,1327,1354,1697,1698, - 1009,1194,1013,1198,1054,1240,1048,1234,1331,1358,1330,1357,1333,1360,1332,1359, - 1699,1700,1396,1401,1395,1400,1398,1403,1397,1402,1399,1404,1094,1281,1087,1274, - 1406,1411,1405,1410,1408,1413,1407,1412,1409,1414,1109,1297,1117,1306,1116,1304, - 1112,1300, 0, 0, 0, 0, 0, 0,1471,1472,1701,1705,1702,1706,1703,1707, - 1430,1431,1715,1719,1716,1720,1717,1721,1477,1478,1729,1731,1730,1732, 0, 0, - 1435,1436,1733,1735,1734,1736, 0, 0,1481,1482,1737,1741,1738,1742,1739,1743, - 1439,1440,1751,1755,1752,1756,1753,1757,1490,1491,1765,1768,1766,1769,1767,1770, - 1447,1448,1771,1774,1772,1775,1773,1776,1495,1496,1777,1779,1778,1780, 0, 0, - 1451,1452,1781,1783,1782,1784, 0, 0,1504,1505,1785,1788,1786,1789,1787,1790, - 0,1459, 0,1791, 0,1792, 0,1793,1509,1510,1794,1798,1795,1799,1796,1800, - 1462,1463,1808,1812,1809,1813,1810,1814,1467, 21,1475, 22,1479, 23,1485, 24, - 1493, 27,1499, 28,1507, 29, 0, 0,1704,1708,1709,1710,1711,1712,1713,1714, - 1718,1722,1723,1724,1725,1726,1727,1728,1740,1744,1745,1746,1747,1748,1749,1750, - 1754,1758,1759,1760,1761,1762,1763,1764,1797,1801,1802,1803,1804,1805,1806,1807, - 1811,1815,1816,1817,1818,1819,1820,1821,1470,1469,1822,1474,1465, 0,1473,1825, - 1429,1428,1426, 12,1432, 0, 26, 0, 0,1315,1823,1484,1466, 0,1483,1829, - 1433, 13,1437, 14,1441,1826,1827,1828,1488,1487,1513, 19, 0, 0,1492,1515, - 1445,1444,1442, 15, 0,1831,1832,1833,1502,1501,1516, 25,1497,1498,1506,1518, - 1457,1456,1454, 17,1453,1313, 11, 3, 0, 0,1824,1512,1519, 0,1511,1830, - 1449, 16,1460, 18,1464, 4, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, - 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, 0,1840, 0, 0, 0, - 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, 0,1843, 0,1844, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, 0,1846, 0, 0,1847, - 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, 0,1853,1854, 0, 0, - 1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, 0,1861,1862, 0, 0, - 1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865,1866, 0, 0, 0, 0, - 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, 0,1881, 0,1882, 0, - 1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, 0, 0,1889, 0,1890, - 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0,1894,1895, 0,1896,1897, - 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, 0,1876, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, 0,1908, 0,1909, 0, - 1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, 0, 0,1916, 0,1917, - 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0,1921,1922, 0,1923,1924, - 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, 0,1903, 0, 0,1929, - 1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, 715, 455, 103, 186, 825, - 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, 738, 411, 434, 474, 500, - 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, 658, 692, 344, 618, 679, - 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, 527, 606, 660, 665, 722, - 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, 299, 573, 612, 487, 540, - 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, 613, 149, 148, 560, 589, - 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, 174, 542, 120, 307, 101, - 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, 349, 632, 355, 517, 110, - 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, 374, 463, 543, 763, 801, - 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, 476, 509, 558, 591, 610, - 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, 311, 353, 423, 572, 494, - 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, 782, 788, 117, 557, 748, - 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, 737, 823, 380, 765, 161, - 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, 769, 122, 273, 446, 727, - 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, 432, 501, 519, 599, 684, - 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, 813, 397, 444, 619, 566, - 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, 578, 256, 435, 383, 729, - 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, 0, 0, 150, 493, 525, - 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, 0, 0, 735, 743, 0, - 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, 166, 169, 177, 207, 213, - 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, 381, 404, 441, 448, 458, - 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, 555, 561, 564, 569, 591, - 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, 706, 716, 717, 733, 735, - 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, 115, 129, 138, 165, 171, - 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, 296, 303, 308, 319, 325, - 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, 389, 393, 421, 424, 438, - 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, 514, 521, 522, 525, 526, - 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, 637, 647, 674, 691, 693, - 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, 736, 747, 754, 770, 777, - 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, 65, 66, 883, 892, 916, - 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579,1580,1581,1583,1584, 0, - 1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0,1595,1596, 0,1598,1599, - 1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,1936, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1937, 0, 0, 0, + 0, 0, 0, 20, 0, 0, 0, 2, 6, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1834,1835, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1836, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1837,1839,1838, 0, 0, 0, + 0,1840, 0, 0, 0, 0,1841, 0, 0,1842, 0, 0, 0, 0, 0, 0, + 0,1843, 0,1844, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1845, 0, + 0,1846, 0, 0,1847, 0,1848, 0, 0, 0, 0, 0, 0, 937, 0,1850, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1849, 936, 938,1851,1852, 0, + 0,1853,1854, 0, 0,1855,1856, 0, 0, 0, 0, 0, 0,1857,1858, 0, + 0,1861,1862, 0, 0,1863,1864, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1867,1868,1869,1870,1859,1860,1865, + 1866, 0, 0, 0, 0, 0, 0,1871,1872,1873,1874, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1875, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1877, 0,1878, 0,1879, 0,1880, + 0,1881, 0,1882, 0,1883, 0,1884, 0,1885, 0,1886, 0,1887, 0,1888, + 0, 0,1889, 0,1890, 0,1891, 0, 0, 0, 0, 0, 0,1892,1893, 0, + 1894,1895, 0,1896,1897, 0,1898,1899, 0,1900,1901, 0, 0, 0, 0, 0, + 0,1876, 0, 0, 0, 0, 0, 0, 0, 0, 0,1902, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0,1904, 0,1905, 0,1906, 0,1907, + 0,1908, 0,1909, 0,1910, 0,1911, 0,1912, 0,1913, 0,1914, 0,1915, + 0, 0,1916, 0,1917, 0,1918, 0, 0, 0, 0, 0, 0,1919,1920, 0, + 1921,1922, 0,1923,1924, 0,1925,1926, 0,1927,1928, 0, 0, 0, 0, 0, + 0,1903, 0, 0,1929,1930,1931,1932, 0, 0, 0,1933, 0, 710, 385, 724, + 715, 455, 103, 186, 825, 825, 242, 751, 205, 241, 336, 524, 601, 663, 676, 688, + 738, 411, 434, 474, 500, 649, 746, 799, 108, 180, 416, 482, 662, 810, 275, 462, + 658, 692, 344, 618, 679, 293, 388, 440, 492, 740, 116, 146, 168, 368, 414, 481, + 527, 606, 660, 665, 722, 781, 803, 809, 538, 553, 588, 642, 758, 811, 701, 233, + 299, 573, 612, 487, 540, 714, 779, 232, 267, 412, 445, 457, 585, 594, 766, 167, + 613, 149, 148, 560, 589, 648, 768, 708, 345, 411, 704, 105, 259, 313, 496, 518, + 174, 542, 120, 307, 101, 430, 372, 584, 183, 228, 529, 650, 697, 424, 732, 428, + 349, 632, 355, 517, 110, 135, 147, 403, 580, 624, 700, 750, 170, 193, 245, 297, + 374, 463, 543, 763, 801, 812, 815, 162, 384, 420, 730, 287, 330, 337, 366, 459, + 476, 509, 558, 591, 610, 726, 652, 734, 759, 154, 163, 198, 473, 683, 697, 292, + 311, 353, 423, 572, 494, 113, 217, 259, 280, 314, 499, 506, 603, 608, 752, 778, + 782, 788, 117, 557, 748, 774, 320, 109, 126, 260, 265, 373, 411, 479, 523, 655, + 737, 823, 380, 765, 161, 395, 398, 438, 451, 502, 516, 537, 583, 791, 136, 340, + 769, 122, 273, 446, 727, 305, 322, 400, 496, 771, 155, 190, 269, 377, 391, 406, + 432, 501, 519, 599, 684, 687, 749, 776, 175, 452, 191, 480, 510, 659, 772, 805, + 813, 397, 444, 619, 566, 568, 575, 491, 471, 707, 111, 636, 156, 153, 288, 346, + 578, 256, 435, 383, 729, 680, 767, 694, 295, 128, 210, 0, 0, 227, 0, 379, + 0, 0, 150, 493, 525, 544, 551, 552, 556, 783, 576, 604, 0, 661, 0, 703, + 0, 0, 735, 743, 0, 0, 0, 793, 794, 795, 808, 741, 773, 118, 127, 130, + 166, 169, 177, 207, 213, 215, 226, 229, 268, 270, 317, 327, 329, 335, 369, 375, + 381, 404, 441, 448, 458, 477, 484, 503, 539, 545, 547, 546, 548, 549, 550, 554, + 555, 561, 564, 569, 591, 593, 595, 598, 607, 620, 625, 625, 651, 690, 695, 705, + 706, 716, 717, 733, 735, 777, 786, 790, 315, 869, 623, 0, 0, 102, 145, 134, + 115, 129, 138, 165, 171, 207, 202, 206, 212, 227, 231, 240, 243, 250, 254, 294, + 296, 303, 308, 319, 325, 321, 329, 326, 335, 341, 357, 360, 362, 370, 379, 388, + 389, 393, 421, 424, 438, 456, 454, 458, 465, 477, 535, 485, 490, 493, 507, 512, + 514, 521, 522, 525, 526, 528, 533, 532, 541, 565, 569, 574, 586, 591, 597, 607, + 637, 647, 674, 691, 693, 695, 698, 703, 699, 705, 704, 702, 706, 709, 717, 728, + 736, 747, 754, 770, 777, 783, 784, 786, 787, 790, 802, 825, 848, 847, 857, 55, + 65, 66, 883, 892, 916, 822, 824, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1586, 0,1605, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1602,1603,1934,1935,1574,1575,1576,1577,1579, + 1580,1581,1583,1584, 0,1585,1587,1588,1589,1591, 0,1592, 0,1593,1594, 0, + 1595,1596, 0,1598,1599,1600,1601,1604,1582,1578,1590,1597, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,1936, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,1937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1938, 0,1939, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1940, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1943,1944, 0, 0, 0, 0, 0, 0, + 1945, 0,1946, 0, 0, 0, 0, 0, 0, 0, 0,1947, 0, 0,1948, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1938, 0,1939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1940, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1941,1942, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1943,1944, 0, 0, 0, 0, 0, 0,1945, 0,1946, 0, 0, - 0, 0, 0, 0, 0, 0,1947, 0, 0,1948, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1950, 0,1949, - 1951, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1953,1952, 0,1954, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0,1955,1956, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1957, 0, 0, 0, 0, 0, 0, 0, 0,1958,1961,1959,1965,1960,1962,1964, - 1963, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1967,1966,1968, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0,1969,1970,1971,1972,1973,1974,1975, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0,1976,1977,1978,1980,1979,1981, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, 826, 114, 118, 119, 121, - 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, 35, 133, 139, 829, 142, - 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, 160, 38, 165, 166, 169, - 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, 182, 833, 468, 184, 185, - 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, 201, 203, 204, 204, 206, - 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, 222, 223, 220, 225, 224, - 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, 248, 249, 246, 251, 39, - 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, 263, 301, 264, 41, 266, - 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, 42, 283, 284, 285, 286, - 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, 621, 300, 300, 45, 852, - 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, 317, 846, 318, 323, 324, - 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, 339, 342, 343, 347, 351, - 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, 363, 365, 367, 364, 50, - 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, 141, 387, 382, 614, 78, - 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, 858, 405, 401, 407, 55, - 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, 419, 422, 424, 425, 861, - 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, 439, 442, 443, 864, 436, - 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, 466, 465, 464, 59, 467, - 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, 871, 488, 489, 872, 873, - 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, 62, 513, 874, 515, 875, - 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, 881, 882, 530, 531, 531, - 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, 549, 886, 887, 556, 559, - 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, 571, 72, 891, 577, 73, - 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, 896, 76, 897, 600, 898, - 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, 616, 79, 617, 252, 902, - 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, 629, 630, 631, 633, 904, - 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, 645, 905, 907, 906, 81, - 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, 664, 665, 666, 667, 669, - 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, 681, 682, 912, 685, 686, - 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, 915, 712, 713, 718, 719, - 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, 742, 744, 920, 745, 753, - 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, 775, 279, 780, 923, 925, - 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, 95, 796, 797, 798, 800, - 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, 932, 933, 814, 100, 816, - 817, 818, 819, 820, 821, 935, 0, 0, + 0, 0,1950, 0,1949,1951, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1953,1952, 0,1954, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0,1955,1956, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1957, 0, 0, 0, 0, 0, 0, 0, 0,1958,1961, + 1959,1965,1960,1962,1964,1963, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0,1967,1966,1968, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,1969,1970,1971,1972,1973, + 1974,1975, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,1976,1977,1978,1980,1979,1981, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106, 104, 107, + 826, 114, 118, 119, 121, 123, 124, 127, 125, 34, 830, 130, 131, 132, 137, 827, + 35, 133, 139, 829, 142, 143, 112, 144, 145, 924, 151, 152, 37, 157, 158, 159, + 160, 38, 165, 166, 169, 171, 172, 173, 174, 176, 177, 178, 179, 181, 182, 182, + 182, 833, 468, 184, 185, 834, 187, 188, 189, 196, 192, 194, 195, 197, 199, 200, + 201, 203, 204, 204, 206, 208, 209, 211, 218, 213, 219, 214, 216, 153, 234, 221, + 222, 223, 220, 225, 224, 230, 835, 235, 236, 237, 238, 239, 244, 836, 837, 247, + 248, 249, 246, 251, 39, 40, 253, 255, 255, 838, 257, 258, 259, 261, 839, 262, + 263, 301, 264, 41, 266, 270, 272, 271, 841, 274, 842, 277, 276, 278, 281, 282, + 42, 283, 284, 285, 286, 43, 843, 44, 289, 290, 291, 293, 934, 298, 845, 845, + 621, 300, 300, 45, 852, 894, 302, 304, 46, 306, 309, 310, 312, 316, 48, 47, + 317, 846, 318, 323, 324, 325, 324, 328, 329, 333, 331, 332, 334, 335, 336, 338, + 339, 342, 343, 347, 351, 849, 350, 348, 352, 354, 359, 850, 361, 358, 356, 49, + 363, 365, 367, 364, 50, 369, 371, 851, 376, 386, 378, 53, 381, 52, 51, 140, + 141, 387, 382, 614, 78, 388, 389, 390, 394, 392, 856, 54, 399, 396, 402, 404, + 858, 405, 401, 407, 55, 408, 409, 410, 413, 859, 415, 56, 417, 860, 418, 57, + 419, 422, 424, 425, 861, 840, 862, 426, 863, 429, 431, 427, 433, 437, 441, 438, + 439, 442, 443, 864, 436, 449, 450, 58, 454, 453, 865, 447, 460, 866, 867, 461, + 466, 465, 464, 59, 467, 470, 469, 472, 828, 475, 868, 478, 870, 483, 485, 486, + 871, 488, 489, 872, 873, 495, 497, 60, 498, 61, 61, 504, 505, 507, 508, 511, + 62, 513, 874, 515, 875, 518, 844, 520, 876, 877, 878, 63, 64, 528, 880, 879, + 881, 882, 530, 531, 531, 533, 66, 534, 67, 68, 884, 536, 538, 541, 69, 885, + 549, 886, 887, 556, 559, 70, 561, 562, 563, 888, 889, 889, 567, 71, 890, 570, + 571, 72, 891, 577, 73, 581, 579, 582, 893, 587, 74, 590, 592, 596, 75, 895, + 896, 76, 897, 600, 898, 602, 605, 607, 899, 900, 609, 901, 611, 853, 77, 615, + 616, 79, 617, 252, 902, 903, 854, 855, 621, 622, 731, 80, 627, 626, 628, 164, + 629, 630, 631, 633, 904, 632, 634, 639, 640, 635, 641, 646, 651, 638, 643, 644, + 645, 905, 907, 906, 81, 653, 654, 656, 911, 657, 908, 82, 83, 909, 910, 84, + 664, 665, 666, 667, 669, 668, 671, 670, 674, 672, 673, 675, 85, 677, 678, 86, + 681, 682, 912, 685, 686, 87, 689, 36, 913, 914, 88, 89, 696, 702, 709, 711, + 915, 712, 713, 718, 719, 917, 831, 721, 720, 723, 832, 725, 728, 918, 919, 739, + 742, 744, 920, 745, 753, 756, 757, 755, 760, 761, 921, 762, 90, 764, 922, 91, + 775, 279, 780, 923, 925, 92, 93, 785, 926, 94, 927, 787, 787, 789, 928, 792, + 95, 796, 797, 798, 800, 96, 929, 802, 804, 806, 97, 98, 807, 930, 99, 931, + 932, 933, 814, 100, 816, 817, 818, 819, 820, 821, 935, 0, 0, }; static const int16_t _hb_ucd_i16[92]= { - 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, - 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, - 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 0, 0, 1, -1, -1, 1, 0, 1, -1, 0, 0, 2, 0, -2, 2, 0, + -2, 0, -7, 0, 0, 16, 0, -16, 3, 3, 3, -3, -3, -3, 0, 2016, + 0, 2527, 1923, 1914, 1918, 0, 2250, 0, 0, 138, 0, 7, 1824, 0, 2104, 0, 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, @@ -4360,11 +4357,13 @@ static const int16_t _hb_ucd_i16[92]= static inline uint8_t _hb_ucd_gc (unsigned u) { - return u<1114110 ? _hb_ucd_u8[6560u+((_hb_ucd_u8[816u+((_hb_ucd_u16[((_hb_ucd_u8[272u+((_hb_ucd_u8[((((((((u)>>1))>>3))>>4))>>4)])<<4)+((((((((u)>>1))>>3))>>4))&15)])<<4)+((((((u)>>1))>>3))&15)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : 2; + /* packtab: [2^8,2^4,2^4,2^3,2^1] */ + return u<1114110u ? (uint8_t)(_hb_ucd_u8[6536u+((_hb_ucd_u8[800u+_hb_ucd_u16[((_hb_ucd_u8[272u+((_hb_ucd_u8[((((((((u)>>1))>>3))>>4))>>4)])<<4)+((((((((u)>>1))>>3))>>4))&15)])<<4)+((((((u)>>1))>>3))&15)]+((((u)>>1))&7)])<<1)+((u)&1)]) : 2; } static inline uint8_t _hb_ucd_ccc (unsigned u) { - return u<125259 ? _hb_ucd_u8[8620u+((_hb_ucd_u8[8036u+((_hb_ucd_u8[7556u+((_hb_ucd_u8[7188u+((_hb_ucd_u8[6942u+((((((((u)>>2))>>2))>>2))>>3)])<<3)+((((((((u)>>2))>>2))>>2))&7)])<<2)+((((((u)>>2))>>2))&3)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 0; + /* packtab: [2^8,2^3,2^2,2^2,2^2] */ + return u<125259u ? (uint8_t)(_hb_ucd_u8[8589u+((_hb_ucd_u8[8006u+((_hb_ucd_u8[7529u+((_hb_ucd_u8[7163u+((_hb_ucd_u8[6918u+((((((((u)>>2))>>2))>>2))>>3)])<<3)+((((((((u)>>2))>>2))>>2))&7)])<<2)+((((((u)>>2))>>2))&3)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)]) : 0; } static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) { @@ -4372,15 +4371,18 @@ static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) } static inline int16_t _hb_ucd_bmg (unsigned u) { - return u<65380 ? _hb_ucd_i16[((_hb_ucd_u8[9516u+((_hb_ucd_u8[9284u+((_hb_ucd_u8[9188u+((_hb_ucd_b4(_hb_ucd_u8+9124u,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)])<<3)+((((((u)>>1))>>2))&7)])<<2)+((((u)>>1))&3)])<<1)+((u)&1)] : 0; + /* packtab: [2^4,2^3,2^3,2^2,2^1] */ + return u<65380u ? (int16_t)(_hb_ucd_i16[_hb_ucd_u8[9480u+_hb_ucd_u8[9251u+_hb_ucd_u8[9157u+((_hb_ucd_b4(_hb_ucd_u8+9093u,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)]+((((((u)>>1))>>2))&7)]+((((u)>>1))&3)]+((u)&1)]) : 0; } static inline uint8_t _hb_ucd_sc (unsigned u) { - return u<918000 ? _hb_ucd_u8[10950u+((_hb_ucd_u16[4648u+((_hb_ucd_u16[2608u+((_hb_ucd_u8[10214u+((_hb_ucd_u8[9764u+((((((((u)>>2))>>2))>>3))>>4)])<<4)+((((((((u)>>2))>>2))>>3))&15)])<<3)+((((((u)>>2))>>2))&7)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 2; + /* packtab: [2^8,2^4,2^3,2^2,2^2] */ + return u<918000u ? (uint8_t)(_hb_ucd_u8[10913u+_hb_ucd_u16[4624u+_hb_ucd_u16[2592u+((_hb_ucd_u8[10177u+((_hb_ucd_u8[9728u+((((((((u)>>2))>>2))>>3))>>4)])<<4)+((((((((u)>>2))>>2))>>3))&15)])<<3)+((((((u)>>2))>>2))&7)]+((((u)>>2))&3)]+((u)&3)]) : 2; } static inline uint16_t _hb_ucd_dm (unsigned u) { - return u<195102 ? _hb_ucd_u16[7480u+((_hb_ucd_u8[13904u+((_hb_ucd_u8[13522u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)] : 0; + /* packtab: [2^8,2^5,2^4] */ + return u<195102u ? (uint16_t)(_hb_ucd_u16[7453u+((_hb_ucd_u8[13867u+((_hb_ucd_u8[13485u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)]) : 0; } @@ -4388,824 +4390,824 @@ static inline uint16_t _hb_ucd_dm (unsigned u) #include -static const uint8_t _hb_ucd_u8[13937]= +static const uint8_t _hb_ucd_u8[13905]= { - 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 9, 10, 7, 7, 7, 7, 7, 11, 12, 12, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 7, 21, 22, 22, 22, 23, 24, 7, 7, - 7, 25, 22, 22, 22, 26, 27, 28, 22, 29, 30, 31, 32, 33, 34, 35, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21, 22, 36, - 7, 7, 7, 7, 7, 7, 37, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 38, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 34, 34, 34, 35, 36, 37, 34, 34, 34, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 67, 70, 71, - 67, 67, 62, 72, 62, 62, 73, 67, 74, 75, 76, 77, 78, 67, 67, 67, - 79, 80, 34, 81, 82, 83, 67, 67, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 84, 34, 34, 34, 34, - 85, 34, 34, 34, 34, 34, 34, 34, 34, 86, 34, 34, 87, 88, 89, 90, - 91, 92, 93, 94, 95, 96, 97, 98, 34, 34, 34, 34, 34, 34, 34, 34, - 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, - 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, - 100,100, 34, 34, 34, 34,101,102, 34, 34,103,104,105,106,107,108, - 34, 34,109,110,111,112,113,114,115,116,117,118, 34, 34, 34,119, - 120,121,122,123,124,125,126,127, 34,128,129,130,131,132,133,134, - 135,136,137,138,139,140,141,142,143,144,111,145,146,147,148,111, - 149,150,151,152,153,154,155,156,157,158,159,160,111,161,162,163, - 34, 34, 34, 34, 34, 34, 34, 34,164, 34, 34,111,111,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,165, - 34, 34, 34, 34, 34, 34, 34, 34,166, 34, 34, 34, 34, 34, 34, 34, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,111, - 111,111,167,111,111,111,111,111,111,111,111,111,111,111,111,111, - 34, 34, 34, 34,168,169,170, 34,111,111,171,111,172,173,174,175, - 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111, - 111,111,111,111,111,111,111,111,111,111,111,111,111,111,111,119, - 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111, - 111,111,111,111,111,111,111,111, 34,176,111,111,111,111,111,111, - 111,111,111,111,111,111,111,111, 67,177, 67, 67, 67,178,179, 67, - 67, 67,180,181,182,131, 65,111,183,184,185,186,187,188,189,190, - 67, 67, 67, 67,191,192,111,111,111,111,111,111,111,111,193,111, - 194,195,196,111,111,197,111,111,111,198,111,199,111,200,111, 34, - 34,201,202,111,111,111,111,111,131,203,204,111, 34,205,111,111, - 67, 67,206, 67, 67,111, 67,207, 67, 67, 67, 67, 67, 67, 67, 67, - 67,208, 67, 67, 67, 67, 67,177,111,111,111,111,111,111,111,111, - 34, 34, 34, 34, 34,111,111,111,111,111,111,111,111,111,111,111, - 34, 34, 34, 34, 34, 34, 34, 34, 34,111,111,111,111,111,111,111, - 209,111,195,195,111,111,111,111,111,111,111,111,111,111,111,111, - 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 2, 4, 5, 6, 2, - 7, 7, 7, 7, 7, 2, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 17, 18, 19, 1, 20, 20, 21, 22, 23, 24, 25, - 26, 27, 15, 2, 28, 29, 27, 30, 11, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 31, 11, 11, 11, 32, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 33, 16, 16, 16, 16, 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 34, 34, 34, 34, 34, 34, 34, 34, 16, 32, 32, 32, - 32, 32, 32, 32, 11, 34, 34, 16, 34, 32, 32, 11, 34, 11, 16, 11, - 11, 34, 32, 11, 32, 16, 11, 34, 32, 32, 32, 11, 34, 16, 32, 11, - 34, 11, 34, 34, 32, 35, 32, 16, 36, 36, 37, 34, 38, 37, 34, 34, - 34, 34, 34, 34, 34, 34, 16, 32, 34, 38, 32, 11, 32, 32, 32, 32, - 32, 32, 16, 16, 16, 11, 34, 32, 34, 34, 11, 32, 32, 32, 32, 32, - 16, 16, 36, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 39, - 39, 40, 40, 39, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, - 39, 39, 41, 40, 40, 40, 41, 41, 40, 40, 40, 40, 40, 40, 40, 40, - 42, 42, 42, 42, 42, 42, 42, 42, 32, 32, 41, 32, 16, 43, 16, 10, - 40, 40, 40, 44, 11, 11, 11, 11, 34, 11, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 16, 16, 16, 16, 34, - 16, 11, 32, 16, 32, 32, 32, 32, 16, 16, 32, 45, 34, 32, 34, 11, - 32, 46, 42, 42, 47, 32, 32, 32, 11, 34, 34, 34, 34, 34, 34, 16, - 11, 11, 11, 11, 48, 2, 2, 2, 16, 16, 16, 16, 49, 50, 51, 52, - 53, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 54, - 55, 56, 42, 55, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 36, - 36, 57, 2, 2, 2, 2, 2, 2, 58, 58, 58, 8, 9, 59, 2, 60, - 42, 42, 42, 42, 42, 56, 61, 2, 62, 36, 36, 36, 36, 63, 42, 42, - 7, 7, 7, 7, 7, 2, 2, 36, 64, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 65, 42, 42, 42, 66, 46, 42, 42, 67, 68, 69, 42, 42, 36, - 7, 7, 7, 7, 7, 36, 70, 71, 2, 2, 2, 2, 2, 2, 2, 72, - 63, 36, 36, 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 64, 36, - 36, 36, 36, 42, 42, 42, 42, 42, 7, 7, 7, 7, 7, 36, 36, 36, - 36, 36, 36, 36, 36, 63, 42, 42, 42, 42, 39, 21, 2, 39, 68, 20, - 36, 36, 36, 42, 42, 68, 42, 42, 42, 42, 68, 42, 68, 42, 42, 42, - 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 36, 36, 63, 42, 42, 2, - 36, 36, 36, 36, 73, 36, 36, 36, 58, 58, 58, 74, 42, 42, 42, 42, - 36, 36, 36, 36, 75, 42, 42, 42, 42, 74, 42, 42, 42, 42, 42, 42, - 42, 76, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 76, 64, 77, - 78, 42, 42, 42, 76, 77, 78, 77, 63, 42, 42, 42, 36, 36, 36, 36, - 36, 42, 2, 7, 7, 7, 7, 7, 79, 36, 36, 36, 36, 36, 36, 36, - 63, 77, 80, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 77, - 78, 42, 42, 76, 77, 77, 78, 36, 36, 36, 36, 81, 77, 77, 36, 36, - 36, 42, 42, 7, 7, 7, 7, 7, 36, 20, 27, 27, 27, 52, 57, 42, - 42, 76, 80, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 42, 77, - 78, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 64, 36, 36, 36, - 36, 36, 36, 7, 7, 7, 7, 7, 42, 36, 63, 2, 2, 2, 2, 2, - 78, 42, 42, 42, 76, 77, 78, 42, 59, 20, 20, 20, 82, 42, 42, 42, - 42, 77, 80, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 64, 78, - 78, 42, 42, 76, 77, 77, 78, 42, 42, 42, 42, 76, 77, 77, 36, 36, - 71, 27, 27, 27, 27, 27, 27, 27, 42, 64, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 77, 76, 77, 77, 77, 77, 77, 78, 42, - 36, 36, 36, 81, 77, 77, 77, 77, 77, 77, 77, 7, 7, 7, 7, 7, - 27, 83, 60, 60, 52, 60, 60, 60, 76, 77, 64, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 64, 42, 76, 77, 77, 42, 42, 42, 42, 42, - 42, 42, 42, 42, 36, 36, 36, 36, 7, 7, 7, 84, 27, 27, 27, 83, - 63, 77, 65, 36, 36, 36, 36, 36, 77, 77, 77, 76, 77, 77, 42, 42, - 42, 42, 76, 77, 77, 77, 36, 36, 85, 81, 77, 77, 77, 77, 77, 77, - 42, 77, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63, 64, 77, - 78, 42, 42, 77, 77, 77, 78, 70, 60, 60, 36, 81, 27, 27, 27, 86, - 27, 27, 27, 27, 83, 36, 36, 36, 36, 36, 36, 36, 36, 42, 42, 76, - 77, 42, 42, 42, 77, 77, 77, 77, 7, 77, 2, 2, 2, 2, 2, 2, - 63, 36, 42, 42, 42, 42, 42, 87, 36, 36, 36, 68, 42, 42, 42, 56, - 7, 7, 7, 7, 7, 2, 2, 2, 63, 36, 42, 42, 42, 42, 64, 36, - 36, 36, 36, 39, 42, 42, 42, 42, 7, 7, 7, 7, 7, 7, 36, 36, - 70, 60, 2, 2, 2, 2, 2, 2, 2, 88, 88, 60, 42, 60, 60, 60, - 7, 7, 7, 7, 7, 27, 27, 27, 27, 27, 46, 46, 46, 4, 4, 77, - 63, 42, 42, 42, 42, 42, 42, 76, 42, 42, 56, 42, 36, 36, 63, 42, - 42, 42, 42, 42, 42, 42, 42, 60, 60, 60, 60, 69, 60, 60, 60, 60, - 2, 2, 88, 60, 21, 2, 2, 2, 36, 36, 36, 36, 36, 81, 78, 42, - 76, 42, 42, 42, 78, 76, 78, 64, 36, 36, 36, 77, 42, 36, 36, 42, - 64, 77, 80, 81, 77, 77, 77, 36, 63, 42, 64, 36, 36, 36, 36, 36, - 36, 76, 78, 76, 77, 77, 78, 81, 7, 7, 7, 7, 7, 77, 78, 60, - 16, 16, 16, 16, 16, 49, 43, 16, 36, 36, 36, 36, 36, 36, 63, 42, - 2, 2, 2, 2, 89, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, - 60, 60, 60, 60, 60, 60, 60, 60, 11, 11, 11, 11, 16, 16, 16, 16, - 90, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 70, 65, - 91, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 92, 93, 93, - 36, 36, 36, 36, 36, 57, 2, 94, 95, 36, 36, 36, 36, 36, 36, 36, - 36, 42, 76, 77, 77, 77, 77, 80, 36, 42, 96, 2, 2, 2, 2, 2, - 36, 42, 42, 42, 42, 42, 42, 42, 36, 36, 42, 78, 42, 42, 42, 77, - 77, 77, 77, 76, 78, 42, 42, 42, 42, 42, 2, 79, 2, 59, 63, 42, - 7, 7, 7, 7, 7, 7, 7, 7, 2, 2, 2, 97, 2, 55, 42, 74, - 36, 75, 36, 36, 36, 36, 36, 36, 36, 36, 63, 64, 36, 36, 36, 36, - 36, 36, 36, 36, 63, 36, 36, 36, 42, 76, 77, 78, 76, 77, 77, 77, - 77, 76, 77, 77, 78, 42, 42, 42, 60, 60, 2, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 27, 27, 60, 36, 36, 36, 63, 76, 78, 42, 2, - 36, 36, 81, 76, 42, 42, 42, 42, 76, 76, 78, 42, 42, 42, 76, 77, - 77, 78, 42, 42, 42, 42, 42, 42, 2, 2, 2, 79, 2, 2, 2, 2, - 42, 42, 42, 42, 42, 42, 42, 98, 42, 42, 80, 36, 36, 36, 36, 36, - 36, 36, 76, 42, 42, 76, 76, 77, 77, 76, 80, 36, 36, 36, 36, 2, - 88, 60, 60, 60, 60, 46, 42, 42, 42, 42, 60, 60, 60, 60, 21, 2, - 42, 80, 36, 36, 36, 36, 36, 36, 81, 42, 42, 77, 42, 78, 42, 36, - 36, 36, 36, 76, 42, 77, 78, 78, 42, 77, 77, 77, 77, 77, 2, 2, - 36, 36, 77, 77, 77, 77, 42, 42, 42, 42, 77, 42, 42, 56, 2, 2, - 7, 7, 7, 7, 7, 7, 85, 36, 36, 36, 36, 36, 39, 39, 39, 2, - 16, 16, 16, 16, 34, 16, 16, 16, 42, 56, 42, 42, 42, 42, 42, 42, - 76, 42, 42, 42, 64, 36, 63, 36, 36, 36, 64, 81, 42, 36, 36, 36, - 16, 16, 16, 16, 16, 16, 39, 39, 39, 39, 39, 39, 39, 43, 16, 16, - 16, 16, 16, 16, 43, 16, 16, 16, 16, 16, 16, 16, 16, 99, 39, 39, - 32, 32, 32, 16, 16, 16, 16, 32, 16, 16, 16, 16, 11, 11, 11, 11, - 16, 16, 16, 16, 34, 11, 11, 11, 16, 16, 16, 16,100,100,100,100, - 16, 16, 16, 16, 11, 11,101,102, 40, 16, 16, 16, 11, 11,101, 40, - 16, 16, 16, 16, 11, 11,103, 40,104,104,104,104,104,105, 58, 58, - 50, 50, 50, 2,106,107,106,107, 2, 2, 2, 2,108, 58, 58,109, - 2, 2, 2, 2,110,111, 2,112,113, 2,114,115, 2, 2, 2, 2, - 2, 9,113, 2, 2, 2, 2,116, 58, 58, 58, 58, 58, 58, 58, 58, - 117, 39, 27, 27, 27, 8,114,118, 27, 27, 27, 27, 27, 8,114, 93, - 20, 20, 20, 20, 20, 20, 20, 20, 42, 42, 42, 42, 42, 42,119, 47, - 98, 47, 98, 42, 42, 42, 42, 42, 60,120, 60,121, 60, 34, 11, 16, - 11, 32,121, 60, 45, 11, 11, 60, 60, 60,120,120,120, 11, 11,122, - 11, 11, 35, 36,123, 60, 16, 11, 8, 8, 45, 16, 16, 26, 60,124, - 94, 94, 94, 94, 94, 94, 94, 94, 94,125,126, 94,127, 60, 60, 60, - 8, 8,128, 60, 60, 8, 60, 60,128, 26, 60,128, 60, 60, 60,128, - 60, 60, 60, 60, 60, 60, 60, 8, 60,128,128, 60, 60, 60, 60, 60, - 60, 60, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, - 60, 60, 60, 60, 4, 4, 60, 60, 8, 60, 60, 60,129,130, 60, 60, - 60, 60, 60, 60, 60, 60,128, 60, 60, 60, 60, 60, 60, 26, 8, 8, - 8, 8, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 8, 8, - 8, 60, 60, 60, 60, 60, 60, 60, 27, 27, 27, 27, 27, 27, 60, 60, - 60, 60, 60, 60, 60, 27, 27, 27, 60, 60, 60, 26, 60, 60, 60, 60, - 26, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 8, 8, 8, 8, - 60, 60, 60, 60, 60, 60, 60, 26, 60, 60, 60, 60, 4, 4, 4, 4, - 4, 4, 4, 27, 27, 27, 27, 27, 27, 27, 60, 60, 60, 60, 60, 60, - 8, 8,114,131, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 4, 4, - 8,114,132,132,132,132,132,132,132,132,132,132,131, 8, 8, 8, - 8, 8, 8, 8, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, - 8, 8,128, 26, 8, 8,128, 60, 32, 11, 32, 34, 34, 34, 34, 11, - 32, 32, 34, 16, 16, 16, 39, 11, 32, 32,124, 60, 60,121, 34,133, - 42, 32, 16, 16, 49, 2, 89, 2, 36, 36, 36, 36, 36, 36, 36, 75, - 2, 2, 2, 2, 2, 2, 2, 55, 2,106,106, 2,110,111,106, 2, - 2, 2, 2, 6, 2, 97,106, 2,106, 4, 4, 4, 4, 2, 2, 79, - 2, 2, 2, 2, 2, 50, 2, 2, 97,134, 2, 2, 2, 2, 2, 2, - 60, 2,135,132,132,132,136, 50, 50, 50, 50, 50, 50, 50, 50, 50, - 1, 2,137,138, 4, 4, 4, 4, 4, 60, 4, 4, 4, 4,139, 93, - 140, 94, 94, 94, 94, 42, 42, 77,141, 39, 39, 60, 94,142, 57, 60, - 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 63,143,144, 62, - 36, 36, 36, 36, 36, 57, 39, 62, 60, 27, 27, 60, 60, 60, 60, 60, - 27, 27, 27, 27, 27, 60, 60, 60, 60, 60, 60, 60, 27, 27, 27, 27, - 145, 27, 27, 27, 27, 27, 27, 27, 36, 36, 75, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36,146, 2, 32, 32, 32, 32, 32, 32, 32, 63, - 47,147, 42, 42, 42, 42, 42, 79, 32, 32, 32, 32, 32, 32, 39, 42, - 36, 36, 36, 94, 94, 94, 94, 94, 42, 2, 2, 2, 2, 2, 2, 2, - 40, 40, 40,144, 39, 39, 39, 39, 40, 32, 32, 32, 32, 32, 32, 32, - 16, 32, 32, 32, 32, 32, 32, 32, 43, 16, 16, 16, 34, 34, 34, 32, - 32, 32, 32, 32, 41,148, 34, 35, 32, 32, 16, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 11, 11, 32, 11, 11, 32, 32, 32, 32, 32, 32, - 32, 32, 11, 11, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 11, 11, - 48, 39,149, 35, 39, 35, 36, 36, 36, 64, 36, 64, 36, 63, 36, 36, - 36, 81, 78, 76, 60, 60, 42, 42, 27, 27, 27, 60,150, 60, 60, 60, - 36, 36, 2, 2, 2, 2, 2, 2, 77, 36, 36, 36, 36, 36, 36, 36, - 36, 36, 77, 77, 77, 77, 77, 77, 77, 77, 42, 42, 42, 42, 42, 2, - 42, 36, 36, 36, 2, 65, 65, 63, 36, 36, 36, 42, 42, 42, 42, 2, - 36, 36, 36, 63, 42, 42, 42, 42, 42, 77, 77, 77, 77, 77, 77, 96, - 36, 63, 77, 42, 42, 77, 42, 77, 96, 2, 2, 2, 2, 2, 2, 79, - 7, 7, 7, 7, 7, 7, 7, 2, 36, 36, 63, 62, 36, 36, 36, 36, - 36, 36, 36, 36, 63, 42, 42, 76, 78, 76, 78, 42, 42, 42, 42, 42, - 36, 63, 36, 36, 36, 36, 76, 77, 7, 7, 7, 7, 7, 7, 2, 2, - 62, 36, 36, 70, 60, 81, 76, 36, 64, 42, 64, 63, 64, 36, 36, 42, - 36, 36, 36, 36, 36, 36, 75, 2, 36, 36, 36, 36, 36, 81, 42, 77, - 2, 75,151, 42, 42, 42, 42, 42, 16, 16, 16, 16, 16,102, 39, 39, - 16, 16, 16, 16, 99, 40, 40, 40, 36, 81, 78, 77, 76, 96, 78, 42, - 152,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153, - 16, 16, 16, 16, 16, 16, 35, 64, 36, 36, 36, 36,154, 36, 36, 36, - 36, 40, 40, 40, 40, 40, 40, 40, 40, 22, 60, 60, 60, 60, 60, 60, - 60, 71, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,132, - 60, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 60, 60, 60, 60, - 36, 36, 36, 36, 36, 36,150, 60, 2, 2, 2,135,115, 2, 2, 2, - 6,155,156,132,132,132,132,132,132,132,115,135,115, 2,112,157, - 2, 2, 2, 2,139,132,132,115, 2,158, 8, 8, 59, 2, 2, 2, - 36, 36, 36, 36, 36, 36, 36,159, 2, 2, 3, 2, 4, 5, 6, 2, - 16, 16, 16, 16, 16, 17, 18,114,115, 4, 2, 36, 36, 36, 36, 36, - 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 39, - 20,160, 52, 20, 26, 8,128, 60, 60, 60, 60, 60,161, 58, 60, 60, - 2, 2, 2, 89, 27, 27, 27, 27, 27, 27, 27, 83, 60, 60, 60, 60, - 94, 94,127, 27, 83, 60, 60, 60, 60, 60, 60, 60, 60, 27, 60, 60, - 60, 60, 60, 60, 60, 60, 46, 42,162,162,162,162,162,162,162,162, - 163, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 86, 36, - 138, 36, 36, 36, 36, 94, 94, 94, 36, 36, 36, 36, 36, 36, 36, 57, - 164, 94, 94, 94, 94, 94, 94, 94, 11, 11, 11, 32, 16, 16, 16, 16, - 36, 36, 36, 57, 27, 27, 27, 27, 36, 36, 36, 70,145, 27, 27, 27, - 36, 36, 36,165, 27, 27, 27, 27, 36, 36, 36, 36, 36,165, 27, 27, - 36, 36, 36, 27, 27, 27, 27, 30, 36, 36, 36, 36, 36, 36, 27, 36, - 63, 42, 42, 42, 42, 42, 42, 42, 36, 36, 36, 36, 42, 42, 42, 42, - 36, 36, 36, 36, 36, 36,165, 30, 36, 36, 36, 36, 36, 36,165, 27, - 36, 36, 36, 36, 71, 36, 36, 36, 36, 36, 63, 42, 42,163, 27, 27, - 36, 36, 36, 36, 57, 2, 2, 2, 36, 36, 36, 36, 27, 27, 27, 27, - 16, 16, 16, 16, 16, 27, 27, 27, 36, 36, 42, 42, 42, 42, 42, 42, - 7, 7, 7, 7, 7, 36, 36, 62, 11, 11, 11, 11,166, 42, 42,141, - 16, 16, 16, 16, 16, 16, 16, 8, 36, 36, 36, 36, 36, 63,167, 50, - 88, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 42, 42, 42, - 27, 27, 27, 86, 36, 36, 36, 36,163, 27, 30, 2, 2, 2, 2, 2, - 36, 42, 42, 2, 2, 2, 2, 2, 36, 36,165, 27, 27, 27, 27, 27, - 78, 80, 36, 36, 36, 36, 36, 36, 42, 42, 42, 56, 2, 2, 2, 2, - 2, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 7, 7, 7, - 64, 63, 64, 36, 36, 36, 36, 63, 77, 78, 42, 76, 78, 56, 72, 2, - 2, 42, 42, 42, 42, 42, 66, 58, 36, 36, 36, 63, 42, 42, 78, 42, - 42, 42, 42, 7, 7, 7, 7, 7, 2, 2, 81, 80, 36, 36, 36, 36, - 36, 63, 2, 36, 36, 36, 36, 36, 36, 81, 77, 42, 42, 42, 42, 76, - 80, 36, 57, 2, 55, 42, 56, 78, 7, 7, 7, 7, 7, 57, 57, 2, - 89, 27, 27, 27, 27, 27, 27, 27, 36, 36, 36, 36, 36, 36, 77, 78, - 42, 77, 76, 42, 2, 2, 2, 64, 36, 36, 36, 36, 36, 36, 36, 63, - 76, 77, 77, 77, 77, 77, 77, 77, 36, 36, 36, 81, 77, 77, 80, 36, - 36, 77, 77, 42, 42, 42, 42, 42, 36, 36, 36, 36, 77, 78, 42, 42, - 42, 77, 77, 77, 77, 77, 77, 76, 64, 64, 2, 2, 2, 2, 2, 2, - 55, 42, 42, 42, 42, 42, 42, 42, 36, 36, 81, 77, 42, 42, 42, 42, - 77, 42, 76, 64, 36, 57, 2, 2, 7, 7, 7, 7, 7, 2, 2, 64, - 77, 78, 42, 42, 76, 76, 77, 78, 76, 42, 36, 65, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 36, 36, 81, 77, 42, 42, 42, 77, 77, 42, 78, - 56, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 36, 36, 42, 42, - 77, 78, 42, 42, 42, 76, 78, 78, 56, 2, 36, 36, 36, 36, 36, 36, - 36, 36, 36, 36, 36, 63, 78, 77, 42, 42, 42, 78, 57, 2, 2, 2, - 36, 36, 36, 36, 36, 36, 63, 78, 77, 42, 42, 78, 42, 42, 42, 42, - 7, 7, 7, 7, 7, 27, 2, 88, 42, 42, 42, 42, 78, 56, 2, 2, - 27, 27, 27, 27, 27, 27, 27, 86, 77, 77, 77, 77, 77, 78, 76, 64, - 80, 78, 2, 2, 2, 2, 2, 2, 81, 77, 42, 42, 42, 42, 77, 77, - 64, 65, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, - 63, 42, 42, 42, 42, 64, 36, 36, 36, 63, 42, 42, 76, 63, 42, 56, - 2, 2, 2, 55, 42, 42, 42, 42, 63, 42, 42, 76, 78, 42, 36, 36, - 36, 36, 36, 36, 36, 42, 42, 42, 42, 42, 42, 76, 42, 2, 65, 2, - 76, 42, 76, 76, 77, 77, 77, 77, 57, 2, 2, 2, 2, 2, 2, 2, - 42, 42, 42, 42, 42, 42, 42, 78, 2, 36, 36, 36, 36, 36, 36, 36, - 42, 42, 42, 42, 76, 42, 42, 42, 76, 42, 78, 42, 42, 42, 42, 42, - 42, 42, 42, 63, 42, 42, 42, 42, 36, 36, 36, 36, 36, 77, 77, 77, - 42, 76, 78, 78, 36, 36, 36, 36, 36, 36, 36, 36, 75, 36, 36, 36, - 36, 63, 76, 96, 2, 2, 2, 2, 42, 81, 36, 36, 36, 36, 36, 36, - 36, 36, 77, 42, 42, 42, 42, 77, 76, 56, 2, 2, 2, 2, 2, 2, - 7, 7, 7, 7, 7, 42, 42, 42, 27, 27, 83, 60, 60, 60, 52, 20, - 150, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 60, 21, - 64, 36, 36, 63, 42, 42, 42, 42, 36, 36, 36, 36, 36, 36, 36, 42, - 42, 42, 42, 42, 42, 77, 78, 42, 42, 42, 56, 2, 2, 2, 2, 2, - 42, 42, 42, 56, 2, 2, 60, 60, 39, 39, 88, 60, 60, 60, 60, 60, - 7, 7, 7, 7, 7,168, 27, 27, 27, 86, 36, 36, 36, 36, 36, 36, - 39, 62, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 75,146, 2, - 27, 27, 27, 30, 2, 2, 2, 2, 11, 11, 11, 11, 11, 32, 16, 16, - 81, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 78, - 42, 67, 39, 39, 39, 39, 39, 39, 39, 79, 42, 42, 42, 42, 42, 42, - 77, 39, 94, 94, 94, 94, 94, 94, 36, 36, 36, 36, 36, 36, 46, 56, - 7, 7, 7, 7, 7, 60, 60, 60, 60, 60,169, 78, 42, 60,169, 77, - 77,170, 58, 58, 58, 74, 42, 42, 42, 69, 46, 42, 42, 42, 60, 60, - 60, 60, 60, 60, 60, 42, 42, 60, 60, 42, 69, 60, 60, 60, 60, 60, - 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 16, 11, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 11, 11, 11, 11, 11, 16, 16, 16, 16, 16, - 31, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, - 16, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, - 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 31, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 33, 16, 16, 16, 11, 11, 11, 11, 11, - 11, 11, 11, 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, - 11, 11, 11, 11, 31, 16, 16, 16, 16, 33, 16, 16, 16, 32, 16, 7, - 42, 42, 42, 69, 60, 46, 42, 42, 42, 42, 42, 42, 42, 42, 69, 60, - 60, 60, 46, 60, 60, 60, 60, 60, 60, 60, 69, 21, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 55, 42, 42, 16, 16, 16, 16, 16,123, 16, 16, - 42, 42, 42, 67, 39, 39, 39, 39, 7, 7, 7, 7, 7, 7, 7, 70, - 36, 36, 36, 36, 36, 36, 42, 42, 7, 7, 7, 7, 7, 7, 7,171, - 36, 36, 36, 36, 36, 75, 42, 42,172, 7, 7, 7, 7, 7, 7, 84, - 36, 63, 36, 64, 36, 36, 36, 42, 36, 36, 63, 42, 42, 42, 42, 75, - 16, 16, 42, 42, 42, 67, 39, 39, 27, 27, 27, 27, 27, 27,145, 27, - 173, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,145, - 27, 27, 27, 27, 27, 27, 83, 60, 60, 60, 60, 60, 60, 25, 40, 40, - 0, 0, 29, 21, 21, 21, 23, 21, 22, 18, 21, 25, 21, 17, 13, 13, - 25, 25, 25, 21, 21, 9, 9, 9, 9, 22, 21, 18, 24, 16, 24, 5, - 5, 5, 5, 22, 25, 18, 25, 0, 23, 23, 26, 21, 24, 26, 7, 20, - 25, 1, 26, 24, 26, 25, 15, 15, 24, 15, 7, 19, 15, 21, 9, 25, - 9, 5, 5, 25, 5, 9, 5, 7, 7, 7, 9, 8, 8, 5, 6, 6, - 24, 24, 6, 24, 12, 12, 6, 5, 9, 21, 25, 9, 26, 12, 11, 11, - 9, 6, 5, 21, 17, 17, 17, 26, 26, 23, 23, 12, 17, 12, 21, 12, - 12, 21, 7, 21, 1, 1, 21, 23, 26, 26, 1, 21, 6, 7, 7, 12, - 12, 7, 21, 7, 12, 1, 12, 6, 6, 12, 12, 26, 7, 26, 26, 7, - 21, 1, 24, 7, 1, 12, 7, 6, 12, 10, 10, 10, 10, 12, 21, 6, - 10, 7, 7, 10, 23, 7, 15, 26, 13, 21, 13, 7, 15, 7, 12, 23, - 21, 26, 21, 15, 17, 7, 29, 7, 7, 22, 18, 18, 14, 14, 14, 7, - 10, 21, 17, 21, 11, 12, 5, 6, 8, 8, 8, 24, 5, 24, 9, 24, - 29, 29, 29, 1, 20, 19, 22, 20, 27, 28, 1, 29, 21, 20, 19, 21, - 21, 16, 16, 21, 25, 22, 18, 21, 21, 29, 15, 6, 18, 6, 12, 11, - 9, 26, 26, 9, 26, 5, 7, 5, 5, 26, 14, 9, 5, 14, 14, 15, - 25, 26, 26, 22, 18, 26, 18, 25, 18, 22, 5, 12, 22, 21, 21, 22, - 18, 17, 26, 6, 7, 14, 17, 22, 26, 14, 17, 6, 14, 6, 12, 24, - 24, 6, 26, 15, 6, 21, 11, 21, 24, 9, 6, 9, 23, 26, 6, 10, - 4, 4, 3, 3, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 7, 1, - 25, 24, 26, 1, 2, 2, 12, 15, 21, 14, 7, 15, 9, 12, 12, 17, - 13, 15, 26, 10, 10, 1, 13, 23, 7, 13, 23, 15, 0, 1, 2, 3, - 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 0, 14, 0, 0, 0, - 0, 0, 15, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 4, 5, 6, 7, 8, 9, 10, 2, 2, 11, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 12, 13, 2, 2, 2, 2, 2, 14, 1, 1, 1, 15, + 16, 17, 18, 19, 20, 21, 22, 2, 3, 0, 0, 0, 23, 24, 2, 2, + 2, 25, 0, 0, 0, 26, 27, 28, 0, 29, 30, 31, 32, 33, 34, 35, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 0, 36, + 2, 2, 2, 2, 2, 2, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, - 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, - 0, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, - 0, 36, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 43, 44, - 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, - 0, 0, 0, 0, 3, 0, 0, 0, 4, 5, 6, 7, 0, 8, 9, 10, - 0, 11, 12, 13, 14, 15, 16, 17, 16, 18, 16, 19, 16, 19, 16, 19, - 0, 19, 16, 20, 16, 19, 21, 19, 0, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, - 0, 0, 0, 0, 0, 0, 34, 0, 0, 35, 0, 0, 36, 0, 37, 0, - 0, 0, 38, 39, 40, 41, 42, 43, 44, 45, 46, 0, 0, 47, 0, 0, - 0, 48, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 50, 0, 51, - 0, 52, 53, 0, 54, 0, 0, 0, 0, 0, 0, 55, 56, 57, 0, 0, - 0, 0, 58, 0, 0, 59, 60, 61, 62, 63, 0, 0, 64, 65, 0, 0, - 0, 66, 0, 0, 0, 0, 67, 0, 0, 0, 68, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0, 70, 0, 71, - 0, 0, 72, 0, 0, 73, 0, 0, 0, 0, 0, 0, 0, 0, 74, 75, - 0, 0, 0, 0, 76, 77, 0, 78, 79, 0, 0, 80, 81, 0, 82, 62, - 0, 83, 84, 0, 0, 85, 86, 87, 0, 88, 0, 89, 0, 90, 0, 0, - 51, 91, 51, 0, 92, 0, 93, 0, 0, 0, 81, 0, 0, 0, 94, 95, - 0, 96, 97, 98, 99, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0,100, - 101, 0, 0, 0, 0, 0, 0,102, 0, 0, 0, 0, 0, 0,103, 0, - 0, 0, 0, 0, 0,104,105, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0,106, 0, 0,107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,108, - 109, 0, 0,110, 0, 0, 0, 0, 0, 0,111, 0,112, 0,105, 0, - 0, 0, 0, 0,113,114, 0, 0, 0, 0, 0, 0, 0,115, 0, 0, - 0,116, 0, 0, 0,117, 0, 0, 0, 0, 0, 0, 0,118, 0,119, - 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0, 8, 0, - 0, 0, 0, 9, 10, 11, 12, 0, 0, 0, 0, 13, 0, 0, 14, 15, - 0, 16, 0, 17, 18, 0, 0, 19, 0, 20, 21, 0, 0, 0, 0, 0, - 22, 23, 0, 24, 25, 0, 0, 26, 0, 0, 0, 27, 0, 0, 28, 29, - 30, 31, 0, 0, 0, 32, 33, 34, 0, 0, 33, 0, 0, 35, 33, 0, - 0, 0, 33, 36, 0, 0, 0, 0, 0, 37, 38, 0, 0, 0, 0, 0, - 0, 39, 40, 0, 0, 0, 0, 0, 0, 41, 42, 0, 0, 0, 0, 43, - 0, 44, 0, 0, 0, 45, 46, 0, 0, 0, 47, 0, 0, 0, 0, 0, - 0, 48, 49, 0, 0, 0, 0, 50, 0, 0, 0, 51, 0, 52, 0, 53, - 0, 0, 0, 0, 54, 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 0, - 57, 58, 0, 0, 0, 59, 60, 61, 62, 0, 0, 0, 0, 63, 52, 0, - 64, 65, 0, 0, 66, 0, 0, 0, 67, 68, 0, 0, 0, 69, 0, 70, - 71, 72, 73, 74, 1, 75, 0, 76, 77, 78, 0, 0, 79, 80, 0, 0, - 0, 81, 0, 0, 1, 1, 0, 0, 82, 0, 0, 83, 0, 0, 0, 0, - 79, 84, 0, 85, 0, 0, 0, 0, 0, 80, 86, 0, 87, 0, 52, 0, - 1, 80, 0, 0, 88, 0, 0, 89, 0, 0, 0, 0, 0, 90, 57, 0, - 0, 0, 0, 0, 0, 91, 92, 0, 0, 86, 0, 0, 33, 0, 0, 93, - 0, 0, 0, 0, 94, 0, 0, 0, 0, 49, 0, 0, 95, 0, 0, 0, - 0, 96, 97, 0, 0, 98, 0, 0, 99, 0, 0, 0,100, 0, 0, 0, - 101, 0, 0, 0,102, 0, 0, 0, 0,103,104, 95, 0, 0,105, 0, - 0, 0, 86, 0, 0,106, 0, 0, 0,107,108, 0, 0,109,110, 0, - 0, 0, 0, 0, 0,111, 0, 0,112, 0, 0, 0, 0,113, 33, 0, - 114,115,116, 57, 0, 0,117, 35, 0, 0,118, 0, 0, 0,119, 0, - 0, 0, 0, 0, 0,120, 0, 0,121, 0, 0, 0, 0,122, 90, 0, - 0, 0, 0, 0, 57, 0, 0, 0, 0, 52,123, 0, 0, 0, 0,124, - 0, 0,125, 0, 0, 0, 0,123, 0, 0,126, 0, 0, 0, 0, 0, - 81, 0, 0, 0, 0,127, 0, 0, 0,128, 0, 0, 0,129, 0,130, - 0, 0, 0, 0,131,132,133, 0,134, 0,135, 0, 0, 0,136,137, - 138, 0, 79, 0, 0, 0, 0, 0, 35, 0, 0, 0,139, 0, 0, 0, - 140, 0, 0, 0,141, 0, 0, 0,142,143, 0,144, 0, 0,145, 0, - 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 3, 4, 5, 6, - 7, 4, 4, 8, 9, 10, 1, 11, 12, 13, 14, 15, 16, 17, 18, 1, - 1, 1, 19, 1, 0, 0, 20, 21, 22, 1, 23, 4, 21, 24, 25, 26, - 27, 28, 29, 30, 0, 0, 1, 1, 31, 0, 0, 0, 32, 33, 34, 35, - 1, 36, 37, 0, 0, 0, 0, 38, 1, 39, 14, 39, 40, 41, 42, 0, - 0, 0, 43, 36, 44, 45, 21, 45, 46, 0, 0, 0, 19, 1, 21, 0, - 0, 47, 0, 38, 48, 1, 1, 49, 49, 50, 0, 0, 51, 0, 0, 19, - 52, 1, 0, 0, 38, 14, 4, 1, 1, 1, 53, 21, 43, 52, 54, 21, - 35, 1, 0, 0, 0, 55, 0, 0, 0, 56, 57, 58, 0, 0, 0, 0, - 0, 59, 0, 60, 0, 0, 0, 0, 61, 62, 0, 0, 63, 0, 0, 0, - 64, 0, 0, 0, 65, 0, 0, 0, 66, 0, 0, 0, 67, 0, 0, 0, - 68, 0, 0, 69, 70, 0, 71, 72, 73, 74, 75, 76, 0, 0, 0, 77, - 0, 0, 0, 78, 79, 0, 0, 0, 0, 47, 0, 0, 0, 49, 0, 80, - 0, 0, 0, 62, 0, 0, 63, 0, 0, 81, 0, 0, 82, 0, 0, 0, - 83, 0, 0, 19, 84, 0, 62, 0, 0, 0, 0, 49, 1, 85, 1, 52, - 15, 86, 36, 10, 21, 1, 1, 1, 1, 41, 1, 21, 87, 0, 0, 55, - 0, 0, 0, 0, 19, 10, 1, 0, 0, 0, 0, 0, 88, 0, 0, 89, - 0, 0, 88, 0, 0, 0, 0, 78, 0, 0, 90, 9, 12, 4, 91, 8, - 92, 47, 0, 58, 50, 0, 21, 1, 21, 93, 94, 1, 1, 1, 1, 95, - 96, 97, 98, 1, 99, 58, 81,100,101, 4, 58, 0, 0, 0, 0, 0, - 0, 19, 50, 0, 0, 0, 0, 0, 0, 61, 0, 0,102,103, 0, 0, - 104, 0, 0, 1, 1, 50, 0, 0, 0, 38, 0, 63, 0, 0, 0, 0, - 0, 62, 0, 0,105, 68, 61, 0, 0, 0, 78, 0, 0, 0,106,107, - 58, 38, 81, 0, 0, 0, 0, 0, 0,108, 1, 14, 4, 12, 84, 0, - 0, 0, 0, 38, 90, 0, 0, 0, 0,109, 0, 0,110, 61, 0,111, - 0, 0, 0, 1, 0, 0, 0, 0, 49, 50, 0, 0, 19, 58, 0, 0, - 112, 51, 0,112, 14, 52,113, 41, 0, 0, 62, 0, 0, 61, 0, 0, - 114, 0, 90, 0, 0, 0, 61, 62, 0, 0, 62, 0, 89, 0, 0,114, - 0, 0, 0, 0,115, 0, 0, 0, 78, 55, 0, 38, 1, 58, 1, 58, - 0, 0, 0, 0, 0, 88, 63, 89, 0, 0,116, 0, 0, 0, 55, 0, - 0, 0, 0,116, 0, 0, 0, 0, 61, 0, 0, 0, 0, 79, 0, 61, - 0, 0, 0, 0, 56, 0, 89, 80, 0, 0, 79, 0, 0, 0, 8, 92, - 0, 0, 1, 90, 0, 0,117, 0, 0, 0, 0, 0, 0,118, 0,119, - 120,121,122, 0,105, 4,123, 49, 23, 0, 0, 0, 38, 50, 38, 58, - 0, 0, 1, 90, 1, 1, 1, 1, 39, 1, 48,106, 90, 0, 0, 0, - 0, 1, 0, 0, 0,124, 0, 0, 0,113, 19, 59, 0, 38, 0, 81, - 0, 0, 4,123, 0, 0, 0, 1,125, 0, 0, 0, 0, 0,230,230, - 230,230,230,232,220,220,220,220,232,216,220,220,220,220,220,202, - 202,220,220,220,220,202,202,220,220,220, 1, 1, 1, 1, 1,220, - 220,220,220,230,230,230,230,240,230,220,220,220,230,230,230,220, - 220, 0,230,230,230,220,220,220,220,230,232,220,220,230,233,234, - 234,233,234,234,233,230, 0, 0, 0,230, 0,220,230,230,230,230, - 220,230,230,230,222,220,230,230,220,220,230,222,228,230, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 0, 23, 0, 24, - 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, 0, 0, 27, 28, 29, - 30, 31, 32, 33, 34,230,230,220,220,230,220,230,230,220, 35, 0, - 0, 0, 0, 0,230,230,230, 0, 0,230,230, 0,220,230,230,220, - 0, 0, 0, 36, 0, 0,230,220,230,230,220,220,230,220,220,230, - 220,230,220,230,230, 0, 0,220, 0, 0,230,230, 0,230, 0,230, - 230,230,230,230, 0, 0, 0,220,220,220,230,220,220,220,230,230, - 0,220, 27, 28, 29,230, 7, 0, 0, 0, 0, 9, 0, 0, 0,230, - 220,230,230, 0, 0, 0, 0, 0,230, 0, 0, 84, 91, 0, 0, 0, - 0, 9, 9, 0, 0, 0, 0, 0, 9, 0,103,103, 9, 0,107,107, - 107,107,118,118, 9, 0,122,122,122,122,220,220, 0, 0, 0,220, - 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, 0, - 130,130,130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, 0, - 220, 0, 0, 0, 0, 7, 0, 9, 9, 0, 9, 9, 0, 0, 0,230, - 0, 0, 0,228, 0, 0, 0,222,230,220,220, 0, 0, 0,230, 0, - 0,220,230,220, 0,220,230,230,230,234, 0, 0, 9, 9, 0, 0, - 7, 0,230,230,230, 0,230, 0, 1, 1, 1, 0, 0, 0,230,234, - 214,220,202,230,230,230,230,230,232,228,228,220,218,230,233,220, - 230,220,230,230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, - 1, 1, 0, 0,218,228,232,222,224,224, 0, 8, 8, 0, 0, 0, - 0,220,230, 0,230,230,220, 0, 0,230, 0, 0, 26, 0, 0,220, - 0,230,230, 1,220, 0, 0,230,220, 0, 0, 0,220,220, 0, 0, - 230,220, 0, 9, 7, 0, 0, 7, 9, 0, 0, 0, 9, 7, 6, 6, - 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, 1, 0, 0, 0,226, - 216,216,216,216,216, 0,220,220,220, 0,232,232,220,230,230,230, - 7, 0, 16, 17, 17, 33, 17, 49, 17, 17, 84, 97,135,145, 26, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17,177, 0, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 4, 3, 3, 3, 3, 3, 5, 3, 3, 3, - 3, 3, 6, 7, 8, 3, 3, 3, 3, 3, 9, 10, 11, 12, 13, 3, - 3, 3, 3, 3, 3, 3, 3, 14, 3, 15, 3, 3, 3, 3, 3, 3, - 16, 17, 18, 19, 20, 21, 3, 3, 3, 22, 23, 24, 3, 3, 3, 3, - 3, 3, 25, 3, 3, 3, 3, 3, 3, 3, 3, 26, 3, 3, 27, 28, - 0, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, - 0, 3, 0, 0, 0, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 9, 0, 0, 0, 0, 0, - 0, 9, 0, 9, 0, 0, 0, 0, 0, 0, 0, 10, 11, 12, 13, 0, - 0, 14, 15, 16, 6, 0, 17, 18, 19, 19, 19, 20, 21, 22, 23, 24, - 19, 25, 0, 26, 27, 19, 19, 28, 29, 30, 0, 31, 0, 0, 0, 8, - 0, 0, 0, 0, 0, 0, 0, 19, 28, 0, 32, 33, 9, 34, 35, 19, - 0, 0, 36, 37, 38, 39, 40, 19, 0, 41, 42, 43, 44, 31, 0, 1, - 45, 42, 0, 0, 0, 0, 0, 32, 14, 14, 0, 0, 0, 0, 14, 0, - 0, 46, 47, 47, 47, 47, 48, 49, 47, 47, 47, 47, 50, 51, 52, 53, - 43, 21, 0, 0, 0, 0, 0, 0, 0, 54, 6, 55, 0, 14, 19, 1, - 0, 0, 0, 0, 56, 57, 0, 0, 0, 0, 0, 19, 58, 31, 0, 0, - 0, 0, 0, 0, 0, 59, 14, 0, 0, 0, 0, 1, 0, 2, 0, 0, - 0, 3, 0, 0, 0, 60, 61, 0, 0, 0, 0, 0, 0, 0, 1, 0, - 0, 0, 0, 0, 2, 3, 0, 4, 5, 0, 0, 6, 0, 0, 0, 7, - 0, 0, 0, 1, 1, 0, 0, 8, 9, 0, 8, 9, 0, 0, 0, 0, - 8, 9, 10, 11, 12, 0, 0, 0, 13, 0, 0, 0, 0, 14, 15, 16, - 17, 0, 0, 0, 1, 0, 0, 18, 19, 0, 0, 0, 20, 0, 0, 0, - 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 8, 21, 9, - 0, 0, 22, 0, 0, 0, 0, 1, 0, 23, 24, 25, 0, 0, 26, 0, - 0, 0, 8, 21, 27, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 28, - 29, 30, 0, 31, 32, 20, 1, 1, 0, 0, 0, 8, 21, 9, 1, 4, - 5, 0, 0, 0, 33, 9, 0, 1, 1, 1, 0, 8, 21, 21, 21, 21, - 34, 1, 35, 21, 21, 21, 9, 36, 0, 0, 37, 38, 1, 0, 39, 0, - 0, 0, 1, 0, 1, 0, 0, 0, 0, 8, 21, 9, 1, 0, 0, 0, - 40, 0, 8, 21, 21, 21, 21, 21, 21, 21, 21, 9, 0, 1, 1, 1, - 1, 8, 21, 21, 21, 9, 0, 0, 0, 41, 0, 42, 43, 0, 0, 0, - 1, 44, 0, 0, 0, 45, 8, 9, 1, 0, 0, 0, 8, 21, 21, 21, - 9, 0, 1, 0, 1, 1, 8, 21, 21, 9, 0, 4, 5, 8, 9, 1, - 0, 0, 16, 50, 84,118,136,152,186,187,187,187,187,187,187,187, - 187,187,187,187,187,187,187,187,187,187,187,187,187,187, 12, 0, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, 13, 13, - 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 15, 16, 17, 18, 18, 18, 18, 18, 18, - 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 20, 21, 22, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 19, 32, 33, 33, 33, 33, 33, - 34, 19, 19, 19, 19, 19, 19, 35, 19, 36, 37, 38, 38, 38, 38, 38, - 38, 39, 40, 19, 19, 19, 19, 19, 19, 19, 41, 42, 19, 19, 43, 19, - 19, 19, 44, 45, 9, 46, 47, 48, 49, 50, 51, 52, 9, 9, 19, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 53, 19, 19, 53, 19, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 54, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 55, - 0, 0, 1, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, - 1, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, - 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 32, 33, 33, 33, 34, 35, 35, 35, 35, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 2, 2, 51, 51, 52, - 53, 54, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, - 57, 56, 56, 56, 56, 56, 56, 58, 59, 60, 61, 56, 62, 62, 63, 64, - 65, 66, 67, 68, 69, 70, 56, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 71, 62, 62, 62, 62, 72, 72, 72, 72, 72, 72, 72, 72, 72, - 73, 74, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 32, - 32, 32, 32, 32, 32, 32, 32, 87, 87, 87, 87, 87, 87, 87, 87, 87, - 87, 62, 62, 62, 62, 88, 89, 89, 89, 90, 89, 91, 92, 93, 94, 95, - 95, 96, 97, 87, 98, 99,100,101,102,103,104,105,105,105, 2,106, - 107,108,109,110,111,112,113,114,115,116,117, 89,118,119,120,121, - 122,123,124,125,126,127,128,129,130, 87,131,132,133,134, 87,135, - 136,137,138,139,140,141,142,143,144,145,146, 87,147,148,149,150, - 150,150,150,150,150,150,150,150,150,150, 87, 87, 87, 87, 87, 87, - 87, 87, 87, 87, 87, 87,151,152,152,152,152,152,152,152,152,153, - 153,153,153,153, 87, 87, 87, 87, 87,154, 87, 87, 87, 87, 87,155, - 155,155,155,156,157,158,158, 87, 87,159, 87,160,161,162,163,164, - 164,164,164,164,164,164,164,164,164,164,164,164,164,165,165,165, - 165,164,164, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, 87,166,167, - 168,169,170,170,170, 87, 87,171,172, 87, 87, 87, 87, 87, 87, 56, - 56, 56, 56, 56, 56,173, 56, 56, 56,174,175, 51, 56, 56, 87,176, - 176,176,176,176,176, 87, 87, 87, 87, 87, 87, 87, 87, 2, 87,177, - 6,178, 87, 87,179, 87, 87, 87,180, 87,181, 87,182, 87, 33,183, - 183,184, 87, 87, 87, 87, 87, 56, 56, 56, 87, 89, 89, 87, 87, 56, - 56, 56, 56,185, 87, 56, 56, 62, 62, 62, 62, 62, 87, 87, 87, 62, - 87, 87, 87, 87, 87, 87, 87, 56, 87,186,186, 0, 1, 2, 2, 0, - 0, 0, 0, 1, 2, 1, 2, 0, 0, 3, 3, 4, 5, 4, 5, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 0, 0, 7, 0, 8, - 8, 8, 8, 8, 8, 8, 9, 10, 11, 11, 11, 11, 11, 12, 11, 13, - 13, 13, 13, 13, 13, 13, 13, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 15, 16, 16, 16, 16, 16, 17, 18, 18, 18, 18, 18, 18, 19, - 20, 21, 21, 22, 23, 21, 24, 21, 21, 21, 21, 21, 25, 21, 21, 26, - 26, 26, 26, 26, 21, 21, 21, 27, 27, 27, 27, 28, 28, 28, 28, 29, - 29, 29, 29, 30, 30, 26, 21, 21, 21, 21, 21, 21, 21, 31, 21, 32, - 32, 32, 32, 32, 33, 34, 32, 35, 35, 35, 35, 35, 35, 35, 35, 36, - 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 38, - 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 40, - 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 42, - 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43, 43, 43, 43, 43, 44, - 44, 44, 45, 44, 44, 44, 44, 46, 46, 46, 46, 46, 46, 46, 46, 47, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 47, 47, 49, - 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, - 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, - 53, 54, 54, 54, 54, 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 56, - 56, 57, 57, 57, 57, 58, 57, 59, 59, 60, 61, 62, 62, 63, 63, 64, - 64, 64, 64, 64, 64, 64, 64, 65, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 55, 55, 55, 55, 55, 67, 67, 67, 67, 67, 68, 68, 68, 69, - 69, 69, 69, 69, 69, 64, 64, 70, 70, 71, 71, 71, 71, 71, 71, 71, - 71, 71, 8, 8, 8, 8, 8, 72, 72, 72, 72, 72, 72, 72, 72, 73, - 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 75, 76, 76, 76, 13, - 50, 50, 50, 73, 77, 78, 79, 4, 4, 80, 4, 4, 81, 82, 83, 4, - 4, 4, 84, 8, 8, 8, 8, 11, 11, 11, 11, 11, 11, 11, 11, 85, - 0, 0, 0, 0, 0, 0, 86, 0, 4, 0, 0, 0, 8, 8, 8, 0, - 0, 87, 88, 89, 0, 4, 4, 6, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 90, 90, 90, 90, 90, 90, 90, 90, 91, - 91, 91, 91, 91, 91, 4, 4, 92, 92, 92, 92, 92, 92, 92, 92, 50, - 50, 50, 93, 93, 93, 93, 93, 53, 53, 53, 53, 53, 53, 13, 13, 94, - 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 94, 0, 95, - 0, 96, 97, 98, 99, 99, 99, 99,100,101,102,102,102,102,103,104, - 104,104,105, 52, 52, 52, 52, 52, 0,104,104, 0, 0, 0,102, 52, - 52, 0, 0, 0, 0, 52,106, 0, 0, 0, 0, 0,102,102,107,102, - 102,102,102,102,108, 0, 0, 94, 94, 94, 94, 0, 0, 0, 0,109, - 109,109,109,109,109,109,109,109,109,109,109,109,110,110,110,111, - 111,111,111,111,111,111,111,111,111,111,111, 13, 13, 13, 13, 13, - 13,112,112,112,112,112,112, 0, 0,113, 4, 4, 4, 4, 4,114, - 4, 4, 4, 4, 4, 4, 4,115,115,115, 0,116,116,116,116,117, - 117,117,117,117,117, 32, 32,118,118,119,120,120,120, 52, 52,121, - 121,121,121,122,121, 49, 49,123,123,123,123,123,123, 49, 49,124, - 124,124,124,124,124,125,125, 53, 53, 53, 4, 4,126,127, 54, 54, - 54, 54, 54,125,125,125,125,128,128,128,128,128,128,128,128, 4, - 129, 18, 18, 18, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, - 21, 21,130, 21, 21, 21, 21, 8, 0,131, 0, 0, 0, 0, 21, 21, - 21, 21, 21, 21, 21, 21,132, 0, 0, 1, 2, 1, 2,133,101,102, - 134, 52, 52, 52, 52, 0, 0,135,135,135,135,135,135,135,135, 0, - 0, 0, 0, 11, 11, 11, 11, 11, 0, 11, 11, 11, 0, 0,136,137, - 137,138,138,138,138,139, 0,140,140,140,141,141,142,142,142,143, - 143,144,144,144,144,144,144,145,145,145,145,145,146,146,146,147, - 147,147,148,148,148,148,148,149,149,149,150,150,150,150,151,151, - 151,151,151,152,152,152,152,153,153,153,153,153,153,153,153,154, - 154,154,154,155,155,156,156,157,157,157,157,157,157,158,158,159, - 159,160,160,161,161,161,161,162,162,163,163,163,163,163,163,164, - 164,164,164,164,164,165,165,166,166,166,166,167,167,167,167,168, - 168,168,168,169,169,170,170,171,171,171,171,171,171,171,171,172, - 172,172,172,172,172,172,172,173,173,173,173,173,173,173,173,174, - 174,174,174,175,175,175,175,175,175,175,175,175,175,175,175,176, - 176,176,176,177, 21, 21, 21,178,178,178,179,179,179,179,180,180, - 180,180,181,181,181,182,182,183,183,183,183,183,183,183,183,184, - 184,184,184,184,185,185,185,186,186,186,186,186,187,187,187,188, - 188,188,188,188,188,189, 43,190,190,190,190,190,190,190,190,191, - 191,191,192,192,192,192,192,193,193,193,194,193,193,193,193,195, - 195,195,195,195,195,195,195,196,196,196,196,196,196,196,196,197, - 197,197,197,197,197,197,197,198,198,198,198,198,198,198,198,199, - 199,199,199,199,199, 66, 66,200,200,200,200,200, 49, 49, 49,201, - 201,201,201,201,201,201,201,202,202,202,202,202,202,202,202,203, - 203,203,203,203,203,203,203,204,204,204,204,204,204,204,204,205, - 205,205,205,205,205,205,205,206,206,206,206,206,207,207,207,207, - 207,207, 55,208,208,208,208, 32, 32, 32, 32, 32, 32,188,188,209, - 209,209,209,209,209,209,209,210,210,210,210,210,210,210,211,211, - 211,211,211,211,211,211,211,212,212,212,212,212,212,213,213,213, - 213,213,214,214,214,214,214,215,215,215,215,215,215,215,215,216, - 216,216,216,216,216,216,216,110,110,110,110, 39, 39, 39, 39,217, - 217,217,217,217,217,217,217,218,218,218,218,218,218,218,218,219, - 219,219,219,219,219,219,219,220,220,220,220,220,220,220,220,221, - 221,221,221,221,221,221,221,112,112,112,112,112,112,112,112,112, - 112,112,112,222,222,222,223,223,223,223,223,223,224,224,224,225, - 225,225,225,225,225,225,225,226,226,226,226,226,226,226,226,227, - 227,227,227,227,227,227,227,227,227,228,228,228,228,228,228,229, - 229,229,229,229,229,229,229,229,229,229,229,229,229,230, 94,231, - 231,231,231,231,231,231,231,232,232,232,232,232,232,232,232,102, - 102,102,102,102,102,102,102,233, 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, 99,102,234, 99,235,102,236,236, - 236,236,236,236,236,236,236,237,237,237,237,237,237,237,237,237, - 237, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 0, 0, 0, 0, - 0, 0, 0, 0, 0,238,239,240, 0,241, 0, 0, 0, 0, 0,242, - 242,242,242,242,242,242,242, 91, 91, 91, 13, 13, 13, 13, 13,243, - 243,243,243,243,243,243,243,244,244,244,244,245,245,245,245,246, - 246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,248, - 248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,250, - 250,250,250,250,250,250,250,251, 0, 0, 0, 0, 0, 0, 0, 8, - 8, 8, 8, 8, 8, 8, 8, 0, 0, 0, 0, 1, 2, 2, 2, 2, - 2, 3, 0, 0, 0, 4, 0, 2, 2, 2, 2, 2, 3, 2, 2, 2, - 2, 5, 0, 2, 5, 6, 0, 7, 7, 7, 7, 8, 9, 8, 10, 8, - 11, 8, 8, 8, 8, 8, 8, 12, 13, 13, 13, 14, 14, 14, 14, 14, - 15, 14, 14, 16, 17, 17, 17, 17, 17, 17, 17, 18, 19, 19, 19, 19, - 19, 19, 19, 20, 21, 20, 22, 20, 20, 23, 23, 20, 20, 20, 20, 22, - 20, 24, 7, 7, 25, 20, 20, 26, 20, 20, 20, 20, 20, 20, 21, 27, - 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, - 31, 31, 31, 32, 20, 20, 20, 33, 33, 33, 33, 34, 35, 33, 33, 33, - 36, 33, 33, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, - 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, - 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, 48, - 48, 48, 48, 49, 49, 49, 49, 49, 50, 51, 49, 52, 52, 52, 52, 53, - 53, 53, 53, 53, 53, 54, 53, 55, 55, 55, 55, 56, 56, 56, 56, 57, - 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, 60, - 60, 61, 62, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 0, 0, 66, - 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 70, 71, 71, 71, - 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, - 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 79, - 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 82, 7, 7, 7, 83, - 7, 84, 85, 0, 84, 86, 0, 2, 87, 88, 2, 2, 2, 2, 89, 90, - 87, 91, 2, 2, 2, 92, 2, 2, 2, 2, 93, 0, 0, 0, 86, 1, - 0, 0, 94, 0, 95, 96, 0, 4, 0, 0, 0, 0, 0, 0, 4, 97, - 97, 97, 97, 98, 98, 98, 98, 13, 13, 13, 13, 99, 99, 99, 99,100, - 100,100,100, 0,101, 0, 0,102,100,103,104, 0, 0,100, 0,105, - 106,106,106,106,106,106,106,106,106,107,105,108,109,109,109,109, - 109,109,109,109,109,110,108,111,111,111,111,112, 55, 55, 55, 55, - 55, 55,113,109,109,109,110,109,109, 0, 0,114,114,114,114,115, - 115,115,115,116,116,116,116,117,117,117,117, 96, 2, 2, 2, 2, - 2, 94, 2,118,118,118,118,119,119,119,119,120,120,120,120,121, - 121,121,121,121,121,121,122,123,123,123,123,124,124,124,124,124, - 124,124,125,126,126,126,126,127,127,127,127,128,128,128,128, 2, - 2, 3, 2, 2,129,130, 0,131,131,131,131,132, 17, 17, 18, 20, - 20, 20,133, 7, 7, 7,134, 20, 20, 20, 23, 0,135,109,109,109, - 109,109,136,137,137,137,137, 0, 0, 0,138,139,139,139,139,140, - 140,140,140, 84, 0, 0, 0,141,141,141,141,142,142,142,142,143, - 143,143,143,144,144,144,144,145,145,145,145,146,146,146,146,147, - 147,147,147,148,148,148,148,149,149,149,149,150,150,150,150,151, - 151,151,151,152,152,152,152,153,153,153,153,154,154,154,154,155, - 155,155,155,156,156,156,156,157,157,157,157,158,158,158,158,159, - 159,159,159,160,160,160,160,161,161,161,161,162,162,162,162,163, - 163,163,163,164,164,164,164,165,165,165,165,166,166,166,166,167, - 167,167,167,168,168,168,168,169,169,169,169,170,170,170,170,171, - 171,171,171,172,172,172,172,173,173,173,173,174,174,174,174,175, - 175,175,175,176,176,176,176,177,177,177,177,178, 20, 20, 20,179, - 179,179,179,180,180,180,180,181,181,181,181,182,182,182,182,183, - 183,183,183,184,184,184,184,185,185,185,185,186,186,186,186,187, - 187,187,187,188,188,188,188,189,189,189,189,190, 45, 45, 45,191, - 191,191,191,192,192,192,192,193,193,193,193,194,194,194,194,194, - 194,195,194,196,196,196,196,197,197,197,197,198,198,198,198,199, - 199,199,199,200,200,200,200,201,201,201,201,202,202,202,202,203, - 203,203,203,204,204,204,204,205,205,205,205,206,206,206,206,207, - 207,207,207,208,208,208,208,209,209,209,209,210,210,210,210,211, - 211,211,211,212,212,212,212,213,213,213,213,214,214,214,214,215, - 215,215,215,216,216,216,216,217,217,217,217,218,218,218,218,219, - 219,219,219,220,220,220,220,221,221,221,221,222,222,222,222,223, - 223,223,223,224,224,224,224,225,225,225,225,226,226,226,226,227, - 227,227,227,228,228,228,228,229,229,229,229,230,230,230,230,231, - 232,232,232,233,233,233,233,232,232,232,232,234,106,106,106,235, - 106,106,106,106,236,109,109,237,237,237,237,238,238,238,238, 0, - 239, 86, 0, 0, 0,239, 7, 82,138, 7, 0, 0, 0,240, 86,241, - 241,241,241,242,242,242,242,243,243,243,243,244,244,244,244,245, - 245,245,245,246,246,246,246,247,247,247,247,248,248,248,248,249, - 249,249,249,250, 0, 0, 0, 0, 0, 0, 0, 0, 19, 19, 19, 19, - 19, 19, 19, 19, 19, 19, 0, 0, 0, 19, 0, 19, 0, 0, 0, 0, - 0, 26, 26, 1, 1, 1, 1, 9, 9, 9, 9, 0, 9, 9, 9, 9, - 9, 0, 9, 9, 0, 9, 0, 9, 9, 55, 55, 55, 55, 55, 55, 6, - 6, 6, 6, 6, 1, 1, 6, 6, 4, 4, 4, 4, 4, 4, 4, 4, - 14, 14, 14, 14, 14, 14, 14, 3, 3, 3, 3, 3, 0, 3, 3, 0, - 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 1, 1, 1, 3, 3, 1, - 3, 3, 3, 37, 37, 37, 37, 38, 38, 38, 38, 64, 64, 64, 64, 90, - 90, 90, 90, 95, 95, 95, 95, 3, 3, 0, 3, 7, 7, 7, 7, 7, - 1, 1, 1, 1, 7, 7, 7, 0, 0, 7, 7, 5, 5, 5, 5, 11, - 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, 22, 22, 22, 22, 23, - 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, 36, 36, 36, 36, 24, - 24, 24, 24, 24, 24, 24, 0, 18, 18, 18, 18, 25, 25, 25, 25, 25, - 0, 0, 0, 0, 25, 25, 25, 33, 33, 33, 33, 8, 8, 8, 8, 8, - 8, 8, 0, 12, 12, 12, 12, 30, 30, 30, 30, 29, 29, 29, 29, 28, - 28, 28, 28, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 0, 0, - 0, 35, 35, 45, 45, 45, 45, 44, 44, 44, 44, 44, 0, 0, 0, 43, - 43, 43, 43, 46, 46, 46, 46, 31, 31, 31, 31, 32, 32, 0, 0, 32, - 0, 32, 32, 32, 32, 32, 32, 48, 48, 48, 48, 52, 52, 52, 52, 58, - 58, 58, 58, 54, 54, 54, 54, 91, 91, 91, 91, 62, 62, 62, 62, 76, - 76, 76, 76, 93, 93, 93, 93, 70, 70, 70, 70, 73, 73, 73, 73, 1, - 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, - 1, 0, 0, 19, 19, 9, 9, 9, 9, 9, 6, 19, 9, 9, 9, 9, - 9, 19, 19, 9, 9, 9, 19, 6, 19, 19, 19, 19, 19, 19, 9, 0, - 0, 0, 19, 0, 0, 9, 0, 0, 0, 19, 19, 27, 27, 27, 27, 56, - 56, 56, 56, 61, 61, 61, 61, 13, 13, 13, 13, 0, 13, 0, 13, 0, - 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 0, 15, 15, 15, 15, - 15, 15, 15, 15, 1, 1, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 0, 26, 26, 26, 26, 26, 12, 12, 12, 12, 12, 12, 0, 39, - 39, 39, 39, 86, 86, 86, 86, 77, 77, 77, 77, 79, 79, 79, 79, 60, - 60, 60, 60, 65, 65, 65, 65, 75, 75, 75, 75, 69, 69, 69, 69, 69, - 69, 0, 69, 74, 74, 74, 74, 84, 84, 84, 84, 84, 84, 84, 0, 68, - 68, 68, 68, 92, 92, 92, 92, 87, 87, 87, 87, 19, 9, 19, 19, 19, - 19, 0, 0, 2, 2, 2, 2, 19, 19, 19, 4, 3, 3, 0, 0, 1, - 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0, 49, 49, 49, 49, 0, - 1, 1, 1, 71, 71, 71, 71, 67, 67, 67, 67, 42, 42, 42, 42, 41, - 41, 41, 41,118,118,118,118, 53, 53, 53, 53, 59, 59, 59, 59, 40, - 40, 40, 40, 51, 51, 51, 51, 50, 50, 50, 50,135,135,135,135,106, - 106,106,106,104,104,104,104,161,161,161,161,170,170,170,170,110, - 110,110,110, 47, 47, 47, 47, 81, 81, 81, 81,120,120,120,120,116, - 116,116,116,128,128,128,128, 66, 66, 66, 66, 72, 72, 72, 72,173, - 173,173,173, 98, 98, 98, 98, 97, 97, 97, 97, 57, 57, 57, 57, 88, - 88, 88, 88,117,117,117,117,112,112,112,112, 78, 78, 78, 78, 83, - 83, 83, 83, 82, 82, 82, 82,122,122,122,122, 89, 89, 89, 89,130, - 130,130,130,144,144,144,144,165,165,165,165,156,156,156,156,156, - 156, 3, 3,147,147,147,147,148,148,148,148,158,158,158,158,153, - 153,153,153,149,149,149,149, 94, 94, 94, 94, 85, 85, 85, 85,101, - 101,101,101, 96, 96, 96, 96,111,111,111,111,100,100,100,100,100, - 36, 36, 36,108,108,108,108,129,129,129,129,109,109,109,109,107, - 107,107,107,107,107,107, 1,171,171,171,171,137,137,137,137,124, - 124,124,124,123,123,123,123,114,114,114,114,102,102,102,102,126, - 126,126,126,142,142,142,142,125,125,125,125,154,154,154,154,150, - 150,150,150,141,141,141,141,140,140,140,140,121,121,121,121,169, - 169,169,169,133,133,133,133,134,134,134,134,138,138,138,138,143, - 143,143,143,175,175,175,175,145,145,145,145,163,163,163,163, 63, - 63, 63, 63,157,157,157,157, 80, 80, 80, 80,127,127,127,127,166, - 166,166,166,115,115,115,115,159,159,159,159,103,103,103,103,119, - 119,119,119,167,167,167,167,146,146,146,146,172,172,172,172, 99, - 99, 99, 99,136,139, 13, 13,155,155,155,155,136,136,136,136, 17, - 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17,139,139,139,139,105, - 105,105,105, 0, 0, 0, 1, 0, 0, 1, 1,131,131,131,131,151, - 151,151,151,160,160,160,160,152,152,152,152,164,164,164,164,168, - 168,168,168,174,174,174,174,113,113,113,113,132,132,132,132, 15, - 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, - 10, 9, 11, 12, 13, 9, 9, 9, 14, 9, 9, 15, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 16, - 17, 9, 9, 9, 9, 18, 9, 9, 9, 9, 9, 19, 20, 21, 9, 22, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 23, 9, 9, 9, 9, 9, 24, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 25, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 26, - 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, + 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 2, 2, 2, 2, 45, 46, 47, 2, 2, 2, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, + 68, 69, 70, 71, 5, 5, 72, 73, 8, 74, 3, 75, 76, 3, 77, 78, + 3, 3, 5, 79, 5, 5, 80, 3, 81, 82, 83, 84, 85, 3, 3, 3, + 86, 87, 2, 88, 89, 90, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 91, 2, 2, 2, 2, + 92, 2, 2, 2, 2, 2, 2, 2, 2, 93, 2, 2, 94, 95, 96, 97, + 98, 99,100,101,102,103,104,105, 2, 2, 2, 2, 2, 2, 2, 2, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 1, 1, 2, 2, 2, 2,106,107, 2, 2,108,109,110,111,112,113, + 2, 2,114,115, 0,116,117,118,119,120,121,122, 2, 2, 2, 9, + 123,124,125,126,127,128,129,130, 2,131,132,133, 6,134,135,136, + 137,138,139,140,141,142,143,144,145,146, 0,147,148,149,150, 0, + 151,152,153,154,155,156,157,158,159,160,161,162, 0,163,164,165, + 2, 2, 2, 2, 2, 2, 2, 2,166, 2, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,167, + 2, 2, 2, 2, 2, 2, 2, 2,168, 2, 2, 2, 2, 2, 2, 2, + 0, 0,169, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2,170,171,172, 2, 0, 0,173, 0,174,175,176,177, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, + 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 2,178, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 3, 10, 3, 3, 3,179,180, 3, + 3, 3,181,182,183, 6, 8, 0,184,185,186,187,188,189,190,191, + 3, 3, 3, 3,192,193, 0, 0, 0, 0, 0, 0, 0, 0,194, 0, + 195, 7,196, 0, 0,197, 0, 0, 0,198, 0,199, 0,200, 0, 2, + 2,201,202, 0, 0, 0, 0, 0, 6,203,204, 0, 2,205, 0, 0, + 3, 3,206, 3, 3, 0, 3,207, 3, 3, 3, 3, 3, 3, 3, 3, + 3,208, 3, 3, 3, 3, 3, 10, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, + 209, 0, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, + 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 11, 11, 11, 11, 11, 11, 11, 11, + 8, 8, 8, 8, 8, 8, 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, + 9, 9, 9, 9, 9, 9, 9, 9, 13, 13, 13, 13, 13, 13, 13, 13, + 10, 10, 10, 10, 10, 10, 10, 10, 15, 15, 15, 15, 15, 15, 15, 15, + 16, 16, 16, 16, 16, 16, 16, 16, 6, 6, 6, 6, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 1, 1, 1, 1, 4, 4, 9, 9, 9, 9, 9, + 25, 25, 25, 25, 25, 25, 25, 25, 14, 14, 14, 14, 14, 14, 14, 14, + 8, 8, 8, 8, 8, 6, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, + 1, 1, 1, 4, 4, 4, 4, 4, 4, 14, 30, 1, 1, 1, 1, 1, + 9, 9, 9, 9, 9, 10, 10, 10, 9, 9, 9, 9, 9, 7, 7, 7, + 8, 8, 8, 8, 6, 6, 6, 6, 1, 4, 4, 4, 4, 4, 4, 4, + 4, 30, 1, 1, 1, 1, 1, 1, 6, 6, 6, 6, 32, 32, 32, 32, + 24, 24, 24, 24, 24, 24, 24, 24, 9, 9, 9, 9, 9, 9, 9, 10, + 1, 1, 1, 1, 1, 1, 1, 29, 21, 4, 4, 4, 4, 4, 4, 4, + 1, 1, 1, 1, 29, 10, 10, 10, 29, 10, 10, 10, 10, 10, 10, 10, + 1, 1, 1, 1, 1, 1, 1, 4, 6, 6, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6, 6, 8, 8, + 8, 8, 8, 6, 6, 6, 6, 6, 9, 9, 9, 9, 9, 10, 11, 65, + 73, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 86, 87, 88, + 74, 6, 6, 6, 6, 6, 6, 6, 20, 20, 20, 20, 20, 20, 20, 20, + 19, 8, 8, 8, 8, 8, 8, 8, 22, 1, 1, 1, 1, 1, 1, 1, + 21, 1, 1, 1, 1, 1, 1, 1, 4, 17, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 22, 14, 4, 17, 30, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 22, 18, 4, 22, 1, 1, 1, 1, 1, 1, + 14, 14, 14, 9, 9, 9, 9, 9, 1, 1, 1, 1, 1, 21, 22, 14, + 10, 10, 10, 10, 60, 7, 7, 7,100, 1, 1, 1, 1, 1, 1, 1, + 14, 18, 4, 4, 4, 4, 4, 4, 1, 1, 1, 1, 13, 13, 13, 10, + 20, 6, 6, 6, 8, 8, 79, 20, 6, 6, 6, 6, 8, 8,103, 20, + 26, 26, 26, 26, 26, 26, 26, 26, 7, 7, 7, 7, 7, 3, 3, 3, + 63, 7, 7, 7, 7, 7, 7, 7, 1, 1, 34, 1, 1, 1, 1, 1, + 1, 1, 1, 46, 7, 7, 7, 7, 1, 1, 1, 1, 4, 4, 4, 4, + 1, 1, 1, 1, 1, 1, 46, 7, 1, 1, 1, 1, 7, 7, 7, 7, + 1, 1, 1, 1, 1, 1, 14, 18, 1, 1, 1, 1, 1, 1, 1, 21, + 1, 1, 1, 1, 1, 1, 1, 27, 9, 9, 9, 9, 9, 3, 3, 3, + 72, 10, 84, 10, 23, 85, 64, 10, 6, 6, 6, 6, 6, 89, 90,119, + 72, 24, 24, 47, 91,120,121, 92, 36, 7, 74, 10,122,123, 7, 54, + 8, 8, 8, 48, 8, 8, 8, 12, 6, 6, 6, 49, 6, 6, 6, 6, + 12, 12, 12, 12, 19, 19, 19, 19, 19, 19, 19, 19, 6, 12, 12, 12, + 12, 12, 12, 12, 8, 19, 19, 6, 19, 12, 12, 8, 19, 8, 6, 8, + 8, 19, 12, 8, 12, 6, 8, 19, 12, 12, 12, 8, 19, 6, 12, 8, + 19, 8, 19, 19, 12, 50, 12, 6, 1, 1, 93, 19, 94, 93, 19, 19, + 19, 19, 19, 19, 19, 19, 6, 12, 19, 94, 12, 8, 12, 12, 12, 12, + 12, 12, 6, 6, 6, 8, 19, 12, 19, 19, 8, 12, 12, 12, 12, 12, + 6, 6, 1, 6, 6, 6, 6, 6, 13, 20, 20, 13, 13, 13, 13, 13, + 13, 20, 20, 20, 20, 20, 20, 20, 13, 13, 55, 20, 20, 20, 55, 55, + 12, 12, 55, 12, 6, 56, 6, 73, 20, 20, 20,124, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 19, + 6, 8, 12, 6, 12, 12, 12, 12, 6, 6, 12, 75, 19, 12, 19, 8, + 12, 33, 4, 4, 66, 12, 12, 12, 8, 19, 19, 19, 19, 19, 19, 6, + 8, 8, 8, 8, 95, 10, 10, 10, 6, 6, 6, 6, 76, 26,125, 57, + 126, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,127, + 37, 31, 4, 37, 4, 4, 4, 4, 1, 29, 10, 10, 10, 10, 10, 10, + 15, 15, 15, 11, 65, 67, 10, 3, 4, 4, 4, 4, 4, 31,128, 10, + 38, 1, 1, 1, 1, 21, 4, 4, 9, 9, 9, 9, 9, 10, 10, 1, + 1, 1, 39, 4, 4, 4, 96, 33, 4, 4, 68, 51, 40, 4, 4, 1, + 9, 9, 9, 9, 9, 1, 41, 58, 10, 10, 10, 10, 10, 10, 10, 97, + 4, 4, 4, 4, 4, 4, 22, 1, 1, 1, 1, 1, 1, 21, 4, 4, + 4, 4, 13, 47, 10, 13, 51, 24, 1, 1, 1, 4, 4, 51, 4, 4, + 4, 4, 51, 4, 51, 4, 4, 4, 1, 1, 1, 1, 21, 4, 4, 10, + 1, 1, 1, 1,129, 1, 1, 1, 15, 15, 15, 69, 4, 4, 4, 4, + 1, 1, 1, 1, 34, 4, 4, 4, 4, 69, 4, 4, 4, 4, 4, 4, + 1, 1, 1, 1, 1, 17, 22, 14, 18, 4, 4, 4, 17, 14, 18, 14, + 21, 4, 4, 4, 1, 1, 1, 1, 1, 4, 10, 9, 9, 9, 9, 9, + 42, 1, 1, 1, 1, 1, 1, 1, 21, 14, 30, 1, 1, 1, 1, 1, + 18, 4, 4, 17, 14, 14, 18, 1, 1, 1, 1, 27, 14, 14, 1, 1, + 1, 24, 7, 7, 7, 57, 29, 4, 1, 1, 1, 1, 1, 1, 4, 14, + 18, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 22, 1, 1, 1, + 1, 1, 1, 9, 9, 9, 9, 9, 4, 1, 21, 10, 10, 10, 10, 10, + 18, 4, 4, 4, 17, 14, 18, 4, 67, 24, 24, 24,130, 4, 4, 4, + 18, 4, 4, 17, 14, 14, 18, 4, 4, 4, 4, 17, 14, 14, 1, 1, + 58, 7, 7, 7, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1, 1, 14, + 17, 14, 14, 14, 14, 14, 18, 4, 1, 1, 1, 27, 14, 14, 14, 14, + 7, 43, 3, 3, 57, 3, 3, 3, 17, 14, 22, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 22, 4, 17, 14, 14, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 1, 1, 1, 1, 9, 9, 9, 98, 7, 7, 7, 43, + 21, 14, 39, 1, 1, 1, 1, 1, 14, 14, 14, 17, 14, 14, 4, 4, + 4, 4, 17, 14, 14, 14, 1, 1, 99, 27, 14, 14, 14, 14, 14, 14, + 4, 14, 1, 1, 1, 1, 1, 1, 18, 4, 4, 14, 14, 14, 18, 41, + 3, 3, 1, 27, 7, 7, 7, 59, 7, 7, 7, 7, 43, 1, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 17, 14, 4, 4, 4, 14, 14, 14, 14, + 9, 14, 10, 10, 10, 10, 10, 10, 21, 1, 4, 4, 4, 4, 4,131, + 1, 1, 1, 51, 4, 4, 4, 31, 21, 1, 4, 4, 4, 4, 22, 1, + 1, 1, 1, 13, 4, 4, 4, 4, 9, 9, 9, 9, 9, 9, 1, 1, + 41, 3, 10, 10, 10, 10, 10, 10, 10, 44, 44, 3, 4, 3, 3, 3, + 7, 7, 33, 33, 33, 23, 23, 14, 21, 4, 4, 4, 4, 4, 4, 17, + 4, 4, 31, 4, 1, 1, 21, 4, 4, 4, 4, 4, 4, 4, 4, 3, + 3, 3, 3, 40, 3, 3, 3, 3, 10, 10, 44, 3, 47, 10, 10, 10, + 1, 1, 1, 1, 1, 27, 18, 4, 17, 4, 4, 4, 18, 17, 18, 22, + 1, 1, 1, 14, 4, 1, 1, 4, 22, 14, 30, 27, 14, 14, 14, 1, + 21, 4, 22, 1, 1, 1, 1, 1, 1, 17, 18, 17, 14, 14, 18, 27, + 9, 9, 9, 9, 9, 14, 18, 3, 6, 6, 6, 6, 6, 76, 56, 6, + 1, 1, 1, 1, 1, 1, 21, 4, 1, 1, 1, 1, 1, 1, 41, 39, + 132, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,133, 70, 70, + 1, 1, 1, 1, 1, 29, 10, 16,134, 1, 1, 1, 1, 1, 1, 1, + 1, 4, 17, 14, 14, 14, 14, 30, 1, 4, 61, 10, 10, 10, 10, 10, + 1, 1, 4, 18, 4, 4, 4, 14, 14, 14, 14, 17, 18, 4, 4, 4, + 4, 4, 10, 42, 10, 67, 21, 4, 10, 10, 10, 77, 10, 37, 4, 69, + 1, 34, 1, 1, 1, 1, 1, 1, 1, 1, 21, 22, 1, 1, 1, 1, + 1, 1, 1, 1, 21, 1, 1, 1, 4, 17, 14, 18, 17, 14, 14, 14, + 14, 17, 14, 14, 18, 4, 4, 4, 3, 3, 10, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 7, 7, 3, 1, 1, 1, 21, 17, 18, 4, 10, + 1, 1, 27, 17, 4, 4, 4, 4, 17, 17, 18, 4, 4, 4, 17, 14, + 10, 10, 10, 42, 10, 10, 10, 10, 4, 4, 4, 4, 4, 4, 4, 78, + 4, 4, 30, 1, 1, 1, 1, 1, 1, 1, 17, 4, 4, 17, 17, 14, + 14, 17, 30, 1, 1, 1, 1, 10, 44, 3, 3, 3, 3, 33, 4, 4, + 4, 4, 3, 3, 3, 3, 47, 10, 27, 4, 4, 14, 4, 18, 4, 1, + 1, 1, 1, 17, 4, 14, 18, 18, 4, 14, 14, 14, 14, 14, 10, 10, + 1, 1, 14, 14, 14, 14, 4, 4, 4, 4, 14, 4, 4, 31, 10, 10, + 9, 9, 9, 9, 9, 9, 99, 1, 6, 6, 6, 6, 19, 6, 6, 6, + 4, 31, 4, 4, 4, 4, 4, 4, 17, 4, 4, 4, 22, 1, 21, 1, + 1, 1, 22, 27, 4, 1, 1, 1, 6, 6, 6, 6, 6, 6, 13, 13, + 13, 13, 13, 13, 13, 56, 6, 6, 6, 6, 6, 6, 56, 6, 6, 6, + 6, 6, 6, 6, 6,101, 13, 13, 12, 12, 12, 6, 6, 6, 6, 12, + 6, 6, 6, 6, 19, 8, 8, 8, 6, 6, 6, 6, 8, 8, 79,102, + 62, 62, 62, 62, 62,135, 15, 15, 26, 26, 26, 10, 45,104, 45,104, + 10, 10, 10, 10,136, 15, 15,137, 10, 10, 10, 10,105,106, 10,107, + 108, 10, 52, 53, 10, 10, 10, 10, 10, 65,108, 10, 10, 10, 10,138, + 139, 13, 7, 7, 7, 11, 52,140, 7, 7, 7, 7, 7, 11, 52, 70, + 4, 4, 4, 4, 4, 4,141, 66, 78, 66, 78, 4, 4, 4, 4, 4, + 3, 71, 3, 80, 3, 19, 8, 6, 8, 12, 80, 3, 75, 8, 8, 3, + 3, 3, 71, 71, 71, 8, 8,142, 8, 8, 50, 1,109, 3, 6, 8, + 11, 11, 75, 6, 6, 36, 3,110, 16,143,144, 16,111, 3, 3, 3, + 11, 11, 35, 3, 3, 11, 3, 3, 35, 36, 3, 35, 3, 3, 3, 35, + 3, 3, 3, 3, 3, 3, 3, 11, 3, 35, 35, 3, 3, 3, 3, 3, + 3, 3, 11, 11, 11, 11, 11, 11, 3, 3, 3, 3, 23, 23, 3, 3, + 11, 3, 3, 3,145,146, 3, 3, 3, 3, 3, 3, 3, 3, 35, 3, + 3, 3, 3, 3, 3, 36, 11, 11, 11, 11, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 11, 11, 11, 3, 3, 3, 3, 3, 3, 3, + 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, 3, 3, 3, 7, 7, 7, + 3, 3, 3, 36, 3, 3, 3, 3, 36, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 11, 11, 11, 11, 3, 3, 3, 3, 3, 3, 3, 36, + 3, 3, 3, 3, 23, 23, 23, 23, 23, 23, 23, 7, 7, 7, 7, 7, + 7, 7, 3, 3, 3, 3, 3, 3, 11, 11, 52,112, 11, 11, 11, 11, + 11, 11, 11, 23, 23, 23, 23, 23, 11, 52, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28,112, 11, 11, 11, 11, 11, 11, 11, 23, 23, 11, 11, + 11, 11, 11, 11, 11, 11, 23, 11, 11, 11, 35, 36, 11, 11, 35, 3, + 12, 8, 12, 19, 19, 19, 19, 8, 12, 12, 19, 6, 6, 6, 13, 8, + 12, 12,110, 3, 3, 80, 19,147, 4, 12, 6, 6, 76, 10, 60, 10, + 1, 1, 1, 1, 1, 1, 1, 34, 10, 10, 10, 10, 10, 10, 10, 37, + 10, 45, 45, 10,105,106, 45, 10, 10, 10, 10, 64, 10, 77, 45, 10, + 45, 23, 23, 23, 23, 10, 10, 42, 10, 10, 10, 10, 10, 26, 10, 10, + 77,148, 10, 10, 10, 10, 10, 10, 3, 10, 81, 28, 28, 28,149, 26, + 72, 10,150,113, 23, 23, 23, 23, 23, 3, 23, 23, 23, 23,114, 70, + 151, 16, 16, 16, 16, 4, 4, 14,115, 13, 13, 3, 16,152, 29, 3, + 58, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 21,153,116, 38, + 1, 1, 1, 1, 1, 29, 13, 38, 3, 7, 7, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 7, 7, 7, 7, 1, 1, 1, 1, 1, 1,117, 10, + 12, 12, 12, 12, 12, 12, 12, 21, 66,154, 4, 4, 4, 4, 4, 42, + 12, 12, 12, 12, 12, 12, 13, 4, 1, 1, 1, 16, 16, 16, 16, 16, + 4, 10, 10, 10, 10, 10, 10, 10, 20, 20, 20,116, 13, 13, 13, 13, + 20, 12, 12, 12, 12, 12, 12, 12, 6, 12, 12, 12, 12, 12, 12, 12, + 56, 6, 6, 6, 19, 19, 19, 12, 12, 12, 12, 12, 55,155, 19, 50, + 12, 12, 6, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 8, 8, 12, + 8, 8, 12, 12, 12, 12, 12, 12, 12, 12, 8, 8, 19, 19, 12, 12, + 12, 12, 12, 12, 12, 12, 8, 8, 95, 13,156, 50, 13, 50, 1, 1, + 1, 22, 1, 22, 1, 21, 1, 1, 1, 27, 18, 17, 3, 3, 4, 4, + 7, 7, 7, 3, 82, 3, 3, 3, 1, 1, 10, 10, 10, 10, 10, 10, + 14, 1, 1, 1, 1, 1, 1, 1, 1, 1, 14, 14, 14, 14, 14, 14, + 14, 14, 4, 4, 4, 4, 4, 10, 4, 1, 1, 1, 10, 39, 39, 21, + 1, 1, 1, 4, 4, 4, 4, 10, 1, 1, 1, 21, 4, 4, 4, 4, + 4, 14, 14, 14, 14, 14, 14, 61, 1, 21, 14, 4, 4, 14, 4, 14, + 61, 10, 10, 10, 10, 10, 10, 42, 1, 1, 21, 38, 1, 1, 1, 1, + 1, 1, 1, 1, 21, 4, 4, 17, 18, 17, 18, 4, 4, 4, 4, 4, + 1, 21, 1, 1, 1, 1, 17, 14, 9, 9, 9, 9, 9, 9, 10, 10, + 38, 1, 1, 41, 3, 27, 17, 1, 22, 4, 22, 21, 22, 1, 1, 4, + 1, 1, 1, 1, 1, 1, 34, 10, 1, 1, 1, 1, 1, 27, 4, 14, + 10, 34,157, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6,102, 13, 13, + 6, 6, 6, 6,101, 20, 20, 20, 1, 27, 18, 14, 17, 61, 18, 4, + 6, 6, 6, 6, 6, 6, 50, 22, 1, 1, 1, 1,158, 1, 1, 1, + 1, 20, 20, 20, 20, 20, 20, 20, 20, 91, 3, 3, 3, 3, 3, 3, + 3, 58, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 28, + 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, + 1, 1, 1, 1, 1, 1, 82, 3, 10, 10, 10, 81, 53, 10, 10, 10, + 64,159,160, 28, 28, 28, 28, 28, 28, 28, 53, 81, 53, 10,107,161, + 10, 10, 10, 10,114, 28, 28, 53, 10,162, 11, 11, 67, 10, 10, 10, + 1, 1, 1, 1, 1, 1, 1,163, 10, 10, 84, 10, 23, 85, 64, 10, + 6, 6, 6, 6, 6, 89, 90, 52, 53, 23, 10, 1, 1, 1, 1, 1, + 38, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 13, + 24,164, 57, 24, 36, 11, 35, 3, 3, 3, 3, 3,165, 15, 3, 3, + 10, 10, 10, 60, 7, 7, 7, 7, 7, 7, 7, 43, 3, 3, 3, 3, + 16, 16,111, 7, 43, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 3, + 3, 3, 3, 3, 3, 3, 33, 4, 83, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 59, 1,113, 1, 1, 1, 1, 16, 16, 16, + 166, 16, 16, 16, 16, 16, 16, 16, 8, 8, 8, 12, 6, 6, 6, 6, + 1, 1, 1, 29, 7, 7, 7, 7, 1, 1, 1, 41, 63, 7, 7, 7, + 1, 1, 1, 1, 1, 46, 7, 7, 1, 1, 1, 7, 7, 7, 7, 54, + 1, 1, 1, 1, 1, 1, 7, 1, 1, 1, 1, 1, 1, 1, 46, 54, + 1, 1, 1, 1, 58, 1, 1, 1, 1, 1, 21, 4, 4, 83, 7, 7, + 6, 6, 6, 6, 6, 7, 7, 7, 1, 1, 4, 4, 4, 4, 4, 4, + 9, 9, 9, 9, 9, 1, 1, 38, 8, 8, 8, 8,167, 4, 4,115, + 6, 6, 6, 6, 6, 6, 6, 11, 1, 1, 1, 1, 1, 21,168, 26, + 44, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, + 7, 7, 7, 59, 1, 1, 1, 1, 83, 7, 54, 10, 10, 10, 10, 10, + 1, 4, 4, 10, 10, 10, 10, 10, 1, 1, 46, 7, 7, 7, 7, 7, + 18, 30, 1, 1, 1, 1, 1, 1, 4, 4, 4, 31, 10, 10, 10, 10, + 10, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 9, 9, 9, 9, 9, + 22, 21, 22, 1, 1, 1, 1, 21, 14, 18, 4, 17, 18, 31, 97, 10, + 10, 4, 4, 4, 4, 4, 96, 15, 1, 1, 1, 21, 4, 4, 18, 4, + 4, 4, 4, 9, 9, 9, 9, 9, 10, 10, 27, 30, 1, 1, 1, 1, + 1, 21, 10, 1, 1, 1, 1, 1, 1, 27, 14, 4, 4, 4, 4, 17, + 30, 1, 29, 10, 37, 4, 31, 18, 9, 9, 9, 9, 9, 29, 29, 10, + 60, 7, 7, 7, 7, 7, 7, 7, 4, 14, 17, 4, 10, 10, 10, 22, + 17, 14, 14, 14, 14, 14, 14, 14, 1, 1, 1, 27, 14, 14, 30, 1, + 1, 14, 14, 4, 4, 4, 4, 4, 1, 1, 1, 1, 14, 18, 4, 4, + 4, 14, 14, 14, 14, 14, 14, 17, 22, 22, 10, 10, 10, 10, 10, 10, + 37, 4, 4, 4, 4, 4, 4, 4, 1, 1, 27, 14, 4, 4, 4, 4, + 14, 4, 17, 22, 1, 29, 10, 10, 9, 9, 9, 9, 9, 10, 10, 22, + 14, 18, 4, 4, 17, 17, 14, 18, 17, 4, 1, 39, 1, 1, 1, 1, + 14, 4, 4, 4, 14, 14, 4, 18, 31, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 1, 1, 4, 4, 14, 18, 4, 4, 4, 17, 18, 18, + 31, 10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 21, 18, 14, + 4, 4, 4, 18, 29, 10, 10, 10, 1, 1, 1, 1, 1, 1, 21, 18, + 14, 4, 4, 18, 4, 4, 4, 4, 9, 9, 9, 9, 9, 7, 10, 44, + 4, 4, 4, 4, 18, 31, 10, 10, 7, 7, 7, 7, 7, 7, 7, 59, + 14, 14, 14, 14, 14, 18, 17, 22, 30, 18, 10, 10, 10, 10, 10, 10, + 27, 14, 4, 4, 4, 4, 14, 14, 22, 39, 14, 14, 14, 14, 14, 14, + 21, 4, 4, 4, 4, 22, 1, 1, 1, 21, 4, 4, 17, 21, 4, 31, + 10, 10, 10, 37, 4, 4, 4, 4, 21, 4, 4, 17, 18, 4, 1, 1, + 1, 1, 1, 1, 1, 4, 4, 4, 4, 4, 4, 17, 4, 10, 39, 10, + 17, 4, 17, 17, 14, 14, 14, 14, 4, 4, 4, 4, 4, 4, 4, 18, + 10, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 17, 4, 4, 4, + 17, 4, 18, 4, 4, 4, 4, 4, 4, 4, 4, 21, 4, 4, 4, 4, + 1, 1, 1, 1, 1, 14, 14, 14, 4, 17, 18, 18, 1, 1, 1, 1, + 1, 1, 1, 1, 34, 1, 1, 1, 1, 21, 17, 61, 10, 10, 10, 10, + 4, 27, 1, 1, 1, 1, 1, 1, 1, 1, 14, 4, 4, 4, 4, 14, + 17, 31, 10, 10, 10, 10, 10, 10, 9, 9, 9, 9, 9, 4, 4, 4, + 7, 7, 43, 3, 3, 3, 57, 24, 82, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 47, 22, 1, 1, 21, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 14, 18, 4, 4, 4, 31, 10, 10, 10, 10, 10, + 4, 4, 4, 31, 10, 10, 3, 3, 13, 13, 44, 3, 3, 3, 3, 3, + 9, 9, 9, 9, 9,169, 7, 7, 7, 59, 1, 1, 1, 1, 1, 1, + 13, 38, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 34,117, 10, + 7, 7, 7, 54, 10, 10, 10, 10, 8, 8, 8, 8, 8, 12, 6, 6, + 27, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 18, + 4, 68, 13, 13, 13, 13, 13, 13, 13, 42, 4, 4, 4, 4, 4, 4, + 14, 13, 16, 16, 16, 16, 16, 16, 1, 1, 1, 1, 1, 1, 33, 31, + 3, 3,118, 18, 4, 3,118, 14, 14,170, 15, 15, 15, 69, 4, 4, + 4, 40, 33, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, + 3, 4, 40, 3, 3, 3, 3, 3, 48, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 49, 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 48, 6, 6, 6, 6, 49, 6, 6, 6, 8, 8, + 8, 8, 48, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 49, + 6, 6, 6, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 48, + 6, 6, 6, 6, 49, 6, 6, 6, 8, 8, 8, 8, 48, 6, 6, 6, + 6, 49, 6, 6, 6, 12, 6, 9, 4, 4, 4, 40, 3, 33, 4, 4, + 4, 4, 4, 4, 4, 4, 40, 3, 3, 3, 33, 3, 3, 3, 3, 3, + 3, 3, 40, 47, 10, 10, 10, 10, 10, 10, 10, 10, 10, 37, 4, 4, + 6, 6, 6, 6, 6,109, 6, 6, 4, 4, 4, 68, 13, 13, 13, 13, + 9, 9, 9, 9, 9, 9, 9, 41, 1, 1, 1, 1, 1, 1, 4, 4, + 9, 9, 9, 9, 9, 9, 9,171, 1, 1, 1, 1, 1, 34, 4, 4, + 172, 9, 9, 9, 9, 9, 9, 98, 1, 21, 1, 22, 1, 1, 1, 4, + 1, 1, 21, 4, 4, 4, 4, 34, 6, 6, 4, 4, 4, 68, 13, 13, + 7, 7, 7, 7, 7, 7, 63, 7,173, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 63, 7, 7, 7, 7, 7, 7, 43, 3, + 3, 3, 3, 3, 3, 92, 20, 20, 2, 2, 7, 7, 3, 3, 26, 26, + 12, 12, 4, 4, 5, 5, 15, 15, 9, 9, 13, 13, 21, 21, 25, 25, + 9, 5, 6, 6, 10, 10, 1, 1, 14, 14, 12, 10, 10, 12, 5, 9, + 24, 24, 7, 12, 12, 7, 22, 18, 23, 23, 0, 0, 17, 17, 7, 10, + 18, 22, 7, 21, 10, 7, 12, 21, 8, 8, 26, 12, 7, 6, 25, 26, + 26, 25, 21, 12, 6, 7, 21, 7, 12, 26, 7, 26, 21, 6, 15, 26, + 21, 26, 20, 19, 7, 15, 26, 21, 9, 25, 5, 25, 5, 7, 6, 12, + 25, 22, 18, 21, 15, 21, 6, 24, 6, 5, 26, 23, 26, 7, 15, 7, + 21, 15, 10, 21, 29, 29, 26, 15, 21, 17, 25, 21, 11, 11, 21, 23, + 12, 6, 1, 12, 18, 18, 9, 26, 29, 21, 21, 9, 24, 5, 25, 9, + 5, 21, 17, 21, 11, 12, 8, 24, 26, 9, 21, 22, 23, 26, 12, 15, + 23, 21, 21, 25, 9, 22, 21, 18, 24, 16, 5, 22, 25, 18, 24, 26, + 26, 24, 9, 8, 8, 5, 9, 6, 12, 1, 21, 1, 13, 21, 13, 7, + 17, 7, 5, 6, 5, 24, 9, 24, 22, 20, 21, 20, 19, 21, 21, 16, + 16, 21, 7, 5, 5, 26, 14, 15, 18, 25, 7, 14, 17, 22, 17, 6, + 24, 6, 6, 21, 26, 10, 25, 0, 7, 20, 25, 1, 24, 15, 7, 19, + 9, 21, 17, 26, 23, 12, 17, 12, 1, 21, 24, 7, 23, 7, 12, 23, + 29, 7, 7, 22, 14, 7, 29, 1, 27, 28, 1, 29, 21, 29, 15, 6, + 18, 6, 12, 11, 26, 5, 14, 9, 5, 14, 26, 22, 18, 26, 5, 12, + 22, 21, 18, 17, 26, 6, 26, 14, 14, 6, 12, 24, 11, 21, 24, 9, + 6, 9, 6, 10, 7, 25, 17, 16, 16, 22, 16, 16, 25, 17, 7, 1, + 25, 24, 26, 1, 21, 14, 9, 12, 12, 17, 13, 15, 10, 1, 13, 23, + 7, 13, 23, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, + 11, 12, 13, 0, 14, 0, 0, 0, 0, 0, 15, 0, 16, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 17, 18, 19, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 20, 0, 21, 22, 23, 0, 0, 0, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 0, 0, 36, 0, 37, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 40, 0, 0, + 0, 0, 0, 0, 41, 42, 43, 44, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 9, 0, 0, 0, 10, + 11, 12, 13, 0, 14, 15, 16, 0, 17, 18, 19, 20, 21, 1, 22, 1, + 23, 1, 2, 1, 2, 1, 2, 0, 2, 1, 24, 1, 2, 25, 2, 0, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 0, 36, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 38, 0, 0, + 39, 0, 0, 40, 0, 41, 0, 0, 0, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 0, 0, 51, 0, 0, 0, 52, 0, 0, 0, 53, 0, 0, 0, + 0, 0, 0, 0, 54, 0, 3, 0, 55, 56, 0, 57, 0, 0, 0, 0, + 0, 0, 58, 59, 60, 0, 0, 0, 0, 61, 0, 0, 62, 63, 64, 4, + 65, 0, 0, 66, 67, 0, 0, 0, 68, 0, 0, 0, 0, 69, 0, 0, + 0, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 71, 0, 0, 0, 72, 0, 73, 0, 0, 74, 0, 0, 75, 0, 0, 0, + 0, 0, 0, 0, 0, 76, 77, 0, 0, 0, 0, 78, 79, 0, 80, 81, + 0, 0, 82, 5, 0, 83, 4, 0, 84, 85, 0, 0, 86, 87, 88, 0, + 89, 0, 90, 0, 91, 0, 0, 3, 92, 3, 0, 93, 0, 94, 0, 0, + 0, 5, 0, 0, 0, 95, 96, 0, 97, 98, 99,100, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 0,101,102, 0, 0, 0, 0, 0, 0,103, 0, + 0, 0, 0, 0, 0,104, 0, 0, 0, 0, 0, 0,105, 6, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0,106, 0, 0,107, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0,108,109, 0, 0,110, 0, 0, 0, 0, 0, + 0,111, 0,112, 0, 6, 0, 0, 0, 0, 0,113,114, 0, 0, 0, + 0, 0, 0, 0,115, 0, 0, 0,116, 0, 0, 0,117, 0, 0, 0, + 0, 0, 0, 0,118, 0,119, 0, 0, 0, 0, 0, 0, 0, 2, 1, + 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 11, 0, 0, 0, 14, 0, + 0, 0, 16, 3, 17, 18, 19, 20, 21, 22, 0, 23, 0, 0, 0, 0, + 24, 25, 26, 27, 0, 0, 0, 0, 28, 0, 0, 29, 30, 0, 31, 0, + 32, 33, 0, 0, 34, 0, 35, 36, 0, 0, 0, 0, 0, 37, 38, 0, + 39, 40, 0, 0, 41, 0, 0, 0, 42, 0, 0, 43, 44, 45, 46, 1, + 47, 0, 0, 1, 0, 0, 7, 1, 48, 0, 0, 0, 0, 0, 49, 50, + 0, 0, 0, 0, 0, 0, 51, 52, 0, 0, 0, 0, 0, 0, 53, 54, + 0, 0, 0, 0, 55, 0, 56, 0, 0, 0, 57, 58, 0, 0, 0, 59, + 0, 0, 0, 0, 0, 0, 60, 12, 0, 0, 0, 0, 61, 0, 0, 0, + 62, 0, 5, 0, 63, 0, 0, 0, 0, 64, 0, 0, 0, 0, 65, 0, + 66, 0, 0, 0, 0, 6, 67, 0, 0, 0, 68, 69, 70, 71, 0, 0, + 0, 0, 72, 5, 0, 73, 74, 0, 0, 75, 0, 0, 0, 76, 77, 0, + 0, 0, 78, 0, 79, 80, 81, 82, 83, 3, 84, 0, 85, 86, 87, 0, + 0, 8, 9, 0, 0, 3, 3, 0, 0, 88, 0, 0, 89, 0, 0, 0, + 0, 8, 90, 0, 91, 0, 0, 0, 0, 0, 9, 10, 0, 92, 0, 5, + 0, 3, 9, 0, 0, 93, 0, 0, 94, 0, 0, 6, 0, 0, 0, 0, + 0, 0, 95, 96, 0, 0, 10, 0, 0, 1, 0, 0, 97, 0, 0, 0, + 0, 98, 0, 0, 0, 0, 12, 0, 0, 13, 0, 0, 0, 0, 99,100, + 0, 0,101, 0, 0,102, 0, 0, 0,103, 0, 0, 0,104, 0, 0, + 0,105, 0, 0, 0, 0,106,107, 13, 0, 0,108, 0, 0, 0, 10, + 0, 0,109,110, 0, 0,111,112, 0, 0, 0, 0, 0, 0,113, 0, + 0,114, 0, 0, 0, 0,115, 1, 0,116,117,118, 6, 0, 0,119, + 7, 0, 0,120, 0, 0, 0,121, 0, 0, 0, 0, 0, 0,122, 0, + 0,123, 0, 0, 0, 0,124, 11, 0, 0, 0, 0, 0, 6, 0, 0, + 0, 0, 5, 15, 0, 0, 0, 0,125, 0, 0,126, 0, 0, 0, 0, + 15, 0, 0,127, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0,128, 0, + 0, 0,129, 0,130, 0, 0, 0, 0,131,132,133, 0,134, 0,135, + 0, 0, 0,136,137,138, 0, 8, 0, 0, 0, 0, 0, 7, 0, 0, + 0,139, 0, 0, 0,140, 0, 0, 0,141, 0, 0, 0,142,143, 0, + 144, 0, 0,145, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 9, + 1, 1, 1, 1, 0, 0, 0, 2, 0, 3, 0, 0, 3, 0, 0, 0, + 0, 0, 0, 19, 0, 0, 0, 6, 11, 0, 0, 0, 0, 14, 0, 0, + 15, 0, 0, 0, 0, 0, 0, 17, 29, 0, 0, 0, 0, 0, 31, 0, + 0, 2, 0, 0, 1, 12, 0, 0, 1, 49, 50, 5, 51, 52, 53, 5, + 5, 22, 32, 23, 1, 54, 24, 55, 16, 33, 56, 57, 58, 1, 1, 1, + 6, 1, 0, 0, 59, 7, 60, 1, 34, 5, 7, 61, 62, 63, 64, 65, + 66, 67, 0, 0, 1, 1, 68, 0, 0, 0, 69, 70, 71, 35, 1, 25, + 72, 0, 0, 0, 0, 8, 1, 26, 16, 26, 73, 27, 74, 0, 0, 0, + 36, 25, 75, 37, 7, 37, 76, 0, 0, 0, 6, 1, 7, 0, 0, 17, + 0, 8, 38, 1, 1, 13, 13, 11, 0, 0, 39, 0, 0, 6, 18, 1, + 0, 0, 8, 16, 5, 1, 1, 1, 77, 7, 36, 18, 78, 7, 35, 1, + 79, 10, 0, 0, 0, 80, 0, 0, 0, 0, 2, 3, 0, 0, 14, 0, + 0, 0, 81, 0, 0, 0, 82, 0, 0, 0, 83, 0, 0, 0, 84, 0, + 0, 0, 40, 0, 0, 85, 86, 0, 87, 88, 89, 90, 91, 92, 0, 0, + 0, 93, 0, 0, 0, 15, 28, 0, 0, 0, 0, 13, 0, 41, 0, 0, + 14, 0, 0, 20, 0, 0, 94, 0, 0, 0, 95, 0, 0, 6, 29, 0, + 0, 13, 1, 96, 1, 18, 33, 97, 25, 23, 7, 1, 1, 1, 1, 27, + 1, 7, 98, 0, 0, 9, 0, 0, 0, 0, 6, 23, 1, 0, 0, 0, + 0, 0, 30, 0, 0, 21, 0, 0, 30, 0, 0, 0, 0, 15, 0, 0, + 12, 32, 24, 5, 99, 22, 42, 17, 0, 10, 11, 0, 7, 1, 7,100, + 101, 1, 1, 1, 1,102,103,104,105, 1,106, 10, 20,107,108, 5, + 10, 0, 0, 0, 0, 0,109,110, 0, 0,111, 0, 0, 1, 1, 11, + 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 43, 40, 2, 0, 0, 0, + 44,112, 10, 8, 20, 0, 0, 0, 0, 0, 0,113, 1, 16, 5, 24, + 0, 8, 12, 0, 0, 0, 0,114, 0, 0,115, 2, 0,116, 0, 0, + 0, 1, 0, 0, 0, 0, 13, 11, 0, 0, 6, 10, 0, 0, 45, 39, + 0, 45, 16, 18, 46, 27, 0, 0, 3, 0, 0, 2, 12, 0, 0, 0, + 2, 3, 0, 0, 3, 0, 21, 0, 0, 31, 0, 0, 0, 0,117, 0, + 0, 0, 15, 9, 0, 8, 1, 10, 1, 10, 0, 0, 0, 0, 0, 30, + 14, 21, 0, 0, 47, 0, 0, 0, 9, 0, 0, 0, 0, 47, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 28, 0, 0, 4, 0, 21, 41, 0, 0, + 28, 0, 0, 0, 22, 42, 0, 0,118, 0, 0, 0, 0, 0, 0,119, + 0,120,121,122,123, 0, 43, 5, 48, 13, 34, 0, 0, 0, 8, 11, + 8, 10, 0, 0, 1, 12, 1, 1, 1, 1, 26, 1, 38, 44, 12, 0, + 0, 0, 0, 1, 0, 0, 0,124, 0, 0, 0, 46, 6, 19, 0, 8, + 0, 20, 0, 0, 5, 48, 0, 0, 0, 1,125, 0, 0, 0, 0,230, + 230,230,230, 0, 0, 0, 9, 9, 0, 0, 0, 0, 9, 0, 0,220, + 220,220,220, 0, 0, 0,230,230,230,220,230, 0, 0,230,230, 7, + 0, 0, 0,230, 0, 0, 0,230,230, 0, 0,230,230,230, 0, 0, + 230,230,230, 0, 0, 9, 0, 0, 0, 0, 7,230,230,230,220, 0, + 220, 0, 0,230,220,220,220, 0, 0,230, 0, 0,230, 0, 0, 0, + 0, 7, 0, 1, 1, 1, 1,220,230,230,230,220,220,230,230,220, + 230,230,220,230, 0, 0,230,230,220, 0, 0, 0, 9, 9, 0,220, + 0, 0, 0, 0, 0, 9, 9, 0, 9, 7, 0, 1,220,220,220,220, + 220,220,230,230,230,220,220,230,220,220,230,230,220,230,230,220, + 230,220,230,230,230, 0,230, 0,220,220,220,220,220, 0, 0, 9, + 9, 0, 0, 1, 0, 0, 0, 0, 0, 0,220,230, 0,230,230, 0, + 0,220,220, 0, 0,230,220, 0, 0, 9, 7,220,220,220, 0,230, + 232,220,220,220,220,232,216,220,202,202,220,220,220,220,202,202, + 220,220,220,230,240,230,220,230,220,220, 0,232,220,220,230,233, + 234,234,233,234,234,233,230, 0,220,230,230,230,230,222,220,230, + 222,228,230, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, + 22, 0, 23, 0, 24, 25, 0,230,220, 0, 18, 30, 31, 32, 0, 0, + 0, 0, 27, 28, 29, 30, 31, 32, 33, 34,230, 35, 0, 0, 0,230, + 0,220,230, 0, 36, 0, 0,220,220,230,220,220,230,230, 0,230, + 230, 0,220, 27, 28, 29,230, 0,230,220,230, 0, 84, 91, 0,103, + 103, 9, 0,107,107,107,107,118,118, 9, 0,122,122,122,122, 0, + 220, 0,220, 0,216, 0, 0, 0,129,130, 0,132, 0, 0, 0, 0, + 0,130,130,130,130, 0, 0,130, 0,230,230, 9, 0,230,230, 0, + 0,220, 0, 0,228, 0, 0, 0,222,230,220,230, 0, 0,220,230, + 220, 0,220,230,230,230,234,230, 0, 1, 1,230,234,214,220,202, + 230,230,230,230,230,232,228,228,220,218,230,233,220,230,220,230, + 230, 1, 1, 1, 1, 1,230, 0, 1, 1,230,220,230, 1, 1, 0, + 0,218,228,232,222,224,224, 0, 8, 8, 0,220, 0, 0,230, 0, + 0, 26, 0, 0,220, 0,230,230, 1,220, 0, 0,230,220, 0, 0, + 7, 9, 0, 6, 6, 0, 0, 0, 0, 1, 0, 0,216,216, 1, 1, + 1, 0, 0, 0,226,216,216,216,216,216, 0,232,232,220,230,230, + 230, 7, 0, 1, 0, 0, 32, 0, 48, 0, 0, 84, 96,135,144, 10, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,176, 0, 0, 0, 0, 0, 0, 0, 0, 8, 16, 24, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 40, 0, 0, + 0, 0, 0, 48, 56, 64, 0, 0, 0, 0, 0, 72, 80, 88, 96,104, + 0, 0, 0, 0, 0, 0, 0, 0,112, 0,120, 0, 0, 0, 0, 0, + 0,128,136,144,152,160,168, 0, 0, 0,176,184,192, 0, 0, 0, + 0, 0, 0,200, 0, 0, 0, 0, 0, 0, 0, 0,208, 0, 0,216, + 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, + 36, 0, 0, 0, 24, 0, 0, 0, 24, 0, 0, 0, 0, 0, 64, 0, + 68, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 72, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 40, 20, 0, 0, 0, 0, 0, 0, + 20, 0, 20, 0, 0, 0, 0, 0, 0, 0, 76, 80, 84, 88, 0, 0, + 12, 92, 96, 28, 0,100,104, 4, 4, 4,108, 44,112,116,120, 4, + 124, 0,128,132, 4, 4, 48,136,140, 0, 32, 0, 0, 0, 40, 0, + 0, 0, 0, 0, 0, 0, 4, 48, 0, 52,144, 20,148,152, 4, 0, + 0,156,160,164,168,172, 4, 0,176, 56, 60,180, 32, 0, 16,184, + 56, 0, 0, 0, 0, 0, 52, 12, 12, 0, 0, 0, 0, 12, 0, 0, + 188, 8, 8, 8, 8,192,196, 8, 8, 8, 8,200,204,208,212, 60, + 44, 0, 0, 0, 0, 0, 0, 0,216, 28,220, 0, 12, 4, 16, 0, + 0, 0, 0,224,228, 0, 0, 0, 0, 0, 4,232, 32, 0, 0, 0, + 0, 0, 0, 0,236, 12, 0, 0, 0, 0, 16, 0, 36, 0, 0, 0, + 24, 0, 0, 0,240,244, 0, 0, 0, 0, 2, 2, 2, 2, 4, 4, + 4, 4, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 6, 8, 0, 10, + 12, 0, 0, 2, 2, 0, 2, 2, 0, 0, 0, 0, 14, 16, 6, 8, + 0, 0, 4, 8, 0, 0, 2, 2, 2, 0, 0, 6, 4, 8, 2, 0, + 2, 0, 0, 0, 0, 6, 0, 20, 0, 0, 0, 22, 0, 0, 0, 6, + 8, 0, 24, 26, 28, 0, 0, 0, 30, 0, 0, 0, 0, 32, 34, 36, + 38, 0, 0, 40, 42, 0, 0, 0, 18, 0, 0, 0, 2, 2, 2, 2, + 0, 2, 2, 2, 0, 6, 44, 0, 0, 0, 0, 2, 0, 46, 48, 50, + 0, 0, 52, 0, 0, 0, 6, 4, 54, 0, 2, 0, 0, 2, 2, 56, + 58, 60, 0, 62, 64, 18, 2, 10, 12, 0, 0, 0, 66, 8, 0, 2, + 2, 2, 0, 6, 4, 4, 4, 4, 68, 2, 70, 4, 4, 4, 8, 72, + 0, 0, 74, 76, 2, 0, 78, 0, 0, 0, 4, 8, 2, 0, 0, 0, + 80, 0, 6, 4, 4, 4, 4, 8, 0, 2, 2, 2, 2, 6, 4, 4, + 4, 8, 0, 0, 0, 82, 0, 84, 86, 0, 0, 0, 2, 88, 0, 0, + 0, 90, 6, 8, 2, 0, 0, 0, 6, 4, 4, 4, 8, 0, 2, 0, + 2, 2, 6, 4, 4, 8, 0, 10, 12, 6, 8, 2, 0, 0, 50, 84, + 118,152, 17,161, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 5, 16, 17, 18, 1, 1, 1, 1, 1, 1, 19, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 20, 21, 22, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 0, 35, 4, 4, 4, 4, 4, 36, 0, 0, 0, + 0, 0, 0, 37, 0, 38, 39, 3, 3, 3, 3, 3, 3, 40, 41, 0, + 0, 0, 0, 0, 0, 0, 42, 43, 0, 0, 44, 0, 0, 0, 45, 46, + 5, 47, 48, 49, 50, 51, 52, 53, 5, 5, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 6, 0, 0, 6, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 54, 0, 0, 55, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, + 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, + 3, 3, 3, 1, 1, 1, 1, 1, 0, 0, 0, 26, 27, 8, 8, 8, + 28, 29, 30, 19, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 2, 2, 13, + 13, 13, 56, 11, 11, 11, 11, 11, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 8, 8, 16, 16, 72, 73, 74, 75, 3, + 3, 3, 3, 20, 20, 3, 3, 3, 3, 3, 3, 76, 77, 78, 79, 3, + 1, 1, 80, 81, 82, 83, 84, 85, 86, 87, 3, 1, 1, 1, 88, 1, + 1, 1, 1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 89, 21, 21, 90, + 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101, 0, 0, 1, 1, 1, + 1,102, 9, 9, 9,103, 9,104,105,106,107, 22, 22,108,109, 0, + 110,111,112,113,114,115,116, 17, 17, 17, 8,117,118,119,120,121, + 122,123,124,125,126,127,128, 9,129,130,131,132,133,134,135,136, + 137,138,139,140,141, 0,142,143,144,145, 0,146,147,148,149,150, + 151,152,153,154,155,156,157, 0,158,159,160, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0,161, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0,162, 0, 0, + 0, 0, 0, 14, 14, 14, 14,163,164, 23, 23, 0, 0,165, 0,166, + 167,168,169, 4, 4, 4, 4, 4, 4, 15, 15, 15, 15, 4, 4, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,170,171,172,173, 18, 18, + 18, 0, 0,174,175, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, + 3,176, 3, 3, 3,177,178, 16, 3, 3, 0, 10, 10, 10, 10, 10, + 10, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0,179, 19,180, 0, 0, + 181, 0, 0, 0,182, 0,183, 0,184, 0, 13, 24, 24,185, 0, 0, + 0, 0, 0, 3, 3, 3, 0, 9, 9, 0, 0, 3, 3, 3, 3,186, + 0, 3, 3, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 25, 25, 0, + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, + 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 8, + 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 7, + 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 6, 10, + 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 16, + 16, 16, 16, 16, 16, 16, 16, 12, 12, 12, 12, 12, 12, 12, 12, 13, + 13, 13, 13, 13, 13, 13, 13, 18, 18, 18, 18, 18, 18, 18, 18, 17, + 17, 17, 17, 17, 17, 17, 17, 20, 20, 20, 20, 20, 20, 20, 20, 19, + 19, 19, 19, 19, 19, 19, 19, 15, 15, 15, 15, 15, 15, 15, 15, 25, + 25, 25, 25, 25, 25, 25, 25, 23, 23, 23, 23, 23, 23, 23, 23, 26, + 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 28, + 28, 28, 28, 28, 28, 28, 28, 14, 14, 14, 14, 14, 14, 14, 14, 3, + 3, 3, 3,118,119,118,119, 3, 3,156,156, 7,157, 7,157, 7, + 7, 7,158, 3, 3,180, 3, 14, 14, 14, 14, 14, 14, 14,181,182, + 17, 17, 17, 17, 17,183, 17,184, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15,185,100,100,100,100,100,186, 40, 40, 40, 40, 40, 40,187, + 188, 6, 6,189,190, 6,191, 6, 6, 6, 6, 6,192, 6, 6, 85, + 85, 85, 85, 85, 6, 6, 6,120,120,120,120,121,121,121,121,122, + 122,122,122,159,159, 85, 6, 6, 6, 6, 6, 6, 6,193, 6, 30, + 30, 30, 30, 30,194,195, 30, 45, 45, 45, 45, 45, 45, 45, 45, 46, + 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 48, + 48, 48, 48, 48, 48, 48, 48, 32, 32, 32, 32, 32, 32, 32, 32, 49, + 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 50, 50, 50, 51, + 51, 51, 51, 51, 51, 51, 51, 41, 41, 41, 41, 41, 41, 41, 41, 81, + 81, 81,196, 81, 81, 81, 81, 52, 52, 52, 52, 52, 52, 52, 52, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,197, 29, 29, 24, + 24, 24, 24, 24, 24, 24, 24, 24, 24, 35, 35, 35, 35, 35,198, 12, + 12, 36, 36, 36, 36, 36, 36,160,160,101,101,101,101,199,101,161, + 161,200,201,162,162,163,163, 37, 37, 37, 37, 37, 37, 37, 37,202, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 11, 11, 11, 11, 11,102, + 102,102,102,102,140,140,140, 86, 86, 86, 86, 86, 86, 37, 37,164, + 164, 42, 42, 42, 42, 42, 42, 42, 42, 42, 14, 14, 14, 14, 14, 53, + 53, 53, 53, 53, 53, 53, 53,103,103,103,103,123,123,123,123,104, + 104,104,104,104,141,141,141, 15, 35, 35, 35,103,203,204,205, 7, + 7,206, 7, 7,207,208,209, 7, 7, 7,210, 14, 14, 14, 14,211, + 3, 3, 3, 3, 3, 3,212, 3, 7, 3, 3, 3, 14, 14, 14, 3, + 3,213,214,215, 3, 7, 7,158, 3, 3, 3, 3, 3, 3, 3, 43, + 43, 43, 43, 43, 43, 7, 7, 54, 54, 54, 54, 54, 54, 54, 54, 35, + 35, 35,105,105,105,105,105, 12, 12, 12, 12, 12, 12, 15, 15, 1, + 1, 1, 1, 1, 1, 1, 3,216, 3,217,218,219, 22, 22, 22, 22, + 220,165, 21, 21, 21, 21,221,106,106,106,222, 2, 2, 2, 2, 2, + 3,106,106, 3, 3, 3, 21, 2, 2, 3, 3, 3, 3, 2,223, 3, + 3, 3, 3, 3, 21, 21,224, 21, 21, 21, 21, 21,225, 3, 3, 1, + 1, 1, 1, 3, 3, 3, 3, 9, 9, 9, 9, 9, 82, 82, 82, 23, + 23, 23, 23, 15, 15, 15, 15, 15, 15, 13, 13, 13, 13, 13, 13, 3, + 3,226, 7, 7, 7, 7, 7,227, 7, 7, 7, 7, 7, 7, 7,142, + 142,142, 3,124,124,124,124, 87, 87, 87, 87, 87, 87, 30, 30,166, + 166,228,143,143,143, 2, 2,107,107,107,107,229,107, 24, 24, 88, + 88, 88, 88, 88, 88, 24, 24, 89, 89, 89, 89, 89, 89, 90, 90, 12, + 12, 12, 7, 7,230,231, 36, 36, 36, 36, 36, 90, 90, 90, 90, 7, + 232, 40, 40, 40, 6, 6, 6, 6, 6, 6,233, 6, 6, 6, 6, 14, + 3,234, 3, 3, 3, 3, 6, 6, 6, 6, 6, 6, 6, 6,235, 3, + 3,118,119,118,119,236,165, 21,237, 2, 2, 2, 2, 3, 3, 3, + 3, 3, 3, 17, 17, 17, 17, 17, 3, 17, 17, 17, 3, 3,238,167, + 167,125,125,125,125,239, 3,144,144,144,168,168,145,145,145,169, + 169, 91, 91, 91, 91, 91, 91,108,108,108,108,108,146,146,146,147, + 147,147,109,109,109,109,109,148,148,148,126,126,126,126,110,110, + 110,110,110,127,127,127,127,128,128,128,128,170,170,171,171, 92, + 92, 92, 92, 92, 92,172,172,173,173,174,174,129,129,129,129,175, + 175, 93, 93, 93, 93, 93, 93, 94, 94, 94, 94, 94, 94,176,176,130, + 130,130,130,131,131,131,131,132,132,132,132,177,177,178,178, 55, + 55, 55, 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 57, + 57, 57, 57, 57, 57, 57, 57,133,133,133,133, 34, 34, 34, 34, 34, + 34, 34, 34, 34, 34, 34, 34,134,134,134,134,240, 6, 6, 6,149, + 149,149,135,135,135,135,136,136,136,136,150,150,150,179,179, 58, + 58, 58, 58, 58, 58, 58, 58,111,111,111,111,111,151,151,151,112, + 112,112,112,112,152,152,152, 59, 59, 59, 59, 59, 59,241, 41, 60, + 60, 60, 60, 60, 60, 60, 60,153,153,153,113,113,113,113,113, 83, + 83, 83,242, 83, 83, 83, 83, 61, 61, 61, 61, 61, 61, 61, 61, 62, + 62, 62, 62, 62, 62, 62, 62, 63, 63, 63, 63, 63, 63, 63, 63, 64, + 64, 64, 64, 64, 64, 64, 64, 95, 95, 95, 95, 95, 95, 33, 33,114, + 114,114,114,114, 24, 24, 24, 65, 65, 65, 65, 65, 65, 65, 65, 66, + 66, 66, 66, 66, 66, 66, 66, 67, 67, 67, 67, 67, 67, 67, 67, 68, + 68, 68, 68, 68, 68, 68, 68, 69, 69, 69, 69, 69, 69, 69, 69,115, + 115,115,115,115, 96, 96, 96, 96, 96, 96, 11,137,137,137,137, 30, + 30, 30, 30, 30, 30, 59, 59, 70, 70, 70, 70, 70, 70, 70, 70, 84, + 84, 84, 84, 84, 84, 84, 44, 44, 44, 44, 44, 44, 44, 44, 44, 97, + 97, 97, 97, 97, 97,116,116,116,116,116,117,117,117,117,117, 71, + 71, 71, 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 82, + 82, 82, 82, 32, 32, 32, 32, 73, 73, 73, 73, 73, 73, 73, 73, 74, + 74, 74, 74, 74, 74, 74, 74, 13, 13, 13, 13,154,154,154, 98, 98, + 98, 98, 98, 98,155,155,155, 75, 75, 75, 75, 75, 75, 75, 75, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 99, 99, 99, 99, 99, 99, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,243, 1, 21, + 21, 21, 21, 21, 21, 21, 21,244, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 22, 22, 22, 22, 22, 22, 22, 21,245, 22,246, 21, 19, 39, + 39, 39, 39, 39, 39, 39, 39, 39, 39, 3, 3, 3, 3, 3, 3, 14, + 14, 14, 14, 14, 3, 3, 3, 3, 3, 3, 3, 3, 3,247,248,249, + 3,250, 3, 3, 3, 3, 3, 43, 43, 43, 15, 15, 15, 15, 15, 76, + 76, 76, 76, 76, 76, 76, 76,138,138,138,138,139,139,139,139, 77, + 77, 77, 77, 77, 77, 77, 77, 78, 78, 78, 78, 78, 78, 78, 78, 79, + 79, 79, 79, 79, 79, 79, 79, 80, 80, 80, 80, 80, 80, 80, 80,251, + 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 1, 1, 1, 1, 2, + 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, + 10, 10, 10, 11, 11, 11, 11, 13, 13, 13, 13, 14, 14, 14, 14, 12, + 12, 12, 12, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, + 18, 18, 18, 21, 21, 21, 21, 22, 22, 22, 22, 19, 19, 19, 19, 20, + 20, 20, 20, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, + 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, + 30, 30, 30, 31, 31, 31, 31, 33, 33, 33, 33, 32, 32, 32, 32, 34, + 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, + 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, + 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 44, 46, 46, 46, 46, 47, + 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, + 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 45, + 45, 45, 45, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, + 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 62, + 62, 62, 62, 63, 63, 63, 63, 64, 64, 64, 64, 65, 65, 65, 65, 66, + 66, 66, 66, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 69, 70, + 70, 70, 70, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, + 74, 74, 74, 75, 75, 75, 75, 76, 76, 76, 76, 77, 77, 77, 77, 78, + 78, 78, 78, 79, 79, 79, 79, 80, 80, 80, 80, 81, 81, 81, 81, 83, + 83, 83, 83, 82, 82, 82, 82, 84, 84, 84, 84, 86, 86, 86, 86, 87, + 87, 87, 87, 88, 88, 88, 88, 89, 89, 89, 89, 90, 90, 90, 90, 91, + 91, 91, 91, 92, 92, 92, 92, 93, 93, 93, 93, 94, 94, 94, 94, 95, + 95, 95, 95, 96, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 99, + 99, 99, 99,100,100,100,100, 85, 85, 85, 85,102,102,102,102,103, + 103,103,103,104,104,104,104,105,105,105,105,106,106,106,106,107, + 107,107,107,101,101,101,101,108,108,108,108,109,109,109,109,110, + 110,110,110,111,111,111,111,112,112,112,112,113,113,113,113,114, + 114,114,114,115,115,115,115,116,116,116,116,117,117,117,117,175, + 7, 7, 7, 7, 7,174, 3,118,118,118,118,119,119,119,119,120, + 120,120,120,121,121,121,121,122,122,122,122,123,123,123,123,124, + 124,124,124,125,125,125,125,126,126,126,126,127,127,127,127,128, + 128,128,128,129,129,129,129,130,130,130,130,131,131,131,131,132, + 132,132,132,133,133,133,133,134,134,134,134,135,135,135,135,136, + 136,136,136,137,137,137,137,138,138,138,138,139,139,139,139,140, + 140,140,140,141,141,141,141,142,142,142,142,143,143,143,143,144, + 144,144,144,145,145,145,145,146,146,146,146,147,147,147,147,148, + 148,148,148,149,149,149,149,150,150,150,150,151,151,151,151,152, + 152,152,152,153,153,153,153, 3, 3,177, 3, 7,174, 7, 7, 7, + 7,179, 3,155,155,155,155,156,156,156,156,157,157,157,157,158, + 158,158,158,159,159,159,159,160,160,160,160,182, 19, 19, 19,154, + 154,154,154,161,161,161,161,162,162,162,162,163,163,163,163,164, + 164,164,164,165,165,165,165,166,166,166,166,167,167,167,167,168, + 168,168,168,169,169,169,169,170,170,170,170,171,171,171,171,172, + 172,172,172,173,173,173,173, 7,179,194, 3, 17,195, 17,196, 17, + 197, 17, 17,198, 45, 45, 45, 15,199, 15, 15,200, 85, 85, 85,183, + 40, 40, 40, 6,184, 6,185, 6, 6,180,180,185, 6,201, 12, 12, + 202, 6, 6,203, 6, 6, 6, 6, 6, 6,184,204, 6, 6, 6,205, + 206, 30, 30, 30,207, 30, 30, 81, 81, 81,208, 29,209,210, 29, 35, + 35,211, 35,102,102,212,213,176,176,176,176,176,214, 3, 3,215, + 216, 32, 32,186, 12, 12, 12,217, 12,181,218, 3,181,178, 3, 7, + 187,219, 7, 7, 7, 7,220,221,187,222, 7, 7, 7,223, 7, 7, + 7, 7,224, 3, 3, 3,178,175, 3, 3,188, 3,225,189, 3,177, + 3, 3, 3, 3, 3, 3,177, 3,226, 3, 3,227, 1,228,229, 3, + 3, 1, 3,190, 20, 20, 20, 20, 20,230,190, 19, 19,191,182,231, + 2, 2, 2, 2, 2, 2,232, 19, 19, 19,191, 19, 19, 3, 3,189, + 7, 7, 7, 7, 7,188, 7,154,154,154,233,101,101,101,234, 7, + 7,174, 7, 7,235,236, 3,237, 85, 85,183, 6, 6, 6,238, 12, + 12, 12,239, 6, 6, 6,180, 3,240, 19, 19, 19, 19, 19,241, 3, + 3, 3,192,181, 3, 3, 3,242, 6, 6, 6,243, 41, 41, 41, 82, + 82,244, 82,245, 18, 18, 18,246, 20, 20, 20,247, 20, 20, 20, 20, + 248, 19, 19, 3,193,178, 3, 3, 3,193, 12,186,192, 12, 3, 3, + 3,249,178,250, 3, 3, 3, 2, 2, 2, 2, 13, 13, 13, 13, 12, + 12, 12, 12, 0, 0, 0, 0,136,136,136,136, 80, 80, 80, 80, 3, + 3, 3, 3, 19, 19, 19, 19, 63, 63, 63, 63, 39, 39, 39, 39,131, + 131,131,131, 28, 28, 28, 28, 1, 1, 1, 1, 30, 30, 30, 30, 79, + 79, 79, 79, 6, 6, 6, 6,127,127,127,127, 9, 9, 9, 9,155, + 155,155,155, 17, 17, 17, 17, 15, 15, 15, 15,139,139,139,139,110, + 110,110,110, 77, 77, 77, 77, 33, 33, 33, 33, 27, 27, 27, 27, 49, + 49, 49, 49,119,119,119,119,113,113,113,113, 25, 25, 25, 25, 7, + 7, 7, 7, 99, 99, 99, 99, 32, 32, 32, 32, 22, 22, 22, 22,165, + 165,165,165, 8, 8, 8, 8, 29, 29, 29, 29, 31, 31, 31, 31,146, + 146,146,146,105,105,105,105, 14, 14, 14, 14, 36, 36, 36, 36, 91, + 91, 91, 91, 56, 56, 56, 56,134,134,134,134, 55, 55, 55, 55, 5, + 5, 5, 5, 11, 11, 11, 11, 10, 10, 10, 10, 21, 21, 21, 21, 23, + 23, 23, 23, 16, 16, 16, 16, 20, 20, 20, 20, 18, 18, 18, 18, 62, + 62, 62, 62,122,122,122,122, 89, 89, 89, 89,130,130,130,130, 94, + 94, 94, 94,100,100,100,100,108,108,108,108,171,171,171,171,137, + 137,137,137,124,124,124,124,123,123,123,123,126,126,126,126,142, + 142,142,142,125,125,125,125,154,154,154,154,150,150,150,150,169, + 169,169,169,145,145,145,145,163,163,163,163,157,157,157,157,166, + 166,166,166,167,167,167,167,151,151,151,151,164,164,164,164,168, + 168,168,168,174,174,174,174,132,132,132,132, 24, 24, 24, 24,107, + 107,107,107, 86, 86, 86, 86,133,133,133,133, 4, 4, 4, 4, 37, + 37, 37, 37, 58, 58, 58, 58, 75, 75, 75, 75, 68, 68, 68, 68, 92, + 92, 92, 92, 87, 87, 87, 87, 59, 59, 59, 59,116,116,116,116, 97, + 97, 97, 97, 57, 57, 57, 57,114,114,114,114,140,140,140,140,138, + 138,138,138,159,159,159,159,172,172,172,172, 84, 84, 84, 84, 35, + 35, 35, 35, 48, 48, 48, 48, 76, 76, 76, 76, 70, 70, 70, 70, 61, + 61, 61, 61, 26, 26, 26, 26, 40, 40, 40, 40,135,135,135,135,161, + 161,161,161, 85, 85, 85, 85, 96, 96, 96, 96,109,109,109,109,102, + 102,102,102,141,141,141,141,143,143,143,143,175,175,175,175, 38, + 38, 38, 38, 64, 64, 64, 64, 90, 90, 90, 90, 93, 93, 93, 93, 65, + 65, 65, 65, 67, 67, 67, 67,104,104,104,104,170,170,170,170, 47, + 47, 47, 47,173,173,173,173,117,117,117,117,112,112,112,112, 78, + 78, 78, 78,144,144,144,144,156,156,156,156,148,148,148,148,158, + 158,158,158,121,121,121,121,160,160,160,160,152,152,152,152, 52, + 52, 52, 52, 73, 73, 73, 73, 60, 60, 60, 60, 74, 74, 74, 74, 42, + 42, 42, 42,118,118,118,118, 51, 51, 51, 51, 50, 50, 50, 50,106, + 106,106,106,147,147,147,147,153,153,153,153,101,101,101,101,111, + 111,111,111,129,129,129,129,115,115,115,115,103,103,103,103, 69, + 69, 69, 69, 95, 95, 95, 95, 34, 34, 34, 34, 45, 45, 45, 45, 43, + 43, 43, 43, 46, 46, 46, 46, 54, 54, 54, 54, 71, 71, 71, 71, 41, + 41, 41, 41, 53, 53, 53, 53, 81, 81, 81, 81,120,120,120,120,128, + 128,128,128, 66, 66, 66, 66, 72, 72, 72, 72, 98, 98, 98, 98, 88, + 88, 88, 88, 83, 83, 83, 83, 82, 82, 82, 82,149,149,149,149, 19, + 19, 19, 0, 0, 19, 19, 19, 44, 44, 44, 44, 0, 0, 19, 0, 1, + 1, 0, 0, 19, 0, 0, 0, 3, 3, 3, 0, 1, 0, 0, 0, 0, + 17, 17, 17, 4, 14, 14, 14, 3, 0, 3, 3, 0, 3, 3, 3, 1, + 1, 1, 0, 19, 19, 9, 9, 0, 0, 0, 19, 0, 0, 19, 19, 0, + 15, 15, 15, 17, 17, 17, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, + 0, 26, 26, 0, 9, 9, 9, 9, 9, 0, 9, 9, 0, 9, 0, 9, + 9, 55, 55, 6, 1, 1, 6, 6, 4, 4, 4, 3, 3, 3, 1, 1, + 1, 3, 3, 1, 3, 3, 3, 3, 3, 0, 3, 7, 1, 1, 1, 1, + 7, 7, 7, 0, 0, 7, 7, 24, 24, 24, 0, 25, 0, 0, 0, 0, + 25, 25, 25, 8, 8, 8, 0, 35, 35, 35, 0, 0, 0, 35, 35, 44, + 0, 0, 0, 32, 32, 0, 0, 32, 0, 32, 32, 1, 0, 1, 1, 0, + 1, 0, 0, 9, 9, 9, 6, 19, 9, 9, 9, 9, 9, 19, 19, 9, + 9, 9, 19, 6, 19, 19, 19, 19, 19, 19, 9, 0, 0, 9, 0, 0, + 13, 0, 13, 0, 13, 13, 13, 13, 13, 1, 1, 1, 1, 12, 12, 15, + 1, 1, 0, 26, 12, 12, 12, 12, 12, 12, 0, 69, 69, 0, 69, 84, + 84, 84, 0, 19, 9, 19, 19, 19, 19, 0, 0, 19, 19, 19, 4, 3, + 3, 0, 0, 1, 1, 6, 6, 0, 0, 17, 17, 17, 17, 0, 0,156, + 156, 3, 3,100, 36, 36, 36,107,107,107, 1,136,139, 13, 13, 17, + 15, 15, 15, 17, 17, 15, 15, 15, 17, 17, 17, 0, 0, 1, 1, 15, + 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, + 10, 0, 11, 12, 13, 0, 0, 0, 14, 0, 0, 15, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, + 17, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 19, 20, 21, 0, 22, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 23, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, + 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 24, 25, 26, 27, 28, 29, 30, 0, @@ -5224,8 +5226,6 @@ static const uint8_t _hb_ucd_u8[13937]= 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 67, 68, 0, 69, 70, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,100,101, @@ -5260,116 +5260,116 @@ static const uint8_t _hb_ucd_u8[13937]= 0,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194, 195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210, 211,212,213, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, - 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, }; static const uint16_t _hb_ucd_u16[5104]= { - 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 10, 11, 12, - 13, 13, 13, 14, 15, 13, 13, 16, 17, 18, 19, 20, 21, 22, 13, 23, - 13, 13, 13, 24, 25, 11, 11, 11, 11, 26, 11, 27, 28, 29, 30, 31, - 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 11, 37, 38, 13, 39, - 9, 9, 9, 11, 11, 11, 13, 13, 40, 13, 13, 13, 41, 13, 13, 13, - 13, 13, 13, 35, 9, 42, 11, 11, 43, 44, 32, 45, 46, 47, 47, 48, - 49, 50, 47, 47, 51, 32, 52, 53, 47, 47, 47, 47, 47, 54, 55, 56, - 57, 58, 47, 32, 59, 47, 47, 47, 47, 47, 60, 53, 61, 47, 62, 63, - 47, 64, 65, 66, 47, 67, 47, 47, 68, 69, 47, 47, 70, 32, 71, 32, - 72, 47, 47, 73, 74, 75, 76, 77, 78, 47, 47, 79, 80, 81, 82, 83, - 84, 47, 47, 85, 86, 87, 88, 89, 84, 47, 47, 79, 90, 47, 82, 91, - 92, 47, 47, 93, 94, 95, 82, 96, 97, 47, 47, 98, 99, 100, 101, 102, - 103, 47, 47, 104, 105, 106, 82, 107, 108, 47, 47, 93, 109, 110, 82, 111, - 112, 47, 47, 113, 114, 115, 82, 116, 92, 47, 47, 47, 117, 118, 101, 119, - 47, 47, 47, 120, 121, 122, 66, 66, 47, 47, 47, 123, 124, 125, 47, 47, - 126, 127, 128, 129, 47, 47, 47, 130, 131, 32, 32, 132, 133, 134, 66, 66, - 47, 47, 135, 136, 122, 137, 138, 139, 140, 141, 9, 9, 9, 11, 11, 142, - 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 143, 144, 145, - 47, 146, 9, 9, 9, 9, 9, 147, 148, 47, 47, 47, 47, 47, 47, 47, - 47, 47, 47, 47, 47, 47, 149, 47, 150, 151, 47, 47, 47, 47, 152, 153, - 47, 154, 47, 155, 47, 156, 47, 156, 47, 47, 47, 157, 158, 159, 160, 145, - 161, 160, 47, 47, 162, 47, 47, 47, 163, 47, 164, 47, 47, 47, 47, 47, - 47, 47, 165, 166, 167, 47, 47, 47, 47, 47, 47, 47, 47, 168, 146, 146, - 47, 169, 47, 47, 47, 170, 171, 172, 160, 160, 173, 174, 32, 32, 32, 32, - 175, 47, 47, 176, 177, 122, 178, 179, 180, 47, 181, 61, 47, 47, 182, 183, - 47, 47, 184, 185, 186, 61, 47, 187, 188, 9, 9, 9, 66, 189, 190, 191, - 11, 11, 192, 27, 27, 27, 193, 194, 11, 195, 27, 27, 32, 32, 32, 32, - 13, 13, 13, 13, 13, 13, 13, 13, 13, 196, 13, 13, 13, 13, 13, 13, - 197, 197, 197, 197, 197, 198, 197, 11, 199, 199, 199, 200, 201, 202, 202, 201, - 203, 204, 205, 206, 207, 208, 209, 210, 211, 27, 212, 212, 212, 213, 214, 32, - 215, 216, 217, 218, 219, 145, 220, 220, 221, 222, 223, 146, 224, 225, 146, 226, - 227, 227, 227, 227, 227, 227, 227, 227, 228, 146, 229, 146, 146, 146, 146, 230, - 146, 231, 227, 232, 146, 233, 234, 146, 146, 146, 146, 146, 146, 146, 145, 145, - 145, 235, 146, 146, 146, 146, 236, 145, 146, 146, 146, 146, 146, 146, 146, 146, - 146, 146, 146, 237, 238, 146, 146, 239, 146, 146, 146, 146, 146, 146, 240, 146, - 146, 146, 146, 146, 146, 146, 241, 242, 145, 243, 146, 146, 244, 227, 245, 227, - 246, 247, 227, 227, 227, 248, 227, 249, 146, 146, 146, 227, 250, 146, 146, 146, - 9, 9, 9, 11, 11, 11, 251, 252, 13, 13, 13, 13, 13, 13, 253, 254, - 11, 11, 11, 47, 47, 47, 255, 256, 47, 47, 47, 47, 47, 47, 32, 32, - 257, 258, 259, 260, 261, 262, 263, 263, 264, 265, 266, 267, 268, 47, 47, 47, - 47, 269, 148, 47, 47, 47, 47, 270, 47, 271, 47, 47, 146, 146, 146, 47, - 146, 146, 272, 146, 273, 274, 146, 146, 272, 146, 146, 274, 146, 146, 146, 146, - 47, 47, 47, 47, 146, 146, 146, 146, 47, 275, 47, 47, 47, 47, 47, 47, - 47, 146, 146, 146, 146, 47, 47, 187, 276, 47, 61, 47, 13, 13, 277, 278, - 13, 279, 47, 47, 47, 47, 280, 281, 31, 282, 283, 284, 13, 13, 13, 285, - 286, 287, 288, 289, 290, 291, 9, 292, 293, 47, 294, 295, 47, 47, 47, 296, - 297, 47, 47, 298, 299, 160, 32, 300, 61, 47, 301, 47, 302, 303, 47, 47, - 72, 47, 47, 304, 305, 306, 307, 61, 47, 47, 308, 309, 310, 311, 47, 312, - 47, 47, 47, 313, 58, 314, 315, 316, 47, 47, 47, 11, 11, 317, 318, 11, - 11, 11, 11, 11, 47, 47, 319, 160, 320, 320, 320, 320, 320, 320, 320, 320, - 321, 321, 321, 321, 321, 321, 321, 321, 11, 322, 323, 47, 47, 47, 47, 47, - 47, 47, 47, 324, 325, 326, 47, 47, 47, 47, 47, 327, 146, 47, 47, 47, - 47, 328, 47, 47, 329, 146, 146, 330, 32, 331, 32, 332, 333, 334, 335, 47, - 47, 47, 47, 47, 47, 47, 47, 336, 337, 2, 3, 4, 5, 338, 339, 340, - 47, 341, 47, 47, 47, 47, 342, 343, 344, 145, 145, 345, 220, 220, 220, 346, - 347, 146, 146, 146, 146, 146, 146, 348, 349, 349, 349, 349, 349, 349, 349, 349, - 47, 47, 47, 47, 47, 47, 350, 145, 47, 47, 351, 47, 352, 47, 47, 60, - 47, 353, 47, 47, 47, 354, 220, 220, 9, 9, 147, 11, 11, 47, 47, 47, - 47, 47, 160, 9, 9, 147, 11, 11, 47, 47, 47, 47, 47, 47, 353, 9, - 9, 355, 11, 11, 47, 47, 47, 47, 27, 27, 27, 27, 27, 27, 27, 27, - 47, 47, 47, 47, 47, 356, 47, 357, 47, 47, 358, 145, 145, 145, 47, 359, - 47, 360, 47, 353, 47, 47, 47, 47, 47, 47, 47, 361, 145, 145, 145, 145, - 362, 47, 47, 363, 145, 66, 47, 364, 47, 365, 145, 145, 366, 47, 367, 66, - 47, 47, 47, 368, 47, 369, 47, 369, 47, 368, 144, 145, 145, 145, 145, 145, - 9, 9, 9, 9, 11, 11, 11, 370, 47, 47, 371, 160, 372, 9, 373, 11, - 374, 227, 227, 227, 227, 227, 227, 227, 145, 145, 145, 145, 145, 145, 145, 145, - 47, 47, 375, 47, 275, 376, 146, 377, 47, 365, 378, 47, 60, 379, 66, 47, - 380, 66, 66, 47, 381, 145, 47, 47, 382, 47, 47, 363, 383, 384, 385, 386, - 180, 47, 47, 387, 388, 47, 47, 160, 97, 47, 389, 390, 391, 47, 47, 392, - 180, 47, 47, 393, 394, 395, 396, 145, 47, 47, 397, 398, 362, 32, 32, 32, - 47, 47, 368, 47, 47, 399, 172, 160, 92, 47, 47, 113, 400, 401, 402, 32, - 47, 47, 47, 403, 404, 405, 406, 32, 47, 47, 47, 407, 408, 409, 47, 47, - 47, 47, 47, 410, 411, 160, 160, 160, 47, 47, 412, 413, 414, 415, 32, 32, - 47, 47, 47, 416, 417, 160, 66, 66, 47, 47, 418, 419, 160, 160, 160, 160, - 47, 420, 421, 422, 47, 47, 47, 47, 47, 47, 397, 423, 66, 66, 66, 66, - 9, 9, 9, 9, 11, 11, 128, 424, 47, 47, 47, 425, 426, 160, 160, 160, - 47, 47, 47, 47, 47, 427, 428, 429, 430, 47, 47, 431, 432, 433, 47, 47, - 434, 435, 66, 47, 47, 47, 47, 47, 66, 66, 66, 66, 66, 66, 436, 429, - 47, 47, 47, 47, 47, 47, 437, 160, 47, 47, 412, 438, 437, 128, 145, 439, - 47, 156, 440, 441, 32, 32, 32, 32, 47, 47, 47, 362, 442, 160, 47, 47, - 443, 444, 160, 47, 47, 445, 160, 160, 47, 47, 47, 47, 47, 47, 47, 446, - 447, 47, 47, 448, 449, 450, 32, 32, 47, 47, 47, 47, 145, 451, 452, 453, - 220, 220, 220, 220, 220, 220, 220, 66, 47, 47, 47, 47, 47, 47, 47, 437, - 47, 47, 47, 209, 454, 32, 47, 47, 47, 455, 456, 160, 160, 160, 160, 160, - 47, 47, 47, 47, 47, 47, 306, 47, 47, 47, 47, 47, 160, 47, 47, 457, - 47, 47, 47, 458, 459, 460, 461, 47, 27, 27, 27, 27, 462, 47, 463, 160, - 9, 9, 9, 9, 9, 9, 11, 11, 145, 464, 9, 465, 11, 11, 11, 11, - 47, 47, 47, 47, 399, 466, 429, 429, 467, 468, 27, 27, 27, 27, 469, 470, - 47, 471, 209, 209, 209, 209, 209, 209, 146, 146, 146, 146, 146, 146, 146, 472, - 146, 146, 146, 146, 146, 146, 146, 227, 32, 32, 32, 32, 32, 146, 146, 146, - 146, 146, 146, 146, 146, 146, 473, 474, 475, 146, 476, 146, 146, 146, 146, 146, - 146, 146, 146, 146, 477, 146, 146, 146, 9, 478, 11, 479, 480, 11, 197, 9, - 481, 482, 9, 483, 11, 9, 478, 11, 479, 480, 11, 197, 9, 481, 482, 9, - 483, 11, 9, 478, 11, 479, 480, 11, 197, 9, 481, 482, 9, 483, 11, 9, - 478, 11, 197, 9, 484, 485, 486, 487, 11, 488, 9, 489, 490, 491, 492, 11, - 493, 9, 494, 11, 495, 160, 160, 160, 32, 32, 32, 496, 32, 32, 497, 498, - 499, 500, 32, 32, 32, 32, 32, 32, 501, 11, 11, 11, 11, 11, 11, 11, - 32, 32, 32, 27, 27, 27, 27, 27, 32, 32, 32, 32, 32, 32, 32, 32, - 47, 47, 47, 502, 503, 146, 146, 146, 47, 47, 455, 32, 47, 47, 504, 505, - 47, 47, 47, 47, 47, 47, 506, 160, 47, 47, 47, 47, 47, 47, 455, 507, - 47, 47, 47, 47, 47, 47, 508, 509, 47, 47, 47, 47, 358, 32, 32, 32, - 9, 9, 481, 11, 510, 306, 66, 66, 145, 145, 511, 512, 145, 145, 145, 145, - 145, 145, 513, 145, 145, 145, 145, 145, 47, 47, 47, 47, 47, 47, 47, 227, - 514, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 146, 515, - 146, 146, 146, 146, 146, 227, 227, 227, 209, 209, 209, 209, 209, 209, 209, 209, + 0, 0, 0, 0, 0, 0, 0, 0, 16, 16, 16, 16, 16, 16, 16, 16, + 8, 8, 8, 8, 8, 8, 8, 8, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 64, 64, 64, 64, 64, 64, 64, 64, + 56, 56, 56, 56, 56, 56, 56, 56, 40, 40, 40, 40, 40, 40, 40, 40, + 24, 24, 24, 24, 24, 24, 56, 56, 96, 96, 96, 96, 96, 96, 96, 96, + 24, 24, 24, 24, 24, 24, 24, 592, 152, 152, 600, 336, 344, 352, 360, 608, + 152, 152, 616, 624, 72, 632, 48, 640, 80, 80, 80, 648, 656, 80, 80, 664, + 672, 680, 688, 696, 704, 712, 80, 720, 80, 80, 80, 728, 736, 48, 48, 48, + 48, 744, 48, 96, 752, 760, 768, 368, 40, 40, 40, 40, 40, 40, 40, 776, + 784, 376, 792, 48, 800, 808, 80, 816, 72, 72, 72, 48, 48, 48, 80, 80, + 824, 80, 80, 80, 832, 80, 80, 80, 80, 80, 80, 376, 72, 840, 48, 48, + 848, 856, 40, 864, 872, 8, 8, 880, 888, 896, 8, 8, 904, 40, 912, 384, + 8, 8, 8, 8, 8, 920, 928, 936, 944, 392, 8, 40, 952, 8, 8, 8, + 8, 8, 184, 384, 136, 8, 960, 968, 8, 976, 984, 104, 8, 992, 8, 8, + 1000,1008, 8, 8,1016, 40,1024, 40, 400, 8, 8,1032,1040,1048,1056,1064, + 1072, 8, 8, 408,1080,1088, 144,1096, 416, 8, 8,1104,1112,1120,1128,1136, + 416, 8, 8, 408,1144, 8, 144,1152, 192, 8, 8, 424,1160,1168, 144,1176, + 432, 8, 8,1184,1192,1200, 440,1208,1216, 8, 8,1224,1232,1240, 144,1248, + 1256, 8, 8, 424,1264,1272, 144,1280,1288, 8, 8, 448,1296,1304, 144,1312, + 192, 8, 8, 8,1320,1328, 440,1336, 8, 8, 8,1344,1352, 200, 104, 104, + 8, 8, 8,1360,1368,1376, 8, 8,1384,1392, 208,1400, 8, 8, 8,1408, + 1416, 40, 40,1424,1432,1440, 104, 104, 8, 8,1448,1456, 200,1464,1472,1480, + 1488,1496, 72, 72, 72, 48, 48,1504, 8, 8, 8, 8, 8,1512, 456, 56, + 8, 24, 72, 72, 72, 72, 72, 216, 464, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8,1520, 8,1528,1536, 8, 8, 8, 8,1544,1552, + 8,1560, 8,1568, 8, 224, 8, 224, 8, 8, 8,1576,1584,1592, 88, 56, + 1600, 88, 8, 8,1608, 8, 8, 8,1616, 8,1624, 8, 8, 8, 8, 8, + 8, 8,1632,1640,1648, 8, 8, 8, 8, 8, 8, 8, 8,1656, 24, 24, + 8,1664, 8, 8, 8,1672,1680, 472, 88, 88,1688,1696, 40, 40, 40, 40, + 1704, 8, 8,1712,1720, 200,1728,1736, 232, 8,1744, 136, 8, 8,1752,1760, + 8, 8,1768,1776,1784, 136, 8, 480,1792, 72, 72, 72, 104,1800,1808,1816, + 48, 48,1824, 96, 96, 96,1832,1840, 48,1848, 96, 96, 40, 40, 40, 40, + 80, 80, 80, 80, 80, 80, 80, 80, 80,1856, 80, 80, 80, 80, 80, 80, + 128, 128, 128, 128, 128,1864, 128, 48, 240, 240, 240,1872, 488, 496, 496, 488, + 1880,1888,1896,1904,1912,1920, 112,1928,1936, 96, 248, 248, 248,1944,1952, 40, + 1960,1968,1976,1984,1992, 56, 120, 120,2000,2008,2016, 24,2024,2032, 24,2040, + 2048, 24,2056, 24, 24, 24, 24,2064, 24,2072, 64,2080, 24,2088,2096, 24, + 56,2104, 24, 24, 24, 24,2112, 56, 24, 24, 24,2120,2128, 24, 24,2136, + 24, 24, 24, 24, 24, 24,2144, 24, 24, 24, 24, 24, 24, 24,2152,2160, + 56,2168, 24, 24,2176, 64,2184, 64,2192,2200, 64, 64, 64,2208, 64,2216, + 24, 24, 24, 64,2224, 24, 24, 24, 72, 72, 72, 48, 48, 48,2232,2240, + 80, 80, 80, 80, 80, 80,2248,2256, 48, 48, 48, 8, 8, 8,2264,2272, + 8, 8, 8, 8, 8, 8, 40, 40,2280,2288,2296,2304,2312,2320, 504, 504, + 2328,2336,2344,2352,2360, 8, 8, 8, 8,2368, 464, 8, 8, 8, 8,2376, + 8,2384, 8, 8, 24, 24, 24, 8, 24, 24, 512, 24,2392, 520, 24, 24, + 512, 24, 24, 520, 24, 24, 24, 24, 8, 8, 8, 8, 24, 24, 24, 24, + 8, 528, 8, 8, 8, 8, 8, 8, 8, 24, 24, 24, 24, 8, 8, 480, + 2400, 8, 136, 8, 80, 80,2408,2416, 80,2424, 8, 8, 8, 8,2432,2440, + 368,2448,2456,2464, 80, 80, 80,2472,2480,2488,2496,2504,2512,2520, 72,2528, + 2536, 8,2544,2552, 8, 8, 8,2560,2568, 8, 8,2576,2584, 88, 40,2592, + 136, 8,2600, 8,2608,2616, 8, 8, 400, 8, 8,2624,2632, 256,2640, 136, + 8, 8,2648,2656,2664,2672, 8,2680, 8, 8, 8,2688, 392,2696,2704,2712, + 8, 8, 8, 48, 48,2720,2728, 48, 48, 48, 48, 48, 8, 8,2736, 88, + 48,2744,2752, 8, 8, 8, 8, 8, 8, 8, 8,2760,2768,2776, 8, 8, + 8, 8, 8,2784, 24, 8, 8, 8, 8,2792, 8, 8,2800, 24, 24,2808, + 40,2816, 40,2824,2832,2840,2848, 8, 8, 8, 8, 8, 8, 8, 8,2856, + 2864, 336, 344, 352, 360,2872,2880,2888, 8,2896, 8, 8, 8, 8,2904,2912, + 2920, 56, 56,2928, 120, 120, 120,2936,2944, 24, 24, 24, 24, 24, 24,2952, + 8, 8, 8, 8, 8, 8,2960, 56, 8, 8,2968, 8,2976, 8, 8, 184, + 8, 264, 8, 8, 8,2984, 120, 120, 72, 72, 216, 48, 48, 8, 8, 8, + 8, 8, 88, 72, 72, 216, 48, 48, 8, 8, 8, 8, 8, 8, 264, 72, + 72,2992, 48, 48, 8, 8, 8, 8, 8, 8, 8, 8, 8,3000, 8,3008, + 8, 8, 536, 56, 56, 56, 8,3016, 8,3024, 8, 264, 8, 8, 8, 8, + 8, 8, 8,3032, 56, 56, 56, 56, 272, 8, 8, 544, 56, 104, 8,3040, + 8, 552, 56, 56,3048, 8,3056, 104, 8, 8, 8, 280, 8, 560, 8, 560, + 8, 280, 456, 56, 56, 56, 56, 56, 72, 72, 72, 72, 48, 48, 48,3064, + 8, 8,3072, 88,3080, 72,3088, 48,3096, 64, 64, 64, 64, 64, 64, 64, + 8, 8,3104, 8, 528,3112, 24,3120, 8, 552,3128, 8, 184,3136, 104, 8, + 3144, 104, 104, 8,3152, 56, 8, 8,3160, 8, 8, 544,3168,3176,3184,3192, + 232, 8, 8,3200,3208, 8, 8, 88, 432, 8,3216,3224,3232, 8, 8,3240, + 232, 8, 8,3248,3256,3264,3272, 56, 8, 8, 568,3280, 272, 40, 40, 40, + 8, 8, 280, 8, 8, 576, 472, 88, 192, 8, 8, 448,3288,3296,3304, 40, + 8, 8, 8,3312,3320,3328,3336, 40, 8, 8, 8,3344,3352,3360, 8, 8, + 8, 8, 8,3368,3376, 88, 88, 88, 8, 8, 584,3384,3392,3400, 40, 40, + 8, 8, 8,3408,3416, 88, 104, 104, 8, 8,3424,3432, 88, 88, 88, 88, + 8,3440,3448,3456, 8, 8, 8, 8, 8, 8, 568,3464, 104, 104, 104, 104, + 72, 72, 72, 72, 48, 48, 208,3472, 8, 8, 8,3480,3488, 88, 88, 88, + 8, 8, 8, 8, 8,3496,3504, 160,3512, 8, 8,3520,3528,3536, 8, 8, + 3544,3552, 104, 8, 8, 8, 8, 8, 104, 104, 104, 104, 104, 104,3560, 160, + 8, 8, 8, 8, 8, 8, 288, 88, 8, 8, 584,3568, 288, 208, 56,3576, + 8, 224,3584,3592, 40, 40, 40, 40, 8, 8, 8, 272,3600, 88, 8, 8, + 3608,3616, 88, 8, 8,3624, 88, 88, 8, 8, 8, 8, 8, 8, 8,3632, + 3640, 8, 8,3648,3656,3664, 40, 40, 8, 8, 8, 8, 56,3672,3680,3688, + 120, 120, 120, 120, 120, 120, 120, 104, 8, 8, 8, 8, 8, 8, 8, 288, + 8, 8, 8, 112,3696, 40, 8, 8, 8, 296,3704, 88, 88, 88, 88, 88, + 8, 8, 8, 8, 8, 8, 256, 8, 8, 8, 8, 8, 88, 8, 8,3712, + 8, 8, 8,3720,3728,3736,3744, 8, 96, 96, 96, 96,3752, 8,3760, 88, + 72, 72, 72, 72, 72, 72, 48, 48, 56,3768, 72,3776, 48, 48, 48, 48, + 8, 8, 8, 8, 576,3784, 160, 160,3792,3800, 96, 96, 96, 96,3808,3816, + 8,3824, 112, 112, 112, 112, 112, 112, 24, 24, 24, 24, 24, 24, 24, 64, + 40, 40, 40, 40, 40, 24, 24, 24, 24, 24, 24, 24, 24, 24,3832,3840, + 3848, 24,3856, 24, 24, 24, 24, 24, 24, 24, 24, 24,3864, 24, 24, 24, + 72, 168, 48, 304, 312, 48, 128, 72, 176, 320, 72, 328, 48, 72, 168, 48, + 304, 312, 48, 128, 72, 176, 320, 72, 328, 48, 72, 168, 48, 304, 312, 48, + 128, 72, 176, 320, 72, 328, 48, 72, 168, 48, 128, 72,3872,3880,3888,3896, + 48,3904, 72,3912,3920,3928,3936, 48,3944, 72,3952, 48,3960, 88, 88, 88, + 40, 40, 40,3968, 40, 40,3976,3984,3992,4000, 40, 40, 40, 40, 40, 40, + 4008, 48, 48, 48, 48, 48, 48, 48, 40, 40, 40, 96, 96, 96, 96, 96, + 8, 8, 8,4016,4024, 24, 24, 24, 8, 8, 296, 40, 8, 8,4032,4040, + 8, 8, 8, 8, 8, 8,4048, 88, 8, 8, 8, 8, 8, 8, 296,4056, + 8, 8, 8, 8, 8, 8,4064,4072, 8, 8, 8, 8, 536, 40, 40, 40, + 72, 72, 176, 48,4080, 256, 104, 104, 56, 56,4088,4096, 56, 56, 56, 56, + 56, 56,4104, 56, 56, 56, 56, 56, 8, 8, 8, 8, 8, 8, 8, 64, + 4112, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,4120, + 24, 24, 24, 24, 24, 64, 64, 64, 112, 112, 112, 112, 112, 112, 112, 112, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 939, 940, 941, 942, 946, 948, 0, 962, 969, 970, 971, 976,1001,1002,1003,1008, 0,1033,1040,1041,1042,1043,1047, 0, 0,1080,1081,1082,1086,1110, 0, 0, @@ -5587,9 +5587,9 @@ static const uint16_t _hb_ucd_u16[5104]= }; static const int16_t _hb_ucd_i16[92]= { - 0, 0, 1, -1, 2, 0, -2, 0, 0, 2, 0, -2, 0, 16, 0, -16, - 0, 1, -1, 0, 3, 3, 3, -3, -3, -3, 0, 2016, 0, 2527, 1923, 1914, - 1918, 0, 2250, 0, 0, 138, 0, 7, -7, 0, -1, 1, 1824, 0, 2104, 0, + 0, 0, 1, -1, -1, 1, 0, 1, -1, 0, 0, 2, 0, -2, 2, 0, + -2, 0, -7, 0, 0, 16, 0, -16, 3, 3, 3, -3, -3, -3, 0, 2016, + 0, 2527, 1923, 1914, 1918, 0, 2250, 0, 0, 138, 0, 7, 1824, 0, 2104, 0, 2108, 2106, 0, 2106, 1316, 0, -1, -138, 8, 8, 8, 0, 7, 7, -8, -8, -8, -7,-1316, 1, -1, 3, -3, 1, 0,-1914,-1918, 0, 0,-1923,-1824, 0, 0,-2016,-2104, 0, 0,-2106,-2108,-2106,-2250, 0,-2527, 0, @@ -5597,11 +5597,13 @@ static const int16_t _hb_ucd_i16[92]= static inline uint8_t _hb_ucd_gc (unsigned u) { - return u<1114112 ? _hb_ucd_u8[5296u+((_hb_ucd_u8[1168u+((_hb_ucd_u16[((_hb_ucd_u8[544u+((_hb_ucd_u8[((((((((u)>>1))>>3))>>3))>>4)])<<4)+((((((((u)>>1))>>3))>>3))&15)])<<3)+((((((u)>>1))>>3))&7)])<<3)+((((u)>>1))&7)])<<1)+((u)&1)] : 2; + /* packtab: [2^8,2^4,2^3,2^3,2^1] */ + return u<1114112u ? (uint8_t)(_hb_ucd_u8[5288u+((_hb_ucd_u8[1160u+_hb_ucd_u16[((_hb_ucd_u8[544u+((_hb_ucd_u8[((((((((u)>>1))>>3))>>3))>>4)])<<4)+((((((((u)>>1))>>3))>>3))&15)])<<3)+((((((u)>>1))>>3))&7)]+((((u)>>1))&7)])<<1)+((u)&1)]) : 2; } static inline uint8_t _hb_ucd_ccc (unsigned u) { - return u<125259 ? _hb_ucd_u8[7322u+((_hb_ucd_u8[6738u+((_hb_ucd_u8[6258u+((_hb_ucd_u8[5890u+((_hb_ucd_u8[5644u+((((((((u)>>2))>>2))>>2))>>3)])<<3)+((((((((u)>>2))>>2))>>2))&7)])<<2)+((((((u)>>2))>>2))&3)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 0; + /* packtab: [2^8,2^3,2^2,2^2,2^2] */ + return u<125259u ? (uint8_t)(_hb_ucd_u8[7307u+((_hb_ucd_u8[6724u+((_hb_ucd_u8[6247u+((_hb_ucd_u8[5881u+((_hb_ucd_u8[5636u+((((((((u)>>2))>>2))>>2))>>3)])<<3)+((((((((u)>>2))>>2))>>2))&7)])<<2)+((((((u)>>2))>>2))&3)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)]) : 0; } static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) { @@ -5609,15 +5611,18 @@ static inline uint8_t _hb_ucd_b4 (const uint8_t* a, unsigned i) } static inline int16_t _hb_ucd_bmg (unsigned u) { - return u<65380 ? _hb_ucd_i16[((_hb_ucd_u8[8218u+((_hb_ucd_u8[7986u+((_hb_ucd_u8[7890u+((_hb_ucd_b4(_hb_ucd_u8+7826u,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)])<<3)+((((((u)>>1))>>2))&7)])<<2)+((((u)>>1))&3)])<<1)+((u)&1)] : 0; + /* packtab: [2^4,2^3,2^3,2^2,2^1] */ + return u<65380u ? (int16_t)(_hb_ucd_i16[_hb_ucd_u8[8198u+_hb_ucd_u8[7969u+_hb_ucd_u8[7875u+((_hb_ucd_b4(_hb_ucd_u8+7811u,((((((((u)>>1))>>2))>>3))>>3)))<<3)+((((((((u)>>1))>>2))>>3))&7)]+((((((u)>>1))>>2))&7)]+((((u)>>1))&3)]+((u)&1)]) : 0; } static inline uint8_t _hb_ucd_sc (unsigned u) { - return u<918016 ? _hb_ucd_u8[11655u+((_hb_ucd_u8[10647u+((_hb_ucd_u8[9151u+((_hb_ucd_u8[8703u+((_hb_ucd_u8[8495u+((_hb_ucd_b4(_hb_ucd_u8+8466u,((((((((((u)>>2))>>2))>>3))>>3))>>4)))<<4)+((((((((((u)>>2))>>2))>>3))>>3))&15)])<<3)+((((((((u)>>2))>>2))>>3))&7)])<<3)+((((((u)>>2))>>2))&7)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)] : 2; + /* packtab: [2^4,2^4,2^3,2^3,2^2,2^2] */ + return u<918016u ? (uint8_t)(_hb_ucd_u8[11623u+((_hb_ucd_u8[10615u+((_hb_ucd_u8[9119u+((_hb_ucd_u8[8675u+((_hb_ucd_u8[8475u+((_hb_ucd_b4(_hb_ucd_u8+8446u,((((((((((u)>>2))>>2))>>3))>>3))>>4)))<<4)+((((((((((u)>>2))>>2))>>3))>>3))&15)])<<3)+((((((((u)>>2))>>2))>>3))&7)])<<3)+((((((u)>>2))>>2))&7)])<<2)+((((u)>>2))&3)])<<2)+((u)&3)]) : 2; } static inline uint16_t _hb_ucd_dm (unsigned u) { - return u<195102 ? _hb_ucd_u16[1680u+((_hb_ucd_u8[13041u+((_hb_ucd_u8[12659u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)] : 0; + /* packtab: [2^8,2^5,2^4] */ + return u<195102u ? (uint16_t)(_hb_ucd_u16[1680u+((_hb_ucd_u8[13009u+((_hb_ucd_u8[12627u+((((u)>>4))>>5)])<<5)+((((u)>>4))&31)])<<4)+((u)&15)]) : 0; } diff --git a/thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh b/thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh index 711dd9b657..7200952547 100644 --- a/thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh +++ b/thirdparty/harfbuzz/src/hb-unicode-emoji-table.hh @@ -27,36 +27,36 @@ static const uint8_t _hb_emoji_u8[624]= { - 16, 17, 17, 17, 50, 20, 21, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,118,152, - 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 50, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 2, 3, 0, 0, 4, 0, 5, 0, 0, 0, 0, 0, 6, 0, 0, 7, - 0, 0, 0, 8, 0, 0, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0, - 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 20, 0, 0, - 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, - 23, 0, 24, 25, 0, 26, 27, 28, 29, 30, 31, 31, 32, 31, 33, 34, - 31, 31, 31, 35, 36, 37, 38, 39, 31, 40, 31, 41, 0, 0, 0, 42, - 43, 44, 45, 46, 47, 48, 31, 31, 0, 49, 31, 31, 0, 0, 0, 0, - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 36, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 16, 0, 2, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 4, 0, 0, 2, 0, 0,240, 3, 0, 6, 0, 0, - 0, 0, 0, 12, 0, 1, 0, 0, 0,128, 0, 0, 0,254, 15, 7, - 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 64, 0, - 1, 0, 0, 0, 0, 0, 0,120, 31, 64, 50, 33, 77,196, 0, 7, - 5,255, 15,128,105, 1, 0,200, 0, 0,252, 26,131, 12, 3, 96, - 48,193, 26, 0, 0, 6,191, 39, 36,191, 84, 32, 2, 1, 24, 0, - 144, 80,184, 0, 24, 0, 0, 0, 0, 0,224, 0, 2, 0, 1,128, - 0, 0, 0, 0, 0, 0, 48, 0,224, 0, 0, 24, 0, 0, 0, 0, - 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 32, - 0, 0,128, 2, 0, 0, 0, 0, 16, 0, 0, 0, 0,240, 0, 0, - 0, 0,240,255, 0,128, 1, 0, 1,128, 1, 0, 0, 0,192,255, - 0, 0, 0, 0, 0, 0, 3,192, 0, 64,254, 7, 0,192,255,255, - 255,255,255,255, 63, 0, 0, 0,254,255, 0, 4, 0,128,252,247, - 0,254,255,255,192,255,255,255,255,255,255,255,255,255,255,255, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,118,152, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 3, 4, 0, 0, 5, 0, 6, 0, 0, 0, 0, 0, 7, 0, 0, 8, + 0, 0, 0, 9, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0, + 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 20, 21, 0, 0, + 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, + 24, 0, 25, 26, 0, 27, 28, 29, 30, 31, 1, 1, 32, 1, 33, 34, + 1, 1, 1, 35, 36, 37, 38, 39, 1, 40, 1, 41, 0, 0, 0, 42, + 43, 44, 45, 46, 47, 48, 1, 1, 0, 49, 1, 1, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0,255,255,255,255,255,255,255,255, + 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 2, + 0, 0,240, 3, 0, 6, 0, 0, 0, 0, 0, 12, 0, 1, 0, 0, + 0,128, 0, 0, 0,254, 15, 7, 4, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 64, 0, 1, 0, 0, 0, 0, 0, 0,120, + 31, 64, 50, 33, 77,196, 0, 7, 5,255, 15,128,105, 1, 0,200, + 0, 0,252, 26,131, 12, 3, 96, 48,193, 26, 0, 0, 6,191, 39, + 36,191, 84, 32, 2, 1, 24, 0,144, 80,184, 0, 24, 0, 0, 0, + 0, 0,224, 0, 2, 0, 1,128, 0, 0, 0, 0, 0, 0, 48, 0, + 224, 0, 0, 24, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 32, 0, 0,128, 2, 0, 0, 0, 0, + 16, 0, 0, 0, 0,240, 0, 0, 0, 0,240,255, 0,128, 1, 0, + 1,128, 1, 0, 0, 0,192,255, 0, 0, 0, 0, 0, 0, 3,192, + 0, 64,254, 7, 0,192,255,255,255,255,255,255, 63, 0, 0, 0, + 254,255, 0, 4, 0,128,252,247, 0,254,255,255,192,255,255,255, 255,255,255,255,243,255,255,255,255,255,207,206,255,255,255,255, 255,255,255,255,255,255,185, 7,255,255,255,255,255,255,255,191, 255,255,255,255,255,255,255, 63, 0,126,255,255,255,128,249, 7, @@ -78,7 +78,8 @@ static inline uint8_t _hb_emoji_b1 (const uint8_t* a, unsigned i) } static inline uint8_t _hb_emoji_is_Extended_Pictographic (unsigned u) { - return u<131070 ? _hb_emoji_b1(_hb_emoji_u8+224u,((_hb_emoji_u8[64u+((_hb_emoji_b4(_hb_emoji_u8,((((u)>>6))>>4)))<<4)+((((u)>>6))&15)])<<6)+((u)&63)) : 0; + /* packtab: [2^4,2^4,2^6] */ + return u<131070u ? (uint8_t)(_hb_emoji_b1(_hb_emoji_u8+224u,((_hb_emoji_u8[64u+((_hb_emoji_b4(_hb_emoji_u8,((((u)>>6))>>4)))<<4)+((((u)>>6))&15)])<<6)+((u)&63))) : 0; } diff --git a/thirdparty/harfbuzz/src/hb-vector-svg-draw.cc b/thirdparty/harfbuzz/src/hb-vector-svg-draw.cc new file mode 100644 index 0000000000..82f31b4f95 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector-svg-draw.cc @@ -0,0 +1,740 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-vector.h" +#include "hb-blob.hh" +#include "hb-geometry.hh" +#include "hb-machinery.hh" +#include "hb-map.hh" +#include "hb-vector-svg-path.hh" +#include "hb-vector-svg-subset.hh" +#include "hb-vector-svg-utils.hh" + +#include +#include +#include + +#include "hb-vector-svg.hh" + +HB_UNUSED static inline bool +hb_svg_buffer_contains (const hb_vector_t &buf, const char *needle) +{ + unsigned nlen = (unsigned) strlen (needle); + if (!nlen || buf.length < nlen) + return false; + + for (unsigned i = 0; i + nlen <= buf.length; i++) + if (buf.arrayZ[i] == needle[0] && + !memcmp (buf.arrayZ + i, needle, nlen)) + return true; + return false; +} + +struct hb_vector_draw_t +{ + hb_object_header_t header; + + hb_vector_format_t format = HB_VECTOR_FORMAT_SVG; + hb_transform_t<> transform = {1, 0, 0, 1, 0, 0}; + float x_scale_factor = 1.f; + float y_scale_factor = 1.f; + hb_vector_extents_t extents = {0, 0, 0, 0}; + bool has_extents = false; + unsigned precision = 2; + bool flat = false; + + hb_vector_t defs; + hb_vector_t body; + hb_vector_t path; + hb_set_t *defined_glyphs = nullptr; + hb_blob_t *recycled_blob = nullptr; + + void append_xy (float x, float y) + { + float tx, ty; + hb_svg_transform_point (transform, x_scale_factor, y_scale_factor, x, y, &tx, &ty); + hb_svg_append_num (&path, tx, precision); + hb_svg_append_c (&path, ','); + hb_svg_append_num (&path, ty, precision); + } +}; + +static void +hb_vector_draw_move_to (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + float to_x, float to_y, + void *) +{ + auto *d = (hb_vector_draw_t *) draw_data; + hb_svg_append_c (&d->path, 'M'); + d->append_xy (to_x, to_y); +} + +static void +hb_vector_draw_line_to (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + float to_x, float to_y, + void *) +{ + auto *d = (hb_vector_draw_t *) draw_data; + hb_svg_append_c (&d->path, 'L'); + d->append_xy (to_x, to_y); +} + +static void +hb_vector_draw_quadratic_to (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + float cx, float cy, + float to_x, float to_y, + void *) +{ + auto *d = (hb_vector_draw_t *) draw_data; + hb_svg_append_c (&d->path, 'Q'); + d->append_xy (cx, cy); + hb_svg_append_c (&d->path, ' '); + d->append_xy (to_x, to_y); +} + +static void +hb_vector_draw_cubic_to (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + float c1x, float c1y, + float c2x, float c2y, + float to_x, float to_y, + void *) +{ + auto *d = (hb_vector_draw_t *) draw_data; + hb_svg_append_c (&d->path, 'C'); + d->append_xy (c1x, c1y); + hb_svg_append_c (&d->path, ' '); + d->append_xy (c2x, c2y); + hb_svg_append_c (&d->path, ' '); + d->append_xy (to_x, to_y); +} + +static void +hb_vector_draw_close_path (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + void *) +{ + auto *d = (hb_vector_draw_t *) draw_data; + hb_svg_append_c (&d->path, 'Z'); +} + +static inline void free_static_vector_draw_funcs (); + +static struct hb_vector_draw_funcs_lazy_loader_t + : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) hb_vector_draw_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) hb_vector_draw_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) hb_vector_draw_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) hb_vector_draw_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) hb_vector_draw_close_path, nullptr, nullptr); + hb_draw_funcs_make_immutable (funcs); + hb_atexit (free_static_vector_draw_funcs); + return funcs; + } +} static_vector_draw_funcs; + +static inline void +free_static_vector_draw_funcs () +{ + static_vector_draw_funcs.free_instance (); +} + +static hb_draw_funcs_t * +hb_vector_draw_funcs_get () +{ + return static_vector_draw_funcs.get_unconst (); +} + +/** + * hb_vector_draw_create_or_fail: + * @format: output format. + * + * Creates a new draw context for vector output. + * + * Return value: (nullable): a newly allocated #hb_vector_draw_t, or `NULL` on failure. + * + * Since: 13.0.0 + */ +hb_vector_draw_t * +hb_vector_draw_create_or_fail (hb_vector_format_t format) +{ + if (format != HB_VECTOR_FORMAT_SVG) + return nullptr; + + hb_vector_draw_t *draw = hb_object_create (); + if (unlikely (!draw)) + return nullptr; + draw->format = format; + draw->defined_glyphs = hb_set_create (); + draw->defs.alloc (2048); + draw->body.alloc (8192); + draw->path.alloc (2048); + return draw; +} + +/** + * hb_vector_draw_reference: + * @draw: a draw context. + * + * Increases the reference count of @draw. + * + * Return value: (transfer full): referenced @draw. + * + * Since: 13.0.0 + */ +hb_vector_draw_t * +hb_vector_draw_reference (hb_vector_draw_t *draw) +{ + return hb_object_reference (draw); +} + +/** + * hb_vector_draw_destroy: + * @draw: a draw context. + * + * Decreases the reference count of @draw and destroys it when it reaches zero. + * + * Since: 13.0.0 + */ +void +hb_vector_draw_destroy (hb_vector_draw_t *draw) +{ + if (!hb_object_should_destroy (draw)) + return; + + hb_blob_destroy (draw->recycled_blob); + hb_set_destroy (draw->defined_glyphs); + hb_object_actually_destroy (draw); + hb_free (draw); +} + +/** + * hb_vector_draw_set_user_data: + * @draw: a draw context. + * @key: user-data key. + * @data: user-data value. + * @destroy: (nullable): destroy callback for @data. + * @replace: whether to replace an existing value for @key. + * + * Attaches user data to @draw. + * + * Return value: `true` on success, `false` otherwise. + * + * Since: 13.0.0 + */ +hb_bool_t +hb_vector_draw_set_user_data (hb_vector_draw_t *draw, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (draw, key, data, destroy, replace); +} + +/** + * hb_vector_draw_get_user_data: + * @draw: a draw context. + * @key: user-data key. + * + * Gets previously attached user data from @draw. + * + * Return value: (nullable): user-data value associated with @key. + * + * Since: 13.0.0 + */ +void * +hb_vector_draw_get_user_data (hb_vector_draw_t *draw, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (draw, key); +} + +/** + * hb_vector_draw_set_transform: + * @draw: a draw context. + * @xx: transform xx component. + * @yx: transform yx component. + * @xy: transform xy component. + * @yy: transform yy component. + * @dx: transform x translation. + * @dy: transform y translation. + * + * Sets the affine transform used when drawing glyphs. + * + * Since: 13.0.0 + */ +void +hb_vector_draw_set_transform (hb_vector_draw_t *draw, + float xx, float yx, + float xy, float yy, + float dx, float dy) +{ + draw->transform = {xx, yx, xy, yy, dx, dy}; +} + +/** + * hb_vector_draw_get_transform: + * @draw: a draw context. + * @xx: (out) (nullable): transform xx component. + * @yx: (out) (nullable): transform yx component. + * @xy: (out) (nullable): transform xy component. + * @yy: (out) (nullable): transform yy component. + * @dx: (out) (nullable): transform x translation. + * @dy: (out) (nullable): transform y translation. + * + * Gets the affine transform used when drawing glyphs. + * + * Since: 13.0.0 + */ +void +hb_vector_draw_get_transform (hb_vector_draw_t *draw, + float *xx, float *yx, + float *xy, float *yy, + float *dx, float *dy) +{ + if (xx) *xx = draw->transform.xx; + if (yx) *yx = draw->transform.yx; + if (xy) *xy = draw->transform.xy; + if (yy) *yy = draw->transform.yy; + if (dx) *dx = draw->transform.x0; + if (dy) *dy = draw->transform.y0; +} + +/** + * hb_vector_draw_set_scale_factor: + * @draw: a draw context. + * @x_scale_factor: x scale factor. + * @y_scale_factor: y scale factor. + * + * Sets additional output scaling factors. + * + * Since: 13.0.0 + */ +void +hb_vector_draw_set_scale_factor (hb_vector_draw_t *draw, + float x_scale_factor, + float y_scale_factor) +{ + draw->x_scale_factor = x_scale_factor > 0.f ? x_scale_factor : 1.f; + draw->y_scale_factor = y_scale_factor > 0.f ? y_scale_factor : 1.f; +} + +/** + * hb_vector_draw_get_scale_factor: + * @draw: a draw context. + * @x_scale_factor: (out) (nullable): x scale factor. + * @y_scale_factor: (out) (nullable): y scale factor. + * + * Gets additional output scaling factors. + * + * Since: 13.0.0 + */ +void +hb_vector_draw_get_scale_factor (hb_vector_draw_t *draw, + float *x_scale_factor, + float *y_scale_factor) +{ + if (x_scale_factor) *x_scale_factor = draw->x_scale_factor; + if (y_scale_factor) *y_scale_factor = draw->y_scale_factor; +} + +/** + * hb_vector_draw_set_extents: + * @draw: a draw context. + * @extents: (nullable): output extents to set or expand. + * + * Sets or expands output extents on @draw. Passing `NULL` clears extents. + * + * Since: 13.0.0 + */ +void +hb_vector_draw_set_extents (hb_vector_draw_t *draw, + const hb_vector_extents_t *extents) +{ + if (!extents) + { + draw->extents = {0, 0, 0, 0}; + draw->has_extents = false; + return; + } + + if (!(extents->width > 0.f && extents->height > 0.f)) + return; + + if (draw->has_extents) + { + float x0 = hb_min (draw->extents.x, extents->x); + float y0 = hb_min (draw->extents.y, extents->y); + float x1 = hb_max (draw->extents.x + draw->extents.width, + extents->x + extents->width); + float y1 = hb_max (draw->extents.y + draw->extents.height, + extents->y + extents->height); + draw->extents = {x0, y0, x1 - x0, y1 - y0}; + } + else + { + draw->extents = *extents; + draw->has_extents = true; + } +} + +/** + * hb_vector_draw_get_extents: + * @draw: a draw context. + * @extents: (out) (nullable): where to store current output extents. + * + * Gets current output extents from @draw. + * + * Return value: `true` if extents are set, `false` otherwise. + * + * Since: 13.0.0 + */ +hb_bool_t +hb_vector_draw_get_extents (hb_vector_draw_t *draw, + hb_vector_extents_t *extents) +{ + if (!draw->has_extents) + return false; + + if (extents) + *extents = draw->extents; + return true; +} + +/** + * hb_vector_draw_set_glyph_extents: + * @draw: a draw context. + * @glyph_extents: glyph extents in font units. + * + * Expands @draw extents using @glyph_extents under the current transform. + * + * Return value: `true` on success, `false` otherwise. + * + * Since: 13.0.0 + */ +hb_bool_t +hb_vector_draw_set_glyph_extents (hb_vector_draw_t *draw, + const hb_glyph_extents_t *glyph_extents) +{ + hb_bool_t has_extents = draw->has_extents; + hb_bool_t ret = hb_svg_set_glyph_extents_common (draw->transform, + draw->x_scale_factor, + draw->y_scale_factor, + glyph_extents, + &draw->extents, + &has_extents); + draw->has_extents = has_extents; + return ret; +} + +/** + * hb_vector_draw_get_funcs: + * + * Gets draw callbacks implemented by the vector draw backend. + * + * Return value: (transfer none): immutable #hb_draw_funcs_t singleton. + * + * Since: 13.0.0 + */ +hb_draw_funcs_t * +hb_vector_draw_get_funcs (void) +{ + return hb_vector_draw_funcs_get (); +} + +/** + * hb_vector_draw_glyph: + * @draw: a draw context. + * @font: font object. + * @glyph: glyph ID. + * @pen_x: glyph x origin before context transform. + * @pen_y: glyph y origin before context transform. + * @extents_mode: extents update mode. + * + * Draws one glyph into @draw. + * + * Return value: `true` if glyph data was emitted, `false` otherwise. + * + * Since: 13.0.0 + */ +hb_bool_t +hb_vector_draw_glyph (hb_vector_draw_t *draw, + hb_font_t *font, + hb_codepoint_t glyph, + float pen_x, + float pen_y, + hb_vector_extents_mode_t extents_mode) +{ + if (draw->format != HB_VECTOR_FORMAT_SVG) + return false; + + if (extents_mode == HB_VECTOR_EXTENTS_MODE_EXPAND) + { + hb_glyph_extents_t ge; + if (hb_font_get_glyph_extents (font, glyph, &ge)) + { + float xx = draw->transform.xx; + float yx = draw->transform.yx; + float xy = draw->transform.xy; + float yy = draw->transform.yy; + float tx = draw->transform.x0 + xx * pen_x + xy * pen_y; + float ty = draw->transform.y0 + yx * pen_x + yy * pen_y; + hb_transform_t<> extents_transform = {xx, yx, -xy, -yy, tx, ty}; + + hb_bool_t has_extents = draw->has_extents; + hb_svg_set_glyph_extents_common (extents_transform, + draw->x_scale_factor, + draw->y_scale_factor, + &ge, + &draw->extents, + &has_extents); + draw->has_extents = has_extents; + } + } + + bool needs_def = !draw->flat && !hb_set_has (draw->defined_glyphs, glyph); + if (needs_def) + { + draw->path.clear (); + hb_svg_path_sink_t sink = {&draw->path, draw->precision}; + hb_font_draw_glyph (font, glyph, hb_svg_path_draw_funcs_get (), &sink); + if (!draw->path.length) + return false; + hb_svg_append_str (&draw->defs, "defs, glyph); + hb_svg_append_str (&draw->defs, "\" d=\""); + hb_svg_append_len (&draw->defs, draw->path.arrayZ, draw->path.length); + hb_svg_append_str (&draw->defs, "\"/>\n"); + hb_set_add (draw->defined_glyphs, glyph); + } + + if (draw->flat) + { + draw->path.clear (); + hb_svg_path_sink_t sink = {&draw->path, draw->precision}; + hb_font_draw_glyph (font, glyph, hb_svg_path_draw_funcs_get (), &sink); + + if (!draw->path.length) + return false; + + float xx = draw->transform.xx; + float yx = draw->transform.yx; + float xy = draw->transform.xy; + float yy = draw->transform.yy; + float tx = draw->transform.x0 + xx * pen_x + xy * pen_y; + float ty = draw->transform.y0 + yx * pen_x + yy * pen_y; + + hb_svg_append_str (&draw->body, "body, draw->path.arrayZ, draw->path.length); + hb_svg_append_str (&draw->body, "\" transform=\""); + hb_svg_append_instance_transform (&draw->body, + draw->precision, + draw->x_scale_factor, + draw->y_scale_factor, + xx, yx, xy, yy, tx, ty); + hb_svg_append_str (&draw->body, "\"/>\n"); + return true; + } + + float xx = draw->transform.xx; + float yx = draw->transform.yx; + float xy = draw->transform.xy; + float yy = draw->transform.yy; + float tx = draw->transform.x0 + xx * pen_x + xy * pen_y; + float ty = draw->transform.y0 + yx * pen_x + yy * pen_y; + + hb_svg_append_str (&draw->body, "body, glyph); + hb_svg_append_str (&draw->body, "\" transform=\""); + hb_svg_append_instance_transform (&draw->body, + draw->precision, + draw->x_scale_factor, + draw->y_scale_factor, + xx, yx, xy, yy, tx, ty); + hb_svg_append_str (&draw->body, "\"/>\n"); + return true; +} + +/** + * hb_vector_svg_set_flat: + * @draw: a draw context. + * @flat: whether to flatten geometry and disable reuse. + * + * Enables or disables SVG draw flattening. + * + * Since: 13.0.0 + */ +void +hb_vector_svg_set_flat (hb_vector_draw_t *draw, + hb_bool_t flat) +{ + draw->flat = !!flat; +} + +/** + * hb_vector_svg_set_precision: + * @draw: a draw context. + * @precision: decimal precision. + * + * Sets numeric output precision for SVG draw output. + * + * Since: 13.0.0 + */ +void +hb_vector_svg_set_precision (hb_vector_draw_t *draw, + unsigned precision) +{ + draw->precision = hb_min (precision, 12u); +} + +/** + * hb_vector_draw_render: + * @draw: a draw context. + * + * Renders accumulated draw content to an SVG blob. + * + * Return value: (transfer full) (nullable): output blob, or `NULL` if rendering cannot proceed. + * + * Since: 13.0.0 + */ +hb_blob_t * +hb_vector_draw_render (hb_vector_draw_t *draw) +{ + if (draw->format != HB_VECTOR_FORMAT_SVG) + return nullptr; + if (!draw->has_extents) + return nullptr; + + hb_vector_t out; + hb_svg_recover_recycled_buffer (draw->recycled_blob, &out); + unsigned estimated = draw->defs.length + + (draw->body.length ? draw->body.length : draw->path.length) + + 256; + out.alloc (estimated); + hb_svg_append_str (&out, "extents.x, draw->precision); + hb_svg_append_c (&out, ' '); + hb_svg_append_num (&out, draw->extents.y, draw->precision); + hb_svg_append_c (&out, ' '); + hb_svg_append_num (&out, draw->extents.width, draw->precision); + hb_svg_append_c (&out, ' '); + hb_svg_append_num (&out, draw->extents.height, draw->precision); + hb_svg_append_str (&out, "\" width=\""); + hb_svg_append_num (&out, draw->extents.width, draw->precision); + hb_svg_append_str (&out, "\" height=\""); + hb_svg_append_num (&out, draw->extents.height, draw->precision); + hb_svg_append_str (&out, "\">\n"); + + if (draw->defs.length) + { + hb_svg_append_str (&out, "\n"); + hb_svg_append_len (&out, draw->defs.arrayZ, draw->defs.length); + hb_svg_append_str (&out, "\n"); + } + + if (draw->body.length) + { + hb_svg_append_len (&out, draw->body.arrayZ, draw->body.length); + } + else if (draw->path.length) + { + hb_svg_append_str (&out, "path.arrayZ, draw->path.length); + hb_svg_append_str (&out, "\"/>\n"); + } + + hb_svg_append_str (&out, "\n"); + + hb_blob_t *blob = hb_svg_blob_from_buffer (&draw->recycled_blob, &out); + + draw->path.clear (); + draw->defs.clear (); + draw->body.clear (); + hb_set_clear (draw->defined_glyphs); + draw->has_extents = false; + draw->extents = {0, 0, 0, 0}; + + return blob; +} + +/** + * hb_vector_draw_reset: + * @draw: a draw context. + * + * Resets @draw state and clears accumulated content. + * + * Since: 13.0.0 + */ +void +hb_vector_draw_reset (hb_vector_draw_t *draw) +{ + draw->transform = {1, 0, 0, 1, 0, 0}; + draw->x_scale_factor = 1.f; + draw->y_scale_factor = 1.f; + draw->extents = {0, 0, 0, 0}; + draw->has_extents = false; + draw->precision = 2; + draw->flat = false; + draw->defs.clear (); + draw->body.clear (); + draw->path.clear (); + hb_set_clear (draw->defined_glyphs); +} + +/** + * hb_vector_draw_recycle_blob: + * @draw: a draw context. + * @blob: (nullable): previously rendered blob to recycle. + * + * Provides a blob for internal buffer reuse by later render calls. + * + * Since: 13.0.0 + */ +void +hb_vector_draw_recycle_blob (hb_vector_draw_t *draw, + hb_blob_t *blob) +{ + hb_blob_destroy (draw->recycled_blob); + draw->recycled_blob = nullptr; + if (!blob || blob == hb_blob_get_empty ()) + return; + draw->recycled_blob = blob; +} diff --git a/thirdparty/harfbuzz/src/hb-vector-svg-paint.cc b/thirdparty/harfbuzz/src/hb-vector-svg-paint.cc new file mode 100644 index 0000000000..b23efb7e3b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector-svg-paint.cc @@ -0,0 +1,1830 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-vector.h" +#include "hb-blob.hh" +#include "hb-geometry.hh" +#include "hb-machinery.hh" +#include "hb-map.hh" +#include "hb-vector-svg-path.hh" +#include "hb-vector-svg-subset.hh" +#include "hb-vector-svg-utils.hh" + +#include +#include +#include + +#include "hb-vector-svg.hh" + +struct hb_svg_color_glyph_cache_key_t +{ + hb_codepoint_t glyph = HB_CODEPOINT_INVALID; + unsigned palette = 0; + hb_color_t foreground = 0; + + hb_svg_color_glyph_cache_key_t () = default; + hb_svg_color_glyph_cache_key_t (hb_codepoint_t g, unsigned p, hb_color_t f) + : glyph (g), palette (p), foreground (f) {} + + bool operator == (const hb_svg_color_glyph_cache_key_t &o) const + { + return glyph == o.glyph && + palette == o.palette && + foreground == o.foreground; + } + + uint32_t hash () const + { + uint32_t h = hb_hash (glyph); + h = h * 31u + hb_hash (palette); + h = h * 31u + hb_hash (foreground); + return h; + } +}; + +struct hb_vector_paint_t +{ + hb_object_header_t header; + + hb_vector_format_t format = HB_VECTOR_FORMAT_SVG; + hb_transform_t<> transform = {1, 0, 0, 1, 0, 0}; + float x_scale_factor = 1.f; + float y_scale_factor = 1.f; + hb_vector_extents_t extents = {0, 0, 0, 0}; + bool has_extents = false; + + hb_color_t foreground = HB_COLOR (0, 0, 0, 255); + int palette = 0; + hb_hashmap_t custom_palette_colors; + unsigned precision = 2; + bool flat = false; + + hb_vector_t defs; + hb_vector_t path; + hb_vector_t> group_stack; + uint64_t transform_group_open_mask = 0; + unsigned transform_group_depth = 0; + unsigned transform_group_overflow_depth = 0; + + unsigned clip_rect_counter = 0; + unsigned gradient_counter = 0; + unsigned color_glyph_counter = 0; + hb_set_t *defined_outlines = nullptr; + hb_set_t *defined_clips = nullptr; + hb_hashmap_t defined_color_glyphs; + hb_vector_t color_stops_scratch; + hb_vector_t subset_body_scratch; + hb_vector_t captured_scratch; + hb_blob_t *recycled_blob = nullptr; + bool current_color_glyph_has_svg_image = false; + hb_codepoint_t current_svg_image_glyph = HB_CODEPOINT_INVALID; + hb_face_t *current_face = nullptr; + unsigned svg_image_counter = 0; + + hb_vector_t ¤t_body () { return group_stack.tail (); } + + void append_global_transform_prefix (hb_vector_t *buf) + { + if (transform.xx == 1.f && transform.yx == 0.f && + transform.xy == 0.f && transform.yy == 1.f && + transform.x0 == 0.f && transform.y0 == 0.f && + x_scale_factor == 1.f && y_scale_factor == 1.f) + return; + + unsigned sprec = hb_svg_scale_precision (precision); + hb_svg_append_str (buf, "\n"); + } + + void append_global_transform_suffix (hb_vector_t *buf) + { + if (transform.xx == 1.f && transform.yx == 0.f && + transform.xy == 0.f && transform.yy == 1.f && + transform.x0 == 0.f && transform.y0 == 0.f && + x_scale_factor == 1.f && y_scale_factor == 1.f) + return; + hb_svg_append_str (buf, "\n"); + } +}; + +static inline uint64_t +hb_svg_pack_color_glyph_cache_entry (unsigned def_id, + bool image_like) +{ + return ((uint64_t) def_id << 1) | (image_like ? 1ull : 0ull); +} + +static inline unsigned +hb_svg_cache_entry_def_id (uint64_t v) +{ + return (unsigned) (v >> 1); +} + +static inline bool +hb_svg_cache_entry_image_like (uint64_t v) +{ + return !!(v & 1ull); +} + +static inline hb_svg_color_glyph_cache_key_t +hb_svg_color_glyph_cache_key (hb_codepoint_t glyph, + unsigned palette, + hb_color_t foreground) +{ + return {glyph, palette, foreground}; +} + +static hb_bool_t +hb_svg_get_color_stops (hb_vector_paint_t *paint, + hb_color_line_t *color_line, + hb_vector_t *stops) +{ + unsigned len = hb_color_line_get_color_stops (color_line, 0, nullptr, nullptr); + if (unlikely (!stops->resize (len))) + return false; + hb_color_line_get_color_stops (color_line, 0, &len, stops->arrayZ); + + for (unsigned i = 0; i < len; i++) + if (stops->arrayZ[i].is_foreground) + stops->arrayZ[i].color = HB_COLOR (hb_color_get_blue (paint->foreground), + hb_color_get_green (paint->foreground), + hb_color_get_red (paint->foreground), + (unsigned) hb_color_get_alpha (stops->arrayZ[i].color) * + hb_color_get_alpha (paint->foreground) / 255); + return true; +} + +static const char * +hb_svg_extend_mode_str (hb_paint_extend_t ext) +{ + switch (ext) + { + case HB_PAINT_EXTEND_PAD: return "pad"; + case HB_PAINT_EXTEND_REPEAT: return "repeat"; + case HB_PAINT_EXTEND_REFLECT: return "reflect"; + default: return "pad"; + } +} + +static int +hb_svg_color_stop_cmp (const void *a, const void *b) +{ + const hb_color_stop_t *x = (const hb_color_stop_t *) a; + const hb_color_stop_t *y = (const hb_color_stop_t *) b; + if (x->offset < y->offset) return -1; + if (x->offset > y->offset) return 1; + return 0; +} + +static void +hb_svg_emit_color_stops (hb_vector_paint_t *paint, + hb_vector_t *buf, + hb_vector_t *stops) +{ + for (unsigned i = 0; i < stops->length; i++) + { + hb_color_t c = stops->arrayZ[i].color; + hb_svg_append_str (buf, "arrayZ[i].offset, 4); + hb_svg_append_str (buf, "\" stop-color=\"rgb("); + hb_svg_append_unsigned (buf, hb_color_get_red (c)); + hb_svg_append_c (buf, ','); + hb_svg_append_unsigned (buf, hb_color_get_green (c)); + hb_svg_append_c (buf, ','); + hb_svg_append_unsigned (buf, hb_color_get_blue (c)); + hb_svg_append_str (buf, ")\""); + if (hb_color_get_alpha (c) != 255) + { + hb_svg_append_str (buf, " stop-opacity=\""); + hb_svg_append_num (buf, hb_color_get_alpha (c) / 255.f, 4); + hb_svg_append_c (buf, '"'); + } + hb_svg_append_str (buf, "/>\n"); + } +} + +static const char * +hb_svg_composite_mode_str (hb_paint_composite_mode_t mode) +{ + switch (mode) + { + case HB_PAINT_COMPOSITE_MODE_CLEAR: + case HB_PAINT_COMPOSITE_MODE_SRC: + case HB_PAINT_COMPOSITE_MODE_DEST: + case HB_PAINT_COMPOSITE_MODE_DEST_OVER: + case HB_PAINT_COMPOSITE_MODE_SRC_IN: + case HB_PAINT_COMPOSITE_MODE_DEST_IN: + case HB_PAINT_COMPOSITE_MODE_SRC_OUT: + case HB_PAINT_COMPOSITE_MODE_DEST_OUT: + case HB_PAINT_COMPOSITE_MODE_SRC_ATOP: + case HB_PAINT_COMPOSITE_MODE_DEST_ATOP: + case HB_PAINT_COMPOSITE_MODE_XOR: + case HB_PAINT_COMPOSITE_MODE_PLUS: + return nullptr; + case HB_PAINT_COMPOSITE_MODE_SRC_OVER: return "normal"; + case HB_PAINT_COMPOSITE_MODE_SCREEN: return "screen"; + case HB_PAINT_COMPOSITE_MODE_OVERLAY: return "overlay"; + case HB_PAINT_COMPOSITE_MODE_DARKEN: return "darken"; + case HB_PAINT_COMPOSITE_MODE_LIGHTEN: return "lighten"; + case HB_PAINT_COMPOSITE_MODE_COLOR_DODGE: return "color-dodge"; + case HB_PAINT_COMPOSITE_MODE_COLOR_BURN: return "color-burn"; + case HB_PAINT_COMPOSITE_MODE_HARD_LIGHT: return "hard-light"; + case HB_PAINT_COMPOSITE_MODE_SOFT_LIGHT: return "soft-light"; + case HB_PAINT_COMPOSITE_MODE_DIFFERENCE: return "difference"; + case HB_PAINT_COMPOSITE_MODE_EXCLUSION: return "exclusion"; + case HB_PAINT_COMPOSITE_MODE_MULTIPLY: return "multiply"; + case HB_PAINT_COMPOSITE_MODE_HSL_HUE: return "hue"; + case HB_PAINT_COMPOSITE_MODE_HSL_SATURATION: return "saturation"; + case HB_PAINT_COMPOSITE_MODE_HSL_COLOR: return "color"; + case HB_PAINT_COMPOSITE_MODE_HSL_LUMINOSITY: return "luminosity"; + default: return nullptr; + } +} + +struct hb_svg_point_t { float x, y; }; +struct hb_svg_rgba_t { float r, g, b, a; }; + +static inline float +hb_svg_lerp (float a, float b, float t) +{ return a + (b - a) * t; } + +static inline float +hb_svg_clamp01 (float v) +{ + if (v < 0.f) return 0.f; + if (v > 1.f) return 1.f; + return v; +} + +static inline hb_svg_rgba_t +hb_svg_rgba_from_hb_color (hb_color_t c) +{ + return {(float) hb_color_get_red (c) / 255.f, + (float) hb_color_get_green (c) / 255.f, + (float) hb_color_get_blue (c) / 255.f, + (float) hb_color_get_alpha (c) / 255.f}; +} + +static inline hb_color_t +hb_svg_hb_color_from_rgba (const hb_svg_rgba_t &c) +{ + unsigned r = (unsigned) roundf (hb_svg_clamp01 (c.r) * 255.f); + unsigned g = (unsigned) roundf (hb_svg_clamp01 (c.g) * 255.f); + unsigned b = (unsigned) roundf (hb_svg_clamp01 (c.b) * 255.f); + unsigned a = (unsigned) roundf (hb_svg_clamp01 (c.a) * 255.f); + return HB_COLOR (b, g, r, a); +} + +static inline hb_svg_rgba_t +hb_svg_lerp_rgba (const hb_svg_rgba_t &c0, + const hb_svg_rgba_t &c1, + float t) +{ + return {hb_svg_lerp (c0.r, c1.r, t), + hb_svg_lerp (c0.g, c1.g, t), + hb_svg_lerp (c0.b, c1.b, t), + hb_svg_lerp (c0.a, c1.a, t)}; +} + +static inline float hb_svg_dot (const hb_svg_point_t &p, const hb_svg_point_t &q) { return p.x * q.x + p.y * q.y; } +static inline hb_svg_point_t hb_svg_add (const hb_svg_point_t &p, const hb_svg_point_t &q) { return {p.x + q.x, p.y + q.y}; } +static inline hb_svg_point_t hb_svg_sub (const hb_svg_point_t &p, const hb_svg_point_t &q) { return {p.x - q.x, p.y - q.y}; } +static inline hb_svg_point_t hb_svg_scale (const hb_svg_point_t &p, float f) { return {p.x * f, p.y * f}; } + +static inline hb_svg_point_t +hb_svg_normalize (const hb_svg_point_t &p) +{ + float len = sqrtf (hb_svg_dot (p, p)); + if (len == 0.f) return {0.f, 0.f}; + return hb_svg_scale (p, 1.f / len); +} + +static void +hb_svg_add_sweep_patch (hb_vector_t *body, + unsigned precision, + float cx, float cy, float radius, + float a0, const hb_svg_rgba_t &c0_in, + float a1, const hb_svg_rgba_t &c1_in) +{ + static const float max_angle = HB_PI / 16.f; + hb_svg_point_t center = {cx, cy}; + int num_splits = (int) ceilf (fabsf (a1 - a0) / max_angle); + if (num_splits < 1) num_splits = 1; + + hb_svg_point_t p0 = {cosf (a0), sinf (a0)}; + hb_svg_rgba_t color0 = c0_in; + + for (int a = 0; a < num_splits; a++) + { + float k = (a + 1.f) / num_splits; + float angle1 = hb_svg_lerp (a0, a1, k); + hb_svg_rgba_t color1 = hb_svg_lerp_rgba (c0_in, c1_in, k); + + hb_svg_point_t p1 = {cosf (angle1), sinf (angle1)}; + hb_svg_point_t sp0 = hb_svg_add (center, hb_svg_scale (p0, radius)); + hb_svg_point_t sp1 = hb_svg_add (center, hb_svg_scale (p1, radius)); + + hb_svg_point_t A = hb_svg_normalize (hb_svg_add (p0, p1)); + hb_svg_point_t U = {-A.y, A.x}; + float up0 = hb_svg_dot (U, p0); + float up1 = hb_svg_dot (U, p1); + if (fabsf (up0) < 1e-6f || fabsf (up1) < 1e-6f) + { + p0 = p1; + color0 = color1; + continue; + } + hb_svg_point_t C0 = hb_svg_add (A, hb_svg_scale (U, hb_svg_dot (hb_svg_sub (p0, A), p0) / up0)); + hb_svg_point_t C1 = hb_svg_add (A, hb_svg_scale (U, hb_svg_dot (hb_svg_sub (p1, A), p1) / up1)); + + hb_svg_point_t sc0 = hb_svg_add (center, hb_svg_scale (hb_svg_add (C0, hb_svg_scale (hb_svg_sub (C0, p0), 0.33333f)), radius)); + hb_svg_point_t sc1 = hb_svg_add (center, hb_svg_scale (hb_svg_add (C1, hb_svg_scale (hb_svg_sub (C1, p1), 0.33333f)), radius)); + + hb_svg_rgba_t mid_color = hb_svg_lerp_rgba (color0, color1, 0.5f); + hb_color_t mid = hb_svg_hb_color_from_rgba (mid_color); + + hb_svg_append_str (body, "\n"); + + p0 = p1; + color0 = color1; + } +} + +static void +hb_svg_add_sweep_gradient_patches (hb_vector_t *body, + unsigned precision, + hb_color_stop_t *stops, + unsigned n_stops, + hb_paint_extend_t extend, + float cx, float cy, float radius, + float start_angle, float end_angle) +{ + if (!n_stops) return; + + hb_svg_rgba_t colors_buf[16]; + float angles_buf[16]; + hb_svg_rgba_t *colors = colors_buf; + float *angles = angles_buf; + bool dynamic = false; + + if (start_angle == end_angle) + { + if (extend == HB_PAINT_EXTEND_PAD) + { + if (start_angle > 0.f) + { + hb_svg_rgba_t c = hb_svg_rgba_from_hb_color (stops[0].color); + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, 0.f, c, start_angle, c); + } + if (end_angle < HB_2_PI) + { + hb_svg_rgba_t c = hb_svg_rgba_from_hb_color (stops[n_stops - 1].color); + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, end_angle, c, HB_2_PI, c); + } + } + return; + } + + if (end_angle < start_angle) + { + float tmp = start_angle; start_angle = end_angle; end_angle = tmp; + for (unsigned i = 0; i < n_stops - 1 - i; i++) + { + hb_color_stop_t t = stops[i]; + stops[i] = stops[n_stops - 1 - i]; + stops[n_stops - 1 - i] = t; + } + for (unsigned i = 0; i < n_stops; i++) + stops[i].offset = 1.f - stops[i].offset; + } + + if (n_stops > 16) + { + angles = (float *) hb_malloc (sizeof (float) * n_stops); + colors = (hb_svg_rgba_t *) hb_malloc (sizeof (hb_svg_rgba_t) * n_stops); + if (!angles || !colors) + { + hb_free (angles); + hb_free (colors); + return; + } + dynamic = true; + } + + for (unsigned i = 0; i < n_stops; i++) + { + angles[i] = start_angle + stops[i].offset * (end_angle - start_angle); + colors[i] = hb_svg_rgba_from_hb_color (stops[i].color); + } + + if (extend == HB_PAINT_EXTEND_PAD) + { + unsigned pos; + hb_svg_rgba_t color0 = colors[0]; + for (pos = 0; pos < n_stops; pos++) + { + if (angles[pos] >= 0) + { + if (pos > 0) + { + float f = (0.f - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + color0 = hb_svg_lerp_rgba (colors[pos - 1], colors[pos], f); + } + break; + } + } + if (pos == n_stops) + { + color0 = colors[n_stops - 1]; + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, 0.f, color0, HB_2_PI, color0); + goto done; + } + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, 0.f, color0, angles[pos], colors[pos]); + for (pos++; pos < n_stops; pos++) + { + if (angles[pos] <= HB_2_PI) + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, angles[pos - 1], colors[pos - 1], angles[pos], colors[pos]); + else + { + float f = (HB_2_PI - angles[pos - 1]) / (angles[pos] - angles[pos - 1]); + hb_svg_rgba_t color1 = hb_svg_lerp_rgba (colors[pos - 1], colors[pos], f); + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, angles[pos - 1], colors[pos - 1], HB_2_PI, color1); + break; + } + } + if (pos == n_stops) + { + color0 = colors[n_stops - 1]; + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, angles[n_stops - 1], color0, HB_2_PI, color0); + goto done; + } + } + else + { + float span = angles[n_stops - 1] - angles[0]; + if (fabsf (span) < 1e-6f) + goto done; + + int k = 0; + if (angles[0] >= 0) + { + float ss = angles[0]; + while (ss > 0) + { + if (span > 0) { ss -= span; k--; } + else { ss += span; k++; } + } + } + else + { + float ee = angles[n_stops - 1]; + while (ee < 0) + { + if (span > 0) { ee += span; k++; } + else { ee -= span; k--; } + } + } + + span = fabsf (span); + for (int l = k; l < 1000; l++) + { + for (unsigned i = 1; i < n_stops; i++) + { + float a0_l, a1_l; + const hb_svg_rgba_t *col0, *col1; + if ((l % 2 != 0) && (extend == HB_PAINT_EXTEND_REFLECT)) + { + a0_l = angles[0] + angles[n_stops - 1] - angles[n_stops - i] + l * span; + a1_l = angles[0] + angles[n_stops - 1] - angles[n_stops - 1 - i] + l * span; + col0 = &colors[n_stops - i]; + col1 = &colors[n_stops - 1 - i]; + } + else + { + a0_l = angles[i - 1] + l * span; + a1_l = angles[i] + l * span; + col0 = &colors[i - 1]; + col1 = &colors[i]; + } + + if (a1_l < 0.f) continue; + if (a0_l < 0.f) + { + float f = (0.f - a0_l) / (a1_l - a0_l); + hb_svg_rgba_t c = hb_svg_lerp_rgba (*col0, *col1, f); + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, 0.f, c, a1_l, *col1); + } + else if (a1_l >= HB_2_PI) + { + float f = (HB_2_PI - a0_l) / (a1_l - a0_l); + hb_svg_rgba_t c = hb_svg_lerp_rgba (*col0, *col1, f); + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, a0_l, *col0, HB_2_PI, c); + goto done; + } + else + hb_svg_add_sweep_patch (body, precision, cx, cy, radius, a0_l, *col0, a1_l, *col1); + } + } + } + +done: + if (dynamic) + { + hb_free (angles); + hb_free (colors); + } +} + + +static void hb_vector_paint_push_transform (hb_paint_funcs_t *, void *, + float, float, float, float, float, float, + void *); +static void hb_vector_paint_pop_transform (hb_paint_funcs_t *, void *, void *); +static void hb_vector_paint_push_clip_glyph (hb_paint_funcs_t *, void *, hb_codepoint_t, hb_font_t *, void *); +static void hb_vector_paint_push_clip_rectangle (hb_paint_funcs_t *, void *, float, float, float, float, void *); +static void hb_vector_paint_pop_clip (hb_paint_funcs_t *, void *, void *); +static void hb_vector_paint_color (hb_paint_funcs_t *, void *, hb_bool_t, hb_color_t, void *); +static hb_bool_t hb_vector_paint_image (hb_paint_funcs_t *, void *, hb_blob_t *, unsigned, unsigned, hb_tag_t, float, hb_glyph_extents_t *, void *); +static void hb_vector_paint_linear_gradient (hb_paint_funcs_t *, void *, hb_color_line_t *, float, float, float, float, float, float, void *); +static void hb_vector_paint_radial_gradient (hb_paint_funcs_t *, void *, hb_color_line_t *, float, float, float, float, float, float, void *); +static void hb_vector_paint_sweep_gradient (hb_paint_funcs_t *, void *, hb_color_line_t *, float, float, float, float, void *); +static void hb_vector_paint_push_group (hb_paint_funcs_t *, void *, void *); +static void hb_vector_paint_pop_group (hb_paint_funcs_t *, void *, hb_paint_composite_mode_t, void *); +static hb_bool_t hb_vector_paint_color_glyph (hb_paint_funcs_t *, void *, hb_codepoint_t, hb_font_t *, void *); +static hb_bool_t hb_vector_paint_custom_palette_color (hb_paint_funcs_t *, void *, unsigned, hb_color_t *, void *); + +static inline void free_static_vector_paint_funcs (); +static struct hb_vector_paint_funcs_lazy_loader_t + : hb_paint_funcs_lazy_loader_t +{ + static hb_paint_funcs_t *create () + { + hb_paint_funcs_t *funcs = hb_paint_funcs_create (); + hb_paint_funcs_set_push_transform_func (funcs, (hb_paint_push_transform_func_t) hb_vector_paint_push_transform, nullptr, nullptr); + hb_paint_funcs_set_pop_transform_func (funcs, (hb_paint_pop_transform_func_t) hb_vector_paint_pop_transform, nullptr, nullptr); + hb_paint_funcs_set_push_clip_glyph_func (funcs, (hb_paint_push_clip_glyph_func_t) hb_vector_paint_push_clip_glyph, nullptr, nullptr); + hb_paint_funcs_set_push_clip_rectangle_func (funcs, (hb_paint_push_clip_rectangle_func_t) hb_vector_paint_push_clip_rectangle, nullptr, nullptr); + hb_paint_funcs_set_pop_clip_func (funcs, (hb_paint_pop_clip_func_t) hb_vector_paint_pop_clip, nullptr, nullptr); + hb_paint_funcs_set_color_func (funcs, (hb_paint_color_func_t) hb_vector_paint_color, nullptr, nullptr); + hb_paint_funcs_set_image_func (funcs, (hb_paint_image_func_t) hb_vector_paint_image, nullptr, nullptr); + hb_paint_funcs_set_linear_gradient_func (funcs, (hb_paint_linear_gradient_func_t) hb_vector_paint_linear_gradient, nullptr, nullptr); + hb_paint_funcs_set_radial_gradient_func (funcs, (hb_paint_radial_gradient_func_t) hb_vector_paint_radial_gradient, nullptr, nullptr); + hb_paint_funcs_set_sweep_gradient_func (funcs, (hb_paint_sweep_gradient_func_t) hb_vector_paint_sweep_gradient, nullptr, nullptr); + hb_paint_funcs_set_push_group_func (funcs, (hb_paint_push_group_func_t) hb_vector_paint_push_group, nullptr, nullptr); + hb_paint_funcs_set_pop_group_func (funcs, (hb_paint_pop_group_func_t) hb_vector_paint_pop_group, nullptr, nullptr); + hb_paint_funcs_set_color_glyph_func (funcs, (hb_paint_color_glyph_func_t) hb_vector_paint_color_glyph, nullptr, nullptr); + hb_paint_funcs_set_custom_palette_color_func (funcs, (hb_paint_custom_palette_color_func_t) hb_vector_paint_custom_palette_color, nullptr, nullptr); + hb_paint_funcs_make_immutable (funcs); + hb_atexit (free_static_vector_paint_funcs); + return funcs; + } +} static_vector_paint_funcs; + +static inline void +free_static_vector_paint_funcs () +{ + static_vector_paint_funcs.free_instance (); +} + +static hb_paint_funcs_t * +hb_vector_paint_funcs_get () +{ + return static_vector_paint_funcs.get_unconst (); +} + +static hb_bool_t +hb_vector_paint_ensure_initialized (hb_vector_paint_t *paint) +{ + if (paint->group_stack.length) + return true; + if (unlikely (!paint->group_stack.push_or_fail ())) + return false; + paint->group_stack.tail ().alloc (4096); + return !paint->group_stack.in_error (); +} + +static void +hb_vector_paint_push_transform (hb_paint_funcs_t *, + void *paint_data, + float xx, float yx, + float xy, float yy, + float dx, float dy, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + + if (unlikely (paint->transform_group_overflow_depth)) + { + paint->transform_group_overflow_depth++; + return; + } + if (unlikely (paint->transform_group_depth >= 64)) + { + paint->transform_group_overflow_depth = 1; + return; + } + + hb_bool_t opened = + !(fabsf (xx - 1.f) < 1e-6f && fabsf (yx) < 1e-6f && + fabsf (xy) < 1e-6f && fabsf (yy - 1.f) < 1e-6f && + fabsf (dx) < 1e-6f && fabsf (dy) < 1e-6f); + paint->transform_group_open_mask = (paint->transform_group_open_mask << 1) | (opened ? 1ull : 0ull); + paint->transform_group_depth++; + + if (!opened) + return; + + auto &body = paint->current_body (); + unsigned sprec = hb_svg_scale_precision (paint->precision); + hb_svg_append_str (&body, "precision); + hb_svg_append_c (&body, ','); + hb_svg_append_num (&body, dy, paint->precision); + hb_svg_append_str (&body, ")\">\n"); +} + +static void +hb_vector_paint_pop_transform (hb_paint_funcs_t *, + void *paint_data, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + if (unlikely (paint->transform_group_overflow_depth)) + { + paint->transform_group_overflow_depth--; + return; + } + if (!paint->transform_group_depth) + return; + paint->transform_group_depth--; + hb_bool_t opened = !!(paint->transform_group_open_mask & 1ull); + paint->transform_group_open_mask >>= 1; + if (opened) + hb_svg_append_str (&paint->current_body (), "\n"); +} + +static void +hb_vector_paint_push_clip_glyph (hb_paint_funcs_t *, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + + if (!hb_set_has (paint->defined_outlines, glyph)) + { + hb_set_add (paint->defined_outlines, glyph); + paint->path.clear (); + hb_svg_path_sink_t sink = {&paint->path, paint->precision}; + hb_font_draw_glyph (font, glyph, hb_svg_path_draw_funcs_get (), &sink); + hb_svg_append_str (&paint->defs, "defs, glyph); + hb_svg_append_str (&paint->defs, "\" d=\""); + hb_svg_append_len (&paint->defs, paint->path.arrayZ, paint->path.length); + hb_svg_append_str (&paint->defs, "\"/>\n"); + } + + if (!hb_set_has (paint->defined_clips, glyph)) + { + hb_set_add (paint->defined_clips, glyph); + hb_svg_append_str (&paint->defs, "defs, glyph); + hb_svg_append_str (&paint->defs, "\">defs, glyph); + hb_svg_append_str (&paint->defs, "\"/>\n"); + } + + hb_svg_append_str (&paint->current_body (), "current_body (), glyph); + hb_svg_append_str (&paint->current_body (), ")\">\n"); +} + +static void +hb_vector_paint_push_clip_rectangle (hb_paint_funcs_t *, + void *paint_data, + float xmin, float ymin, + float xmax, float ymax, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + + unsigned clip_id = paint->clip_rect_counter++; + hb_svg_append_str (&paint->defs, "defs, clip_id); + hb_svg_append_str (&paint->defs, "\">defs, xmin, paint->precision); + hb_svg_append_str (&paint->defs, "\" y=\""); + hb_svg_append_num (&paint->defs, ymin, paint->precision); + hb_svg_append_str (&paint->defs, "\" width=\""); + hb_svg_append_num (&paint->defs, xmax - xmin, paint->precision); + hb_svg_append_str (&paint->defs, "\" height=\""); + hb_svg_append_num (&paint->defs, ymax - ymin, paint->precision); + hb_svg_append_str (&paint->defs, "\"/>\n"); + + hb_svg_append_str (&paint->current_body (), "current_body (), clip_id); + hb_svg_append_str (&paint->current_body (), ")\">\n"); +} + +static void +hb_vector_paint_pop_clip (hb_paint_funcs_t *, + void *paint_data, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + hb_svg_append_str (&paint->current_body (), "\n"); +} + +static void +hb_vector_paint_color (hb_paint_funcs_t *, + void *paint_data, + hb_bool_t is_foreground, + hb_color_t color, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + + hb_color_t c = color; + if (is_foreground) + c = HB_COLOR (hb_color_get_blue (paint->foreground), + hb_color_get_green (paint->foreground), + hb_color_get_red (paint->foreground), + (unsigned) hb_color_get_alpha (paint->foreground) * hb_color_get_alpha (color) / 255); + + auto &body = paint->current_body (); + hb_svg_append_str (&body, "\n"); +} + +static hb_bool_t +hb_vector_paint_image (hb_paint_funcs_t *, + void *paint_data, + hb_blob_t *image, + unsigned width, + unsigned height, + hb_tag_t format, + float slant HB_UNUSED, + hb_glyph_extents_t *extents, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return false; + + auto &body = paint->current_body (); + if (format == HB_TAG ('s','v','g',' ')) + { + paint->current_color_glyph_has_svg_image = true; + + paint->subset_body_scratch.clear (); + bool subset_ok = hb_svg_subset_glyph_image (paint->current_face, + image, + paint->current_svg_image_glyph, + &paint->svg_image_counter, + &paint->defs, + &paint->subset_body_scratch); + if (unlikely (!subset_ok)) + return false; + + if (extents) + { + hb_svg_append_str (&body, "x_bearing, paint->precision); + hb_svg_append_c (&body, ','); + hb_svg_append_num (&body, (float) extents->y_bearing, paint->precision); + hb_svg_append_str (&body, ") scale("); + hb_svg_append_num (&body, (float) extents->width / width, paint->precision); + hb_svg_append_c (&body, ','); + hb_svg_append_num (&body, (float) extents->height / height, paint->precision); + hb_svg_append_str (&body, ")\">\n"); + } + + hb_svg_append_len (&body, + paint->subset_body_scratch.arrayZ, + paint->subset_body_scratch.length); + hb_svg_append_c (&body, '\n'); + + if (extents) + hb_svg_append_str (&body, "\n"); + + return true; + } + + if (format == HB_TAG ('p','n','g',' ')) + { + if (!extents || !width || !height) + return false; + + unsigned len = 0; + const char *png_data = hb_blob_get_data (image, &len); + if (!png_data || !len) + return false; + + hb_svg_append_str (&body, "x_bearing, paint->precision); + hb_svg_append_c (&body, ','); + hb_svg_append_num (&body, (float) extents->y_bearing, paint->precision); + hb_svg_append_str (&body, ") scale("); + hb_svg_append_num (&body, (float) extents->width / width, paint->precision); + hb_svg_append_c (&body, ','); + hb_svg_append_num (&body, (float) extents->height / height, paint->precision); + hb_svg_append_str (&body, ")\">\n"); + + hb_svg_append_str (&body, "precision); + hb_svg_append_str (&body, "\" height=\""); + hb_svg_append_num (&body, (float) height, paint->precision); + hb_svg_append_str (&body, "\"/>\n\n"); + + return true; + } + + return false; +} + +static void +hb_vector_paint_linear_gradient (hb_paint_funcs_t *, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, + float x1, float y1, + float x2, float y2, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + + hb_vector_t &stops = paint->color_stops_scratch; + if (!hb_svg_get_color_stops (paint, color_line, &stops) || !stops.length) + return; + + qsort (stops.arrayZ, stops.length, sizeof (hb_color_stop_t), hb_svg_color_stop_cmp); + + unsigned grad_id = paint->gradient_counter++; + + hb_svg_append_str (&paint->defs, "defs, grad_id); + hb_svg_append_str (&paint->defs, "\" gradientUnits=\"userSpaceOnUse\" x1=\""); + hb_svg_append_num (&paint->defs, x0, paint->precision); + hb_svg_append_str (&paint->defs, "\" y1=\""); + hb_svg_append_num (&paint->defs, y0, paint->precision); + hb_svg_append_str (&paint->defs, "\" x2=\""); + hb_svg_append_num (&paint->defs, x1 + (x1 - x2), paint->precision); + hb_svg_append_str (&paint->defs, "\" y2=\""); + hb_svg_append_num (&paint->defs, y1 + (y1 - y2), paint->precision); + hb_svg_append_str (&paint->defs, "\" spreadMethod=\""); + hb_svg_append_str (&paint->defs, hb_svg_extend_mode_str (hb_color_line_get_extend (color_line))); + hb_svg_append_str (&paint->defs, "\">\n"); + hb_svg_emit_color_stops (paint, &paint->defs, &stops); + hb_svg_append_str (&paint->defs, "\n"); + + hb_svg_append_str (&paint->current_body (), + "current_body (), grad_id); + hb_svg_append_str (&paint->current_body (), ")\"/>\n"); +} + +static void +hb_vector_paint_radial_gradient (hb_paint_funcs_t *, + void *paint_data, + hb_color_line_t *color_line, + float x0, float y0, float r0, + float x1, float y1, float r1, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + + hb_vector_t &stops = paint->color_stops_scratch; + if (!hb_svg_get_color_stops (paint, color_line, &stops) || !stops.length) + return; + + qsort (stops.arrayZ, stops.length, sizeof (hb_color_stop_t), hb_svg_color_stop_cmp); + + unsigned grad_id = paint->gradient_counter++; + + hb_svg_append_str (&paint->defs, "defs, grad_id); + hb_svg_append_str (&paint->defs, "\" gradientUnits=\"userSpaceOnUse\" cx=\""); + hb_svg_append_num (&paint->defs, x1, paint->precision); + hb_svg_append_str (&paint->defs, "\" cy=\""); + hb_svg_append_num (&paint->defs, y1, paint->precision); + hb_svg_append_str (&paint->defs, "\" r=\""); + hb_svg_append_num (&paint->defs, r1, paint->precision); + hb_svg_append_str (&paint->defs, "\" fx=\""); + hb_svg_append_num (&paint->defs, x0, paint->precision); + hb_svg_append_str (&paint->defs, "\" fy=\""); + hb_svg_append_num (&paint->defs, y0, paint->precision); + if (r0 > 0) + { + hb_svg_append_str (&paint->defs, "\" fr=\""); + hb_svg_append_num (&paint->defs, r0, paint->precision); + } + hb_svg_append_str (&paint->defs, "\" spreadMethod=\""); + hb_svg_append_str (&paint->defs, hb_svg_extend_mode_str (hb_color_line_get_extend (color_line))); + hb_svg_append_str (&paint->defs, "\">\n"); + hb_svg_emit_color_stops (paint, &paint->defs, &stops); + hb_svg_append_str (&paint->defs, "\n"); + + hb_svg_append_str (&paint->current_body (), + "current_body (), grad_id); + hb_svg_append_str (&paint->current_body (), ")\"/>\n"); +} + +static void +hb_vector_paint_sweep_gradient (hb_paint_funcs_t *, + void *paint_data, + hb_color_line_t *color_line, + float cx, float cy, + float start_angle, float end_angle, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + + hb_vector_t &stops = paint->color_stops_scratch; + if (!hb_svg_get_color_stops (paint, color_line, &stops) || !stops.length) + return; + + qsort (stops.arrayZ, stops.length, sizeof (hb_color_stop_t), hb_svg_color_stop_cmp); + hb_svg_add_sweep_gradient_patches (&paint->current_body (), + paint->precision, + stops.arrayZ, + stops.length, + hb_color_line_get_extend (color_line), + cx, cy, + 32767.f, + start_angle, + end_angle); +} + +static void +hb_vector_paint_push_group (hb_paint_funcs_t *, + void *paint_data, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + paint->group_stack.push_or_fail (hb_vector_t {}); +} + +static void +hb_vector_paint_pop_group (hb_paint_funcs_t *, + void *paint_data, + hb_paint_composite_mode_t mode, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return; + if (paint->group_stack.length < 2) + return; + + hb_vector_t group = paint->group_stack.pop (); + auto &body = paint->current_body (); + + const char *blend = hb_svg_composite_mode_str (mode); + if (blend) + { + hb_svg_append_str (&body, "\n"); + hb_svg_append_len (&body, group.arrayZ, group.length); + hb_svg_append_str (&body, "\n"); + } + else + hb_svg_append_len (&body, group.arrayZ, group.length); +} + +static hb_bool_t +hb_vector_paint_color_glyph (hb_paint_funcs_t *, + void *paint_data, + hb_codepoint_t glyph, + hb_font_t *font, + void *) +{ + auto *paint = (hb_vector_paint_t *) paint_data; + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return false; + hb_codepoint_t old_gid = paint->current_svg_image_glyph; + hb_face_t *old_face = paint->current_face; + paint->current_svg_image_glyph = glyph; + paint->current_face = hb_font_get_face (font); + hb_font_paint_glyph (font, glyph, + hb_vector_paint_funcs_get (), + paint, + paint->palette, + paint->foreground); + paint->current_svg_image_glyph = old_gid; + paint->current_face = old_face; + return true; +} + + + +/** + * hb_vector_paint_create_or_fail: + * @format: output format. + * + * Creates a new paint context for vector output. + * + * Return value: (nullable): a newly allocated #hb_vector_paint_t, or `NULL` on failure. + * + * Since: 13.0.0 + */ +hb_vector_paint_t * +hb_vector_paint_create_or_fail (hb_vector_format_t format) +{ + if (format != HB_VECTOR_FORMAT_SVG) + return nullptr; + + hb_vector_paint_t *paint = hb_object_create (); + if (unlikely (!paint)) + return nullptr; + paint->format = format; + + paint->defined_outlines = hb_set_create (); + paint->defined_clips = hb_set_create (); + paint->defs.alloc (4096); + paint->path.alloc (2048); + paint->subset_body_scratch.alloc (2048); + paint->captured_scratch.alloc (4096); + paint->color_stops_scratch.alloc (16); + + return paint; +} + +/** + * hb_vector_paint_reference: + * @paint: a paint context. + * + * Increases the reference count of @paint. + * + * Return value: (transfer full): referenced @paint. + * + * Since: 13.0.0 + */ +hb_vector_paint_t * +hb_vector_paint_reference (hb_vector_paint_t *paint) +{ + return hb_object_reference (paint); +} + +/** + * hb_vector_paint_destroy: + * @paint: a paint context. + * + * Decreases the reference count of @paint and destroys it when it reaches zero. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_destroy (hb_vector_paint_t *paint) +{ + if (!hb_object_should_destroy (paint)) + return; + + hb_blob_destroy (paint->recycled_blob); + hb_set_destroy (paint->defined_outlines); + hb_set_destroy (paint->defined_clips); + hb_object_actually_destroy (paint); + hb_free (paint); +} + +/** + * hb_vector_paint_set_user_data: + * @paint: a paint context. + * @key: user-data key. + * @data: user-data value. + * @destroy: (nullable): destroy callback for @data. + * @replace: whether to replace an existing value for @key. + * + * Attaches user data to @paint. + * + * Return value: `true` on success, `false` otherwise. + * + * Since: 13.0.0 + */ +hb_bool_t +hb_vector_paint_set_user_data (hb_vector_paint_t *paint, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace) +{ + return hb_object_set_user_data (paint, key, data, destroy, replace); +} + +/** + * hb_vector_paint_get_user_data: + * @paint: a paint context. + * @key: user-data key. + * + * Gets previously attached user data from @paint. + * + * Return value: (nullable): user-data value associated with @key. + * + * Since: 13.0.0 + */ +void * +hb_vector_paint_get_user_data (hb_vector_paint_t *paint, + hb_user_data_key_t *key) +{ + return hb_object_get_user_data (paint, key); +} + +/** + * hb_vector_paint_set_transform: + * @paint: a paint context. + * @xx: transform xx component. + * @yx: transform yx component. + * @xy: transform xy component. + * @yy: transform yy component. + * @dx: transform x translation. + * @dy: transform y translation. + * + * Sets the affine transform used when painting glyphs. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_set_transform (hb_vector_paint_t *paint, + float xx, float yx, + float xy, float yy, + float dx, float dy) +{ + paint->transform = {xx, yx, xy, yy, dx, dy}; +} + +/** + * hb_vector_paint_get_transform: + * @paint: a paint context. + * @xx: (out) (nullable): transform xx component. + * @yx: (out) (nullable): transform yx component. + * @xy: (out) (nullable): transform xy component. + * @yy: (out) (nullable): transform yy component. + * @dx: (out) (nullable): transform x translation. + * @dy: (out) (nullable): transform y translation. + * + * Gets the affine transform used when painting glyphs. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_get_transform (hb_vector_paint_t *paint, + float *xx, float *yx, + float *xy, float *yy, + float *dx, float *dy) +{ + if (xx) *xx = paint->transform.xx; + if (yx) *yx = paint->transform.yx; + if (xy) *xy = paint->transform.xy; + if (yy) *yy = paint->transform.yy; + if (dx) *dx = paint->transform.x0; + if (dy) *dy = paint->transform.y0; +} + +/** + * hb_vector_paint_set_scale_factor: + * @paint: a paint context. + * @x_scale_factor: x scale factor. + * @y_scale_factor: y scale factor. + * + * Sets additional output scaling factors. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_set_scale_factor (hb_vector_paint_t *paint, + float x_scale_factor, + float y_scale_factor) +{ + paint->x_scale_factor = x_scale_factor > 0.f ? x_scale_factor : 1.f; + paint->y_scale_factor = y_scale_factor > 0.f ? y_scale_factor : 1.f; +} + +/** + * hb_vector_paint_get_scale_factor: + * @paint: a paint context. + * @x_scale_factor: (out) (nullable): x scale factor. + * @y_scale_factor: (out) (nullable): y scale factor. + * + * Gets additional output scaling factors. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_get_scale_factor (hb_vector_paint_t *paint, + float *x_scale_factor, + float *y_scale_factor) +{ + if (x_scale_factor) *x_scale_factor = paint->x_scale_factor; + if (y_scale_factor) *y_scale_factor = paint->y_scale_factor; +} + +/** + * hb_vector_paint_set_extents: + * @paint: a paint context. + * @extents: (nullable): output extents to set or expand. + * + * Sets or expands output extents on @paint. Passing `NULL` clears extents. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_set_extents (hb_vector_paint_t *paint, + const hb_vector_extents_t *extents) +{ + if (!extents) + { + paint->extents = {0, 0, 0, 0}; + paint->has_extents = false; + return; + } + + if (!(extents->width > 0.f && extents->height > 0.f)) + return; + + if (paint->has_extents) + { + float x0 = hb_min (paint->extents.x, extents->x); + float y0 = hb_min (paint->extents.y, extents->y); + float x1 = hb_max (paint->extents.x + paint->extents.width, + extents->x + extents->width); + float y1 = hb_max (paint->extents.y + paint->extents.height, + extents->y + extents->height); + paint->extents = {x0, y0, x1 - x0, y1 - y0}; + } + else + { + paint->extents = *extents; + paint->has_extents = true; + } +} + +/** + * hb_vector_paint_get_extents: + * @paint: a paint context. + * @extents: (out) (nullable): where to store current output extents. + * + * Gets current output extents from @paint. + * + * Return value: `true` if extents are set, `false` otherwise. + * + * Since: 13.0.0 + */ +hb_bool_t +hb_vector_paint_get_extents (hb_vector_paint_t *paint, + hb_vector_extents_t *extents) +{ + if (!paint->has_extents) + return false; + + if (extents) + *extents = paint->extents; + return true; +} + +/** + * hb_vector_paint_set_glyph_extents: + * @paint: a paint context. + * @glyph_extents: glyph extents in font units. + * + * Expands @paint extents using @glyph_extents under the current transform. + * + * Return value: `true` on success, `false` otherwise. + * + * Since: 13.0.0 + */ +hb_bool_t +hb_vector_paint_set_glyph_extents (hb_vector_paint_t *paint, + const hb_glyph_extents_t *glyph_extents) +{ + hb_bool_t has_extents = paint->has_extents; + hb_bool_t ret = hb_svg_set_glyph_extents_common (paint->transform, + paint->x_scale_factor, + paint->y_scale_factor, + glyph_extents, + &paint->extents, + &has_extents); + paint->has_extents = has_extents; + return ret; +} + +/** + * hb_vector_paint_set_foreground: + * @paint: a paint context. + * @foreground: foreground color used for COLR foreground paints. + * + * Sets fallback foreground color used by paint operations. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_set_foreground (hb_vector_paint_t *paint, + hb_color_t foreground) +{ + paint->foreground = foreground; +} + +/** + * hb_vector_paint_set_palette: + * @paint: a paint context. + * @palette: palette index for color glyph painting. + * + * Sets the color palette index used by paint operations. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_set_palette (hb_vector_paint_t *paint, + int palette) +{ + paint->palette = palette; +} + +/** + * hb_vector_paint_set_custom_palette_color: + * @paint: a paint context. + * @color_index: color index to override. + * @color: replacement color. + * + * Overrides one font palette color entry for subsequent paint operations. + * Overrides are keyed by @color_index and persist on @paint until cleared + * (or replaced for the same index). + * + * These overrides are consulted by paint operations that resolve CPAL + * entries, including SVG glyph content using `var(--colorN)`. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_set_custom_palette_color (hb_vector_paint_t *paint, + unsigned color_index, + hb_color_t color) +{ + paint->custom_palette_colors.set (color_index, color); +} + +/** + * hb_vector_paint_clear_custom_palette_colors: + * @paint: a paint context. + * + * Clears all custom palette color overrides previously set on @paint. + * + * After this call, palette lookups use the selected font palette without + * custom override entries. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_clear_custom_palette_colors (hb_vector_paint_t *paint) +{ + paint->custom_palette_colors.clear (); +} + +/** + * hb_vector_paint_get_funcs: + * + * Gets paint callbacks implemented by the vector paint backend. + * + * Return value: (transfer none): immutable #hb_paint_funcs_t singleton. + * + * Since: 13.0.0 + */ +hb_paint_funcs_t * +hb_vector_paint_get_funcs (void) +{ + return hb_vector_paint_funcs_get (); +} + +static hb_bool_t +hb_vector_paint_custom_palette_color (hb_paint_funcs_t *pfuncs HB_UNUSED, + void *paint_data, + unsigned color_index, + hb_color_t *color, + void *user_data HB_UNUSED) +{ + hb_vector_paint_t *paint = (hb_vector_paint_t *) paint_data; + if (!paint || !color) + return false; + + hb_color_t *value = nullptr; + if (!paint->custom_palette_colors.has (color_index, &value) || !value) + return false; + *color = *value; + return true; +} + +/** + * hb_vector_paint_glyph: + * @paint: a paint context. + * @font: font object. + * @glyph: glyph ID. + * @pen_x: glyph x origin before context transform. + * @pen_y: glyph y origin before context transform. + * @extents_mode: extents update mode. + * + * Paints one color glyph into @paint. + * + * Return value: `true` if glyph paint data was emitted, `false` otherwise. + * + * Since: 13.0.0 + */ +hb_bool_t +hb_vector_paint_glyph (hb_vector_paint_t *paint, + hb_font_t *font, + hb_codepoint_t glyph, + float pen_x, + float pen_y, + hb_vector_extents_mode_t extents_mode) +{ + float xx = paint->transform.xx; + float yx = paint->transform.yx; + float xy = paint->transform.xy; + float yy = paint->transform.yy; + float tx = paint->transform.x0 + xx * pen_x + xy * pen_y; + float ty = paint->transform.y0 + yx * pen_x + yy * pen_y; + + if (extents_mode == HB_VECTOR_EXTENTS_MODE_EXPAND) + { + hb_glyph_extents_t ge; + if (hb_font_get_glyph_extents (font, glyph, &ge)) + { + hb_bool_t has_extents = paint->has_extents; + hb_transform_t<> extents_transform = {xx, yx, -xy, -yy, tx, ty}; + hb_bool_t ret = hb_svg_set_glyph_extents_common (extents_transform, + paint->x_scale_factor, + paint->y_scale_factor, + &ge, + &paint->extents, + &has_extents); + paint->has_extents = has_extents; + (void) ret; + } + } + + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return false; + + bool can_cache = !paint->flat; + hb_svg_color_glyph_cache_key_t cache_key = hb_svg_color_glyph_cache_key (glyph, + (unsigned) paint->palette, + paint->foreground); + if (can_cache) + { + if (paint->defined_color_glyphs.has (cache_key)) + { + uint64_t entry = paint->defined_color_glyphs.get (cache_key); + unsigned def_id = hb_svg_cache_entry_def_id (entry); + bool image_like = hb_svg_cache_entry_image_like (entry); + auto &body = paint->current_body (); + hb_svg_append_str (&body, "precision, + paint->x_scale_factor, + paint->y_scale_factor, + tx, ty); + else + hb_svg_append_instance_transform (&body, paint->precision, + paint->x_scale_factor, + paint->y_scale_factor, + xx, yx, xy, yy, tx, ty); + hb_svg_append_str (&body, "\"/>\n"); + return true; + } + } + + bool has_svg_image = false; + if (can_cache) + { + if (unlikely (!paint->group_stack.push_or_fail (hb_vector_t {}))) + return false; + paint->current_color_glyph_has_svg_image = false; + } + + if (can_cache) + { + hb_codepoint_t old_gid = paint->current_svg_image_glyph; + hb_face_t *old_face = paint->current_face; + paint->current_svg_image_glyph = glyph; + paint->current_face = hb_font_get_face (font); + hb_bool_t ret = hb_font_paint_glyph_or_fail (font, glyph, + hb_vector_paint_get_funcs (), paint, + (unsigned) paint->palette, + paint->foreground); + paint->current_svg_image_glyph = old_gid; + paint->current_face = old_face; + if (unlikely (!ret)) + { + paint->group_stack.pop (); + return false; + } + + paint->captured_scratch = paint->group_stack.pop (); + has_svg_image = paint->current_color_glyph_has_svg_image; + if (unlikely (!paint->captured_scratch.length)) + return false; + + unsigned def_id = paint->color_glyph_counter++; + if (unlikely (!paint->defined_color_glyphs.set (cache_key, + hb_svg_pack_color_glyph_cache_entry (def_id, + has_svg_image)))) + return false; + + hb_svg_append_str (&paint->defs, "defs, def_id); + hb_svg_append_str (&paint->defs, "\">\n"); + hb_svg_append_len (&paint->defs, + paint->captured_scratch.arrayZ, + paint->captured_scratch.length); + hb_svg_append_str (&paint->defs, "\n"); + + auto &body = paint->current_body (); + hb_svg_append_str (&body, "precision, + paint->x_scale_factor, + paint->y_scale_factor, + tx, ty); + else + hb_svg_append_instance_transform (&body, paint->precision, + paint->x_scale_factor, + paint->y_scale_factor, + xx, yx, xy, yy, tx, ty); + hb_svg_append_str (&body, "\"/>\n"); + return true; + } + + auto &body = paint->current_body (); + hb_svg_append_str (&body, "precision, + paint->x_scale_factor, + paint->y_scale_factor, + xx, yx, xy, yy, tx, ty); + hb_svg_append_str (&body, "\">\n"); + hb_codepoint_t old_gid = paint->current_svg_image_glyph; + hb_face_t *old_face = paint->current_face; + paint->current_svg_image_glyph = glyph; + paint->current_face = hb_font_get_face (font); + hb_bool_t ret = hb_font_paint_glyph_or_fail (font, glyph, + hb_vector_paint_get_funcs (), paint, + (unsigned) paint->palette, + paint->foreground); + paint->current_svg_image_glyph = old_gid; + paint->current_face = old_face; + hb_svg_append_str (&body, "\n"); + return ret; +} + +/** + * hb_vector_svg_paint_set_flat: + * @paint: a paint context. + * @flat: whether to flatten paint output and disable glyph-group reuse. + * + * Enables or disables SVG paint flattening. + * + * Since: 13.0.0 + */ +void +hb_vector_svg_paint_set_flat (hb_vector_paint_t *paint, + hb_bool_t flat) +{ + paint->flat = !!flat; +} + +/** + * hb_vector_svg_paint_set_precision: + * @paint: a paint context. + * @precision: decimal precision. + * + * Sets numeric output precision for SVG paint output. + * + * Since: 13.0.0 + */ +void +hb_vector_svg_paint_set_precision (hb_vector_paint_t *paint, + unsigned precision) +{ + paint->precision = hb_min (precision, 12u); +} + +static void +hb_vector_paint_clear_render_state (hb_vector_paint_t *paint) +{ + paint->extents = {0, 0, 0, 0}; + paint->has_extents = false; + + paint->defs.clear (); + paint->path.clear (); + paint->group_stack.clear (); + paint->transform_group_open_mask = 0; + paint->transform_group_depth = 0; + paint->transform_group_overflow_depth = 0; + paint->clip_rect_counter = 0; + paint->gradient_counter = 0; + paint->color_glyph_counter = 0; + paint->current_color_glyph_has_svg_image = false; + paint->current_svg_image_glyph = HB_CODEPOINT_INVALID; + paint->current_face = nullptr; + paint->svg_image_counter = 0; + hb_set_clear (paint->defined_outlines); + hb_set_clear (paint->defined_clips); + paint->defined_color_glyphs.clear (); + paint->color_stops_scratch.clear (); + paint->subset_body_scratch.clear (); + paint->captured_scratch.clear (); +} + +/** + * hb_vector_paint_render: + * @paint: a paint context. + * + * Renders accumulated paint content to an SVG blob. + * + * Return value: (transfer full) (nullable): output blob, or `NULL` if rendering cannot proceed. + * + * Since: 13.0.0 + */ +hb_blob_t * +hb_vector_paint_render (hb_vector_paint_t *paint) +{ + if (paint->format != HB_VECTOR_FORMAT_SVG) + return nullptr; + if (!paint->has_extents) + return nullptr; + + if (unlikely (!hb_vector_paint_ensure_initialized (paint))) + return nullptr; + + hb_vector_t out; + hb_svg_recover_recycled_buffer (paint->recycled_blob, &out); + unsigned estimated = paint->defs.length + + paint->group_stack.arrayZ[0].length + + 320; + out.alloc (estimated); + hb_svg_append_str (&out, "extents.x, paint->precision); + hb_svg_append_c (&out, ' '); + hb_svg_append_num (&out, paint->extents.y, paint->precision); + hb_svg_append_c (&out, ' '); + hb_svg_append_num (&out, paint->extents.width, paint->precision); + hb_svg_append_c (&out, ' '); + hb_svg_append_num (&out, paint->extents.height, paint->precision); + hb_svg_append_str (&out, "\" width=\""); + hb_svg_append_num (&out, paint->extents.width, paint->precision); + hb_svg_append_str (&out, "\" height=\""); + hb_svg_append_num (&out, paint->extents.height, paint->precision); + hb_svg_append_str (&out, "\">\n"); + + if (paint->defs.length) + { + hb_svg_append_str (&out, "\n"); + hb_svg_append_len (&out, paint->defs.arrayZ, paint->defs.length); + hb_svg_append_str (&out, "\n"); + } + + paint->append_global_transform_prefix (&out); + hb_svg_append_len (&out, paint->group_stack.arrayZ[0].arrayZ, paint->group_stack.arrayZ[0].length); + paint->append_global_transform_suffix (&out); + + hb_svg_append_str (&out, "\n"); + + hb_blob_t *blob = hb_svg_blob_from_buffer (&paint->recycled_blob, &out); + + hb_vector_paint_clear_render_state (paint); + + return blob; +} + +/** + * hb_vector_paint_reset: + * @paint: a paint context. + * + * Resets @paint state and clears accumulated content. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_reset (hb_vector_paint_t *paint) +{ + paint->transform = {1, 0, 0, 1, 0, 0}; + paint->x_scale_factor = 1.f; + paint->y_scale_factor = 1.f; + paint->foreground = HB_COLOR (0, 0, 0, 255); + paint->palette = 0; + paint->precision = 2; + paint->flat = false; + hb_vector_paint_clear_render_state (paint); +} + +/** + * hb_vector_paint_recycle_blob: + * @paint: a paint context. + * @blob: (nullable): previously rendered blob to recycle. + * + * Provides a blob for internal buffer reuse by later render calls. + * + * Since: 13.0.0 + */ +void +hb_vector_paint_recycle_blob (hb_vector_paint_t *paint, + hb_blob_t *blob) +{ + hb_blob_destroy (paint->recycled_blob); + paint->recycled_blob = nullptr; + if (!blob || blob == hb_blob_get_empty ()) + return; + paint->recycled_blob = blob; +} diff --git a/thirdparty/harfbuzz/src/hb-vector-svg-path.cc b/thirdparty/harfbuzz/src/hb-vector-svg-path.cc new file mode 100644 index 0000000000..286bc31a66 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector-svg-path.cc @@ -0,0 +1,144 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-machinery.hh" +#include "hb-vector-svg-path.hh" +#include "hb-vector-svg-utils.hh" + +static void +hb_svg_path_move_to (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + float to_x, float to_y, + void *) +{ + auto *s = (hb_svg_path_sink_t *) draw_data; + hb_svg_append_c (s->path, 'M'); + hb_svg_append_num (s->path, to_x, s->precision); + hb_svg_append_c (s->path, ','); + hb_svg_append_num (s->path, to_y, s->precision); +} + +static void +hb_svg_path_line_to (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + float to_x, float to_y, + void *) +{ + auto *s = (hb_svg_path_sink_t *) draw_data; + hb_svg_append_c (s->path, 'L'); + hb_svg_append_num (s->path, to_x, s->precision); + hb_svg_append_c (s->path, ','); + hb_svg_append_num (s->path, to_y, s->precision); +} + +static void +hb_svg_path_quadratic_to (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + float cx, float cy, + float to_x, float to_y, + void *) +{ + auto *s = (hb_svg_path_sink_t *) draw_data; + hb_svg_append_c (s->path, 'Q'); + hb_svg_append_num (s->path, cx, s->precision); + hb_svg_append_c (s->path, ','); + hb_svg_append_num (s->path, cy, s->precision); + hb_svg_append_c (s->path, ' '); + hb_svg_append_num (s->path, to_x, s->precision); + hb_svg_append_c (s->path, ','); + hb_svg_append_num (s->path, to_y, s->precision); +} + +static void +hb_svg_path_cubic_to (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + float c1x, float c1y, + float c2x, float c2y, + float to_x, float to_y, + void *) +{ + auto *s = (hb_svg_path_sink_t *) draw_data; + hb_svg_append_c (s->path, 'C'); + hb_svg_append_num (s->path, c1x, s->precision); + hb_svg_append_c (s->path, ','); + hb_svg_append_num (s->path, c1y, s->precision); + hb_svg_append_c (s->path, ' '); + hb_svg_append_num (s->path, c2x, s->precision); + hb_svg_append_c (s->path, ','); + hb_svg_append_num (s->path, c2y, s->precision); + hb_svg_append_c (s->path, ' '); + hb_svg_append_num (s->path, to_x, s->precision); + hb_svg_append_c (s->path, ','); + hb_svg_append_num (s->path, to_y, s->precision); +} + +static void +hb_svg_path_close_path (hb_draw_funcs_t *, + void *draw_data, + hb_draw_state_t *, + void *) +{ + auto *s = (hb_svg_path_sink_t *) draw_data; + hb_svg_append_c (s->path, 'Z'); +} + +static inline void +free_static_svg_path_draw_funcs (); + +static struct hb_svg_path_draw_funcs_lazy_loader_t + : hb_draw_funcs_lazy_loader_t +{ + static hb_draw_funcs_t *create () + { + hb_draw_funcs_t *funcs = hb_draw_funcs_create (); + hb_draw_funcs_set_move_to_func (funcs, (hb_draw_move_to_func_t) hb_svg_path_move_to, nullptr, nullptr); + hb_draw_funcs_set_line_to_func (funcs, (hb_draw_line_to_func_t) hb_svg_path_line_to, nullptr, nullptr); + hb_draw_funcs_set_quadratic_to_func (funcs, (hb_draw_quadratic_to_func_t) hb_svg_path_quadratic_to, nullptr, nullptr); + hb_draw_funcs_set_cubic_to_func (funcs, (hb_draw_cubic_to_func_t) hb_svg_path_cubic_to, nullptr, nullptr); + hb_draw_funcs_set_close_path_func (funcs, (hb_draw_close_path_func_t) hb_svg_path_close_path, nullptr, nullptr); + hb_draw_funcs_make_immutable (funcs); + hb_atexit (free_static_svg_path_draw_funcs); + return funcs; + } +} static_svg_path_draw_funcs; + +static inline void +free_static_svg_path_draw_funcs () +{ + static_svg_path_draw_funcs.free_instance (); +} + +hb_draw_funcs_t * +hb_svg_path_draw_funcs_get (void) +{ + return static_svg_path_draw_funcs.get_unconst (); +} diff --git a/thirdparty/harfbuzz/src/hb-vector-svg-path.hh b/thirdparty/harfbuzz/src/hb-vector-svg-path.hh new file mode 100644 index 0000000000..7e3b25c198 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector-svg-path.hh @@ -0,0 +1,43 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_VECTOR_SVG_PATH_HH +#define HB_VECTOR_SVG_PATH_HH + +#include "hb.hh" +#include "hb-vector.hh" +#include "hb-draw.h" + +struct hb_svg_path_sink_t +{ + hb_vector_t *path; + unsigned precision; +}; + +HB_INTERNAL hb_draw_funcs_t * +hb_svg_path_draw_funcs_get (void); + +#endif /* HB_VECTOR_SVG_PATH_HH */ diff --git a/thirdparty/harfbuzz/src/hb-vector-svg-subset.cc b/thirdparty/harfbuzz/src/hb-vector-svg-subset.cc new file mode 100644 index 0000000000..b46f4047b1 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector-svg-subset.cc @@ -0,0 +1,454 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-face.hh" +#include "hb-vector-svg-subset.hh" +#ifndef HB_NO_SVG +#include "OT/Color/svg/svg.hh" +#include "hb-raster-svg-parse.hh" +#endif +#include "hb-vector-svg-utils.hh" +#include "hb-map.hh" +#include "hb-ot-color.h" + +#include +#include +#include + +#ifndef HB_NO_SVG +#include "hb-vector-svg.hh" + +static bool +hb_svg_append_with_prefix (hb_vector_t *out, + const char *s, + unsigned n, + const char *prefix, + unsigned prefix_len) +{ + unsigned i = 0; + while (i < n) + { + if (i + 4 <= n && !memcmp (s + i, "id=\"", 4)) + { + if (!hb_svg_append_len (out, s + i, 4)) return false; + i += 4; + if (!hb_svg_append_len (out, prefix, prefix_len)) return false; + while (i < n && s[i] != '"') + { + if (!hb_svg_append_c (out, s[i])) return false; + i++; + } + continue; + } + if (i + 4 <= n && !memcmp (s + i, "id='", 4)) + { + if (!hb_svg_append_len (out, s + i, 4)) return false; + i += 4; + if (!hb_svg_append_len (out, prefix, prefix_len)) return false; + while (i < n && s[i] != '\'') + { + if (!hb_svg_append_c (out, s[i])) return false; + i++; + } + continue; + } + if (i + 7 <= n && !memcmp (s + i, "href=\"#", 7)) + { + if (!hb_svg_append_len (out, s + i, 7)) return false; + i += 7; + if (!hb_svg_append_len (out, prefix, prefix_len)) return false; + while (i < n && s[i] != '"') + { + if (!hb_svg_append_c (out, s[i])) return false; + i++; + } + continue; + } + if (i + 7 <= n && !memcmp (s + i, "href='#", 7)) + { + if (!hb_svg_append_len (out, s + i, 7)) return false; + i += 7; + if (!hb_svg_append_len (out, prefix, prefix_len)) return false; + while (i < n && s[i] != '\'') + { + if (!hb_svg_append_c (out, s[i])) return false; + i++; + } + continue; + } + if (i + 13 <= n && !memcmp (s + i, "xlink:href=\"#", 13)) + { + if (!hb_svg_append_len (out, s + i, 13)) return false; + i += 13; + if (!hb_svg_append_len (out, prefix, prefix_len)) return false; + while (i < n && s[i] != '"') + { + if (!hb_svg_append_c (out, s[i])) return false; + i++; + } + continue; + } + if (i + 13 <= n && !memcmp (s + i, "xlink:href='#", 13)) + { + if (!hb_svg_append_len (out, s + i, 13)) return false; + i += 13; + if (!hb_svg_append_len (out, prefix, prefix_len)) return false; + while (i < n && s[i] != '\'') + { + if (!hb_svg_append_c (out, s[i])) return false; + i++; + } + continue; + } + if (i + 5 <= n && !memcmp (s + i, "url(#", 5)) + { + if (!hb_svg_append_len (out, s + i, 5)) return false; + i += 5; + if (!hb_svg_append_len (out, prefix, prefix_len)) return false; + while (i < n && s[i] != ')') + { + if (!hb_svg_append_c (out, s[i])) return false; + i++; + } + continue; + } + if (i + 6 <= n && !memcmp (s + i, "url(\"#", 6)) + { + if (!hb_svg_append_len (out, s + i, 6)) return false; + i += 6; + if (!hb_svg_append_len (out, prefix, prefix_len)) return false; + while (i < n && s[i] != '"') + { + if (!hb_svg_append_c (out, s[i])) return false; + i++; + } + continue; + } + if (i + 6 <= n && !memcmp (s + i, "url('#", 6)) + { + if (!hb_svg_append_len (out, s + i, 6)) return false; + i += 6; + if (!hb_svg_append_len (out, prefix, prefix_len)) return false; + while (i < n && s[i] != '\'') + { + if (!hb_svg_append_c (out, s[i])) return false; + i++; + } + continue; + } + if (!hb_svg_append_c (out, s[i])) + return false; + i++; + } + return true; +} + +static bool +hb_svg_add_unique_id (hb_vector_t *v, + hb_hashmap_t *seen_ids, + const char *p, + unsigned n) +{ + if (!n) return true; + OT::SVG::svg_id_span_t key = {p, n}; + if (seen_ids->has (key)) + return true; + if (unlikely (!seen_ids->set (key, true))) + return false; + auto *slot = v->push (); + if (unlikely (v->in_error ())) + return false; + *slot = key; + return true; +} + +static bool +hb_svg_collect_refs (const char *s, + unsigned n, + hb_vector_t *ids, + hb_hashmap_t *seen_ids) +{ + unsigned i = 0; + while (i < n) + { + if (i + 7 <= n && !memcmp (s + i, "href=\"#", 7)) + { + i += 7; + unsigned b = i; + while (i < n && s[i] != '"' && s[i] != '\'' && s[i] != ' ' && s[i] != '>') i++; + if (i > b && unlikely (!hb_svg_add_unique_id (ids, seen_ids, s + b, i - b))) return false; + continue; + } + if (i + 7 <= n && !memcmp (s + i, "href='#", 7)) + { + i += 7; + unsigned b = i; + while (i < n && s[i] != '\'' && s[i] != '"' && s[i] != ' ' && s[i] != '>') i++; + if (i > b && unlikely (!hb_svg_add_unique_id (ids, seen_ids, s + b, i - b))) return false; + continue; + } + if (i + 13 <= n && !memcmp (s + i, "xlink:href=\"#", 13)) + { + i += 13; + unsigned b = i; + while (i < n && s[i] != '"' && s[i] != '\'' && s[i] != ' ' && s[i] != '>') i++; + if (i > b && unlikely (!hb_svg_add_unique_id (ids, seen_ids, s + b, i - b))) return false; + continue; + } + if (i + 13 <= n && !memcmp (s + i, "xlink:href='#", 13)) + { + i += 13; + unsigned b = i; + while (i < n && s[i] != '\'' && s[i] != '"' && s[i] != ' ' && s[i] != '>') i++; + if (i > b && unlikely (!hb_svg_add_unique_id (ids, seen_ids, s + b, i - b))) return false; + continue; + } + if (i + 5 <= n && !memcmp (s + i, "url(#", 5)) + { + i += 5; + unsigned b = i; + while (i < n && s[i] != ')') i++; + if (i > b && unlikely (!hb_svg_add_unique_id (ids, seen_ids, s + b, i - b))) return false; + continue; + } + if (i + 6 <= n && !memcmp (s + i, "url(\"#", 6)) + { + i += 6; + unsigned b = i; + while (i < n && s[i] != '"') i++; + if (i > b && unlikely (!hb_svg_add_unique_id (ids, seen_ids, s + b, i - b))) return false; + continue; + } + if (i + 6 <= n && !memcmp (s + i, "url('#", 6)) + { + i += 6; + unsigned b = i; + while (i < n && s[i] != '\'') i++; + if (i > b && unlikely (!hb_svg_add_unique_id (ids, seen_ids, s + b, i - b))) return false; + continue; + } + i++; + } + return true; +} + +static bool +hb_svg_find_root_open_tag (const char *svg, + unsigned len, + unsigned *root_open_len, + bool *missing_viewport) +{ + hb_svg_xml_parser_t parser (svg, len); + hb_svg_token_type_t tok = parser.next (); + if (!((tok == SVG_TOKEN_OPEN_TAG || tok == SVG_TOKEN_SELF_CLOSE_TAG) && + parser.tag_name.eq ("svg"))) + return false; + + if (missing_viewport) + { + *missing_viewport = + parser.find_attr ("width").is_null () || + parser.find_attr ("height").is_null (); + } + + if (root_open_len) + *root_open_len = (unsigned) (parser.p - parser.tag_start); + return true; +} + +bool +hb_svg_subset_glyph_image (hb_face_t *face, + hb_blob_t *image, + hb_codepoint_t glyph, + unsigned *image_counter, + hb_vector_t *defs_dst, + hb_vector_t *body_dst) +{ + bool ret = false; + hb_blob_t *normalized_image = nullptr; + unsigned len = 0; + const char *svg = nullptr; + unsigned doc_index = 0; + hb_codepoint_t start_glyph = HB_CODEPOINT_INVALID; + hb_codepoint_t end_glyph = HB_CODEPOINT_INVALID; + const OT::SVG::svg_doc_cache_t *doc_cache = nullptr; + unsigned glyph_start = 0, glyph_end = 0; + const hb_vector_t *defs_entries = nullptr; + hb_vector_t needed_ids; + hb_hashmap_t needed_ids_set; + hb_vector_t chosen_defs; + hb_vector_t chosen_def_marks; + unsigned root_open_len = 0; + bool glyph_is_root_svg = false; + bool root_missing_viewport = false; + char prefix[32]; + int prefix_len = 0; + + if (glyph == HB_CODEPOINT_INVALID || !image_counter || !defs_dst || !body_dst) + goto done; + + normalized_image = OT::hb_ot_svg_reference_normalized_blob (image, &svg, &len); + if (!normalized_image || !svg || !len) + goto done; + + if (!hb_ot_color_glyph_get_svg_document_index (face, glyph, &doc_index)) + goto done; + + if (!hb_ot_color_get_svg_document_glyph_range (face, doc_index, &start_glyph, &end_glyph)) + goto done; + + doc_cache = face->table.SVG->get_or_create_doc_cache (normalized_image, svg, len, + doc_index, start_glyph, end_glyph); + if (!doc_cache) + goto done; + svg = face->table.SVG->doc_cache_get_svg (doc_cache, &len); + + if (!face->table.SVG->doc_cache_get_glyph_span (doc_cache, glyph, &glyph_start, &glyph_end)) + goto done; + + defs_entries = face->table.SVG->doc_cache_get_defs_entries (doc_cache); + if (!hb_svg_find_root_open_tag (svg, len, &root_open_len, &root_missing_viewport)) + goto done; + glyph_is_root_svg = glyph_start == 0; + + needed_ids.alloc (16); + if (!hb_svg_collect_refs (svg + glyph_start, glyph_end - glyph_start, + &needed_ids, &needed_ids_set)) + goto done; + + chosen_defs.alloc (16); + if (unlikely (!chosen_def_marks.resize ((int) defs_entries->length))) + goto done; + hb_memset (chosen_def_marks.arrayZ, 0, defs_entries->length); + for (unsigned qi = 0; qi < needed_ids.length; qi++) + { + const auto &need = needed_ids.arrayZ[qi]; + for (unsigned i = 0; i < defs_entries->length; i++) + { + const auto &e = defs_entries->arrayZ[i]; + if (e.id.len == need.len && !memcmp (e.id.p, need.p, need.len)) + { + if (!chosen_def_marks.arrayZ[i]) + { + chosen_def_marks.arrayZ[i] = 1; + if (unlikely (!chosen_defs.push_or_fail (i))) + goto done; + if (!hb_svg_collect_refs (svg + e.start, e.end - e.start, + &needed_ids, &needed_ids_set)) + goto done; + } + break; + } + } + } + + prefix_len = snprintf (prefix, sizeof (prefix), "hbimg%u_", (*image_counter)++); + if (prefix_len <= 0 || (unsigned) prefix_len >= sizeof (prefix)) + goto done; + + body_dst->alloc (body_dst->length + (glyph_end - glyph_start) + 64); + + for (unsigned i = 0; i < chosen_defs.length; i++) + { + const auto &e = defs_entries->arrayZ[chosen_defs.arrayZ[i]]; + if (!hb_svg_append_with_prefix (defs_dst, svg + e.start, e.end - e.start, prefix, (unsigned) prefix_len)) + goto done; + if (!hb_svg_append_c (defs_dst, '\n')) + goto done; + } + + if (glyph_is_root_svg && root_missing_viewport && root_open_len > 1) + { + unsigned upem = face->get_upem (); + if (!hb_svg_append_with_prefix (body_dst, + svg + glyph_start, + root_open_len - 1, + prefix, + (unsigned) prefix_len)) + goto done; + if (!hb_svg_append_str (body_dst, " width=\"")) + goto done; + if (!hb_svg_append_unsigned (body_dst, upem)) + goto done; + if (!hb_svg_append_str (body_dst, "\" height=\"")) + goto done; + if (!hb_svg_append_unsigned (body_dst, upem)) + goto done; + if (!hb_svg_append_str (body_dst, "\" overflow=\"visible")) + goto done; + if (!hb_svg_append_c (body_dst, '"')) + goto done; + if (!hb_svg_append_c (body_dst, '>')) + goto done; + ret = hb_svg_append_with_prefix (body_dst, + svg + glyph_start + root_open_len, + glyph_end - glyph_start - root_open_len, + prefix, + (unsigned) prefix_len); + } + else if (glyph_is_root_svg && root_open_len > 1) + { + if (!hb_svg_append_with_prefix (body_dst, + svg + glyph_start, + root_open_len - 1, + prefix, + (unsigned) prefix_len)) + goto done; + if (!hb_svg_append_str (body_dst, " overflow=\"visible\"")) + goto done; + if (!hb_svg_append_c (body_dst, '>')) + goto done; + ret = hb_svg_append_with_prefix (body_dst, + svg + glyph_start + root_open_len, + glyph_end - glyph_start - root_open_len, + prefix, + (unsigned) prefix_len); + } + else + ret = hb_svg_append_with_prefix (body_dst, + svg + glyph_start, + glyph_end - glyph_start, + prefix, + (unsigned) prefix_len); + +done: + hb_blob_destroy (normalized_image); + return ret; +} +#else +bool +hb_svg_subset_glyph_image (hb_face_t *face HB_UNUSED, + hb_blob_t *image HB_UNUSED, + hb_codepoint_t glyph HB_UNUSED, + unsigned *image_counter HB_UNUSED, + hb_vector_t *defs_dst HB_UNUSED, + hb_vector_t *body_dst HB_UNUSED) +{ + return false; +} +#endif diff --git a/thirdparty/harfbuzz/src/hb-vector-svg-subset.hh b/thirdparty/harfbuzz/src/hb-vector-svg-subset.hh new file mode 100644 index 0000000000..32cb66c8d1 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector-svg-subset.hh @@ -0,0 +1,44 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_VECTOR_SVG_SUBSET_HH +#define HB_VECTOR_SVG_SUBSET_HH + +#include "hb.hh" +#include "hb-vector.hh" +#include "hb-blob.hh" +#include "hb-face.h" + +HB_INTERNAL bool +hb_svg_subset_glyph_image (hb_face_t *face, + hb_blob_t *image, + hb_codepoint_t glyph, + unsigned *image_counter, + hb_vector_t *defs_dst, + hb_vector_t *body_dst); + + +#endif /* HB_VECTOR_SVG_SUBSET_HH */ diff --git a/thirdparty/harfbuzz/src/hb-vector-svg-utils.cc b/thirdparty/harfbuzz/src/hb-vector-svg-utils.cc new file mode 100644 index 0000000000..766b98e7f9 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector-svg-utils.cc @@ -0,0 +1,92 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-machinery.hh" +#include "hb-vector-svg-utils.hh" + +#include + +struct hb_svg_decimal_point_t +{ + char value[8]; +}; + +static hb_svg_decimal_point_t hb_svg_decimal_point_default = {{'.', '\0'}}; + +static inline void free_static_svg_decimal_point (); + +static struct hb_svg_decimal_point_lazy_loader_t + : hb_lazy_loader_t +{ + static hb_svg_decimal_point_t *create () + { + auto *p = (hb_svg_decimal_point_t *) hb_calloc (1, sizeof (hb_svg_decimal_point_t)); + if (!p) + return nullptr; + + p->value[0] = '.'; + p->value[1] = '\0'; + +#ifndef HB_NO_SETLOCALE + lconv *lc = nullptr; +#ifdef HAVE_LOCALECONV_L + hb_locale_t current_locale = hb_uselocale ((hb_locale_t) 0); + if (current_locale) + lc = localeconv_l (current_locale); +#endif + if (!lc) + lc = localeconv (); + if (lc && lc->decimal_point && lc->decimal_point[0]) + { + strncpy (p->value, lc->decimal_point, sizeof (p->value) - 1); + p->value[sizeof (p->value) - 1] = '\0'; + } +#endif + + hb_atexit (free_static_svg_decimal_point); + return p; + } + + static void destroy (hb_svg_decimal_point_t *p) + { hb_free (p); } + + static const hb_svg_decimal_point_t *get_null () + { return &hb_svg_decimal_point_default; } +} static_svg_decimal_point; + +static inline void +free_static_svg_decimal_point () +{ + static_svg_decimal_point.free_instance (); +} + +const char * +hb_svg_decimal_point_get (void) +{ + return static_svg_decimal_point.get_unconst ()->value; +} diff --git a/thirdparty/harfbuzz/src/hb-vector-svg-utils.hh b/thirdparty/harfbuzz/src/hb-vector-svg-utils.hh new file mode 100644 index 0000000000..138b51b66d --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector-svg-utils.hh @@ -0,0 +1,129 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_VECTOR_SVG_UTILS_HH +#define HB_VECTOR_SVG_UTILS_HH + +#include "hb.hh" +#include "hb-vector.hh" +#include +#include +#include + +HB_INTERNAL const char * +hb_svg_decimal_point_get (void); + +static inline bool +hb_svg_append_len (hb_vector_t *buf, + const char *s, + unsigned len) +{ + unsigned old_len = buf->length; + if (unlikely (!buf->resize_dirty ((int) (old_len + len)))) + return false; + hb_memcpy (buf->arrayZ + old_len, s, len); + return true; +} + +static inline bool +hb_svg_append_c (hb_vector_t *buf, char c) +{ + return buf->push_or_fail (c); +} + +static inline void +hb_svg_append_num (hb_vector_t *buf, + float v, + unsigned precision, + bool keep_nonzero = false) +{ + unsigned effective_precision = precision; + if (effective_precision > 12) + effective_precision = 12; + if (keep_nonzero && v != 0.f) + while (effective_precision < 12) + { + float rounded_zero_threshold = 0.5f; + for (unsigned i = 0; i < effective_precision; i++) + rounded_zero_threshold *= 0.1f; + if (fabsf (v) >= rounded_zero_threshold) + break; + effective_precision++; + } + + float rounded_zero_threshold = 0.5f; + for (unsigned i = 0; i < effective_precision; i++) + rounded_zero_threshold *= 0.1f; + if (fabsf (v) < rounded_zero_threshold) + v = 0.f; + + if (!(v == v) || !std::isfinite (v)) + { + hb_svg_append_c (buf, '0'); + return; + } + + static const char float_formats[13][6] = { + "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f", + "%.7f", "%.8f", "%.9f", "%.10f", "%.11f", "%.12f", + }; + char out[128]; + snprintf (out, sizeof (out), float_formats[effective_precision], (double) v); + + const char *decimal_point = hb_svg_decimal_point_get (); + + if (decimal_point[0] != '.' || decimal_point[1] != '\0') + { + char *p = strstr (out, decimal_point); + if (p) + { + unsigned dp_len = (unsigned) strlen (decimal_point); + unsigned tail_len = (unsigned) strlen (p + dp_len); + memmove (p + 1, p + dp_len, tail_len + 1); + *p = '.'; + } + } + + char *dot = strchr (out, '.'); + if (dot) + { + char *end = out + strlen (out) - 1; + while (end > dot && *end == '0') + *end-- = '\0'; + if (end == dot) + *end = '\0'; + } + + hb_svg_append_len (buf, out, (unsigned) strlen (out)); +} + +static inline unsigned +hb_svg_scale_precision (unsigned precision) +{ + return precision < 7 ? 7 : precision; +} + +#endif /* HB_VECTOR_SVG_UTILS_HH */ diff --git a/thirdparty/harfbuzz/src/hb-vector-svg.hh b/thirdparty/harfbuzz/src/hb-vector-svg.hh new file mode 100644 index 0000000000..418d5bc46c --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector-svg.hh @@ -0,0 +1,366 @@ +#ifndef HB_VECTOR_SVG_HH +#define HB_VECTOR_SVG_HH + +#include "hb-vector.h" + +static inline bool +hb_svg_append_str (hb_vector_t *buf, const char *s) +{ + return hb_svg_append_len (buf, s, (unsigned) strlen (s)); +} + +static inline bool +hb_svg_append_unsigned (hb_vector_t *buf, unsigned v) +{ + char tmp[10]; + unsigned n = 0; + do { + tmp[n++] = (char) ('0' + (v % 10)); + v /= 10; + } while (v); + + unsigned old_len = buf->length; + if (unlikely (!buf->resize_dirty ((int) (old_len + n)))) + return false; + + for (unsigned i = 0; i < n; i++) + buf->arrayZ[old_len + i] = tmp[n - 1 - i]; + return true; +} + +static inline bool +hb_svg_append_hex_byte (hb_vector_t *buf, unsigned v) +{ + static const char hex[] = "0123456789ABCDEF"; + char tmp[2] = {hex[(v >> 4) & 15], hex[v & 15]}; + return hb_svg_append_len (buf, tmp, 2); +} + +static inline bool +hb_svg_append_base64 (hb_vector_t *buf, + const uint8_t *data, + unsigned len) +{ + static const char b64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + unsigned out_len = ((len + 2) / 3) * 4; + unsigned old_len = buf->length; + if (unlikely (!buf->resize_dirty ((int) (old_len + out_len)))) + return false; + + char *dst = buf->arrayZ + old_len; + unsigned di = 0; + unsigned i = 0; + while (i + 2 < len) + { + unsigned v = ((unsigned) data[i] << 16) | + ((unsigned) data[i + 1] << 8) | + ((unsigned) data[i + 2]); + dst[di++] = b64[(v >> 18) & 63]; + dst[di++] = b64[(v >> 12) & 63]; + dst[di++] = b64[(v >> 6) & 63]; + dst[di++] = b64[v & 63]; + i += 3; + } + + if (i < len) + { + unsigned v = (unsigned) data[i] << 16; + if (i + 1 < len) + v |= (unsigned) data[i + 1] << 8; + dst[di++] = b64[(v >> 18) & 63]; + dst[di++] = b64[(v >> 12) & 63]; + dst[di++] = (i + 1 < len) ? b64[(v >> 6) & 63] : '='; + dst[di++] = '='; + } + + return true; +} + +struct hb_svg_blob_meta_t +{ + char *data; + int allocated; + bool transferred; + bool in_replace; +}; + +static hb_user_data_key_t hb_svg_blob_meta_user_data_key; + +static inline void +hb_svg_blob_meta_set_buffer (hb_svg_blob_meta_t *meta, + char *data, + int allocated) +{ + meta->data = data; + meta->allocated = allocated; + meta->transferred = false; +} + +static inline void +hb_svg_blob_meta_release_buffer (hb_svg_blob_meta_t *meta) +{ + if (!meta) + return; + if (!meta->transferred && meta->data) + hb_free (meta->data); + meta->data = nullptr; + meta->allocated = 0; + meta->transferred = true; +} + +static inline void +hb_svg_blob_meta_destroy (void *data) +{ + auto *meta = (hb_svg_blob_meta_t *) data; + hb_svg_blob_meta_release_buffer (meta); + if (meta->in_replace) + { + meta->in_replace = false; + return; + } + hb_free (meta); +} + +static inline hb_blob_t * +hb_svg_blob_from_buffer (hb_blob_t **recycled_blob, + hb_vector_t *buf) +{ + unsigned len = 0; + int allocated = 0; + char *data = buf->steal (&len, &allocated); + if (!data) + return nullptr; + + hb_blob_t *blob = nullptr; + if (*recycled_blob) + blob = *recycled_blob; + bool reused_blob = blob && blob != hb_blob_get_empty (); + bool new_meta = false; + auto *meta = reused_blob + ? (hb_svg_blob_meta_t *) hb_blob_get_user_data (blob, &hb_svg_blob_meta_user_data_key) + : nullptr; + if (!meta) + { + meta = (hb_svg_blob_meta_t *) hb_malloc (sizeof (hb_svg_blob_meta_t)); + if (!meta) + { + hb_free (data); + return nullptr; + } + meta->data = nullptr; + meta->allocated = 0; + meta->transferred = true; + meta->in_replace = false; + new_meta = true; + } + + if (reused_blob) + { + meta->in_replace = true; + blob->replace_buffer (data, len, HB_MEMORY_MODE_WRITABLE, meta, hb_svg_blob_meta_destroy); + hb_svg_blob_meta_set_buffer (meta, data, allocated); + } + else + { + hb_svg_blob_meta_set_buffer (meta, data, allocated); + blob = hb_blob_create_or_fail (data, len, HB_MEMORY_MODE_WRITABLE, meta, hb_svg_blob_meta_destroy); + if (unlikely (!blob)) + return nullptr; + } + + if (unlikely (blob == hb_blob_get_empty ())) + { + if (new_meta) + hb_free (meta); + hb_free (data); + return nullptr; + } + + if (new_meta && + !hb_blob_set_user_data (blob, + &hb_svg_blob_meta_user_data_key, + meta, + nullptr, + true)) + { + if (!reused_blob) + hb_blob_destroy (blob); + return nullptr; + } + + if (*recycled_blob) + *recycled_blob = nullptr; + + return blob; +} + +static inline void +hb_svg_recover_recycled_buffer (hb_blob_t *blob, + hb_vector_t *buf) +{ + if (!blob) + return; + + auto *meta = (hb_svg_blob_meta_t *) hb_blob_get_user_data (blob, &hb_svg_blob_meta_user_data_key); + if (!meta || meta->transferred || !meta->data) + return; + + buf->recycle_buffer (meta->data, 0, meta->allocated); + meta->data = nullptr; + meta->allocated = 0; + meta->transferred = true; +} + +static inline void +hb_svg_append_color (hb_vector_t *buf, + hb_color_t color, + bool with_alpha) +{ + static const char hex[] = "0123456789ABCDEF"; + unsigned r = hb_color_get_red (color); + unsigned g = hb_color_get_green (color); + unsigned b = hb_color_get_blue (color); + unsigned a = hb_color_get_alpha (color); + hb_svg_append_c (buf, '#'); + if (((r >> 4) == (r & 0xF)) && + ((g >> 4) == (g & 0xF)) && + ((b >> 4) == (b & 0xF))) + { + hb_svg_append_c (buf, hex[r & 0xF]); + hb_svg_append_c (buf, hex[g & 0xF]); + hb_svg_append_c (buf, hex[b & 0xF]); + } + else + { + hb_svg_append_hex_byte (buf, r); + hb_svg_append_hex_byte (buf, g); + hb_svg_append_hex_byte (buf, b); + } + if (with_alpha && a != 255) + { + hb_svg_append_str (buf, "\" fill-opacity=\""); + hb_svg_append_num (buf, a / 255.f, 4); + } +} + +static inline void +hb_svg_transform_point (const hb_transform_t<> &t, + float x_scale_factor, + float y_scale_factor, + float x, float y, + float *tx, float *ty) +{ + float xx = x, yy = y; + t.transform_point (xx, yy); + *tx = xx / (x_scale_factor > 0 ? x_scale_factor : 1.f); + *ty = yy / (y_scale_factor > 0 ? y_scale_factor : 1.f); +} + +static inline hb_bool_t +hb_svg_set_glyph_extents_common (const hb_transform_t<> &transform, + float x_scale_factor, + float y_scale_factor, + const hb_glyph_extents_t *glyph_extents, + hb_vector_extents_t *extents, + hb_bool_t *has_extents) +{ + float x0 = (float) glyph_extents->x_bearing; + float y0 = (float) glyph_extents->y_bearing; + float x1 = x0 + glyph_extents->width; + float y1 = y0 + glyph_extents->height; + + float px[4] = {x0, x0, x1, x1}; + float py[4] = {y0, y1, y0, y1}; + + float tx, ty; + hb_svg_transform_point (transform, x_scale_factor, y_scale_factor, px[0], py[0], &tx, &ty); + float tx_min = tx, tx_max = tx; + float ty_min = ty, ty_max = ty; + + for (unsigned i = 1; i < 4; i++) + { + hb_svg_transform_point (transform, x_scale_factor, y_scale_factor, px[i], py[i], &tx, &ty); + tx_min = hb_min (tx_min, tx); + tx_max = hb_max (tx_max, tx); + ty_min = hb_min (ty_min, ty); + ty_max = hb_max (ty_max, ty); + } + + if (tx_max <= tx_min || ty_max <= ty_min) + return false; + + if (*has_extents) + { + float x0 = hb_min (extents->x, tx_min); + float y0 = hb_min (extents->y, ty_min); + float x1 = hb_max (extents->x + extents->width, tx_max); + float y1 = hb_max (extents->y + extents->height, ty_max); + *extents = {x0, y0, x1 - x0, y1 - y0}; + } + else + { + *extents = {tx_min, ty_min, tx_max - tx_min, ty_max - ty_min}; + *has_extents = true; + } + return true; +} + +static inline void +hb_svg_append_instance_transform (hb_vector_t *out, + unsigned precision, + float x_scale_factor, + float y_scale_factor, + float xx, float yx, + float xy, float yy, + float tx, float ty) +{ + unsigned sprec = hb_svg_scale_precision (precision); + if (xx == 1.f && yx == 0.f && xy == 0.f && yy == 1.f) + { + float sx = 1.f / x_scale_factor; + float sy = 1.f / y_scale_factor; + hb_svg_append_str (out, "translate("); + hb_svg_append_num (out, tx / x_scale_factor, precision); + hb_svg_append_c (out, ','); + hb_svg_append_num (out, -ty / y_scale_factor, precision); + hb_svg_append_str (out, ") scale("); + hb_svg_append_num (out, sx, sprec, true); + hb_svg_append_c (out, ','); + hb_svg_append_num (out, -sy, sprec, true); + hb_svg_append_c (out, ')'); + } + else + { + hb_svg_append_str (out, "matrix("); + hb_svg_append_num (out, xx / x_scale_factor, sprec, true); + hb_svg_append_c (out, ','); + hb_svg_append_num (out, yx / y_scale_factor, sprec, true); + hb_svg_append_c (out, ','); + hb_svg_append_num (out, -xy / x_scale_factor, sprec, true); + hb_svg_append_c (out, ','); + hb_svg_append_num (out, -yy / y_scale_factor, sprec, true); + hb_svg_append_c (out, ','); + hb_svg_append_num (out, tx / x_scale_factor, precision); + hb_svg_append_c (out, ','); + hb_svg_append_num (out, -ty / y_scale_factor, precision); + hb_svg_append_c (out, ')'); + } +} + +static inline void +hb_svg_append_image_instance_translate (hb_vector_t *out, + unsigned precision, + float x_scale_factor, + float y_scale_factor, + float tx, float ty) +{ + hb_svg_append_str (out, "translate("); + hb_svg_append_num (out, tx / x_scale_factor, precision); + hb_svg_append_c (out, ','); + hb_svg_append_num (out, -ty / y_scale_factor, precision); + hb_svg_append_c (out, ')'); +} + +#endif /* HB_VECTOR_SVG_HH */ diff --git a/thirdparty/harfbuzz/src/hb-vector.cc b/thirdparty/harfbuzz/src/hb-vector.cc new file mode 100644 index 0000000000..2b42225350 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector.cc @@ -0,0 +1,41 @@ +/* + * Copyright © 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#include "hb.hh" + +#include "hb-vector.h" + +/** + * SECTION:hb-vector + * @title: hb-vector + * @short_description: Glyph vector conversion + * @include: hb-vector.h + * + * Functions for converting glyph draw and paint callbacks to vector output. + * + * The initial backend is SVG. Extents must be set before calling render + * functions; otherwise render returns `NULL`. + **/ diff --git a/thirdparty/harfbuzz/src/hb-vector.h b/thirdparty/harfbuzz/src/hb-vector.h new file mode 100644 index 0000000000..dd32f2ec8b --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-vector.h @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2026 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Author(s): Behdad Esfahbod + */ + +#ifndef HB_VECTOR_H +#define HB_VECTOR_H + +#include "hb.h" + +HB_BEGIN_DECLS + +/** + * hb_vector_format_t: + * @HB_VECTOR_FORMAT_INVALID: Invalid format. + * @HB_VECTOR_FORMAT_SVG: SVG output. + * + * Output format for vector conversion. + * + * Since: 13.0.0 + */ +typedef enum { + HB_VECTOR_FORMAT_INVALID = HB_TAG_NONE, + HB_VECTOR_FORMAT_SVG = HB_TAG ('s','v','g',' '), +} hb_vector_format_t; + +/** + * hb_vector_extents_t: + * @x: Left edge of the output coordinate system. + * @y: Top edge of the output coordinate system. + * @width: Width of the output coordinate system. + * @height: Height of the output coordinate system. + * + * Vector output extents, mapped to SVG viewBox. + * + * Since: 13.0.0 + */ +typedef struct hb_vector_extents_t { + float x, y; + float width, height; +} hb_vector_extents_t; + +/** + * hb_vector_extents_mode_t: + * @HB_VECTOR_EXTENTS_MODE_NONE: Do not update extents. + * @HB_VECTOR_EXTENTS_MODE_EXPAND: Union glyph ink extents into current extents. + * + * Controls whether convenience glyph APIs update context extents. + * + * Since: 13.0.0 + */ +typedef enum { + HB_VECTOR_EXTENTS_MODE_NONE = 0, + HB_VECTOR_EXTENTS_MODE_EXPAND = 1, +} hb_vector_extents_mode_t; + +/** + * hb_vector_draw_t: + * + * Opaque draw context for vector outline conversion. + * + * Since: 13.0.0 + */ +typedef struct hb_vector_draw_t hb_vector_draw_t; + +/** + * hb_vector_paint_t: + * + * Opaque paint context for vector color-glyph conversion. + * + * Since: 13.0.0 + */ +typedef struct hb_vector_paint_t hb_vector_paint_t; + +/* hb_vector_draw_t */ + +HB_EXTERN hb_vector_draw_t * +hb_vector_draw_create_or_fail (hb_vector_format_t format); + +HB_EXTERN hb_vector_draw_t * +hb_vector_draw_reference (hb_vector_draw_t *draw); + +HB_EXTERN void +hb_vector_draw_destroy (hb_vector_draw_t *draw); + +HB_EXTERN hb_bool_t +hb_vector_draw_set_user_data (hb_vector_draw_t *draw, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_vector_draw_get_user_data (hb_vector_draw_t *draw, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_vector_draw_set_transform (hb_vector_draw_t *draw, + float xx, float yx, + float xy, float yy, + float dx, float dy); + +HB_EXTERN void +hb_vector_draw_get_transform (hb_vector_draw_t *draw, + float *xx, float *yx, + float *xy, float *yy, + float *dx, float *dy); + +HB_EXTERN void +hb_vector_draw_set_scale_factor (hb_vector_draw_t *draw, + float x_scale_factor, + float y_scale_factor); + +HB_EXTERN void +hb_vector_draw_get_scale_factor (hb_vector_draw_t *draw, + float *x_scale_factor, + float *y_scale_factor); + +HB_EXTERN void +hb_vector_draw_set_extents (hb_vector_draw_t *draw, + const hb_vector_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_vector_draw_get_extents (hb_vector_draw_t *draw, + hb_vector_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_vector_draw_set_glyph_extents (hb_vector_draw_t *draw, + const hb_glyph_extents_t *glyph_extents); + +HB_EXTERN hb_draw_funcs_t * +hb_vector_draw_get_funcs (void); + +HB_EXTERN hb_bool_t +hb_vector_draw_glyph (hb_vector_draw_t *draw, + hb_font_t *font, + hb_codepoint_t glyph, + float pen_x, + float pen_y, + hb_vector_extents_mode_t extents_mode); + +HB_EXTERN void +hb_vector_svg_set_flat (hb_vector_draw_t *draw, + hb_bool_t flat); + +HB_EXTERN void +hb_vector_svg_set_precision (hb_vector_draw_t *draw, + unsigned precision); + +HB_EXTERN hb_blob_t * +hb_vector_draw_render (hb_vector_draw_t *draw); + +HB_EXTERN void +hb_vector_draw_reset (hb_vector_draw_t *draw); + +HB_EXTERN void +hb_vector_draw_recycle_blob (hb_vector_draw_t *draw, + hb_blob_t *blob); + + +/* hb_vector_paint_t */ + +HB_EXTERN hb_vector_paint_t * +hb_vector_paint_create_or_fail (hb_vector_format_t format); + +HB_EXTERN hb_vector_paint_t * +hb_vector_paint_reference (hb_vector_paint_t *paint); + +HB_EXTERN void +hb_vector_paint_destroy (hb_vector_paint_t *paint); + +HB_EXTERN hb_bool_t +hb_vector_paint_set_user_data (hb_vector_paint_t *paint, + hb_user_data_key_t *key, + void *data, + hb_destroy_func_t destroy, + hb_bool_t replace); + +HB_EXTERN void * +hb_vector_paint_get_user_data (hb_vector_paint_t *paint, + hb_user_data_key_t *key); + +HB_EXTERN void +hb_vector_paint_set_transform (hb_vector_paint_t *paint, + float xx, float yx, + float xy, float yy, + float dx, float dy); + +HB_EXTERN void +hb_vector_paint_get_transform (hb_vector_paint_t *paint, + float *xx, float *yx, + float *xy, float *yy, + float *dx, float *dy); + +HB_EXTERN void +hb_vector_paint_set_scale_factor (hb_vector_paint_t *paint, + float x_scale_factor, + float y_scale_factor); + +HB_EXTERN void +hb_vector_paint_get_scale_factor (hb_vector_paint_t *paint, + float *x_scale_factor, + float *y_scale_factor); + +HB_EXTERN void +hb_vector_paint_set_extents (hb_vector_paint_t *paint, + const hb_vector_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_vector_paint_get_extents (hb_vector_paint_t *paint, + hb_vector_extents_t *extents); + +HB_EXTERN hb_bool_t +hb_vector_paint_set_glyph_extents (hb_vector_paint_t *paint, + const hb_glyph_extents_t *glyph_extents); + +HB_EXTERN void +hb_vector_paint_set_foreground (hb_vector_paint_t *paint, + hb_color_t foreground); + +HB_EXTERN void +hb_vector_paint_set_palette (hb_vector_paint_t *paint, + int palette); + +HB_EXTERN void +hb_vector_paint_set_custom_palette_color (hb_vector_paint_t *paint, + unsigned color_index, + hb_color_t color); + +HB_EXTERN void +hb_vector_paint_clear_custom_palette_colors (hb_vector_paint_t *paint); + +HB_EXTERN hb_paint_funcs_t * +hb_vector_paint_get_funcs (void); + +HB_EXTERN hb_bool_t +hb_vector_paint_glyph (hb_vector_paint_t *paint, + hb_font_t *font, + hb_codepoint_t glyph, + float pen_x, + float pen_y, + hb_vector_extents_mode_t extents_mode); + +HB_EXTERN void +hb_vector_svg_paint_set_flat (hb_vector_paint_t *paint, + hb_bool_t flat); + +HB_EXTERN void +hb_vector_svg_paint_set_precision (hb_vector_paint_t *paint, + unsigned precision); + +HB_EXTERN hb_blob_t * +hb_vector_paint_render (hb_vector_paint_t *paint); + +HB_EXTERN void +hb_vector_paint_reset (hb_vector_paint_t *paint); + +HB_EXTERN void +hb_vector_paint_recycle_blob (hb_vector_paint_t *paint, + hb_blob_t *blob); + +HB_END_DECLS + +#endif /* HB_VECTOR_H */ diff --git a/thirdparty/harfbuzz/src/hb-vector.hh b/thirdparty/harfbuzz/src/hb-vector.hh index 75836ea632..71c0c27a15 100644 --- a/thirdparty/harfbuzz/src/hb-vector.hh +++ b/thirdparty/harfbuzz/src/hb-vector.hh @@ -62,7 +62,7 @@ struct hb_vector_t hb_requires (hb_is_iterable (Iterable))> explicit hb_vector_t (const Iterable &o) : hb_vector_t () { - extend (o); + extend (o, true); } HB_ALWAYS_INLINE_VECTOR_ALLOCS hb_vector_t (const hb_vector_t &o) : hb_vector_t () @@ -117,12 +117,12 @@ struct hb_vector_t template HB_ALWAYS_INLINE_VECTOR_ALLOCS - void extend (const Iterable &o) + void extend (const Iterable &o, bool exact=false) { auto iter = hb_iter (o); if (iter.is_random_access_iterator || iter.has_fast_len) { - if (unlikely (!alloc (hb_len (iter), true))) + if (unlikely (!alloc (length + hb_len (iter), exact))) return; unsigned count = hb_len (iter); for (unsigned i = 0; i < count; i++) @@ -138,16 +138,16 @@ struct hb_vector_t } } HB_ALWAYS_INLINE_VECTOR_ALLOCS - void extend (array_t o) + void extend (array_t o, bool exact=false) { - alloc (length + o.length); + alloc (length + o.length, exact); if (unlikely (in_error ())) return; copy_array (o); } HB_ALWAYS_INLINE_VECTOR_ALLOCS - void extend (c_array_t o) + void extend (c_array_t o, bool exact=false) { - alloc (length + o.length); + alloc (length + o.length, exact); if (unlikely (in_error ())) return; copy_array (o); } @@ -186,6 +186,34 @@ struct hb_vector_t return *this; } + /* Transfer ownership of the backing storage to caller. + * Returns nullptr if storage is not owned by this vector. */ + Type * + steal (unsigned *len = nullptr, int *allocated_out = nullptr) + { + if (!is_owned ()) + return nullptr; + if (len) + *len = length; + if (allocated_out) + *allocated_out = allocated; + Type *p = arrayZ; + init (); + return p; + } + + /* Adopt a previously detached owned buffer. */ + void + recycle_buffer (Type *buffer, + unsigned len, + int allocated_len) + { + fini (); + arrayZ = buffer; + length = len; + allocated = allocated_len; + } + friend void swap (hb_vector_t& a, hb_vector_t& b) noexcept { hb_swap (a.allocated, b.allocated); @@ -290,6 +318,12 @@ struct hb_vector_t } template HB_ALWAYS_INLINE_VECTOR_ALLOCS + bool push_or_fail (Args&&... args) + { + return push (std::forward (args)...) != std::addressof (Crap (Type)); + } + template + HB_ALWAYS_INLINE_VECTOR_ALLOCS Type *push_has_room (Args&&... args) { /* Emplace. */ diff --git a/thirdparty/harfbuzz/src/hb-version.h b/thirdparty/harfbuzz/src/hb-version.h index 4e143507fb..fe844a3a8d 100644 --- a/thirdparty/harfbuzz/src/hb-version.h +++ b/thirdparty/harfbuzz/src/hb-version.h @@ -41,26 +41,26 @@ HB_BEGIN_DECLS * * The major component of the library version available at compile-time. */ -#define HB_VERSION_MAJOR 12 +#define HB_VERSION_MAJOR 13 /** * HB_VERSION_MINOR: * * The minor component of the library version available at compile-time. */ -#define HB_VERSION_MINOR 3 +#define HB_VERSION_MINOR 1 /** * HB_VERSION_MICRO: * * The micro component of the library version available at compile-time. */ -#define HB_VERSION_MICRO 2 +#define HB_VERSION_MICRO 1 /** * HB_VERSION_STRING: * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "12.3.2" +#define HB_VERSION_STRING "13.1.1" /** * HB_VERSION_ATLEAST: diff --git a/thirdparty/harfbuzz/src/hb-zlib.cc b/thirdparty/harfbuzz/src/hb-zlib.cc new file mode 100644 index 0000000000..2703e943d4 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-zlib.cc @@ -0,0 +1,98 @@ +#ifndef HB_ZLIB_CC +#define HB_ZLIB_CC + +#include "hb.hh" + +#include "hb-zlib.hh" + +#ifdef HAVE_ZLIB +#include +#endif + +hb_blob_t * +hb_blob_decompress_gzip (hb_blob_t *blob, + unsigned max_output_len) +{ +#ifndef HAVE_ZLIB + return nullptr; +#else + unsigned compressed_len = 0; + const uint8_t *compressed = (const uint8_t *) hb_blob_get_data (blob, &compressed_len); + if (!compressed || !compressed_len) + return nullptr; + + z_stream stream = {}; + stream.next_in = (Bytef *) compressed; + stream.avail_in = compressed_len; + + if (inflateInit2 (&stream, 16 + MAX_WBITS) != Z_OK) + return nullptr; + + uint32_t expected_size = 0; + hb_gzip_get_uncompressed_size ((const char *) compressed, + compressed_len, + &expected_size); + + size_t allocated = hb_min ((size_t) hb_max (expected_size, 4096u), + (size_t) max_output_len); + char *output = (char *) hb_malloc (allocated); + if (!output) + { + inflateEnd (&stream); + return nullptr; + } + + int status = Z_OK; + while (true) + { + size_t produced = (size_t) stream.total_out; + if (unlikely (produced >= (size_t) max_output_len)) + goto fail; + + if (produced == allocated) + { + size_t new_allocated = hb_min (allocated * 2, (size_t) max_output_len); + if (unlikely (new_allocated <= allocated)) + goto fail; + + char *new_output = (char *) hb_realloc (output, new_allocated); + if (unlikely (!new_output)) + goto fail; + + output = new_output; + allocated = new_allocated; + } + + stream.next_out = (Bytef *) output + stream.total_out; + stream.avail_out = (uInt) (allocated - (size_t) stream.total_out); + + status = inflate (&stream, Z_FINISH); + if (status == Z_STREAM_END) + break; + + if ((status == Z_OK || status == Z_BUF_ERROR) && stream.avail_out == 0) + continue; + + goto fail; + } + + inflateEnd (&stream); + + if (unlikely ((size_t) stream.total_out > (size_t) max_output_len)) + goto fail_free; + + return hb_blob_create_or_fail (output, + (unsigned) stream.total_out, + HB_MEMORY_MODE_WRITABLE, + output, + hb_free); + +fail: + inflateEnd (&stream); +fail_free: + hb_free (output); + return nullptr; +#endif +} + +#endif /* HB_ZLIB_CC */ diff --git a/thirdparty/harfbuzz/src/hb-zlib.hh b/thirdparty/harfbuzz/src/hb-zlib.hh new file mode 100644 index 0000000000..3b8eb791a1 --- /dev/null +++ b/thirdparty/harfbuzz/src/hb-zlib.hh @@ -0,0 +1,37 @@ +#ifndef HB_ZLIB_HH +#define HB_ZLIB_HH + +#include "hb-blob.hh" + +static inline bool +hb_blob_is_gzip (const char *data, + unsigned data_len) +{ + return data_len >= 3 && + (unsigned char) data[0] == 0x1Fu && + (unsigned char) data[1] == 0x8Bu && + (unsigned char) data[2] == 0x08u; +} + +static inline bool +hb_gzip_get_uncompressed_size (const char *data, + unsigned data_len, + uint32_t *size) +{ + if (data_len < 4) + return false; + + const unsigned char *trailer = (const unsigned char *) data + data_len - 4; + if (size) + *size = (uint32_t) trailer[0] | + ((uint32_t) trailer[1] << 8) | + ((uint32_t) trailer[2] << 16) | + ((uint32_t) trailer[3] << 24); + return true; +} + +HB_INTERNAL hb_blob_t * +hb_blob_decompress_gzip (hb_blob_t *blob, + unsigned max_output_len); + +#endif /* HB_ZLIB_HH */ diff --git a/thirdparty/harfbuzz/src/hb.hh b/thirdparty/harfbuzz/src/hb.hh index 34a6df8a09..2ebae65f20 100644 --- a/thirdparty/harfbuzz/src/hb.hh +++ b/thirdparty/harfbuzz/src/hb.hh @@ -526,7 +526,7 @@ static_assert ((sizeof (hb_var_int_t) == 4), ""); /* Pie time. */ // https://github.com/harfbuzz/harfbuzz/issues/4166 -#define HB_PI 3.14159265358979f +#define HB_PI 3.14159265358979323846f #define HB_2_PI (2.f * HB_PI) /* Compile-time custom allocator support. */