* [FFmpeg-devel] [PATCH v7 02/14] vvcdec: add vvc_data
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
@ 2023-12-10 15:57 ` Nuo Mi
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 03/14] vvcdec: add parameter parser for sps, pps, ph, sh Nuo Mi
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 15:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
---
libavcodec/vvc/Makefile | 1 +
libavcodec/vvc/vvc_data.c | 3486 +++++++++++++++++++++++++++++++++++++
libavcodec/vvc/vvc_data.h | 80 +
3 files changed, 3567 insertions(+)
create mode 100644 libavcodec/vvc/vvc_data.c
create mode 100644 libavcodec/vvc/vvc_data.h
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index bd14dbc1df..8a5c66ab13 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -2,3 +2,4 @@ clean::
$(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%)
OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
+ vvc/vvc_data.o \
diff --git a/libavcodec/vvc/vvc_data.c b/libavcodec/vvc/vvc_data.c
new file mode 100644
index 0000000000..0c9376d098
--- /dev/null
+++ b/libavcodec/vvc/vvc_data.c
@@ -0,0 +1,3486 @@
+/*
+ * VVC shared tables
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdint.h>
+
+#include "libavutil/avassert.h"
+
+#include "vvc_data.h"
+
+const uint8_t ff_vvc_diag_scan_x[5][5][16*16] = {
+ {
+ //1x1
+ { 0, },
+ //1x2
+ { 0, 0, },
+ //1x4
+ { 0, 0, 0, 0, },
+ //1x8
+ { 0, 0, 0, 0, 0, 0, 0, 0, },
+ //1x16
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ },
+ {
+ //2x1
+ { 0, 1, },
+ //2x2
+ { 0, 0, 1, 1, },
+ //2x4
+ { 0, 0, 1, 0, 1, 0, 1, 1, },
+ //2x8
+ { 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, },
+ //2x16
+ {
+ 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
+ 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1,
+ },
+ },
+ {
+ //4x1
+ { 0, 1, 2, 3, },
+ //4x2
+ { 0, 0, 1, 1, 2, 2, 3, 3, },
+ //4x4
+ { 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3, },
+ //4x8
+ {
+ 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
+ 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3,
+ },
+ //4x16
+ {
+ 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
+ 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
+ 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
+ 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 2, 3, 3,
+ },
+ },
+ {
+ //8x1
+ { 0, 1, 2, 3, 4, 5, 6, 7, },
+ //8x2
+ { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, },
+ //8x4
+ {
+ 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 4, 2, 3,
+ 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7,
+ },
+ //8x8
+ {
+ 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0,
+ 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3,
+ 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6,
+ 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7,
+ },
+ //8x16
+ {
+ 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0,
+ 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3,
+ 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3,
+ 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3,
+ 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3,
+ 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3,
+ 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 2, 3, 4, 5, 6,
+ 7, 3, 4, 5, 6, 7, 4, 5, 6, 7, 5, 6, 7, 6, 7, 7,
+ },
+ },
+ {
+ //16x1
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, },
+ //16x2
+ {
+ 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15,
+ },
+ //16x4
+ {
+ 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 1, 2, 3, 4, 2, 3,
+ 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8, 6, 7,
+ 8, 9, 7, 8, 9, 10, 8, 9, 10, 11, 9, 10, 11, 12, 10, 11,
+ 12, 13, 11, 12, 13, 14, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15,
+ },
+ //16x8
+ {
+ 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0,
+ 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3,
+ 4, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7, 8, 2, 3, 4, 5,
+ 6, 7, 8, 9, 3, 4, 5, 6, 7, 8, 9, 10, 4, 5, 6, 7,
+ 8, 9, 10, 11, 5, 6, 7, 8, 9, 10, 11, 12, 6, 7, 8, 9,
+ 10, 11, 12, 13, 7, 8, 9, 10, 11, 12, 13, 14, 8, 9, 10, 11,
+ 12, 13, 14, 15, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14,
+ 15, 11, 12, 13, 14, 15, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15,
+ },
+ //16x16
+ {
+ 0, 0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0,
+ 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3,
+ 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 1, 2,
+ 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1,
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 7, 8, 9, 10, 11, 12, 13, 14, 15, 8, 9, 10, 11,
+ 12, 13, 14, 15, 9, 10, 11, 12, 13, 14, 15, 10, 11, 12, 13, 14,
+ 15, 11, 12, 13, 14, 15, 12, 13, 14, 15, 13, 14, 15, 14, 15, 15,
+ },
+ },
+};
+
+const uint8_t ff_vvc_diag_scan_y[5][5][16*16] = {
+ {
+ //1x1
+ { 0, },
+ //1x2
+ { 0, 1, },
+ //1x4
+ { 0, 1, 2, 3, },
+ //1x8
+ { 0, 1, 2, 3, 4, 5, 6, 7, },
+ //1x16
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, },
+ },
+ {
+ //2x1
+ { 0, 0, },
+ //2x2
+ { 0, 1, 0, 1, },
+ //2x4
+ { 0, 1, 0, 2, 1, 3, 2, 3, },
+ //2x8
+ { 0, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 7, },
+ //2x16
+ {
+ 0, 1, 0, 2, 1, 3, 2, 4, 3, 5, 4, 6, 5, 7, 6, 8,
+ 7, 9, 8, 10, 9, 11, 10, 12, 11, 13, 12, 14, 13, 15, 14, 15,
+ },
+ },
+ {
+ //4x1
+ { 0, 0, 0, 0, },
+ //4x2
+ { 0, 1, 0, 1, 0, 1, 0, 1, },
+ //4x4
+ { 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3, },
+ //4x8
+ {
+ 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 5, 4,
+ 3, 2, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7,
+ },
+ //4x16
+ {
+ 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 5, 4,
+ 3, 2, 6, 5, 4, 3, 7, 6, 5, 4, 8, 7, 6, 5, 9, 8,
+ 7, 6, 10, 9, 8, 7, 11, 10, 9, 8, 12, 11, 10, 9, 13, 12,
+ 11, 10, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15,
+ },
+ },
+ {
+ //8x1
+ { 0, 0, 0, 0, 0, 0, 0, 0, },
+ //8x2
+ { 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, },
+ //8x4
+ {
+ 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2,
+ 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3,
+ },
+ //8x8
+ {
+ 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5,
+ 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4,
+ 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3,
+ 2, 7, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7,
+ },
+ //8x16
+ {
+ 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5,
+ 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4,
+ 3, 2, 1, 0, 8, 7, 6, 5, 4, 3, 2, 1, 9, 8, 7, 6,
+ 5, 4, 3, 2, 10, 9, 8, 7, 6, 5, 4, 3, 11, 10, 9, 8,
+ 7, 6, 5, 4, 12, 11, 10, 9, 8, 7, 6, 5, 13, 12, 11, 10,
+ 9, 8, 7, 6, 14, 13, 12, 11, 10, 9, 8, 7, 15, 14, 13, 12,
+ 11, 10, 9, 8, 15, 14, 13, 12, 11, 10, 9, 15, 14, 13, 12, 11,
+ 10, 15, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15,
+ },
+ },
+ {
+ //16x1
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
+ //16x2
+ {
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
+ },
+ //16x4
+ {
+ 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2,
+ 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2,
+ 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2,
+ 1, 0, 3, 2, 1, 0, 3, 2, 1, 0, 3, 2, 1, 3, 2, 3,
+ },
+ //16x8
+ {
+ 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5,
+ 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4,
+ 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4,
+ 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4,
+ 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4,
+ 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4,
+ 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 7, 6, 5, 4, 3,
+ 2, 7, 6, 5, 4, 3, 7, 6, 5, 4, 7, 6, 5, 7, 6, 7,
+ },
+ //16x16
+ {
+ 0, 1, 0, 2, 1, 0, 3, 2, 1, 0, 4, 3, 2, 1, 0, 5,
+ 4, 3, 2, 1, 0, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4,
+ 3, 2, 1, 0, 8, 7, 6, 5, 4, 3, 2, 1, 0, 9, 8, 7,
+ 6, 5, 4, 3, 2, 1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2,
+ 1, 0, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 12, 11,
+ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 13, 12, 11, 10, 9,
+ 8, 7, 6, 5, 4, 3, 2, 1, 0, 14, 13, 12, 11, 10, 9, 8,
+ 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8,
+ 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8,
+ 7, 6, 5, 4, 3, 2, 1, 15, 14, 13, 12, 11, 10, 9, 8, 7,
+ 6, 5, 4, 3, 2, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5,
+ 4, 3, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 15, 14,
+ 13, 12, 11, 10, 9, 8, 7, 6, 5, 15, 14, 13, 12, 11, 10, 9,
+ 8, 7, 6, 15, 14, 13, 12, 11, 10, 9, 8, 7, 15, 14, 13, 12,
+ 11, 10, 9, 8, 15, 14, 13, 12, 11, 10, 9, 15, 14, 13, 12, 11,
+ 10, 15, 14, 13, 12, 11, 15, 14, 13, 12, 15, 14, 13, 15, 14, 15,
+ },
+ },
+};
+
+const uint8_t ff_vvc_scaling_pred_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, 8, 8,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+};
+
+const uint8_t ff_vvc_scaling_pred_16[8 * 8] = {
+ 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,
+ 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,
+};
+
+const int ff_vvc_scaling_list0[8 * 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, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 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 uint8_t mip_matrix_4x4[16][16][4] = {
+ {
+ { 32, 30, 90, 28 },
+ { 32, 32, 72, 28 },
+ { 34, 77, 53, 30 },
+ { 51, 124, 36, 37 },
+ { 31, 31, 95, 37 },
+ { 33, 31, 70, 50 },
+ { 52, 80, 25, 60 },
+ { 78, 107, 1, 65 },
+ { 31, 29, 37, 95 },
+ { 38, 34, 19, 101 },
+ { 73, 85, 0, 81 },
+ { 92, 99, 0, 65 },
+ { 34, 29, 14, 111 },
+ { 48, 48, 7, 100 },
+ { 80, 91, 0, 74 },
+ { 89, 97, 0, 64 }
+ },
+ {
+ { 31, 23, 34, 29 },
+ { 31, 43, 34, 31 },
+ { 30, 95, 34, 32 },
+ { 29, 100, 35, 33 },
+ { 31, 23, 34, 29 },
+ { 31, 43, 34, 31 },
+ { 30, 95, 34, 32 },
+ { 29, 99, 35, 33 },
+ { 31, 24, 35, 29 },
+ { 31, 44, 34, 31 },
+ { 30, 95, 35, 32 },
+ { 29, 99, 35, 33 },
+ { 31, 24, 35, 30 },
+ { 31, 44, 35, 31 },
+ { 30, 95, 35, 32 },
+ { 29, 99, 35, 33 }
+ },
+ {
+ { 32, 32, 36, 58 },
+ { 32, 29, 26, 66 },
+ { 36, 37, 23, 61 },
+ { 79, 84, 3, 37 },
+ { 32, 32, 30, 69 },
+ { 33, 29, 24, 71 },
+ { 44, 16, 21, 70 },
+ { 96, 18, 0, 57 },
+ { 32, 31, 24, 74 },
+ { 33, 30, 23, 71 },
+ { 36, 24, 24, 71 },
+ { 59, 9, 16, 68 },
+ { 32, 32, 23, 75 },
+ { 33, 30, 24, 70 },
+ { 32, 30, 25, 71 },
+ { 36, 26, 25, 70 }
+ },
+ {
+ { 32, 33, 34, 32 },
+ { 32, 30, 22, 38 },
+ { 29, 46, 25, 38 },
+ { 53, 123, 28, 22 },
+ { 32, 33, 30, 37 },
+ { 32, 30, 21, 38 },
+ { 32, 40, 24, 38 },
+ { 64, 116, 26, 17 },
+ { 32, 32, 23, 49 },
+ { 32, 30, 21, 39 },
+ { 34, 39, 24, 37 },
+ { 72, 109, 23, 16 },
+ { 33, 31, 17, 60 },
+ { 32, 31, 21, 39 },
+ { 35, 41, 24, 37 },
+ { 72, 106, 22, 18 }
+ },
+ {
+ { 34, 25, 89, 20 },
+ { 38, 32, 47, 24 },
+ { 40, 86, 29, 27 },
+ { 38, 98, 32, 29 },
+ { 34, 31, 94, 40 },
+ { 44, 25, 83, 27 },
+ { 54, 72, 43, 16 },
+ { 47, 94, 33, 22 },
+ { 33, 31, 36, 94 },
+ { 43, 23, 51, 76 },
+ { 62, 55, 64, 25 },
+ { 57, 89, 38, 15 },
+ { 32, 32, 28, 101 },
+ { 38, 26, 33, 94 },
+ { 55, 38, 68, 47 },
+ { 59, 80, 52, 16 }
+ },
+ {
+ { 28, 30, 68, 29 },
+ { 23, 48, 23, 48 },
+ { 39, 98, 16, 42 },
+ { 84, 86, 20, 17 },
+ { 25, 31, 52, 74 },
+ { 38, 68, 5, 70 },
+ { 95, 78, 7, 21 },
+ { 127, 54, 12, 0 },
+ { 30, 47, 14, 107 },
+ { 79, 76, 0, 53 },
+ { 127, 59, 7, 1 },
+ { 127, 51, 9, 0 },
+ { 50, 71, 1, 96 },
+ { 109, 69, 7, 25 },
+ { 127, 56, 9, 0 },
+ { 123, 53, 13, 0 }
+ },
+ {
+ { 40, 20, 72, 18 },
+ { 48, 29, 44, 18 },
+ { 53, 81, 35, 18 },
+ { 48, 96, 33, 22 },
+ { 45, 23, 79, 49 },
+ { 61, 21, 56, 49 },
+ { 72, 52, 32, 48 },
+ { 65, 69, 20, 50 },
+ { 41, 27, 29, 96 },
+ { 49, 22, 28, 94 },
+ { 52, 22, 28, 93 },
+ { 49, 27, 27, 92 },
+ { 37, 29, 26, 98 },
+ { 39, 28, 28, 97 },
+ { 38, 28, 30, 97 },
+ { 38, 29, 30, 95 }
+ },
+ {
+ { 33, 27, 43, 27 },
+ { 32, 29, 31, 31 },
+ { 31, 73, 33, 31 },
+ { 35, 104, 34, 28 },
+ { 32, 30, 63, 22 },
+ { 33, 26, 33, 29 },
+ { 33, 57, 33, 30 },
+ { 37, 100, 35, 27 },
+ { 32, 31, 85, 25 },
+ { 34, 25, 39, 25 },
+ { 35, 39, 32, 28 },
+ { 40, 91, 35, 25 },
+ { 32, 30, 77, 50 },
+ { 34, 26, 54, 22 },
+ { 37, 31, 34, 27 },
+ { 45, 75, 34, 23 }
+ },
+ {
+ { 34, 25, 77, 19 },
+ { 36, 34, 56, 24 },
+ { 41, 83, 39, 30 },
+ { 47, 96, 28, 35 },
+ { 34, 31, 70, 65 },
+ { 38, 29, 53, 77 },
+ { 43, 36, 37, 83 },
+ { 48, 39, 28, 83 },
+ { 33, 31, 31, 98 },
+ { 33, 31, 30, 99 },
+ { 34, 30, 31, 98 },
+ { 36, 29, 31, 96 },
+ { 32, 32, 30, 97 },
+ { 32, 32, 31, 96 },
+ { 31, 33, 33, 96 },
+ { 32, 33, 34, 94 }
+ },
+ {
+ { 30, 30, 93, 19 },
+ { 31, 59, 67, 34 },
+ { 31, 79, 36, 59 },
+ { 30, 67, 17, 79 },
+ { 30, 38, 68, 69 },
+ { 29, 40, 43, 91 },
+ { 26, 35, 32, 101 },
+ { 23, 32, 30, 101 },
+ { 26, 34, 30, 101 },
+ { 23, 33, 30, 102 },
+ { 20, 32, 31, 102 },
+ { 18, 33, 32, 102 },
+ { 23, 33, 31, 100 },
+ { 20, 34, 32, 100 },
+ { 18, 35, 33, 100 },
+ { 18, 35, 33, 100 }
+ },
+ {
+ { 31, 54, 90, 26 },
+ { 32, 60, 53, 61 },
+ { 34, 49, 37, 84 },
+ { 34, 39, 35, 89 },
+ { 35, 38, 41, 88 },
+ { 35, 35, 32, 96 },
+ { 35, 31, 33, 96 },
+ { 35, 32, 35, 94 },
+ { 34, 34, 30, 97 },
+ { 35, 32, 33, 95 },
+ { 35, 32, 34, 94 },
+ { 35, 34, 34, 93 },
+ { 34, 34, 34, 93 },
+ { 35, 34, 34, 93 },
+ { 35, 34, 34, 92 },
+ { 36, 34, 35, 91 }
+ },
+ {
+ { 32, 29, 54, 24 },
+ { 31, 32, 34, 29 },
+ { 31, 43, 34, 29 },
+ { 32, 67, 36, 28 },
+ { 31, 34, 69, 37 },
+ { 31, 35, 46, 33 },
+ { 30, 35, 39, 33 },
+ { 30, 42, 39, 36 },
+ { 31, 35, 39, 88 },
+ { 30, 38, 41, 84 },
+ { 30, 39, 40, 81 },
+ { 39, 46, 38, 78 },
+ { 31, 36, 34, 96 },
+ { 34, 38, 37, 93 },
+ { 55, 42, 38, 82 },
+ { 89, 53, 38, 65 }
+ },
+ {
+ { 32, 33, 43, 29 },
+ { 32, 30, 29, 33 },
+ { 31, 47, 31, 33 },
+ { 33, 100, 31, 31 },
+ { 32, 33, 74, 25 },
+ { 32, 32, 34, 31 },
+ { 32, 33, 30, 33 },
+ { 32, 68, 30, 32 },
+ { 32, 31, 91, 40 },
+ { 32, 32, 58, 26 },
+ { 31, 31, 30, 32 },
+ { 31, 42, 30, 33 },
+ { 32, 31, 49, 85 },
+ { 32, 31, 83, 35 },
+ { 31, 33, 48, 29 },
+ { 31, 36, 32, 33 }
+ },
+ {
+ { 31, 29, 81, 35 },
+ { 32, 28, 34, 50 },
+ { 31, 75, 16, 43 },
+ { 34, 103, 29, 32 },
+ { 32, 32, 53, 78 },
+ { 31, 28, 36, 88 },
+ { 30, 52, 18, 73 },
+ { 52, 88, 17, 35 },
+ { 32, 32, 35, 94 },
+ { 30, 31, 35, 95 },
+ { 36, 29, 31, 92 },
+ { 100, 43, 16, 40 },
+ { 32, 32, 35, 93 },
+ { 30, 32, 38, 93 },
+ { 55, 18, 37, 83 },
+ { 127, 0, 30, 40 }
+ },
+ {
+ { 31, 22, 47, 30 },
+ { 31, 48, 25, 34 },
+ { 30, 95, 31, 32 },
+ { 32, 103, 33, 32 },
+ { 30, 24, 57, 31 },
+ { 30, 47, 26, 34 },
+ { 31, 95, 31, 32 },
+ { 43, 97, 35, 25 },
+ { 29, 26, 44, 63 },
+ { 37, 38, 24, 47 },
+ { 74, 63, 28, 20 },
+ { 110, 58, 34, 3 },
+ { 46, 22, 5, 108 },
+ { 93, 5, 9, 77 },
+ { 127, 0, 17, 52 },
+ { 127, 0, 15, 50 }
+ },
+ {
+ { 32, 27, 68, 24 },
+ { 35, 23, 35, 28 },
+ { 35, 64, 29, 29 },
+ { 37, 104, 33, 28 },
+ { 32, 32, 91, 40 },
+ { 36, 23, 67, 36 },
+ { 49, 23, 39, 28 },
+ { 60, 67, 30, 20 },
+ { 32, 32, 36, 95 },
+ { 35, 29, 38, 93 },
+ { 50, 16, 30, 84 },
+ { 72, 16, 15, 65 },
+ { 32, 32, 27, 100 },
+ { 33, 32, 29, 100 },
+ { 37, 29, 30, 98 },
+ { 48, 21, 29, 90 }
+ }
+};
+
+static const uint8_t mip_matrix_8x8[8][16][8] = {
+ {
+ { 30, 63, 46, 37, 25, 33, 33, 34 },
+ { 30, 60, 66, 38, 32, 31, 32, 33 },
+ { 29, 45, 74, 42, 32, 32, 32, 33 },
+ { 30, 39, 62, 58, 32, 33, 32, 33 },
+ { 30, 66, 55, 39, 32, 30, 30, 36 },
+ { 29, 54, 69, 40, 33, 31, 31, 33 },
+ { 28, 48, 71, 43, 32, 33, 32, 33 },
+ { 28, 41, 72, 46, 32, 34, 32, 33 },
+ { 30, 66, 56, 40, 32, 33, 28, 33 },
+ { 29, 55, 69, 39, 33, 33, 30, 32 },
+ { 27, 46, 72, 43, 33, 33, 32, 33 },
+ { 27, 42, 69, 48, 32, 34, 32, 33 },
+ { 30, 63, 55, 40, 32, 33, 35, 30 },
+ { 29, 56, 66, 40, 33, 33, 33, 30 },
+ { 27, 47, 69, 44, 33, 33, 33, 32 },
+ { 27, 42, 65, 50, 32, 34, 32, 33 }
+ },
+ {
+ { 32, 33, 30, 31, 74, 30, 31, 32 },
+ { 33, 56, 28, 30, 41, 29, 32, 32 },
+ { 33, 77, 52, 26, 29, 34, 30, 32 },
+ { 33, 37, 80, 41, 31, 34, 30, 32 },
+ { 32, 32, 33, 31, 59, 76, 28, 31 },
+ { 33, 31, 31, 30, 78, 40, 28, 32 },
+ { 33, 47, 28, 29, 53, 27, 31, 31 },
+ { 33, 61, 44, 28, 34, 32, 31, 31 },
+ { 32, 31, 34, 30, 26, 64, 76, 27 },
+ { 32, 31, 34, 29, 45, 86, 36, 29 },
+ { 33, 27, 34, 29, 73, 55, 25, 32 },
+ { 33, 33, 34, 30, 62, 33, 30, 31 },
+ { 32, 31, 34, 30, 30, 29, 58, 74 },
+ { 32, 31, 35, 29, 27, 53, 77, 35 },
+ { 32, 30, 36, 29, 40, 80, 44, 31 },
+ { 33, 28, 37, 30, 58, 60, 31, 33 }
+ },
+ {
+ { 32, 51, 27, 32, 27, 50, 29, 32 },
+ { 32, 95, 42, 29, 29, 42, 30, 32 },
+ { 32, 27, 99, 34, 31, 41, 29, 32 },
+ { 32, 34, 21, 104, 31, 42, 30, 32 },
+ { 32, 45, 30, 32, 9, 88, 40, 30 },
+ { 32, 77, 38, 30, 9, 76, 38, 30 },
+ { 32, 38, 78, 33, 14, 67, 37, 30 },
+ { 32, 30, 30, 87, 20, 59, 38, 31 },
+ { 33, 37, 32, 32, 27, 18, 106, 34 },
+ { 34, 44, 34, 31, 25, 17, 108, 31 },
+ { 36, 39, 45, 31, 24, 15, 108, 30 },
+ { 37, 31, 31, 54, 25, 14, 101, 32 },
+ { 36, 33, 32, 30, 29, 37, 13, 110 },
+ { 39, 32, 32, 29, 27, 37, 15, 108 },
+ { 44, 33, 31, 27, 25, 37, 16, 106 },
+ { 47, 30, 31, 32, 25, 34, 19, 102 }
+ },
+ {
+ { 32, 48, 35, 35, 47, 68, 31, 31 },
+ { 32, 33, 59, 40, 27, 71, 33, 30 },
+ { 32, 29, 47, 65, 24, 62, 37, 30 },
+ { 33, 33, 31, 81, 26, 50, 42, 32 },
+ { 32, 30, 40, 38, 30, 70, 55, 31 },
+ { 32, 20, 46, 50, 26, 55, 64, 31 },
+ { 33, 30, 29, 66, 25, 41, 72, 33 },
+ { 36, 34, 27, 69, 26, 31, 67, 39 },
+ { 33, 28, 36, 40, 30, 26, 85, 47 },
+ { 36, 27, 33, 50, 31, 20, 79, 53 },
+ { 43, 30, 26, 57, 28, 17, 67, 62 },
+ { 51, 27, 28, 55, 22, 23, 49, 70 },
+ { 38, 29, 32, 39, 28, 30, 22, 104 },
+ { 51, 31, 28, 43, 24, 31, 17, 102 },
+ { 69, 23, 30, 40, 15, 38, 10, 95 },
+ { 77, 13, 35, 38, 8, 43, 8, 90 }
+ },
+ {
+ { 32, 38, 32, 33, 101, 40, 29, 32 },
+ { 32, 40, 37, 32, 100, 36, 30, 32 },
+ { 32, 37, 46, 35, 94, 33, 30, 31 },
+ { 33, 34, 30, 62, 81, 35, 30, 31 },
+ { 32, 32, 33, 32, 22, 102, 39, 29 },
+ { 32, 31, 33, 33, 26, 104, 34, 28 },
+ { 33, 33, 33, 33, 31, 103, 32, 28 },
+ { 33, 32, 34, 36, 37, 94, 33, 28 },
+ { 32, 33, 32, 32, 34, 24, 99, 36 },
+ { 32, 34, 33, 33, 33, 30, 98, 32 },
+ { 33, 33, 34, 33, 31, 37, 95, 29 },
+ { 33, 33, 33, 36, 30, 46, 85, 31 },
+ { 32, 33, 32, 33, 30, 34, 23, 104 },
+ { 32, 34, 33, 33, 31, 32, 30, 98 },
+ { 32, 33, 34, 34, 31, 29, 39, 91 },
+ { 33, 33, 32, 37, 32, 30, 47, 82 }
+ },
+ {
+ { 32, 52, 48, 31, 38, 76, 26, 32 },
+ { 33, 19, 62, 50, 25, 50, 51, 31 },
+ { 33, 30, 20, 74, 29, 29, 54, 51 },
+ { 34, 35, 23, 56, 31, 25, 41, 76 },
+ { 33, 25, 38, 39, 28, 39, 83, 35 },
+ { 35, 28, 25, 47, 31, 23, 57, 74 },
+ { 37, 35, 22, 38, 31, 27, 30, 101 },
+ { 38, 32, 33, 29, 30, 31, 27, 103 },
+ { 34, 32, 27, 37, 32, 25, 41, 92 },
+ { 38, 33, 28, 32, 30, 31, 18, 111 },
+ { 40, 32, 33, 27, 29, 33, 18, 111 },
+ { 40, 32, 34, 27, 28, 33, 23, 105 },
+ { 35, 32, 30, 33, 31, 33, 20, 107 },
+ { 38, 31, 33, 30, 29, 33, 21, 106 },
+ { 40, 32, 33, 29, 29, 34, 22, 105 },
+ { 40, 32, 33, 30, 29, 34, 24, 101 }
+ },
+ {
+ { 32, 28, 31, 33, 92, 33, 30, 31 },
+ { 33, 30, 28, 33, 71, 26, 32, 30 },
+ { 33, 60, 26, 33, 47, 28, 33, 30 },
+ { 33, 63, 44, 36, 37, 31, 33, 30 },
+ { 33, 30, 31, 33, 43, 90, 33, 29 },
+ { 33, 28, 29, 34, 71, 71, 26, 30 },
+ { 33, 30, 26, 33, 86, 45, 28, 30 },
+ { 33, 38, 29, 32, 74, 32, 33, 29 },
+ { 33, 32, 30, 32, 29, 41, 95, 27 },
+ { 34, 31, 29, 33, 26, 71, 73, 22 },
+ { 34, 31, 29, 33, 37, 88, 46, 25 },
+ { 33, 32, 28, 34, 55, 75, 36, 28 },
+ { 34, 31, 30, 32, 33, 27, 43, 89 },
+ { 35, 32, 28, 33, 33, 23, 77, 59 },
+ { 34, 33, 28, 33, 30, 35, 91, 37 },
+ { 34, 34, 28, 34, 33, 53, 74, 31 }
+ },
+ {
+ { 33, 49, 26, 32, 26, 52, 28, 31 },
+ { 33, 71, 72, 24, 30, 32, 34, 31 },
+ { 32, 23, 70, 68, 32, 32, 32, 32 },
+ { 31, 33, 21, 106, 33, 32, 32, 33 },
+ { 34, 47, 32, 29, 5, 86, 44, 26 },
+ { 34, 44, 89, 28, 28, 37, 33, 30 },
+ { 32, 27, 46, 89, 33, 31, 31, 32 },
+ { 30, 33, 20, 107, 33, 33, 32, 33 },
+ { 35, 39, 42, 27, 26, 24, 92, 35 },
+ { 34, 27, 87, 43, 30, 34, 38, 31 },
+ { 31, 31, 32, 100, 32, 33, 30, 32 },
+ { 29, 32, 22, 106, 33, 33, 32, 33 },
+ { 35, 29, 47, 32, 32, 32, 17, 100 },
+ { 34, 24, 69, 60, 34, 33, 28, 44 },
+ { 31, 33, 31, 99, 32, 33, 32, 31 },
+ { 29, 33, 25, 103, 33, 33, 32, 35 }
+ }
+};
+
+static const uint8_t mip_matrix_16x16[6][64][7] = {
+ {
+ { 42, 37, 33, 27, 44, 33, 35 },
+ { 71, 39, 34, 24, 36, 35, 36 },
+ { 77, 46, 35, 33, 30, 34, 36 },
+ { 64, 60, 35, 33, 31, 32, 36 },
+ { 49, 71, 38, 32, 32, 31, 36 },
+ { 42, 66, 50, 33, 31, 32, 36 },
+ { 40, 52, 67, 33, 31, 32, 35 },
+ { 38, 43, 75, 33, 32, 32, 35 },
+ { 56, 40, 33, 26, 43, 38, 36 },
+ { 70, 49, 34, 30, 28, 38, 38 },
+ { 65, 57, 36, 34, 28, 33, 39 },
+ { 59, 60, 39, 33, 30, 31, 38 },
+ { 55, 60, 43, 33, 30, 31, 38 },
+ { 51, 61, 47, 33, 30, 32, 37 },
+ { 46, 62, 51, 34, 30, 32, 37 },
+ { 42, 60, 55, 33, 31, 32, 37 },
+ { 60, 42, 34, 30, 37, 43, 38 },
+ { 68, 52, 35, 35, 22, 37, 40 },
+ { 62, 58, 37, 34, 28, 31, 40 },
+ { 58, 59, 41, 33, 30, 30, 39 },
+ { 56, 59, 44, 34, 30, 31, 38 },
+ { 53, 60, 45, 33, 30, 31, 38 },
+ { 49, 65, 45, 33, 30, 31, 38 },
+ { 45, 64, 47, 33, 31, 32, 38 },
+ { 59, 44, 35, 31, 34, 43, 41 },
+ { 66, 53, 36, 35, 25, 31, 43 },
+ { 61, 58, 38, 34, 29, 30, 40 },
+ { 59, 57, 41, 33, 30, 31, 39 },
+ { 57, 58, 43, 33, 30, 31, 39 },
+ { 54, 61, 43, 33, 31, 31, 39 },
+ { 51, 64, 43, 33, 31, 31, 39 },
+ { 48, 64, 45, 33, 32, 31, 39 },
+ { 57, 45, 35, 30, 35, 40, 44 },
+ { 65, 54, 37, 33, 33, 24, 44 },
+ { 63, 56, 38, 34, 30, 29, 39 },
+ { 61, 56, 41, 34, 30, 32, 39 },
+ { 58, 58, 42, 33, 31, 31, 39 },
+ { 54, 62, 41, 33, 31, 31, 39 },
+ { 51, 65, 42, 33, 31, 31, 39 },
+ { 48, 63, 43, 33, 32, 31, 39 },
+ { 55, 46, 35, 30, 36, 38, 47 },
+ { 65, 53, 37, 32, 36, 26, 40 },
+ { 65, 54, 38, 33, 31, 30, 38 },
+ { 63, 55, 39, 33, 30, 32, 38 },
+ { 59, 58, 40, 33, 31, 31, 39 },
+ { 54, 64, 40, 33, 31, 30, 40 },
+ { 49, 66, 40, 32, 32, 30, 41 },
+ { 48, 64, 42, 32, 32, 30, 41 },
+ { 54, 46, 35, 30, 34, 39, 49 },
+ { 64, 52, 36, 32, 34, 34, 35 },
+ { 65, 53, 37, 33, 32, 32, 37 },
+ { 63, 55, 38, 33, 31, 31, 39 },
+ { 59, 60, 38, 33, 31, 31, 40 },
+ { 54, 64, 38, 33, 32, 30, 40 },
+ { 49, 66, 39, 33, 32, 29, 41 },
+ { 47, 64, 42, 32, 33, 29, 42 },
+ { 51, 46, 35, 31, 33, 37, 54 },
+ { 61, 51, 36, 32, 33, 38, 36 },
+ { 63, 53, 37, 32, 32, 34, 37 },
+ { 62, 55, 37, 33, 32, 32, 39 },
+ { 58, 59, 37, 33, 32, 31, 40 },
+ { 53, 63, 38, 33, 32, 31, 40 },
+ { 49, 64, 40, 33, 33, 30, 41 },
+ { 46, 62, 42, 33, 33, 30, 42 }
+ },
+ {
+ { 39, 34, 33, 58, 44, 31, 32 },
+ { 60, 38, 32, 40, 51, 30, 31 },
+ { 73, 49, 31, 39, 48, 32, 31 },
+ { 60, 73, 30, 39, 46, 33, 32 },
+ { 43, 87, 35, 38, 45, 33, 32 },
+ { 35, 78, 54, 36, 45, 33, 32 },
+ { 33, 47, 86, 35, 44, 33, 32 },
+ { 31, 17, 114, 34, 44, 34, 33 },
+ { 43, 37, 32, 53, 70, 30, 31 },
+ { 53, 50, 30, 42, 72, 31, 30 },
+ { 52, 66, 30, 39, 70, 32, 30 },
+ { 46, 78, 35, 37, 68, 34, 30 },
+ { 43, 75, 48, 37, 66, 34, 30 },
+ { 40, 62, 68, 35, 65, 35, 30 },
+ { 33, 37, 97, 33, 62, 37, 31 },
+ { 26, 14, 122, 32, 59, 38, 33 },
+ { 40, 39, 33, 34, 87, 37, 30 },
+ { 45, 54, 32, 34, 84, 41, 29 },
+ { 41, 70, 35, 33, 83, 40, 29 },
+ { 37, 73, 44, 32, 82, 40, 30 },
+ { 37, 65, 60, 31, 81, 41, 29 },
+ { 35, 48, 82, 30, 79, 43, 29 },
+ { 28, 27, 108, 28, 76, 45, 30 },
+ { 19, 11, 127, 27, 70, 46, 32 },
+ { 38, 40, 34, 27, 73, 62, 28 },
+ { 39, 54, 35, 30, 73, 62, 28 },
+ { 33, 65, 41, 29, 75, 59, 28 },
+ { 30, 65, 53, 27, 76, 58, 29 },
+ { 29, 53, 72, 26, 77, 58, 29 },
+ { 27, 35, 95, 24, 77, 60, 28 },
+ { 19, 19, 117, 23, 74, 61, 30 },
+ { 9, 16, 127, 23, 68, 60, 34 },
+ { 35, 40, 35, 29, 44, 89, 30 },
+ { 33, 51, 39, 29, 49, 86, 30 },
+ { 28, 57, 49, 28, 53, 83, 30 },
+ { 24, 52, 65, 26, 56, 82, 30 },
+ { 22, 39, 86, 24, 58, 82, 30 },
+ { 18, 22, 108, 23, 59, 82, 31 },
+ { 10, 13, 125, 22, 58, 80, 33 },
+ { 0, 19, 127, 22, 56, 74, 40 },
+ { 33, 40, 36, 31, 28, 90, 45 },
+ { 29, 46, 44, 29, 31, 92, 43 },
+ { 24, 45, 58, 28, 34, 91, 43 },
+ { 19, 37, 78, 26, 37, 91, 43 },
+ { 15, 22, 99, 25, 38, 91, 42 },
+ { 11, 11, 118, 24, 39, 90, 44 },
+ { 2, 11, 127, 23, 41, 85, 48 },
+ { 0, 17, 127, 23, 43, 75, 55 },
+ { 31, 37, 39, 30, 28, 54, 82 },
+ { 27, 37, 52, 28, 30, 58, 79 },
+ { 22, 30, 70, 27, 32, 58, 79 },
+ { 15, 19, 91, 26, 33, 58, 79 },
+ { 10, 8, 111, 25, 34, 58, 79 },
+ { 5, 2, 125, 25, 35, 57, 80 },
+ { 0, 9, 127, 25, 36, 53, 84 },
+ { 0, 13, 127, 25, 39, 47, 88 },
+ { 28, 29, 46, 28, 39, 2, 123 },
+ { 24, 24, 62, 27, 41, 1, 125 },
+ { 19, 14, 81, 25, 43, 0, 126 },
+ { 13, 4, 101, 24, 44, 0, 127 },
+ { 6, 0, 116, 23, 45, 0, 127 },
+ { 0, 0, 126, 23, 45, 1, 127 },
+ { 0, 4, 127, 25, 44, 2, 127 },
+ { 0, 9, 127, 25, 44, 3, 127 }
+ },
+ {
+ { 30, 32, 32, 42, 34, 32, 32 },
+ { 63, 26, 34, 16, 38, 32, 32 },
+ { 98, 26, 34, 25, 34, 33, 32 },
+ { 75, 61, 30, 31, 32, 33, 32 },
+ { 36, 94, 32, 30, 33, 32, 32 },
+ { 26, 76, 58, 30, 33, 32, 32 },
+ { 30, 39, 91, 31, 32, 33, 31 },
+ { 32, 23, 105, 32, 32, 32, 32 },
+ { 34, 30, 33, 31, 52, 29, 32 },
+ { 66, 24, 34, 11, 41, 33, 32 },
+ { 97, 28, 34, 24, 34, 33, 32 },
+ { 71, 65, 30, 30, 32, 33, 32 },
+ { 34, 92, 35, 30, 33, 32, 32 },
+ { 26, 70, 64, 29, 34, 32, 32 },
+ { 30, 37, 94, 30, 33, 32, 31 },
+ { 32, 23, 105, 31, 33, 33, 31 },
+ { 37, 29, 33, 8, 79, 27, 32 },
+ { 71, 22, 35, 5, 50, 32, 32 },
+ { 98, 29, 34, 23, 34, 34, 32 },
+ { 66, 70, 30, 31, 31, 33, 32 },
+ { 31, 92, 38, 30, 33, 32, 32 },
+ { 26, 66, 68, 29, 34, 32, 31 },
+ { 30, 34, 97, 30, 34, 33, 31 },
+ { 31, 22, 106, 30, 34, 33, 31 },
+ { 40, 28, 34, 0, 76, 46, 28 },
+ { 76, 21, 35, 0, 55, 35, 32 },
+ { 97, 32, 34, 21, 37, 33, 33 },
+ { 61, 75, 29, 30, 32, 32, 32 },
+ { 29, 92, 40, 29, 33, 32, 32 },
+ { 26, 62, 73, 29, 34, 32, 31 },
+ { 29, 32, 99, 30, 34, 33, 30 },
+ { 31, 22, 107, 30, 34, 33, 31 },
+ { 42, 27, 34, 1, 48, 79, 25 },
+ { 80, 20, 35, 0, 48, 47, 31 },
+ { 94, 36, 32, 17, 40, 33, 33 },
+ { 55, 80, 29, 27, 35, 31, 32 },
+ { 27, 90, 43, 28, 34, 32, 31 },
+ { 26, 58, 76, 29, 33, 33, 30 },
+ { 29, 30, 101, 29, 34, 34, 30 },
+ { 31, 21, 108, 29, 35, 34, 30 },
+ { 44, 26, 34, 6, 30, 80, 40 },
+ { 81, 21, 35, 0, 41, 52, 35 },
+ { 90, 41, 31, 14, 41, 35, 33 },
+ { 51, 82, 29, 24, 37, 32, 32 },
+ { 27, 87, 47, 27, 35, 32, 31 },
+ { 26, 54, 79, 29, 34, 33, 30 },
+ { 29, 29, 102, 28, 34, 33, 30 },
+ { 31, 21, 108, 28, 35, 33, 31 },
+ { 47, 26, 34, 7, 34, 44, 75 },
+ { 80, 24, 34, 0, 41, 41, 50 },
+ { 84, 45, 31, 12, 40, 36, 36 },
+ { 49, 81, 31, 22, 37, 33, 32 },
+ { 28, 81, 51, 26, 35, 33, 31 },
+ { 28, 51, 81, 28, 34, 33, 30 },
+ { 29, 30, 101, 28, 35, 33, 31 },
+ { 31, 22, 107, 28, 35, 33, 32 },
+ { 48, 27, 34, 10, 40, 16, 97 },
+ { 75, 27, 34, 3, 42, 26, 66 },
+ { 77, 47, 33, 12, 40, 32, 43 },
+ { 49, 75, 36, 21, 37, 33, 35 },
+ { 32, 72, 55, 25, 36, 33, 32 },
+ { 30, 49, 81, 27, 35, 33, 31 },
+ { 30, 32, 98, 28, 35, 32, 32 },
+ { 31, 24, 104, 28, 35, 32, 33 }
+ },
+ {
+ { 36, 29, 33, 43, 47, 29, 31 },
+ { 74, 20, 35, 19, 47, 34, 32 },
+ { 92, 35, 32, 29, 31, 40, 34 },
+ { 53, 80, 26, 33, 28, 36, 37 },
+ { 24, 91, 41, 31, 31, 31, 38 },
+ { 25, 57, 74, 31, 32, 30, 37 },
+ { 32, 28, 99, 32, 32, 29, 36 },
+ { 34, 20, 105, 33, 32, 30, 35 },
+ { 50, 26, 34, 33, 74, 30, 31 },
+ { 75, 28, 33, 23, 46, 47, 33 },
+ { 64, 58, 29, 30, 26, 46, 40 },
+ { 31, 85, 37, 31, 27, 33, 44 },
+ { 22, 67, 64, 30, 31, 28, 42 },
+ { 29, 35, 93, 31, 32, 27, 40 },
+ { 33, 20, 105, 32, 33, 27, 37 },
+ { 34, 19, 106, 33, 32, 29, 36 },
+ { 51, 29, 33, 25, 72, 51, 30 },
+ { 61, 42, 31, 30, 31, 60, 39 },
+ { 40, 70, 34, 32, 24, 41, 50 },
+ { 22, 72, 54, 30, 31, 27, 50 },
+ { 25, 44, 83, 30, 33, 25, 44 },
+ { 32, 23, 102, 32, 33, 26, 40 },
+ { 34, 18, 107, 32, 33, 28, 37 },
+ { 34, 19, 105, 33, 32, 30, 35 },
+ { 45, 35, 32, 30, 39, 79, 33 },
+ { 43, 53, 33, 35, 24, 53, 55 },
+ { 27, 67, 45, 32, 29, 27, 61 },
+ { 22, 53, 72, 30, 33, 22, 52 },
+ { 28, 31, 95, 31, 33, 25, 43 },
+ { 32, 20, 105, 32, 33, 27, 38 },
+ { 34, 18, 107, 32, 32, 29, 36 },
+ { 34, 20, 105, 33, 31, 31, 35 },
+ { 38, 40, 32, 35, 23, 72, 54 },
+ { 31, 55, 39, 34, 29, 32, 73 },
+ { 22, 57, 60, 31, 35, 18, 64 },
+ { 25, 39, 86, 31, 35, 22, 49 },
+ { 30, 24, 101, 32, 33, 27, 40 },
+ { 33, 19, 106, 32, 32, 30, 36 },
+ { 34, 18, 107, 33, 31, 31, 35 },
+ { 34, 20, 104, 33, 31, 32, 34 },
+ { 33, 42, 35, 34, 28, 39, 82 },
+ { 26, 51, 50, 33, 34, 18, 80 },
+ { 23, 46, 74, 31, 35, 20, 59 },
+ { 27, 32, 93, 32, 34, 26, 44 },
+ { 31, 22, 103, 32, 32, 30, 37 },
+ { 33, 19, 106, 33, 31, 31, 35 },
+ { 34, 19, 106, 33, 31, 32, 34 },
+ { 35, 21, 103, 34, 31, 32, 34 },
+ { 29, 41, 41, 33, 34, 20, 92 },
+ { 24, 44, 62, 34, 35, 18, 73 },
+ { 24, 37, 83, 34, 33, 25, 52 },
+ { 28, 28, 97, 33, 32, 30, 40 },
+ { 32, 23, 103, 33, 31, 32, 36 },
+ { 34, 20, 105, 34, 30, 33, 34 },
+ { 35, 20, 104, 34, 30, 33, 33 },
+ { 35, 22, 102, 34, 30, 33, 34 },
+ { 27, 38, 51, 34, 34, 20, 86 },
+ { 26, 37, 71, 35, 34, 24, 64 },
+ { 27, 33, 87, 35, 32, 30, 47 },
+ { 30, 28, 96, 34, 31, 32, 39 },
+ { 32, 24, 100, 35, 30, 32, 36 },
+ { 34, 23, 101, 34, 30, 33, 34 },
+ { 35, 23, 101, 34, 30, 32, 34 },
+ { 34, 24, 99, 35, 30, 33, 34 }
+ },
+ {
+ { 39, 30, 31, 67, 33, 34, 31 },
+ { 72, 21, 32, 43, 39, 33, 31 },
+ { 100, 23, 32, 35, 39, 34, 31 },
+ { 75, 63, 24, 32, 38, 34, 32 },
+ { 32, 98, 26, 29, 37, 35, 32 },
+ { 22, 77, 55, 29, 36, 35, 31 },
+ { 31, 37, 90, 31, 35, 35, 32 },
+ { 35, 22, 100, 33, 33, 36, 33 },
+ { 47, 29, 32, 74, 54, 32, 31 },
+ { 71, 24, 32, 60, 50, 36, 30 },
+ { 86, 31, 30, 46, 48, 37, 30 },
+ { 65, 63, 25, 34, 46, 39, 30 },
+ { 33, 85, 32, 28, 43, 40, 30 },
+ { 26, 64, 60, 27, 39, 41, 30 },
+ { 33, 33, 87, 29, 35, 41, 31 },
+ { 37, 23, 93, 32, 33, 41, 32 },
+ { 41, 32, 32, 45, 84, 32, 32 },
+ { 55, 31, 32, 50, 70, 40, 30 },
+ { 62, 37, 31, 45, 61, 45, 29 },
+ { 53, 55, 31, 36, 55, 48, 29 },
+ { 38, 63, 40, 29, 48, 50, 28 },
+ { 34, 49, 60, 27, 43, 51, 29 },
+ { 38, 30, 78, 28, 38, 50, 31 },
+ { 40, 24, 83, 30, 36, 48, 33 },
+ { 35, 33, 33, 29, 75, 58, 29 },
+ { 39, 35, 33, 34, 68, 59, 29 },
+ { 41, 39, 34, 36, 61, 62, 29 },
+ { 41, 43, 37, 33, 54, 64, 28 },
+ { 41, 43, 45, 30, 48, 65, 29 },
+ { 42, 36, 56, 27, 44, 63, 30 },
+ { 42, 30, 65, 27, 41, 60, 33 },
+ { 42, 28, 68, 28, 37, 56, 36 },
+ { 33, 34, 33, 31, 42, 88, 30 },
+ { 31, 36, 34, 31, 44, 84, 31 },
+ { 31, 37, 35, 32, 43, 83, 31 },
+ { 35, 35, 39, 32, 40, 82, 31 },
+ { 40, 32, 44, 31, 38, 81, 31 },
+ { 44, 30, 48, 30, 37, 78, 33 },
+ { 44, 30, 52, 28, 37, 72, 36 },
+ { 43, 30, 55, 29, 35, 66, 40 },
+ { 32, 33, 33, 34, 25, 85, 48 },
+ { 30, 34, 34, 33, 25, 88, 44 },
+ { 30, 34, 36, 34, 25, 90, 41 },
+ { 33, 32, 38, 34, 25, 90, 40 },
+ { 38, 29, 41, 34, 26, 88, 40 },
+ { 42, 29, 41, 33, 27, 85, 41 },
+ { 43, 30, 42, 31, 28, 80, 43 },
+ { 42, 31, 45, 31, 30, 72, 47 },
+ { 32, 33, 33, 33, 26, 54, 79 },
+ { 31, 32, 34, 35, 20, 68, 68 },
+ { 32, 32, 35, 36, 17, 76, 62 },
+ { 34, 31, 36, 36, 17, 79, 59 },
+ { 37, 29, 37, 36, 18, 78, 58 },
+ { 39, 29, 37, 35, 20, 77, 58 },
+ { 41, 30, 37, 34, 22, 74, 58 },
+ { 40, 31, 40, 32, 26, 68, 59 },
+ { 33, 31, 34, 33, 29, 31, 98 },
+ { 34, 30, 34, 35, 23, 45, 88 },
+ { 34, 31, 34, 36, 20, 54, 82 },
+ { 35, 31, 34, 36, 18, 59, 78 },
+ { 36, 31, 34, 37, 19, 60, 76 },
+ { 38, 30, 34, 36, 20, 61, 74 },
+ { 39, 31, 35, 35, 22, 60, 73 },
+ { 39, 31, 37, 34, 24, 59, 71 }
+ },
+ {
+ { 30, 33, 32, 55, 32, 32, 32 },
+ { 47, 30, 31, 29, 36, 32, 32 },
+ { 81, 28, 32, 28, 34, 32, 32 },
+ { 85, 46, 29, 32, 32, 33, 32 },
+ { 54, 82, 26, 32, 32, 33, 32 },
+ { 30, 90, 38, 31, 32, 33, 32 },
+ { 30, 56, 73, 31, 33, 32, 32 },
+ { 37, 21, 102, 32, 32, 32, 32 },
+ { 33, 32, 31, 68, 39, 31, 31 },
+ { 38, 32, 31, 43, 34, 33, 31 },
+ { 63, 30, 31, 29, 34, 32, 32 },
+ { 82, 37, 30, 29, 33, 32, 32 },
+ { 71, 63, 27, 31, 32, 33, 32 },
+ { 44, 86, 30, 30, 33, 33, 32 },
+ { 33, 72, 55, 30, 32, 32, 31 },
+ { 37, 37, 86, 31, 32, 33, 31 },
+ { 34, 33, 32, 60, 61, 29, 32 },
+ { 36, 33, 31, 56, 38, 32, 31 },
+ { 51, 30, 31, 38, 33, 33, 32 },
+ { 75, 31, 31, 30, 33, 33, 32 },
+ { 80, 47, 29, 30, 32, 33, 31 },
+ { 60, 73, 27, 30, 33, 33, 31 },
+ { 41, 78, 41, 30, 33, 32, 31 },
+ { 38, 53, 68, 30, 32, 33, 31 },
+ { 33, 33, 32, 43, 77, 35, 30 },
+ { 35, 33, 31, 55, 54, 29, 32 },
+ { 43, 32, 31, 46, 39, 31, 32 },
+ { 64, 30, 31, 35, 34, 33, 32 },
+ { 79, 37, 30, 31, 32, 33, 31 },
+ { 73, 57, 28, 30, 32, 33, 31 },
+ { 54, 73, 33, 30, 32, 33, 31 },
+ { 43, 64, 52, 30, 32, 33, 31 },
+ { 33, 33, 32, 34, 68, 58, 28 },
+ { 34, 33, 31, 45, 70, 33, 31 },
+ { 38, 33, 31, 48, 52, 29, 32 },
+ { 54, 31, 31, 40, 39, 31, 32 },
+ { 73, 32, 31, 34, 34, 33, 31 },
+ { 77, 45, 29, 31, 32, 32, 32 },
+ { 65, 63, 30, 31, 31, 33, 31 },
+ { 51, 66, 42, 30, 32, 33, 31 },
+ { 33, 32, 32, 34, 44, 81, 31 },
+ { 34, 33, 31, 38, 66, 52, 28 },
+ { 36, 33, 30, 44, 62, 34, 31 },
+ { 47, 31, 31, 43, 48, 30, 32 },
+ { 64, 31, 31, 38, 38, 32, 32 },
+ { 75, 38, 30, 33, 34, 32, 32 },
+ { 71, 53, 30, 31, 32, 33, 32 },
+ { 59, 61, 37, 30, 32, 33, 32 },
+ { 33, 32, 31, 35, 31, 71, 54 },
+ { 34, 33, 31, 37, 49, 70, 33 },
+ { 36, 33, 31, 41, 60, 48, 30 },
+ { 43, 32, 31, 43, 54, 35, 31 },
+ { 56, 31, 31, 40, 44, 32, 32 },
+ { 68, 35, 30, 36, 37, 32, 32 },
+ { 70, 45, 30, 33, 34, 33, 32 },
+ { 63, 55, 35, 31, 33, 33, 32 },
+ { 33, 32, 31, 33, 34, 36, 87 },
+ { 34, 32, 31, 36, 38, 62, 52 },
+ { 36, 33, 31, 39, 50, 57, 36 },
+ { 41, 33, 31, 41, 53, 43, 33 },
+ { 50, 33, 31, 41, 48, 36, 32 },
+ { 59, 35, 31, 37, 41, 34, 32 },
+ { 65, 42, 31, 35, 36, 33, 32 },
+ { 62, 49, 35, 33, 34, 34, 33 }
+ }
+};
+
+const uint8_t* ff_vvc_get_mip_matrix(const int size_id, const int mode_id)
+{
+ av_assert0(size_id < 3);
+ if (size_id == 0)
+ return &mip_matrix_4x4[mode_id][0][0];
+ if (size_id == 1)
+ return &mip_matrix_8x8[mode_id][0][0];
+ return &mip_matrix_16x16[mode_id][0][0];
+}
+
+// DCT-8
+#define DEFINE_DCT8_P4_MATRIX(a,b,c,d) \
+{ \
+ { a, b, c, d }, \
+ { b, 0, -b, -b }, \
+ { c, -b, -d, a }, \
+ { d, -b, a, -c }, \
+}
+
+#define DEFINE_DCT8_P8_MATRIX(a,b,c,d,e,f,g,h) \
+{ \
+ { a, b, c, d, e, f, g, h }, \
+ { b, e, h, -g, -d, -a, -c, -f }, \
+ { c, h, -e, -a, -f, g, b, d }, \
+ { d, -g, -a, -h, c, e, -f, -b }, \
+ { e, -d, -f, c, g, -b, -h, a }, \
+ { f, -a, g, e, -b, h, d, -c }, \
+ { g, -c, b, -f, -h, d, -a, e }, \
+ { h, -f, d, -b, a, -c, e, -g }, \
+}
+
+#define DEFINE_DCT8_P16_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \
+{ \
+ { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }, \
+ { b, e, h, k, n, 0, -n, -k, -h, -e, -b, -b, -e, -h, -k, -n }, \
+ { c, h, m, -p, -k, -f, -a, -e, -j, -o, n, i, d, b, g, l }, \
+ { d, k, -p, -i, -b, -f, -m, n, g, a, h, o, -l, -e, -c, -j }, \
+ { e, n, -k, -b, -h, 0, h, b, k, -n, -e, -e, -n, k, b, h }, \
+ { f, 0, -f, -f, 0, f, f, 0, -f, -f, 0, f, f, 0, -f, -f }, \
+ { g, -n, -a, -m, h, f, -o, -b, -l, i, e, -p, -c, -k, j, d }, \
+ { h, -k, -e, n, b, 0, -b, -n, e, k, -h, -h, k, e, -n, -b }, \
+ { i, -h, -j, g, k, -f, -l, e, m, -d, -n, c, o, -b, -p, a }, \
+ { j, -e, -o, a, -n, -f, i, k, -d, -p, b, -m, -g, h, l, -c }, \
+ { k, -b, n, h, -e, 0, e, -h, -n, b, -k, -k, b, -n, -h, e }, \
+ { l, -b, i, o, -e, f, -p, -h, c, -m, -k, a, -j, -n, d, -g }, \
+ { m, -e, d, -l, -n, f, -c, k, o, -g, b, -j, -p, h, -a, i }, \
+ { n, -h, b, -e, k, 0, -k, e, -b, h, -n, -n, h, -b, e, -k }, \
+ { o, -k, g, -c, b, -f, j, -n, -p, l, -h, d, -a, e, -i, m }, \
+ { p, -n, l, -j, h, -f, d, -b, a, -c, e, -g, i, -k, m, -o }, \
+}
+
+#define DEFINE_DCT8_P32_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F) \
+{ \
+ { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F }, \
+ { b, e, h, k, n, q, t, w, z, C, F, -E, -B, -y, -v, -s, -p, -m, -j, -g, -d, -a, -c, -f, -i, -l, -o, -r, -u, -x, -A, -D }, \
+ { c, h, m, r, w, B, 0, -B, -w, -r, -m, -h, -c, -c, -h, -m, -r, -w, -B, 0, B, w, r, m, h, c, c, h, m, r, w, B }, \
+ { d, k, r, y, F, -A, -t, -m, -f, -b, -i, -p, -w, -D, C, v, o, h, a, g, n, u, B, -E, -x, -q, -j, -c, -e, -l, -s, -z }, \
+ { e, n, w, F, -y, -p, -g, -c, -l, -u, -D, A, r, i, a, j, s, B, -C, -t, -k, -b, -h, -q, -z, E, v, m, d, f, o, x }, \
+ { f, q, B, -A, -p, -e, -g, -r, -C, z, o, d, h, s, D, -y, -n, -c, -i, -t, -E, x, m, b, j, u, F, -w, -l, -a, -k, -v }, \
+ { g, t, 0, -t, -g, -g, -t, 0, t, g, g, t, 0, -t, -g, -g, -t, 0, t, g, g, t, 0, -t, -g, -g, -t, 0, t, g, g, t }, \
+ { h, w, -B, -m, -c, -r, 0, r, c, m, B, -w, -h, -h, -w, B, m, c, r, 0, -r, -c, -m, -B, w, h, h, w, -B, -m, -c, -r }, \
+ { i, z, -w, -f, -l, -C, t, c, o, F, -q, -a, -r, E, n, d, u, -B, -k, -g, -x, y, h, j, A, -v, -e, -m, -D, s, b, p }, \
+ { j, C, -r, -b, -u, z, g, m, F, -o, -e, -x, w, d, p, -E, -l, -h, -A, t, a, s, -B, -i, -k, -D, q, c, v, -y, -f, -n }, \
+ { k, F, -m, -i, -D, o, g, B, -q, -e, -z, s, c, x, -u, -a, -v, w, b, t, -y, -d, -r, A, f, p, -C, -h, -n, E, j, l }, \
+ { l, -E, -h, -p, A, d, t, -w, -a, -x, s, e, B, -o, -i, -F, k, m, -D, -g, -q, z, c, u, -v, -b, -y, r, f, C, -n, -j }, \
+ { m, -B, -c, -w, r, h, 0, -h, -r, w, c, B, -m, -m, B, c, w, -r, -h, 0, h, r, -w, -c, -B, m, m, -B, -c, -w, r, h }, \
+ { n, -y, -c, -D, i, s, -t, -h, E, d, x, -o, -m, z, b, C, -j, -r, u, g, -F, -e, -w, p, l, -A, -a, -B, k, q, -v, -f }, \
+ { o, -v, -h, C, a, D, -g, -w, n, p, -u, -i, B, b, E, -f, -x, m, q, -t, -j, A, c, F, -e, -y, l, r, -s, -k, z, d }, \
+ { p, -s, -m, v, j, -y, -g, B, d, -E, -a, -F, c, C, -f, -z, i, w, -l, -t, o, q, -r, -n, u, k, -x, -h, A, e, -D, -b }, \
+ { q, -p, -r, o, s, -n, -t, m, u, -l, -v, k, w, -j, -x, i, y, -h, -z, g, A, -f, -B, e, C, -d, -D, c, E, -b, -F, a }, \
+ { r, -m, -w, h, B, -c, 0, c, -B, -h, w, m, -r, -r, m, w, -h, -B, c, 0, -c, B, h, -w, -m, r, r, -m, -w, h, B, -c }, \
+ { s, -j, -B, a, -C, -i, t, r, -k, -A, b, -D, -h, u, q, -l, -z, c, -E, -g, v, p, -m, -y, d, -F, -f, w, o, -n, -x, e }, \
+ { t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g, 0, g, -t, -t, g, 0, -g, t, t, -g }, \
+ { u, -d, B, n, -k, -E, g, -r, -x, a, -y, -q, h, -F, -j, o, A, -c, v, t, -e, C, m, -l, -D, f, -s, -w, b, -z, -p, i }, \
+ { v, -a, w, u, -b, x, t, -c, y, s, -d, z, r, -e, A, q, -f, B, p, -g, C, o, -h, D, n, -i, E, m, -j, F, l, -k }, \
+ { w, -c, r, B, -h, m, 0, -m, h, -B, -r, c, -w, -w, c, -r, -B, h, -m, 0, m, -h, B, r, -c, w, w, -c, r, B, -h, m }, \
+ { x, -f, m, -E, -q, b, -t, -B, j, -i, A, u, -c, p, F, -n, e, -w, -y, g, -l, D, r, -a, s, C, -k, h, -z, -v, d, -o }, \
+ { y, -i, h, -x, -z, j, -g, w, A, -k, f, -v, -B, l, -e, u, C, -m, d, -t, -D, n, -c, s, E, -o, b, -r, -F, p, -a, q }, \
+ { z, -l, c, -q, E, u, -g, h, -v, -D, p, -b, m, -A, -y, k, -d, r, -F, -t, f, -i, w, C, -o, a, -n, B, x, -j, e, -s }, \
+ { A, -o, c, -j, v, F, -t, h, -e, q, -C, -y, m, -a, l, -x, -D, r, -f, g, -s, E, w, -k, b, -n, z, B, -p, d, -i, u }, \
+ { B, -r, h, -c, m, -w, 0, w, -m, c, -h, r, -B, -B, r, -h, c, -m, w, 0, -w, m, -c, h, -r, B, B, -r, h, -c, m, -w }, \
+ { C, -u, m, -e, d, -l, t, -B, -D, v, -n, f, -c, k, -s, A, E, -w, o, -g, b, -j, r, -z, -F, x, -p, h, -a, i, -q, y }, \
+ { D, -x, r, -l, f, -a, g, -m, s, -y, E, C, -w, q, -k, e, -b, h, -n, t, -z, F, B, -v, p, -j, d, -c, i, -o, u, -A }, \
+ { E, -A, w, -s, o, -k, g, -c, b, -f, j, -n, r, -v, z, -D, -F, B, -x, t, -p, l, -h, d, -a, e, -i, m, -q, u, -y, C }, \
+ { F, -D, B, -z, x, -v, t, -r, p, -n, l, -j, h, -f, d, -b, a, -c, e, -g, i, -k, m, -o, q, -s, u, -w, y, -A, C, -E }, \
+}
+
+const int8_t ff_vvc_dct8_4x4[4][4] = DEFINE_DCT8_P4_MATRIX(84, 74, 55, 29);
+const int8_t ff_vvc_dct8_8x8[8][8] = DEFINE_DCT8_P8_MATRIX(86, 85, 78, 71, 60, 46, 32, 17);
+const int8_t ff_vvc_dct8_16x16[16][16] = DEFINE_DCT8_P16_MATRIX(88, 88, 87, 85, 81, 77, 73, 68, 62, 55, 48, 40, 33, 25, 17, 8);
+const int8_t ff_vvc_dct8_32x32[32][32] = DEFINE_DCT8_P32_MATRIX(90, 90, 89, 88, 87, 86, 85, 84, 82, 80, 78, 77, 74, 72, 68, 66, 63, 60, 56, 53, 50, 46, 42, 38, 34, 30, 26, 21, 17, 13, 9, 4);
+
+// DST-7
+#define DEFINE_DST7_P4_MATRIX(a,b,c,d) \
+{ \
+ { a, b, c, d }, \
+ { c, c, 0, -c }, \
+ { d, -a, -c, b }, \
+ { b, -d, c, -a }, \
+}
+
+#define DEFINE_DST7_P8_MATRIX(a,b,c,d,e,f,g,h) \
+{ \
+ { a, b, c, d, e, f, g, h }, \
+ { c, f, h, e, b, -a, -d, -g }, \
+ { e, g, b, -c, -h, -d, a, f }, \
+ { g, c, -d, -f, a, h, b, -e }, \
+ { h, -a, -g, b, f, -c, -e, d }, \
+ { f, -e, -a, g, -d, -b, h, -c }, \
+ { d, -h, e, -a, -c, g, -f, b }, \
+ { b, -d, f, -h, g, -e, c, -a }, \
+}
+
+#define DEFINE_DST7_P16_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p) \
+{ \
+ { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p }, \
+ { c, f, i, l, o, o, l, i, f, c, 0, -c, -f, -i, -l, -o }, \
+ { e, j, o, m, h, c, -b, -g, -l, -p, -k, -f, -a, d, i, n }, \
+ { g, n, l, e, -b, -i, -p, -j, -c, d, k, o, h, a, -f, -m }, \
+ { i, o, f, -c, -l, -l, -c, f, o, i, 0, -i, -o, -f, c, l }, \
+ { k, k, 0, -k, -k, 0, k, k, 0, -k, -k, 0, k, k, 0, -k }, \
+ { m, g, -f, -n, -a, l, h, -e, -o, -b, k, i, -d, -p, -c, j }, \
+ { o, c, -l, -f, i, i, -f, -l, c, o, 0, -o, -c, l, f, -i }, \
+ { p, -a, -o, b, n, -c, -m, d, l, -e, -k, f, j, -g, -i, h }, \
+ { n, -e, -i, j, d, -o, a, m, -f, -h, k, c, -p, b, l, -g }, \
+ { l, -i, -c, o, -f, -f, o, -c, -i, l, 0, -l, i, c, -o, f }, \
+ { j, -m, c, g, -p, f, d, -n, i, a, -k, l, -b, -h, o, -e }, \
+ { h, -p, i, -a, -g, o, -j, b, f, -n, k, -c, -e, m, -l, d }, \
+ { f, -l, o, -i, c, c, -i, o, -l, f, 0, -f, l, -o, i, -c }, \
+ { d, -h, l, -p, m, -i, e, -a, -c, g, -k, o, -n, j, -f, b }, \
+ { b, -d, f, -h, j, -l, n, -p, o, -m, k, -i, g, -e, c, -a }, \
+}
+
+#define DEFINE_DST7_P32_MATRIX(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F) \
+{ \
+ { a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F }, \
+ { c, f, i, l, o, r, u, x, A, D, F, C, z, w, t, q, n, k, h, e, b, -a, -d, -g, -j, -m, -p, -s, -v, -y, -B, -E }, \
+ { e, j, o, t, y, D, D, y, t, o, j, e, 0, -e, -j, -o, -t, -y, -D, -D, -y, -t, -o, -j, -e, 0, e, j, o, t, y, D }, \
+ { g, n, u, B, D, w, p, i, b, -e, -l, -s, -z, -F, -y, -r, -k, -d, c, j, q, x, E, A, t, m, f, -a, -h, -o, -v, -C }, \
+ { i, r, A, C, t, k, b, -g, -p, -y, -E, -v, -m, -d, e, n, w, F, x, o, f, -c, -l, -u, -D, -z, -q, -h, a, j, s, B }, \
+ { k, v, F, u, j, -a, -l, -w, -E, -t, -i, b, m, x, D, s, h, -c, -n, -y, -C, -r, -g, d, o, z, B, q, f, -e, -p, -A }, \
+ { m, z, z, m, 0, -m, -z, -z, -m, 0, m, z, z, m, 0, -m, -z, -z, -m, 0, m, z, z, m, 0, -m, -z, -z, -m, 0, m, z }, \
+ { o, D, t, e, -j, -y, -y, -j, e, t, D, o, 0, -o, -D, -t, -e, j, y, y, j, -e, -t, -D, -o, 0, o, D, t, e, -j, -y }, \
+ { q, E, n, -c, -t, -B, -k, f, w, y, h, -i, -z, -v, -e, l, C, s, b, -o, -F, -p, a, r, D, m, -d, -u, -A, -j, g, x }, \
+ { s, A, h, -k, -D, -p, c, v, x, e, -n, -F, -m, f, y, u, b, -q, -C, -j, i, B, r, -a, -t, -z, -g, l, E, o, -d, -w }, \
+ { u, w, b, -s, -y, -d, q, A, f, -o, -C, -h, m, E, j, -k, -F, -l, i, D, n, -g, -B, -p, e, z, r, -c, -x, -t, a, v }, \
+ { w, s, -d, -A, -o, h, E, k, -l, -D, -g, p, z, c, -t, -v, a, x, r, -e, -B, -n, i, F, j, -m, -C, -f, q, y, b, -u }, \
+ { y, o, -j, -D, -e, t, t, -e, -D, -j, o, y, 0, -y, -o, j, D, e, -t, -t, e, D, j, -o, -y, 0, y, o, -j, -D, -e, t }, \
+ { A, k, -p, -v, e, F, f, -u, -q, j, B, a, -z, -l, o, w, -d, -E, -g, t, r, -i, -C, -b, y, m, -n, -x, c, D, h, -s }, \
+ { C, g, -v, -n, o, u, -h, -B, a, D, f, -w, -m, p, t, -i, -A, b, E, e, -x, -l, q, s, -j, -z, c, F, d, -y, -k, r }, \
+ { E, c, -B, -f, y, i, -v, -l, s, o, -p, -r, m, u, -j, -x, g, A, -d, -D, a, F, b, -C, -e, z, h, -w, -k, t, n, -q }, \
+ { F, -a, -E, b, D, -c, -C, d, B, -e, -A, f, z, -g, -y, h, x, -i, -w, j, v, -k, -u, l, t, -m, -s, n, r, -o, -q, p }, \
+ { D, -e, -y, j, t, -o, -o, t, j, -y, -e, D, 0, -D, e, y, -j, -t, o, o, -t, -j, y, e, -D, 0, D, -e, -y, j, t, -o }, \
+ { B, -i, -s, r, j, -A, -a, C, -h, -t, q, k, -z, -b, D, -g, -u, p, l, -y, -c, E, -f, -v, o, m, -x, -d, F, -e, -w, n }, \
+ { z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m, -m, z, 0, -z, m, m, -z, 0, z, -m }, \
+ { x, -q, -g, E, -j, -n, A, -c, -u, t, d, -B, m, k, -D, f, r, -w, -a, y, -p, -h, F, -i, -o, z, -b, -v, s, e, -C, l }, \
+ { v, -u, -a, w, -t, -b, x, -s, -c, y, -r, -d, z, -q, -e, A, -p, -f, B, -o, -g, C, -n, -h, D, -m, -i, E, -l, -j, F, -k }, \
+ { t, -y, e, o, -D, j, j, -D, o, e, -y, t, 0, -t, y, -e, -o, D, -j, -j, D, -o, -e, y, -t, 0, t, -y, e, o, -D, j }, \
+ { r, -C, k, g, -y, v, -d, -n, F, -o, -c, u, -z, h, j, -B, s, -a, -q, D, -l, -f, x, -w, e, m, -E, p, b, -t, A, -i }, \
+ { p, -F, q, -a, -o, E, -r, b, n, -D, s, -c, -m, C, -t, d, l, -B, u, -e, -k, A, -v, f, j, -z, w, -g, -i, y, -x, h }, \
+ { n, -B, w, -i, -e, s, -F, r, -d, -j, x, -A, m, a, -o, C, -v, h, f, -t, E, -q, c, k, -y, z, -l, -b, p, -D, u, -g }, \
+ { l, -x, C, -q, e, g, -s, E, -v, j, b, -n, z, -A, o, -c, -i, u, -F, t, -h, -d, p, -B, y, -m, a, k, -w, D, -r, f }, \
+ { j, -t, D, -y, o, -e, -e, o, -y, D, -t, j, 0, -j, t, -D, y, -o, e, e, -o, y, -D, t, -j, 0, j, -t, D, -y, o, -e }, \
+ { h, -p, x, -F, y, -q, i, -a, -g, o, -w, E, -z, r, -j, b, f, -n, v, -D, A, -s, k, -c, -e, m, -u, C, -B, t, -l, d }, \
+ { f, -l, r, -x, D, -C, w, -q, k, -e, -a, g, -m, s, -y, E, -B, v, -p, j, -d, -b, h, -n, t, -z, F, -A, u, -o, i, -c }, \
+ { d, -h, l, -p, t, -x, B, -F, C, -y, u, -q, m, -i, e, -a, -c, g, -k, o, -s, w, -A, E, -D, z, -v, r, -n, j, -f, b }, \
+ { b, -d, f, -h, j, -l, n, -p, r, -t, v, -x, z, -B, D, -F, E, -C, A, -y, w, -u, s, -q, o, -m, k, -i, g, -e, c, -a }, \
+}
+
+const int8_t ff_vvc_dst7_4x4[4][4] = DEFINE_DST7_P4_MATRIX (29, 55, 74, 84);
+const int8_t ff_vvc_dst7_8x8[8][8] = DEFINE_DST7_P8_MATRIX (17, 32, 46, 60, 71, 78, 85, 86);
+const int8_t ff_vvc_dst7_16x16[16][16] = DEFINE_DST7_P16_MATRIX( 8, 17, 25, 33, 40, 48, 55, 62, 68, 73, 77, 81, 85, 87, 88, 88);
+const int8_t ff_vvc_dst7_32x32[32][32] = DEFINE_DST7_P32_MATRIX( 4, 9, 13, 17, 21, 26, 30, 34, 38, 42, 46, 50, 53, 56, 60, 63, 66, 68, 72, 74, 77, 78, 80, 82, 84, 85, 86, 87, 88, 89, 90, 90);
+
+const int8_t ff_vvc_lfnst_8x8[4][2][16][48] = {
+ { //0
+ {
+ { -117, 28, 18, 2, 4, 1, 2, 1, 32, -18, -2, 0, -1, 0, 0, 0, 14, -1, -3, 0, -1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, -1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 },
+ { -29, -91, 47, 1, 9, 0, 3, 0, -54, 26, -8, 3, 0, 1, 0, 0, 33, 5, -9, -1, -2, 0, -1, 0, -3, 3, 0, 0, 0, 0, 0, 0, 7, 2, -2, 0, -1, 1, 0, 0, 2, 1, -1, 0, 0, 0, 0, 0 },
+ { -10, 62, -11, -8, -2, -2, -1, -1, -95, 3, 32, 0, 4, 0, 2, 0, 32, -30, -4, 4, -1, 1, 0, 0, 6, 2, -5, 0, 0, 0, 0, 0, 6, -3, 0, 0, 2, 0, -1, 0, 2, -1, 0, 0, 1, 0, 0, 0 },
+ { -15, 15, -10, -2, 1, 0, 1, 0, 10, 112, -20, -17, -4, -4, -1, -2, -20, -26, 31, 1, 0, 0, 0, 0, 2, -16, -1, 6, 0, 1, 0, 0, 1, -4, 0, 0, 0, -3, 0, 1, 0, -1, 0, 0, 0, -2, 0, 0 },
+ { 32, 39, 92, -44, 4, -10, 1, -4, 26, 12, -15, 13, -5, 2, -2, 0, 29, -16, -22, 8, 0, 1, 0, 1, -20, 6, 4, -3, 1, 0, 0, 0, 1, -4, -3, 2, -4, 1, 0, 0, 1, -1, -2, 1, -2, 0, 0, 0 },
+ { -10, 1, 50, -15, 2, -3, 1, -1, -28, -15, 14, 6, 1, 1, 1, 0, -99, -4, 9, 5, 5, 2, 2, 1, 44, -10, -11, 1, -2, 0, -1, 0, -5, 4, -3, 0, 8, -1, -2, 0, -2, 1, -1, 0, 4, 0, -1, 0 },
+ { 1, -33, -11, -14, 7, -2, 2, 0, 29, -12, 37, -7, -4, 0, -1, 0, 6, -99, 3, 26, -1, 5, 0, 2, 14, 30, -27, -2, 1, -1, 0, -1, -6, 6, 6, -3, 1, 3, -3, 0, -1, 1, 1, 0, 0, 1, -1, 0 },
+ { 0, 6, -6, 21, -4, 2, 0, 0, -20, -24, -104, 30, 5, 5, 1, 2, -7, -46, 10, -14, 7, 0, 1, 0, 9, 21, 7, -6, -2, -1, 0, -1, 2, 2, 5, -2, 0, 3, 4, -1, 0, 0, 1, 0, 0, 1, 2, -1 },
+ { -13, -13, -37, -101, 29, -11, 8, -3, -12, -15, -20, 2, -11, 5, -2, 1, -12, 10, 26, 12, -6, 0, -1, 0, -32, -2, 11, 3, 3, -1, 1, 0, 11, -5, -1, 6, -4, 2, 1, 0, 3, -1, 1, 2, -1, 0, 0, 0 },
+ { 6, 1, -14, -36, 9, -3, 2, 0, 10, 9, -18, -1, -3, 1, 0, 0, 38, 26, -13, -1, -5, -1, -1, 0, 102, 3, -14, -1, -5, -1, -2, 0, -29, 10, 10, 0, 10, -4, -1, 1, -7, 1, 2, 1, 2, -1, 0, 0 },
+ { -12, -2, -26, -12, -9, 2, -1, 1, -3, 30, 4, 34, -4, 0, -1, 0, -30, 3, -92, 14, 19, 0, 3, 0, -11, 34, 21, -33, 1, -2, 0, -1, -9, -4, 18, 3, 2, 0, 0, -2, -1, -1, 3, 0, 0, 0, 0, -1 },
+ { 0, -3, 0, -4, -15, 6, -3, 1, -7, -15, -28, -86, 19, -5, 4, -1, -5, -17, -41, 42, -6, 2, -1, 1, -1, -40, 37, 13, -4, 2, -1, 1, -10, 13, -1, -4, 4, -4, 3, 4, -2, 2, -1, -1, 1, -1, 1, 2 },
+ { -1, 9, 13, 5, 14, -2, 2, -1, -8, 3, -4, -62, 4, 1, 1, 0, -12, 23, 16, -11, -17, 0, -1, 0, -11, 97, -3, -3, 0, -6, 0, -2, -21, -5, 23, 0, 2, -2, -1, 6, -3, -3, 1, 0, 0, 0, 0, 2 },
+ { 6, 2, -3, 2, 10, -1, 2, 0, 8, 3, -1, -20, 0, 1, 0, 0, -4, 4, -16, 0, -2, 0, 1, 0, 34, 23, 6, -7, -4, -2, -1, 0, 108, -5, -30, 6, -27, 10, 7, -2, 11, -3, -1, 1, -4, 1, 0, 1 },
+ { 6, 9, -2, 35, 110, -22, 11, -4, -2, 0, -3, 1, -18, 12, -3, 2, -5, -4, -22, 8, -25, 3, 0, 0, -3, -21, 2, -3, 9, -2, 1, 0, -7, 1, 3, -5, 3, 0, -1, 0, 0, 1, 0, -1, 1, 0, 0, 0 },
+ { -1, 7, -2, 9, -11, 5, -1, 1, -7, 2, -22, 4, -13, 0, -1, 0, 0, 28, 0, 76, 4, -6, 0, -2, -13, 5, -76, -4, 33, -1, 3, 0, 9, 18, -3, -35, -4, -1, 6, 1, 1, 2, 0, -3, -1, 0, 2, 0 },
+ },
+ {
+ { -108, 48, 9, 1, 1, 1, 0, 0, 44, -6, -9, -1, -1, 0, -1, 0, 9, -9, -1, 1, 0, 0, 0, 0, 3, -1, 1, 0, 0, 0, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0 },
+ { 55, 66, -37, -5, -6, -1, -2, 0, 67, -30, -20, 4, -2, 0, -1, 0, -31, -19, 14, 4, 1, 1, 1, 0, -6, 3, 5, -2, 0, 0, 0, 0, -7, -1, 1, 0, -1, 1, 1, 0, -2, -1, 1, 0, 0, 0, 0, 0 },
+ { 2, 86, -21, -13, -4, -2, -1, -1, -88, 5, 6, 4, 5, 1, 1, 0, 14, -5, 0, 3, 0, 0, 0, 0, 10, -5, -2, 0, -1, 0, 0, 0, 6, -5, 0, 1, 2, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0 },
+ { -24, -21, -38, 19, 0, 4, -1, 2, -23, -89, 31, 20, 2, 3, 1, 1, -30, 26, 36, -8, -2, -2, 0, -1, 14, 18, -7, -9, -1, -1, 0, 0, 1, 3, -2, -1, 3, 2, -2, -1, 0, 1, 0, 0, 1, 1, -1, 0 },
+ { 9, 20, 98, -26, -3, -5, 0, -2, -9, -26, 15, -16, 2, 0, 1, 0, -61, -3, -2, 3, 7, 1, 1, 0, 12, 16, -6, -1, 0, -1, 0, 0, 2, 0, -8, 1, 3, 1, -1, 1, 0, -1, -2, 0, 1, 0, -1, 0 },
+ { -21, -7, -37, 10, 2, 2, -1, 1, -10, 69, -5, -7, -2, -2, 0, -1, -93, 2, 19, 0, 3, 0, 2, 0, 17, 4, 0, 0, -1, 0, 0, 0, 5, -4, -2, 0, 4, -2, 0, 1, 0, 0, 0, 0, 2, -1, 0, 0 },
+ { -10, -25, 4, -17, 8, -2, 2, -1, -27, -17, -71, 25, 8, 2, 1, 1, -4, -66, 28, 36, -5, 3, 0, 1, -10, 20, 33, -13, -8, 0, 0, -1, 3, 6, -3, -7, -1, 3, 3, -1, 1, 0, -1, 0, 0, 1, 1, -1 },
+ { 2, 5, 10, 64, -9, 4, -3, 1, -4, 8, 62, 3, -17, 1, -2, 0, -3, -75, 5, -14, 1, 4, 0, 1, -36, 3, 18, -4, 4, 0, 1, 0, 1, 14, -2, -8, -2, 1, -3, 0, 2, 2, -1, -2, 0, 1, -1, 0 },
+ { -11, -15, -28, -97, 6, -1, 4, -1, 7, 3, 57, -15, 10, -2, 0, -1, -1, -27, 13, 6, 1, -1, 0, 0, -34, -6, 0, 3, 4, 1, 2, 0, -2, 8, 1, 5, -2, 0, -3, 1, 1, 1, 0, 2, -1, 0, -1, 0 },
+ { 9, 13, 24, -6, 7, -2, 1, -1, 16, 39, 20, 47, -2, -2, -2, 0, 28, 23, 76, -5, -25, -3, -3, -1, 6, 36, -7, -39, -4, -1, 0, -1, 2, -4, -18, -3, -1, -1, -2, -2, 1, -2, -2, 0, 0, 0, -1, -1 },
+ { -7, 11, 12, 7, 2, -1, 0, -1, -14, -1, -24, 11, 2, 0, 0, 0, -20, 48, 11, -13, -5, -2, 0, -1, -105, -19, 17, 0, 6, 2, 3, 0, -14, 8, 8, 2, 1, 2, -1, -2, 3, 0, -1, 0, 0, 0, 0, 0 },
+ { 0, 0, 7, -6, 23, -3, 3, -1, 5, 1, 18, 96, 13, -9, -1, -1, -21, -7, -42, 14, -24, -3, 0, 0, 11, -47, -7, 3, -5, 9, 1, 2, 0, -1, 19, -1, 1, 0, -1, -6, -1, 1, 2, 0, 1, 0, 0, -2 },
+ { -2, -6, -1, -10, 0, 1, 1, 0, -7, -2, -28, 20, -15, 4, -3, 1, -2, -32, -2, -66, 3, 7, 1, 2, -11, 13, -70, 5, 43, -2, 3, 0, 8, -14, -3, 43, -1, 2, 7, -1, 1, -2, 1, 3, -1, 1, 1, 0 },
+ { -1, 6, -16, 0, 24, -3, 1, -1, 2, 6, 6, 16, 18, -7, 1, -1, -3, 11, -63, 9, 4, -5, 2, -1, -22, 94, -4, -6, -4, -4, 1, -2, 10, 23, -19, -5, 0, -6, -4, 6, 3, -2, 1, 1, 0, -1, 0, 0 },
+ { -5, -6, -3, -19, -104, 18, -4, 3, 0, 6, 0, 35, -41, 20, -2, 2, -2, 10, -18, 16, 21, 3, -2, 0, -2, 11, 6, -10, 6, -3, -1, 0, -1, 5, -1, -6, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, -1 },
+ { -1, -2, 0, 23, -9, 0, -2, 0, 1, 1, 8, -1, 29, 1, 1, 0, 3, -6, 13, 76, 30, -11, -1, -2, -26, -8, -69, 7, -9, -7, 3, -1, -10, -34, -25, 13, -1, 0, 11, 5, 1, -1, 1, -2, 0, 0, 2, 0 },
+ }
+ },
+ { //1
+ {
+ { 110, -49, -3, -4, -1, -1, 0, -1, -38, -1, 10, 0, 2, 0, 1, 0, -9, 13, 1, -2, 0, 0, 0, 0, -4, 2, -3, 0, 0, 0, 0, 0, -2, 2, 0, 1, -1, 1, 0, 0, -1, 1, 0, 0, -1, 0, 0, 0 },
+ { -43, -19, 17, -1, 3, 0, 1, 0, -98, 46, 14, -1, 2, 0, 1, 0, 26, 26, -15, -3, -2, -1, -1, 0, 11, -7, -9, 2, 0, 0, 0, 0, 9, -3, -1, 2, 3, -3, 0, 0, 4, -1, 0, 0, 2, -1, 0, 0 },
+ { -19, 17, -7, 3, -2, 1, -1, 0, -32, -59, 29, 3, 4, 0, 2, 0, -72, 43, 34, -9, 3, -2, 1, -1, 13, 36, -18, -10, 0, -2, 0, -1, 3, 0, -12, 3, 6, 1, -3, 2, 1, -1, -2, 0, 3, 1, -1, 1 },
+ { -35, -103, 39, 1, 7, 0, 2, 0, 38, -13, 25, -6, 1, -1, 0, 0, -1, 7, 6, -7, 1, -1, 0, 0, -13, 14, 2, -4, 2, -1, 0, 0, -2, 11, -6, -2, -2, 4, -3, 0, 0, 3, -2, 0, -1, 1, -1, 0 },
+ { 9, 5, -6, -1, -1, 0, -1, 0, 42, 4, 21, -11, 1, -3, 1, -1, 21, 70, -32, -21, 0, -4, -1, -1, 34, -26, -57, 11, 4, 2, 0, 1, -4, -32, 5, 24, 1, -6, 12, 4, -3, -2, 4, -2, 0, -1, 0, 0 },
+ { -5, -5, -28, 9, -3, 2, -1, 1, -20, -78, 22, 16, 1, 3, 0, 1, 80, -6, 25, -5, -4, -1, -1, 0, 6, -24, 7, -9, 0, 0, 0, 0, -7, 3, 13, -4, -3, 5, 1, -5, -2, 3, 1, -2, -1, 2, -1, -2 },
+ { 14, 17, 27, -12, 1, -3, 1, -1, 8, 19, -13, 4, -2, 1, -1, 0, 48, -1, 48, -15, -4, -2, -1, -1, 1, 60, -28, -42, 5, -6, 1, -2, 11, -11, -51, 11, -2, -10, -2, 13, 2, -6, -4, 4, -2, -3, 2, 2 },
+ { 7, 35, 17, -4, -1, 0, 0, 0, 3, 8, 54, -17, 1, -2, 1, -1, 10, 14, -11, -34, 4, -4, 1, -1, -80, -7, -6, 2, 15, 0, 3, 0, -16, 46, 1, 3, 2, 7, -24, 0, 2, -2, -5, 8, 1, -1, -2, 2 },
+ { -13, -27, -101, 24, -8, 6, -3, 2, 11, 43, 6, 28, -6, 3, -1, 1, -3, 14, 21, -12, -7, -2, -1, -1, -23, 10, -4, -12, 3, 0, 1, 0, 2, 9, -10, 0, 1, -5, -4, 4, 2, -2, 2, 2, 0, -2, 1, 0 },
+ { -11, -13, -3, -10, 3, -1, 1, 0, -19, -19, -37, 8, 4, 2, 0, 1, -12, -30, 3, -9, 5, 0, 1, 0, -56, -9, -47, 8, 21, 1, 4, 1, -11, -30, 10, 59, -2, 8, 41, 8, 2, 5, 6, -7, -1, 3, 5, -2 },
+ { -4, -10, -24, -11, 3, -2, 0, -1, -6, -37, -45, -17, 8, -2, 2, -1, 17, 14, -58, 14, 15, 0, 2, 0, -10, 34, -7, 28, 4, -1, 1, 0, 23, 34, -31, 4, 10, -22, -30, 22, 4, -15, 9, 20, 2, -5, 9, 4 },
+ { -2, 1, 13, -17, 3, -5, 1, -2, 3, 0, -55, 22, 6, 1, 1, 0, 8, 74, 21, 40, -14, 0, -2, 0, -36, -8, 11, -13, -23, 1, -3, 0, -36, 6, 16, -14, 2, 19, -4, -12, -1, 0, -7, -3, 0, 2, -2, -1 },
+ { 3, 1, 5, -15, 1, -2, 1, -1, 7, 4, -7, 29, -1, 2, -1, 1, 8, 3, 12, -14, -9, -1, -1, 0, 4, 29, -15, 31, 10, 4, 1, 1, 61, 22, 55, 14, 13, 3, -9, -65, 1, -11, -21, -7, 0, 0, -1, 3 },
+ { -4, -8, -1, -50, 6, -4, 2, -2, -1, 5, -22, 20, 6, 1, 0, 0, -16, -15, 18, -29, -11, 2, -2, 1, 40, -45, -19, -22, 31, 2, 4, 1, -25, 41, 0, 12, 9, 7, -42, 12, -3, -14, 2, 28, 5, 1, 6, 2 },
+ { 5, -1, 26, 102, -13, 12, -4, 4, -4, -2, -40, -7, -23, 3, -5, 1, -1, 5, 8, -23, 7, 2, 1, 1, 10, -11, -13, -3, 12, -3, 2, 0, -9, 23, 4, 9, 14, 9, -14, -4, 0, -12, -7, 6, 3, 0, 6, 3 },
+ { -5, -6, -27, -22, -12, 0, -3, 0, -5, 8, -20, -83, 0, 0, 0, 0, 9, 7, 24, -20, 41, 3, 6, 1, 15, 20, 12, 11, 17, -9, 1, -2, -26, -1, 18, -1, -12, 32, 3, -18, -5, 10, -25, -5, -2, 1, -8, 10 },
+ },
+ {
+ { 80, -49, 6, -4, 1, -1, 1, -1, -72, 36, 4, 0, 1, 0, 0, 0, 26, 0, -12, 2, -2, 1, -1, 0, -7, -9, 6, 1, 0, 0, 0, 0, 3, 5, -1, -2, -2, -2, -1, 1, 1, 1, 0, 0, -1, -1, 0, 0 },
+ { -72, -6, 17, 0, 3, 0, 1, 0, -23, 58, -21, 2, -3, 1, -1, 0, 55, -46, -1, 6, -2, 1, -1, 0, -22, 7, 17, -7, 2, -1, 1, 0, 9, 5, -12, 1, -3, -4, 4, 2, 4, 1, -2, -1, -1, -1, 1, 0 },
+ { -50, 19, -15, 4, -1, 1, -1, 1, -58, -2, 30, -3, 4, -1, 2, 0, 6, 57, -34, 0, -2, 0, -1, 0, 34, -48, -2, 14, -4, 3, -1, 1, -10, 7, 21, -10, 6, 1, -11, 0, -1, -1, 4, 2, 3, 0, -2, -1 },
+ { -33, -43, 28, -7, 4, -2, 2, -1, -38, 11, -8, 4, 1, 1, 0, 0, -55, 24, 26, -5, 2, -1, 1, 0, 15, 46, -40, -1, -1, 0, -1, 0, 17, -38, 1, 17, -3, 11, 15, -11, 3, -1, -10, 1, 0, 1, 3, 2 },
+ { 10, 66, -21, -3, -3, 0, -1, 0, -53, -41, -2, 16, -1, 4, -1, 1, 36, -5, 41, -20, 3, -3, 1, -1, -30, 26, -32, -3, 7, -2, 2, -1, 15, -8, 1, 17, -1, -2, 4, -8, 2, 0, -1, 3, 0, 0, 0, -1 },
+ { 18, 14, 13, -9, 2, -2, 1, -1, 34, 32, -31, 12, -5, 2, -2, 1, 40, 4, -4, -9, -3, -2, -1, -1, 27, -31, -43, 19, -2, 3, -1, 1, 7, -49, 52, 10, -11, 22, 7, -26, -1, -6, -9, 6, -2, 2, 4, -2 },
+ { 21, 66, -1, 9, -4, 2, -1, 1, -21, 41, -30, -10, 0, -2, 0, -1, -35, -17, -3, 26, -6, 5, -2, 2, 56, 3, 18, -25, -1, -2, -1, -1, -15, -13, -27, 9, 9, -6, 20, 5, -3, 2, -6, -9, 3, -3, 1, 5 },
+ { 1, -6, -24, 17, -5, 3, -2, 1, 24, 10, 39, -21, 5, -4, 2, -1, 33, 32, -30, 4, -3, -1, -1, 0, -4, 13, -16, -10, 0, -1, 0, 0, 24, -26, -37, 33, 5, -32, 55, -5, -7, 22, -14, -22, 1, -9, -3, 13 },
+ { 9, 33, -24, 1, 4, 0, 1, 0, 6, 50, 26, 1, -10, 0, -2, 0, -27, 1, -28, -21, 16, -5, 3, -2, -23, 36, -2, 40, -17, 4, -3, 1, 43, -13, 4, -41, -19, -2, -24, 17, 11, -4, 8, 4, -3, -3, -3, -3 },
+ { -7, -9, -32, 14, -3, 3, -1, 1, -23, -28, 0, -5, -1, 0, 0, 0, -36, -59, -24, 14, 4, 2, 1, 1, -23, -26, 23, 26, -3, 5, 0, 2, 10, -26, 38, 7, -12, 11, 42, -22, -5, 20, -14, -15, -1, -2, 1, 6 },
+ { 6, 30, 69, -18, 5, -4, 3, -1, -3, -11, -34, -16, 9, -4, 2, -1, -16, 35, -35, 30, -9, 3, -2, 1, -57, -13, 6, 4, -5, 5, -1, 1, 28, 10, 4, 7, 0, -15, 7, -10, -1, 7, -2, 2, 1, -3, 0, 0 },
+ { 1, -8, 24, -3, 7, -2, 2, -1, -6, -51, -6, -4, -5, 0, -1, 0, 38, -1, 0, 25, 6, 2, 1, 1, 47, 20, 35, 1, -27, 1, -5, 0, 37, -37, -9, -47, -28, 5, 0, 18, 8, 6, 0, -8, -4, -3, -3, 1 },
+ { 4, 10, 4, 17, -9, 4, -2, 1, 5, 14, 32, -15, 9, -3, 2, -1, 7, 13, 19, 15, -8, 1, -1, 0, 3, 25, 30, -18, 1, -2, 0, -1, 11, 24, 22, -11, -3, 37, -13, -58, -5, 12, -63, 26, 9, -15, 11, 8 },
+ { -3, -9, -23, 10, -10, 3, -3, 1, -5, -14, -16, -27, 13, -5, 2, -1, -1, -13, -30, 11, -5, 2, -1, 0, -5, -8, -22, -16, 10, 0, 1, 0, 0, -29, -27, 6, -27, -10, -30, 9, -3, -10, -7, 77, 9, -13, 45, -8 },
+ { 2, 11, 22, 2, 9, -2, 2, 0, -6, -7, 20, -32, -3, -4, 0, -1, 13, -5, -28, 6, 18, -4, 3, -1, -26, 27, -14, 6, -20, 0, -2, 0, -76, -26, -4, -7, 12, 51, 5, 24, 7, -17, -16, -12, -5, 4, 2, 13 },
+ { 2, -3, 8, 14, -5, 3, -1, 1, -2, -11, 5, -18, 8, -3, 2, -1, 12, -23, -19, 22, 2, 0, 1, 0, 23, 41, -7, 35, -10, 4, -1, 1, 5, 7, 23, 5, 69, -38, -8, -32, -15, -31, 24, 11, 2, 18, 11, -15 },
+ }
+ },
+ { //2
+ {
+ { -121, 33, 4, 4, 1, 2, 0, 1, -1, -1, 1, 0, 0, 0, 0, 0, 24, -5, -1, -1, 0, 0, 0, 0, 5, -1, 0, 0, 0, 0, 0, 0, 3, -1, 0, 0, 2, -1, 0, 0, 2, -1, 0, 0, 1, 0, 0, 0 },
+ { 0, -2, 0, 0, 0, 0, 0, 0, 121, -23, -7, -3, -2, -1, -1, 0, 17, 1, -2, 0, 0, 0, 0, 0, -27, 4, 2, 0, 0, 0, 0, 0, -12, 2, 1, 0, -5, 1, 0, 0, -1, 0, 0, 0, -2, 0, 0, 0 },
+ { -20, 19, -5, 2, -1, 1, 0, 0, 16, 3, -2, 0, 0, 0, 0, 0, -120, 14, 8, 1, 3, 1, 1, 0, -18, -2, 3, 0, 1, 0, 0, 0, 17, -3, -1, 0, 6, -1, -1, 0, 2, 0, 0, 0, 2, 0, 0, 0 },
+ { 32, 108, -43, 10, -9, 3, -3, 1, 4, 19, -7, 1, -1, 0, 0, 0, 11, -30, 9, -2, 1, -1, 0, 0, 0, -8, 2, 0, 0, 0, 0, 0, -7, -1, 2, 0, -3, -1, 1, 0, -2, -2, 1, 0, 0, 0, 0, 0 },
+ { -3, 0, -1, 0, 0, 0, 0, 0, -29, 11, -2, 1, 0, 0, 0, 0, 12, 7, -1, 0, 0, 0, 0, 0, -117, 12, 9, 1, 3, 0, 1, 0, -32, -3, 3, 0, 12, -2, -1, 0, 7, 0, 0, 0, 1, 0, 0, 0 },
+ { -4, -12, -3, 1, -1, 0, 0, 0, 19, 105, -31, 7, -6, 1, -2, 0, 9, 46, -6, 0, 0, 0, 0, 0, 8, -29, 9, -3, 1, 0, 0, 0, -3, -19, 3, 0, -4, -6, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0 },
+ { 7, 1, 2, 0, 0, 0, 0, 0, 4, 3, -2, 0, 0, 0, 0, 0, 22, -8, 1, -1, 0, 0, 0, 0, -28, -9, 4, 0, 1, 0, 0, 0, 117, -10, -8, 0, 32, 1, -4, 0, 3, 1, -1, 0, -3, 1, 0, 0 },
+ { -8, -31, 14, -4, 3, -1, 1, 0, 9, 43, 0, 1, -1, 0, 0, 0, -13, -105, 17, -2, 2, 0, 0, 0, -8, -25, -3, 0, 0, 0, 0, 0, -7, 32, -5, 1, -1, 4, 0, 0, 2, -1, 0, 0, 1, 0, -1, 0 },
+ { -15, -43, -100, 23, -12, 6, -4, 2, -6, -17, -48, 10, -5, 2, -1, 1, 1, -5, 19, -6, 3, -1, 1, 0, 2, 7, 15, -3, 1, -1, 0, 0, 4, 10, 5, -1, 0, 3, 1, 0, -2, 1, 2, 0, -1, 1, 1, 0 },
+ { -3, 1, 2, 0, 0, 0, 0, 0, -6, 3, 1, 0, 0, 0, 0, 0, 0, 3, -2, 0, 0, 0, 0, 0, -20, 8, -2, 0, 0, 0, 0, 0, 30, 13, -3, 0, -116, 6, 10, 0, -35, -5, 4, 0, -3, -1, 0, 0 },
+ { -1, -6, -3, 2, -1, 0, 0, 0, -6, -35, 9, 0, 2, 0, 0, 0, 1, -6, 11, -2, 2, 0, 1, 0, -9, -100, 17, -1, 1, 0, 0, 0, -10, -63, 1, 2, -17, 3, -4, 0, -1, 9, -1, 0, 3, 4, -1, 0 },
+ { -5, -14, -48, 2, -5, 1, -2, 0, 10, 24, 99, -17, 10, -4, 3, -1, 4, 14, 32, 0, 2, 0, 1, 0, -4, 0, -39, 6, -4, 1, -1, 0, 2, -3, -4, 0, 2, -2, -2, 0, 0, 0, -1, 0, 0, -1, -1, 0 },
+ { -2, 0, 2, 0, 0, 0, 0, 0, -2, 0, 1, 0, 0, 0, 0, 0, -1, -1, 1, -1, 0, 0, 0, 0, -1, -4, 2, 0, 0, 0, 0, 0, -8, -2, -1, 1, 30, 4, -4, 1, -102, 4, 8, -1, -69, -2, 6, -1 },
+ { -2, -10, -4, 0, 0, 0, 0, 0, 3, 11, -1, -1, 0, 0, 0, 0, -6, -40, -15, 6, -2, 1, 0, 0, 5, 57, -6, 2, 0, 0, 0, 0, 1, -95, 18, -6, -10, -34, -2, 0, -4, 17, -2, 0, 0, 2, 1, 0 },
+ { -2, -3, -25, -2, -3, 0, -1, 0, -1, -3, -1, 4, -2, 2, 0, 1, -7, -8, -97, 17, -9, 3, -3, 1, -8, -26, -61, -1, -3, -1, -1, -1, 2, 10, 24, -7, 5, 9, 19, -1, 0, 1, 4, 0, -2, 0, 1, 0 },
+ { 4, -4, 28, 103, -42, 24, -9, 7, 1, 2, 4, 0, 3, -1, 0, 0, -1, 0, -9, -42, 17, -9, 3, -2, -1, 1, -14, 6, -4, 2, -1, 0, -1, -2, -4, 4, 0, 3, 1, -1, 0, 2, 0, -2, 2, 0, 0, 0 },
+ },
+ {
+ { 87, -41, 3, -4, 1, -1, 0, -1, -73, 28, 2, 1, 1, 1, 0, 0, 30, -5, -6, 1, -1, 0, 0, 0, -8, -3, 3, 0, 0, 0, 0, 0, 3, 2, -1, 0, -2, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0 },
+ { -75, 4, 7, 0, 2, 0, 1, 0, -41, 36, -7, 3, -1, 1, 0, 0, 72, -29, -2, 0, -1, 0, -1, 0, -37, 6, 7, -2, 1, 0, 0, 0, 12, 3, -4, 0, -3, -2, 1, 0, 4, 0, 0, 0, -1, 0, 0, 0 },
+ { 26, -44, 22, -6, 4, -2, 1, -1, 77, 24, -22, 2, -4, 0, -1, 0, 7, -38, 10, 0, 1, 0, 0, 0, -51, 27, 4, -3, 2, -1, 1, 0, 31, -5, -8, 3, -14, 0, 5, -1, 6, 1, -3, 0, -4, -1, 1, 0 },
+ { -39, -68, 37, -7, 6, -2, 2, 0, -9, 56, -21, 1, -2, 0, -1, 0, -45, 4, -3, 6, -1, 2, 0, 1, 49, -13, 3, -3, -1, 0, 0, 0, -19, 2, 0, 0, 5, 1, 1, 0, -2, 0, -1, 0, 1, 0, 0, 0 },
+ { 10, -20, 2, 0, 1, 0, 0, 0, 50, -1, 8, -5, 1, -1, 0, 0, 66, 17, -24, 4, -3, 1, -1, 0, 13, -49, 15, 1, 0, 0, 0, 0, -53, 34, 6, -5, 30, -7, -11, 3, -11, -2, 5, 1, 4, 2, -1, -1 },
+ { -21, -45, 8, -2, 3, -1, 1, 0, -7, -30, 26, -8, 3, -1, 1, -1, -9, 69, -33, 5, -2, 0, -1, 0, -44, -31, 10, 7, -2, 2, 0, 1, 49, 7, 2, -6, -23, -3, -2, 2, 9, 4, 0, 0, -2, -1, -1, 0 },
+ { -4, -2, -55, 28, -8, 5, -3, 2, -2, 37, 43, -19, 1, -2, 1, -1, -47, -34, -27, 5, 4, -1, 1, 0, -39, -2, 27, 4, -2, 1, 0, 0, -11, 32, -8, -7, 27, -12, -6, 6, -13, 0, 4, -3, 3, -1, -2, 1 },
+ { 2, 19, 47, -23, 6, -4, 2, -1, -23, -22, -44, 17, -2, 2, -1, 0, -33, 3, 22, -2, -4, 1, -1, 0, -58, -17, 6, -6, 7, -1, 1, 0, -23, 40, -2, 5, 43, -11, -8, -1, -18, -4, 5, 2, 4, 3, 0, -1 },
+ { -19, -62, -9, 3, 0, 0, 0, 0, -12, -56, 27, -7, 3, -1, 1, 0, 7, -8, 16, -6, 4, -2, 1, -1, -15, 54, -23, 2, -1, 0, 0, 0, -42, -25, 4, 6, 34, 8, 2, -2, -15, -1, 0, -1, 3, 2, 0, 1 },
+ { 1, 9, -5, 0, -1, 0, 0, 0, 0, 22, -1, 2, 0, 1, 0, 0, -13, 17, 0, -2, 0, -1, 0, 0, -46, -10, -10, 4, -1, 1, 0, 0, -80, -27, 20, -4, -66, 23, -2, -2, 20, -3, -2, 3, -14, 2, 3, -1 },
+ { 5, 17, -9, 0, -2, 1, 0, 0, 13, 54, -2, 7, -1, 1, 0, 0, 4, 51, -3, -6, -1, -1, 0, 0, -20, 6, -34, 9, -2, 2, -1, 0, 16, -52, 28, 1, 59, 15, -8, -5, -28, -7, 2, 2, 10, 3, 0, -1 },
+ { 7, 27, 56, -2, 10, -3, 3, -1, -2, -6, 8, -28, 3, -4, 1, -1, -1, -4, -68, 35, -5, 5, -2, 1, 0, 35, 43, -4, -6, 1, -1, 0, -14, -38, -12, -10, 9, 5, 7, 6, -9, 7, -4, -3, 4, -4, 0, 3 },
+ { 0, 0, 19, -4, 3, -2, 2, -1, -3, -13, 10, -4, 1, 0, 0, 0, -6, -37, -18, -5, 2, -2, 1, -1, 6, -6, -7, 25, -6, 4, -1, 1, 16, 10, 55, -24, 15, 46, -52, 1, 35, -43, 10, 12, -23, 13, 5, -8 },
+ { -3, 0, -27, -80, 40, -16, 6, -4, 4, 3, 31, 61, -22, 7, -1, 1, -4, -7, -26, -6, -10, 6, -4, 1, 3, 8, 14, -18, 15, -5, 2, -1, -2, -4, -1, 13, 0, 2, -4, -3, 3, -1, 2, 1, -2, 0, -2, -1 },
+ { 1, 2, -8, 6, -1, 1, 0, 0, 2, 8, -5, -1, 0, 0, 0, 0, 1, 24, 3, 5, -1, 1, 0, 0, -3, 12, 6, -10, 1, -1, 0, 0, -9, -1, -25, 10, 45, -11, 18, 2, 86, 1, -13, -4, -65, -6, 7, 2 },
+ { -4, -18, -57, 8, -8, 1, -3, 0, -5, -20, -69, 7, -6, 2, -2, 1, 1, 4, 0, 33, -7, 5, -2, 1, 0, -9, 53, -22, 3, -1, 0, 0, 4, -27, -2, -9, 5, 36, -13, 5, -7, -17, 1, 2, 4, 6, 4, -1 },
+ }
+ },
+ { //3
+ {
+ { -115, 37, 9, 2, 2, 1, 1, 0, 10, -29, 8, 0, 1, 0, 1, 0, 23, -8, -8, 1, -1, 0, 0, 0, 3, 3, -2, -1, 0, 0, 0, 0, 4, 0, 0, -1, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0 },
+ { 15, 51, -18, 0, -3, 0, -1, 0, -95, 7, 34, -3, 5, -1, 2, 0, 23, -47, 1, 6, 0, 1, 0, 1, 8, 5, -12, 0, -1, 0, 0, 0, 3, -3, 1, -1, 2, 1, -2, 0, 1, -1, 0, 0, 1, 1, -1, 0 },
+ { 29, -22, 16, -6, 3, -2, 1, -1, -4, -80, 12, 15, 0, 3, 0, 1, 45, 7, -59, 7, -2, 1, -1, 0, -15, 41, -3, -16, 2, -3, 0, -1, 1, 0, 7, -2, -3, 6, 1, -2, 0, 0, 1, 0, -1, 2, 0, -1 },
+ { -36, -98, 25, 5, 4, 1, 2, 1, -59, 11, -17, 1, 1, 1, 0, 0, 6, -13, 7, -3, 0, 0, 0, 0, 14, -4, -14, 3, -1, 0, 0, 0, 2, 8, -3, -5, 2, 0, 0, 0, 0, 3, 0, -1, 1, 0, 0, 0 },
+ { -6, 18, 3, -3, -1, 0, 0, 0, -50, -5, -38, 12, 0, 2, 0, 1, 3, 67, -7, -40, 3, -6, 1, -3, -12, -13, 65, -3, -10, 0, -1, 0, 9, -20, -5, 22, -2, 0, 0, -1, 2, -3, -2, 3, -1, 0, 1, 0 },
+ { 4, 15, 52, -13, 5, -3, 2, -1, -17, -45, 16, 24, -2, 4, -1, 2, -87, -8, -14, 7, 8, 1, 2, 0, 23, -35, -6, -3, 1, 1, 0, 0, 2, 5, -17, 0, 3, -1, -1, -5, 0, 1, -4, 0, 1, 0, 0, -2 },
+ { -20, -7, -43, 4, 0, 1, -1, 1, -7, 35, 0, 12, -4, 1, -1, 0, -51, -2, -57, 5, 15, 0, 4, 0, 7, 39, 5, -55, 1, -7, 1, -3, 1, -10, 41, 2, 4, -3, -2, 3, -1, -2, 7, 1, 1, -1, -1, 0 },
+ { 4, 29, 1, 26, -5, 4, -2, 1, -17, -7, -73, 6, 6, 2, 1, 1, -5, 21, -3, 5, -1, -3, 0, -1, -11, 2, -52, -3, 27, -2, 5, 0, 0, 27, 8, -58, 2, -5, 25, 3, 0, 3, 0, -5, 0, -2, 7, 0 },
+ { 12, 13, 10, 2, -1, 3, -1, 1, 17, -2, -46, 12, 7, 0, 2, 0, 16, -45, -9, -53, 6, 1, 1, 0, 70, 16, 8, -4, -37, 1, -7, 0, -12, 29, 3, 21, 4, 0, 5, -1, -3, 4, 1, 4, 2, 0, 1, 0 },
+ { 5, 20, 90, -17, 4, -3, 2, -1, 6, 66, 8, 28, -7, 3, -1, 1, 29, 5, -19, 12, 9, -1, 1, 0, -10, 14, -1, -13, 7, 0, 1, 0, 0, -6, 13, -4, 0, -4, 1, 5, 0, -1, -1, 1, 0, -1, 0, 0 },
+ { -3, -4, -34, -12, 2, -1, -1, 0, 5, 25, 11, 43, -10, 4, -2, 1, 23, 20, -40, 12, 21, -3, 4, -1, 25, -28, -10, 5, 8, 6, 0, 2, -4, 21, -64, -8, -5, 19, 10, -48, 3, -1, 10, -3, 0, 4, 3, -6 },
+ { -1, -3, 2, 19, -2, 4, -1, 2, 9, 3, -35, 22, 11, 1, 2, 0, -7, -65, -19, -22, 11, 4, 2, 1, -75, -18, 3, -1, -10, 2, 0, 1, 2, -35, -27, 4, 1, 8, -17, -19, 3, 0, 3, -6, 0, 2, -1, -2 },
+ { 10, -4, -6, 12, 5, 1, 1, 0, 11, -9, -12, -2, -7, 0, -1, 0, 33, -10, -4, 18, 18, -4, 4, -1, 28, -72, 1, -49, 15, 2, 2, 1, 56, -23, 22, -1, 4, -1, -15, 26, 6, 4, -10, 0, 0, 2, -3, 2 },
+ { 4, 6, 14, 53, -4, 4, 0, 2, 0, -1, -20, -13, 3, 2, -1, 1, -3, 1, -5, 35, -16, -6, -1, -2, 46, 29, 13, 21, 37, -5, 4, -1, -10, -53, -18, 8, 9, 12, -41, -25, -2, 2, 13, -16, 4, 1, -5, 1 },
+ { 2, 9, 13, 37, 19, 6, 2, 2, -9, -3, -9, -28, -20, -4, -3, -1, 1, 18, 9, 28, 24, 6, 2, 2, -20, -5, -25, -33, -36, 9, -2, 2, -13, 42, 1, 57, -22, -2, -25, -28, 5, 6, 19, -12, -5, -3, -2, 4 },
+ { 3, -3, 12, 84, -12, 8, -2, 3, 6, 13, 50, -1, 45, 1, 7, 0, -2, 18, -22, -37, -13, 14, 0, 3, 1, -12, -3, 2, -15, -8, 1, -1, 19, 14, -4, -12, -4, 5, 17, 8, 2, -4, -4, 4, -2, 2, 1, 0 },
+ },
+ {
+ { 109, -26, -8, -3, -2, -1, -1, 0, -50, 28, 2, 1, 0, 0, 0, 0, -18, -8, 6, 0, 1, 0, 1, 0, 6, -2, -3, 0, 0, 0, 0, 0, -3, 2, 1, -1, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, 0 },
+ { -39, 31, -5, 2, -1, 1, 0, 0, -95, 6, 18, 0, 4, 0, 1, 0, 32, -49, 5, 1, 1, 0, 0, 0, 27, -1, -14, 2, -2, 1, -1, 0, 3, 5, -3, -2, 4, 1, -1, -1, 2, 0, 0, 0, 2, 0, 0, 0 },
+ { 29, -3, -2, -2, 0, 0, 0, 0, 0, -41, 9, 0, 2, 0, 1, 0, 86, 4, -33, 2, -6, 1, -2, 0, -32, 58, 1, -7, 0, -2, 0, -1, -14, -8, 20, 0, -2, -3, 0, 4, -1, -1, 0, 0, -1, 1, 0, 0 },
+ { 18, 96, -23, 2, -5, 1, -2, 0, -10, 6, 10, -2, 1, -1, 1, 0, -14, 26, 2, -4, 1, -1, 0, 0, -43, -9, 35, -2, 4, -1, 1, 0, 14, -40, 1, 10, 2, 1, -10, 1, 2, -4, -1, -1, 0, 0, -1, 0 },
+ { -29, -60, 16, -2, 3, -1, 1, 0, -52, 9, -17, 5, -2, 1, -1, 1, 13, 56, -2, -9, 0, -2, 0, -1, -34, -18, 41, 0, 3, 0, 1, 0, 19, -36, -10, 13, 3, 6, -14, -1, 3, 1, -1, -3, 1, 1, -1, -1 },
+ { -23, -5, -15, 5, -2, 1, -1, 1, 2, 79, -13, -4, -2, -1, -1, 0, -9, 1, 5, -1, 1, 0, 0, 0, -4, 49, 2, -14, 1, -3, 0, -1, -31, -14, 56, -1, 13, -37, -4, 20, -2, 2, -10, 0, 2, -4, 0, -1 },
+ { -7, -3, 12, -3, 3, -1, 1, 0, -31, -62, 8, 7, 0, 2, 0, 1, -75, 9, -45, 5, -1, 1, -1, 0, 14, 35, 0, -23, 2, -5, 1, -2, 1, -8, 32, -1, 7, -12, -4, 10, 0, 2, -6, -1, 2, 0, 0, -2 },
+ { 1, -26, 5, 0, 1, 0, 1, 0, 24, -3, 43, -6, 4, -2, 1, -1, -7, -64, 9, 14, 0, 3, 0, 1, -12, -4, 5, 3, -1, 1, 0, 0, 8, -59, -3, 26, 14, 6, -58, 6, -5, 17, -7, -18, 3, 3, -1, -5 },
+ { 11, 14, 6, -3, 1, -1, 1, 0, 10, -7, -9, 3, -2, 1, -1, 0, 22, 21, 1, -21, 2, -4, 1, -2, 92, 1, 53, 0, -9, 1, -2, 0, -21, -11, 1, 40, -5, -4, -24, 5, -4, 5, -6, -5, 0, 0, 0, -3 },
+ { -10, -11, -47, 3, -4, 1, -1, 0, 5, 28, 11, -2, -1, 0, 0, 0, -12, -2, -38, 2, 0, 1, 0, 0, 16, 38, 11, -16, -1, -3, 0, -2, 12, -9, -22, 7, -8, 60, 4, -36, -6, -15, 54, 7, 3, -7, -8, 14 },
+ { -8, -24, -99, 11, -10, 3, -4, 1, -5, -36, 19, -26, 4, -5, 1, -2, 0, 25, 41, 5, -3, 1, 0, 0, 10, -5, -7, 12, 2, 1, 0, 0, -1, 1, 9, -3, -3, -14, -3, 12, 2, 4, -13, -2, -1, 3, 2, -4 },
+ { -5, 1, -1, 0, 1, 0, 0, 0, -10, -14, -6, 8, 0, 1, 0, 0, -17, -2, 7, -5, 3, -1, 0, 0, -16, 13, 3, 31, -1, 6, 0, 2, -93, -15, -46, -3, 23, -19, 0, -47, 8, 4, 8, 3, 2, 3, 0, 0 },
+ { 1, 12, -20, 21, -4, 5, -2, 2, -5, -2, -75, 9, -1, 2, -1, 1, -1, -2, -16, -4, 0, -1, 0, 0, -7, 7, -31, 0, 3, 0, 0, 0, 4, 11, -12, 4, -12, 14, -50, -1, -8, 32, -4, -54, 2, 0, 30, -15 },
+ { 2, -9, -18, 8, -3, 3, -1, 1, 3, -25, -62, -6, 0, -2, 0, -1, -6, -61, 14, -51, 2, -6, 0, -2, -19, 0, 40, -7, -17, 0, -3, 0, 13, -4, 11, 9, 17, 0, 24, 5, 1, -12, 4, 28, 0, 0, -15, 8 },
+ { 4, 9, 39, 18, 0, 2, 0, 1, -6, -16, -22, -37, 5, -5, 1, -2, -5, 15, 63, 9, -16, 0, -3, 0, 18, 42, -18, 27, 15, 1, 3, 1, 12, -34, 9, -24, 4, 28, -2, 4, -11, -4, 30, 2, 5, -13, -4, 18 },
+ { -7, -2, 15, -6, 1, -1, 1, -1, -11, -3, 22, -14, 0, -2, 1, -1, -18, -7, 30, -9, -4, 0, -1, 0, -35, 23, 23, 10, -17, 1, -3, 0, -19, 53, 6, 48, -65, 12, -12, 11, -8, -16, 10, -21, -2, -12, 6, 2 },
+ }
+ }
+};
+
+const int8_t ff_vvc_lfnst_4x4[4][2][16][16] = {
+ { //0
+ {
+ { 108, -44, -15, 1, -44, 19, 7, -1, -11, 6, 2, -1, 0, -1, -1, 0 },
+ { -40, -97, 56, 12, -11, 29, -12, -3, 18, 18, -15, -3, -1, -3, 2, 1 },
+ { 25, -31, -1, 7, 100, -16, -29, 1, -54, 21, 14, -4, -7, 2, 4, 0 },
+ { -32, -39, -92, 51, -6, -16, 36, -8, 3, 22, 18, -15, 4, 1, -5, 2 },
+ { 8, -9, 33, -8, -16, -102, 36, 23, -4, 38, -27, -5, 5, 16, -8, -6 },
+ { -25, 5, 16, -3, -38, 14, 11, -3, -97, 7, 26, 1, 55, -10, -19, 3 },
+ { 8, 9, 16, 1, 37, 36, 94, -38, -7, 3, -47, 11, -6, -13, -17, 10 },
+ { 2, 34, -5, 1, -7, 24, -25, -3, 8, 99, -28, -29, 6, -43, 21, 11 },
+ { -16, -27, -39, -109, 6, 10, 16, 24, 3, 19, 10, 24, -4, -7, -2, -3 },
+ { -9, -10, -34, 4, -9, -5, -29, 5, -33, -26, -96, 33, 14, 4, 39, -14 },
+ { -13, 1, 4, -9, -30, -17, -3, -64, -35, 11, 17, 19, -86, 6, 36, 14 },
+ { 8, -7, -5, -15, 7, -30, -28, -87, 31, 4, 4, 33, 61, -5, -17, 22 },
+ { -2, 13, -6, -4, -2, 28, -13, -14, -3, 37, -15, -3, -2, 107, -36, -24 },
+ { 4, 9, 11, 31, 4, 9, 16, 19, 12, 33, 32, 94, 12, 0, 34, -45 },
+ { 2, -2, 8, -16, 8, 5, 28, -17, 6, -7, 18, -45, 40, 36, 97, -8 },
+ { 0, -2, 0, -10, -1, -7, -3, -35, -1, -7, -2, -32, -6, -33, -16, -112 },
+ },
+ {
+ { 119, -30, -22, -3, -23, -2, 3, 2, -16, 3, 6, 0, -3, 2, 1, 0 },
+ { -27, -101, 31, 17, -47, 2, 22, 3, 19, 30, -7, -9, 5, 3, -5, -1 },
+ { 0, 58, 22, -15, -102, 2, 38, 2, 10, -13, -5, 4, 14, -1, -9, 0 },
+ { 23, 4, 66, -11, 22, 89, -2, -26, 13, -8, -38, -1, -9, -20, -2, 8 },
+ { -19, -5, -89, 2, -26, 76, -11, -17, 20, 13, 18, -4, 1, -15, 3, 5 },
+ { -10, -1, -1, 6, 23, 25, 87, -7, -74, 4, 39, -5, 0, -1, -20, -1 },
+ { -17, -28, 12, -8, -32, 14, -53, -6, -68, -67, 17, 29, 2, 6, 25, 4 },
+ { 1, -24, -23, 1, 17, -7, 52, 9, 50, -92, -15, 27, -15, -10, -6, 3 },
+ { -6, -17, -2, -111, 7, -17, 8, -42, 9, 18, 16, 25, -4, 2, -1, 11 },
+ { 9, 5, 35, 0, 6, 21, -9, 34, 44, -3, 102, 11, -7, 13, 11, -20 },
+ { 4, -5, -5, -10, 15, 19, -2, 6, 6, -12, -13, 6, 95, 69, -29, -24 },
+ { -6, -4, -9, -39, 1, 22, 0, 102, -19, 19, -32, 30, -16, -14, -8, -23 },
+ { 4, -4, 7, 8, 4, -13, -18, 5, 0, 0, 21, 22, 58, -88, -54, 28 },
+ { -4, -7, 0, -24, -7, 0, -25, 3, -3, -30, 8, -76, -34, 4, -80, -26 },
+ { 0, 6, 0, 30, -6, 1, -13, -23, 1, 20, -2, 80, -44, 37, -68, 1 },
+ { 0, 0, -1, 5, -1, -7, 1, -34, -2, 3, -6, 19, 5, -38, 11, -115 },
+ }
+ },
+ { //1
+ {
+ { -111, 39, 4, 3, 44, 11, -12, -1, 7, -16, -5, 2, 3, -1, 4, 2 },
+ { -47, -27, 15, -1, -92, 43, 20, -2, 20, 39, -16, -5, 10, -5, -13, 2 },
+ { -35, -23, 4, 4, -17, -72, 32, 6, -59, 18, 50, -6, 0, 40, 0, -13 },
+ { 13, 93, -27, -4, -48, 13, -34, 4, -52, 11, 1, 10, 3, 16, -3, 1 },
+ { -11, -27, 1, 2, -47, -4, -36, 10, -2, -85, 14, 29, -20, -2, 57, 4 },
+ { 0, -35, 32, -2, 26, 60, -3, -17, -82, 1, -30, 0, -37, 21, 3, 12 },
+ { -17, -46, -92, 14, 7, -10, -39, 29, -17, 27, -28, 17, 1, -15, -13, 17 },
+ { 4, -10, -23, 4, 16, 58, -17, 26, 30, 21, 67, 2, -13, 59, 13, -40 },
+ { 5, -20, 32, -5, 8, -3, -46, -7, -4, 2, -15, 24, 100, 44, 0, 5 },
+ { -4, -1, 38, -18, -7, -42, -63, -6, 33, 34, -23, 15, -65, 33, -20, 2 },
+ { -2, -10, 35, -19, 5, 8, -44, 14, -25, 25, 58, 17, 7, -84, -16, -18 },
+ { 5, 13, 18, 34, 11, -4, 18, 18, 5, 58, -3, 42, -2, -10, 85, 38 },
+ { -5, -7, -34, -83, 2, -1, -4, -73, 4, 20, 15, -12, 4, -3, 44, 12 },
+ { 0, 4, -2, -60, 5, 9, 42, 34, 5, -14, 9, 80, -5, 13, -38, 37 },
+ { -1, 2, 7, -57, 3, -7, 9, 68, -9, 6, -49, -20, 6, -4, 36, -64 },
+ { -1, 0, -12, 23, 1, -4, 17, -53, -3, 4, -21, 72, -4, -8, -3, -83 },
+ },
+ {
+ { 88, -55, 6, -3, -66, 27, 9, -2, 11, 11, -13, 1, -2, -7, 1, 2 },
+ { -58, -20, 27, -2, -27, 75, -29, 0, 47, -42, -11, 11, -9, -3, 19, -4 },
+ { -51, 23, -22, 5, -63, 3, 37, -5, 1, 64, -35, -4, 29, -31, -11, 13 },
+ { -27, -76, 49, -2, 40, 14, 9, -17, -56, 36, -25, 6, 14, 3, -6, 8 },
+ { 19, -4, -36, 22, 52, 7, 36, -23, 28, -17, -64, 15, -5, -44, 48, 9 },
+ { 29, 50, 13, -10, 1, 34, -59, 1, -51, 4, -16, 30, 52, -33, 24, -5 },
+ { -12, -21, -74, 43, -13, 39, 18, -5, -58, -35, 27, -5, 19, 26, 6, -5 },
+ { 19, 38, -10, -5, 28, 66, 0, -5, -4, 19, -30, -26, -40, 28, -60, 37 },
+ { -6, 27, 18, -5, -37, -18, 12, -25, -44, -10, -38, 37, -66, 45, 40, -7 },
+ { -13, -28, -45, -39, 0, -5, -39, 69, -23, 16, -12, -18, -50, -31, 24, 13 },
+ { -1, 8, 24, -51, -15, -9, 44, 10, -28, -70, -12, -39, 24, -18, -4, 51 },
+ { -8, -22, -17, 33, -18, -45, -57, -27, 0, -31, -30, 29, -2, -13, -53, 49 },
+ { 1, 12, 32, 51, -8, 8, -2, -31, -22, 4, 46, -39, -49, -67, 14, 17 },
+ { 4, 5, 24, 60, -5, -14, -23, 38, 9, 8, -34, -59, 24, 47, 42, 28 },
+ { -1, -5, -20, -34, 4, 4, -15, -46, 18, 31, 42, 10, 10, 27, 49, 78 },
+ { -3, -7, -22, -34, -5, -11, -36, -69, -1, -3, -25, -73, 5, 4, 4, -49 },
+ }
+ },
+ { //2
+ {
+ { -112, 47, -2, 2, -34, 13, 2, 0, 15, -7, 1, 0, 8, -3, -1, 0 },
+ { 29, -7, 1, -1, -108, 40, 2, 0, -45, 13, 4, -1, 8, -5, 1, 0 },
+ { -36, -87, 69, -10, -17, -33, 26, -2, 7, 14, -11, 2, 6, 8, -7, 0 },
+ { 28, -5, 2, -2, -29, 13, -2, 0, 103, -36, -4, 1, 48, -16, -4, 1 },
+ { -12, -24, 15, -3, 26, 80, -61, 9, 15, 54, -36, 2, 0, -4, 6, -2 },
+ { 18, 53, 69, -74, 14, 24, 28, -30, -6, -7, -11, 12, -5, -7, -6, 8 },
+ { 5, -1, 2, 0, -26, 6, 0, 1, 45, -9, -1, 0, -113, 28, 8, -1 },
+ { -13, -32, 18, -2, 15, 34, -27, 7, -25, -80, 47, -1, -16, -50, 28, 2 },
+ { -4, -13, -10, 19, 18, 46, 60, -48, 16, 33, 60, -48, 1, 0, 5, -2 },
+ { 15, 33, 63, 89, 8, 15, 25, 40, -4, -8, -15, -8, -2, -6, -9, -7 },
+ { -8, -24, -27, 15, 12, 41, 26, -29, -17, -50, -39, 27, 0, 35, -67, 26 },
+ { -2, -6, -24, 13, -1, -8, 37, -22, 3, 18, -51, 22, -23, -95, 17, 17 },
+ { -3, -7, -16, -21, 10, 24, 46, 75, 8, 20, 38, 72, 1, 2, 1, 7 },
+ { 2, 6, 10, -3, -5, -16, -31, 12, 7, 24, 41, -16, -16, -41, -89, 49 },
+ { 4, 8, 21, 40, -4, -11, -28, -57, 5, 14, 31, 70, 7, 18, 32, 52 },
+ { 0, 1, 4, 11, -2, -4, -13, -34, 3, 7, 20, 47, -6, -19, -42, -101 },
+ },
+ {
+ { -99, 39, -1, 2, 65, -20, -5, 0, -15, -2, 5, -1, 0, 3, -1, 0 },
+ { 58, 42, -33, 3, 33, -63, 23, -1, -55, 32, 3, -5, 21, -2, -8, 3 },
+ { -15, 71, -44, 5, -58, -29, 25, 3, 62, -7, -4, -4, -19, 4, 0, 1 },
+ { 46, 5, 4, -6, 71, -12, -15, 5, 52, -38, 13, -2, -63, 23, 3, -3 },
+ { -14, -54, -29, 29, 25, -9, 61, -29, 27, 44, -48, 5, -27, -21, 12, 7 },
+ { -3, 3, 69, -42, -11, -50, -26, 26, 24, 63, -19, -5, -18, -22, 12, 0 },
+ { 17, 16, -2, 1, 38, 18, -12, 0, 62, 1, -14, 5, 89, -42, 8, -2 },
+ { 15, 54, -8, 6, 6, 60, -26, -8, -30, 17, -38, 22, -43, -45, 42, -7 },
+ { -6, -17, -55, -28, 9, 30, -8, 58, 4, 34, 41, -52, -16, -36, -20, 16 },
+ { -2, -1, -9, -79, 7, 11, 48, 44, -13, -34, -55, 6, 12, 23, 20, -11 },
+ { 7, 29, 14, -6, 12, 53, 10, -11, 14, 59, -15, -3, 5, 71, -54, 13 },
+ { -5, -24, -53, 15, -3, -15, -61, 26, 6, 30, -16, 23, 13, 56, 44, -35 },
+ { 4, 8, 21, 52, -1, -1, -5, 29, -7, -17, -44, -84, 8, 20, 31, 39 },
+ { -2, -11, -25, -4, -4, -21, -53, 2, -5, -26, -64, 19, -8, -19, -73, 39 },
+ { -3, -5, -23, -57, -2, -4, -24, -75, 1, 3, 9, -25, 6, 15, 41, 61 },
+ { 1, 1, 7, 18, 1, 2, 16, 47, 2, 5, 24, 67, 3, 9, 25, 88 },
+ }
+ },
+ { //3
+ {
+ { -114, 37, 3, 2, -22, -23, 14, 0, 21, -17, -5, 2, 5, 2, -4, -1 },
+ { -19, -41, 19, -2, 85, -60, -11, 7, 17, 31, -34, 2, -11, 19, 2, -8 },
+ { 36, -25, 18, -2, -42, -53, 35, 5, 46, -60, -25, 19, 8, 21, -33, -1 },
+ { -27, -80, 44, -3, -58, 1, -29, 19, -41, 18, -12, -7, 12, -17, 7, -6 },
+ { -11, -21, 37, -10, 44, -4, 47, -12, -37, -41, 58, 18, 10, -46, -16, 31 },
+ { 15, 47, 10, -6, -16, -44, 42, 10, -80, 25, -40, 21, -23, -2, 3, -14 },
+ { 13, 25, 79, -39, -13, 10, 31, -4, 49, 45, 12, -8, 3, -1, 43, 7 },
+ { 16, 11, -26, 13, -13, -74, -20, -1, 5, -6, 29, -47, 26, -49, 54, 2 },
+ { -8, -34, -26, 7, -26, -19, 29, -37, 1, 22, 46, -9, -81, 37, 14, 20 },
+ { -6, -30, -42, -12, -3, 5, 57, -52, -2, 37, -12, 6, 74, 10, 6, -15 },
+ { 5, 9, -6, 42, -15, -18, -9, 26, 15, 58, 14, 43, 23, -10, -37, 75 },
+ { -5, -23, -23, 36, 3, 22, 36, 40, 27, -4, -16, 56, -25, -46, 56, -24 },
+ { 1, 3, 23, 73, 8, 5, 34, 46, -12, 2, 35, -38, 26, 52, 2, -31 },
+ { -3, -2, -21, -52, 1, -10, -17, 44, -19, -20, 30, 45, 27, 61, 49, 21 },
+ { -2, -7, -33, -56, -4, -6, 21, 63, 15, 31, 32, -22, -10, -26, -52, -38 },
+ { -5, -12, -18, -12, 8, 22, 38, 36, -5, -15, -51, -63, -5, 0, 15, 73 },
+ },
+ {
+ { -102, 22, 7, 2, 66, -25, -6, -1, -15, 14, 1, -1, 2, -2, 1, 0 },
+ { 12, 93, -27, -6, -27, -64, 36, 6, 13, 5, -23, 0, -2, 6, 5, -3 },
+ { -59, -24, 17, 1, -62, -2, -3, 2, 83, -12, -17, -2, -24, 14, 7, -2 },
+ { -33, 23, -36, 11, -21, 50, 35, -16, -23, -78, 16, 19, 22, 15, -30, -5 },
+ { 0, -38, -81, 30, 27, 5, 51, -32, 24, 36, -16, 12, -24, -8, 9, 1 },
+ { 28, 38, 8, -9, 62, 32, -13, 2, 51, -32, 15, 5, -66, 28, 0, -1 },
+ { 11, -35, 21, -17, 30, -18, 31, 18, -11, -36, -80, 12, 16, 49, 13, -32 },
+ { -13, 23, 22, -36, -12, 64, 39, 25, -19, 23, -36, 9, -30, -58, 33, -7 },
+ { -9, -20, -55, -83, 3, -2, 1, 62, 8, 2, 27, -28, 7, 15, -11, 5 },
+ { -6, 24, -38, 23, -8, 40, -49, 0, -7, 9, -25, -44, 23, 39, 70, -3 },
+ { 12, 17, 17, 0, 32, 27, 21, 2, 67, 11, -6, -10, 89, -22, -12, 16 },
+ { 2, -9, 8, 45, 7, -8, 27, 35, -9, -31, -17, -87, -23, -22, -19, 44 },
+ { -1, -9, 28, -24, -1, -10, 49, -30, -8, -7, 40, 1, 4, 33, 65, 67 },
+ { 5, -12, -24, -17, 13, -34, -32, -16, 14, -67, -7, 9, 7, -74, 49, 1 },
+ { 2, -6, 11, 45, 3, -10, 33, 55, 8, -5, 59, 4, 7, -4, 44, -66 },
+ { -1, 1, -14, 36, -1, 2, -20, 69, 0, 0, -15, 72, 3, 4, 5, 65 },
+ }
+ }
+};
+
+const uint8_t ff_vvc_lfnst_tr_set_index[95] =
+{
+ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 3, 3, 3, 3, 3, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+uint8_t ff_vvc_default_scale_m[64 * 64];
+
+//< AlfFixFiltCoeff
+const int16_t ff_vvc_alf_fix_filt_coeff[64][12] = {
+ { 0, 0, 2, -3, 1, -4, 1, 7, -1, 1, -1, 5 },
+ { 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, -1, 2 },
+ { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1 },
+ { 2, 2, -7, -3, 0, -5, 13, 22, 12, -3, -3, 17 },
+ { -1, 0, 6, -8, 1, -5, 1, 23, 0, 2, -5, 10 },
+ { 0, 0, -1, -1, 0, -1, 2, 1, 0, 0, -1, 4 },
+ { 0, 0, 3, -11, 1, 0, -1, 35, 5, 2, -9, 9 },
+ { 0, 0, 8, -8, -2, -7, 4, 4, 2, 1, -1, 25 },
+ { 0, 0, 1, -1, 0, -3, 1, 3, -1, 1, -1, 3 },
+ { 0, 0, 3, -3, 0, -6, 5, -1, 2, 1, -4, 21 },
+ { -7, 1, 5, 4, -3, 5, 11, 13, 12, -8, 11, 12 },
+ { -5, -3, 6, -2, -3, 8, 14, 15, 2, -7, 11, 16 },
+ { 2, -1, -6, -5, -2, -2, 20, 14, -4, 0, -3, 25 },
+ { 3, 1, -8, -4, 0, -8, 22, 5, -3, 2, -10, 29 },
+ { 2, 1, -7, -1, 2, -11, 23, -5, 0, 2, -10, 29 },
+ { -6, -3, 8, 9, -4, 8, 9, 7, 14, -2, 8, 9 },
+ { 2, 1, -4, -7, 0, -8, 17, 22, 1, -1, -4, 23 },
+ { 3, 0, -5, -7, 0, -7, 15, 18, -5, 0, -5, 27 },
+ { 2, 0, 0, -7, 1, -10, 13, 13, -4, 2, -7, 24 },
+ { 3, 3, -13, 4, -2, -5, 9, 21, 25, -2, -3, 12 },
+ { -5, -2, 7, -3, -7, 9, 8, 9, 16, -2, 15, 12 },
+ { 0, -1, 0, -7, -5, 4, 11, 11, 8, -6, 12, 21 },
+ { 3, -2, -3, -8, -4, -1, 16, 15, -2, -3, 3, 26 },
+ { 2, 1, -5, -4, -1, -8, 16, 4, -2, 1, -7, 33 },
+ { 2, 1, -4, -2, 1, -10, 17, -2, 0, 2, -11, 33 },
+ { 1, -2, 7, -15, -16, 10, 8, 8, 20, 11, 14, 11 },
+ { 2, 2, 3, -13, -13, 4, 8, 12, 2, -3, 16, 24 },
+ { 1, 4, 0, -7, -8, -4, 9, 9, -2, -2, 8, 29 },
+ { 1, 1, 2, -4, -1, -6, 6, 3, -1, -1, -3, 30 },
+ { -7, 3, 2, 10, -2, 3, 7, 11, 19, -7, 8, 10 },
+ { 0, -2, -5, -3, -2, 4, 20, 15, -1, -3, -1, 22 },
+ { 3, -1, -8, -4, -1, -4, 22, 8, -4, 2, -8, 28 },
+ { 0, 3, -14, 3, 0, 1, 19, 17, 8, -3, -7, 20 },
+ { 0, 2, -1, -8, 3, -6, 5, 21, 1, 1, -9, 13 },
+ { -4, -2, 8, 20, -2, 2, 3, 5, 21, 4, 6, 1 },
+ { 2, -2, -3, -9, -4, 2, 14, 16, 3, -6, 8, 24 },
+ { 2, 1, 5, -16, -7, 2, 3, 11, 15, -3, 11, 22 },
+ { 1, 2, 3, -11, -2, -5, 4, 8, 9, -3, -2, 26 },
+ { 0, -1, 10, -9, -1, -8, 2, 3, 4, 0, 0, 29 },
+ { 1, 2, 0, -5, 1, -9, 9, 3, 0, 1, -7, 20 },
+ { -2, 8, -6, -4, 3, -9, -8, 45, 14, 2, -13, 7 },
+ { 1, -1, 16, -19, -8, -4, -3, 2, 19, 0, 4, 30 },
+ { 1, 1, -3, 0, 2, -11, 15, -5, 1, 2, -9, 24 },
+ { 0, 1, -2, 0, 1, -4, 4, 0, 0, 1, -4, 7 },
+ { 0, 1, 2, -5, 1, -6, 4, 10, -2, 1, -4, 10 },
+ { 3, 0, -3, -6, -2, -6, 14, 8, -1, -1, -3, 31 },
+ { 0, 1, 0, -2, 1, -6, 5, 1, 0, 1, -5, 13 },
+ { 3, 1, 9, -19, -21, 9, 7, 6, 13, 5, 15, 21 },
+ { 2, 4, 3, -12, -13, 1, 7, 8, 3, 0, 12, 26 },
+ { 3, 1, -8, -2, 0, -6, 18, 2, -2, 3, -10, 23 },
+ { 1, 1, -4, -1, 1, -5, 8, 1, -1, 2, -5, 10 },
+ { 0, 1, -1, 0, 0, -2, 2, 0, 0, 1, -2, 3 },
+ { 1, 1, -2, -7, 1, -7, 14, 18, 0, 0, -7, 21 },
+ { 0, 1, 0, -2, 0, -7, 8, 1, -2, 0, -3, 24 },
+ { 0, 1, 1, -2, 2, -10, 10, 0, -2, 1, -7, 23 },
+ { 0, 2, 2, -11, 2, -4, -3, 39, 7, 1, -10, 9 },
+ { 1, 0, 13, -16, -5, -6, -1, 8, 6, 0, 6, 29 },
+ { 1, 3, 1, -6, -4, -7, 9, 6, -3, -2, 3, 33 },
+ { 4, 0, -17, -1, -1, 5, 26, 8, -2, 3, -15, 30 },
+ { 0, 1, -2, 0, 2, -8, 12, -6, 1, 1, -6, 16 },
+ { 0, 0, 0, -1, 1, -4, 4, 0, 0, 0, -3, 11 },
+ { 0, 1, 2, -8, 2, -6, 5, 15, 0, 2, -7, 9 },
+ { 1, -1, 12, -15, -7, -2, 3, 6, 6, -1, 7, 30 },
+};
+
+//< AlfClassToFiltMap
+const uint8_t ff_vvc_alf_class_to_filt_map[16][25] = {
+ { 8, 2, 2, 2, 3, 4, 53, 9, 9, 52, 4, 4, 5, 9, 2, 8, 10, 9, 1, 3, 39, 39, 10, 9, 52 },
+ { 11, 12, 13, 14, 15, 30, 11, 17, 18, 19, 16, 20, 20, 4, 53, 21, 22, 23, 14, 25, 26, 26, 27, 28, 10 },
+ { 16, 12, 31, 32, 14, 16, 30, 33, 53, 34, 35, 16, 20, 4, 7, 16, 21, 36, 18, 19, 21, 26, 37, 38, 39 },
+ { 35, 11, 13, 14, 43, 35, 16, 4, 34, 62, 35, 35, 30, 56, 7, 35, 21, 38, 24, 40, 16, 21, 48, 57, 39 },
+ { 11, 31, 32, 43, 44, 16, 4, 17, 34, 45, 30, 20, 20, 7, 5, 21, 22, 46, 40, 47, 26, 48, 63, 58, 10 },
+ { 12, 13, 50, 51, 52, 11, 17, 53, 45, 9, 30, 4, 53, 19, 0, 22, 23, 25, 43, 44, 37, 27, 28, 10, 55 },
+ { 30, 33, 62, 51, 44, 20, 41, 56, 34, 45, 20, 41, 41, 56, 5, 30, 56, 38, 40, 47, 11, 37, 42, 57, 8 },
+ { 35, 11, 23, 32, 14, 35, 20, 4, 17, 18, 21, 20, 20, 20, 4, 16, 21, 36, 46, 25, 41, 26, 48, 49, 58 },
+ { 12, 31, 59, 59, 3, 33, 33, 59, 59, 52, 4, 33, 17, 59, 55, 22, 36, 59, 59, 60, 22, 36, 59, 25, 55 },
+ { 31, 25, 15, 60, 60, 22, 17, 19, 55, 55, 20, 20, 53, 19, 55, 22, 46, 25, 43, 60, 37, 28, 10, 55, 52 },
+ { 12, 31, 32, 50, 51, 11, 33, 53, 19, 45, 16, 4, 4, 53, 5, 22, 36, 18, 25, 43, 26, 27, 27, 28, 10 },
+ { 5, 2, 44, 52, 3, 4, 53, 45, 9, 3, 4, 56, 5, 0, 2, 5, 10, 47, 52, 3, 63, 39, 10, 9, 52 },
+ { 12, 34, 44, 44, 3, 56, 56, 62, 45, 9, 56, 56, 7, 5, 0, 22, 38, 40, 47, 52, 48, 57, 39, 10, 9 },
+ { 35, 11, 23, 14, 51, 35, 20, 41, 56, 62, 16, 20, 41, 56, 7, 16, 21, 38, 24, 40, 26, 26, 42, 57, 39 },
+ { 33, 34, 51, 51, 52, 41, 41, 34, 62, 0, 41, 41, 56, 7, 5, 56, 38, 38, 40, 44, 37, 42, 57, 39, 10 },
+ { 16, 31, 32, 15, 60, 30, 4, 17, 19, 25, 22, 20, 4, 53, 19, 21, 22, 46, 25, 55, 26, 48, 63, 58, 55 },
+};
+
+const uint8_t ff_vvc_alf_aps_class_to_filt_map[25] = {
+ 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,
+};
+
+const int8_t ff_vvc_inter_luma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_LUMA_FACTS][VVC_INTER_LUMA_TAPS] = {
+ {
+ //1x, hpelIfIdx == 0, Table 27
+ { 0, 0, 0, 64, 0, 0, 0, 0 },
+ { 0, 1, -3, 63, 4, -2, 1, 0 },
+ { -1, 2, -5, 62, 8, -3, 1, 0 },
+ { -1, 3, -8, 60, 13, -4, 1, 0 },
+ { -1, 4, -10, 58, 17, -5, 1, 0 },
+ { -1, 4, -11, 52, 26, -8, 3, -1 },
+ { -1, 3, -9, 47, 31, -10, 4, -1 },
+ { -1, 4, -11, 45, 34, -10, 4, -1 },
+ { -1, 4, -11, 40, 40, -11, 4, -1 },
+ { -1, 4, -10, 34, 45, -11, 4, -1 },
+ { -1, 4, -10, 31, 47, -9, 3, -1 },
+ { -1, 3, -8, 26, 52, -11, 4, -1 },
+ { 0, 1, -5, 17, 58, -10, 4, -1 },
+ { 0, 1, -4, 13, 60, -8, 3, -1 },
+ { 0, 1, -3, 8, 62, -5, 2, -1 },
+ { 0, 1, -2, 4, 63, -3, 1, 0 },
+ },
+
+ {
+ //1x, hpelIfIdx == 1, Table 27
+ { 0, 0, 0, 64, 0, 0, 0, 0 },
+ { 0, 1, -3, 63, 4, -2, 1, 0 },
+ { -1, 2, -5, 62, 8, -3, 1, 0 },
+ { -1, 3, -8, 60, 13, -4, 1, 0 },
+ { -1, 4, -10, 58, 17, -5, 1, 0 },
+ { -1, 4, -11, 52, 26, -8, 3, -1 },
+ { -1, 3, -9, 47, 31, -10, 4, -1 },
+ { -1, 4, -11, 45, 34, -10, 4, -1 },
+ { 0, 3, 9, 20, 20, 9, 3, 0 },
+ { -1, 4, -10, 34, 45, -11, 4, -1 },
+ { -1, 4, -10, 31, 47, -9, 3, -1 },
+ { -1, 3, -8, 26, 52, -11, 4, -1 },
+ { 0, 1, -5, 17, 58, -10, 4, -1 },
+ { 0, 1, -4, 13, 60, -8, 3, -1 },
+ { 0, 1, -3, 8, 62, -5, 2, -1 },
+ { 0, 1, -2, 4, 63, -3, 1, 0 },
+ },
+
+ {
+ //1x, affine, Table 30
+ { 0, 0, 0, 64, 0, 0, 0, 0 },
+ { 0, 1, -3, 63, 4, -2, 1, 0 },
+ { 0, 1, -5, 62, 8, -3, 1, 0 },
+ { 0, 2, -8, 60, 13, -4, 1, 0 },
+ { 0, 3, -10, 58, 17, -5, 1, 0 },
+ { 0, 3, -11, 52, 26, -8, 2, 0 },
+ { 0, 2, -9, 47, 31, -10, 3, 0 },
+ { 0, 3, -11, 45, 34, -10, 3, 0 },
+ { 0, 3, -11, 40, 40, -11, 3, 0 },
+ { 0, 3, -10, 34, 45, -11, 3, 0 },
+ { 0, 3, -10, 31, 47, -9, 2, 0 },
+ { 0, 2, -8, 26, 52, -11, 3, 0 },
+ { 0, 1, -5, 17, 58, -10, 3, 0 },
+ { 0, 1, -4, 13, 60, -8, 2, 0 },
+ { 0, 1, -3, 8, 62, -5, 1, 0 },
+ { 0, 1, -2, 4, 63, -3, 1, 0 },
+ },
+
+};
+
+const int8_t ff_vvc_inter_chroma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_CHROMA_FACTS][VVC_INTER_CHROMA_TAPS] = {
+ {
+ //1x, Table 33
+ { 0, 64, 0, 0 },
+ { -1, 63, 2, 0 },
+ { -2, 62, 4, 0 },
+ { -2, 60, 7, -1 },
+ { -2, 58, 10, -2 },
+ { -3, 57, 12, -2 },
+ { -4, 56, 14, -2 },
+ { -4, 55, 15, -2 },
+ { -4, 54, 16, -2 },
+ { -5, 53, 18, -2 },
+ { -6, 52, 20, -2 },
+ { -6, 49, 24, -3 },
+ { -6, 46, 28, -4 },
+ { -5, 44, 29, -4 },
+ { -4, 42, 30, -4 },
+ { -4, 39, 33, -4 },
+ { -4, 36, 36, -4 },
+ { -4, 33, 39, -4 },
+ { -4, 30, 42, -4 },
+ { -4, 29, 44, -5 },
+ { -4, 28, 46, -6 },
+ { -3, 24, 49, -6 },
+ { -2, 20, 52, -6 },
+ { -2, 18, 53, -5 },
+ { -2, 16, 54, -4 },
+ { -2, 15, 55, -4 },
+ { -2, 14, 56, -4 },
+ { -2, 12, 57, -3 },
+ { -2, 10, 58, -2 },
+ { -1, 7, 60, -2 },
+ { 0, 4, 62, -2 },
+ { 0, 2, 63, -1 },
+ },
+ {
+ //1.5x, Table 34
+ { 12, 40, 12, 0 },
+ { 11, 40, 13, 0 },
+ { 10, 40, 15, -1 },
+ { 9, 40, 16, -1 },
+ { 8, 40, 17, -1 },
+ { 8, 39, 18, -1 },
+ { 7, 39, 19, -1 },
+ { 6, 38, 21, -1 },
+ { 5, 38, 22, -1 },
+ { 4, 38, 23, -1 },
+ { 4, 37, 24, -1 },
+ { 3, 36, 25, 0 },
+ { 3, 35, 26, 0 },
+ { 2, 34, 28, 0 },
+ { 2, 33, 29, 0 },
+ { 1, 33, 30, 0 },
+ { 1, 31, 31, 1 },
+ { 0, 30, 33, 1 },
+ { 0, 29, 33, 2 },
+ { 0, 28, 34, 2 },
+ { 0, 26, 35, 3 },
+ { 0, 25, 36, 3 },
+ { -1, 24, 37, 4 },
+ { -1, 23, 38, 4 },
+ { -1, 22, 38, 5 },
+ { -1, 21, 38, 6 },
+ { -1, 19, 39, 7 },
+ { -1, 18, 39, 8 },
+ { -1, 17, 40, 8 },
+ { -1, 16, 40, 9 },
+ { -1, 15, 40, 10 },
+ { 0, 13, 40, 11 },
+ },
+ {
+ //2x, Table 35
+ { 17, 30, 17, 0 },
+ { 17, 30, 18, -1 },
+ { 16, 30, 18, 0 },
+ { 16, 30, 18, 0 },
+ { 15, 30, 18, 1 },
+ { 14, 30, 18, 2 },
+ { 13, 29, 19, 3 },
+ { 13, 29, 19, 3 },
+ { 12, 29, 20, 3 },
+ { 11, 28, 21, 4 },
+ { 10, 28, 22, 4 },
+ { 10, 27, 22, 5 },
+ { 9, 27, 23, 5 },
+ { 9, 26, 24, 5 },
+ { 8, 26, 24, 6 },
+ { 7, 26, 25, 6 },
+ { 7, 25, 25, 7 },
+ { 6, 25, 26, 7 },
+ { 6, 24, 26, 8 },
+ { 5, 24, 26, 9 },
+ { 5, 23, 27, 9 },
+ { 5, 22, 27, 10 },
+ { 4, 22, 28, 10 },
+ { 4, 21, 28, 11 },
+ { 3, 20, 29, 12 },
+ { 3, 19, 29, 13 },
+ { 3, 19, 29, 13 },
+ { 2, 18, 30, 14 },
+ { 1, 18, 30, 15 },
+ { 0, 18, 30, 16 },
+ { 0, 18, 30, 16 },
+ { -1, 18, 30, 17 },
+ },
+};
+
+const int8_t ff_vvc_inter_luma_dmvr_filters[VVC_INTER_LUMA_DMVR_FACTS][VVC_INTER_LUMA_DMVR_TAPS] = {
+ { 16, 0 },
+ { 15, 1 },
+ { 14, 2 },
+ { 13, 3 },
+ { 12, 4 },
+ { 11, 5 },
+ { 10, 6 },
+ { 9, 7 },
+ { 8, 8 },
+ { 7, 9 },
+ { 6, 10 },
+ { 5, 11 },
+ { 4, 12 },
+ { 3, 13 },
+ { 2, 14 },
+ { 1, 15 },
+};
+
+#define FILTER_G(fact) { 16 - (fact >> 1), 32 - (fact >> 1), 16 + (fact >> 1), fact >> 1}
+// Table 25 – Specification of interpolation filter coefficients fC and fG
+const int8_t ff_vvc_intra_luma_filter[VVC_INTRA_LUMA_TYPES][VVC_INTRA_LUMA_FACTS][VVC_INTRA_LUMA_TAPS] = {
+ {
+ { 0, 64, 0, 0 },
+ { -1, 63, 2, 0 },
+ { -2, 62, 4, 0 },
+ { -2, 60, 7, -1 },
+ { -2, 58, 10, -2 },
+ { -3, 57, 12, -2 },
+ { -4, 56, 14, -2 },
+ { -4, 55, 15, -2 },
+ { -4, 54, 16, -2 },
+ { -5, 53, 18, -2 },
+ { -6, 52, 20, -2 },
+ { -6, 49, 24, -3 },
+ { -6, 46, 28, -4 },
+ { -5, 44, 29, -4 },
+ { -4, 42, 30, -4 },
+ { -4, 39, 33, -4 },
+ { -4, 36, 36, -4 },
+ { -4, 33, 39, -4 },
+ { -4, 30, 42, -4 },
+ { -4, 29, 44, -5 },
+ { -4, 28, 46, -6 },
+ { -3, 24, 49, -6 },
+ { -2, 20, 52, -6 },
+ { -2, 18, 53, -5 },
+ { -2, 16, 54, -4 },
+ { -2, 15, 55, -4 },
+ { -2, 14, 56, -4 },
+ { -2, 12, 57, -3 },
+ { -2, 10, 58, -2 },
+ { -1, 7, 60, -2 },
+ { 0, 4, 62, -2 },
+ { 0, 2, 63, -1 },
+ },
+ {
+ FILTER_G(0),
+ FILTER_G(1),
+ FILTER_G(2),
+ FILTER_G(3),
+ FILTER_G(4),
+ FILTER_G(5),
+ FILTER_G(6),
+ FILTER_G(7),
+ FILTER_G(8),
+ FILTER_G(9),
+ FILTER_G(10),
+ FILTER_G(11),
+ FILTER_G(12),
+ FILTER_G(13),
+ FILTER_G(14),
+ FILTER_G(15),
+ FILTER_G(16),
+ FILTER_G(17),
+ FILTER_G(18),
+ FILTER_G(19),
+ FILTER_G(20),
+ FILTER_G(21),
+ FILTER_G(22),
+ FILTER_G(23),
+ FILTER_G(24),
+ FILTER_G(25),
+ FILTER_G(26),
+ FILTER_G(27),
+ FILTER_G(28),
+ FILTER_G(29),
+ FILTER_G(30),
+ FILTER_G(31),
+ }
+};
+
+const uint8_t ff_vvc_gpm_angle_idx[VVC_GPM_NUM_PARTITION] = {
+ 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5,
+ 5, 5, 8, 8, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13,
+ 14, 14, 14, 14, 16, 16, 18, 18, 18, 19, 19, 19, 20, 20, 20, 21,
+ 21, 21, 24, 24, 27, 27, 27, 28, 28, 28, 29, 29, 29, 30, 30, 30,
+};
+
+const uint8_t ff_vvc_gpm_distance_idx[VVC_GPM_NUM_PARTITION] = {
+ 1, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1,
+ 2, 3, 1, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3,
+ 0, 1, 2, 3, 1, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1,
+ 2, 3, 1, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3,
+};
+
+const int8_t ff_vvc_gpm_distance_lut[VVC_GPM_NUM_ANGLES] = {
+ 8, 8, 8, 8, 4, 4, 2, 1, 0, -1, -2, -4, -4, -8, -8, -8, -8, -8, -8, -8, -4, -4, -2, -1, 0, 1, 2, 4, 4, 8, 8, 8,
+};
+
+const uint8_t ff_vvc_gpm_angle_to_mirror[VVC_GPM_NUM_ANGLES] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2,
+};
+
+#define INV -1
+const uint8_t ff_vvc_gpm_angle_to_weights_idx[VVC_GPM_NUM_ANGLES] = {
+ 0, INV, 1, 2, 3, 4, INV, INV, 5, INV, INV, 4, 3, 2, 1, INV,
+ 0, INV, 1, 2, 3, 4, INV, INV, 5, INV, INV, 4, 3, 2, 1, INV,
+};
+#undef INV
+
+const uint8_t ff_vvc_gpm_weights_offset_x[VVC_GPM_NUM_PARTITION][4][4] = {
+ {
+ { 53, 50, 44, 32 },
+ { 53, 50, 44, 32 },
+ { 53, 50, 44, 32 },
+ { 53, 50, 44, 32 },
+ },
+ {
+ { 55, 54, 52, 48 },
+ { 55, 54, 52, 48 },
+ { 55, 54, 52, 48 },
+ { 55, 54, 52, 48 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 50, 44, 32 },
+ { 52, 48, 44, 32 },
+ { 52, 48, 40, 32 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 52, 48, 40 },
+ { 52, 48, 48, 40 },
+ { 52, 48, 40, 40 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 54, 52, 48 },
+ { 52, 48, 52, 48 },
+ { 52, 48, 40, 48 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 50, 44, 32 },
+ { 52, 48, 44, 32 },
+ { 52, 48, 40, 32 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 52, 48, 40 },
+ { 52, 48, 48, 40 },
+ { 52, 48, 40, 40 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 54, 52, 48 },
+ { 52, 48, 52, 48 },
+ { 52, 48, 40, 48 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 50, 44, 32 },
+ { 52, 48, 44, 32 },
+ { 52, 48, 40, 32 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 52, 48, 40 },
+ { 52, 48, 48, 40 },
+ { 52, 48, 40, 40 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 54, 52, 48 },
+ { 52, 48, 52, 48 },
+ { 52, 48, 40, 48 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 50, 44, 32 },
+ { 52, 48, 44, 32 },
+ { 52, 48, 40, 32 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 52, 48, 40 },
+ { 52, 48, 48, 40 },
+ { 52, 48, 40, 40 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 54, 52, 48 },
+ { 52, 48, 52, 48 },
+ { 52, 48, 40, 48 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 50, 44, 32 },
+ { 52, 48, 44, 32 },
+ { 52, 48, 40, 32 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 52, 48, 40 },
+ { 52, 48, 48, 40 },
+ { 52, 48, 40, 40 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 54, 52, 48 },
+ { 52, 48, 52, 48 },
+ { 52, 48, 40, 48 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 50, 44, 32 },
+ { 52, 48, 44, 32 },
+ { 52, 48, 40, 32 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 52, 48, 40 },
+ { 52, 48, 48, 40 },
+ { 52, 48, 40, 40 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 54, 52, 48 },
+ { 52, 48, 52, 48 },
+ { 52, 48, 40, 48 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 50, 44, 32 },
+ { 52, 48, 44, 32 },
+ { 52, 48, 40, 32 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 52, 48, 40 },
+ { 52, 48, 48, 40 },
+ { 52, 48, 40, 40 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 54, 52, 48 },
+ { 52, 48, 52, 48 },
+ { 52, 48, 40, 48 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 50, 44, 32 },
+ { 52, 48, 44, 32 },
+ { 52, 48, 40, 32 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 52, 48, 40 },
+ { 52, 48, 48, 40 },
+ { 52, 48, 40, 40 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 54, 52, 48 },
+ { 52, 48, 52, 48 },
+ { 52, 48, 40, 48 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 51, 46, 36, 16 },
+ { 51, 46, 36, 16 },
+ { 51, 46, 36, 16 },
+ { 51, 46, 36, 16 },
+ },
+ {
+ { 49, 42, 28, 0 },
+ { 49, 42, 28, 0 },
+ { 49, 42, 28, 0 },
+ { 49, 42, 28, 0 },
+ },
+ {
+ { 52, 46, 36, 16 },
+ { 52, 48, 36, 16 },
+ { 52, 48, 40, 16 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 44, 32, 8 },
+ { 52, 48, 32, 8 },
+ { 52, 48, 40, 8 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 42, 28, 0 },
+ { 52, 48, 28, 0 },
+ { 52, 48, 40, 0 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 46, 36, 16 },
+ { 52, 48, 36, 16 },
+ { 52, 48, 40, 16 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 44, 32, 8 },
+ { 52, 48, 32, 8 },
+ { 52, 48, 40, 8 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 42, 28, 0 },
+ { 52, 48, 28, 0 },
+ { 52, 48, 40, 0 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 46, 36, 16 },
+ { 52, 48, 36, 16 },
+ { 52, 48, 40, 16 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 44, 32, 8 },
+ { 52, 48, 32, 8 },
+ { 52, 48, 40, 8 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 42, 28, 0 },
+ { 52, 48, 28, 0 },
+ { 52, 48, 40, 0 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 46, 36, 16 },
+ { 52, 48, 36, 16 },
+ { 52, 48, 40, 16 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 44, 32, 8 },
+ { 52, 48, 32, 8 },
+ { 52, 48, 40, 8 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 42, 28, 0 },
+ { 52, 48, 28, 0 },
+ { 52, 48, 40, 0 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 46, 36, 16 },
+ { 52, 48, 36, 16 },
+ { 52, 48, 40, 16 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 44, 32, 8 },
+ { 52, 48, 32, 8 },
+ { 52, 48, 40, 8 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 42, 28, 0 },
+ { 52, 48, 28, 0 },
+ { 52, 48, 40, 0 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 46, 36, 16 },
+ { 52, 48, 36, 16 },
+ { 52, 48, 40, 16 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 44, 32, 8 },
+ { 52, 48, 32, 8 },
+ { 52, 48, 40, 8 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 42, 28, 0 },
+ { 52, 48, 28, 0 },
+ { 52, 48, 40, 0 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 46, 36, 16 },
+ { 52, 48, 36, 16 },
+ { 52, 48, 40, 16 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 44, 32, 8 },
+ { 52, 48, 32, 8 },
+ { 52, 48, 40, 8 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 42, 28, 0 },
+ { 52, 48, 28, 0 },
+ { 52, 48, 40, 0 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 46, 36, 16 },
+ { 52, 48, 36, 16 },
+ { 52, 48, 40, 16 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 44, 32, 8 },
+ { 52, 48, 32, 8 },
+ { 52, 48, 40, 8 },
+ { 52, 48, 40, 24 },
+ },
+ {
+ { 52, 42, 28, 0 },
+ { 52, 48, 28, 0 },
+ { 52, 48, 40, 0 },
+ { 52, 48, 40, 24 },
+ },
+};
+
+const uint8_t ff_vvc_gpm_weights_offset_y[VVC_GPM_NUM_PARTITION][4][4] = {
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 53, 52, 52, 52 },
+ { 50, 50, 48, 48 },
+ { 44, 44, 44, 40 },
+ { 32, 32, 32, 32 },
+ },
+ {
+ { 54, 52, 52, 52 },
+ { 52, 52, 48, 48 },
+ { 48, 48, 48, 40 },
+ { 40, 40, 40, 40 },
+ },
+ {
+ { 55, 52, 52, 52 },
+ { 54, 54, 48, 48 },
+ { 52, 52, 52, 40 },
+ { 48, 48, 48, 48 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 53, 52, 52, 52 },
+ { 50, 50, 48, 48 },
+ { 44, 44, 44, 40 },
+ { 32, 32, 32, 32 },
+ },
+ {
+ { 54, 52, 52, 52 },
+ { 52, 52, 48, 48 },
+ { 48, 48, 48, 40 },
+ { 40, 40, 40, 40 },
+ },
+ {
+ { 55, 52, 52, 52 },
+ { 54, 54, 48, 48 },
+ { 52, 52, 52, 40 },
+ { 48, 48, 48, 48 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 53, 52, 52, 52 },
+ { 50, 50, 48, 48 },
+ { 44, 44, 44, 40 },
+ { 32, 32, 32, 32 },
+ },
+ {
+ { 54, 52, 52, 52 },
+ { 52, 52, 48, 48 },
+ { 48, 48, 48, 40 },
+ { 40, 40, 40, 40 },
+ },
+ {
+ { 55, 52, 52, 52 },
+ { 54, 54, 48, 48 },
+ { 52, 52, 52, 40 },
+ { 48, 48, 48, 48 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 53, 52, 52, 52 },
+ { 50, 50, 48, 48 },
+ { 44, 44, 44, 40 },
+ { 32, 32, 32, 32 },
+ },
+ {
+ { 54, 52, 52, 52 },
+ { 52, 52, 48, 48 },
+ { 48, 48, 48, 40 },
+ { 40, 40, 40, 40 },
+ },
+ {
+ { 55, 52, 52, 52 },
+ { 54, 54, 48, 48 },
+ { 52, 52, 52, 40 },
+ { 48, 48, 48, 48 },
+ },
+ {
+ { 53, 53, 53, 53 },
+ { 50, 50, 50, 50 },
+ { 44, 44, 44, 44 },
+ { 32, 32, 32, 32 },
+ },
+ {
+ { 55, 55, 55, 55 },
+ { 54, 54, 54, 54 },
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 53, 52, 52, 52 },
+ { 50, 50, 48, 48 },
+ { 44, 44, 44, 40 },
+ { 32, 32, 32, 32 },
+ },
+ {
+ { 54, 52, 52, 52 },
+ { 52, 52, 48, 48 },
+ { 48, 48, 48, 40 },
+ { 40, 40, 40, 40 },
+ },
+ {
+ { 55, 52, 52, 52 },
+ { 54, 54, 48, 48 },
+ { 52, 52, 52, 40 },
+ { 48, 48, 48, 48 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 53, 52, 52, 52 },
+ { 50, 50, 48, 48 },
+ { 44, 44, 44, 40 },
+ { 32, 32, 32, 32 },
+ },
+ {
+ { 54, 52, 52, 52 },
+ { 52, 52, 48, 48 },
+ { 48, 48, 48, 40 },
+ { 40, 40, 40, 40 },
+ },
+ {
+ { 55, 52, 52, 52 },
+ { 54, 54, 48, 48 },
+ { 52, 52, 52, 40 },
+ { 48, 48, 48, 48 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 53, 52, 52, 52 },
+ { 50, 50, 48, 48 },
+ { 44, 44, 44, 40 },
+ { 32, 32, 32, 32 },
+ },
+ {
+ { 54, 52, 52, 52 },
+ { 52, 52, 48, 48 },
+ { 48, 48, 48, 40 },
+ { 40, 40, 40, 40 },
+ },
+ {
+ { 55, 52, 52, 52 },
+ { 54, 54, 48, 48 },
+ { 52, 52, 52, 40 },
+ { 48, 48, 48, 48 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 53, 52, 52, 52 },
+ { 50, 50, 48, 48 },
+ { 44, 44, 44, 40 },
+ { 32, 32, 32, 32 },
+ },
+ {
+ { 54, 52, 52, 52 },
+ { 52, 52, 48, 48 },
+ { 48, 48, 48, 40 },
+ { 40, 40, 40, 40 },
+ },
+ {
+ { 55, 52, 52, 52 },
+ { 54, 54, 48, 48 },
+ { 52, 52, 52, 40 },
+ { 48, 48, 48, 48 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 52, 52, 52, 52 },
+ { 48, 48, 48, 48 },
+ { 40, 40, 40, 40 },
+ { 24, 24, 24, 24 },
+ },
+ {
+ { 51, 52, 52, 52 },
+ { 46, 46, 48, 48 },
+ { 36, 36, 36, 40 },
+ { 16, 16, 16, 16 },
+ },
+ {
+ { 50, 52, 52, 52 },
+ { 44, 44, 48, 48 },
+ { 32, 32, 32, 40 },
+ { 8, 8, 8, 8 },
+ },
+ {
+ { 49, 52, 52, 52 },
+ { 42, 42, 48, 48 },
+ { 28, 28, 28, 40 },
+ { 0, 0, 0, 0 },
+ },
+ {
+ { 51, 52, 52, 52 },
+ { 46, 46, 48, 48 },
+ { 36, 36, 36, 40 },
+ { 16, 16, 16, 16 },
+ },
+ {
+ { 50, 52, 52, 52 },
+ { 44, 44, 48, 48 },
+ { 32, 32, 32, 40 },
+ { 8, 8, 8, 8 },
+ },
+ {
+ { 49, 52, 52, 52 },
+ { 42, 42, 48, 48 },
+ { 28, 28, 28, 40 },
+ { 0, 0, 0, 0 },
+ },
+ {
+ { 51, 52, 52, 52 },
+ { 46, 46, 48, 48 },
+ { 36, 36, 36, 40 },
+ { 16, 16, 16, 16 },
+ },
+ {
+ { 50, 52, 52, 52 },
+ { 44, 44, 48, 48 },
+ { 32, 32, 32, 40 },
+ { 8, 8, 8, 8 },
+ },
+ {
+ { 49, 52, 52, 52 },
+ { 42, 42, 48, 48 },
+ { 28, 28, 28, 40 },
+ { 0, 0, 0, 0 },
+ },
+ {
+ { 51, 52, 52, 52 },
+ { 46, 46, 48, 48 },
+ { 36, 36, 36, 40 },
+ { 16, 16, 16, 16 },
+ },
+ {
+ { 50, 52, 52, 52 },
+ { 44, 44, 48, 48 },
+ { 32, 32, 32, 40 },
+ { 8, 8, 8, 8 },
+ },
+ {
+ { 49, 52, 52, 52 },
+ { 42, 42, 48, 48 },
+ { 28, 28, 28, 40 },
+ { 0, 0, 0, 0 },
+ },
+ {
+ { 51, 51, 51, 51 },
+ { 46, 46, 46, 46 },
+ { 36, 36, 36, 36 },
+ { 16, 16, 16, 16 },
+ },
+ {
+ { 49, 49, 49, 49 },
+ { 42, 42, 42, 42 },
+ { 28, 28, 28, 28 },
+ { 0, 0, 0, 0 },
+ },
+ {
+ { 51, 52, 52, 52 },
+ { 46, 46, 48, 48 },
+ { 36, 36, 36, 40 },
+ { 16, 16, 16, 16 },
+ },
+ {
+ { 50, 52, 52, 52 },
+ { 44, 44, 48, 48 },
+ { 32, 32, 32, 40 },
+ { 8, 8, 8, 8 },
+ },
+ {
+ { 49, 52, 52, 52 },
+ { 42, 42, 48, 48 },
+ { 28, 28, 28, 40 },
+ { 0, 0, 0, 0 },
+ },
+ {
+ { 51, 52, 52, 52 },
+ { 46, 46, 48, 48 },
+ { 36, 36, 36, 40 },
+ { 16, 16, 16, 16 },
+ },
+ {
+ { 50, 52, 52, 52 },
+ { 44, 44, 48, 48 },
+ { 32, 32, 32, 40 },
+ { 8, 8, 8, 8 },
+ },
+ {
+ { 49, 52, 52, 52 },
+ { 42, 42, 48, 48 },
+ { 28, 28, 28, 40 },
+ { 0, 0, 0, 0 },
+ },
+ {
+ { 51, 52, 52, 52 },
+ { 46, 46, 48, 48 },
+ { 36, 36, 36, 40 },
+ { 16, 16, 16, 16 },
+ },
+ {
+ { 50, 52, 52, 52 },
+ { 44, 44, 48, 48 },
+ { 32, 32, 32, 40 },
+ { 8, 8, 8, 8 },
+ },
+ {
+ { 49, 52, 52, 52 },
+ { 42, 42, 48, 48 },
+ { 28, 28, 28, 40 },
+ { 0, 0, 0, 0 },
+ },
+ {
+ { 51, 52, 52, 52 },
+ { 46, 46, 48, 48 },
+ { 36, 36, 36, 40 },
+ { 16, 16, 16, 16 },
+ },
+ {
+ { 50, 52, 52, 52 },
+ { 44, 44, 48, 48 },
+ { 32, 32, 32, 40 },
+ { 8, 8, 8, 8 },
+ },
+ {
+ { 49, 52, 52, 52 },
+ { 42, 42, 48, 48 },
+ { 28, 28, 28, 40 },
+ { 0, 0, 0, 0 },
+ },
+};
+
+const uint8_t ff_vvc_gpm_weights[6][VVC_GPM_WEIGHT_SIZE * VVC_GPM_WEIGHT_SIZE] = {
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ },
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 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, 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, 4, 6, 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, 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, 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, 4, 6, 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, 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, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ },
+ {
+ 0, 0, 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, 3, 5, 7, 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, 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,
+ 0, 0, 0, 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, 4, 6, 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, 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,
+ 0, 0, 0, 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, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 4, 6, 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, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 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, 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, 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, 4, 6, 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, 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, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 3, 5, 7, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 4, 6, 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,
+ },
+ {
+ 4, 5, 6, 7, 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, 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,
+ 3, 4, 5, 6, 7, 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, 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,
+ 2, 3, 4, 5, 6, 7, 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, 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,
+ 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 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, 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, 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,
+ 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, 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, 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,
+ 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, 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, 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,
+ 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, 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, 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,
+ 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, 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, 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,
+ 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, 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, 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,
+ 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, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 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, 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, 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, 8, 8, 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, 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, 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, 8, 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, 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, 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, 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, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ },
+ {
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 8, 8, 8,
+ 6, 7, 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, 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,
+ 4, 5, 6, 7, 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, 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,
+ 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 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,
+ 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, 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, 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,
+ 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, 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, 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,
+ 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, 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, 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,
+ 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, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 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, 8, 8, 8, 8, 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, 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, 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, 8, 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, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 8, 8, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
+ 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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,
+ 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, 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, 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, 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,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ },
+};
diff --git a/libavcodec/vvc/vvc_data.h b/libavcodec/vvc/vvc_data.h
new file mode 100644
index 0000000000..1f9a463bc7
--- /dev/null
+++ b/libavcodec/vvc/vvc_data.h
@@ -0,0 +1,80 @@
+/*
+ * VVC shared tables
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVC_DATA_H
+#define AVCODEC_VVC_VVC_DATA_H
+
+#include <stdint.h>
+
+extern const uint8_t ff_vvc_diag_scan_x[5][5][16 * 16];
+extern const uint8_t ff_vvc_diag_scan_y[5][5][16 * 16];
+
+extern const uint8_t ff_vvc_scaling_pred_8[8 * 8];
+extern const uint8_t ff_vvc_scaling_pred_16[8 * 8];
+extern const int ff_vvc_scaling_list0[8 * 8];
+
+extern const int8_t ff_vvc_dct8_4x4[4][4];
+extern const int8_t ff_vvc_dct8_8x8[8][8];
+extern const int8_t ff_vvc_dct8_16x16[16][16];
+extern const int8_t ff_vvc_dct8_32x32[32][32];
+extern const int8_t ff_vvc_dst7_4x4[4][4];
+extern const int8_t ff_vvc_dst7_8x8[8][8];
+extern const int8_t ff_vvc_dst7_16x16[16][16];
+extern const int8_t ff_vvc_dst7_32x32[32][32];
+extern const int8_t ff_vvc_lfnst_4x4[4][2][16][16];
+extern const int8_t ff_vvc_lfnst_8x8[4][2][16][48];
+extern const uint8_t ff_vvc_lfnst_tr_set_index[95];
+extern uint8_t ff_vvc_default_scale_m[64 * 64];
+
+#define VVC_INTER_FILTER_TYPES 3
+#define VVC_INTER_LUMA_FACTS 16
+#define VVC_INTER_LUMA_TAPS 8
+#define VVC_INTER_CHROMA_FACTS 32
+#define VVC_INTER_CHROMA_TAPS 4
+#define VVC_INTER_LUMA_DMVR_FACTS 16
+#define VVC_INTER_LUMA_DMVR_TAPS 2
+extern const int8_t ff_vvc_inter_luma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_LUMA_FACTS][VVC_INTER_LUMA_TAPS];
+extern const int8_t ff_vvc_inter_chroma_filters[VVC_INTER_FILTER_TYPES][VVC_INTER_CHROMA_FACTS][VVC_INTER_CHROMA_TAPS];
+extern const int8_t ff_vvc_inter_luma_dmvr_filters[VVC_INTER_LUMA_DMVR_FACTS][VVC_INTER_LUMA_DMVR_TAPS];
+
+#define VVC_INTRA_LUMA_TYPES 2
+#define VVC_INTRA_LUMA_FACTS 32
+#define VVC_INTRA_LUMA_TAPS 4
+extern const int8_t ff_vvc_intra_luma_filter[VVC_INTRA_LUMA_TYPES][VVC_INTRA_LUMA_FACTS][VVC_INTRA_LUMA_TAPS];
+
+#define VVC_GPM_NUM_PARTITION 64
+#define VVC_GPM_NUM_ANGLES 32
+#define VVC_GPM_WEIGHT_SIZE 112
+extern const uint8_t ff_vvc_gpm_angle_idx[VVC_GPM_NUM_PARTITION];
+extern const uint8_t ff_vvc_gpm_distance_idx[VVC_GPM_NUM_PARTITION];
+extern const int8_t ff_vvc_gpm_distance_lut[VVC_GPM_NUM_ANGLES];
+extern const uint8_t ff_vvc_gpm_angle_to_mirror[VVC_GPM_NUM_ANGLES];
+extern const uint8_t ff_vvc_gpm_angle_to_weights_idx[VVC_GPM_NUM_ANGLES];
+extern const uint8_t ff_vvc_gpm_weights_offset_x[VVC_GPM_NUM_PARTITION][4][4];
+extern const uint8_t ff_vvc_gpm_weights_offset_y[VVC_GPM_NUM_PARTITION][4][4];
+extern const uint8_t ff_vvc_gpm_weights[6][VVC_GPM_WEIGHT_SIZE * VVC_GPM_WEIGHT_SIZE];
+
+extern const int16_t ff_vvc_alf_fix_filt_coeff[64][12];
+extern const uint8_t ff_vvc_alf_class_to_filt_map[16][25];
+extern const uint8_t ff_vvc_alf_aps_class_to_filt_map[25];
+
+const uint8_t* ff_vvc_get_mip_matrix(const int size_id, const int mode_idx);
+
+#endif /* AVCODEC_VVC_VVC_DATA_H */
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH v7 03/14] vvcdec: add parameter parser for sps, pps, ph, sh
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 02/14] vvcdec: add vvc_data Nuo Mi
@ 2023-12-10 15:57 ` Nuo Mi
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 04/14] vvcdec: add cabac decoder Nuo Mi
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 15:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
---
libavcodec/vvc/Makefile | 1 +
libavcodec/vvc/vvc_ps.c | 1150 +++++++++++++++++++++++++++++++++++++++
libavcodec/vvc/vvc_ps.h | 263 +++++++++
libavcodec/vvc/vvcdec.h | 7 +
4 files changed, 1421 insertions(+)
create mode 100644 libavcodec/vvc/vvc_ps.c
create mode 100644 libavcodec/vvc/vvc_ps.h
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index 8a5c66ab13..b2b187ac6f 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -3,3 +3,4 @@ clean::
OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
vvc/vvc_data.o \
+ vvc/vvc_ps.o \
diff --git a/libavcodec/vvc/vvc_ps.c b/libavcodec/vvc/vvc_ps.c
new file mode 100644
index 0000000000..7f1d9f6d26
--- /dev/null
+++ b/libavcodec/vvc/vvc_ps.c
@@ -0,0 +1,1150 @@
+/*
+ * VVC parameter set parser
+ *
+ * Copyright (C) 2023 Nuo Mi
+ * Copyright (C) 2022 Xu Mu
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "libavcodec/cbs_h266.h"
+#include "libavutil/imgutils.h"
+#include "libavcodec/refstruct.h"
+#include "vvc_data.h"
+#include "vvc_ps.h"
+#include "vvcdec.h"
+
+static int sps_map_pixel_format(VVCSPS *sps, void *log_ctx)
+{
+ const H266RawSPS *r = sps->r;
+ const AVPixFmtDescriptor *desc;
+
+ switch (sps->bit_depth) {
+ case 8:
+ if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY8;
+ if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P;
+ if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P;
+ if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P;
+ break;
+ case 10:
+ if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY10;
+ if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P10;
+ if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P10;
+ if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P10;
+ break;
+ case 12:
+ if (r->sps_chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY12;
+ if (r->sps_chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P12;
+ if (r->sps_chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P12;
+ if (r->sps_chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P12;
+ break;
+ default:
+ av_log(log_ctx, AV_LOG_ERROR,
+ "The following bit-depths are currently specified: 8, 10, 12 bits, "
+ "chroma_format_idc is %d, depth is %d\n",
+ r->sps_chroma_format_idc, sps->bit_depth);
+ return AVERROR_INVALIDDATA;
+ }
+
+ desc = av_pix_fmt_desc_get(sps->pix_fmt);
+ if (!desc)
+ return AVERROR(EINVAL);
+
+ sps->hshift[0] = sps->vshift[0] = 0;
+ sps->hshift[2] = sps->hshift[1] = desc->log2_chroma_w;
+ sps->vshift[2] = sps->vshift[1] = desc->log2_chroma_h;
+
+ sps->pixel_shift = sps->bit_depth > 8;
+
+ return 0;
+}
+
+static int sps_bit_depth(VVCSPS *sps, void *log_ctx)
+{
+ const H266RawSPS *r = sps->r;
+
+ sps->bit_depth = r->sps_bitdepth_minus8 + 8;
+ sps->qp_bd_offset = 6 * (sps->bit_depth - 8);
+ sps->log2_transform_range =
+ r->sps_extended_precision_flag ? FFMAX(15, FFMIN(20, sps->bit_depth + 6)) : 15;
+ return sps_map_pixel_format(sps, log_ctx);
+}
+
+static int sps_chroma_qp_table(VVCSPS *sps)
+{
+ const H266RawSPS *r = sps->r;
+ const int num_qp_tables = r->sps_same_qp_table_for_chroma_flag ?
+ 1 : (r->sps_joint_cbcr_enabled_flag ? 3 : 2);
+
+ for (int i = 0; i < num_qp_tables; i++) {
+ int num_points_in_qp_table;
+ int8_t qp_in[VVC_MAX_POINTS_IN_QP_TABLE], qp_out[VVC_MAX_POINTS_IN_QP_TABLE];
+ unsigned int delta_qp_in[VVC_MAX_POINTS_IN_QP_TABLE];
+ int off = sps->qp_bd_offset;
+
+ num_points_in_qp_table = r->sps_num_points_in_qp_table_minus1[i] + 1;
+
+ qp_out[0] = qp_in[0] = r->sps_qp_table_start_minus26[i] + 26;
+ for (int j = 0; j < num_points_in_qp_table; j++ ) {
+ delta_qp_in[j] = r->sps_delta_qp_in_val_minus1[i][j] + 1;
+ qp_in[j+1] = qp_in[j] + delta_qp_in[j];
+ qp_out[j+1] = qp_out[j] + (r->sps_delta_qp_in_val_minus1[i][j] ^ r->sps_delta_qp_diff_val[i][j]);
+ }
+ sps->chroma_qp_table[i][qp_in[0] + off] = qp_out[0];
+ for (int k = qp_in[0] - 1 + off; k >= 0; k--)
+ sps->chroma_qp_table[i][k] = av_clip(sps->chroma_qp_table[i][k+1]-1, -off, 63);
+
+ for (int j = 0; j < num_points_in_qp_table; j++) {
+ int sh = delta_qp_in[j] >> 1;
+ for (int k = qp_in[j] + 1 + off, m = 1; k <= qp_in[j+1] + off; k++, m++) {
+ sps->chroma_qp_table[i][k] = sps->chroma_qp_table[i][qp_in[j] + off] +
+ ((qp_out[j+1] - qp_out[j]) * m + sh) / delta_qp_in[j];
+ }
+ }
+ for (int k = qp_in[num_points_in_qp_table] + 1 + off; k <= 63 + off; k++)
+ sps->chroma_qp_table[i][k] = av_clip(sps->chroma_qp_table[i][k-1] + 1, -sps->qp_bd_offset, 63);
+ }
+ if (r->sps_same_qp_table_for_chroma_flag) {
+ memcpy(&sps->chroma_qp_table[1], &sps->chroma_qp_table[0], sizeof(sps->chroma_qp_table[0]));
+ memcpy(&sps->chroma_qp_table[2], &sps->chroma_qp_table[0], sizeof(sps->chroma_qp_table[0]));
+ }
+
+ return 0;
+}
+
+static void sps_poc(VVCSPS *sps)
+{
+ sps->max_pic_order_cnt_lsb = 1 << (sps->r->sps_log2_max_pic_order_cnt_lsb_minus4 + 4);
+}
+
+static void sps_inter(VVCSPS *sps)
+{
+ const H266RawSPS *r = sps->r;
+
+ sps->max_num_merge_cand = 6 - r->sps_six_minus_max_num_merge_cand;
+ sps->max_num_ibc_merge_cand = 6 - r->sps_six_minus_max_num_ibc_merge_cand;
+
+ if (sps->r->sps_gpm_enabled_flag) {
+ sps->max_num_gpm_merge_cand = 2;
+ if (sps->max_num_merge_cand >= 3)
+ sps->max_num_gpm_merge_cand = sps->max_num_merge_cand - r->sps_max_num_merge_cand_minus_max_num_gpm_cand;
+ }
+
+ sps->log2_parallel_merge_level = r->sps_log2_parallel_merge_level_minus2 + 2;
+}
+
+static void sps_partition_constraints(VVCSPS* sps)
+{
+ const H266RawSPS *r = sps->r;
+
+ sps->ctb_log2_size_y = r->sps_log2_ctu_size_minus5 + 5;
+ sps->ctb_size_y = 1 << sps->ctb_log2_size_y;
+ sps->min_cb_log2_size_y = r->sps_log2_min_luma_coding_block_size_minus2 + 2;
+ sps->min_cb_size_y = 1 << sps->min_cb_log2_size_y;
+ sps->max_tb_size_y = 1 << (r->sps_max_luma_transform_size_64_flag ? 6 : 5);
+ sps->max_ts_size = 1 << (r->sps_log2_transform_skip_max_size_minus2 + 2);
+}
+
+static void sps_ladf(VVCSPS* sps)
+{
+ const H266RawSPS *r = sps->r;
+
+ if (r->sps_ladf_enabled_flag) {
+ sps->num_ladf_intervals = r->sps_num_ladf_intervals_minus2 + 2;
+ sps->ladf_interval_lower_bound[0] = 0;
+ for (int i = 0; i < sps->num_ladf_intervals - 1; i++) {
+ sps->ladf_interval_lower_bound[i + 1] =
+ sps->ladf_interval_lower_bound[i] + r->sps_ladf_delta_threshold_minus1[i] + 1;
+ }
+ }
+}
+
+static int sps_derive(VVCSPS *sps, void *log_ctx)
+{
+ int ret;
+ const H266RawSPS *r = sps->r;
+
+ sps->width = r->sps_pic_width_max_in_luma_samples;
+ sps->height = r->sps_pic_height_max_in_luma_samples;
+
+ ret = sps_bit_depth(sps, log_ctx);
+ if (ret < 0)
+ return ret;
+ sps_poc(sps);
+ sps_inter(sps);
+ sps_partition_constraints(sps);
+ sps_ladf(sps);
+ if (r->sps_chroma_format_idc != 0)
+ sps_chroma_qp_table(sps);
+
+ return 0;
+}
+
+static void sps_free(FFRefStructOpaque opaque, void *obj)
+{
+ VVCSPS *sps = obj;
+ ff_refstruct_unref(&sps->r);
+}
+
+static const VVCSPS *sps_alloc(const H266RawSPS *rsps, void *log_ctx)
+{
+ int ret;
+ VVCSPS *sps = ff_refstruct_alloc_ext(sizeof(*sps), 0, NULL, sps_free);
+
+ if (!sps)
+ return NULL;
+
+ ff_refstruct_replace(&sps->r, rsps);
+
+ ret = sps_derive(sps, log_ctx);
+ if (ret < 0)
+ goto fail;
+
+ return sps;
+
+fail:
+ ff_refstruct_unref(&sps);
+ return NULL;
+}
+
+static int decode_sps(VVCParamSets *ps, const H266RawSPS *rsps, void *log_ctx)
+{
+ const int sps_id = rsps->sps_seq_parameter_set_id;
+ const VVCSPS *old_sps = ps->sps_list[sps_id];
+ const VVCSPS *sps;
+
+ if (old_sps && old_sps->r == rsps)
+ return 0;
+
+ sps = sps_alloc(rsps, log_ctx);
+ if (!sps)
+ return AVERROR(ENOMEM);
+
+ ff_refstruct_unref(&ps->sps_list[sps_id]);
+ ps->sps_list[sps_id] = sps;
+
+ return 0;
+}
+
+static void pps_chroma_qp_offset(VVCPPS *pps)
+{
+ pps->chroma_qp_offset[CB - 1] = pps->r->pps_cb_qp_offset;
+ pps->chroma_qp_offset[CR - 1] = pps->r->pps_cr_qp_offset;
+ pps->chroma_qp_offset[JCBCR - 1]= pps->r->pps_joint_cbcr_qp_offset_value;
+ for (int i = 0; i < 6; i++) {
+ pps->chroma_qp_offset_list[i][CB - 1] = pps->r->pps_cb_qp_offset_list[i];
+ pps->chroma_qp_offset_list[i][CR - 1] = pps->r->pps_cr_qp_offset_list[i];
+ pps->chroma_qp_offset_list[i][JCBCR - 1]= pps->r->pps_joint_cbcr_qp_offset_list[i];
+ }
+}
+
+static void pps_width_height(VVCPPS *pps, const VVCSPS *sps)
+{
+ const H266RawPPS *r = pps->r;
+
+ pps->width = r->pps_pic_width_in_luma_samples;
+ pps->height = r->pps_pic_height_in_luma_samples;
+
+ pps->ctb_width = AV_CEIL_RSHIFT(pps->width, sps->ctb_log2_size_y);
+ pps->ctb_height = AV_CEIL_RSHIFT(pps->height, sps->ctb_log2_size_y);
+ pps->ctb_count = pps->ctb_width * pps->ctb_height;
+
+ pps->min_cb_width = pps->width >> sps->min_cb_log2_size_y;
+ pps->min_cb_height = pps->height >> sps->min_cb_log2_size_y;
+
+ pps->min_pu_width = pps->width >> MIN_PU_LOG2;
+ pps->min_pu_height = pps->height >> MIN_PU_LOG2;
+ pps->min_tu_width = pps->width >> MIN_TU_LOG2;
+ pps->min_tu_height = pps->height >> MIN_TU_LOG2;
+
+ pps->width32 = AV_CEIL_RSHIFT(pps->width, 5);
+ pps->height32 = AV_CEIL_RSHIFT(pps->height, 5);
+ pps->width64 = AV_CEIL_RSHIFT(pps->width, 6);
+ pps->height64 = AV_CEIL_RSHIFT(pps->height, 6);
+}
+
+static int pps_bd(VVCPPS *pps)
+{
+ const H266RawPPS *r = pps->r;
+
+ pps->col_bd = av_calloc(r->num_tile_columns + 1, sizeof(*pps->col_bd));
+ pps->row_bd = av_calloc(r->num_tile_rows + 1, sizeof(*pps->row_bd));
+ pps->ctb_to_col_bd = av_calloc(pps->ctb_width + 1, sizeof(*pps->ctb_to_col_bd));
+ pps->ctb_to_row_bd = av_calloc(pps->ctb_height + 1, sizeof(*pps->ctb_to_col_bd));
+ if (!pps->col_bd || !pps->row_bd || !pps->ctb_to_col_bd || !pps->ctb_to_row_bd)
+ return AVERROR(ENOMEM);
+
+ for (int i = 0, j = 0; i < r->num_tile_columns; i++) {
+ pps->col_bd[i] = j;
+ j += r->col_width_val[i];
+ for (int k = pps->col_bd[i]; k < j; k++)
+ pps->ctb_to_col_bd[k] = pps->col_bd[i];
+ }
+
+ for (int i = 0, j = 0; i < r->num_tile_rows; i++) {
+ pps->row_bd[i] = j;
+ j += r->row_height_val[i];
+ for (int k = pps->row_bd[i]; k < j; k++)
+ pps->ctb_to_row_bd[k] = pps->row_bd[i];
+ }
+ return 0;
+}
+
+
+static int next_tile_idx(int tile_idx, const int i, const H266RawPPS *r)
+{
+ if (r->pps_tile_idx_delta_present_flag) {
+ tile_idx += r->pps_tile_idx_delta_val[i];
+ } else {
+ tile_idx += r->pps_slice_width_in_tiles_minus1[i] + 1;
+ if (tile_idx % r->num_tile_columns == 0)
+ tile_idx += (r->pps_slice_height_in_tiles_minus1[i]) * r->num_tile_columns;
+ }
+ return tile_idx;
+}
+
+static void tile_xy(int *tile_x, int *tile_y, const int tile_idx, const VVCPPS *pps)
+{
+ *tile_x = tile_idx % pps->r->num_tile_columns;
+ *tile_y = tile_idx / pps->r->num_tile_columns;
+}
+
+static void ctu_xy(int *ctu_x, int *ctu_y, const int tile_x, const int tile_y, const VVCPPS *pps)
+{
+ *ctu_x = pps->col_bd[tile_x];
+ *ctu_y = pps->row_bd[tile_y];
+}
+
+static int ctu_rs(const int ctu_x, const int ctu_y, const VVCPPS *pps)
+{
+ return pps->ctb_width * ctu_y + ctu_x;
+}
+
+static int pps_add_ctus(VVCPPS *pps, int *off, const int ctu_x, const int ctu_y,
+ const int w, const int h)
+{
+ int start = *off;
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ pps->ctb_addr_in_slice[*off] = ctu_rs(ctu_x + x, ctu_y + y, pps);
+ (*off)++;
+ }
+ }
+ return *off - start;
+}
+
+static int pps_one_tile_slices(VVCPPS *pps, const int tile_idx, int i, int *off)
+{
+ const H266RawPPS *r = pps->r;
+ int ctu_x, ctu_y, ctu_y_end, tile_x, tile_y;
+
+ tile_xy(&tile_x, &tile_y, tile_idx, pps);
+ ctu_xy(&ctu_x, &ctu_y, tile_x, tile_y, pps);
+ ctu_y_end = ctu_y + r->row_height_val[tile_y];
+ while (ctu_y < ctu_y_end) {
+ pps->slice_start_offset[i] = *off;
+ pps->num_ctus_in_slice[i] = pps_add_ctus(pps, off, ctu_x, ctu_y,
+ r->col_width_val[tile_x], r->slice_height_in_ctus[i]);
+ ctu_y += r->slice_height_in_ctus[i++];
+ }
+ i--;
+ return i;
+}
+
+static void pps_multi_tiles_slice(VVCPPS *pps, const int tile_idx, const int i, int *off)
+{
+ const H266RawPPS *r = pps->r;
+ int ctu_x, ctu_y,tile_x, tile_y;
+
+ tile_xy(&tile_x, &tile_y, tile_idx, pps);
+ pps->slice_start_offset[i] = *off;
+ pps->num_ctus_in_slice[i] = 0;
+ for (int ty = tile_y; ty <= tile_y + r->pps_slice_height_in_tiles_minus1[i]; ty++) {
+ for (int tx = tile_x; tx <= tile_x + r->pps_slice_width_in_tiles_minus1[i]; tx++) {
+ ctu_xy(&ctu_x, &ctu_y, tx, ty, pps);
+ pps->num_ctus_in_slice[i] += pps_add_ctus(pps, off, ctu_x, ctu_y,
+ r->col_width_val[tx], r->row_height_val[ty]);
+ }
+ }
+}
+
+static void pps_rect_slice(VVCPPS* pps)
+{
+ const H266RawPPS* r = pps->r;
+ int tile_idx = 0, off = 0;
+
+ for (int i = 0; i < r->pps_num_slices_in_pic_minus1 + 1; i++) {
+ if (!r->pps_slice_width_in_tiles_minus1[i] &&
+ !r->pps_slice_height_in_tiles_minus1[i]) {
+ i = pps_one_tile_slices(pps, tile_idx, i, &off);
+ } else {
+ pps_multi_tiles_slice(pps, tile_idx, i, &off);
+
+ }
+ tile_idx = next_tile_idx(tile_idx, i, r);
+ }
+}
+
+static void pps_no_rect_slice(VVCPPS* pps)
+{
+ const H266RawPPS* r = pps->r;
+ int ctu_x, ctu_y, off = 0;
+
+ for (int tile_y = 0; tile_y < r->num_tile_rows; tile_y++) {
+ for (int tile_x = 0; tile_x < r->num_tile_columns; tile_x++) {
+ ctu_xy(&ctu_x, &ctu_y, tile_x, tile_y, pps);
+ pps_add_ctus(pps, &off, ctu_x, ctu_y, r->col_width_val[tile_x], r->row_height_val[tile_y]);
+ }
+ }
+}
+
+static int pps_slice_map(VVCPPS *pps)
+{
+ pps->ctb_addr_in_slice = av_calloc(pps->ctb_count, sizeof(*pps->ctb_addr_in_slice));
+ if (!pps->ctb_addr_in_slice)
+ return AVERROR(ENOMEM);
+
+ if (pps->r->pps_rect_slice_flag)
+ pps_rect_slice(pps);
+ else
+ pps_no_rect_slice(pps);
+
+ return 0;
+}
+
+static void pps_ref_wraparound_offset(VVCPPS *pps, const VVCSPS *sps)
+{
+ const H266RawPPS *r = pps->r;
+
+ if (r->pps_ref_wraparound_enabled_flag)
+ pps->ref_wraparound_offset = (pps->width / sps->min_cb_size_y) - r->pps_pic_width_minus_wraparound_offset;
+}
+
+static int pps_derive(VVCPPS *pps, const VVCSPS *sps)
+{
+ int ret;
+
+ pps_chroma_qp_offset(pps);
+ pps_width_height(pps, sps);
+
+ ret = pps_bd(pps);
+ if (ret < 0)
+ return ret;
+
+ ret = pps_slice_map(pps);
+ if (ret < 0)
+ return ret;
+
+ pps_ref_wraparound_offset(pps, sps);
+
+ return 0;
+}
+
+static void pps_free(FFRefStructOpaque opaque, void *obj)
+{
+ VVCPPS *pps = obj;
+
+ ff_refstruct_unref(&pps->r);
+
+ av_freep(&pps->col_bd);
+ av_freep(&pps->row_bd);
+ av_freep(&pps->ctb_to_col_bd);
+ av_freep(&pps->ctb_to_row_bd);
+ av_freep(&pps->ctb_addr_in_slice);
+}
+
+static const VVCPPS *pps_alloc(const H266RawPPS *rpps, const VVCSPS *sps)
+{
+ int ret;
+ VVCPPS *pps = ff_refstruct_alloc_ext(sizeof(*pps), 0, NULL, pps_free);
+
+ if (!pps)
+ return NULL;
+
+ ff_refstruct_replace(&pps->r, rpps);
+
+ ret = pps_derive(pps, sps);
+ if (ret < 0)
+ goto fail;
+
+ return pps;
+
+fail:
+ ff_refstruct_unref(&pps);
+ return NULL;
+}
+
+static int decode_pps(VVCParamSets *ps, const H266RawPPS *rpps)
+{
+ int ret = 0;
+ const int pps_id = rpps->pps_pic_parameter_set_id;
+ const int sps_id = rpps->pps_seq_parameter_set_id;
+ const VVCPPS *old_pps = ps->pps_list[pps_id];
+ const VVCPPS *pps;
+
+ if (old_pps && old_pps->r == rpps)
+ return 0;
+
+ pps = pps_alloc(rpps, ps->sps_list[sps_id]);
+ if (!pps)
+ return AVERROR(ENOMEM);
+
+ ff_refstruct_unref(&ps->pps_list[pps_id]);
+ ps->pps_list[pps_id] = pps;
+
+ return ret;
+}
+
+static int decode_ps(VVCParamSets *ps, const CodedBitstreamH266Context *h266, void *log_ctx)
+{
+ const H266RawPictureHeader *ph = h266->ph;
+ const H266RawPPS *rpps;
+ const H266RawSPS *rsps;
+ int ret;
+
+ if (!ph)
+ return AVERROR_INVALIDDATA;
+
+ rpps = h266->pps[ph->ph_pic_parameter_set_id];
+ if (!rpps)
+ return AVERROR_INVALIDDATA;
+
+ rsps = h266->sps[rpps->pps_seq_parameter_set_id];
+ if (!rsps)
+ return AVERROR_INVALIDDATA;
+
+ ret = decode_sps(ps, rsps, log_ctx);
+ if (ret < 0)
+ return ret;
+
+ ret = decode_pps(ps, rpps);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+#define WEIGHT_TABLE(x) \
+ w->nb_weights[L##x] = r->num_weights_l##x; \
+ for (int i = 0; i < w->nb_weights[L##x]; i++) { \
+ w->weight_flag[L##x][LUMA][i] = r->luma_weight_l##x##_flag[i]; \
+ w->weight_flag[L##x][CHROMA][i] = r->chroma_weight_l##x##_flag[i]; \
+ w->weight[L##x][LUMA][i] = denom[LUMA] + r->delta_luma_weight_l##x[i]; \
+ w->offset[L##x][LUMA][i] = r->luma_offset_l##x[i]; \
+ for (int j = CB; j <= CR; j++) { \
+ w->weight[L##x][j][i] = denom[CHROMA] + r->delta_chroma_weight_l##x[i][j - 1]; \
+ w->offset[L##x][j][i] = 128 + r->delta_chroma_offset_l##x[i][j - 1]; \
+ w->offset[L##x][j][i] -= (128 * w->weight[L##x][j][i]) >> w->log2_denom[CHROMA]; \
+ w->offset[L##x][j][i] = av_clip_intp2(w->offset[L##x][j][i], 7); \
+ } \
+ } \
+
+static void pred_weight_table(PredWeightTable *w, const H266RawPredWeightTable *r)
+{
+ int denom[2];
+
+ w->log2_denom[LUMA] = r->luma_log2_weight_denom;
+ w->log2_denom[CHROMA] = w->log2_denom[LUMA] + r->delta_chroma_log2_weight_denom;
+ denom[LUMA] = 1 << w->log2_denom[LUMA];
+ denom[CHROMA] = 1 << w->log2_denom[CHROMA];
+ WEIGHT_TABLE(0)
+ WEIGHT_TABLE(1)
+}
+
+// 8.3.1 Decoding process for picture order count
+static int ph_compute_poc(const H266RawPictureHeader *ph, const H266RawSPS *sps, const int poc_tid0, const int is_clvss)
+{
+ const int max_poc_lsb = 1 << (sps->sps_log2_max_pic_order_cnt_lsb_minus4 + 4);
+ const int prev_poc_lsb = poc_tid0 % max_poc_lsb;
+ const int prev_poc_msb = poc_tid0 - prev_poc_lsb;
+ const int poc_lsb = ph->ph_pic_order_cnt_lsb;
+ int poc_msb;
+
+ if (ph->ph_poc_msb_cycle_present_flag) {
+ poc_msb = ph->ph_poc_msb_cycle_val * max_poc_lsb;
+ } else if (is_clvss) {
+ poc_msb = 0;
+ } else {
+ if (poc_lsb < prev_poc_lsb && prev_poc_lsb - poc_lsb >= max_poc_lsb / 2)
+ poc_msb = prev_poc_msb + max_poc_lsb;
+ else if (poc_lsb > prev_poc_lsb && poc_lsb - prev_poc_lsb > max_poc_lsb / 2)
+ poc_msb = prev_poc_msb - max_poc_lsb;
+ else
+ poc_msb = prev_poc_msb;
+ }
+
+ return poc_msb + poc_lsb;
+}
+
+static av_always_inline uint16_t lmcs_derive_lut_sample(uint16_t sample,
+ uint16_t *pivot1, uint16_t *pivot2, uint16_t *scale_coeff, const int idx, const int max)
+{
+ const int lut_sample =
+ pivot1[idx] + ((scale_coeff[idx] * (sample - pivot2[idx]) + (1<< 10)) >> 11);
+ return av_clip(lut_sample, 0, max - 1);
+}
+
+//8.8.2.2 Inverse mapping process for a luma sample
+static int lmcs_derive_lut(VVCLMCS *lmcs, const H266RawAPS *rlmcs, const H266RawSPS *sps)
+{
+ const int bit_depth = (sps->sps_bitdepth_minus8 + 8);
+ const int max = (1 << bit_depth);
+ const int org_cw = max / LMCS_MAX_BIN_SIZE;
+ const int shift = av_log2(org_cw);
+ const int off = 1 << (shift - 1);
+ int cw[LMCS_MAX_BIN_SIZE];
+ uint16_t input_pivot[LMCS_MAX_BIN_SIZE];
+ uint16_t scale_coeff[LMCS_MAX_BIN_SIZE];
+ uint16_t inv_scale_coeff[LMCS_MAX_BIN_SIZE];
+ int i, delta_crs;
+ if (bit_depth > LMCS_MAX_BIT_DEPTH)
+ return AVERROR_PATCHWELCOME;
+
+ if (!rlmcs)
+ return AVERROR_INVALIDDATA;
+
+ lmcs->min_bin_idx = rlmcs->lmcs_min_bin_idx;
+ lmcs->max_bin_idx = LMCS_MAX_BIN_SIZE - 1 - rlmcs->lmcs_min_bin_idx;
+
+ memset(cw, 0, sizeof(cw));
+ for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++)
+ cw[i] = org_cw + (1 - 2 * rlmcs->lmcs_delta_sign_cw_flag[i]) * rlmcs->lmcs_delta_abs_cw[i];
+
+ delta_crs = (1 - 2 * rlmcs->lmcs_delta_sign_crs_flag) * rlmcs->lmcs_delta_abs_crs;
+
+ lmcs->pivot[0] = 0;
+ for (i = 0; i < LMCS_MAX_BIN_SIZE; i++) {
+ input_pivot[i] = i * org_cw;
+ lmcs->pivot[i + 1] = lmcs->pivot[i] + cw[i];
+ scale_coeff[i] = (cw[i] * (1 << 11) + off) >> shift;
+ if (cw[i] == 0) {
+ inv_scale_coeff[i] = 0;
+ lmcs->chroma_scale_coeff[i] = (1 << 11);
+ } else {
+ inv_scale_coeff[i] = org_cw * (1 << 11) / cw[i];
+ lmcs->chroma_scale_coeff[i] = org_cw * (1 << 11) / (cw[i] + delta_crs);
+ }
+ }
+
+ //derive lmcs_fwd_lut
+ for (uint16_t sample = 0; sample < max; sample++) {
+ const int idx_y = sample / org_cw;
+ const uint16_t fwd_sample = lmcs_derive_lut_sample(sample, lmcs->pivot,
+ input_pivot, scale_coeff, idx_y, max);
+ if (bit_depth > 8)
+ ((uint16_t *)lmcs->fwd_lut)[sample] = fwd_sample;
+ else
+ lmcs->fwd_lut[sample] = fwd_sample;
+
+ }
+
+ //derive lmcs_inv_lut
+ i = lmcs->min_bin_idx;
+ for (uint16_t sample = 0; sample < max; sample++) {
+ uint16_t inv_sample;
+ while (sample >= lmcs->pivot[i + 1] && i <= lmcs->max_bin_idx)
+ i++;
+
+ inv_sample = lmcs_derive_lut_sample(sample, input_pivot, lmcs->pivot,
+ inv_scale_coeff, i, max);
+
+ if (bit_depth > 8)
+ ((uint16_t *)lmcs->inv_lut)[sample] = inv_sample;
+ else
+ lmcs->inv_lut[sample] = inv_sample;
+ }
+
+ return 0;
+}
+
+static int ph_max_num_subblock_merge_cand(const H266RawSPS *sps, const H266RawPictureHeader *ph)
+{
+ if (sps->sps_affine_enabled_flag)
+ return 5 - sps->sps_five_minus_max_num_subblock_merge_cand;
+ return sps->sps_sbtmvp_enabled_flag && ph->ph_temporal_mvp_enabled_flag;
+}
+
+static int ph_derive(VVCPH *ph, const H266RawSPS *sps, const H266RawPPS *pps, const int poc_tid0, const int is_clvss)
+{
+ ph->max_num_subblock_merge_cand = ph_max_num_subblock_merge_cand(sps, ph->r);
+
+ ph->poc = ph_compute_poc(ph->r, sps, poc_tid0, is_clvss);
+
+ if (pps->pps_wp_info_in_ph_flag)
+ pred_weight_table(&ph->pwt, &ph->r->ph_pred_weight_table);
+
+ return 0;
+}
+
+static int decode_ph(VVCFrameParamSets *fps, const H266RawPictureHeader *rph, void *rph_ref,
+ const int poc_tid0, const int is_clvss)
+{
+ int ret;
+ VVCPH *ph = &fps->ph;
+ const H266RawSPS *sps = fps->sps->r;
+ const H266RawPPS *pps = fps->pps->r;
+
+ ph->r = rph;
+ ff_refstruct_replace(&ph->rref, rph_ref);
+ ret = ph_derive(ph, sps, pps, poc_tid0, is_clvss);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int decode_frame_ps(VVCFrameParamSets *fps, const VVCParamSets *ps,
+ const CodedBitstreamH266Context *h266, const int poc_tid0, const int is_clvss)
+{
+ const H266RawPictureHeader *ph = h266->ph;
+ const H266RawPPS *rpps;
+ int ret;
+
+ if (!ph)
+ return AVERROR_INVALIDDATA;
+
+ rpps = h266->pps[ph->ph_pic_parameter_set_id];
+ if (!rpps)
+ return AVERROR_INVALIDDATA;
+
+ ff_refstruct_replace(&fps->sps, ps->sps_list[rpps->pps_seq_parameter_set_id]);
+ ff_refstruct_replace(&fps->pps, ps->pps_list[rpps->pps_pic_parameter_set_id]);
+
+ ret = decode_ph(fps, ph, h266->ph_ref, poc_tid0, is_clvss);
+ if (ret < 0)
+ return ret;
+
+ if (ph->ph_explicit_scaling_list_enabled_flag)
+ ff_refstruct_replace(&fps->sl, ps->scaling_list[ph->ph_scaling_list_aps_id]);
+
+ if (ph->ph_lmcs_enabled_flag) {
+ ret = lmcs_derive_lut(&fps->lmcs, ps->lmcs_list[ph->ph_lmcs_aps_id], fps->sps->r);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (int i = 0; i < FF_ARRAY_ELEMS(fps->alf_list); i++)
+ ff_refstruct_replace(&fps->alf_list[i], ps->alf_list[i]);
+
+ return 0;
+}
+
+static void decode_recovery_flag(VVCContext *s)
+{
+ if (IS_IDR(s))
+ s->no_output_before_recovery_flag = 0;
+ else if (IS_CRA(s) || IS_GDR(s))
+ s->no_output_before_recovery_flag = s->last_eos;
+}
+
+static void decode_recovery_poc(VVCContext *s, const VVCPH *ph)
+{
+ if (s->no_output_before_recovery_flag) {
+ if (IS_GDR(s))
+ s->gdr_recovery_point_poc = ph->poc + ph->r->ph_recovery_poc_cnt;
+ if (!GDR_IS_RECOVERED(s) && s->gdr_recovery_point_poc <= ph->poc)
+ GDR_SET_RECOVERED(s);
+ }
+}
+
+int ff_vvc_decode_frame_ps(VVCFrameParamSets *fps, struct VVCContext *s)
+{
+ int ret = 0;
+ VVCParamSets *ps = &s->ps;
+ const CodedBitstreamH266Context *h266 = s->cbc->priv_data;
+
+ ret = decode_ps(ps, h266, s->avctx);
+ if (ret < 0)
+ return ret;
+
+ decode_recovery_flag(s);
+ ret = decode_frame_ps(fps, ps, h266, s->poc_tid0, IS_CLVSS(s));
+ decode_recovery_poc(s, &fps->ph);
+ return ret;
+}
+
+void ff_vvc_frame_ps_free(VVCFrameParamSets *fps)
+{
+ ff_refstruct_unref(&fps->sps);
+ ff_refstruct_unref(&fps->pps);
+ ff_refstruct_unref(&fps->ph.rref);
+ ff_refstruct_unref(&fps->sl);
+ for (int i = 0; i < FF_ARRAY_ELEMS(fps->alf_list); i++)
+ ff_refstruct_unref(&fps->alf_list[i]);
+}
+
+void ff_vvc_ps_uninit(VVCParamSets *ps)
+{
+ for (int i = 0; i < FF_ARRAY_ELEMS(ps->scaling_list); i++)
+ ff_refstruct_unref(&ps->scaling_list[i]);
+ for (int i = 0; i < FF_ARRAY_ELEMS(ps->lmcs_list); i++)
+ ff_refstruct_unref(&ps->lmcs_list[i]);
+ for (int i = 0; i < FF_ARRAY_ELEMS(ps->alf_list); i++)
+ ff_refstruct_unref(&ps->alf_list[i]);
+ for (int i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++)
+ ff_refstruct_unref(&ps->sps_list[i]);
+ for (int i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++)
+ ff_refstruct_unref(&ps->pps_list[i]);
+}
+
+enum {
+ APS_ALF,
+ APS_LMCS,
+ APS_SCALING,
+};
+
+static void alf_coeff(int16_t *coeff,
+ const uint8_t *abs, const uint8_t *sign, const int size)
+{
+ for (int i = 0; i < size; i++)
+ coeff[i] = (1 - 2 * sign[i]) * abs[i];
+}
+
+static void alf_coeff_cc(int16_t *coeff,
+ const uint8_t *mapped_abs, const uint8_t *sign)
+{
+ for (int i = 0; i < ALF_NUM_COEFF_CC; i++) {
+ int c = mapped_abs[i];;
+ if (c)
+ c = (1 - 2 * sign[i]) * (1 << (c - 1));
+ coeff[i] = c;
+ }
+}
+
+static void alf_luma(VVCALF *alf, const H266RawAPS *aps)
+{
+ if (!aps->alf_luma_filter_signal_flag)
+ return;
+
+ for (int i = 0; i < ALF_NUM_FILTERS_LUMA; i++) {
+ const int ref = aps->alf_luma_coeff_delta_idx[i];
+ const uint8_t *abs = aps->alf_luma_coeff_abs[ref];
+ const uint8_t *sign = aps->alf_luma_coeff_sign[ref];
+
+ alf_coeff(alf->luma_coeff[i], abs, sign, ALF_NUM_COEFF_LUMA);
+ memcpy(alf->luma_clip_idx[i], aps->alf_luma_clip_idx[ref],
+ sizeof(alf->luma_clip_idx[i]));
+ }
+}
+
+static void alf_chroma(VVCALF *alf, const H266RawAPS *aps)
+{
+ if (!aps->alf_chroma_filter_signal_flag)
+ return;
+
+ alf->num_chroma_filters = aps->alf_chroma_num_alt_filters_minus1 + 1;
+ for (int i = 0; i < alf->num_chroma_filters; i++) {
+ const uint8_t *abs = aps->alf_chroma_coeff_abs[i];
+ const uint8_t *sign = aps->alf_chroma_coeff_sign[i];
+
+ alf_coeff(alf->chroma_coeff[i], abs, sign, ALF_NUM_COEFF_CHROMA);
+ memcpy(alf->chroma_clip_idx[i], aps->alf_chroma_clip_idx[i],
+ sizeof(alf->chroma_clip_idx[i]));
+ }
+}
+
+static void alf_cc(VVCALF *alf, const H266RawAPS *aps)
+{
+ const uint8_t (*abs[])[ALF_NUM_COEFF_CC] =
+ { aps->alf_cc_cb_mapped_coeff_abs, aps->alf_cc_cr_mapped_coeff_abs };
+ const uint8_t (*sign[])[ALF_NUM_COEFF_CC] =
+ {aps->alf_cc_cb_coeff_sign, aps->alf_cc_cr_coeff_sign };
+ const int signaled[] = { aps->alf_cc_cb_filter_signal_flag, aps->alf_cc_cr_filter_signal_flag};
+
+ alf->num_cc_filters[0] = aps->alf_cc_cb_filters_signalled_minus1 + 1;
+ alf->num_cc_filters[1] = aps->alf_cc_cr_filters_signalled_minus1 + 1;
+
+ for (int idx = 0; idx < 2; idx++) {
+ if (signaled[idx]) {
+ for (int i = 0; i < alf->num_cc_filters[idx]; i++)
+ alf_coeff_cc(alf->cc_coeff[idx][i], abs[idx][i], sign[idx][i]);
+ }
+ }
+}
+
+static void alf_derive(VVCALF *alf, const H266RawAPS *aps)
+{
+ alf_luma(alf, aps);
+ alf_chroma(alf, aps);
+ alf_cc(alf, aps);
+}
+
+static int aps_decode_alf(const VVCALF **alf, const H266RawAPS *aps)
+{
+ VVCALF *a = ff_refstruct_allocz(sizeof(*a));
+ if (!a)
+ return AVERROR(ENOMEM);
+
+ alf_derive(a, aps);
+ ff_refstruct_replace(alf, a);
+ ff_refstruct_unref(&a);
+
+ return 0;
+}
+
+static int is_luma_list(const int id)
+{
+ return id % VVC_MAX_SAMPLE_ARRAYS == SL_START_4x4 || id == SL_START_64x64 + 1;
+}
+
+static int derive_matrix_size(const int id)
+{
+ return id < SL_START_4x4 ? 2 : (id < SL_START_8x8 ? 4 : 8);
+}
+
+// 7.4.3.20 Scaling list data semantics
+static void scaling_derive(VVCScalingList *sl, const H266RawAPS *aps)
+{
+ for (int id = 0; id < SL_MAX_ID; id++) {
+ const int matrix_size = derive_matrix_size(id);
+ const int log2_size = log2(matrix_size);
+ const int list_size = matrix_size * matrix_size;
+ int coeff[SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE];
+ const uint8_t *pred;
+ const int *scaling_list;
+ int dc = 0;
+
+ if (aps->aps_chroma_present_flag || is_luma_list(id)) {
+ if (!aps->scaling_list_copy_mode_flag[id]) {
+ int next_coef = 0;
+
+ if (id >= SL_START_16x16)
+ dc = next_coef = aps->scaling_list_dc_coef[id - SL_START_16x16];
+
+ for (int i = 0; i < list_size; i++) {
+ const int x = ff_vvc_diag_scan_x[3][3][i];
+ const int y = ff_vvc_diag_scan_y[3][3][i];
+
+ if (!(id >= SL_START_64x64 && x >= 4 && y >= 4))
+ next_coef += aps->scaling_list_delta_coef[id][i];
+ coeff[i] = next_coef;
+ }
+ }
+ }
+
+ //dc
+ if (id >= SL_START_16x16) {
+ if (!aps->scaling_list_copy_mode_flag[id] && !aps->scaling_list_pred_mode_flag[id]) {
+ sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 8;
+ } else if (!aps->scaling_list_pred_id_delta[id]) {
+ sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 16;
+ } else {
+ const int ref_id = id - aps->scaling_list_pred_id_delta[id];
+ if (ref_id >= SL_START_16x16)
+ dc += sl->scaling_matrix_dc_rec[ref_id - SL_START_16x16];
+ else
+ dc += sl->scaling_matrix_rec[ref_id][0];
+ sl->scaling_matrix_dc_rec[id - SL_START_16x16] = dc & 255;
+ }
+ }
+
+ //ac
+ scaling_list = aps->scaling_list_copy_mode_flag[id] ? ff_vvc_scaling_list0 : coeff;
+ if (!aps->scaling_list_copy_mode_flag[id] && !aps->scaling_list_pred_mode_flag[id])
+ pred = ff_vvc_scaling_pred_8;
+ else if (!aps->scaling_list_pred_id_delta[id])
+ pred = ff_vvc_scaling_pred_16;
+ else
+ pred = sl->scaling_matrix_rec[id - aps->scaling_list_pred_id_delta[id]];
+ for (int i = 0; i < list_size; i++) {
+ const int x = ff_vvc_diag_scan_x[log2_size][log2_size][i];
+ const int y = ff_vvc_diag_scan_y[log2_size][log2_size][i];
+ const int off = y * matrix_size + x;
+ sl->scaling_matrix_rec[id][off] = (pred[off] + scaling_list[i]) & 255;
+ }
+ }
+}
+
+static int aps_decode_scaling(const VVCScalingList **scaling, const H266RawAPS *aps)
+{
+ VVCScalingList *sl = ff_refstruct_allocz(sizeof(*sl));
+ if (!sl)
+ return AVERROR(ENOMEM);
+
+ scaling_derive(sl, aps);
+ ff_refstruct_replace(scaling, sl);
+ ff_refstruct_unref(&sl);
+
+ return 0;
+}
+
+int ff_vvc_decode_aps(VVCParamSets *ps, const CodedBitstreamUnit *unit)
+{
+ const H266RawAPS *aps = unit->content_ref;
+ int ret = 0;
+
+ if (!aps)
+ return AVERROR_INVALIDDATA;
+
+ switch (aps->aps_params_type) {
+ case APS_ALF:
+ ret = aps_decode_alf(&ps->alf_list[aps->aps_adaptation_parameter_set_id], aps);
+ break;
+ case APS_LMCS:
+ ff_refstruct_replace(&ps->lmcs_list[aps->aps_adaptation_parameter_set_id], aps);
+ break;
+ case APS_SCALING:
+ ret = aps_decode_scaling(&ps->scaling_list[aps->aps_adaptation_parameter_set_id], aps);
+ break;
+ }
+
+ return ret;
+}
+
+static void sh_slice_address(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps)
+{
+ const int slice_address = sh->r->sh_slice_address;
+
+ if (pps->r->pps_rect_slice_flag) {
+ int pic_level_slice_idx = slice_address;
+ for (int j = 0; j < sh->r->curr_subpic_idx; j++)
+ pic_level_slice_idx += pps->r->num_slices_in_subpic[j];
+ sh->ctb_addr_in_curr_slice = pps->ctb_addr_in_slice + pps->slice_start_offset[pic_level_slice_idx];
+ sh->num_ctus_in_curr_slice = pps->num_ctus_in_slice[pic_level_slice_idx];
+ } else {
+ int tile_x = slice_address % pps->r->num_tile_columns;
+ int tile_y = slice_address / pps->r->num_tile_columns;
+ const int slice_start_ctb = pps->row_bd[tile_y] * pps->ctb_width + pps->col_bd[tile_x] * pps->r->row_height_val[tile_y];
+
+ sh->ctb_addr_in_curr_slice = pps->ctb_addr_in_slice + slice_start_ctb;
+
+ sh->num_ctus_in_curr_slice = 0;
+ for (int tile_idx = slice_address; tile_idx <= slice_address + sh->r->sh_num_tiles_in_slice_minus1; tile_idx++) {
+ tile_x = tile_idx % pps->r->num_tile_columns;
+ tile_y = tile_idx / pps->r->num_tile_columns;
+ sh->num_ctus_in_curr_slice += pps->r->row_height_val[tile_y] * pps->r->col_width_val[tile_x];
+ }
+ }
+}
+
+static void sh_qp_y(VVCSH *sh, const H266RawPPS *pps, const H266RawPictureHeader *ph)
+{
+ const int init_qp = pps->pps_init_qp_minus26 + 26;
+
+ if (!pps->pps_qp_delta_info_in_ph_flag)
+ sh->slice_qp_y = init_qp + sh->r->sh_qp_delta;
+ else
+ sh->slice_qp_y = init_qp + ph->ph_qp_delta;
+}
+
+static void sh_inter(VVCSH *sh, const H266RawSPS *sps, const H266RawPPS *pps)
+{
+ const H266RawSliceHeader *rsh = sh->r;
+
+ if (!pps->pps_wp_info_in_ph_flag &&
+ ((pps->pps_weighted_pred_flag && IS_P(rsh)) ||
+ (pps->pps_weighted_bipred_flag && IS_B(rsh))))
+ pred_weight_table(&sh->pwt, &rsh->sh_pred_weight_table);
+}
+
+static void sh_deblock_offsets(VVCSH *sh)
+{
+ const H266RawSliceHeader *r = sh->r;
+
+ if (!r->sh_deblocking_filter_disabled_flag) {
+ sh->deblock.beta_offset[LUMA] = r->sh_luma_beta_offset_div2 << 1;
+ sh->deblock.tc_offset[LUMA] = r->sh_luma_tc_offset_div2 << 1;
+ sh->deblock.beta_offset[CB] = r->sh_cb_beta_offset_div2 << 1;
+ sh->deblock.tc_offset[CB] = r->sh_cb_tc_offset_div2 << 1;
+ sh->deblock.beta_offset[CR] = r->sh_cr_beta_offset_div2 << 1;
+ sh->deblock.tc_offset[CR] = r->sh_cr_tc_offset_div2 << 1;
+ }
+}
+
+static void sh_partition_constraints(VVCSH *sh, const H266RawSPS *sps, const H266RawPictureHeader *ph)
+{
+ const int min_cb_log2_size_y = sps->sps_log2_min_luma_coding_block_size_minus2 + 2;
+ int min_qt_log2_size_y[2];
+
+ if (IS_I(sh->r)) {
+ min_qt_log2_size_y[LUMA] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_intra_slice_luma);
+ min_qt_log2_size_y[CHROMA] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_intra_slice_chroma);
+
+ sh->max_bt_size[LUMA] = 1 << (min_qt_log2_size_y[LUMA] + ph->ph_log2_diff_max_bt_min_qt_intra_slice_luma);
+ sh->max_bt_size[CHROMA] = 1 << (min_qt_log2_size_y[CHROMA]+ ph->ph_log2_diff_max_bt_min_qt_intra_slice_chroma);
+
+ sh->max_tt_size[LUMA] = 1 << (min_qt_log2_size_y[LUMA] + ph->ph_log2_diff_max_tt_min_qt_intra_slice_luma);
+ sh->max_tt_size[CHROMA] = 1 << (min_qt_log2_size_y[CHROMA]+ ph->ph_log2_diff_max_tt_min_qt_intra_slice_chroma);
+
+ sh->max_mtt_depth[LUMA] = ph->ph_max_mtt_hierarchy_depth_intra_slice_luma;
+ sh->max_mtt_depth[CHROMA] = ph->ph_max_mtt_hierarchy_depth_intra_slice_chroma;
+
+ sh->cu_qp_delta_subdiv = ph->ph_cu_qp_delta_subdiv_intra_slice;
+ sh->cu_chroma_qp_offset_subdiv = ph->ph_cu_chroma_qp_offset_subdiv_intra_slice;
+ } else {
+ for (int i = LUMA; i <= CHROMA; i++) {
+ min_qt_log2_size_y[i] = (min_cb_log2_size_y + ph->ph_log2_diff_min_qt_min_cb_inter_slice);
+ sh->max_bt_size[i] = 1 << (min_qt_log2_size_y[i] + ph->ph_log2_diff_max_bt_min_qt_inter_slice);
+ sh->max_tt_size[i] = 1 << (min_qt_log2_size_y[i] + ph->ph_log2_diff_max_tt_min_qt_inter_slice);
+ sh->max_mtt_depth[i] = ph->ph_max_mtt_hierarchy_depth_inter_slice;
+ }
+
+ sh->cu_qp_delta_subdiv = ph->ph_cu_qp_delta_subdiv_inter_slice;
+ sh->cu_chroma_qp_offset_subdiv = ph->ph_cu_chroma_qp_offset_subdiv_inter_slice;
+ }
+
+ sh->min_qt_size[LUMA] = 1 << min_qt_log2_size_y[LUMA];
+ sh->min_qt_size[CHROMA] = 1 << min_qt_log2_size_y[CHROMA];
+}
+
+static void sh_entry_points(VVCSH *sh, const H266RawSPS *sps, const VVCPPS *pps)
+{
+ if (sps->sps_entry_point_offsets_present_flag) {
+ for (int i = 1, j = 0; i < sh->num_ctus_in_curr_slice; i++) {
+ const int pre_ctb_addr_x = sh->ctb_addr_in_curr_slice[i - 1] % pps->ctb_width;
+ const int pre_ctb_addr_y = sh->ctb_addr_in_curr_slice[i - 1] / pps->ctb_width;
+ const int ctb_addr_x = sh->ctb_addr_in_curr_slice[i] % pps->ctb_width;
+ const int ctb_addr_y = sh->ctb_addr_in_curr_slice[i] / pps->ctb_width;
+ if (pps->ctb_to_row_bd[ctb_addr_y] != pps->ctb_to_row_bd[pre_ctb_addr_y] ||
+ pps->ctb_to_col_bd[ctb_addr_x] != pps->ctb_to_col_bd[pre_ctb_addr_x] ||
+ (ctb_addr_y != pre_ctb_addr_y && sps->sps_entropy_coding_sync_enabled_flag)) {
+ sh->entry_point_start_ctu[j++] = i;
+ }
+ }
+ }
+}
+
+static int sh_derive(VVCSH *sh, const VVCFrameParamSets *fps)
+{
+ const H266RawSPS *sps = fps->sps->r;
+ const H266RawPPS *pps = fps->pps->r;
+ const H266RawPictureHeader *ph = fps->ph.r;
+
+ sh_slice_address(sh, sps, fps->pps);
+ sh_inter(sh, sps, pps);
+ sh_qp_y(sh, pps, ph);
+ sh_deblock_offsets(sh);
+ sh_partition_constraints(sh, sps, ph);
+ sh_entry_points(sh, sps, fps->pps);
+
+ return 0;
+}
+
+int ff_vvc_decode_sh(VVCSH *sh, const VVCFrameParamSets *fps, const CodedBitstreamUnit *unit)
+{
+ int ret;
+
+ if (!fps->sps || !fps->pps)
+ return AVERROR_INVALIDDATA;
+
+ ff_refstruct_replace(&sh->r, unit->content_ref);
+
+ ret = sh_derive(sh, fps);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
diff --git a/libavcodec/vvc/vvc_ps.h b/libavcodec/vvc/vvc_ps.h
new file mode 100644
index 0000000000..ede4e74009
--- /dev/null
+++ b/libavcodec/vvc/vvc_ps.h
@@ -0,0 +1,263 @@
+/*
+ * VVC parameter set parser
+ *
+ * Copyright (C) 2023 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVC_PS_H
+#define AVCODEC_VVC_VVC_PS_H
+
+#include "libavcodec/cbs_h266.h"
+#include "libavcodec/vvc.h"
+
+#define IS_IDR(s) ((s)->vcl_unit_type == VVC_IDR_W_RADL || (s)->vcl_unit_type == VVC_IDR_N_LP)
+#define IS_CRA(s) ((s)->vcl_unit_type == VVC_CRA_NUT)
+#define IS_IRAP(s) (IS_IDR(s) || IS_CRA(s))
+#define IS_GDR(s) ((s)->vcl_unit_type == VVC_GDR_NUT)
+#define IS_CVSS(s) (IS_IRAP(s)|| IS_GDR(s))
+#define IS_CLVSS(s) (IS_CVSS(s) && s->no_output_before_recovery_flag)
+#define IS_RASL(s) ((s)->vcl_unit_type == VVC_RASL_NUT)
+#define IS_RADL(s) ((s)->vcl_unit_type == VVC_RADL_NUT)
+
+#define IS_I(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_I)
+#define IS_P(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_P)
+#define IS_B(rsh) ((rsh)->sh_slice_type == VVC_SLICE_TYPE_B)
+
+#define INV_POC INT_MIN
+#define GDR_IS_RECOVERED(s) (s->gdr_recovery_point_poc == INV_POC)
+#define GDR_SET_RECOVERED(s) (s->gdr_recovery_point_poc = INV_POC)
+
+#define LMCS_MAX_BIT_DEPTH 12
+#define LMCS_MAX_LUT_SIZE (1 << LMCS_MAX_BIT_DEPTH)
+#define LMCS_MAX_BIN_SIZE 16
+#define LADF_MAX_INTERVAL 5
+
+enum {
+ CHROMA_FORMAT_MONO,
+ CHROMA_FORMAT_420,
+ CHROMA_FORMAT_422,
+ CHROMA_FORMAT_444,
+};
+
+typedef struct VVCSPS {
+ const H266RawSPS *r; ///< RefStruct reference
+
+ //derived values
+ uint16_t width;
+ uint16_t height;
+ uint8_t hshift[VVC_MAX_SAMPLE_ARRAYS];
+ uint8_t vshift[VVC_MAX_SAMPLE_ARRAYS];
+ uint32_t max_pic_order_cnt_lsb; ///< MaxPicOrderCntLsb
+
+ uint8_t pixel_shift;
+ enum AVPixelFormat pix_fmt;
+
+ uint8_t bit_depth; ///< BitDepth
+ uint8_t qp_bd_offset; ///< QpBdOffset
+ uint8_t ctb_log2_size_y; ///< CtbLog2SizeY
+ uint8_t ctb_size_y; ///< CtbSizeY
+ uint8_t min_cb_log2_size_y; ///< MinCbLog2SizeY
+ uint8_t min_cb_size_y; ///< MinCbSizeY
+ uint8_t max_tb_size_y; ///< MaxTbSizeY
+ uint8_t max_ts_size; ///< MaxTsSize
+ uint8_t max_num_merge_cand; ///< MaxNumMergeCand
+ uint8_t max_num_ibc_merge_cand; ///< MaxNumIbcMergeCand
+ uint8_t max_num_gpm_merge_cand; ///< MaxNumGpmMergeCand
+ uint8_t num_ladf_intervals; ///< sps_num_ladf_intervals_minus2 + 2;
+ uint32_t ladf_interval_lower_bound[LADF_MAX_INTERVAL]; ///< SpsLadfIntervalLowerBound[]
+ uint8_t log2_parallel_merge_level; ///< sps_log2_parallel_merge_level_minus2 + 2;
+ uint8_t log2_transform_range; ///< Log2TransformRange
+ int8_t chroma_qp_table[3][VVC_MAX_POINTS_IN_QP_TABLE]; ///< ChromaQpTable
+} VVCSPS;
+
+typedef struct DBParams {
+ int8_t beta_offset[VVC_MAX_SAMPLE_ARRAYS];
+ int8_t tc_offset[VVC_MAX_SAMPLE_ARRAYS];
+} DBParams;
+
+typedef struct VVCPPS {
+ const H266RawPPS *r; ///< RefStruct reference
+
+ //derived value;
+ int8_t chroma_qp_offset[3]; ///< pps_cb_qp_offset, pps_cr_qp_offset, pps_joint_cbcr_qp_offset_value
+ int8_t chroma_qp_offset_list[6][3]; ///< pps_cb_qp_offset_list, pps_cr_qp_offset_list, pps_joint_cbcr_qp_offset_list
+
+ uint16_t width;
+ uint16_t height;
+
+ uint16_t slice_start_offset[VVC_MAX_SLICES];
+ uint16_t num_ctus_in_slice [VVC_MAX_SLICES];
+
+ uint16_t min_cb_width;
+ uint16_t min_cb_height;
+
+ uint16_t ctb_width;
+ uint16_t ctb_height;
+ uint32_t ctb_count;
+
+ uint16_t min_pu_width;
+ uint16_t min_pu_height;
+ uint16_t min_tu_width;
+ uint16_t min_tu_height;
+
+ uint32_t *ctb_addr_in_slice; ///< CtbAddrInCurrSlice for entire picture
+ uint16_t *col_bd;
+ uint16_t *row_bd;
+ uint16_t *ctb_to_col_bd;
+ uint16_t *ctb_to_row_bd;
+
+ uint16_t width32; ///< width in 32 pixels
+ uint16_t height32; ///< height in 32 pixels
+ uint16_t width64; ///< width in 64 pixels
+ uint16_t height64; ///< height in 64 pixels
+
+ uint16_t ref_wraparound_offset; ///< PpsRefWraparoundOffset
+
+} VVCPPS;
+
+#define MAX_WEIGHTS 15
+typedef struct PredWeightTable {
+ uint8_t log2_denom[2]; ///< luma_log2_weight_denom, ChromaLog2WeightDenom
+
+ uint8_t nb_weights[2]; ///< num_l0_weights, num_l1_weights
+ uint8_t weight_flag[2][2][MAX_WEIGHTS]; ///< luma_weight_l0_flag, chroma_weight_l0_flag,
+ ///< luma_weight_l1_flag, chroma_weight_l1_flag,
+ int16_t weight[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< LumaWeightL0, LumaWeightL1, ChromaWeightL0, ChromaWeightL1
+ int16_t offset[2][VVC_MAX_SAMPLE_ARRAYS][MAX_WEIGHTS]; ///< luma_offset_l0, luma_offset_l1, ChromaOffsetL0, ChromaOffsetL1
+} PredWeightTable;
+
+typedef struct VVCPH {
+ const H266RawPictureHeader *r;
+ void *rref; ///< RefStruct reference, backing ph above
+
+ //derived values
+ uint32_t max_num_subblock_merge_cand; ///< MaxNumSubblockMergeCand
+ int32_t poc; ///< PicOrderCntVal
+ PredWeightTable pwt;
+} VVCPH;
+
+#define ALF_NUM_FILTERS_LUMA 25
+#define ALF_NUM_FILTERS_CHROMA 8
+#define ALF_NUM_FILTERS_CC 5
+
+#define ALF_NUM_COEFF_LUMA 12
+#define ALF_NUM_COEFF_CHROMA 6
+#define ALF_NUM_COEFF_CC 7
+
+typedef struct VVCALF {
+ int16_t luma_coeff [ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA];
+ uint8_t luma_clip_idx [ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA];
+
+ uint8_t num_chroma_filters;
+ int16_t chroma_coeff [ALF_NUM_FILTERS_CHROMA][ALF_NUM_COEFF_CHROMA];
+ uint8_t chroma_clip_idx[ALF_NUM_FILTERS_CHROMA][ALF_NUM_COEFF_CHROMA];
+
+ uint8_t num_cc_filters[2]; ///< alf_cc_cb_filters_signalled_minus1 + 1, alf_cc_cr_filters_signalled_minus1 + 1
+ int16_t cc_coeff[2][ALF_NUM_FILTERS_CC][ALF_NUM_COEFF_CC];
+} VVCALF;
+
+enum {
+ SL_START_2x2 = 0,
+ SL_START_4x4 = 2,
+ SL_START_8x8 = 8,
+ SL_START_16x16 = 14,
+ SL_START_32x32 = 20,
+ SL_START_64x64 = 26,
+ SL_MAX_ID = 28,
+};
+
+#define SL_MAX_MATRIX_SIZE 8
+
+typedef struct VVCScalingList {
+ uint8_t scaling_matrix_rec[SL_MAX_ID][SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE]; ///< ScalingMatrixRec
+ uint8_t scaling_matrix_dc_rec[SL_MAX_ID - SL_START_16x16]; ///< ScalingMatrixDcRec[refId − 14]
+} VVCScalingList;
+
+typedef struct VVCLMCS {
+ uint8_t min_bin_idx;
+ uint8_t max_bin_idx;
+
+ //*2 for high depth
+ uint8_t fwd_lut[LMCS_MAX_LUT_SIZE * 2];
+ uint8_t inv_lut[LMCS_MAX_LUT_SIZE * 2];
+
+ uint16_t pivot[LMCS_MAX_BIN_SIZE + 1];
+ uint16_t chroma_scale_coeff[LMCS_MAX_BIN_SIZE];
+} VVCLMCS;
+
+#define VVC_MAX_ALF_COUNT 8
+#define VVC_MAX_LMCS_COUNT 4
+#define VVC_MAX_SL_COUNT 8
+
+typedef struct VVCParamSets {
+ const VVCSPS *sps_list[VVC_MAX_SPS_COUNT]; ///< RefStruct reference
+ const VVCPPS *pps_list[VVC_MAX_PPS_COUNT]; ///< RefStruct reference
+ const VVCALF *alf_list[VVC_MAX_ALF_COUNT]; ///< RefStruct reference
+ const H266RawAPS *lmcs_list[VVC_MAX_LMCS_COUNT]; ///< RefStruct reference
+ const VVCScalingList *scaling_list[VVC_MAX_SL_COUNT]; ///< RefStruct reference
+} VVCParamSets;
+
+typedef struct VVCFrameParamSets {
+ const VVCSPS *sps; ///< RefStruct reference
+ const VVCPPS *pps; ///< RefStruct reference
+ VVCPH ph;
+ const VVCALF *alf_list[VVC_MAX_ALF_COUNT]; ///< RefStruct reference
+ VVCLMCS lmcs;
+ const VVCScalingList *sl; ///< RefStruct reference
+} VVCFrameParamSets;
+
+typedef struct VVCSH {
+ const H266RawSliceHeader *r; ///< RefStruct reference
+
+ // derived values
+ // ctu address
+ uint32_t num_ctus_in_curr_slice; ///< NumCtusInCurrSlice
+ const uint32_t* ctb_addr_in_curr_slice; ///< CtbAddrInCurrSlice
+
+ // inter
+ PredWeightTable pwt;
+ int8_t ref_idx_sym[2]; ///< RefIdxSymL0, RefIdxSymL1
+
+ // qp_y
+ int8_t slice_qp_y; ///< SliceQpY
+
+ // deblock_offsets
+ DBParams deblock;
+
+ // partition constrains
+ uint8_t min_qt_size[2]; ///< MinQtSizeY, MinQtSizeC
+ uint8_t max_bt_size[2]; ///< MaxBtSizeY, MaxBtSizeC
+ uint8_t max_tt_size[2]; ///< MaxTtSizeY, MaxTtSizeC
+ uint8_t max_mtt_depth[2]; ///< MaxMttDepthY, MaxMttDepthC
+ uint8_t cu_qp_delta_subdiv; ///< CuQpDeltaSubdiv
+ uint8_t cu_chroma_qp_offset_subdiv; ///< CuChromaQpOffsetSubdiv
+
+ // entries
+ uint32_t entry_point_start_ctu[VVC_MAX_ENTRY_POINTS]; ///< entry point start in ctu_addr
+} VVCSH;
+
+struct VVCContext;
+
+int ff_vvc_decode_frame_ps(VVCFrameParamSets *fps, struct VVCContext *s);
+int ff_vvc_decode_aps(VVCParamSets *ps, const CodedBitstreamUnit *unit);
+int ff_vvc_decode_sh(VVCSH *sh, const VVCFrameParamSets *ps, const CodedBitstreamUnit *unit);
+void ff_vvc_frame_ps_free(VVCFrameParamSets *fps);
+void ff_vvc_ps_uninit(VVCParamSets *ps);
+
+#endif /* AVCODEC_VVC_VVC_PS_H */
diff --git a/libavcodec/vvc/vvcdec.h b/libavcodec/vvc/vvcdec.h
index 875ffe0a9a..b1757b00b5 100644
--- a/libavcodec/vvc/vvcdec.h
+++ b/libavcodec/vvc/vvcdec.h
@@ -26,6 +26,8 @@
#include "libavcodec/vvc.h"
+#include "vvc_ps.h"
+
#define LUMA 0
#define CHROMA 1
#define CB 1
@@ -176,6 +178,11 @@ typedef struct VVCFrameContext {
typedef struct VVCContext {
struct AVCodecContext *avctx;
+ CodedBitstreamContext *cbc;
+ CodedBitstreamFragment current_frame;
+
+ VVCParamSets ps;
+
int temporal_id; ///< temporal_id_plus1 - 1
int poc_tid0;
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH v7 04/14] vvcdec: add cabac decoder
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 02/14] vvcdec: add vvc_data Nuo Mi
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 03/14] vvcdec: add parameter parser for sps, pps, ph, sh Nuo Mi
@ 2023-12-10 15:57 ` Nuo Mi
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 05/14] vvcdec: add reference management Nuo Mi
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 15:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
add Context-based Adaptive Binary Arithmetic Coding (CABAC) decoder
---
libavcodec/vvc/Makefile | 2 +
libavcodec/vvc/vvc_cabac.c | 2476 ++++++++++++++++++++++++++++++++++++
libavcodec/vvc/vvc_cabac.h | 126 ++
libavcodec/vvc/vvc_ctu.c | 32 +
libavcodec/vvc/vvc_ctu.h | 463 +++++++
libavcodec/vvc/vvcdec.h | 7 +
6 files changed, 3106 insertions(+)
create mode 100644 libavcodec/vvc/vvc_cabac.c
create mode 100644 libavcodec/vvc/vvc_cabac.h
create mode 100644 libavcodec/vvc/vvc_ctu.c
create mode 100644 libavcodec/vvc/vvc_ctu.h
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index b2b187ac6f..6c03ba19ac 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -2,5 +2,7 @@ clean::
$(RM) $(CLEANSUFFIXES:%=libavcodec/vvc/%)
OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
+ vvc/vvc_cabac.o \
+ vvc/vvc_ctu.o \
vvc/vvc_data.o \
vvc/vvc_ps.o \
diff --git a/libavcodec/vvc/vvc_cabac.c b/libavcodec/vvc/vvc_cabac.c
new file mode 100644
index 0000000000..a145c10dc8
--- /dev/null
+++ b/libavcodec/vvc/vvc_cabac.c
@@ -0,0 +1,2476 @@
+/*
+ * VVC CABAC decoder
+ *
+ * Copyright (C) 2021 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "libavcodec/cabac_functions.h"
+
+#include "vvc_cabac.h"
+#include "vvc_ctu.h"
+#include "vvc_data.h"
+
+#define CABAC_MAX_BIN 31
+
+#define CNU 35
+
+enum SyntaxElement {
+ ALF_CTB_FLAG = 0,
+ ALF_USE_APS_FLAG = ALF_CTB_FLAG + 9,
+ ALF_CTB_CC_CB_IDC,
+ ALF_CTB_CC_CR_IDC = ALF_CTB_CC_CB_IDC + 3,
+ ALF_CTB_FILTER_ALT_IDX = ALF_CTB_CC_CR_IDC + 3,
+ SAO_MERGE_FLAG = ALF_CTB_FILTER_ALT_IDX + 2,
+ SAO_TYPE_IDX,
+ SPLIT_CU_FLAG,
+ SPLIT_QT_FLAG = SPLIT_CU_FLAG + 9,
+ MTT_SPLIT_CU_VERTICAL_FLAG = SPLIT_QT_FLAG + 6,
+ MTT_SPLIT_CU_BINARY_FLAG = MTT_SPLIT_CU_VERTICAL_FLAG + 5,
+ NON_INTER_FLAG = MTT_SPLIT_CU_BINARY_FLAG + 4,
+ CU_SKIP_FLAG = NON_INTER_FLAG + 2,
+ PRED_MODE_IBC_FLAG = CU_SKIP_FLAG + 3,
+ PRED_MODE_FLAG = PRED_MODE_IBC_FLAG + 3,
+ PRED_MODE_PLT_FLAG = PRED_MODE_FLAG + 2,
+ CU_ACT_ENABLED_FLAG,
+ INTRA_BDPCM_LUMA_FLAG,
+ INTRA_BDPCM_LUMA_DIR_FLAG,
+ INTRA_MIP_FLAG,
+ INTRA_LUMA_REF_IDX = INTRA_MIP_FLAG + 4,
+ INTRA_SUBPARTITIONS_MODE_FLAG = INTRA_LUMA_REF_IDX + 2,
+ INTRA_SUBPARTITIONS_SPLIT_FLAG,
+ INTRA_LUMA_MPM_FLAG,
+ INTRA_LUMA_NOT_PLANAR_FLAG,
+ INTRA_BDPCM_CHROMA_FLAG = INTRA_LUMA_NOT_PLANAR_FLAG + 2,
+ INTRA_BDPCM_CHROMA_DIR_FLAG,
+ CCLM_MODE_FLAG,
+ CCLM_MODE_IDX,
+ INTRA_CHROMA_PRED_MODE,
+ GENERAL_MERGE_FLAG,
+ INTER_PRED_IDC,
+ INTER_AFFINE_FLAG = INTER_PRED_IDC + 6,
+ CU_AFFINE_TYPE_FLAG = INTER_AFFINE_FLAG + 3,
+ SYM_MVD_FLAG,
+ REF_IDX_LX,
+ MVP_LX_FLAG = REF_IDX_LX + 2,
+ AMVR_FLAG,
+ AMVR_PRECISION_IDX = AMVR_FLAG + 2,
+ BCW_IDX = AMVR_PRECISION_IDX + 3,
+ CU_CODED_FLAG,
+ CU_SBT_FLAG,
+ CU_SBT_QUAD_FLAG = CU_SBT_FLAG + 2,
+ CU_SBT_HORIZONTAL_FLAG,
+ CU_SBT_POS_FLAG = CU_SBT_HORIZONTAL_FLAG + 3,
+ LFNST_IDX,
+ MTS_IDX = LFNST_IDX + 3,
+ COPY_ABOVE_PALETTE_INDICES_FLAG = MTS_IDX + 4,
+ PALETTE_TRANSPOSE_FLAG,
+ RUN_COPY_FLAG,
+ REGULAR_MERGE_FLAG = RUN_COPY_FLAG + 8,
+ MMVD_MERGE_FLAG = REGULAR_MERGE_FLAG + 2,
+ MMVD_CAND_FLAG,
+ MMVD_DISTANCE_IDX,
+ CIIP_FLAG,
+ MERGE_SUBBLOCK_FLAG,
+ MERGE_SUBBLOCK_IDX = MERGE_SUBBLOCK_FLAG + 3,
+ MERGE_IDX,
+ ABS_MVD_GREATER0_FLAG,
+ ABS_MVD_GREATER1_FLAG,
+ TU_Y_CODED_FLAG,
+ TU_CB_CODED_FLAG = TU_Y_CODED_FLAG + 4,
+ TU_CR_CODED_FLAG = TU_CB_CODED_FLAG + 2,
+ CU_QP_DELTA_ABS = TU_CR_CODED_FLAG + 3,
+ CU_CHROMA_QP_OFFSET_FLAG = CU_QP_DELTA_ABS + 2,
+ CU_CHROMA_QP_OFFSET_IDX,
+ TRANSFORM_SKIP_FLAG,
+ TU_JOINT_CBCR_RESIDUAL_FLAG = TRANSFORM_SKIP_FLAG + 2,
+ LAST_SIG_COEFF_X_PREFIX = TU_JOINT_CBCR_RESIDUAL_FLAG + 3,
+ LAST_SIG_COEFF_Y_PREFIX = LAST_SIG_COEFF_X_PREFIX +23,
+ SB_CODED_FLAG = LAST_SIG_COEFF_Y_PREFIX +23,
+ SIG_COEFF_FLAG = SB_CODED_FLAG + 7,
+ PAR_LEVEL_FLAG = SIG_COEFF_FLAG +63,
+ ABS_LEVEL_GTX_FLAG = PAR_LEVEL_FLAG +33,
+ COEFF_SIGN_FLAG = ABS_LEVEL_GTX_FLAG +72,
+ SYNTAX_ELEMENT_LAST = COEFF_SIGN_FLAG + 6,
+};
+
+static const uint8_t init_values[4][SYNTAX_ELEMENT_LAST] = {
+ {
+ //alf_ctb_flag
+ 62, 39, 39, 54, 39, 39, 31, 39, 39,
+ //alf_use_aps_flag
+ 46,
+ //alf_ctb_cc_cb_idc
+ 18, 30, 31,
+ //alf_ctb_cc_cr_idc
+ 18, 30, 31,
+ //alf_ctb_filter_alt_idx
+ 11, 11,
+ //sao_merge_left_flag and sao_merge_up_flag
+ 60,
+ //sao_type_idx_luma and sao_type_idx_chroma
+ 13,
+ //split_cu_flag
+ 19, 28, 38, 27, 29, 38, 20, 30, 31,
+ //split_qt_flag
+ 27, 6, 15, 25, 19, 37,
+ //mtt_split_cu_vertical_flag
+ 43, 42, 29, 27, 44,
+ //mtt_split_cu_binary_flag
+ 36, 45, 36, 45,
+ //non_inter_flag
+ CNU, CNU,
+ //cu_skip_flag
+ 0, 26, 28,
+ //pred_mode_ibc_flag
+ 17, 42, 36,
+ //pred_mode_flag
+ CNU, CNU,
+ //pred_mode_plt_flag
+ 25,
+ //cu_act_enabled_flag
+ 52,
+ //intra_bdpcm_luma_flag
+ 19,
+ //intra_bdpcm_luma_dir_flag
+ 35,
+ //intra_mip_flag
+ 33, 49, 50, 25,
+ //intra_luma_ref_idx
+ 25, 60,
+ //intra_subpartitions_mode_flag
+ 33,
+ //intra_subpartitions_split_flag
+ 43,
+ //intra_luma_mpm_flag
+ 45,
+ //intra_luma_not_planar_flag
+ 13, 28,
+ //intra_bdpcm_chroma_flag
+ 1,
+ //intra_bdpcm_chroma_dir_flag
+ 27,
+ //cclm_mode_flag
+ 59,
+ //cclm_mode_idx
+ 27,
+ //intra_chroma_pred_mode
+ 34,
+ //general_merge_flag
+ 26,
+ //inter_pred_idc
+ CNU, CNU, CNU, CNU, CNU, CNU,
+ //inter_affine_flag
+ CNU, CNU, CNU,
+ //cu_affine_type_flag
+ CNU,
+ //sym_mvd_flag
+ CNU,
+ //ref_idx_l0 and ref_idx_l1
+ CNU, CNU,
+ //mvp_l0_flag and mvp_l1_flag
+ 42,
+ //amvr_flag
+ CNU, CNU,
+ //amvr_precision_idx
+ 35, 34, 35,
+ //bcw_idx
+ CNU,
+ //cu_coded_flag
+ 6,
+ //cu_sbt_flag
+ CNU, CNU,
+ //cu_sbt_quad_flag
+ CNU,
+ //cu_sbt_horizontal_flag
+ CNU, CNU, CNU,
+ //cu_sbt_pos_flag
+ CNU,
+ //lfnst_idx
+ 28, 52, 42,
+ //mts_idx
+ 29, 0, 28, 0,
+ //copy_above_palette_indices_flag
+ 42,
+ //palette_transpose_flag
+ 42,
+ //run_copy_flag
+ 50, 37, 45, 30, 46, 45, 38, 46,
+ //regular_merge_flag
+ CNU, CNU,
+ //mmvd_merge_flag
+ CNU,
+ //mmvd_cand_flag
+ CNU,
+ //mmvd_distance_idx
+ CNU,
+ //ciip_flag
+ CNU,
+ //merge_subblock_flag
+ CNU, CNU, CNU,
+ //merge_subblock_idx
+ CNU,
+ //merge_idx, merge_gpm_idx0, and merge_gpm_idx1
+ 34,
+ //abs_mvd_greater0_flag
+ 14,
+ //abs_mvd_greater1_flag
+ 45,
+ //tu_y_coded_flag
+ 15, 12, 5, 7,
+ //tu_cb_coded_flag
+ 12, 21,
+ //tu_cr_coded_flag
+ 33, 28, 36,
+ //cu_qp_delta_abs
+ CNU, CNU,
+ //cu_chroma_qp_offset_flag
+ CNU,
+ //cu_chroma_qp_offset_idx
+ CNU,
+ //transform_skip_flag
+ 25, 9,
+ //tu_joint_cbcr_residual_flag
+ 12, 21, 35,
+ //last_sig_coeff_x_prefix
+ 13, 5, 4, 21, 14, 4, 6, 14, 21, 11, 14, 7, 14, 5, 11, 21,
+ 30, 22, 13, 42, 12, 4, 3,
+ //last_sig_coeff_y_prefix
+ 13, 5, 4, 6, 13, 11, 14, 6, 5, 3, 14, 22, 6, 4, 3, 6,
+ 22, 29, 20, 34, 12, 4, 3,
+ //sb_coded_flag
+ 18, 31, 25, 15, 18, 20, 38,
+ //sig_coeff_flag
+ 25, 19, 28, 14, 25, 20, 29, 30, 19, 37, 30, 38, 11, 38, 46, 54,
+ 27, 39, 39, 39, 44, 39, 39, 39, 18, 39, 39, 39, 27, 39, 39, 39,
+ 0, 39, 39, 39, 25, 27, 28, 37, 34, 53, 53, 46, 19, 46, 38, 39,
+ 52, 39, 39, 39, 11, 39, 39, 39, 19, 39, 39, 39, 25, 28, 38,
+ //par_level_flag
+ 33, 25, 18, 26, 34, 27, 25, 26, 19, 42, 35, 33, 19, 27, 35, 35,
+ 34, 42, 20, 43, 20, 33, 25, 26, 42, 19, 27, 26, 50, 35, 20, 43,
+ 11,
+ //abs_level_gtx_flag
+ 25, 25, 11, 27, 20, 21, 33, 12, 28, 21, 22, 34, 28, 29, 29, 30,
+ 36, 29, 45, 30, 23, 40, 33, 27, 28, 21, 37, 36, 37, 45, 38, 46,
+ 25, 1, 40, 25, 33, 11, 17, 25, 25, 18, 4, 17, 33, 26, 19, 13,
+ 33, 19, 20, 28, 22, 40, 9, 25, 18, 26, 35, 25, 26, 35, 28, 37,
+ 11, 5, 5, 14, 10, 3, 3, 3,
+ //coeff_sign_flag
+ 12, 17, 46, 28, 25, 46,
+ },
+ {
+ //alf_ctb_flag
+ 13, 23, 46, 4, 61, 54, 19, 46, 54,
+ //alf_use_aps_flag
+ 46,
+ //alf_ctb_cc_cb_idc
+ 18, 21, 38,
+ //alf_ctb_cc_cr_idc
+ 18, 21, 38,
+ //alf_ctb_filter_alt_idx
+ 20, 12,
+ //sao_merge_left_flag and sao_merge_up_flag
+ 60,
+ //sao_type_idx_luma and sao_type_idx_chroma
+ 5,
+ //split_cu_flag
+ 11, 35, 53, 12, 6, 30, 13, 15, 31,
+ //split_qt_flag
+ 20, 14, 23, 18, 19, 6,
+ //mtt_split_cu_vertical_flag
+ 43, 35, 37, 34, 52,
+ //mtt_split_cu_binary_flag
+ 43, 37, 21, 22,
+ //non_inter_flag
+ 25, 12,
+ //cu_skip_flag
+ 57, 59, 45,
+ //pred_mode_ibc_flag
+ 0, 57, 44,
+ //pred_mode_flag
+ 40, 35,
+ //pred_mode_plt_flag
+ 0,
+ //cu_act_enabled_flag
+ 46,
+ //intra_bdpcm_luma_flag
+ 40,
+ //intra_bdpcm_luma_dir_flag
+ 36,
+ //intra_mip_flag
+ 41, 57, 58, 26,
+ //intra_luma_ref_idx
+ 25, 58,
+ //intra_subpartitions_mode_flag
+ 33,
+ //intra_subpartitions_split_flag
+ 36,
+ //intra_luma_mpm_flag
+ 36,
+ //intra_luma_not_planar_flag
+ 12, 20,
+ //intra_bdpcm_chroma_flag
+ 0,
+ //intra_bdpcm_chroma_dir_flag
+ 13,
+ //cclm_mode_flag
+ 34,
+ //cclm_mode_idx
+ 27,
+ //intra_chroma_pred_mode
+ 25,
+ //general_merge_flag
+ 21,
+ //inter_pred_idc
+ 7, 6, 5, 12, 4, 40,
+ //inter_affine_flag
+ 12, 13, 14,
+ //cu_affine_type_flag
+ 35,
+ //sym_mvd_flag
+ 28,
+ //ref_idx_l0 and ref_idx_l1
+ 20, 35,
+ //mvp_l0_flag and mvp_l1_flag
+ 34,
+ //amvr_flag
+ 59, 58,
+ //amvr_precision_idx
+ 60, 48, 60,
+ //bcw_idx
+ 4,
+ //cu_coded_flag
+ 5,
+ //cu_sbt_flag
+ 56, 57,
+ //cu_sbt_quad_flag
+ 42,
+ //cu_sbt_horizontal_flag
+ 20, 43, 12,
+ //cu_sbt_pos_flag
+ 28,
+ //lfnst_idx
+ 37, 45, 27,
+ //mts_idx
+ 45, 40, 27, 0,
+ //copy_above_palette_indices_flag
+ 59,
+ //palette_transpose_flag
+ 42,
+ //run_copy_flag
+ 51, 30, 30, 38, 23, 38, 53, 46,
+ //regular_merge_flag
+ 38, 7,
+ //mmvd_merge_flag
+ 26,
+ //mmvd_cand_flag
+ 43,
+ //mmvd_distance_idx
+ 60,
+ //ciip_flag
+ 57,
+ //merge_subblock_flag
+ 48, 57, 44,
+ //merge_subblock_idx
+ 5,
+ //merge_idx, merge_gpm_idx0, and merge_gpm_idx1
+ 20,
+ //abs_mvd_greater0_flag
+ 44,
+ //abs_mvd_greater1_flag
+ 43,
+ //tu_y_coded_flag
+ 23, 5, 20, 7,
+ //tu_cb_coded_flag
+ 25, 28,
+ //tu_cr_coded_flag
+ 25, 29, 45,
+ //cu_qp_delta_abs
+ CNU, CNU,
+ //cu_chroma_qp_offset_flag
+ CNU,
+ //cu_chroma_qp_offset_idx
+ CNU,
+ //transform_skip_flag
+ 25, 9,
+ //tu_joint_cbcr_residual_flag
+ 27, 36, 45,
+ //last_sig_coeff_x_prefix
+ 6, 13, 12, 6, 6, 12, 14, 14, 13, 12, 29, 7, 6, 13, 36, 28,
+ 14, 13, 5, 26, 12, 4, 18,
+ //last_sig_coeff_y_prefix
+ 5, 5, 12, 6, 6, 4, 6, 14, 5, 12, 14, 7, 13, 5, 13, 21,
+ 14, 20, 12, 34, 11, 4, 18,
+ //sb_coded_flag
+ 25, 30, 25, 45, 18, 12, 29,
+ //sig_coeff_flag
+ 17, 41, 42, 29, 25, 49, 43, 37, 33, 58, 51, 30, 19, 38, 38, 46,
+ 34, 54, 54, 39, 6, 39, 39, 39, 19, 39, 54, 39, 19, 39, 39, 39,
+ 56, 39, 39, 39, 17, 34, 35, 21, 41, 59, 60, 38, 35, 45, 53, 54,
+ 44, 39, 39, 39, 34, 38, 62, 39, 26, 39, 39, 39, 40, 35, 44,
+ //par_level_flag
+ 18, 17, 33, 18, 26, 42, 25, 33, 26, 42, 27, 25, 34, 42, 42, 35,
+ 26, 27, 42, 20, 20, 25, 25, 26, 11, 19, 27, 33, 42, 35, 35, 43,
+ 3,
+ //abs_level_gtx_flag
+ 0, 17, 26, 19, 35, 21, 25, 34, 20, 28, 29, 33, 27, 28, 29, 22,
+ 34, 28, 44, 37, 38, 0, 25, 19, 20, 13, 14, 57, 44, 30, 30, 23,
+ 17, 0, 1, 17, 25, 18, 0, 9, 25, 33, 34, 9, 25, 18, 26, 20,
+ 25, 18, 19, 27, 29, 17, 9, 25, 10, 18, 4, 17, 33, 19, 20, 29,
+ 18, 11, 4, 28, 2, 10, 3, 3,
+ //coeff_sign_flag
+ 5, 10, 53, 43, 25, 46,
+ },
+ {
+ //alf_ctb_flag
+ 33, 52, 46, 25, 61, 54, 25, 61, 54,
+ //alf_use_aps_flag
+ 46,
+ //alf_ctb_cc_cb_idc
+ 25, 35, 38,
+ //alf_ctb_cc_cr_idc
+ 25, 28, 38,
+ //alf_ctb_filter_alt_idx
+ 11, 26,
+ //sao_merge_left_flag and sao_merge_up_flag
+ 2,
+ //sao_type_idx_luma and sao_type_idx_chroma
+ 2,
+ //split_cu_flag
+ 18, 27, 15, 18, 28, 45, 26, 7, 23,
+ //split_qt_flag
+ 26, 36, 38, 18, 34, 21,
+ //mtt_split_cu_vertical_flag
+ 43, 42, 37, 42, 44,
+ //mtt_split_cu_binary_flag
+ 28, 29, 28, 29,
+ //non_inter_flag
+ 25, 20,
+ //cu_skip_flag
+ 57, 60, 46,
+ //pred_mode_ibc_flag
+ 0, 43, 45,
+ //pred_mode_flag
+ 40, 35,
+ //pred_mode_plt_flag
+ 17,
+ //cu_act_enabled_flag
+ 46,
+ //intra_bdpcm_luma_flag
+ 19,
+ //intra_bdpcm_luma_dir_flag
+ 21,
+ //intra_mip_flag
+ 56, 57, 50, 26,
+ //intra_luma_ref_idx
+ 25, 59,
+ //intra_subpartitions_mode_flag
+ 33,
+ //intra_subpartitions_split_flag
+ 43,
+ //intra_luma_mpm_flag
+ 44,
+ //intra_luma_not_planar_flag
+ 13, 6,
+ //intra_bdpcm_chroma_flag
+ 0,
+ //intra_bdpcm_chroma_dir_flag
+ 28,
+ //cclm_mode_flag
+ 26,
+ //cclm_mode_idx
+ 27,
+ //intra_chroma_pred_mode
+ 25,
+ //general_merge_flag
+ 6,
+ //inter_pred_idc
+ 14, 13, 5, 4, 3, 40,
+ //inter_affine_flag
+ 19, 13, 6,
+ //cu_affine_type_flag
+ 35,
+ //sym_mvd_flag
+ 28,
+ //ref_idx_l0 and ref_idx_l1
+ 5, 35,
+ //mvp_l0_flag and mvp_l1_flag
+ 34,
+ //amvr_flag
+ 59, 50,
+ //amvr_precision_idx
+ 38, 26, 60,
+ //bcw_idx
+ 5,
+ //cu_coded_flag
+ 12,
+ //cu_sbt_flag
+ 41, 57,
+ //cu_sbt_quad_flag
+ 42,
+ //cu_sbt_horizontal_flag
+ 35, 51, 27,
+ //cu_sbt_pos_flag
+ 28,
+ //lfnst_idx
+ 52, 37, 27,
+ //mts_idx
+ 45, 25, 27, 0,
+ //copy_above_palette_indices_flag
+ 50,
+ //palette_transpose_flag
+ 35,
+ //run_copy_flag
+ 58, 45, 45, 30, 38, 45, 38, 46,
+ //regular_merge_flag
+ 46, 15,
+ //mmvd_merge_flag
+ 25,
+ //mmvd_cand_flag
+ 43,
+ //mmvd_distance_idx
+ 59,
+ //ciip_flag
+ 57,
+ //merge_subblock_flag
+ 25, 58, 45,
+ //merge_subblock_idx
+ 4,
+ //merge_idx, merge_gpm_idx0, and merge_gpm_idx1
+ 18,
+ //abs_mvd_greater0_flag
+ 51,
+ //abs_mvd_greater1_flag
+ 36,
+ //tu_y_coded_flag
+ 15, 6, 5, 14,
+ //tu_cb_coded_flag
+ 25, 37,
+ //tu_cr_coded_flag
+ 9, 36, 45,
+ //cu_qp_delta_abs
+ CNU, CNU,
+ //cu_chroma_qp_offset_flag
+ CNU,
+ //cu_chroma_qp_offset_idx
+ CNU,
+ //transform_skip_flag
+ 25, 17,
+ //tu_joint_cbcr_residual_flag
+ 42, 43, 52,
+ //last_sig_coeff_x_prefix
+ 6, 6, 12, 14, 6, 4, 14, 7, 6, 4, 29, 7, 6, 6, 12, 28,
+ 7, 13, 13, 35, 19, 5, 4,
+ //last_sig_coeff_y_prefix
+ 5, 5, 20, 13, 13, 19, 21, 6, 12, 12, 14, 14, 5, 4, 12, 13,
+ 7, 13, 12, 41, 11, 5, 27,
+ //sb_coded_flag
+ 25, 45, 25, 14, 18, 35, 45,
+ //sig_coeff_flag
+ 17, 41, 49, 36, 1, 49, 50, 37, 48, 51, 58, 45, 26, 45, 53, 46,
+ 49, 54, 61, 39, 35, 39, 39, 39, 19, 54, 39, 39, 50, 39, 39, 39,
+ 0, 39, 39, 39, 9, 49, 50, 36, 48, 59, 59, 38, 34, 45, 38, 31,
+ 58, 39, 39, 39, 34, 38, 54, 39, 41, 39, 39, 39, 25, 50, 37,
+ //par_level_flag
+ 33, 40, 25, 41, 26, 42, 25, 33, 26, 34, 27, 25, 41, 42, 42, 35,
+ 33, 27, 35, 42, 43, 33, 25, 26, 34, 19, 27, 33, 42, 43, 35, 43,
+ 11,
+ //abs_level_gtx_flag
+ 0, 0, 33, 34, 35, 21, 25, 34, 35, 28, 29, 40, 42, 43, 29, 30,
+ 49, 36, 37, 45, 38, 0, 40, 34, 43, 36, 37, 57, 52, 45, 38, 46,
+ 25, 0, 0, 17, 25, 26, 0, 9, 25, 33, 19, 0, 25, 33, 26, 20,
+ 25, 33, 27, 35, 22, 25, 1, 25, 33, 26, 12, 25, 33, 27, 28, 37,
+ 19, 11, 4, 6, 3, 4, 4, 5,
+ //coeff_sign_flag
+ 35, 25, 46, 28, 33, 38,
+ },
+ //shiftIdx
+ {
+ //alf_ctb_flag
+ 0, 0, 0, 4, 0, 0, 1, 0, 0,
+ //alf_use_aps_flag
+ 0,
+ //alf_ctb_cc_cb_idc
+ 4, 1, 4,
+ //alf_ctb_cc_cr_idc
+ 4, 1, 4,
+ //alf_ctb_filter_alt_idx
+ 0, 0,
+ //sao_merge_left_flag and sao_merge_up_flag
+ 0,
+ //sao_type_idx_luma and sao_type_idx_chroma
+ 4,
+ //split_cu_flag
+ 12, 13, 8, 8, 13, 12, 5, 9, 9,
+ //split_qt_flag
+ 0, 8, 8, 12, 12, 8,
+ //mtt_split_cu_vertical_flag
+ 9, 8, 9, 8, 5,
+ //mtt_split_cu_binary_flag
+ 12, 13, 12, 13,
+ //non_inter_flag
+ 1, 0,
+ //cu_skip_flag
+ 5, 4, 8,
+ //pred_mode_ibc_flag
+ 1, 5, 8,
+ //pred_mode_flag
+ 5, 1,
+ //pred_mode_plt_flag
+ 1,
+ //cu_act_enabled_flag
+ 1,
+ //intra_bdpcm_luma_flag
+ 1,
+ //intra_bdpcm_luma_dir_flag
+ 4,
+ //intra_mip_flag
+ 9, 10, 9, 6,
+ //intra_luma_ref_idx
+ 5, 8,
+ //intra_subpartitions_mode_flag
+ 9,
+ //intra_subpartitions_split_flag
+ 2,
+ //intra_luma_mpm_flag
+ 6,
+ //intra_luma_not_planar_flag
+ 1, 5,
+ //intra_bdpcm_chroma_flag
+ 1,
+ //intra_bdpcm_chroma_dir_flag
+ 0,
+ //cclm_mode_flag
+ 4,
+ //cclm_mode_idx
+ 9,
+ //intra_chroma_pred_mode
+ 5,
+ //general_merge_flag
+ 4,
+ //inter_pred_idc
+ 0, 0, 1, 4, 4, 0,
+ //inter_affine_flag
+ 4, 0, 0,
+ //cu_affine_type_flag
+ 4,
+ //sym_mvd_flag
+ 5,
+ //ref_idx_l0 and ref_idx_l1
+ 0, 4,
+ //mvp_l0_flag and mvp_l1_flag
+ 12,
+ //amvr_flag
+ 0, 0,
+ //amvr_precision_idx
+ 4, 5, 0,
+ //bcw_idx
+ 1,
+ //cu_coded_flag
+ 4,
+ //cu_sbt_flag
+ 1, 5,
+ //cu_sbt_quad_flag
+ 10,
+ //cu_sbt_horizontal_flag
+ 8, 4, 1,
+ //cu_sbt_pos_flag
+ 13,
+ //lfnst_idx
+ 9, 9, 10,
+ //mts_idx
+ 8, 0, 9, 0,
+ //copy_above_palette_indices_flag
+ 9,
+ //palette_transpose_flag
+ 5,
+ //run_copy_flag
+ 9, 6, 9, 10, 5, 0, 9, 5,
+ //regular_merge_flag
+ 5, 5,
+ //mmvd_merge_flag
+ 4,
+ //mmvd_cand_flag
+ 10,
+ //mmvd_distance_idx
+ 0,
+ //ciip_flag
+ 1,
+ //merge_subblock_flag
+ 4, 4, 4,
+ //merge_subblock_idx
+ 0,
+ //merge_idx, merge_gpm_idx0, and merge_gpm_idx1
+ 4,
+ //abs_mvd_greater0_flag
+ 9,
+ //abs_mvd_greater1_flag
+ 5,
+ //tu_y_coded_flag
+ 5, 1, 8, 9,
+ //tu_cb_coded_flag
+ 5, 0,
+ //tu_cr_coded_flag
+ 2, 1, 0,
+ //cu_qp_delta_abs
+ 8, 8,
+ //cu_chroma_qp_offset_flag
+ 8,
+ //cu_chroma_qp_offset_idx
+ 8,
+ //transform_skip_flag
+ 1, 1,
+ //tu_joint_cbcr_residual_flag
+ 1, 1, 0,
+ //last_sig_coeff_x_prefix
+ 8, 5, 4, 5, 4, 4, 5, 4, 1, 0, 4, 1, 0, 0, 0, 0,
+ 1, 0, 0, 0, 5, 4, 4,
+ //last_sig_coeff_y_prefix
+ 8, 5, 8, 5, 5, 4, 5, 5, 4, 0, 5, 4, 1, 0, 0, 1,
+ 4, 0, 0, 0, 6, 5, 5,
+ //sb_coded_flag
+ 8, 5, 5, 8, 5, 8, 8,
+ //sig_coeff_flag
+ 12, 9, 9, 10, 9, 9, 9, 10, 8, 8, 8, 10, 9, 13, 8, 8,
+ 8, 8, 8, 5, 8, 0, 0, 0, 8, 8, 8, 8, 8, 0, 4, 4,
+ 0, 0, 0, 0, 12, 12, 9, 13, 4, 5, 8, 9, 8, 12, 12, 8,
+ 4, 0, 0, 0, 8, 8, 8, 8, 4, 0, 0, 0, 13, 13, 8,
+ //par_level_flag
+ 8, 9, 12, 13, 13, 13, 10, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+ 10, 13, 13, 13, 13, 8, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13,
+ 6,
+ //abs_level_gtx_flag
+ 9, 5, 10, 13, 13, 10, 9, 10, 13, 13, 13, 9, 10, 10, 10, 13,
+ 8, 9, 10, 10, 13, 8, 8, 9, 12, 12, 10, 5, 9, 9, 9, 13,
+ 1, 5, 9, 9, 9, 6, 5, 9, 10, 10, 9, 9, 9, 9, 9, 9,
+ 6, 8, 9, 9, 10, 1, 5, 8, 8, 9, 6, 6, 9, 8, 8, 9,
+ 4, 2, 1, 6, 1, 1, 1, 1,
+ //coeff_sign_flag
+ 1, 4, 4, 5, 8, 8,
+ }
+};
+
+#define MAX_SUB_BLOCKS 16
+#define MAX_SUB_BLOCK_SIZE 4
+#define MAX_TB_SIZE 64
+
+typedef struct ResidualCoding {
+ //common for ts and non ts
+ TransformBlock *tb;
+
+ int log2_sb_w;
+ int log2_sb_h;
+ int last_sub_block;
+ int hist_value;
+ int update_hist;
+ int num_sb_coeff;
+ int rem_bins_pass1;
+
+ int width_in_sbs;
+ int height_in_sbs;
+ int nb_sbs;
+
+ const uint8_t *sb_scan_x_off;
+ const uint8_t *sb_scan_y_off;
+ const uint8_t *scan_x_off;
+ const uint8_t *scan_y_off;
+
+ uint8_t sb_coded_flag[MAX_SUB_BLOCKS * MAX_SUB_BLOCKS];
+ int sig_coeff_flag[MAX_TB_SIZE * MAX_TB_SIZE];
+ int abs_level_pass1[MAX_TB_SIZE * MAX_TB_SIZE]; ///< AbsLevelPass1[][]
+ int abs_level[MAX_TB_SIZE * MAX_TB_SIZE];
+
+ //for ts only
+ uint8_t infer_sb_cbf;
+ int coeff_sign_level[MAX_TB_SIZE * MAX_TB_SIZE]; ///< CoeffSignLevel[][]
+
+ //for non ts only
+ int qstate;
+ int last_scan_pos;
+ int last_significant_coeff_x;
+ int last_significant_coeff_y;
+} ResidualCoding;
+
+static int cabac_reinit(VVCLocalContext *lc)
+{
+ return skip_bytes(&lc->ep->cc, 0) == NULL ? AVERROR_INVALIDDATA : 0;
+}
+
+static void cabac_init_state(VVCLocalContext *lc)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const int qp = av_clip_uintp2(lc->sc->sh.slice_qp_y, 6);
+ int init_type = 2 - rsh->sh_slice_type;
+
+ av_assert0(VVC_CONTEXTS == SYNTAX_ELEMENT_LAST);
+
+ ff_vvc_ep_init_stat_coeff(lc->ep, sps->bit_depth, sps->r->sps_persistent_rice_adaptation_enabled_flag);
+
+ if (rsh->sh_cabac_init_flag && !IS_I(rsh))
+ init_type ^= 3;
+
+ for (int i = 0; i < VVC_CONTEXTS; i++) {
+ VVCCabacState *state = &lc->ep->cabac_state[i];
+ const int init_value = init_values[init_type][i];
+ const int shift_idx = init_values[3][i];
+ const int m = (init_value >> 3) - 4;
+ const int n = ((init_value & 7) * 18) + 1;
+ const int pre = av_clip(((m * (qp - 16)) >> 1) + n, 1, 127);
+
+ state->state[0] = pre << 3;
+ state->state[1] = pre << 7;
+ state->shift[0] = (shift_idx >> 2 ) + 2;
+ state->shift[1] = (shift_idx & 3 ) + 3 + state->shift[0];
+ }
+}
+
+int ff_vvc_cabac_init(VVCLocalContext *lc,
+ const int ctu_idx, const int rx, const int ry)
+{
+ int ret = 0;
+ const VVCPPS *pps = lc->fc->ps.pps;
+ const int first_ctb_in_slice = !ctu_idx;
+ const int first_ctb_in_tile = rx == pps->ctb_to_col_bd[rx] && ry == pps->ctb_to_row_bd[ry];
+
+ if (first_ctb_in_slice|| first_ctb_in_tile) {
+ if (lc->sc->nb_eps == 1 && !first_ctb_in_slice)
+ ret = cabac_reinit(lc);
+ if (ret == 0)
+ cabac_init_state(lc);
+ }
+ return ret;
+}
+
+//fixme
+static void vvc_refill2(CABACContext* c) {
+ int i;
+ unsigned x;
+#if !HAVE_FAST_CLZ
+ x = c->low ^ (c->low - 1);
+ i = 7 - ff_h264_norm_shift[x >> (CABAC_BITS - 1)];
+#else
+ i = ff_ctz(c->low) - CABAC_BITS;
+#endif
+
+ x = -CABAC_MASK;
+
+#if CABAC_BITS == 16
+ x += (c->bytestream[0] << 9) + (c->bytestream[1] << 1);
+#else
+ x += c->bytestream[0] << 1;
+#endif
+
+ c->low += x << i;
+#if !UNCHECKED_BITSTREAM_READER
+ if (c->bytestream < c->bytestream_end)
+#endif
+ c->bytestream += CABAC_BITS / 8;
+}
+
+static int inline vvc_get_cabac(CABACContext *c, VVCCabacState* base, const int ctx)
+{
+ VVCCabacState *s = base + ctx;
+ const int qRangeIdx = c->range >> 5;
+ const int pState = s->state[1] + (s->state[0] << 4);
+ const int valMps = pState >> 14;
+ const int RangeLPS = (qRangeIdx * ((valMps ? 32767 - pState : pState) >> 9 ) >> 1) + 4;
+ int bit, lps_mask;
+
+ c->range -= RangeLPS;
+ lps_mask = ((c->range<<(CABAC_BITS+1)) - c->low)>>31;
+
+ c->low -= (c->range<<(CABAC_BITS+1)) & lps_mask;
+ c->range += (RangeLPS - c->range) & lps_mask;
+
+ bit = valMps ^ (lps_mask & 1);
+
+ lps_mask = ff_h264_norm_shift[c->range];
+ c->range <<= lps_mask;
+ c->low <<= lps_mask;
+
+ if (!(c->low & CABAC_MASK))
+ vvc_refill2(c);
+ s->state[0] = s->state[0] - (s->state[0] >> s->shift[0]) + (1023 * bit >> s->shift[0]);
+ s->state[1] = s->state[1] - (s->state[1] >> s->shift[1]) + (16383 * bit >> s->shift[1]);
+ return bit;
+}
+
+#define GET_CABAC(ctx) vvc_get_cabac(&lc->ep->cc, lc->ep->cabac_state, ctx)
+
+//9.3.3.4 Truncated binary (TB) binarization process
+static int truncated_binary_decode(VVCLocalContext *lc, const int c_max)
+{
+ const int n = c_max + 1;
+ const int k = av_log2(n);
+ const int u = (1 << (k+1)) - n;
+ int v = 0;
+ for (int i = 0; i < k; i++)
+ v = (v << 1) | get_cabac_bypass(&lc->ep->cc);
+ if (v >= u) {
+ v = (v << 1) | get_cabac_bypass(&lc->ep->cc);
+ v -= u;
+ }
+ return v;
+}
+
+// 9.3.3.6 Limited k-th order Exp-Golomb binarization process
+static int limited_kth_order_egk_decode(CABACContext *c, const int k, const int max_pre_ext_len, const int trunc_suffix_len)
+{
+ int pre_ext_len = 0;
+ int escape_length;
+ int val = 0;
+ while ((pre_ext_len < max_pre_ext_len) && get_cabac_bypass(c))
+ pre_ext_len++;
+ if (pre_ext_len == max_pre_ext_len)
+ escape_length = trunc_suffix_len;
+ else
+ escape_length = pre_ext_len + k;
+ while (escape_length-- > 0) {
+ val = (val << 1) + get_cabac_bypass(c);
+ }
+ val += ((1 << pre_ext_len) - 1) << k;
+ return val;
+}
+
+static av_always_inline
+void get_left_top(const VVCLocalContext *lc, uint8_t *left, uint8_t *top,
+ const int x0, const int y0, const uint8_t *left_ctx, const uint8_t *top_ctx)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+ const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y);
+ const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y);
+ const int x_cb = x0 >> sps->min_cb_log2_size_y;
+ const int y_cb = y0 >> sps->min_cb_log2_size_y;
+
+ if (lc->ctb_left_flag || x0b)
+ *left = SAMPLE_CTB(left_ctx, x_cb - 1, y_cb);
+ if (lc->ctb_up_flag || y0b)
+ *top = SAMPLE_CTB(top_ctx, x_cb, y_cb - 1);
+}
+
+static av_always_inline
+uint8_t get_inc(VVCLocalContext *lc, const uint8_t *ctx)
+{
+ uint8_t left = 0, top = 0;
+ get_left_top(lc, &left, &top, lc->cu->x0, lc->cu->y0, ctx, ctx);
+ return left + top;
+}
+
+int ff_vvc_sao_merge_flag_decode(VVCLocalContext *lc)
+{
+ return GET_CABAC(SAO_MERGE_FLAG);
+}
+
+int ff_vvc_sao_type_idx_decode(VVCLocalContext *lc)
+{
+ if (!GET_CABAC(SAO_TYPE_IDX))
+ return SAO_NOT_APPLIED;
+
+ if (!get_cabac_bypass(&lc->ep->cc))
+ return SAO_BAND;
+ return SAO_EDGE;
+}
+
+int ff_vvc_sao_band_position_decode(VVCLocalContext *lc)
+{
+ int value = get_cabac_bypass(&lc->ep->cc);
+
+ for (int i = 0; i < 4; i++)
+ value = (value << 1) | get_cabac_bypass(&lc->ep->cc);
+ return value;
+}
+
+int ff_vvc_sao_offset_abs_decode(VVCLocalContext *lc)
+{
+ int i = 0;
+ int length = (1 << (FFMIN(lc->fc->ps.sps->bit_depth, 10) - 5)) - 1;
+
+ while (i < length && get_cabac_bypass(&lc->ep->cc))
+ i++;
+ return i;
+}
+
+int ff_vvc_sao_offset_sign_decode(VVCLocalContext *lc)
+{
+ return get_cabac_bypass(&lc->ep->cc);
+}
+
+int ff_vvc_sao_eo_class_decode(VVCLocalContext *lc)
+{
+ int ret = get_cabac_bypass(&lc->ep->cc) << 1;
+ ret |= get_cabac_bypass(&lc->ep->cc);
+ return ret;
+}
+
+int ff_vvc_alf_ctb_flag(VVCLocalContext *lc, const int rx, const int ry, const int c_idx)
+{
+ int inc = c_idx * 3;
+ const VVCFrameContext *fc = lc->fc;
+ if (lc->ctb_left_flag) {
+ const ALFParams *left = &CTB(fc->tab.alf, rx - 1, ry);
+ inc += left->ctb_flag[c_idx];
+ }
+ if (lc->ctb_up_flag) {
+ const ALFParams *above = &CTB(fc->tab.alf, rx, ry - 1);
+ inc += above->ctb_flag[c_idx];
+ }
+ return GET_CABAC(ALF_CTB_FLAG + inc);
+}
+
+int ff_vvc_alf_use_aps_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(ALF_USE_APS_FLAG);
+}
+
+int ff_vvc_alf_luma_prev_filter_idx(VVCLocalContext *lc)
+{
+ return truncated_binary_decode(lc, lc->sc->sh.r->sh_num_alf_aps_ids_luma - 1);
+}
+
+int ff_vvc_alf_luma_fixed_filter_idx(VVCLocalContext *lc)
+{
+ return truncated_binary_decode(lc, 15);
+}
+
+int ff_vvc_alf_ctb_filter_alt_idx(VVCLocalContext *lc, const int c_idx, const int num_chroma_filters)
+{
+ int i = 0;
+ const int length = num_chroma_filters - 1;
+
+ while (i < length && GET_CABAC(ALF_CTB_FILTER_ALT_IDX + c_idx - 1))
+ i++;
+ return i;
+}
+
+int ff_vvc_alf_ctb_cc_idc(VVCLocalContext *lc, const int rx, const int ry, const int idx, const int cc_filters_signalled)
+{
+ int inc = !idx ? ALF_CTB_CC_CB_IDC : ALF_CTB_CC_CR_IDC;
+ int i = 0;
+ const VVCFrameContext *fc = lc->fc;
+ if (lc->ctb_left_flag) {
+ const ALFParams *left = &CTB(fc->tab.alf, rx - 1, ry);
+ inc += left->ctb_cc_idc[idx] != 0;
+ }
+ if (lc->ctb_up_flag) {
+ const ALFParams *above = &CTB(fc->tab.alf, rx, ry - 1);
+ inc += above->ctb_cc_idc[idx] != 0;
+ }
+
+ if (!GET_CABAC(inc))
+ return 0;
+ i++;
+ while (i < cc_filters_signalled && get_cabac_bypass(&lc->ep->cc))
+ i++;
+ return i;
+}
+
+int ff_vvc_split_cu_flag(VVCLocalContext *lc, const int x0, const int y0,
+ const int cb_width, const int cb_height, const int is_chroma, const VVCAllowedSplit *a)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCPPS *pps = fc->ps.pps;
+ const int is_inside = (x0 + cb_width <= pps->width) && (y0 + cb_height <= pps->height);
+
+ if ((a->btv || a->bth || a->ttv || a->tth || a->qt) && is_inside)
+ {
+ uint8_t inc = 0, left_height = cb_height, top_width = cb_width;
+
+ get_left_top(lc, &left_height, &top_width, x0, y0, fc->tab.cb_height[is_chroma], fc->tab.cb_width[is_chroma]);
+ inc += left_height < cb_height;
+ inc += top_width < cb_width;
+ inc += (a->btv + a->bth + a->ttv + a->tth + 2 * a->qt - 1) / 2 * 3;
+
+ return GET_CABAC(SPLIT_CU_FLAG + inc);
+
+ }
+ return !is_inside;
+}
+
+static int split_qt_flag_decode(VVCLocalContext *lc, const int x0, const int y0, const int ch_type, const int cqt_depth)
+{
+ const VVCFrameContext *fc = lc->fc;
+ int inc = 0;
+ uint8_t depth_left = 0, depth_top = 0;
+
+ get_left_top(lc, &depth_left, &depth_top, x0, y0, fc->tab.cqt_depth[ch_type], fc->tab.cqt_depth[ch_type]);
+ inc += depth_left > cqt_depth;
+ inc += depth_top > cqt_depth;
+ inc += (cqt_depth >= 2) * 3;
+
+ return GET_CABAC(SPLIT_QT_FLAG + inc);
+}
+
+static int mtt_split_cu_vertical_flag_decode(VVCLocalContext *lc, const int x0, const int y0,
+ const int cb_width, const int cb_height, const int ch_type, const VVCAllowedSplit* a)
+{
+ if ((a->bth || a->tth) && (a->btv || a->ttv)) {
+ int inc;
+ const int v = a->btv + a->ttv;
+ const int h = a->bth + a->tth;
+ if (v > h)
+ inc = 4;
+ else if (v < h)
+ inc = 3;
+ else {
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+ const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y);
+ const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y);
+ const int x_cb = x0 >> sps->min_cb_log2_size_y;
+ const int y_cb = y0 >> sps->min_cb_log2_size_y;
+ const int available_a = lc->ctb_up_flag || y0b;
+ const int available_l = lc->ctb_left_flag || x0b;
+ const int da = cb_width / (available_a ? SAMPLE_CTB(fc->tab.cb_width[ch_type], x_cb, y_cb - 1) : 1);
+ const int dl = cb_height / (available_l ? SAMPLE_CTB(fc->tab.cb_height[ch_type], x_cb - 1, y_cb) : 1);
+
+ if (da == dl || !available_a || !available_l)
+ inc = 0;
+ else if (da < dl)
+ inc = 1;
+ else
+ inc = 2;
+ }
+ return GET_CABAC(MTT_SPLIT_CU_VERTICAL_FLAG + inc);
+ }
+ return !(a->bth || a->tth);
+}
+
+static int mtt_split_cu_binary_flag_decode(VVCLocalContext *lc, const int mtt_split_cu_vertical_flag, const int mtt_depth)
+{
+ int inc = (2 * mtt_split_cu_vertical_flag) + ((mtt_depth <= 1) ? 1 : 0);
+ return GET_CABAC(MTT_SPLIT_CU_BINARY_FLAG + inc);
+}
+
+VVCSplitMode ff_vvc_split_mode(VVCLocalContext *lc, const int x0, const int y0, const int cb_width, const int cb_height,
+ const int cqt_depth, const int mtt_depth, const int ch_type, const VVCAllowedSplit *a)
+{
+ int allow_no_qt = a->btv || a->bth || a->ttv || a->tth;
+ int split_qt_flag;
+ int mtt_split_cu_vertical_flag;
+ int mtt_split_cu_binary_flag;
+ const VVCSplitMode mtt_split_modes[] = {
+ SPLIT_TT_HOR, SPLIT_BT_HOR, SPLIT_TT_VER, SPLIT_BT_VER,
+ };
+ if (allow_no_qt && a->qt) {
+ split_qt_flag = split_qt_flag_decode(lc, x0, y0, ch_type, cqt_depth);
+ } else {
+ split_qt_flag = !allow_no_qt || a->qt;
+ }
+ if (split_qt_flag)
+ return SPLIT_QT;
+ mtt_split_cu_vertical_flag = mtt_split_cu_vertical_flag_decode(lc, x0, y0, cb_width, cb_height, ch_type, a);
+ if ((a->btv && a->ttv && mtt_split_cu_vertical_flag) ||
+ (a->bth && a->tth && !mtt_split_cu_vertical_flag)) {
+ mtt_split_cu_binary_flag = mtt_split_cu_binary_flag_decode(lc, mtt_split_cu_vertical_flag, mtt_depth);
+ } else {
+ if (!a->btv && !a->bth)
+ mtt_split_cu_binary_flag = 0;
+ else if (!a->ttv && !a->tth)
+ mtt_split_cu_binary_flag = 1;
+ else if (a->bth && a->ttv)
+ mtt_split_cu_binary_flag = 1 - mtt_split_cu_vertical_flag;
+ else
+ mtt_split_cu_binary_flag = mtt_split_cu_vertical_flag;
+ }
+ return mtt_split_modes[(mtt_split_cu_vertical_flag << 1) + mtt_split_cu_binary_flag];
+}
+
+int ff_vvc_non_inter_flag(VVCLocalContext *lc, const int x0, const int y0, const int ch_type)
+{
+ const VVCFrameContext *fc = lc->fc;
+ uint8_t inc, left = 0, top = 0;
+
+ get_left_top(lc, &left, &top, x0, y0, fc->tab.cpm[ch_type], fc->tab.cpm[ch_type]);
+ inc = left || top;
+ return GET_CABAC(NON_INTER_FLAG + inc);
+}
+
+int ff_vvc_pred_mode_flag(VVCLocalContext *lc, const int is_chroma)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ uint8_t inc, left = 0, top = 0;
+
+ get_left_top(lc, &left, &top, cu->x0, cu->y0, fc->tab.cpm[is_chroma], fc->tab.cpm[is_chroma]);
+ inc = left || top;
+ return GET_CABAC(PRED_MODE_FLAG + inc);
+}
+
+int ff_vvc_pred_mode_plt_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(PRED_MODE_PLT_FLAG);
+}
+
+int ff_vvc_intra_bdpcm_luma_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(INTRA_BDPCM_LUMA_FLAG);
+}
+
+int ff_vvc_intra_bdpcm_luma_dir_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(INTRA_BDPCM_LUMA_DIR_FLAG);
+}
+
+int ff_vvc_intra_bdpcm_chroma_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(INTRA_BDPCM_CHROMA_FLAG);
+}
+
+int ff_vvc_intra_bdpcm_chroma_dir_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(INTRA_BDPCM_CHROMA_DIR_FLAG);
+}
+
+int ff_vvc_cu_skip_flag(VVCLocalContext *lc, const uint8_t *cu_skip_flag)
+{
+ const int inc = get_inc(lc, cu_skip_flag);
+ return GET_CABAC(CU_SKIP_FLAG + inc);
+}
+
+int ff_vvc_pred_mode_ibc_flag(VVCLocalContext *lc, const int is_chroma)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ uint8_t left_mode = MODE_INTER, top_mode = MODE_INTER;
+ int inc;
+
+ get_left_top(lc, &left_mode, &top_mode, cu->x0, cu->y0, fc->tab.cpm[is_chroma], fc->tab.cpm[is_chroma]);
+ inc = (left_mode == MODE_IBC) + (top_mode == MODE_IBC);
+ return GET_CABAC(PRED_MODE_IBC_FLAG + inc);
+}
+
+int ff_vvc_intra_mip_flag(VVCLocalContext *lc, const uint8_t *intra_mip_flag)
+{
+ const int w = lc->cu->cb_width;
+ const int h = lc->cu->cb_height;
+ const int inc = (w > h * 2 || h > w * 2) ? 3 : get_inc(lc, intra_mip_flag);
+ return GET_CABAC(INTRA_MIP_FLAG + inc);
+}
+
+int ff_vvc_intra_mip_transposed_flag(VVCLocalContext *lc)
+{
+ return get_cabac_bypass(&lc->ep->cc);
+}
+
+int ff_vvc_intra_mip_mode(VVCLocalContext *lc)
+{
+ const int w = lc->cu->cb_width;
+ const int h = lc->cu->cb_height;
+ const int c_max = (w == 4 && h == 4) ? 15 :
+ ((w == 4 || h == 4) || (w == 8 && h == 8)) ? 7: 5;
+ return truncated_binary_decode(lc, c_max);
+}
+
+int ff_vvc_intra_luma_ref_idx(VVCLocalContext *lc)
+{
+ int i;
+ for (i = 0; i < 2; i++) {
+ if (!GET_CABAC(INTRA_LUMA_REF_IDX + i))
+ return i;
+ }
+ return i;
+}
+
+int ff_vvc_intra_subpartitions_mode_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(INTRA_SUBPARTITIONS_MODE_FLAG);
+}
+
+enum IspType ff_vvc_isp_split_type(VVCLocalContext *lc, const int intra_subpartitions_mode_flag)
+{
+ if (!intra_subpartitions_mode_flag)
+ return ISP_NO_SPLIT;
+ return 1 + GET_CABAC(INTRA_SUBPARTITIONS_SPLIT_FLAG);
+}
+
+int ff_vvc_intra_luma_mpm_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(INTRA_LUMA_MPM_FLAG);
+}
+
+int ff_vvc_intra_luma_not_planar_flag(VVCLocalContext *lc, const int intra_subpartitions_mode_flag)
+{
+ return GET_CABAC(INTRA_LUMA_NOT_PLANAR_FLAG + !intra_subpartitions_mode_flag);
+}
+
+int ff_vvc_intra_luma_mpm_idx(VVCLocalContext *lc)
+{
+ int i;
+ for (i = 0; i < 4 && get_cabac_bypass(&lc->ep->cc); i++)
+ /* nothing */;
+ return i;
+}
+
+int ff_vvc_intra_luma_mpm_remainder(VVCLocalContext *lc)
+{
+ return truncated_binary_decode(lc, 60);
+}
+
+int ff_vvc_cclm_mode_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(CCLM_MODE_FLAG);
+}
+
+int ff_vvc_cclm_mode_idx(VVCLocalContext *lc)
+{
+ if (!GET_CABAC(CCLM_MODE_IDX))
+ return 0;
+ return get_cabac_bypass(&lc->ep->cc) + 1;
+}
+
+int ff_vvc_intra_chroma_pred_mode(VVCLocalContext *lc)
+{
+ if (!GET_CABAC(INTRA_CHROMA_PRED_MODE))
+ return 4;
+ return (get_cabac_bypass(&lc->ep->cc) << 1) | get_cabac_bypass(&lc->ep->cc);
+}
+
+int ff_vvc_general_merge_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(GENERAL_MERGE_FLAG);
+}
+
+static int get_inter_flag_inc(VVCLocalContext *lc, const int x0, const int y0)
+{
+ uint8_t left_merge = 0, top_merge = 0;
+ uint8_t left_affine = 0, top_affine = 0;
+ const VVCFrameContext *fc = lc->fc;
+
+ get_left_top(lc, &left_merge, &top_merge, x0, y0, fc->tab.msf, fc->tab.msf);
+ get_left_top(lc, &left_affine, &top_affine, x0, y0, fc->tab.iaf, fc->tab.iaf);
+ return (left_merge || left_affine) + (top_merge + top_affine);
+}
+
+int ff_vvc_merge_subblock_flag(VVCLocalContext *lc)
+{
+ const int inc = get_inter_flag_inc(lc, lc->cu->x0, lc->cu->y0);
+ return GET_CABAC(MERGE_SUBBLOCK_FLAG + inc);
+}
+
+int ff_vvc_merge_subblock_idx(VVCLocalContext *lc, const int max_num_subblock_merge_cand)
+{
+ int i;
+ if (!GET_CABAC(MERGE_SUBBLOCK_IDX))
+ return 0;
+ for (i = 1; i < max_num_subblock_merge_cand - 1 && get_cabac_bypass(&lc->ep->cc); i++)
+ /* nothing */;
+ return i;
+}
+
+int ff_vvc_regular_merge_flag(VVCLocalContext *lc, const int cu_skip_flag)
+{
+ int inc = !cu_skip_flag;
+ return GET_CABAC(REGULAR_MERGE_FLAG + inc);
+}
+
+int ff_vvc_mmvd_merge_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(MMVD_MERGE_FLAG);
+}
+
+int ff_vvc_mmvd_cand_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(MMVD_CAND_FLAG);
+}
+
+static int mmvd_distance_idx_decode(VVCLocalContext *lc)
+{
+ int i;
+ if (!GET_CABAC(MMVD_DISTANCE_IDX))
+ return 0;
+ for (i = 1; i < 7 && get_cabac_bypass(&lc->ep->cc); i++)
+ /* nothing */;
+ return i;
+}
+
+static int mmvd_direction_idx_decode(VVCLocalContext *lc)
+{
+ return (get_cabac_bypass(&lc->ep->cc) << 1) | get_cabac_bypass(&lc->ep->cc);
+}
+
+void ff_vvc_mmvd_offset_coding(VVCLocalContext *lc, Mv *mmvd_offset, const int ph_mmvd_fullpel_only_flag)
+{
+ const int shift = ph_mmvd_fullpel_only_flag ? 4 : 2;
+ const int mmvd_distance = 1 << (mmvd_distance_idx_decode(lc) + shift);
+ const int mmvd_direction_idx = mmvd_direction_idx_decode(lc);
+ const int mmvd_signs[][2] = { {1, 0}, {-1, 0}, {0, 1}, {0, -1} };
+ mmvd_offset->x = mmvd_distance * mmvd_signs[mmvd_direction_idx][0];
+ mmvd_offset->y = mmvd_distance * mmvd_signs[mmvd_direction_idx][1];
+}
+
+static PredMode get_luma_pred_mode(VVCLocalContext *lc)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ PredMode pred_mode;
+ if (cu->tree_type != DUAL_TREE_CHROMA) {
+ pred_mode = cu->pred_mode;
+ } else {
+ const int x_cb = cu->x0 >> fc->ps.sps->min_cb_log2_size_y;
+ const int y_cb = cu->y0 >> fc->ps.sps->min_cb_log2_size_y;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+ pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_cb, y_cb);
+ }
+ return pred_mode;
+}
+
+int ff_vvc_merge_idx(VVCLocalContext *lc)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const int is_ibc = get_luma_pred_mode(lc) == MODE_IBC;
+ const int c_max = (is_ibc ? sps->max_num_ibc_merge_cand : sps->max_num_merge_cand) - 1;
+ int i;
+
+ if (!GET_CABAC(MERGE_IDX))
+ return 0;
+
+ for (i = 1; i < c_max && get_cabac_bypass(&lc->ep->cc); i++)
+ /* nothing */;
+ return i;
+}
+
+int ff_vvc_merge_gpm_partition_idx(VVCLocalContext *lc)
+{
+ int i = 0;
+
+ for (int j = 0; j < 6; j++)
+ i = (i << 1) | get_cabac_bypass(&lc->ep->cc);
+
+ return i;
+}
+
+int ff_vvc_merge_gpm_idx(VVCLocalContext *lc, const int idx)
+{
+ const int c_max = lc->fc->ps.sps->max_num_gpm_merge_cand - idx - 1;
+ int i;
+
+ if (!GET_CABAC(MERGE_IDX))
+ return 0;
+
+ for (i = 1; i < c_max && get_cabac_bypass(&lc->ep->cc); i++)
+ /* nothing */;
+
+ return i;
+}
+
+int ff_vvc_ciip_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(CIIP_FLAG);
+}
+
+PredFlag ff_vvc_pred_flag(VVCLocalContext *lc, const int is_b)
+{
+ const int w = lc->cu->cb_width;
+ const int h = lc->cu->cb_height;
+ if (!is_b)
+ return PF_L0;
+ if (w + h > 12) {
+ const int log2 = av_log2(w) + av_log2(h);
+ const int inc = 7 - ((1 + log2)>>1);
+ if (GET_CABAC(INTER_PRED_IDC + inc))
+ return PF_BI;
+ }
+ return PF_L0 + GET_CABAC(INTER_PRED_IDC + 5);
+}
+
+int ff_vvc_inter_affine_flag(VVCLocalContext *lc)
+{
+ const int inc = get_inter_flag_inc(lc, lc->cu->x0, lc->cu->y0);
+ return GET_CABAC(INTER_AFFINE_FLAG + inc);
+}
+
+int ff_vvc_cu_affine_type_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(CU_AFFINE_TYPE_FLAG);
+}
+
+int ff_vvc_sym_mvd_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(SYM_MVD_FLAG);
+}
+
+int ff_vvc_ref_idx_lx(VVCLocalContext *lc, const uint8_t nb_refs)
+{
+ const int c_max = nb_refs - 1;
+ const int max_ctx = FFMIN(c_max, 2);
+ int i = 0;
+
+ while (i < max_ctx && GET_CABAC(REF_IDX_LX + i))
+ i++;
+ if (i == 2) {
+ while (i < c_max && get_cabac_bypass(&lc->ep->cc))
+ i++;
+ }
+ return i;
+}
+
+int ff_vvc_abs_mvd_greater0_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(ABS_MVD_GREATER0_FLAG);
+}
+
+int ff_vvc_abs_mvd_greater1_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(ABS_MVD_GREATER1_FLAG);
+}
+
+int ff_vvc_abs_mvd_minus2(VVCLocalContext *lc)
+{
+ return limited_kth_order_egk_decode(&lc->ep->cc, 1, 15, 17);
+}
+
+int ff_vvc_mvd_sign_flag(VVCLocalContext *lc)
+{
+ return get_cabac_bypass(&lc->ep->cc);
+}
+
+int ff_vvc_mvp_lx_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(MVP_LX_FLAG);
+}
+
+static int amvr_flag(VVCLocalContext *lc, const int inter_affine_flag)
+{
+ return GET_CABAC(AMVR_FLAG + inter_affine_flag);
+}
+
+static int amvr_precision_idx(VVCLocalContext *lc, const int inc, const int c_max)
+{
+ int i = 0;
+ if (!GET_CABAC(AMVR_PRECISION_IDX + inc))
+ return 0;
+ i++;
+ if (i < c_max && GET_CABAC(AMVR_PRECISION_IDX + 1))
+ i++;
+ return i;
+}
+
+int ff_vvc_amvr_shift(VVCLocalContext *lc, const int inter_affine_flag,
+ const PredMode pred_mode, const int has_amvr_flag)
+{
+ int amvr_shift = 2;
+ if (has_amvr_flag) {
+ if (amvr_flag(lc, inter_affine_flag)) {
+ int idx;
+ if (inter_affine_flag) {
+ idx = amvr_precision_idx(lc, 2, 1);
+ amvr_shift = idx * 4;
+ } else if (pred_mode == MODE_IBC) {
+ idx = amvr_precision_idx(lc, 1, 1);
+ amvr_shift = 4 + idx * 2;
+ } else {
+ static const int shifts[] = {3, 4, 6};
+ idx = amvr_precision_idx(lc, 0, 2);
+ amvr_shift = shifts[idx];
+ }
+ }
+ }
+ return amvr_shift;
+}
+
+int ff_vvc_bcw_idx(VVCLocalContext *lc, const int no_backward_pred_flag)
+{
+ const int c_max = no_backward_pred_flag ? 4 : 2;
+ int i = 1;
+ if (!GET_CABAC(BCW_IDX))
+ return 0;
+ while (i < c_max && get_cabac_bypass(&lc->ep->cc))
+ i++;
+ return i;
+}
+
+int ff_vvc_tu_cb_coded_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(TU_CB_CODED_FLAG + lc->cu->bdpcm_flag[1]);
+}
+
+int ff_vvc_tu_cr_coded_flag(VVCLocalContext *lc, int tu_cb_coded_flag)
+{
+ return GET_CABAC(TU_CR_CODED_FLAG + (lc->cu->bdpcm_flag[1] ? 2 : tu_cb_coded_flag));
+}
+
+int ff_vvc_tu_y_coded_flag(VVCLocalContext *lc)
+{
+ const CodingUnit *cu = lc->cu;
+ int inc;
+ if (cu->bdpcm_flag[0])
+ inc = 1;
+ else if (cu->isp_split_type == ISP_NO_SPLIT)
+ inc = 0;
+ else
+ inc = 2 + lc->parse.prev_tu_cbf_y;
+ lc->parse.prev_tu_cbf_y = GET_CABAC(TU_Y_CODED_FLAG + inc);
+ return lc->parse.prev_tu_cbf_y;
+}
+
+int ff_vvc_cu_qp_delta_abs(VVCLocalContext *lc)
+{
+ int v, i, k;
+ if (!GET_CABAC(CU_QP_DELTA_ABS))
+ return 0;
+
+ // prefixVal
+ for (v = 1; v < 5 && GET_CABAC(CU_QP_DELTA_ABS + 1); v++)
+ /* nothing */;
+ if (v < 5)
+ return v;
+
+ // 9.3.3.5 k-th order Exp-Golomb binarization process
+ // suffixVal
+
+ // CuQpDeltaVal shall in the range of −( 32 + QpBdOffset / 2 ) to +( 31 + QpBdOffset / 2 )
+ // so k = 6 should enough
+ for (k = 0; k < 6 && get_cabac_bypass(&lc->ep->cc); k++)
+ /* nothing */;
+ i = (1 << k) - 1;
+ v = 0;
+ while (k--)
+ v = (v << 1) + get_cabac_bypass(&lc->ep->cc);
+ v += i;
+
+ return v + 5;
+}
+
+int ff_vvc_cu_qp_delta_sign_flag(VVCLocalContext *lc)
+{
+ return get_cabac_bypass(&lc->ep->cc);
+}
+
+int ff_vvc_cu_chroma_qp_offset_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(CU_CHROMA_QP_OFFSET_FLAG);
+}
+int ff_vvc_cu_chroma_qp_offset_idx(VVCLocalContext *lc)
+{
+ const int c_max = lc->fc->ps.pps->r->pps_chroma_qp_offset_list_len_minus1;
+ int i;
+ for (i = 0; i < c_max && GET_CABAC(CU_CHROMA_QP_OFFSET_IDX); i++)
+ /* nothing */;
+ return i;
+}
+
+static av_always_inline int last_significant_coeff_xy_prefix(VVCLocalContext *lc,
+ const int log2_tb_size, const int log2_zo_tb_size, const int c_idx, const int ctx)
+{
+ int i = 0;
+ int max = (log2_zo_tb_size << 1) - 1;
+ int ctx_offset, ctx_shift;
+ if (!log2_tb_size)
+ return 0;
+ if (!c_idx) {
+ const int offset_y[] = {0, 0, 3, 6, 10, 15};
+ ctx_offset = offset_y[log2_tb_size - 1];
+ ctx_shift = (log2_tb_size + 1) >> 2;
+ } else {
+ const int shifts[] = {0, 0, 0, 1, 2, 2, 2};
+ ctx_offset = 20;
+ ctx_shift = shifts[log2_tb_size];
+ }
+ while (i < max && GET_CABAC(ctx + (i >> ctx_shift) + ctx_offset))
+ i++;
+ return i;
+}
+
+static av_always_inline int last_significant_coeff_x_prefix_decode(VVCLocalContext *lc,
+ const int log2_tb_width, const int log2_zo_tb_width, const int c_idx)
+{
+ return last_significant_coeff_xy_prefix(lc, log2_tb_width, log2_zo_tb_width, c_idx, LAST_SIG_COEFF_X_PREFIX);
+}
+
+static av_always_inline int last_significant_coeff_y_prefix_decode(VVCLocalContext *lc,
+ const int log2_tb_height, const int log2_zo_tb_height, const int c_idx)
+{
+ return last_significant_coeff_xy_prefix(lc, log2_tb_height, log2_zo_tb_height, c_idx, LAST_SIG_COEFF_Y_PREFIX);
+}
+
+static av_always_inline int last_sig_coeff_suffix_decode(VVCLocalContext *lc,
+ const int last_significant_coeff_y_prefix)
+{
+ const int length = (last_significant_coeff_y_prefix >> 1) - 1;
+ int value = get_cabac_bypass(&lc->ep->cc);
+
+ for (int i = 1; i < length; i++)
+ value = (value << 1) | get_cabac_bypass(&lc->ep->cc);
+ return value;
+}
+
+int ff_vvc_tu_joint_cbcr_residual_flag(VVCLocalContext *lc, const int tu_cb_coded_flag, const int tu_cr_coded_flag)
+{
+ return GET_CABAC(TU_JOINT_CBCR_RESIDUAL_FLAG + 2 * tu_cb_coded_flag + tu_cr_coded_flag - 1);
+}
+
+int ff_vvc_transform_skip_flag(VVCLocalContext *lc, const int inc)
+{
+ return GET_CABAC(TRANSFORM_SKIP_FLAG + inc);
+}
+
+//9.3.4.2.7 Derivation process for the variables locNumSig, locSumAbsPass1
+static int get_local_sum(const int *level, const int w, const int h,
+ const int xc, const int yc, const int hist_value)
+{
+ int loc_sum = 3 * hist_value;
+ level += w * yc + xc;
+ if (xc < w - 1) {
+ loc_sum += level[1];
+ if (xc < w - 2)
+ loc_sum += level[2] - hist_value;
+ if (yc < h - 1)
+ loc_sum += level[w + 1] - hist_value;
+ }
+ if (yc < h - 1) {
+ loc_sum += level[w];
+ if (yc < h - 2)
+ loc_sum += level[w << 1] - hist_value;
+ }
+ return loc_sum;
+}
+
+//9.3.4.2.7 Derivation process for the variables locNumSig, locSumAbsPass1
+static int get_local_sum_ts(const int *level, const int w, const int h, const int xc, const int yc)
+{
+ int loc_sum = 0;
+ level += w * yc + xc;
+ if (xc > 0)
+ loc_sum += level[-1];
+ if (yc > 0)
+ loc_sum += level[-w];
+ return loc_sum;
+}
+
+static int get_gtx_flag_inc(const ResidualCoding* rc, const int xc, const int yc, const int last)
+{
+ const TransformBlock *tb = rc->tb;
+ int inc;
+ if (last) {
+ const int incs[] = {0, 21, 21};
+ inc = incs[tb->c_idx];
+ } else {
+ const int d = xc + yc;
+ const int local_sum_sig = get_local_sum(rc->sig_coeff_flag,
+ tb->tb_width,tb->tb_height, xc, yc, rc->hist_value);
+ const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1,
+ tb->tb_width, tb->tb_height, xc, yc, rc->hist_value);
+ const int offset = FFMIN(loc_sum_abs_pass1 - local_sum_sig, 4);
+
+ if (!tb->c_idx)
+ inc = 1 + offset + (!d ? 15 : (d < 3 ? 10 : (d < 10 ? 5 : 0)));
+ else
+ inc = 22 + offset + (!d ? 5 : 0);
+ }
+ return inc;
+}
+
+static int abs_level_gtx_flag_decode(VVCLocalContext *lc, const int inc)
+{
+ return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc);
+}
+
+static int par_level_flag_decode(VVCLocalContext *lc, const int inc)
+{
+ return GET_CABAC(PAR_LEVEL_FLAG + inc);
+}
+
+static int par_level_flag_ts_decode(VVCLocalContext *lc)
+{
+ const int inc = 32;
+ return GET_CABAC(PAR_LEVEL_FLAG + inc);
+}
+
+static int sb_coded_flag_decode(VVCLocalContext *lc, const uint8_t *sb_coded_flag,
+ const ResidualCoding *rc, const int xs, const int ys)
+{
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const TransformBlock *tb = rc->tb;
+ const int w = rc->width_in_sbs;
+ const int h = rc->height_in_sbs;
+ int inc;
+
+ if (tb->ts && !rsh->sh_ts_residual_coding_disabled_flag) {
+ const int left = xs > 0 ? sb_coded_flag[-1] : 0;
+ const int above = ys > 0 ? sb_coded_flag[-w] : 0;
+ inc = left + above + 4;
+ } else {
+ const int right = (xs < w - 1) ? sb_coded_flag[1] : 0;
+ const int bottom = (ys < h - 1) ? sb_coded_flag[w] : 0;
+ inc = (right | bottom) + (tb->c_idx ? 2 : 0);
+ }
+ return GET_CABAC(SB_CODED_FLAG + inc);
+}
+
+static int sig_coeff_flag_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc)
+{
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const TransformBlock *tb = rc->tb;
+ int inc;
+
+ if (tb->ts && !rsh->sh_ts_residual_coding_disabled_flag) {
+ const int local_num_sig = get_local_sum_ts(rc->sig_coeff_flag, tb->tb_width, tb->tb_height, xc, yc);
+ inc = 60 + local_num_sig;
+ } else {
+ const int d = xc + yc;
+ const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1,
+ tb->tb_width, tb->tb_height, xc, yc, 0);
+
+ if (!tb->c_idx) {
+ inc = 12 * FFMAX(0, rc->qstate - 1) + FFMIN((loc_sum_abs_pass1 + 1) >> 1, 3) + ((d < 2) ? 8 : (d < 5 ? 4 : 0));
+ } else {
+ inc = 36 + 8 * FFMAX(0, rc->qstate - 1) + FFMIN((loc_sum_abs_pass1 + 1) >> 1, 3) + (d < 2 ? 4 : 0);
+ }
+ }
+ return GET_CABAC(SIG_COEFF_FLAG + inc);
+}
+
+static int abs_get_rice_param(VVCLocalContext *lc, const ResidualCoding* rc,
+ const int xc, const int yc, const int base_level)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const TransformBlock* tb = rc->tb;
+ const int rice_params[] = {
+ 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3,
+ };
+ int loc_sum_abs;
+ int shift_val;
+
+ loc_sum_abs = get_local_sum(rc->abs_level, tb->tb_width, tb->tb_height, xc,
+ yc, rc->hist_value);
+
+ if (!sps->r->sps_rrc_rice_extension_flag) {
+ shift_val = 0;
+ } else {
+ shift_val = (av_log2(FFMAX(FFMIN(loc_sum_abs, 2048), 8)) - 3) & ~1;
+ }
+
+ loc_sum_abs = av_clip_uintp2((loc_sum_abs >> shift_val) - base_level * 5, 5);
+
+ return rice_params[loc_sum_abs] + shift_val;
+}
+
+static int abs_decode(VVCLocalContext *lc, const int c_rice_param)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const int MAX_BIN = 6;
+ int prefix = 0;
+ int suffix = 0;
+
+ while (prefix < MAX_BIN && get_cabac_bypass(&lc->ep->cc))
+ prefix++;
+ if (prefix < MAX_BIN) {
+ for (int i = 0; i < c_rice_param; i++) {
+ suffix = (suffix << 1) | get_cabac_bypass(&lc->ep->cc);
+ }
+ } else {
+ suffix = limited_kth_order_egk_decode(&lc->ep->cc,
+ c_rice_param + 1,
+ 26 - sps->log2_transform_range,
+ sps->log2_transform_range);
+ }
+ return suffix + (prefix << c_rice_param);
+}
+
+static int abs_remainder_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const int base_level[][2][2] = {
+ { {4, 4}, {4, 4} },
+ { {3, 2}, {2, 1} }
+ };
+ const int c_rice_param = abs_get_rice_param(lc, rc, xc, yc,
+ base_level[sps->r->sps_rrc_rice_extension_flag][sps->bit_depth > 12][IS_I(rsh)]);
+ const int rem = abs_decode(lc, c_rice_param);
+ return rem;
+}
+
+static int abs_remainder_ts_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc)
+{
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const int c_rice_param = rsh->sh_ts_residual_coding_rice_idx_minus1 + 1;
+ const int rem = abs_decode(lc, c_rice_param);
+
+ return rem;
+}
+
+static int coeff_sign_flag_decode(VVCLocalContext *lc)
+{
+ return get_cabac_bypass(&lc->ep->cc);
+}
+
+//9.3.4.2.10 Derivation process of ctxInc for the syntax element coeff_sign_flag for transform skip mode
+static int coeff_sign_flag_ts_decode(VVCLocalContext *lc, const CodingUnit *cu, const ResidualCoding *rc, const int xc, const int yc)
+{
+ const TransformBlock *tb = rc->tb;
+ const int w = tb->tb_width;
+ const int *level = rc->coeff_sign_level + yc * w + xc;
+ const int left_sign = (xc == 0) ? 0 : level[-1];
+ const int above_sign = (yc == 0) ? 0 : level[-w];
+ const int bdpcm_flag = cu->bdpcm_flag[tb->c_idx];
+ int inc;
+
+ if (left_sign == -above_sign)
+ inc = bdpcm_flag == 0 ? 0 : 3;
+ else if (left_sign >= 0 && above_sign >= 0)
+ inc = bdpcm_flag == 0 ? 1 : 4;
+ else
+ inc = bdpcm_flag == 0 ? 2 : 5;
+ return GET_CABAC(COEFF_SIGN_FLAG + inc);
+}
+
+static int abs_level_gt1_flag_ts_decode(VVCLocalContext *lc, const CodingUnit *cu, const ResidualCoding *rc, const int xc, const int yc)
+{
+ const TransformBlock *tb = rc->tb;
+ const int *sig_coeff_flag = rc->sig_coeff_flag + yc * tb->tb_width + xc;
+ int inc;
+
+ if (cu->bdpcm_flag[tb->c_idx]) {
+ inc = 67;
+ } else {
+ const int l = xc > 0 ? sig_coeff_flag[-1] : 0;
+ const int a = yc > 0 ? sig_coeff_flag[-tb->tb_width] : 0;
+ inc = 64 + a + l;
+ }
+ return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc);
+}
+
+static int abs_level_gtx_flag_ts_decode(VVCLocalContext *lc, const int j)
+{
+ const int inc = 67 + j;
+ return GET_CABAC(ABS_LEVEL_GTX_FLAG + inc);
+}
+
+static const uint8_t qstate_translate_table[][2] = {
+ { 0, 2 }, { 2, 0 }, { 1, 3 }, { 3, 1 }
+};
+
+static int dec_abs_level_decode(VVCLocalContext *lc, const ResidualCoding *rc,
+ const int xc, const int yc, int *abs_level)
+{
+ const int c_rice_param = abs_get_rice_param(lc, rc, xc, yc, 0);
+ const int dec_abs_level = abs_decode(lc, c_rice_param);
+ const int zero_pos = (rc->qstate < 2 ? 1 : 2) << c_rice_param;
+
+ *abs_level = 0;
+ if (dec_abs_level != zero_pos) {
+ *abs_level = dec_abs_level;
+ if (dec_abs_level < zero_pos)
+ *abs_level += 1;
+ }
+ return dec_abs_level;
+}
+
+static void ep_update_hist(EntryPoint *ep, ResidualCoding *rc,
+ const int remainder, const int addin)
+{
+ int *stat = ep->stat_coeff + rc->tb->c_idx;
+ if (rc->update_hist && remainder > 0) {
+ *stat = (*stat + av_log2(remainder) + addin) >> 1;
+ rc->update_hist = 0;
+ }
+}
+
+static void init_residual_coding(VVCLocalContext *lc, ResidualCoding *rc,
+ const int log2_zo_tb_width, const int log2_zo_tb_height,
+ TransformBlock *tb)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ int log2_sb_w = (FFMIN(log2_zo_tb_width, log2_zo_tb_height ) < 2 ? 1 : 2 );
+ int log2_sb_h = log2_sb_w;
+
+ if ( log2_zo_tb_width + log2_zo_tb_height > 3 ) {
+ if ( log2_zo_tb_width < 2 ) {
+ log2_sb_w = log2_zo_tb_width;
+ log2_sb_h = 4 - log2_sb_w;
+ } else if ( log2_zo_tb_height < 2 ) {
+ log2_sb_h = log2_zo_tb_height;
+ log2_sb_w = 4 - log2_sb_h;
+ }
+ }
+ rc->log2_sb_w = log2_sb_w;
+ rc->log2_sb_h = log2_sb_h;
+ rc->num_sb_coeff = 1 << (log2_sb_w + log2_sb_h);
+ rc->last_sub_block = ( 1 << ( log2_zo_tb_width + log2_zo_tb_height - (log2_sb_w + log2_sb_h))) - 1;
+ rc->hist_value = sps->r->sps_persistent_rice_adaptation_enabled_flag ? (1 << lc->ep->stat_coeff[tb->c_idx]) : 0;
+ rc->update_hist = sps->r->sps_persistent_rice_adaptation_enabled_flag ? 1 : 0;
+ rc->rem_bins_pass1 = (( 1 << ( log2_zo_tb_width + log2_zo_tb_height)) * 7 ) >> 2;
+
+
+ rc->sb_scan_x_off = ff_vvc_diag_scan_x[log2_zo_tb_width - log2_sb_w][log2_zo_tb_height - log2_sb_h];
+ rc->sb_scan_y_off = ff_vvc_diag_scan_y[log2_zo_tb_width - log2_sb_w][log2_zo_tb_height - log2_sb_h];
+
+ rc->scan_x_off = ff_vvc_diag_scan_x[log2_sb_w][log2_sb_h];
+ rc->scan_y_off = ff_vvc_diag_scan_y[log2_sb_w][log2_sb_h];
+
+ rc->infer_sb_cbf = 1;
+
+ rc->width_in_sbs = (1 << (log2_zo_tb_width - log2_sb_w));
+ rc->height_in_sbs = (1 << (log2_zo_tb_height - log2_sb_h));
+ rc->nb_sbs = rc->width_in_sbs * rc->height_in_sbs;
+
+ rc->last_scan_pos = rc->num_sb_coeff;
+ rc->qstate = 0;
+
+ rc->tb = tb;
+}
+
+static int residual_ts_coding_subblock(VVCLocalContext *lc, ResidualCoding* rc, const int i)
+{
+ const CodingUnit *cu = lc->cu;
+ TransformBlock *tb = rc->tb;
+ const int bdpcm_flag = cu->bdpcm_flag[tb->c_idx];
+ const int xs = rc->sb_scan_x_off[i];
+ const int ys = rc->sb_scan_y_off[i];
+ uint8_t *sb_coded_flag = rc->sb_coded_flag + ys * rc->width_in_sbs + xs;
+ int infer_sb_sig_coeff_flag = 1;
+ int last_scan_pos_pass1 = -1, last_scan_pos_pass2 = -1, n;
+ int abs_level_gtx_flag[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE];
+ int abs_level_pass2[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE]; ///< AbsLevelPass2
+
+ if (i != rc->last_sub_block || !rc->infer_sb_cbf)
+ *sb_coded_flag = sb_coded_flag_decode(lc, sb_coded_flag, rc, xs, ys);
+ else
+ *sb_coded_flag = 1;
+ if (*sb_coded_flag && i < rc->last_sub_block)
+ rc->infer_sb_cbf = 0;
+
+ //first scan pass
+ for (n = 0; n < rc->num_sb_coeff && rc->rem_bins_pass1 >= 4; n++) {
+ const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n];
+ const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n];
+ const int off = yc * tb->tb_width + xc;
+ int *sig_coeff_flag = rc->sig_coeff_flag + off;
+ int *abs_level_pass1 = rc->abs_level_pass1 + off;
+ int *coeff_sign_level = rc->coeff_sign_level + off;
+ int par_level_flag = 0;
+
+ abs_level_gtx_flag[n] = 0;
+ last_scan_pos_pass1 = n;
+ if (*sb_coded_flag && (n != rc->num_sb_coeff - 1 || !infer_sb_sig_coeff_flag)) {
+ *sig_coeff_flag = sig_coeff_flag_decode(lc, rc, xc, yc);
+ rc->rem_bins_pass1--;
+ if (*sig_coeff_flag)
+ infer_sb_sig_coeff_flag = 0;
+ } else {
+ *sig_coeff_flag = (n == rc->num_sb_coeff - 1) && infer_sb_sig_coeff_flag && *sb_coded_flag;
+ }
+ *coeff_sign_level = 0;
+ if (*sig_coeff_flag) {
+ *coeff_sign_level = 1 - 2 * coeff_sign_flag_ts_decode(lc, cu, rc, xc, yc);
+ abs_level_gtx_flag[n] = abs_level_gt1_flag_ts_decode(lc, cu, rc, xc, yc);
+ rc->rem_bins_pass1 -= 2;
+ if (abs_level_gtx_flag[n]) {
+ par_level_flag = par_level_flag_ts_decode(lc);
+ rc->rem_bins_pass1--;
+ }
+ }
+ *abs_level_pass1 = *sig_coeff_flag + par_level_flag + abs_level_gtx_flag[n];
+ }
+
+ //greater than x scan pass
+ for (n = 0; n < rc->num_sb_coeff && rc->rem_bins_pass1 >= 4; n++) {
+ const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n];
+ const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n];
+ const int off = yc * tb->tb_width + xc;
+
+ abs_level_pass2[n] = rc->abs_level_pass1[off];
+ for (int j = 1; j < 5 && abs_level_gtx_flag[n]; j++) {
+ abs_level_gtx_flag[n] = abs_level_gtx_flag_ts_decode(lc, j);
+ abs_level_pass2[n] += abs_level_gtx_flag[n] << 1;
+ rc->rem_bins_pass1--;
+ }
+ last_scan_pos_pass2 = n;
+ }
+
+ /* remainder scan pass */
+ for (n = 0; n < rc->num_sb_coeff; n++) {
+ const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n];
+ const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n];
+ const int off = yc * tb->tb_width + xc;
+ const int *abs_level_pass1 = rc->abs_level_pass1 + off;
+ int *abs_level = rc->abs_level + off;
+ int *coeff_sign_level = rc->coeff_sign_level + off;
+ int abs_remainder = 0;
+
+ if ((n <= last_scan_pos_pass2 && abs_level_pass2[n] >= 10) ||
+ (n > last_scan_pos_pass2 && n <= last_scan_pos_pass1 &&
+ *abs_level_pass1 >= 2) ||
+ (n > last_scan_pos_pass1 && *sb_coded_flag))
+ abs_remainder = abs_remainder_ts_decode(lc, rc, xc, yc);
+ if (n <= last_scan_pos_pass2) {
+ *abs_level = abs_level_pass2[n] + 2 * abs_remainder;
+ } else if (n <= last_scan_pos_pass1) {
+ *abs_level = *abs_level_pass1 + 2 * abs_remainder;
+ } else {
+ *abs_level = abs_remainder;
+ if (abs_remainder) {
+ //n > lastScanPosPass1
+ *coeff_sign_level = 1 - 2 * coeff_sign_flag_decode(lc);
+ }
+ }
+ if (!bdpcm_flag && n <= last_scan_pos_pass1) {
+ const int left = xc > 0 ? abs_level[-1] : 0;
+ const int above = yc > 0 ? abs_level[-tb->tb_width] : 0;
+ const int pred = FFMAX(left, above);
+
+ if (*abs_level == 1 && pred > 0)
+ *abs_level = pred;
+ else if (*abs_level > 0 && *abs_level <= pred)
+ (*abs_level)--;
+ }
+ if (*abs_level) {
+ tb->coeffs[off] = *coeff_sign_level * *abs_level;
+ tb->max_scan_x = FFMAX(xc, tb->max_scan_x);
+ tb->max_scan_y = FFMAX(yc, tb->max_scan_y);
+ tb->min_scan_x = FFMIN(xc, tb->min_scan_x);
+ tb->min_scan_y = FFMIN(yc, tb->min_scan_y);
+ } else {
+ tb->coeffs[off] = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int hls_residual_ts_coding(VVCLocalContext *lc, TransformBlock *tb)
+{
+ ResidualCoding rc;
+ tb->min_scan_x = tb->min_scan_y = INT_MAX;
+ init_residual_coding(lc, &rc, tb->log2_tb_width, tb->log2_tb_height, tb);
+ for (int i = 0; i <= rc.last_sub_block; i++) {
+ int ret = residual_ts_coding_subblock(lc, &rc, i);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline int residual_coding_subblock(VVCLocalContext *lc, ResidualCoding *rc, const int i)
+{
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ TransformBlock *tb = rc->tb;
+ int first_sig_scan_pos_sb, last_sig_scan_pos_sb;
+ int first_pos_mode0, first_pos_mode1;
+ int infer_sb_dc_sig_coeff_flag = 0;
+ int n, sig_hidden_flag, sum = 0;
+ int abs_level_gt2_flag[MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE];
+ const int start_qstate_sb = rc->qstate;
+ const int xs = rc->sb_scan_x_off[i];
+ const int ys = rc->sb_scan_y_off[i];
+ uint8_t *sb_coded_flag = rc->sb_coded_flag + ys * rc->width_in_sbs + xs;
+
+
+ av_assert0(rc->num_sb_coeff <= MAX_SUB_BLOCK_SIZE * MAX_SUB_BLOCK_SIZE);
+ if (i < rc->last_sub_block && i > 0) {
+ *sb_coded_flag = sb_coded_flag_decode(lc, sb_coded_flag, rc, xs, ys);
+ infer_sb_dc_sig_coeff_flag = 1;
+ } else {
+ *sb_coded_flag = 1;
+ }
+ if (*sb_coded_flag && (xs > 3 || ys > 3) && tb->c_idx == 0)
+ lc->parse.mts_zero_out_sig_coeff_flag = 0;
+
+ if (!*sb_coded_flag)
+ return 0;
+
+ first_sig_scan_pos_sb = rc->num_sb_coeff;
+ last_sig_scan_pos_sb = -1;
+ first_pos_mode0 = (i == rc->last_sub_block ? rc->last_scan_pos : rc->num_sb_coeff -1);
+ first_pos_mode1 = first_pos_mode0;
+ for (n = first_pos_mode0; n >= 0 && rc->rem_bins_pass1 >= 4; n--) {
+ const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n];
+ const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n];
+ const int last = (xc == rc->last_significant_coeff_x && yc == rc->last_significant_coeff_y);
+ int *abs_level_pass1 = rc->abs_level_pass1 + yc * tb->tb_width + xc;
+ int *sig_coeff_flag = rc->sig_coeff_flag + yc * tb->tb_width + xc;
+
+ if ((n > 0 || !infer_sb_dc_sig_coeff_flag ) && !last) {
+ *sig_coeff_flag = sig_coeff_flag_decode(lc, rc, xc, yc);
+ rc->rem_bins_pass1--;
+ if (*sig_coeff_flag)
+ infer_sb_dc_sig_coeff_flag = 0;
+ } else {
+ *sig_coeff_flag = last || (!rc->scan_x_off[n] && !rc ->scan_y_off[n] &&
+ infer_sb_dc_sig_coeff_flag);
+ }
+ *abs_level_pass1 = 0;
+ if (*sig_coeff_flag) {
+ int abs_level_gt1_flag, par_level_flag = 0;
+ const int inc = get_gtx_flag_inc(rc, xc, yc, last);
+ abs_level_gt1_flag = abs_level_gtx_flag_decode(lc, inc);
+ rc->rem_bins_pass1--;
+ if (abs_level_gt1_flag) {
+ par_level_flag = par_level_flag_decode(lc, inc);
+ abs_level_gt2_flag[n] = abs_level_gtx_flag_decode(lc, inc + 32);
+ rc->rem_bins_pass1 -= 2;
+ } else {
+ abs_level_gt2_flag[n] = 0;
+ }
+ if (last_sig_scan_pos_sb == -1)
+ last_sig_scan_pos_sb = n;
+ first_sig_scan_pos_sb = n;
+
+ *abs_level_pass1 =
+ 1 + par_level_flag + abs_level_gt1_flag + (abs_level_gt2_flag[n] << 1);
+ } else {
+ abs_level_gt2_flag[n] = 0;
+ }
+
+ if (rsh->sh_dep_quant_used_flag)
+ rc->qstate = qstate_translate_table[rc->qstate][*abs_level_pass1 & 1];
+
+ first_pos_mode1 = n - 1;
+ }
+ for (n = first_pos_mode0; n > first_pos_mode1; n--) {
+ const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n];
+ const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n];
+ const int *abs_level_pass1 = rc->abs_level_pass1 + yc * tb->tb_width + xc;
+ int *abs_level = rc->abs_level + yc * tb->tb_width + xc;
+
+ *abs_level = *abs_level_pass1;
+ if (abs_level_gt2_flag[n]) {
+ const int abs_remainder = abs_remainder_decode(lc, rc, xc, yc);
+ ep_update_hist(lc->ep, rc, abs_remainder, 2);
+ *abs_level += 2 * abs_remainder;
+ }
+ }
+ for (n = first_pos_mode1; n >= 0; n--) {
+ const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n];
+ const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n];
+ int *abs_level = rc->abs_level + yc * tb->tb_width + xc;
+
+ if (*sb_coded_flag) {
+ const int dec_abs_level = dec_abs_level_decode(lc, rc, xc, yc, abs_level);
+ ep_update_hist(lc->ep, rc, dec_abs_level, 0);
+ }
+ if (*abs_level > 0) {
+ if (last_sig_scan_pos_sb == -1)
+ last_sig_scan_pos_sb = n;
+ first_sig_scan_pos_sb = n;
+ }
+ if (rsh->sh_dep_quant_used_flag)
+ rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1];
+ }
+ sig_hidden_flag = rsh->sh_sign_data_hiding_used_flag &&
+ (last_sig_scan_pos_sb - first_sig_scan_pos_sb > 3 ? 1 : 0);
+
+ if (rsh->sh_dep_quant_used_flag)
+ rc->qstate = start_qstate_sb;
+ n = (i == rc->last_sub_block ? rc->last_scan_pos : rc->num_sb_coeff -1);
+ for (/* nothing */; n >= 0; n--) {
+ int trans_coeff_level;
+ const int xc = (xs << rc->log2_sb_w) + rc->scan_x_off[n];
+ const int yc = (ys << rc->log2_sb_h) + rc->scan_y_off[n];
+ const int off = yc * tb->tb_width + xc;
+ const int *abs_level = rc->abs_level + off;
+
+ if (*abs_level > 0) {
+ int sign = 1;
+ if (!sig_hidden_flag || (n != first_sig_scan_pos_sb))
+ sign = 1 - 2 * coeff_sign_flag_decode(lc);
+ if (rsh->sh_dep_quant_used_flag) {
+ trans_coeff_level = (2 * *abs_level - (rc->qstate > 1)) * sign;
+ } else {
+ trans_coeff_level = *abs_level * sign;
+ if (sig_hidden_flag) {
+ sum += *abs_level;
+ if (n == first_sig_scan_pos_sb && (sum % 2))
+ trans_coeff_level = -trans_coeff_level;
+ }
+ }
+ tb->coeffs[off] = trans_coeff_level;
+ tb->max_scan_x = FFMAX(xc, tb->max_scan_x);
+ tb->max_scan_y = FFMAX(yc, tb->max_scan_y);
+ }
+ if (rsh->sh_dep_quant_used_flag)
+ rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1];
+ }
+
+ return 0;
+}
+
+static void derive_last_scan_pos(ResidualCoding *rc,
+ const int log2_zo_tb_width, int log2_zo_tb_height)
+{
+ int xc, yc, xs, ys;
+ do {
+ if (rc->last_scan_pos == 0) {
+ rc->last_scan_pos = rc->num_sb_coeff;
+ rc->last_sub_block--;
+ }
+ rc->last_scan_pos--;
+ xs = rc->sb_scan_x_off[rc->last_sub_block];
+ ys = rc->sb_scan_y_off[rc->last_sub_block];
+ xc = (xs << rc->log2_sb_w) + rc->scan_x_off[rc->last_scan_pos];
+ yc = (ys << rc->log2_sb_h) + rc->scan_y_off[rc->last_scan_pos];
+ } while ((xc != rc->last_significant_coeff_x) || (yc != rc->last_significant_coeff_y));
+}
+
+static void last_significant_coeff_x_y_decode(ResidualCoding *rc, VVCLocalContext *lc,
+ const int log2_zo_tb_width, const int log2_zo_tb_height)
+{
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const TransformBlock *tb = rc->tb;
+ int last_significant_coeff_x, last_significant_coeff_y;
+
+ last_significant_coeff_x = last_significant_coeff_x_prefix_decode(lc,
+ tb->log2_tb_width, log2_zo_tb_width, tb->c_idx);
+
+ last_significant_coeff_y = last_significant_coeff_y_prefix_decode(lc,
+ tb->log2_tb_height, log2_zo_tb_height, tb->c_idx);
+
+ if (last_significant_coeff_x > 3) {
+ int suffix = last_sig_coeff_suffix_decode(lc, last_significant_coeff_x);
+ last_significant_coeff_x = (1 << ((last_significant_coeff_x >> 1) - 1)) *
+ (2 + (last_significant_coeff_x & 1)) + suffix;
+ }
+ if (last_significant_coeff_y > 3) {
+ int suffix = last_sig_coeff_suffix_decode(lc, last_significant_coeff_y);
+ last_significant_coeff_y = (1 << ((last_significant_coeff_y >> 1) - 1)) *
+ (2 + (last_significant_coeff_y & 1)) + suffix;
+ }
+ if (rsh->sh_reverse_last_sig_coeff_flag) {
+ last_significant_coeff_x = (1 << log2_zo_tb_width) - 1 - last_significant_coeff_x;
+ last_significant_coeff_y = (1 << log2_zo_tb_height) - 1 - last_significant_coeff_y;
+ }
+ rc->last_significant_coeff_x = last_significant_coeff_x;
+ rc->last_significant_coeff_y = last_significant_coeff_y;
+}
+
+static int hls_residual_coding(VVCLocalContext *lc, TransformBlock *tb)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const CodingUnit *cu = lc->cu;
+ const int log2_tb_width = tb->log2_tb_width;
+ const int log2_tb_height = tb->log2_tb_height;
+ const int c_idx = tb->c_idx;
+ int log2_zo_tb_width, log2_zo_tb_height;
+ ResidualCoding rc;
+
+ if (sps->r->sps_mts_enabled_flag && cu->sbt_flag && c_idx == 0 && log2_tb_width == 5 && log2_tb_height < 6)
+ log2_zo_tb_width = 4;
+ else
+ log2_zo_tb_width = FFMIN(log2_tb_width, 5 );
+
+ if (sps->r->sps_mts_enabled_flag && cu->sbt_flag && c_idx == 0 && log2_tb_width < 6 && log2_tb_height == 5 )
+ log2_zo_tb_height = 4;
+ else
+ log2_zo_tb_height = FFMIN(log2_tb_height, 5);
+
+ init_residual_coding(lc, &rc, log2_zo_tb_width, log2_zo_tb_height, tb);
+ last_significant_coeff_x_y_decode(&rc, lc, log2_zo_tb_width, log2_zo_tb_height);
+ derive_last_scan_pos(&rc, log2_zo_tb_width, log2_zo_tb_height);
+
+ if (rc.last_sub_block == 0 && log2_tb_width >= 2 && log2_tb_height >= 2 && !tb->ts && rc.last_scan_pos > 0)
+ lc->parse.lfnst_dc_only = 0;
+ if ((rc.last_sub_block > 0 && log2_tb_width >= 2 && log2_tb_height >= 2 ) ||
+ (rc.last_scan_pos > 7 && (log2_tb_width == 2 || log2_tb_width == 3 ) &&
+ log2_tb_width == log2_tb_height))
+ lc->parse.lfnst_zero_out_sig_coeff_flag = 0;
+ if ((rc.last_sub_block > 0 || rc.last_scan_pos > 0 ) && c_idx == 0)
+ lc->parse.mts_dc_only = 0;
+
+ memset(tb->coeffs, 0, tb->tb_width * tb->tb_height * sizeof(*tb->coeffs));
+ memset(rc.abs_level, 0, tb->tb_width * tb->tb_height * sizeof(rc.abs_level[0]));
+ memset(rc.sb_coded_flag, 0, rc.nb_sbs);
+ memset(rc.abs_level_pass1, 0, tb->tb_width * tb->tb_height * sizeof(rc.abs_level_pass1[0]));
+ memset(rc.sig_coeff_flag, 0, tb->tb_width * tb->tb_height * sizeof(rc.sig_coeff_flag[0]));
+
+ for (int i = rc.last_sub_block; i >= 0; i--) {
+ int ret = residual_coding_subblock(lc, &rc, i);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+int ff_vvc_residual_coding(VVCLocalContext *lc, TransformBlock *tb)
+{
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const int ts = !rsh->sh_ts_residual_coding_disabled_flag && tb->ts;
+
+ return ts ? hls_residual_ts_coding(lc, tb) : hls_residual_coding(lc, tb);
+}
+
+int ff_vvc_cu_coded_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(CU_CODED_FLAG);
+}
+
+int ff_vvc_sbt_flag(VVCLocalContext *lc)
+{
+ const int w = lc->cu->cb_width;
+ const int h = lc->cu->cb_height;
+ const int inc = w * h <= 256;
+ return GET_CABAC(CU_SBT_FLAG + inc);
+}
+
+int ff_vvc_sbt_quad_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(CU_SBT_QUAD_FLAG);
+}
+
+int ff_vvc_sbt_horizontal_flag(VVCLocalContext *lc)
+{
+ const int w = lc->cu->cb_width;
+ const int h = lc->cu->cb_height;
+ const int inc = (w == h) ? 0 : ((w < h) ? 1 : 2);
+ return GET_CABAC(CU_SBT_HORIZONTAL_FLAG + inc);
+}
+
+int ff_vvc_sbt_pos_flag(VVCLocalContext *lc)
+{
+ return GET_CABAC(CU_SBT_POS_FLAG);
+}
+
+int ff_vvc_lfnst_idx(VVCLocalContext *lc, const int inc)
+{
+ if (!GET_CABAC(LFNST_IDX + inc))
+ return 0;
+ if (!GET_CABAC(LFNST_IDX + 2))
+ return 1;
+ return 2;
+}
+
+int ff_vvc_mts_idx(VVCLocalContext *lc)
+{
+ int i;
+ for (i = 0; i < 4; i++) {
+ if (!GET_CABAC(MTS_IDX + i))
+ return i;
+ }
+ return i;
+}
+
+int ff_vvc_end_of_slice_flag_decode(VVCLocalContext *lc)
+{
+ return get_cabac_terminate(&lc->ep->cc);
+}
+
+int ff_vvc_end_of_tile_one_bit(VVCLocalContext *lc)
+{
+ return get_cabac_terminate(&lc->ep->cc);
+}
+
+int ff_vvc_end_of_subset_one_bit(VVCLocalContext *lc)
+{
+ return get_cabac_terminate(&lc->ep->cc);
+}
diff --git a/libavcodec/vvc/vvc_cabac.h b/libavcodec/vvc/vvc_cabac.h
new file mode 100644
index 0000000000..172ab272ff
--- /dev/null
+++ b/libavcodec/vvc/vvc_cabac.h
@@ -0,0 +1,126 @@
+/*
+ * VVC CABAC decoder
+ *
+ * Copyright (C) 2022 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVC_CABAC_H
+#define AVCODEC_VVC_VVC_CABAC_H
+
+#include "vvc_ctu.h"
+
+int ff_vvc_cabac_init(VVCLocalContext *lc, int ctu_idx, int rx, int ry);
+
+//sao
+int ff_vvc_sao_merge_flag_decode(VVCLocalContext *lc);
+int ff_vvc_sao_type_idx_decode(VVCLocalContext *lc);
+int ff_vvc_sao_band_position_decode(VVCLocalContext *lc);
+int ff_vvc_sao_offset_abs_decode(VVCLocalContext *lc);
+int ff_vvc_sao_offset_sign_decode(VVCLocalContext *lc);
+int ff_vvc_sao_eo_class_decode(VVCLocalContext *lc);
+
+//alf
+int ff_vvc_alf_ctb_flag(VVCLocalContext *lc, int rx, int ry, int c_idx);
+int ff_vvc_alf_use_aps_flag(VVCLocalContext *lc);
+int ff_vvc_alf_luma_prev_filter_idx(VVCLocalContext *lc);
+int ff_vvc_alf_luma_fixed_filter_idx(VVCLocalContext *lc);
+int ff_vvc_alf_ctb_filter_alt_idx(VVCLocalContext *lc, int c_idx, int num_chroma_filters);
+int ff_vvc_alf_ctb_cc_idc(VVCLocalContext *lc, int rx, int ry, int idx, int cc_filters_signalled);
+
+//coding_tree
+int ff_vvc_split_cu_flag(VVCLocalContext* lc, int x0, int y0, int cb_width, int cb_height,
+ int ch_type, const VVCAllowedSplit *a);
+VVCSplitMode ff_vvc_split_mode(VVCLocalContext *lc, int x0, int y0, int cb_width, int cb_height,
+ int cqt_depth, int mtt_depth, int ch_type, const VVCAllowedSplit *a);
+int ff_vvc_non_inter_flag(VVCLocalContext *lc, int x0, int y0, int ch_type);
+
+//coding unit
+int ff_vvc_pred_mode_flag(VVCLocalContext *lc, int is_chroma);
+int ff_vvc_pred_mode_plt_flag(VVCLocalContext *lc);
+int ff_vvc_intra_bdpcm_luma_flag(VVCLocalContext *lc);
+int ff_vvc_intra_bdpcm_luma_dir_flag(VVCLocalContext *lc);
+int ff_vvc_intra_bdpcm_chroma_flag(VVCLocalContext *lc);
+int ff_vvc_intra_bdpcm_chroma_dir_flag(VVCLocalContext *lc);
+int ff_vvc_cu_skip_flag(VVCLocalContext *lc, const uint8_t *cu_skip_flag);
+int ff_vvc_pred_mode_ibc_flag(VVCLocalContext *lc, int ch_type);
+int ff_vvc_cu_coded_flag(VVCLocalContext *lc);
+int ff_vvc_cu_qp_delta_abs(VVCLocalContext *lc);
+int ff_vvc_cu_qp_delta_sign_flag(VVCLocalContext *lc);
+int ff_vvc_sbt_flag(VVCLocalContext *lc);
+int ff_vvc_sbt_quad_flag(VVCLocalContext *lc);
+int ff_vvc_sbt_horizontal_flag(VVCLocalContext *lc);
+int ff_vvc_sbt_pos_flag(VVCLocalContext *lc);
+
+//intra
+int ff_vvc_intra_mip_flag(VVCLocalContext *lc, const uint8_t *intra_mip_flag);
+int ff_vvc_intra_mip_transposed_flag(VVCLocalContext *lc);
+int ff_vvc_intra_mip_mode(VVCLocalContext *lc);
+int ff_vvc_intra_luma_ref_idx(VVCLocalContext *lc);
+int ff_vvc_intra_subpartitions_mode_flag(VVCLocalContext *lc);
+enum IspType ff_vvc_isp_split_type(VVCLocalContext *lc, int intra_subpartitions_mode_flag);
+int ff_vvc_intra_luma_mpm_flag(VVCLocalContext *lc);
+int ff_vvc_intra_luma_not_planar_flag(VVCLocalContext *lc, int intra_subpartitions_mode_flag);
+int ff_vvc_intra_luma_mpm_idx(VVCLocalContext *lc);
+int ff_vvc_intra_luma_mpm_remainder(VVCLocalContext *lc);
+int ff_vvc_cclm_mode_flag(VVCLocalContext *lc);
+int ff_vvc_cclm_mode_idx(VVCLocalContext *lc);
+int ff_vvc_intra_chroma_pred_mode(VVCLocalContext *lc);
+
+//inter
+int ff_vvc_general_merge_flag(VVCLocalContext *lc);
+int ff_vvc_merge_subblock_flag(VVCLocalContext *lc);
+int ff_vvc_merge_subblock_idx(VVCLocalContext *lc, int max_num_subblock_merge_cand);
+int ff_vvc_regular_merge_flag(VVCLocalContext *lc, int cu_skip_flag);
+int ff_vvc_merge_idx(VVCLocalContext *lc);
+int ff_vvc_mmvd_merge_flag(VVCLocalContext *lc);
+int ff_vvc_mmvd_cand_flag(VVCLocalContext *lc);
+void ff_vvc_mmvd_offset_coding(VVCLocalContext *lc, Mv *mvd_offset, int ph_mmvd_fullpel_only_flag);
+int ff_vvc_ciip_flag(VVCLocalContext *lc);
+int ff_vvc_merge_gpm_partition_idx(VVCLocalContext *lc);
+int ff_vvc_merge_gpm_idx(VVCLocalContext *lc, int idx);
+PredFlag ff_vvc_pred_flag(VVCLocalContext *lc, int is_b);
+int ff_vvc_inter_affine_flag(VVCLocalContext *lc);
+int ff_vvc_cu_affine_type_flag(VVCLocalContext *lc);
+int ff_vvc_sym_mvd_flag(VVCLocalContext *lc);
+int ff_vvc_ref_idx_lx(VVCLocalContext *lc, uint8_t nb_refs);
+int ff_vvc_abs_mvd_greater0_flag(VVCLocalContext *lc);
+int ff_vvc_abs_mvd_greater1_flag(VVCLocalContext *lc);
+int ff_vvc_abs_mvd_minus2(VVCLocalContext *lc);
+int ff_vvc_mvd_sign_flag(VVCLocalContext *lc);
+int ff_vvc_mvp_lx_flag(VVCLocalContext *lc);
+int ff_vvc_amvr_shift(VVCLocalContext *lc, int inter_affine_flag, PredMode pred_mode, int has_amvr_flag);
+int ff_vvc_bcw_idx(VVCLocalContext *lc, int no_backward_pred_flag);
+
+//transform
+int ff_vvc_tu_cb_coded_flag(VVCLocalContext *lc);
+int ff_vvc_tu_cr_coded_flag(VVCLocalContext *lc, int tu_cb_coded_flag);
+int ff_vvc_tu_y_coded_flag(VVCLocalContext *lc);
+int ff_vvc_cu_chroma_qp_offset_flag(VVCLocalContext *lc);
+int ff_vvc_cu_chroma_qp_offset_idx(VVCLocalContext *lc);
+int ff_vvc_tu_joint_cbcr_residual_flag(VVCLocalContext *lc, int tu_cb_coded_flag, int tu_cr_coded_flag);
+int ff_vvc_transform_skip_flag(VVCLocalContext *lc, int ctx);
+int ff_vvc_residual_coding(VVCLocalContext *lc, TransformBlock *tb);
+int ff_vvc_lfnst_idx(VVCLocalContext *lc, int inc);
+int ff_vvc_mts_idx(VVCLocalContext *lc);
+
+int ff_vvc_end_of_slice_flag_decode(VVCLocalContext *lc);
+int ff_vvc_end_of_tile_one_bit(VVCLocalContext *lc);
+int ff_vvc_end_of_subset_one_bit(VVCLocalContext *lc);
+
+#endif //AVCODEC_VVC_VVC_CABAC_H
diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c
new file mode 100644
index 0000000000..78b13ffb00
--- /dev/null
+++ b/libavcodec/vvc/vvc_ctu.c
@@ -0,0 +1,32 @@
+/*
+ * VVC CTU(Coding Tree Unit) parser
+ *
+ * Copyright (C) 2022 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "vvc_ctu.h"
+
+void ff_vvc_ep_init_stat_coeff(EntryPoint *ep,
+ const int bit_depth, const int persistent_rice_adaptation_enabled_flag)
+{
+ for (size_t i = 0; i < FF_ARRAY_ELEMS(ep->stat_coeff); ++i) {
+ ep->stat_coeff[i] =
+ persistent_rice_adaptation_enabled_flag ? 2 * (av_log2(bit_depth - 10)) : 0;
+ }
+}
diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h
new file mode 100644
index 0000000000..e2b82f538e
--- /dev/null
+++ b/libavcodec/vvc/vvc_ctu.h
@@ -0,0 +1,463 @@
+/*
+ * VVC CTU(Coding Tree Unit) parser
+ *
+ * Copyright (C) 2022 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVC_CTU_H
+#define AVCODEC_VVC_VVC_CTU_H
+
+#include "libavcodec/cabac.h"
+#include "libavutil/mem_internal.h"
+
+#include "vvcdec.h"
+
+#define MAX_CTU_SIZE 128
+
+#define MAX_CU_SIZE MAX_CTU_SIZE
+#define MIN_CU_SIZE 4
+#define MIN_CU_LOG2 2
+#define MAX_CU_DEPTH 7
+
+#define MAX_PARTS_IN_CTU ((MAX_CTU_SIZE >> MIN_CU_LOG2) * (MAX_CTU_SIZE >> MIN_CU_LOG2))
+
+#define MIN_PU_SIZE 4
+
+#define MAX_TB_SIZE 64
+#define MIN_TU_SIZE 4
+#define MAX_TUS_IN_CU 64
+
+#define MAX_QP 63
+
+#define MAX_PB_SIZE 128
+#define EDGE_EMU_BUFFER_STRIDE (MAX_PB_SIZE + 32)
+
+#define CHROMA_EXTRA_BEFORE 1
+#define CHROMA_EXTRA_AFTER 2
+#define CHROMA_EXTRA 3
+#define LUMA_EXTRA_BEFORE 3
+#define LUMA_EXTRA_AFTER 4
+#define LUMA_EXTRA 7
+#define BILINEAR_EXTRA_BEFORE 0
+#define BILINEAR_EXTRA_AFTER 1
+#define BILINEAR_EXTRA 1
+
+#define MAX_CONTROL_POINTS 3
+
+#define AFFINE_MIN_BLOCK_SIZE 4
+
+#define MRG_MAX_NUM_CANDS 6
+#define MAX_NUM_HMVP_CANDS 5
+
+#define SAO_PADDING_SIZE 1
+
+#define ALF_PADDING_SIZE 8
+#define ALF_BLOCK_SIZE 4
+
+#define ALF_BORDER_LUMA 3
+#define ALF_BORDER_CHROMA 2
+
+#define ALF_VB_POS_ABOVE_LUMA 4
+#define ALF_VB_POS_ABOVE_CHROMA 2
+
+#define ALF_GRADIENT_STEP 2
+#define ALF_GRADIENT_BORDER 2
+#define ALF_GRADIENT_SIZE ((MAX_CU_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP)
+#define ALF_NUM_DIR 4
+
+
+/**
+ * Value of the luma sample at position (x, y) in the 2D array tab.
+ */
+#define SAMPLE(tab, x, y) ((tab)[(y) * s->sps->width + (x)])
+#define SAMPLE_CTB(tab, x, y) ((tab)[(y) * min_cb_width + (x)])
+#define CTB(tab, x, y) ((tab)[(y) * fc->ps.pps->ctb_width + (x)])
+
+enum SAOType {
+ SAO_NOT_APPLIED = 0,
+ SAO_BAND,
+ SAO_EDGE,
+};
+
+enum SAOEOClass {
+ SAO_EO_HORIZ = 0,
+ SAO_EO_VERT,
+ SAO_EO_135D,
+ SAO_EO_45D,
+};
+
+typedef struct NeighbourAvailable {
+ int cand_left;
+ int cand_up;
+ int cand_up_left;
+ int cand_up_right;
+ int cand_up_right_sap;
+} NeighbourAvailable;
+
+enum IspType{
+ ISP_NO_SPLIT,
+ ISP_HOR_SPLIT,
+ ISP_VER_SPLIT,
+};
+
+typedef enum VVCSplitMode {
+ SPLIT_NONE,
+ SPLIT_TT_HOR,
+ SPLIT_BT_HOR,
+ SPLIT_TT_VER,
+ SPLIT_BT_VER,
+ SPLIT_QT,
+} VVCSplitMode;
+
+typedef enum MtsIdx {
+ MTS_DCT2_DCT2,
+ MTS_DST7_DST7,
+ MTS_DST7_DCT8,
+ MTS_DCT8_DST7,
+ MTS_DCT8_DCT8,
+} MtsIdx;
+
+typedef struct TransformBlock {
+ uint8_t has_coeffs;
+ uint8_t c_idx;
+ uint8_t ts; ///< transform_skip_flag
+ int x0;
+ int y0;
+
+ int tb_width;
+ int tb_height;
+ int log2_tb_width;
+ int log2_tb_height;
+
+ int max_scan_x;
+ int max_scan_y;
+ int min_scan_x;
+ int min_scan_y;
+
+ int qp;
+ int rect_non_ts_flag;
+ int bd_shift;
+ int bd_offset;
+
+ int *coeffs;
+} TransformBlock;
+
+typedef enum VVCTreeType {
+ SINGLE_TREE,
+ DUAL_TREE_LUMA,
+ DUAL_TREE_CHROMA,
+} VVCTreeType;
+
+typedef struct TransformUnit {
+ int x0;
+ int y0;
+ int width;
+ int height;
+
+ uint8_t joint_cbcr_residual_flag; ///< tu_joint_cbcr_residual_flag
+
+ uint8_t coded_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< tu_y_coded_flag, tu_cb_coded_flag, tu_cr_coded_flag
+ uint8_t nb_tbs;
+ TransformBlock tbs[VVC_MAX_SAMPLE_ARRAYS];
+
+ struct TransformUnit *next; ///< RefStruct reference
+} TransformUnit;
+
+typedef enum PredMode {
+ MODE_INTER,
+ MODE_INTRA,
+ MODE_SKIP,
+ MODE_PLT,
+ MODE_IBC,
+} PredMode;
+
+typedef struct Mv {
+ int x; ///< horizontal component of motion vector
+ int y; ///< vertical component of motion vector
+} Mv;
+
+typedef struct MvField {
+ DECLARE_ALIGNED(4, Mv, mv)[2]; ///< mvL0, vvL1
+ int8_t ref_idx[2]; ///< refIdxL0, refIdxL1
+ uint8_t hpel_if_idx; ///< hpelIfIdx
+ uint8_t bcw_idx; ///< bcwIdx
+ uint8_t pred_flag;
+ uint8_t ciip_flag; ///< ciip_flag
+} MvField;
+
+typedef struct DMVRInfo {
+ DECLARE_ALIGNED(4, Mv, mv)[2]; ///< mvL0, vvL1
+ uint8_t dmvr_enabled;
+} DMVRInfo;
+
+typedef enum MotionModelIdc {
+ MOTION_TRANSLATION,
+ MOTION_4_PARAMS_AFFINE,
+ MOTION_6_PARAMS_AFFINE,
+} MotionModelIdc;
+
+typedef enum PredFlag {
+ PF_INTRA = 0x0,
+ PF_L0 = 0x1,
+ PF_L1 = 0x2,
+ PF_BI = 0x3,
+} PredFlag;
+
+typedef enum IntraPredMode {
+ INTRA_INVALID = -1,
+ INTRA_PLANAR = 0,
+ INTRA_DC,
+ INTRA_HORZ = 18,
+ INTRA_DIAG = 34,
+ INTRA_VERT = 50,
+ INTRA_VDIAG = 66,
+ INTRA_LT_CCLM = 81,
+ INTRA_L_CCLM,
+ INTRA_T_CCLM
+} IntraPredMode;
+
+typedef struct MotionInfo {
+ MotionModelIdc motion_model_idc; ///< MotionModelIdc
+ int8_t ref_idx[2]; ///< refIdxL0, refIdxL1
+ uint8_t hpel_if_idx; ///< hpelIfIdx
+ uint8_t bcw_idx; ///< bcwIdx
+ PredFlag pred_flag;
+
+ Mv mv[2][MAX_CONTROL_POINTS];
+
+ int num_sb_x, num_sb_y;
+} MotionInfo;
+
+typedef struct PredictionUnit {
+ uint8_t general_merge_flag;
+ uint8_t mmvd_merge_flag;
+ //InterPredIdc inter_pred_idc;
+ uint8_t inter_affine_flag;
+
+ //subblock predict
+ uint8_t merge_subblock_flag;
+
+ uint8_t merge_gpm_flag;
+ uint8_t gpm_partition_idx;
+ MvField gpm_mv[2];
+
+ int sym_mvd_flag;
+
+ MotionInfo mi;
+
+ // for regular prediction only
+ uint8_t dmvr_flag;
+ uint8_t bdof_flag;
+
+ int16_t diff_mv_x[2][AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; ///< diffMvLX
+ int16_t diff_mv_y[2][AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE]; ///< diffMvLX
+ int cb_prof_flag[2];
+} PredictionUnit;
+
+typedef struct CodingUnit {
+ VVCTreeType tree_type;
+ int x0;
+ int y0;
+ int cb_width;
+ int cb_height;
+ int ch_type;
+ int cqt_depth;
+
+ uint8_t coded_flag;
+
+ uint8_t sbt_flag;
+ uint8_t sbt_horizontal_flag;
+ uint8_t sbt_pos_flag;
+
+ int lfnst_idx;
+ MtsIdx mts_idx;
+
+ uint8_t act_enabled_flag;
+
+ uint8_t intra_luma_ref_idx; ///< IntraLumaRefLineIdx[][]
+ uint8_t intra_mip_flag; ///< intra_mip_flag
+ uint8_t skip_flag; ///< cu_skip_flag;
+
+ //inter
+ uint8_t ciip_flag;
+
+ // Inferred parameters
+ enum IspType isp_split_type; ///< IntraSubPartitionsSplitType
+
+ enum PredMode pred_mode; ///< PredMode
+
+ int num_intra_subpartitions;
+
+ IntraPredMode intra_pred_mode_y; ///< IntraPredModeY
+ IntraPredMode intra_pred_mode_c; ///< IntraPredModeC
+ int mip_chroma_direct_flag; ///< MipChromaDirectFlag
+
+ int bdpcm_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< BdpcmFlag
+
+ int apply_lfnst_flag[VVC_MAX_SAMPLE_ARRAYS]; ///< ApplyLfnstFlag[]
+
+ struct {
+ TransformUnit *head; ///< RefStruct reference
+ TransformUnit *tail; ///< RefStruct reference
+ } tus;
+
+ int8_t qp[4]; ///< QpY, Qp′Cb, Qp′Cr, Qp′CbCr
+
+ PredictionUnit pu;
+
+ struct CodingUnit *next; ///< RefStruct reference
+} CodingUnit;
+
+typedef struct CTU {
+ CodingUnit *cus;
+ int max_y[2][VVC_MAX_REF_ENTRIES];
+ int max_y_idx[2];
+} CTU;
+
+typedef struct ReconstructedArea {
+ int x;
+ int y;
+ int w;
+ int h;
+} ReconstructedArea;
+
+typedef struct VVCCabacState {
+ uint16_t state[2];
+ uint8_t shift[2];
+} VVCCabacState;
+
+// VVC_CONTEXTS matched with SYNTAX_ELEMENT_LAST, it's checked by cabac_init_state.
+#define VVC_CONTEXTS 378
+typedef struct EntryPoint {
+ int8_t qp_y; ///< QpY
+
+ int stat_coeff[VVC_MAX_SAMPLE_ARRAYS]; ///< StatCoeff
+
+ VVCCabacState cabac_state[VVC_CONTEXTS];
+ CABACContext cc;
+
+ int ctu_start;
+ int ctu_end;
+
+ uint8_t is_first_qg; // first quantization group
+ MvField hmvp[MAX_NUM_HMVP_CANDS]; ///< HmvpCandList
+ int num_hmvp; ///< NumHmvpCand
+} EntryPoint;
+
+typedef struct VVCLocalContext {
+ uint8_t ctb_left_flag;
+ uint8_t ctb_up_flag;
+ uint8_t ctb_up_right_flag;
+ uint8_t ctb_up_left_flag;
+ int end_of_tiles_x;
+ int end_of_tiles_y;
+
+ /* +7 is for subpixel interpolation, *2 for high bit depths */
+ DECLARE_ALIGNED(32, uint8_t, edge_emu_buffer)[(MAX_PB_SIZE + 7) * EDGE_EMU_BUFFER_STRIDE * 2];
+ /* The extended size between the new edge emu buffer is abused by SAO */
+ DECLARE_ALIGNED(32, uint8_t, edge_emu_buffer2)[(MAX_PB_SIZE + 7) * EDGE_EMU_BUFFER_STRIDE * 2];
+ DECLARE_ALIGNED(32, int16_t, tmp)[MAX_PB_SIZE * MAX_PB_SIZE];
+ DECLARE_ALIGNED(32, int16_t, tmp1)[MAX_PB_SIZE * MAX_PB_SIZE];
+ DECLARE_ALIGNED(32, int16_t, tmp2)[MAX_PB_SIZE * MAX_PB_SIZE];
+ DECLARE_ALIGNED(32, uint8_t, ciip_tmp1)[MAX_PB_SIZE * MAX_PB_SIZE * 2];
+ DECLARE_ALIGNED(32, uint8_t, ciip_tmp2)[MAX_PB_SIZE * MAX_PB_SIZE * 2];
+ DECLARE_ALIGNED(32, uint8_t, sao_buffer)[(MAX_CTU_SIZE + 2 * SAO_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2];
+ DECLARE_ALIGNED(32, uint8_t, alf_buffer_luma)[(MAX_CTU_SIZE + 2 * ALF_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2];
+ DECLARE_ALIGNED(32, uint8_t, alf_buffer_chroma)[(MAX_CTU_SIZE + 2 * ALF_PADDING_SIZE) * EDGE_EMU_BUFFER_STRIDE * 2];
+ DECLARE_ALIGNED(32, int32_t, alf_gradient_tmp)[ALF_GRADIENT_SIZE * ALF_GRADIENT_SIZE * ALF_NUM_DIR];
+
+ struct {
+ int sbt_num_fourths_tb0; ///< SbtNumFourthsTb0
+
+ uint8_t is_cu_qp_delta_coded; ///< IsCuQpDeltaCoded
+ int cu_qg_top_left_x; ///< CuQgTopLeftX
+ int cu_qg_top_left_y; ///< CuQgTopLeftY
+ int is_cu_chroma_qp_offset_coded; ///< IsCuChromaQpOffsetCoded
+ int chroma_qp_offset[3]; ///< CuQpOffsetCb, CuQpOffsetCr, CuQpOffsetCbCr
+
+ int infer_tu_cbf_luma; ///< InferTuCbfLuma
+ int prev_tu_cbf_y; ///< prevTuCbfY;
+
+ int lfnst_dc_only; ///< LfnstDcOnly
+ int lfnst_zero_out_sig_coeff_flag; ///< LfnstZeroOutSigCoeffFlag
+
+ int mts_dc_only; ///< MtsDcOnly
+ int mts_zero_out_sig_coeff_flag; ///< MtsZeroOutSigCoeffFlag;
+ } parse;
+
+ struct {
+ // lmcs cache, for recon only
+ int chroma_scale;
+ int x_vpdu;
+ int y_vpdu;
+ } lmcs;
+
+ CodingUnit *cu;
+ ReconstructedArea ras[2][MAX_PARTS_IN_CTU];
+ int num_ras[2];
+
+ NeighbourAvailable na;
+
+#define BOUNDARY_LEFT_SLICE (1 << 0)
+#define BOUNDARY_LEFT_TILE (1 << 1)
+#define BOUNDARY_UPPER_SLICE (1 << 2)
+#define BOUNDARY_UPPER_TILE (1 << 3)
+ /* properties of the boundary of the current CTB for the purposes
+ * of the deblocking filter */
+ int boundary_flags;
+
+ SliceContext *sc;
+ VVCFrameContext *fc;
+ EntryPoint *ep;
+ int *coeffs;
+} VVCLocalContext;
+
+typedef struct VVCAllowedSplit {
+ int qt;
+ int btv;
+ int bth;
+ int ttv;
+ int tth;
+} VVCAllowedSplit;
+
+typedef struct SAOParams {
+ int offset_abs[3][4]; ///< sao_offset_abs
+ int offset_sign[3][4]; ///< sao_offset_sign
+
+ uint8_t band_position[3]; ///< sao_band_position
+
+ int eo_class[3]; ///< sao_eo_class
+
+ int16_t offset_val[3][5]; ///<SaoOffsetVal
+
+ uint8_t type_idx[3]; ///< sao_type_idx
+} SAOParams;
+
+typedef struct ALFParams {
+ uint8_t ctb_flag[3]; ///< alf_ctb_flag[]
+ uint8_t ctb_filt_set_idx_y; ///< AlfCtbFiltSetIdxY
+ uint8_t alf_ctb_filter_alt_idx[2]; ///< alf_ctb_filter_alt_idx[]
+ uint8_t ctb_cc_idc[2]; ///< alf_ctb_cc_cb_idc, alf_ctb_cc_cr_idc
+
+ uint8_t applied[3];
+} ALFParams;
+
+void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, int bit_depth, int persistent_rice_adaptation_enabled_flag);
+
+#endif // AVCODEC_VVC_VVC_CTU_H
diff --git a/libavcodec/vvc/vvcdec.h b/libavcodec/vvc/vvcdec.h
index b1757b00b5..1ef71a4832 100644
--- a/libavcodec/vvc/vvcdec.h
+++ b/libavcodec/vvc/vvcdec.h
@@ -80,6 +80,7 @@ typedef struct VVCFrame {
typedef struct SliceContext {
int slice_idx;
+ VVCSH sh;
struct EntryPoint *eps;
int nb_eps;
RefPicList *rpl;
@@ -95,6 +96,8 @@ typedef struct VVCFrameContext {
struct AVFrame *frame;
struct AVFrame *output_frame;
+ VVCFrameParamSets ps;
+
SliceContext **slices;
int nb_slices;
int nb_slices_allocated;
@@ -114,6 +117,10 @@ typedef struct VVCFrameContext {
struct {
int16_t *slice_idx;
+ DBParams *deblock;
+ struct SAOParams *sao;
+ struct ALFParams *alf;
+
int *cb_pos_x[2]; ///< CbPosX[][][]
int *cb_pos_y[2]; ///< CbPosY[][][]
uint8_t *cb_width[2]; ///< CbWidth[][][]
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH v7 05/14] vvcdec: add reference management
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
` (2 preceding siblings ...)
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 04/14] vvcdec: add cabac decoder Nuo Mi
@ 2023-12-10 15:57 ` Nuo Mi
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 06/14] vvcdec: add motion vector decoder Nuo Mi
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 15:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
---
libavcodec/vvc/Makefile | 1 +
libavcodec/vvc/vvc_refs.c | 560 ++++++++++++++++++++++++++++++++++++++
libavcodec/vvc/vvc_refs.h | 57 ++++
3 files changed, 618 insertions(+)
create mode 100644 libavcodec/vvc/vvc_refs.c
create mode 100644 libavcodec/vvc/vvc_refs.h
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index 6c03ba19ac..1d89985d13 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -6,3 +6,4 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
vvc/vvc_ctu.o \
vvc/vvc_data.o \
vvc/vvc_ps.o \
+ vvc/vvc_refs.o \
diff --git a/libavcodec/vvc/vvc_refs.c b/libavcodec/vvc/vvc_refs.c
new file mode 100644
index 0000000000..9148e90e8d
--- /dev/null
+++ b/libavcodec/vvc/vvc_refs.c
@@ -0,0 +1,560 @@
+/*
+ * VVC reference management
+ *
+ * Copyright (C) 2023 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdatomic.h>
+
+#include "libavutil/thread.h"
+#include "libavcodec/refstruct.h"
+#include "libavcodec/thread.h"
+
+#include "vvc_refs.h"
+
+#define VVC_FRAME_FLAG_OUTPUT (1 << 0)
+#define VVC_FRAME_FLAG_SHORT_REF (1 << 1)
+#define VVC_FRAME_FLAG_LONG_REF (1 << 2)
+#define VVC_FRAME_FLAG_BUMPING (1 << 3)
+
+typedef struct FrameProgress {
+ atomic_int progress[VVC_PROGRESS_LAST];
+ VVCProgressListener *listener[VVC_PROGRESS_LAST];
+ AVMutex lock;
+ AVCond cond;
+ uint8_t has_lock;
+ uint8_t has_cond;
+} FrameProgress;
+
+void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags)
+{
+ /* frame->frame can be NULL if context init failed */
+ if (!frame->frame || !frame->frame->buf[0])
+ return;
+
+ frame->flags &= ~flags;
+ if (!frame->flags) {
+ av_frame_unref(frame->frame);
+ ff_refstruct_unref(&frame->progress);
+
+ ff_refstruct_unref(&frame->tab_dmvr_mvf);
+
+ ff_refstruct_unref(&frame->rpl);
+ frame->nb_rpl_elems = 0;
+ ff_refstruct_unref(&frame->rpl_tab);
+
+ frame->collocated_ref = NULL;
+ }
+}
+
+const RefPicList *ff_vvc_get_ref_list(const VVCFrameContext *fc, const VVCFrame *ref, int x0, int y0)
+{
+ int x_cb = x0 >> fc->ps.sps->ctb_log2_size_y;
+ int y_cb = y0 >> fc->ps.sps->ctb_log2_size_y;
+ int pic_width_cb = fc->ps.pps->ctb_width;
+ int ctb_addr_rs = y_cb * pic_width_cb + x_cb;
+
+ return (const RefPicList *)ref->rpl_tab[ctb_addr_rs];
+}
+
+void ff_vvc_clear_refs(VVCFrameContext *fc)
+{
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++)
+ ff_vvc_unref_frame(fc, &fc->DPB[i],
+ VVC_FRAME_FLAG_SHORT_REF | VVC_FRAME_FLAG_LONG_REF);
+}
+
+static void free_progress(FFRefStructOpaque unused, void *obj)
+{
+ FrameProgress *p = (FrameProgress *)obj;
+
+ if (p->has_cond)
+ ff_cond_destroy(&p->cond);
+ if (p->has_lock)
+ ff_mutex_destroy(&p->lock);
+}
+
+static FrameProgress *alloc_progress(void)
+{
+ FrameProgress *p = ff_refstruct_alloc_ext(sizeof(*p), 0, NULL, free_progress);
+
+ if (p) {
+ p->has_lock = !ff_mutex_init(&p->lock, NULL);
+ p->has_cond = !ff_cond_init(&p->cond, NULL);
+ if (!p->has_lock || !p->has_cond)
+ ff_refstruct_unref(&p);
+ }
+ return p;
+}
+
+static VVCFrame *alloc_frame(VVCContext *s, VVCFrameContext *fc)
+{
+ const VVCPPS *pps = fc->ps.pps;
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+ int ret;
+ VVCFrame *frame = &fc->DPB[i];
+ if (frame->frame->buf[0])
+ continue;
+
+ ret = ff_thread_get_buffer(s->avctx, frame->frame, AV_GET_BUFFER_FLAG_REF);
+ if (ret < 0)
+ return NULL;
+
+ frame->rpl = ff_refstruct_allocz(s->current_frame.nb_units * sizeof(RefPicListTab));
+ if (!frame->rpl)
+ goto fail;
+ frame->nb_rpl_elems = s->current_frame.nb_units;
+
+ frame->tab_dmvr_mvf = ff_refstruct_pool_get(fc->tab_dmvr_mvf_pool);
+ if (!frame->tab_dmvr_mvf)
+ goto fail;
+
+ frame->rpl_tab = ff_refstruct_pool_get(fc->rpl_tab_pool);
+ if (!frame->rpl_tab)
+ goto fail;
+ frame->ctb_count = pps->ctb_width * pps->ctb_height;
+ for (int j = 0; j < frame->ctb_count; j++)
+ frame->rpl_tab[j] = frame->rpl;
+
+ frame->progress = alloc_progress();
+ if (!frame->progress)
+ goto fail;
+
+ return frame;
+fail:
+ ff_vvc_unref_frame(fc, frame, ~0);
+ return NULL;
+ }
+ av_log(s->avctx, AV_LOG_ERROR, "Error allocating frame, DPB full.\n");
+ return NULL;
+}
+
+int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, AVFrame **frame)
+{
+ const VVCPH *ph= &fc->ps.ph;
+ const int poc = ph->poc;
+ VVCFrame *ref;
+ int i;
+
+ /* check that this POC doesn't already exist */
+ for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+ VVCFrame *frame = &fc->DPB[i];
+
+ if (frame->frame->buf[0] && frame->sequence == s->seq_decode &&
+ frame->poc == poc) {
+ av_log(s->avctx, AV_LOG_ERROR, "Duplicate POC in a sequence: %d.\n", poc);
+ return AVERROR_INVALIDDATA;
+ }
+ }
+
+ ref = alloc_frame(s, fc);
+ if (!ref)
+ return AVERROR(ENOMEM);
+
+ *frame = ref->frame;
+ fc->ref = ref;
+
+ if (s->no_output_before_recovery_flag && (IS_RASL(s) || !GDR_IS_RECOVERED(s)))
+ ref->flags = 0;
+ else if (ph->r->ph_pic_output_flag)
+ ref->flags = VVC_FRAME_FLAG_OUTPUT;
+
+ if (!ph->r->ph_non_ref_pic_flag)
+ ref->flags |= VVC_FRAME_FLAG_SHORT_REF;
+
+ ref->poc = poc;
+ ref->sequence = s->seq_decode;
+ ref->frame->crop_left = fc->ps.pps->r->pps_conf_win_left_offset;
+ ref->frame->crop_right = fc->ps.pps->r->pps_conf_win_right_offset;
+ ref->frame->crop_top = fc->ps.pps->r->pps_conf_win_top_offset;
+ ref->frame->crop_bottom = fc->ps.pps->r->pps_conf_win_bottom_offset;
+
+ return 0;
+}
+
+int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *out, const int no_output_of_prior_pics_flag, int flush)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ do {
+ int nb_output = 0;
+ int min_poc = INT_MAX;
+ int min_idx, ret;
+
+ if (no_output_of_prior_pics_flag) {
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+ VVCFrame *frame = &fc->DPB[i];
+ if (!(frame->flags & VVC_FRAME_FLAG_BUMPING) && frame->poc != fc->ps.ph.poc &&
+ frame->sequence == s->seq_output) {
+ ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT);
+ }
+ }
+ }
+
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+ VVCFrame *frame = &fc->DPB[i];
+ if ((frame->flags & VVC_FRAME_FLAG_OUTPUT) &&
+ frame->sequence == s->seq_output) {
+ nb_output++;
+ if (frame->poc < min_poc || nb_output == 1) {
+ min_poc = frame->poc;
+ min_idx = i;
+ }
+ }
+ }
+
+ /* wait for more frames before output */
+ if (!flush && s->seq_output == s->seq_decode && sps &&
+ nb_output <= sps->r->sps_dpb_params.dpb_max_dec_pic_buffering_minus1[sps->r->sps_max_sublayers_minus1] + 1)
+ return 0;
+
+ if (nb_output) {
+ VVCFrame *frame = &fc->DPB[min_idx];
+
+ ret = av_frame_ref(out, frame->frame);
+ if (frame->flags & VVC_FRAME_FLAG_BUMPING)
+ ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT | VVC_FRAME_FLAG_BUMPING);
+ else
+ ff_vvc_unref_frame(fc, frame, VVC_FRAME_FLAG_OUTPUT);
+ if (ret < 0)
+ return ret;
+
+ av_log(s->avctx, AV_LOG_DEBUG,
+ "Output frame with POC %d.\n", frame->poc);
+ return 1;
+ }
+
+ if (s->seq_output != s->seq_decode)
+ s->seq_output = (s->seq_output + 1) & 0xff;
+ else
+ break;
+ } while (1);
+ return 0;
+}
+
+void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ const int poc = fc->ps.ph.poc;
+ int dpb = 0;
+ int min_poc = INT_MAX;
+
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+ VVCFrame *frame = &fc->DPB[i];
+ if ((frame->flags) &&
+ frame->sequence == s->seq_output &&
+ frame->poc != poc) {
+ dpb++;
+ }
+ }
+
+ if (sps && dpb >= sps->r->sps_dpb_params.dpb_max_dec_pic_buffering_minus1[sps->r->sps_max_sublayers_minus1] + 1) {
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+ VVCFrame *frame = &fc->DPB[i];
+ if ((frame->flags) &&
+ frame->sequence == s->seq_output &&
+ frame->poc != poc) {
+ if (frame->flags == VVC_FRAME_FLAG_OUTPUT && frame->poc < min_poc) {
+ min_poc = frame->poc;
+ }
+ }
+ }
+
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+ VVCFrame *frame = &fc->DPB[i];
+ if (frame->flags & VVC_FRAME_FLAG_OUTPUT &&
+ frame->sequence == s->seq_output &&
+ frame->poc <= min_poc) {
+ frame->flags |= VVC_FRAME_FLAG_BUMPING;
+ }
+ }
+
+ dpb--;
+ }
+}
+
+static VVCFrame *find_ref_idx(VVCContext *s, VVCFrameContext *fc, int poc, uint8_t use_msb)
+{
+ int mask = use_msb ? ~0 : fc->ps.sps->max_pic_order_cnt_lsb - 1;
+
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+ VVCFrame *ref = &fc->DPB[i];
+ if (ref->frame->buf[0] && ref->sequence == s->seq_decode) {
+ if ((ref->poc & mask) == poc)
+ return ref;
+ }
+ }
+ return NULL;
+}
+
+static void mark_ref(VVCFrame *frame, int flag)
+{
+ frame->flags &= ~(VVC_FRAME_FLAG_LONG_REF | VVC_FRAME_FLAG_SHORT_REF);
+ frame->flags |= flag;
+}
+
+static VVCFrame *generate_missing_ref(VVCContext *s, VVCFrameContext *fc, int poc)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ VVCFrame *frame;
+
+ frame = alloc_frame(s, fc);
+ if (!frame)
+ return NULL;
+
+ if (!s->avctx->hwaccel) {
+ if (!sps->pixel_shift) {
+ for (int i = 0; frame->frame->buf[i]; i++)
+ memset(frame->frame->buf[i]->data, 1 << (sps->bit_depth - 1),
+ frame->frame->buf[i]->size);
+ } else {
+ for (int i = 0; frame->frame->data[i]; i++)
+ for (int y = 0; y < (sps->height >> sps->vshift[i]); y++) {
+ uint8_t *dst = frame->frame->data[i] + y * frame->frame->linesize[i];
+ AV_WN16(dst, 1 << (sps->bit_depth - 1));
+ av_memcpy_backptr(dst + 2, 2, 2*(sps->width >> sps->hshift[i]) - 2);
+ }
+ }
+ }
+
+ frame->poc = poc;
+ frame->sequence = s->seq_decode;
+ frame->flags = 0;
+
+ ff_vvc_report_frame_finished(frame);
+
+ return frame;
+}
+
+/* add a reference with the given poc to the list and mark it as used in DPB */
+static int add_candidate_ref(VVCContext *s, VVCFrameContext *fc, RefPicList *list,
+ int poc, int ref_flag, uint8_t use_msb)
+{
+ VVCFrame *ref = find_ref_idx(s, fc, poc, use_msb);
+
+ if (ref == fc->ref || list->nb_refs >= VVC_MAX_REF_ENTRIES)
+ return AVERROR_INVALIDDATA;
+
+ if (!ref) {
+ ref = generate_missing_ref(s, fc, poc);
+ if (!ref)
+ return AVERROR(ENOMEM);
+ }
+
+ list->list[list->nb_refs] = poc;
+ list->ref[list->nb_refs] = ref;
+ list->isLongTerm[list->nb_refs] = ref_flag & VVC_FRAME_FLAG_LONG_REF;
+ list->nb_refs++;
+
+ mark_ref(ref, ref_flag);
+ return 0;
+}
+
+static int init_slice_rpl(const VVCFrameContext *fc, SliceContext *sc)
+{
+ VVCFrame *frame = fc->ref;
+ const VVCSH *sh = &sc->sh;
+
+ if (sc->slice_idx >= frame->nb_rpl_elems)
+ return AVERROR_INVALIDDATA;
+
+ for (int i = 0; i < sh->num_ctus_in_curr_slice; i++) {
+ const int rs = sh->ctb_addr_in_curr_slice[i];
+ frame->rpl_tab[rs] = frame->rpl + sc->slice_idx;
+ }
+
+ sc->rpl = frame->rpl_tab[sh->ctb_addr_in_curr_slice[0]]->refPicList;
+
+ return 0;
+}
+
+static int delta_poc_st(const H266RefPicListStruct *rpls,
+ const int lx, const int i, const VVCSPS *sps)
+{
+ int abs_delta_poc_st = rpls->abs_delta_poc_st[i];
+ if (!((sps->r->sps_weighted_pred_flag ||
+ sps->r->sps_weighted_bipred_flag) && i != 0))
+ abs_delta_poc_st++;
+ return (1 - 2 * rpls->strp_entry_sign_flag[i]) * abs_delta_poc_st;
+}
+
+static int poc_lt(int *prev_delta_poc_msb, const int poc, const H266RefPicLists *ref_lists,
+ const int lx, const int j, const int max_poc_lsb)
+{
+ const H266RefPicListStruct *rpls = ref_lists->rpl_ref_list + lx;
+ int lt_poc = rpls->ltrp_in_header_flag ? ref_lists->poc_lsb_lt[lx][j] : rpls->rpls_poc_lsb_lt[j];
+
+ if (ref_lists->delta_poc_msb_cycle_present_flag[lx][j]) {
+ const uint32_t delta = ref_lists->delta_poc_msb_cycle_lt[lx][j] + *prev_delta_poc_msb;
+ lt_poc += poc - delta * max_poc_lsb - (poc & (max_poc_lsb - 1));
+ *prev_delta_poc_msb = delta;
+ }
+ return lt_poc;
+}
+
+int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ const H266RawPPS *pps = fc->ps.pps->r;
+ const VVCPH *ph = &fc->ps.ph;
+ const H266RawSliceHeader *rsh = sc->sh.r;
+ const int max_poc_lsb = sps->max_pic_order_cnt_lsb;
+ const H266RefPicLists *ref_lists =
+ pps->pps_rpl_info_in_ph_flag ? &ph->r->ph_ref_pic_lists : &rsh->sh_ref_pic_lists;
+ int ret = 0;
+
+ ret = init_slice_rpl(fc, sc);
+ if (ret < 0)
+ return ret;
+
+ for (int lx = L0; lx <= L1; lx++) {
+ const H266RefPicListStruct *rpls = ref_lists->rpl_ref_list + lx;
+ RefPicList *rpl = sc->rpl + lx;
+ int poc_base = ph->poc;
+ int prev_delta_poc_msb = 0;
+
+ rpl->nb_refs = 0;
+ for (int i = 0, j = 0; i < rpls->num_ref_entries; i++) {
+ int poc;
+ if (!rpls->inter_layer_ref_pic_flag[i]) {
+ int use_msb = 1;
+ int ref_flag;
+ if (rpls->st_ref_pic_flag[i]) {
+ poc = poc_base + delta_poc_st(rpls, lx, i, sps);
+ poc_base = poc;
+ ref_flag = VVC_FRAME_FLAG_SHORT_REF;
+ } else {
+ use_msb = ref_lists->delta_poc_msb_cycle_present_flag[lx][j];
+ poc = poc_lt(&prev_delta_poc_msb, ph->poc, ref_lists, lx, j, max_poc_lsb);
+ ref_flag = VVC_FRAME_FLAG_LONG_REF;
+ j++;
+ }
+ ret = add_candidate_ref(s, fc, rpl, poc, ref_flag, use_msb);
+ if (ret < 0)
+ return ret;
+ } else {
+ avpriv_request_sample(fc->log_ctx, "Inter layer ref");
+ ret = AVERROR_PATCHWELCOME;
+ return ret;
+ }
+ }
+ if ((!rsh->sh_collocated_from_l0_flag) == lx &&
+ rsh->sh_collocated_ref_idx < rpl->nb_refs)
+ fc->ref->collocated_ref = rpl->ref[rsh->sh_collocated_ref_idx];
+ }
+ return 0;
+}
+
+int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc)
+{
+ int ret = 0;
+
+ /* clear the reference flags on all frames except the current one */
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+ VVCFrame *frame = &fc->DPB[i];
+
+ if (frame == fc->ref)
+ continue;
+
+ mark_ref(frame, 0);
+ }
+
+ if ((ret = ff_vvc_slice_rpl(s, fc, sc)) < 0)
+ goto fail;
+
+fail:
+ /* release any frames that are now unused */
+ for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++)
+ ff_vvc_unref_frame(fc, &fc->DPB[i], 0);
+ return ret;
+}
+
+void ff_vvc_report_frame_finished(VVCFrame *frame)
+{
+ ff_vvc_report_progress(frame, VVC_PROGRESS_MV, INT_MAX);
+ ff_vvc_report_progress(frame, VVC_PROGRESS_PIXEL, INT_MAX);
+}
+
+static int is_progress_done(const FrameProgress *p, const VVCProgressListener *l)
+{
+ return p->progress[l->vp] > l->y;
+}
+
+static void add_listener(VVCProgressListener **prev, VVCProgressListener *l)
+{
+ l->next = *prev;
+ *prev = l;
+}
+
+static VVCProgressListener* remove_listener(VVCProgressListener **prev, VVCProgressListener *l)
+{
+ *prev = l->next;
+ l->next = NULL;
+ return l;
+}
+
+static VVCProgressListener* get_done_listener(FrameProgress *p, const VVCProgress vp)
+{
+ VVCProgressListener *list = NULL;
+ VVCProgressListener **prev = &p->listener[vp];
+
+ while (*prev) {
+ if (is_progress_done(p, *prev)) {
+ VVCProgressListener *l = remove_listener(prev, *prev);
+ add_listener(&list, l);
+ } else {
+ prev = &(*prev)->next;
+ }
+ }
+ return list;
+}
+
+void ff_vvc_report_progress(VVCFrame *frame, const VVCProgress vp, const int y)
+{
+ FrameProgress *p = frame->progress;
+ VVCProgressListener *l = NULL;
+
+ ff_mutex_lock(&p->lock);
+
+ av_assert0(p->progress[vp] < y || p->progress[vp] == INT_MAX);
+ p->progress[vp] = y;
+ l = get_done_listener(p, vp);
+ ff_cond_signal(&p->cond);
+
+ ff_mutex_unlock(&p->lock);
+
+ while (l) {
+ l->progress_done(l);
+ l = l->next;
+ }
+}
+
+void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l)
+{
+ FrameProgress *p = frame->progress;
+
+ ff_mutex_lock(&p->lock);
+
+ if (is_progress_done(p, l)) {
+ ff_mutex_unlock(&p->lock);
+ l->progress_done(l);
+ } else {
+ add_listener(p->listener + l->vp, l);
+ ff_mutex_unlock(&p->lock);
+ }
+}
diff --git a/libavcodec/vvc/vvc_refs.h b/libavcodec/vvc/vvc_refs.h
new file mode 100644
index 0000000000..cd3b5f5632
--- /dev/null
+++ b/libavcodec/vvc/vvc_refs.h
@@ -0,0 +1,57 @@
+/*
+ * VVC reference management
+ *
+ * Copyright (C) 2023 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVC_REFS_H
+#define AVCODEC_VVC_VVC_REFS_H
+
+#include "vvcdec.h"
+
+int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, struct AVFrame *out, int no_output_of_prior_pics_flag, int flush);
+void ff_vvc_bump_frame(VVCContext *s, VVCFrameContext *fc);
+int ff_vvc_set_new_ref(VVCContext *s, VVCFrameContext *fc, struct AVFrame **frame);
+const RefPicList *ff_vvc_get_ref_list(const VVCFrameContext *fc, const VVCFrame *ref, int x0, int y0);
+int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc);
+int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, SliceContext *sc);
+void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags);
+void ff_vvc_clear_refs(VVCFrameContext *fc);
+
+typedef enum VVCProgress {
+ VVC_PROGRESS_MV,
+ VVC_PROGRESS_PIXEL,
+ VVC_PROGRESS_LAST,
+} VVCProgress;
+
+typedef struct VVCProgressListener VVCProgressListener;
+typedef void (*progress_done_fn)(VVCProgressListener *l);
+
+struct VVCProgressListener {
+ VVCProgress vp;
+ int y;
+ progress_done_fn progress_done;
+ VVCProgressListener *next; //used by ff_vvc_add_progress_listener only
+};
+
+void ff_vvc_report_frame_finished(VVCFrame *frame);
+void ff_vvc_report_progress(VVCFrame *frame, VVCProgress vp, int y);
+void ff_vvc_add_progress_listener(VVCFrame *frame, VVCProgressListener *l);
+
+#endif // AVCODEC_VVC_VVC_REFS_H
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH v7 06/14] vvcdec: add motion vector decoder
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
` (3 preceding siblings ...)
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 05/14] vvcdec: add reference management Nuo Mi
@ 2023-12-10 15:57 ` Nuo Mi
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 07/14] vvcdec: add inter prediction Nuo Mi
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 15:57 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
---
libavcodec/vvc/Makefile | 1 +
libavcodec/vvc/vvc_ctu.c | 18 +
libavcodec/vvc/vvc_ctu.h | 2 +
libavcodec/vvc/vvc_mvs.c | 1799 ++++++++++++++++++++++++++++++++++++++
libavcodec/vvc/vvc_mvs.h | 46 +
5 files changed, 1866 insertions(+)
create mode 100644 libavcodec/vvc/vvc_mvs.c
create mode 100644 libavcodec/vvc/vvc_mvs.h
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index 1d89985d13..912e9f516c 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -5,5 +5,6 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
vvc/vvc_cabac.o \
vvc/vvc_ctu.o \
vvc/vvc_data.o \
+ vvc/vvc_mvs.o \
vvc/vvc_ps.o \
vvc/vvc_refs.o \
diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c
index 78b13ffb00..3d31b862ae 100644
--- a/libavcodec/vvc/vvc_ctu.c
+++ b/libavcodec/vvc/vvc_ctu.c
@@ -20,7 +20,25 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "vvc_cabac.h"
#include "vvc_ctu.h"
+#include "vvc_mvs.h"
+
+void ff_vvc_set_neighbour_available(VVCLocalContext *lc,
+ const int x0, const int y0, const int w, const int h)
+{
+ const int log2_ctb_size = lc->fc->ps.sps->ctb_log2_size_y;
+ const int x0b = av_mod_uintp2(x0, log2_ctb_size);
+ const int y0b = av_mod_uintp2(y0, log2_ctb_size);
+
+ lc->na.cand_up = (lc->ctb_up_flag || y0b);
+ lc->na.cand_left = (lc->ctb_left_flag || x0b);
+ lc->na.cand_up_left = (x0b || y0b) ? lc->na.cand_left && lc->na.cand_up : lc->ctb_up_left_flag;
+ lc->na.cand_up_right_sap =
+ (x0b + w == 1 << log2_ctb_size) ? lc->ctb_up_right_flag && !y0b : lc->na.cand_up;
+ lc->na.cand_up_right = lc->na.cand_up_right_sap && (x0 + w) < lc->end_of_tiles_x;
+}
+
void ff_vvc_ep_init_stat_coeff(EntryPoint *ep,
const int bit_depth, const int persistent_rice_adaptation_enabled_flag)
diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h
index e2b82f538e..82801b558e 100644
--- a/libavcodec/vvc/vvc_ctu.h
+++ b/libavcodec/vvc/vvc_ctu.h
@@ -458,6 +458,8 @@ typedef struct ALFParams {
uint8_t applied[3];
} ALFParams;
+//utils
+void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h);
void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, int bit_depth, int persistent_rice_adaptation_enabled_flag);
#endif // AVCODEC_VVC_VVC_CTU_H
diff --git a/libavcodec/vvc/vvc_mvs.c b/libavcodec/vvc/vvc_mvs.c
new file mode 100644
index 0000000000..57a50f5112
--- /dev/null
+++ b/libavcodec/vvc/vvc_mvs.c
@@ -0,0 +1,1799 @@
+/*
+ * VVC motion vector decoder
+ *
+ * Copyright (C) 2023 Nuo Mi
+ * Copyright (C) 2022 Xu Mu
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "vvc_ctu.h"
+#include "vvc_data.h"
+#include "vvc_refs.h"
+#include "vvc_mvs.h"
+
+#define IS_SAME_MV(a, b) (AV_RN64A(a) == AV_RN64A(b))
+
+//check if the two luma locations belong to the same motion estimation region
+static av_always_inline int is_same_mer(const VVCFrameContext *fc, const int xN, const int yN, const int xP, const int yP)
+{
+ const uint8_t plevel = fc->ps.sps->log2_parallel_merge_level;
+
+ return xN >> plevel == xP >> plevel &&
+ yN >> plevel == yP >> plevel;
+}
+
+//return true if we have same mvs and ref_idxs
+static av_always_inline int compare_mv_ref_idx(const MvField *n, const MvField *o)
+{
+ if (!o || n->pred_flag != o->pred_flag)
+ return 0;
+ for (int i = 0; i < 2; i++) {
+ PredFlag mask = i + 1;
+ if (n->pred_flag & mask) {
+ const int same_ref_idx = n->ref_idx[i] == o->ref_idx[i];
+ const int same_mv = IS_SAME_MV(n->mv + i, o->mv + i);
+ if (!same_ref_idx || !same_mv)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+// 8.5.2.15 Temporal motion buffer compression process for collocated motion vectors
+static av_always_inline void mv_compression(Mv *motion)
+{
+ int mv[2] = {motion->x, motion->y};
+ for (int i = 0; i < 2; i++) {
+ const int s = mv[i] >> 17;
+ const int f = av_log2((mv[i] ^ s) | 31) - 4;
+ const int mask = (-1 << f) >> 1;
+ const int round = (1 << f) >> 2;
+ mv[i] = (mv[i] + round) & mask;
+ }
+ motion->x = mv[0];
+ motion->y = mv[1];
+}
+
+void ff_vvc_mv_scale(Mv *dst, const Mv *src, int td, int tb)
+{
+ int tx, scale_factor;
+
+ td = av_clip_int8(td);
+ tb = av_clip_int8(tb);
+ tx = (0x4000 + (abs(td) >> 1)) / td;
+ scale_factor = av_clip_intp2((tb * tx + 32) >> 6, 12);
+ dst->x = av_clip_intp2((scale_factor * src->x + 127 +
+ (scale_factor * src->x < 0)) >> 8, 17);
+ dst->y = av_clip_intp2((scale_factor * src->y + 127 +
+ (scale_factor * src->y < 0)) >> 8, 17);
+}
+
+//part of 8.5.2.12 Derivation process for collocated motion vectors
+static int check_mvset(Mv *mvLXCol, Mv *mvCol,
+ int colPic, int poc,
+ const RefPicList *refPicList, int X, int refIdxLx,
+ const RefPicList *refPicList_col, int listCol, int refidxCol)
+{
+ int cur_lt = refPicList[X].isLongTerm[refIdxLx];
+ int col_lt = refPicList_col[listCol].isLongTerm[refidxCol];
+ int col_poc_diff, cur_poc_diff;
+
+ if (cur_lt != col_lt) {
+ mvLXCol->x = 0;
+ mvLXCol->y = 0;
+ return 0;
+ }
+
+ col_poc_diff = colPic - refPicList_col[listCol].list[refidxCol];
+ cur_poc_diff = poc - refPicList[X].list[refIdxLx];
+
+ mv_compression(mvCol);
+ if (cur_lt || col_poc_diff == cur_poc_diff) {
+ mvLXCol->x = av_clip_intp2(mvCol->x, 17);
+ mvLXCol->y = av_clip_intp2(mvCol->y, 17);
+ } else {
+ ff_vvc_mv_scale(mvLXCol, mvCol, col_poc_diff, cur_poc_diff);
+ }
+ return 1;
+}
+
+#define CHECK_MVSET(l) \
+ check_mvset(mvLXCol, temp_col.mv + l, \
+ colPic, fc->ps.ph.poc, \
+ refPicList, X, refIdxLx, \
+ refPicList_col, L ## l, temp_col.ref_idx[l])
+
+//derive NoBackwardPredFlag
+int ff_vvc_no_backward_pred_flag(const VVCLocalContext *lc)
+{
+ int check_diffpicount = 0;
+ int i, j;
+ const RefPicList *rpl = lc->sc->rpl;
+
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < rpl[j].nb_refs; i++) {
+ if (rpl[j].list[i] > lc->fc->ps.ph.poc) {
+ check_diffpicount++;
+ break;
+ }
+ }
+ }
+ return !check_diffpicount;
+}
+
+//8.5.2.12 Derivation process for collocated motion vectors
+static int derive_temporal_colocated_mvs(const VVCLocalContext *lc, MvField temp_col,
+ int refIdxLx, Mv *mvLXCol, int X,
+ int colPic, const RefPicList *refPicList_col, int sb_flag)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const SliceContext *sc = lc->sc;
+ RefPicList* refPicList = sc->rpl;
+
+ if (temp_col.pred_flag == PF_INTRA)
+ return 0;
+
+ if (sb_flag){
+ if (X == 0) {
+ if (temp_col.pred_flag & PF_L0)
+ return CHECK_MVSET(0);
+ else if (ff_vvc_no_backward_pred_flag(lc) && (temp_col.pred_flag & PF_L1))
+ return CHECK_MVSET(1);
+ } else {
+ if (temp_col.pred_flag & PF_L1)
+ return CHECK_MVSET(1);
+ else if (ff_vvc_no_backward_pred_flag(lc) && (temp_col.pred_flag & PF_L0))
+ return CHECK_MVSET(0);
+ }
+ } else {
+ if (!(temp_col.pred_flag & PF_L0))
+ return CHECK_MVSET(1);
+ else if (temp_col.pred_flag == PF_L0)
+ return CHECK_MVSET(0);
+ else if (temp_col.pred_flag == PF_BI) {
+ if (ff_vvc_no_backward_pred_flag(lc)) {
+ if (X == 0)
+ return CHECK_MVSET(0);
+ else
+ return CHECK_MVSET(1);
+ } else {
+ if (!lc->sc->sh.r->sh_collocated_from_l0_flag)
+ return CHECK_MVSET(0);
+ else
+ return CHECK_MVSET(1);
+ }
+ }
+ }
+ return 0;
+}
+
+#define TAB_MVF(x, y) \
+ tab_mvf[((y) >> MIN_PU_LOG2) * min_pu_width + ((x) >> MIN_PU_LOG2)]
+
+#define TAB_MVF_PU(v) \
+ TAB_MVF(x ## v, y ## v)
+
+#define TAB_CP_MV(lx, x, y) \
+ fc->tab.cp_mv[lx][((((y) >> min_cb_log2_size) * min_cb_width + ((x) >> min_cb_log2_size)) ) * MAX_CONTROL_POINTS]
+
+
+#define DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag) \
+ derive_temporal_colocated_mvs(lc, temp_col, \
+ refIdxLx, mvLXCol, X, colPic, \
+ ff_vvc_get_ref_list(fc, ref, x, y), sb_flag)
+
+//8.5.2.11 Derivation process for temporal luma motion vector prediction
+static int temporal_luma_motion_vector(const VVCLocalContext *lc,
+ const int refIdxLx, Mv *mvLXCol, const int X, int check_center, int sb_flag)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const CodingUnit *cu = lc->cu;
+ int x, y, colPic, availableFlagLXCol = 0;
+ int min_pu_width = fc->ps.pps->min_pu_width;
+ VVCFrame *ref = fc->ref->collocated_ref;
+ MvField *tab_mvf;
+ MvField temp_col;
+
+ if (!ref) {
+ memset(mvLXCol, 0, sizeof(*mvLXCol));
+ return 0;
+ }
+
+ if (!fc->ps.ph.r->ph_temporal_mvp_enabled_flag || (cu->cb_width * cu->cb_height <= 32))
+ return 0;
+
+ tab_mvf = ref->tab_dmvr_mvf;
+ colPic = ref->poc;
+
+ //bottom right collocated motion vector
+ x = cu->x0 + cu->cb_width;
+ y = cu->y0 + cu->cb_height;
+
+ if (tab_mvf &&
+ (cu->y0 >> sps->ctb_log2_size_y) == (y >> sps->ctb_log2_size_y) &&
+ y < fc->ps.sps->height &&
+ x < fc->ps.sps->width) {
+ x &= ~7;
+ y &= ~7;
+ temp_col = TAB_MVF(x, y);
+ availableFlagLXCol = DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag);
+ }
+ if (check_center) {
+ // derive center collocated motion vector
+ if (tab_mvf && !availableFlagLXCol) {
+ x = cu->x0 + (cu->cb_width >> 1);
+ y = cu->y0 + (cu->cb_height >> 1);
+ x &= ~7;
+ y &= ~7;
+ temp_col = TAB_MVF(x, y);
+ availableFlagLXCol = DERIVE_TEMPORAL_COLOCATED_MVS(sb_flag);
+ }
+ }
+ return availableFlagLXCol;
+}
+
+void ff_vvc_set_mvf(const VVCLocalContext *lc, const int x0, const int y0, const int w, const int h, const MvField *mvf)
+{
+ const VVCFrameContext *fc = lc->fc;
+ MvField *tab_mvf = fc->tab.mvf;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const int min_pu_size = 1 << MIN_PU_LOG2;
+ for (int dy = 0; dy < h; dy += min_pu_size) {
+ for (int dx = 0; dx < w; dx += min_pu_size) {
+ const int x = x0 + dx;
+ const int y = y0 + dy;
+ TAB_MVF(x, y) = *mvf;
+ }
+ }
+}
+
+void ff_vvc_set_intra_mvf(const VVCLocalContext *lc)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ MvField *tab_mvf = fc->tab.mvf;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const int min_pu_size = 1 << MIN_PU_LOG2;
+ for (int dy = 0; dy < cu->cb_height; dy += min_pu_size) {
+ for (int dx = 0; dx < cu->cb_width; dx += min_pu_size) {
+ const int x = cu->x0 + dx;
+ const int y = cu->y0 + dy;
+ TAB_MVF(x, y).pred_flag = PF_INTRA;
+ }
+ }
+}
+
+//cbProfFlagLX from 8.5.5.9 Derivation process for motion vector arrays from affine control point motion vectors
+static int derive_cb_prof_flag_lx(const VVCLocalContext *lc, const PredictionUnit* pu, int lx, int is_fallback)
+{
+ const MotionInfo* mi = &pu->mi;
+ const Mv* cp_mv = &mi->mv[lx][0];
+ if (lc->fc->ps.ph.r->ph_prof_disabled_flag || is_fallback)
+ return 0;
+ if (mi->motion_model_idc == MOTION_4_PARAMS_AFFINE) {
+ if (IS_SAME_MV(cp_mv, cp_mv + 1))
+ return 0;
+ }
+ if (mi->motion_model_idc == MOTION_6_PARAMS_AFFINE) {
+ if (IS_SAME_MV(cp_mv, cp_mv + 1) && IS_SAME_MV(cp_mv, cp_mv + 2))
+ return 0;
+ }
+ //fixme: RprConstraintsActiveFlag
+ return 1;
+}
+
+typedef struct SubblockParams {
+ int d_hor_x;
+ int d_ver_x;
+ int d_hor_y;
+ int d_ver_y;
+ int mv_scale_hor;
+ int mv_scale_ver;
+ int is_fallback;
+
+ int cb_width;
+ int cb_height;
+} SubblockParams;
+
+static int is_fallback_mode(const SubblockParams *sp, const PredFlag pred_flag)
+{
+ const int a = 4 * (2048 + sp->d_hor_x);
+ const int b = 4 * sp->d_hor_y;
+ const int c = 4 * (2048 + sp->d_ver_y);
+ const int d = 4 * sp->d_ver_x;
+ if (pred_flag == PF_BI) {
+ const int max_w4 = FFMAX(0, FFMAX(a, FFMAX(b, a + b)));
+ const int min_w4 = FFMIN(0, FFMIN(a, FFMIN(b, a + b)));
+ const int max_h4 = FFMAX(0, FFMAX(c, FFMAX(d, c + d)));
+ const int min_h4 = FFMIN(0, FFMIN(c, FFMIN(d, c + d)));
+ const int bx_wx4 = ((max_w4 - min_w4) >> 11) + 9;
+ const int bx_hx4 = ((max_h4 - min_h4) >> 11) + 9;
+ return bx_wx4 * bx_hx4 > 225;
+ } else {
+ const int bx_wxh = (FFABS(a) >> 11) + 9;
+ const int bx_hxh = (FFABS(d) >> 11) + 9;
+ const int bx_wxv = (FFABS(b) >> 11) + 9;
+ const int bx_hxv = (FFABS(c) >> 11) + 9;
+ if (bx_wxh * bx_hxh <= 165 && bx_wxv * bx_hxv <= 165)
+ return 0;
+ }
+ return 1;
+}
+
+static void init_subblock_params(SubblockParams *sp, const MotionInfo* mi,
+ const int cb_width, const int cb_height, const int lx)
+{
+ const int log2_cbw = av_log2(cb_width);
+ const int log2_cbh = av_log2(cb_height);
+ const Mv* cp_mv = mi->mv[lx];
+ const int num_cp_mv = mi->motion_model_idc + 1;
+ sp->d_hor_x = (cp_mv[1].x - cp_mv[0].x) << (MAX_CU_DEPTH - log2_cbw);
+ sp->d_ver_x = (cp_mv[1].y - cp_mv[0].y) << (MAX_CU_DEPTH - log2_cbw);
+ if (num_cp_mv == 3) {
+ sp->d_hor_y = (cp_mv[2].x - cp_mv[0].x) << (MAX_CU_DEPTH - log2_cbh);
+ sp->d_ver_y = (cp_mv[2].y - cp_mv[0].y) << (MAX_CU_DEPTH - log2_cbh);
+ } else {
+ sp->d_hor_y = -sp->d_ver_x;
+ sp->d_ver_y = sp->d_hor_x;
+ }
+ sp->mv_scale_hor = (cp_mv[0].x) << MAX_CU_DEPTH;
+ sp->mv_scale_ver = (cp_mv[0].y) << MAX_CU_DEPTH;
+ sp->cb_width = cb_width;
+ sp->cb_height = cb_height;
+ sp->is_fallback = is_fallback_mode(sp, mi->pred_flag);
+}
+
+static void derive_subblock_diff_mvs(const VVCLocalContext *lc, PredictionUnit* pu, const SubblockParams* sp, const int lx)
+{
+ pu->cb_prof_flag[lx] = derive_cb_prof_flag_lx(lc, pu, lx, sp->is_fallback);
+ if (pu->cb_prof_flag[lx]) {
+ const int dmv_limit = 1 << 5;
+ const int pos_offset_x = 6 * (sp->d_hor_x + sp->d_hor_y);
+ const int pos_offset_y = 6 * (sp->d_ver_x + sp->d_ver_y);
+ for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) {
+ for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) {
+ Mv diff;
+ diff.x = x * (sp->d_hor_x << 2) + y * (sp->d_hor_y << 2) - pos_offset_x;
+ diff.y = x * (sp->d_ver_x << 2) + y * (sp->d_ver_y << 2) - pos_offset_y;
+ ff_vvc_round_mv(&diff, 0, 8);
+ pu->diff_mv_x[lx][AFFINE_MIN_BLOCK_SIZE * y + x] = av_clip(diff.x, -dmv_limit + 1, dmv_limit - 1);
+ pu->diff_mv_y[lx][AFFINE_MIN_BLOCK_SIZE * y + x] = av_clip(diff.y, -dmv_limit + 1, dmv_limit - 1);
+ }
+ }
+ }
+}
+
+static void store_cp_mv(const VVCLocalContext *lc, const MotionInfo *mi, const int lx)
+{
+ VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y;
+ const int min_cb_size = fc->ps.sps->min_cb_size_y;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+ const int num_cp_mv = mi->motion_model_idc + 1;
+
+ for (int dy = 0; dy < cu->cb_height; dy += min_cb_size) {
+ for (int dx = 0; dx < cu->cb_width; dx += min_cb_size) {
+ const int x_cb = (cu->x0 + dx) >> log2_min_cb_size;
+ const int y_cb = (cu->y0 + dy) >> log2_min_cb_size;
+ const int offset = (y_cb * min_cb_width + x_cb) * MAX_CONTROL_POINTS;
+
+ memcpy(&fc->tab.cp_mv[lx][offset], mi->mv[lx], sizeof(Mv) * num_cp_mv);
+ SAMPLE_CTB(fc->tab.mmi, x_cb, y_cb) = mi->motion_model_idc;
+ }
+ }
+}
+
+//8.5.5.9 Derivation process for motion vector arrays from affine control point motion vectors
+void ff_vvc_store_sb_mvs(const VVCLocalContext *lc, PredictionUnit *pu)
+{
+ const CodingUnit *cu = lc->cu;
+ const MotionInfo *mi = &pu->mi;
+ const int sbw = cu->cb_width / mi->num_sb_x;
+ const int sbh = cu->cb_height / mi->num_sb_y;
+ SubblockParams params[2];
+ MvField mvf;
+
+ mvf.pred_flag = mi->pred_flag;
+ mvf.bcw_idx = mi->bcw_idx;
+ mvf.hpel_if_idx = mi->hpel_if_idx;
+ mvf.ciip_flag = 0;
+ for (int i = 0; i < 2; i++) {
+ const PredFlag mask = i + 1;
+ if (mi->pred_flag & mask) {
+ store_cp_mv(lc, mi, i);
+ init_subblock_params(params + i, mi, cu->cb_width, cu->cb_height, i);
+ derive_subblock_diff_mvs(lc, pu, params + i, i);
+ mvf.ref_idx[i] = mi->ref_idx[i];
+ }
+ }
+
+ for (int sby = 0; sby < mi->num_sb_y; sby++) {
+ for (int sbx = 0; sbx < mi->num_sb_x; sbx++) {
+ const int x0 = cu->x0 + sbx * sbw;
+ const int y0 = cu->y0 + sby * sbh;
+ for (int i = 0; i < 2; i++) {
+ const PredFlag mask = i + 1;
+ if (mi->pred_flag & mask) {
+ const SubblockParams* sp = params + i;
+ const int x_pos_cb = sp->is_fallback ? (cu->cb_width >> 1) : (2 + (sbx << MIN_CU_LOG2));
+ const int y_pos_cb = sp->is_fallback ? (cu->cb_height >> 1) : (2 + (sby << MIN_CU_LOG2));
+ Mv *mv = mvf.mv + i;
+
+ mv->x = sp->mv_scale_hor + sp->d_hor_x * x_pos_cb + sp->d_hor_y * y_pos_cb;
+ mv->y = sp->mv_scale_ver + sp->d_ver_x * x_pos_cb + sp->d_ver_y * y_pos_cb;
+ ff_vvc_round_mv(mv, 0, MAX_CU_DEPTH);
+ ff_vvc_clip_mv(mv);
+ }
+ }
+ ff_vvc_set_mvf(lc, x0, y0, sbw, sbh, &mvf);
+ }
+ }
+}
+
+void ff_vvc_store_gpm_mvf(const VVCLocalContext *lc, const PredictionUnit *pu)
+{
+ const CodingUnit *cu = lc->cu;
+ const int angle_idx = ff_vvc_gpm_angle_idx[pu->gpm_partition_idx];
+ const int distance_idx = ff_vvc_gpm_distance_idx[pu->gpm_partition_idx];
+ const int displacement_x = ff_vvc_gpm_distance_lut[angle_idx];
+ const int displacement_y = ff_vvc_gpm_distance_lut[(angle_idx + 8) % 32];
+ const int is_flip = angle_idx >= 13 &&angle_idx <= 27;
+ const int shift_hor = (angle_idx % 16 == 8 || (angle_idx % 16 && cu->cb_height >= cu->cb_width)) ? 0 : 1;
+ const int sign = angle_idx < 16 ? 1 : -1;
+ const int block_size = 4;
+ int offset_x = (-cu->cb_width) >> 1;
+ int offset_y = (-cu->cb_height) >> 1;
+
+ if (!shift_hor)
+ offset_y += sign * ((distance_idx * cu->cb_height) >> 3);
+ else
+ offset_x += sign * ((distance_idx * cu->cb_width) >> 3);
+
+ for (int y = 0; y < cu->cb_height; y += block_size) {
+ for (int x = 0; x < cu->cb_width; x += block_size) {
+ const int motion_idx = (((x + offset_x) << 1) + 5) * displacement_x +
+ (((y + offset_y) << 1) + 5) * displacement_y;
+ const int s_type = FFABS(motion_idx) < 32 ? 2 : (motion_idx <= 0 ? (1 - is_flip) : is_flip);
+ const int pred_flag = pu->gpm_mv[0].pred_flag | pu->gpm_mv[1].pred_flag;
+ const int x0 = cu->x0 + x;
+ const int y0 = cu->y0 + y;
+
+ if (!s_type)
+ ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, pu->gpm_mv + 0);
+ else if (s_type == 1 || (s_type == 2 && pred_flag != PF_BI))
+ ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, pu->gpm_mv + 1);
+ else {
+ MvField mvf = pu->gpm_mv[0];
+ const MvField *mv1 = &pu->gpm_mv[1];
+ const int lx = mv1->pred_flag - PF_L0;
+ mvf.pred_flag = PF_BI;
+ mvf.ref_idx[lx] = mv1->ref_idx[lx];
+ mvf.mv[lx] = mv1->mv[lx];
+ ff_vvc_set_mvf(lc, x0, y0, block_size, block_size, &mvf);
+ }
+ }
+ }
+}
+
+void ff_vvc_store_mvf(const VVCLocalContext *lc, const MvField *mvf)
+{
+ const CodingUnit *cu = lc->cu;
+ ff_vvc_set_mvf(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height, mvf);
+}
+
+void ff_vvc_store_mv(const VVCLocalContext *lc, const MotionInfo *mi)
+{
+ const CodingUnit *cu = lc->cu;
+ MvField mvf;
+
+ mvf.hpel_if_idx = mi->hpel_if_idx;
+ mvf.bcw_idx = mi->bcw_idx;
+ mvf.pred_flag = mi->pred_flag;
+ mvf.ciip_flag = 0;
+
+ for (int i = 0; i < 2; i++) {
+ const PredFlag mask = i + 1;
+ if (mvf.pred_flag & mask) {
+ mvf.mv[i] = mi->mv[i][0];
+ mvf.ref_idx[i] = mi->ref_idx[i];
+ }
+ }
+ ff_vvc_set_mvf(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height, &mvf);
+}
+
+typedef enum NeighbourIdx {
+ A0,
+ A1,
+ A2,
+ B0,
+ B1,
+ B2,
+ B3,
+ NUM_NBS,
+ NB_IDX_NONE = NUM_NBS,
+} NeighbourIdx;
+
+typedef struct Neighbour {
+ int x;
+ int y;
+
+ int checked;
+ int available;
+} Neighbour;
+
+typedef struct NeighbourContext {
+ Neighbour neighbours[NUM_NBS];
+ const VVCLocalContext *lc;
+} NeighbourContext;
+
+static int is_a0_available(const VVCLocalContext *lc, const CodingUnit *cu)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int x0b = av_mod_uintp2(cu->x0, sps->ctb_log2_size_y);
+ int cand_bottom_left;
+
+ if (!x0b && !lc->ctb_left_flag) {
+ cand_bottom_left = 0;
+ } else {
+ const int log2_min_cb_size = sps->min_cb_log2_size_y;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+ const int x = (cu->x0 - 1) >> log2_min_cb_size;
+ const int y = (cu->y0 + cu->cb_height) >> log2_min_cb_size;
+ const int max_y = FFMIN(fc->ps.pps->height, ((cu->y0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y);
+ if (cu->y0 + cu->cb_height >= max_y)
+ cand_bottom_left = 0;
+ else
+ cand_bottom_left = SAMPLE_CTB(fc->tab.cb_width[0], x, y) != 0;
+ }
+ return cand_bottom_left;
+}
+
+static void init_neighbour_context(NeighbourContext *ctx, const VVCLocalContext *lc)
+{
+ const CodingUnit *cu = lc->cu;
+ const NeighbourAvailable *na = &lc->na;
+ const int x0 = cu->x0;
+ const int y0 = cu->y0;
+ const int cb_width = cu->cb_width;
+ const int cb_height = cu->cb_height;
+ const int a0_available = is_a0_available(lc, cu);
+
+ Neighbour neighbours[NUM_NBS] = {
+ { x0 - 1, y0 + cb_height, !a0_available }, //A0
+ { x0 - 1, y0 + cb_height - 1, !na->cand_left }, //A1
+ { x0 - 1, y0, !na->cand_left }, //A2
+ { x0 + cb_width, y0 - 1, !na->cand_up_right }, //B0
+ { x0 + cb_width - 1, y0 - 1, !na->cand_up }, //B1
+ { x0 - 1, y0 - 1, !na->cand_up_left }, //B2
+ { x0, y0 - 1, !na->cand_up }, //B3
+ };
+
+ memcpy(ctx->neighbours, neighbours, sizeof(neighbours));
+ ctx->lc = lc;
+}
+
+static int check_available(Neighbour *n, const VVCLocalContext *lc, const int is_mvp)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const CodingUnit *cu = lc->cu;
+ const MvField *tab_mvf = fc->tab.mvf;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+
+ if (!n->checked) {
+ n->checked = 1;
+ n->available = !sps->r->sps_entropy_coding_sync_enabled_flag || ((n->x >> sps->ctb_log2_size_y) <= (cu->x0 >> sps->ctb_log2_size_y));
+ n->available &= TAB_MVF(n->x, n->y).pred_flag != PF_INTRA;
+ if (!is_mvp)
+ n->available &= !is_same_mer(fc, n->x, n->y, cu->x0, cu->y0);
+ }
+ return n->available;
+}
+
+static const MvField *mv_merge_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const MvField* tab_mvf = fc->tab.mvf;
+ const MvField *mvf = &TAB_MVF(x_cand, y_cand);
+
+ return mvf;
+}
+
+static const MvField* mv_merge_from_nb(NeighbourContext *ctx, const NeighbourIdx nb)
+{
+ const VVCLocalContext *lc = ctx->lc;
+ const int is_mvp = 0;
+ Neighbour *n = &ctx->neighbours[nb];
+
+ if (check_available(n, lc, is_mvp))
+ return mv_merge_candidate(lc, n->x, n->y);
+ return 0;
+}
+#define MV_MERGE_FROM_NB(nb) mv_merge_from_nb(&nctx, nb)
+
+//8.5.2.3 Derivation process for spatial merging candidates
+static int mv_merge_spatial_candidates(const VVCLocalContext *lc, const int merge_idx,
+ const MvField **nb_list, MvField *cand_list, int *nb_merge_cand)
+{
+ const MvField *cand;
+ int num_cands = 0;
+ NeighbourContext nctx;
+
+ static NeighbourIdx nbs[][2] = {
+ {B1, NB_IDX_NONE },
+ {A1, B1 },
+ {B0, B1 },
+ {A0, A1 },
+ };
+
+ init_neighbour_context(&nctx, lc);
+ for (int i = 0; i < FF_ARRAY_ELEMS(nbs); i++) {
+ NeighbourIdx nb = nbs[i][0];
+ NeighbourIdx old = nbs[i][1];
+ cand = nb_list[nb] = MV_MERGE_FROM_NB(nb);
+ if (cand && !compare_mv_ref_idx(cand, nb_list[old])) {
+ cand_list[num_cands] = *cand;
+ if (merge_idx == num_cands)
+ return 1;
+ num_cands++;
+ }
+ }
+ if (num_cands != 4) {
+ cand = MV_MERGE_FROM_NB(B2);
+ if (cand && !compare_mv_ref_idx(cand, nb_list[A1])
+ && !compare_mv_ref_idx(cand, nb_list[B1])) {
+ cand_list[num_cands] = *cand;
+ if (merge_idx == num_cands)
+ return 1;
+ num_cands++;
+ }
+ }
+ *nb_merge_cand = num_cands;
+ return 0;
+}
+
+static int mv_merge_temporal_candidate(const VVCLocalContext *lc, MvField *cand)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+
+ memset(cand, 0, sizeof(*cand));
+ if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag && (cu->cb_width * cu->cb_height > 32)) {
+ int available_l0 = temporal_luma_motion_vector(lc, 0, cand->mv + 0, 0, 1, 0);
+ int available_l1 = IS_B(lc->sc->sh.r) ?
+ temporal_luma_motion_vector(lc, 0, cand->mv + 1, 1, 1, 0) : 0;
+ cand->pred_flag = available_l0 + (available_l1 << 1);
+ }
+ return cand->pred_flag;
+}
+
+//8.5.2.6 Derivation process for history-based merging candidates
+static int mv_merge_history_candidates(const VVCLocalContext *lc, const int merge_idx,
+ const MvField **nb_list, MvField *cand_list, int *num_cands)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const EntryPoint* ep = lc->ep;
+ for (int i = 1; i <= ep->num_hmvp && (*num_cands < sps->max_num_merge_cand - 1); i++) {
+ const MvField *h = &ep->hmvp[ep->num_hmvp - i];
+ const int same_motion = i <= 2 && (compare_mv_ref_idx(h, nb_list[A1]) || compare_mv_ref_idx(h, nb_list[B1]));
+ if (!same_motion) {
+ cand_list[*num_cands] = *h;
+ if (merge_idx == *num_cands)
+ return 1;
+ (*num_cands)++;
+ }
+ }
+ return 0;
+}
+
+//8.5.2.4 Derivation process for pairwise average merging candidate
+static int mv_merge_pairwise_candidate(MvField *cand_list, const int num_cands, const int is_b)
+{
+ if (num_cands > 1) {
+ const int num_ref_rists = is_b ? 2 : 1;
+ const MvField* p0 = cand_list + 0;
+ const MvField* p1 = cand_list + 1;
+ MvField* cand = cand_list + num_cands;
+
+ cand->pred_flag = 0;
+ for (int i = 0; i < num_ref_rists; i++) {
+ PredFlag mask = i + 1;
+ if (p0->pred_flag & mask) {
+ cand->pred_flag |= mask;
+ cand->ref_idx[i] = p0->ref_idx[i];
+ if (p1->pred_flag & mask) {
+ Mv *mv = cand->mv + i;
+ mv->x = p0->mv[i].x + p1->mv[i].x;
+ mv->y = p0->mv[i].y + p1->mv[i].y;
+ ff_vvc_round_mv(mv, 0, 1);
+ } else {
+ cand->mv[i] = p0->mv[i];
+ }
+ } else if (p1->pred_flag & mask) {
+ cand->pred_flag |= mask;
+ cand->mv[i] = p1->mv[i];
+ cand->ref_idx[i] = p1->ref_idx[i];
+ }
+ }
+ if (cand->pred_flag) {
+ cand->hpel_if_idx = p0->hpel_if_idx == p1->hpel_if_idx ? p0->hpel_if_idx : 0;
+ cand->bcw_idx = 0;
+ cand->ciip_flag = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+//8.5.2.5 Derivation process for zero motion vector merging candidates
+static void mv_merge_zero_motion_candidate(const VVCLocalContext *lc, const int merge_idx,
+ MvField *cand_list, int num_cands)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const int num_ref_idx = IS_P(rsh) ?
+ rsh->num_ref_idx_active[L0] : FFMIN(rsh->num_ref_idx_active[L0], rsh->num_ref_idx_active[L1]);
+ int zero_idx = 0;
+
+ while (num_cands < sps->max_num_merge_cand) {
+ MvField *cand = cand_list + num_cands;
+
+ cand->pred_flag = PF_L0 + (IS_B(rsh) << 1);
+ AV_ZERO64(cand->mv + 0);
+ AV_ZERO64(cand->mv + 1);
+ cand->ref_idx[0] = zero_idx < num_ref_idx ? zero_idx : 0;
+ cand->ref_idx[1] = zero_idx < num_ref_idx ? zero_idx : 0;
+ cand->bcw_idx = 0;
+ cand->hpel_if_idx = 0;
+ if (merge_idx == num_cands)
+ return;
+ num_cands++;
+ zero_idx++;
+ }
+}
+
+static void mv_merge_mode(const VVCLocalContext *lc, const int merge_idx, MvField *cand_list)
+{
+ int num_cands = 0;
+ const MvField *nb_list[NUM_NBS + 1] = { NULL };
+
+ if (mv_merge_spatial_candidates(lc, merge_idx, nb_list, cand_list, &num_cands))
+ return;
+
+ if (mv_merge_temporal_candidate(lc, &cand_list[num_cands])) {
+ if (merge_idx == num_cands)
+ return;
+ num_cands++;
+ }
+
+ if (mv_merge_history_candidates(lc, merge_idx, nb_list, cand_list, &num_cands))
+ return;
+
+ if (mv_merge_pairwise_candidate(cand_list, num_cands, IS_B(lc->sc->sh.r))) {
+ if (merge_idx == num_cands)
+ return;
+ num_cands++;
+ }
+
+ mv_merge_zero_motion_candidate(lc, merge_idx, cand_list, num_cands);
+}
+
+//8.5.2.2 Derivation process for luma motion vectors for merge mode
+void ff_vvc_luma_mv_merge_mode(VVCLocalContext *lc, const int merge_idx, const int ciip_flag, MvField *mv)
+{
+ const CodingUnit *cu = lc->cu;
+ MvField cand_list[MRG_MAX_NUM_CANDS];
+
+ ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+ mv_merge_mode(lc, merge_idx, cand_list);
+ *mv = cand_list[merge_idx];
+ //ciip flag in not inhritable
+ mv->ciip_flag = ciip_flag;
+}
+
+//8.5.4.2 Derivation process for luma motion vectors for geometric partitioning merge mode
+void ff_vvc_luma_mv_merge_gpm(VVCLocalContext *lc, const int merge_gpm_idx[2], MvField *mv)
+{
+ const CodingUnit *cu = lc->cu;
+ MvField cand_list[MRG_MAX_NUM_CANDS];
+
+ const int idx[] = { merge_gpm_idx[0], merge_gpm_idx[1] + (merge_gpm_idx[1] >= merge_gpm_idx[0]) };
+
+ ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+ mv_merge_mode(lc, FFMAX(idx[0], idx[1]), cand_list);
+ memset(mv, 0, 2 * sizeof(*mv));
+ for (int i = 0; i < 2; i++) {
+ int lx = idx[i] & 1;
+ int mask = lx + PF_L0;
+ MvField *cand = cand_list + idx[i];
+ if (!(cand->pred_flag & mask)) {
+ lx = !lx;
+ mask = lx + PF_L0;
+ }
+ mv[i].pred_flag = mask;
+ mv[i].ref_idx[lx] = cand->ref_idx[lx];
+ mv[i].mv[lx] = cand->mv[lx];
+ }
+
+}
+
+//8.5.5.5 Derivation process for luma affine control point motion vectors from a neighbouring block
+static void affine_cps_from_nb(const VVCLocalContext *lc,
+ const int x_nb, int y_nb, const int nbw, const int nbh, const int lx,
+ Mv *cps, int num_cps)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const int x0 = cu->x0;
+ const int y0 = cu->y0;
+ const int cb_width = cu->cb_width;
+ const int cb_height = cu->cb_height;
+ const MvField* tab_mvf = fc->tab.mvf;
+ const int min_cb_log2_size = fc->ps.sps->min_cb_log2_size_y;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+
+ const int log2_nbw = ff_log2(nbw);
+ const int log2_nbh = ff_log2(nbh);
+ const int is_ctb_boundary = !((y_nb + nbh) % fc->ps.sps->ctb_size_y) && (y_nb + nbh == y0);
+ const Mv *l, *r;
+ int mv_scale_hor, mv_scale_ver, d_hor_x, d_ver_x, d_hor_y, d_ver_y, motion_model_idc_nb;
+ if (is_ctb_boundary) {
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ l = &TAB_MVF(x_nb, y_nb + nbh - 1).mv[lx];
+ r = &TAB_MVF(x_nb + nbw - 1, y_nb + nbh - 1).mv[lx];
+ } else {
+ const int x = x_nb >> min_cb_log2_size;
+ const int y = y_nb >> min_cb_log2_size;
+ motion_model_idc_nb = SAMPLE_CTB(fc->tab.mmi, x, y);
+
+ l = &TAB_CP_MV(lx, x_nb, y_nb);
+ r = &TAB_CP_MV(lx, x_nb + nbw - 1, y_nb) + 1;
+ }
+ mv_scale_hor = l->x << 7;
+ mv_scale_ver = l->y << 7;
+ d_hor_x = (r->x - l->x) << (7 - log2_nbw);
+ d_ver_x = (r->y - l->y) << (7 - log2_nbw);
+ if (!is_ctb_boundary && motion_model_idc_nb == MOTION_6_PARAMS_AFFINE) {
+ const Mv* lb = &TAB_CP_MV(lx, x_nb, y_nb + nbh - 1) + 2;
+ d_hor_y = (lb->x - l->x) << (7 - log2_nbh);
+ d_ver_y = (lb->y - l->y) << (7 - log2_nbh);
+ } else {
+ d_hor_y = -d_ver_x;
+ d_ver_y = d_hor_x;
+ }
+
+ if (is_ctb_boundary) {
+ y_nb = y0;
+ }
+ cps[0].x = mv_scale_hor + d_hor_x * (x0 - x_nb) + d_hor_y * (y0 - y_nb);
+ cps[0].y = mv_scale_ver + d_ver_x * (x0 - x_nb) + d_ver_y * (y0 - y_nb);
+ cps[1].x = mv_scale_hor + d_hor_x * (x0 + cb_width - x_nb) + d_hor_y * (y0 - y_nb);
+ cps[1].y = mv_scale_ver + d_ver_x * (x0 + cb_width - x_nb) + d_ver_y * (y0 - y_nb);
+ if (num_cps == 3) {
+ cps[2].x = mv_scale_hor + d_hor_x * (x0 - x_nb) + d_hor_y * (y0 + cb_height - y_nb);
+ cps[2].y = mv_scale_ver + d_ver_x * (x0 - x_nb) + d_ver_y * (y0 + cb_height - y_nb);
+ }
+ for (int i = 0; i < num_cps; i++) {
+ ff_vvc_round_mv(cps + i, 0, 7);
+ ff_vvc_clip_mv(cps + i);
+ }
+}
+
+//derive affine neighbour's postion, width and height,
+static int affine_neighbour_cb(const VVCFrameContext *fc, const int x_nb, const int y_nb, int *x_cb, int *y_cb, int *cbw, int *cbh)
+{
+ const int log2_min_cb_size = fc->ps.sps->min_cb_log2_size_y;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+ const int x = x_nb >> log2_min_cb_size;
+ const int y = y_nb >> log2_min_cb_size;
+ const int motion_model_idc = SAMPLE_CTB(fc->tab.mmi, x, y);
+ if (motion_model_idc) {
+ *x_cb = SAMPLE_CTB(fc->tab.cb_pos_x[0], x, y);
+ *y_cb = SAMPLE_CTB(fc->tab.cb_pos_y[0], x, y);
+ *cbw = SAMPLE_CTB(fc->tab.cb_width[0], x, y);
+ *cbh = SAMPLE_CTB(fc->tab.cb_height[0], x, y);
+ }
+ return motion_model_idc;
+}
+
+//part of 8.5.5.2 Derivation process for motion vectors and reference indices in subblock merge mode
+static int affine_merge_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand, MotionInfo* mi)
+{
+ const VVCFrameContext *fc = lc->fc;
+ int x, y, w, h, motion_model_idc;
+
+ motion_model_idc = affine_neighbour_cb(fc, x_cand, y_cand, &x, &y, &w, &h);
+ if (motion_model_idc) {
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const MvField* tab_mvf = fc->tab.mvf;
+ const MvField *mvf = &TAB_MVF(x, y);
+
+ mi->bcw_idx = mvf->bcw_idx;
+ mi->pred_flag = mvf->pred_flag;
+ for (int i = 0; i < 2; i++) {
+ PredFlag mask = i + 1;
+ if (mi->pred_flag & mask) {
+ affine_cps_from_nb(lc, x, y, w, h, i, &mi->mv[i][0], motion_model_idc + 1);
+ }
+ mi->ref_idx[i] = mvf->ref_idx[i];
+ }
+ mi->motion_model_idc = motion_model_idc;
+ }
+ return motion_model_idc;
+}
+
+static int affine_merge_from_nbs(NeighbourContext *ctx, const NeighbourIdx *nbs, const int num_nbs, MotionInfo* cand)
+{
+ const VVCLocalContext *lc = ctx->lc;
+ const int is_mvp = 0;
+ for (int i = 0; i < num_nbs; i++) {
+ Neighbour *n = &ctx->neighbours[nbs[i]];
+ if (check_available(n, lc, is_mvp) && affine_merge_candidate(lc, n->x, n->y, cand))
+ return 1;
+ }
+ return 0;
+}
+#define AFFINE_MERGE_FROM_NBS(nbs) affine_merge_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), mi)
+
+
+static const MvField* derive_corner_mvf(NeighbourContext *ctx, const NeighbourIdx *neighbour, const int num_neighbour)
+{
+ const VVCFrameContext *fc = ctx->lc->fc;
+ const MvField *tab_mvf = fc->tab.mvf;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ for (int i = 0; i < num_neighbour; i++) {
+ Neighbour *n = &ctx->neighbours[neighbour[i]];
+ if (check_available(n, ctx->lc, 0)) {
+ return &TAB_MVF(n->x, n->y);
+ }
+ }
+ return NULL;
+}
+
+#define DERIVE_CORNER_MV(nbs) derive_corner_mvf(nctx, nbs, FF_ARRAY_ELEMS(nbs))
+
+// check if the mv's and refidx are the same between A and B
+static av_always_inline int compare_pf_ref_idx(const MvField *A, const struct MvField *B, const struct MvField *C, const int lx)
+{
+
+ const PredFlag mask = (lx + 1) & A->pred_flag;
+ if (!(B->pred_flag & mask))
+ return 0;
+ if (A->ref_idx[lx] != B->ref_idx[lx])
+ return 0;
+ if (C) {
+ if (!(C->pred_flag & mask))
+ return 0;
+ if (A->ref_idx[lx] != C->ref_idx[lx])
+ return 0;
+ }
+ return 1;
+}
+
+static av_always_inline void sb_clip_location(const VVCFrameContext *fc,
+ const int x_ctb, const int y_ctb, const Mv* temp_mv, int *x, int *y)
+{
+ const VVCPPS *pps = fc->ps.pps;
+ const int ctb_log2_size = fc->ps.sps->ctb_log2_size_y;
+ *y = av_clip(*y + temp_mv->y, y_ctb, FFMIN(pps->height - 1, y_ctb + (1 << ctb_log2_size) - 1)) & ~7;
+ *x = av_clip(*x + temp_mv->x, x_ctb, FFMIN(pps->width - 1, x_ctb + (1 << ctb_log2_size) + 3)) & ~7;
+}
+
+static void sb_temproal_luma_motion(const VVCLocalContext *lc,
+ const int x_ctb, const int y_ctb, const Mv *temp_mv,
+ int x, int y, uint8_t *pred_flag, Mv *mv)
+{
+ MvField temp_col;
+ Mv* mvLXCol;
+ const int refIdxLx = 0;
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSH *sh = &lc->sc->sh;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ VVCFrame *ref = fc->ref->collocated_ref;
+ MvField *tab_mvf = ref->tab_dmvr_mvf;
+ int colPic = ref->poc;
+ int X = 0;
+
+ sb_clip_location(fc, x_ctb, y_ctb, temp_mv, &x, &y);
+
+ temp_col = TAB_MVF(x, y);
+ mvLXCol = mv + 0;
+ *pred_flag = DERIVE_TEMPORAL_COLOCATED_MVS(1);
+ if (IS_B(sh->r)) {
+ X = 1;
+ mvLXCol = mv + 1;
+ *pred_flag |= (DERIVE_TEMPORAL_COLOCATED_MVS(1)) << 1;
+ }
+}
+
+//8.5.5.4 Derivation process for subblock-based temporal merging base motion data
+static int sb_temporal_luma_motion_data(const VVCLocalContext *lc, const MvField *a1,
+ const int x_ctb, const int y_ctb, MvField *ctr_mvf, Mv *temp_mv)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const RefPicList *rpl = lc->sc->rpl;
+ const CodingUnit *cu = lc->cu;
+ const int x = cu->x0 + cu->cb_width / 2;
+ const int y = cu->y0 + cu->cb_height / 2;
+ const VVCFrame *ref = fc->ref->collocated_ref;
+
+ int colPic;
+
+ memset(temp_mv, 0, sizeof(*temp_mv));
+
+ if (!ref) {
+ memset(ctr_mvf, 0, sizeof(*ctr_mvf));
+ return 0;
+ }
+
+ colPic = ref->poc;
+
+ AV_ZERO64(temp_mv);
+ if (a1) {
+ if ((a1->pred_flag & PF_L0) && colPic == rpl[0].list[a1->ref_idx[0]])
+ *temp_mv = a1->mv[0];
+ else if ((a1->pred_flag & PF_L1) && colPic == rpl[1].list[a1->ref_idx[1]])
+ *temp_mv = a1->mv[1];
+ ff_vvc_round_mv(temp_mv, 0, 4);
+ }
+ sb_temproal_luma_motion(lc, x_ctb, y_ctb, temp_mv, x, y, &ctr_mvf->pred_flag , ctr_mvf->mv);
+
+ return ctr_mvf->pred_flag;
+}
+
+
+//8.5.5.3 Derivation process for subblock-based temporal merging candidates
+static int sb_temporal_merge_candidate(const VVCLocalContext* lc, NeighbourContext *nctx, PredictionUnit *pu)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const VVCSPS *sps = fc->ps.sps;
+ const VVCPH *ph = &fc->ps.ph;
+ MotionInfo *mi = &pu->mi;
+ const int ctb_log2_size = sps->ctb_log2_size_y;
+ const int x0 = cu->x0;
+ const int y0 = cu->y0;
+ const NeighbourIdx n = A1;
+ const MvField *a1;
+ MvField ctr_mvf;
+ Mv temp_mv;
+ const int x_ctb = (x0 >> ctb_log2_size) << ctb_log2_size;
+ const int y_ctb = (y0 >> ctb_log2_size) << ctb_log2_size;
+
+
+ if (!ph->r->ph_temporal_mvp_enabled_flag ||
+ !sps->r->sps_sbtmvp_enabled_flag ||
+ (cu->cb_width < 8 && cu->cb_height < 8))
+ return 0;
+
+ mi->num_sb_x = cu->cb_width >> 3;
+ mi->num_sb_y = cu->cb_height >> 3;
+
+ a1 = derive_corner_mvf(nctx, &n, 1);
+ if (sb_temporal_luma_motion_data(lc, a1, x_ctb, y_ctb, &ctr_mvf, &temp_mv)) {
+ const int sbw = cu->cb_width / mi->num_sb_x;
+ const int sbh = cu->cb_height / mi->num_sb_y;
+ MvField mvf = {0};
+ for (int sby = 0; sby < mi->num_sb_y; sby++) {
+ for (int sbx = 0; sbx < mi->num_sb_x; sbx++) {
+ int x = x0 + sbx * sbw;
+ int y = y0 + sby * sbh;
+ sb_temproal_luma_motion(lc, x_ctb, y_ctb, &temp_mv, x + sbw / 2, y + sbh / 2, &mvf.pred_flag, mvf.mv);
+ if (!mvf.pred_flag) {
+ mvf.pred_flag = ctr_mvf.pred_flag;
+ memcpy(mvf.mv, ctr_mvf.mv, sizeof(mvf.mv));
+ }
+ ff_vvc_set_mvf(lc, x, y, sbw, sbh, &mvf);
+ }
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static int affine_merge_const1(const MvField *c0, const MvField *c1, const MvField *c2, MotionInfo *mi)
+{
+ if (c0 && c1 && c2) {
+ mi->pred_flag = 0;
+ for (int i = 0; i < 2; i++) {
+ PredFlag mask = i + 1;
+ if (compare_pf_ref_idx(c0, c1, c2, i)) {
+ mi->pred_flag |= mask;
+ mi->ref_idx[i] = c0->ref_idx[i];
+ mi->mv[i][0] = c0->mv[i];
+ mi->mv[i][1] = c1->mv[i];
+ mi->mv[i][2] = c2->mv[i];
+ }
+ }
+ if (mi->pred_flag) {
+ if (mi->pred_flag == PF_BI)
+ mi->bcw_idx = c0->bcw_idx;
+ mi->motion_model_idc = MOTION_6_PARAMS_AFFINE;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int affine_merge_const2(const MvField *c0, const MvField *c1, const MvField *c3, MotionInfo *mi)
+{
+ if (c0 && c1 && c3) {
+ mi->pred_flag = 0;
+ for (int i = 0; i < 2; i++) {
+ PredFlag mask = i + 1;
+ if (compare_pf_ref_idx(c0, c1, c3, i)) {
+ mi->pred_flag |= mask;
+ mi->ref_idx[i] = c0->ref_idx[i];
+ mi->mv[i][0] = c0->mv[i];
+ mi->mv[i][1] = c1->mv[i];
+ mi->mv[i][2].x = c3->mv[i].x + c0->mv[i].x - c1->mv[i].x;
+ mi->mv[i][2].y = c3->mv[i].y + c0->mv[i].y - c1->mv[i].y;
+ ff_vvc_clip_mv(&mi->mv[i][2]);
+ }
+ }
+ if (mi->pred_flag) {
+ mi->bcw_idx = mi->pred_flag == PF_BI ? c0->bcw_idx : 0;
+ mi->motion_model_idc = MOTION_6_PARAMS_AFFINE;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int affine_merge_const3(const MvField *c0, const MvField *c2, const MvField *c3, MotionInfo *mi)
+{
+ if (c0 && c2 && c3) {
+ mi->pred_flag = 0;
+ for (int i = 0; i < 2; i++) {
+ PredFlag mask = i + 1;
+ if (compare_pf_ref_idx(c0, c2, c3, i)) {
+ mi->pred_flag |= mask;
+ mi->ref_idx[i] = c0->ref_idx[i];
+ mi->mv[i][0] = c0->mv[i];
+ mi->mv[i][1].x = c3->mv[i].x + c0->mv[i].x - c2->mv[i].x;
+ mi->mv[i][1].y = c3->mv[i].y + c0->mv[i].y - c2->mv[i].y;
+ ff_vvc_clip_mv(&mi->mv[i][1]);
+ mi->mv[i][2] = c2->mv[i];
+ }
+ }
+ if (mi->pred_flag) {
+ mi->bcw_idx = mi->pred_flag == PF_BI ? c0->bcw_idx : 0;
+ mi->motion_model_idc = MOTION_6_PARAMS_AFFINE;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int affine_merge_const4(const MvField *c1, const MvField *c2, const MvField *c3, MotionInfo *mi)
+{
+ if (c1 && c2 && c3) {
+ mi->pred_flag = 0;
+ for (int i = 0; i < 2; i++) {
+ PredFlag mask = i + 1;
+ if (compare_pf_ref_idx(c1, c2, c3, i)) {
+ mi->pred_flag |= mask;
+ mi->ref_idx[i] = c1->ref_idx[i];
+ mi->mv[i][0].x = c1->mv[i].x + c2->mv[i].x - c3->mv[i].x;
+ mi->mv[i][0].y = c1->mv[i].y + c2->mv[i].y - c3->mv[i].y;
+ ff_vvc_clip_mv(&mi->mv[i][0]);
+ mi->mv[i][1] = c1->mv[i];
+ mi->mv[i][2] = c2->mv[i];
+ }
+ }
+ if (mi->pred_flag) {
+ mi->bcw_idx = mi->pred_flag == PF_BI ? c1->bcw_idx : 0;
+ mi->motion_model_idc = MOTION_6_PARAMS_AFFINE;
+ return 1;
+ }
+ }
+ return 0;
+
+}
+
+static int affine_merge_const5(const MvField *c0, const MvField *c1, MotionInfo *mi)
+{
+ if (c0 && c1) {
+ mi->pred_flag = 0;
+ for (int i = 0; i < 2; i++) {
+ PredFlag mask = i + 1;
+ if (compare_pf_ref_idx(c0, c1, NULL, i)) {
+ mi->pred_flag |= mask;
+ mi->ref_idx[i] = c0->ref_idx[i];
+ mi->mv[i][0] = c0->mv[i];
+ mi->mv[i][1] = c1->mv[i];
+ }
+ }
+ if (mi->pred_flag) {
+ if (mi->pred_flag == PF_BI)
+ mi->bcw_idx = c0->bcw_idx;
+ mi->motion_model_idc = MOTION_4_PARAMS_AFFINE;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int affine_merge_const6(const MvField* c0, const MvField* c2, const int cb_width, const int cb_height, MotionInfo *mi)
+{
+ if (c0 && c2) {
+ const int shift = 7 + av_log2(cb_width) - av_log2(cb_height);
+ mi->pred_flag = 0;
+ for (int i = 0; i < 2; i++) {
+ PredFlag mask = i + 1;
+ if (compare_pf_ref_idx(c0, c2, NULL, i)) {
+ mi->pred_flag |= mask;
+ mi->ref_idx[i] = c0->ref_idx[i];
+ mi->mv[i][0] = c0->mv[i];
+ mi->mv[i][1].x = (c0->mv[i].x << 7) + ((c2->mv[i].y - c0->mv[i].y) << shift);
+ mi->mv[i][1].y = (c0->mv[i].y << 7) - ((c2->mv[i].x - c0->mv[i].x) << shift);
+ ff_vvc_round_mv(&mi->mv[i][1], 0, 7);
+ ff_vvc_clip_mv(&mi->mv[i][1]);
+ }
+ }
+ if (mi->pred_flag) {
+ if (mi->pred_flag == PF_BI)
+ mi->bcw_idx = c0->bcw_idx;
+ mi->motion_model_idc = MOTION_4_PARAMS_AFFINE;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void affine_merge_zero_motion(const VVCLocalContext *lc, MotionInfo *mi)
+{
+ const CodingUnit *cu = lc->cu;
+
+ memset(mi, 0, sizeof(*mi));
+ mi->pred_flag = PF_L0 + (IS_B(lc->sc->sh.r) << 1);
+ mi->motion_model_idc = MOTION_4_PARAMS_AFFINE;
+ mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2;
+ mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2;
+}
+
+//8.5.5.6 Derivation process for constructed affine control point motion vector merging candidates
+static int affine_merge_const_candidates(const VVCLocalContext *lc, MotionInfo *mi,
+ NeighbourContext *nctx, const int merge_subblock_idx, int num_cands)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const NeighbourIdx tl[] = { B2, B3, A2 };
+ const NeighbourIdx tr[] = { B1, B0};
+ const NeighbourIdx bl[] = { A1, A0};
+ const MvField *c0, *c1, *c2;
+
+ c0 = DERIVE_CORNER_MV(tl);
+ c1 = DERIVE_CORNER_MV(tr);
+ c2 = DERIVE_CORNER_MV(bl);
+
+ if (fc->ps.sps->r->sps_6param_affine_enabled_flag) {
+ MvField corner3, *c3 = NULL;
+ //Const1
+ if (affine_merge_const1(c0, c1, c2, mi)) {
+ if (merge_subblock_idx == num_cands)
+ return 1;
+ num_cands++;
+ }
+
+ memset(&corner3, 0, sizeof(corner3));
+ if (fc->ps.ph.r->ph_temporal_mvp_enabled_flag){
+ const int available_l0 = temporal_luma_motion_vector(lc, 0, corner3.mv + 0, 0, 0, 0);
+ const int available_l1 = (lc->sc->sh.r->sh_slice_type == VVC_SLICE_TYPE_B) ?
+ temporal_luma_motion_vector(lc, 0, corner3.mv + 1, 1, 0, 0) : 0;
+
+ corner3.pred_flag = available_l0 + (available_l1 << 1);
+ if (corner3.pred_flag)
+ c3 = &corner3;
+ }
+
+ //Const2
+ if (affine_merge_const2(c0, c1, c3, mi)) {
+ if (merge_subblock_idx == num_cands)
+ return 1;
+ num_cands++;
+ }
+
+ //Const3
+ if (affine_merge_const3(c0, c2, c3, mi)) {
+ if (merge_subblock_idx == num_cands)
+ return 1;
+ num_cands++;
+ }
+
+ //Const4
+ if (affine_merge_const4(c1, c2, c3, mi)) {
+ if (merge_subblock_idx == num_cands)
+ return 1;
+ num_cands++;
+ }
+ }
+
+ //Const5
+ if (affine_merge_const5(c0, c1, mi)) {
+ if (merge_subblock_idx == num_cands)
+ return 1;
+ num_cands++;
+ }
+
+ if (affine_merge_const6(c0, c2, cu->cb_width, cu->cb_height, mi)) {
+ if (merge_subblock_idx == num_cands)
+ return 1;
+ }
+ return 0;
+}
+
+//8.5.5.2 Derivation process for motion vectors and reference indices in subblock merge mode
+//return 1 if candidate is SbCol
+static int sb_mv_merge_mode(const VVCLocalContext *lc, const int merge_subblock_idx, PredictionUnit *pu)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const CodingUnit *cu = lc->cu;
+ MotionInfo *mi = &pu->mi;
+ int num_cands = 0;
+ NeighbourContext nctx;
+
+ init_neighbour_context(&nctx, lc);
+
+ //SbCol
+ if (sb_temporal_merge_candidate(lc, &nctx, pu)) {
+ if (merge_subblock_idx == num_cands)
+ return 1;
+ num_cands++;
+ }
+
+ pu->inter_affine_flag = 1;
+ mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2;
+ mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2;
+
+ if (sps->r->sps_affine_enabled_flag) {
+ const NeighbourIdx ak[] = { A0, A1 };
+ const NeighbourIdx bk[] = { B0, B1, B2 };
+ //A
+ if (AFFINE_MERGE_FROM_NBS(ak)) {
+ if (merge_subblock_idx == num_cands)
+ return 0;
+ num_cands++;
+ }
+
+ //B
+ if (AFFINE_MERGE_FROM_NBS(bk)) {
+ if (merge_subblock_idx == num_cands)
+ return 0;
+ num_cands++;
+ }
+
+ //Const1 to Const6
+ if (affine_merge_const_candidates(lc, mi, &nctx, merge_subblock_idx, num_cands))
+ return 0;
+ }
+ //Zero
+ affine_merge_zero_motion(lc, mi);
+ return 0;
+}
+
+void ff_vvc_sb_mv_merge_mode(VVCLocalContext *lc, const int merge_subblock_idx, PredictionUnit *pu)
+{
+ const CodingUnit *cu = lc->cu;
+ ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+ if (!sb_mv_merge_mode(lc, merge_subblock_idx, pu)) {
+ ff_vvc_store_sb_mvs(lc, pu);
+ }
+}
+
+static int mvp_candidate(const VVCLocalContext *lc, const int x_cand, const int y_cand,
+ const int lx, const int8_t *ref_idx, Mv *mv)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const RefPicList *rpl = lc->sc->rpl;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const MvField* tab_mvf = fc->tab.mvf;
+ const MvField *mvf = &TAB_MVF(x_cand, y_cand);
+ const PredFlag maskx = lx + 1;
+ const int poc = rpl[lx].list[ref_idx[lx]];
+ int available = 0;
+
+ if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) {
+ available = 1;
+ *mv = mvf->mv[lx];
+ } else {
+ const int ly = !lx;
+ const PredFlag masky = ly + 1;
+ if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) {
+ available = 1;
+ *mv = mvf->mv[ly];
+ }
+ }
+
+ return available;
+}
+
+static int affine_mvp_candidate(const VVCLocalContext *lc,
+ const int x_cand, const int y_cand, const int lx, const int8_t *ref_idx,
+ Mv *cps, const int num_cp)
+{
+ const VVCFrameContext *fc = lc->fc;
+ int x_nb, y_nb, nbw, nbh, motion_model_idc, available = 0;
+
+ motion_model_idc = affine_neighbour_cb(fc, x_cand, y_cand, &x_nb, &y_nb, &nbw, &nbh);
+ if (motion_model_idc) {
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const MvField* tab_mvf = fc->tab.mvf;
+ const MvField *mvf = &TAB_MVF(x_nb, y_nb);
+ RefPicList* rpl = lc->sc->rpl;
+ const PredFlag maskx = lx + 1;
+ const int poc = rpl[lx].list[ref_idx[lx]];
+
+ if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) {
+ available = 1;
+ affine_cps_from_nb(lc, x_nb, y_nb, nbw, nbh, lx, cps, num_cp);
+ } else {
+ const int ly = !lx;
+ const PredFlag masky = ly + 1;
+ if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) {
+ available = 1;
+ affine_cps_from_nb(lc, x_nb, y_nb, nbw, nbh, ly, cps, num_cp);
+ }
+ }
+
+ }
+ return available;
+}
+
+static int mvp_from_nbs(NeighbourContext *ctx,
+ const NeighbourIdx *nbs, const int num_nbs, const int lx, const int8_t *ref_idx, const int amvr_shift,
+ Mv *cps, const int num_cps)
+{
+ const VVCLocalContext *lc = ctx->lc;
+ const int is_mvp = 1;
+ int available = 0;
+
+ for (int i = 0; i < num_nbs; i++) {
+ Neighbour *n = &ctx->neighbours[nbs[i]];
+ if (check_available(n, lc, is_mvp)) {
+ if (num_cps > 1)
+ available = affine_mvp_candidate(lc, n->x, n->y, lx, ref_idx, cps, num_cps);
+ else
+ available = mvp_candidate(lc, n->x, n->y, lx, ref_idx, cps);
+ if (available) {
+ for (int c = 0; c < num_cps; c++)
+ ff_vvc_round_mv(cps + c, amvr_shift, amvr_shift);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+//get mvp from neighbours
+#define AFFINE_MVP_FROM_NBS(nbs) \
+ mvp_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), lx, ref_idx, amvr_shift, cps, num_cp) \
+
+#define MVP_FROM_NBS(nbs) \
+ mvp_from_nbs(&nctx, nbs, FF_ARRAY_ELEMS(nbs), lx, ref_idx, amvr_shift, mv, 1) \
+
+static int mvp_spatial_candidates(const VVCLocalContext *lc,
+ const int mvp_lx_flag, const int lx, const int8_t* ref_idx, const int amvr_shift,
+ Mv* mv, int *nb_merge_cand)
+{
+ const NeighbourIdx ak[] = { A0, A1 };
+ const NeighbourIdx bk[] = { B0, B1, B2 };
+ NeighbourContext nctx;
+ int available_a, num_cands = 0;
+ Mv mv_a;
+
+ init_neighbour_context(&nctx, lc);
+
+ available_a = MVP_FROM_NBS(ak);
+ if (available_a) {
+ if (mvp_lx_flag == num_cands)
+ return 1;
+ num_cands++;
+ mv_a = *mv;
+ }
+ if (MVP_FROM_NBS(bk)) {
+ if (!available_a || !IS_SAME_MV(&mv_a, mv)) {
+ if (mvp_lx_flag == num_cands)
+ return 1;
+ num_cands++;
+ }
+ }
+ *nb_merge_cand = num_cands;
+ return 0;
+}
+
+static int mvp_temporal_candidates(const VVCLocalContext* lc,
+ const int mvp_lx_flag, const int lx, const int8_t *ref_idx, const int amvr_shift,
+ Mv* mv, int *num_cands)
+{
+ if (temporal_luma_motion_vector(lc, ref_idx[lx], mv, lx, 1, 0)) {
+ if (mvp_lx_flag == *num_cands) {
+ ff_vvc_round_mv(mv, amvr_shift, amvr_shift);
+ return 1;
+ }
+ (*num_cands)++;
+ }
+ return 0;
+
+}
+
+static int mvp_history_candidates(const VVCLocalContext *lc,
+ const int mvp_lx_flag, const int lx, const int8_t ref_idx, const int amvr_shift,
+ Mv *mv, int num_cands)
+{
+ const EntryPoint* ep = lc->ep;
+ const RefPicList* rpl = lc->sc->rpl;
+ const int poc = rpl[lx].list[ref_idx];
+
+ if (ep->num_hmvp == 0)
+ return 0;
+ for (int i = 1; i <= FFMIN(4, ep->num_hmvp); i++) {
+ const MvField* h = &ep->hmvp[i - 1];
+ for (int j = 0; j < 2; j++) {
+ const int ly = (j ? !lx : lx);
+ PredFlag mask = PF_L0 + ly;
+ if ((h->pred_flag & mask) && poc == rpl[ly].list[h->ref_idx[ly]]) {
+ if (mvp_lx_flag == num_cands) {
+ *mv = h->mv[ly];
+ ff_vvc_round_mv(mv, amvr_shift, amvr_shift);
+ return 1;
+ }
+ num_cands++;
+ }
+ }
+ }
+ return 0;
+}
+
+//8.5.2.8 Derivation process for luma motion vector prediction
+static void mvp(const VVCLocalContext *lc, const int mvp_lx_flag, const int lx,
+ const int8_t *ref_idx, const int amvr_shift, Mv *mv)
+{
+ int num_cands;
+
+ if (mvp_spatial_candidates(lc, mvp_lx_flag, lx, ref_idx, amvr_shift, mv, &num_cands))
+ return;
+
+ if (mvp_temporal_candidates(lc, mvp_lx_flag, lx, ref_idx, amvr_shift, mv, &num_cands))
+ return;
+
+ if (mvp_history_candidates(lc, mvp_lx_flag, lx, ref_idx[lx], amvr_shift, mv, num_cands))
+ return;
+
+ memset(mv, 0, sizeof(*mv));
+}
+
+void ff_vvc_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi)
+{
+ const CodingUnit *cu = lc->cu;
+ mi->num_sb_x = 1;
+ mi->num_sb_y = 1;
+
+ ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+ if (mi->pred_flag != PF_L1)
+ mvp(lc, mvp_lx_flag[L0], L0, mi->ref_idx, amvr_shift, &mi->mv[L0][0]);
+ if (mi->pred_flag != PF_L0)
+ mvp(lc, mvp_lx_flag[L1], L1, mi->ref_idx, amvr_shift, &mi->mv[L1][0]);
+}
+
+static int affine_mvp_constructed_cp(NeighbourContext *ctx,
+ const NeighbourIdx *neighbour, const int num_neighbour,
+ const int lx, const int8_t ref_idx, const int amvr_shift, Mv *cp)
+{
+ const VVCLocalContext *lc = ctx->lc;
+ const VVCFrameContext *fc = lc->fc;
+ const MvField *tab_mvf = fc->tab.mvf;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const RefPicList* rpl = lc->sc->rpl;
+ const int is_mvp = 1;
+ int available = 0;
+
+ for (int i = 0; i < num_neighbour; i++) {
+ Neighbour *n = &ctx->neighbours[neighbour[i]];
+ if (check_available(n, ctx->lc, is_mvp)) {
+ const PredFlag maskx = lx + 1;
+ const MvField* mvf = &TAB_MVF(n->x, n->y);
+ const int poc = rpl[lx].list[ref_idx];
+ if ((mvf->pred_flag & maskx) && rpl[lx].list[mvf->ref_idx[lx]] == poc) {
+ available = 1;
+ *cp = mvf->mv[lx];
+ } else {
+ const int ly = !lx;
+ const PredFlag masky = ly + 1;
+ if ((mvf->pred_flag & masky) && rpl[ly].list[mvf->ref_idx[ly]] == poc) {
+ available = 1;
+ *cp = mvf->mv[ly];
+ }
+ }
+ if (available) {
+ ff_vvc_round_mv(cp, amvr_shift, amvr_shift);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+#define AFFINE_MVP_CONSTRUCTED_CP(cands, cp) \
+ affine_mvp_constructed_cp(nctx, cands, FF_ARRAY_ELEMS(cands), lx, ref_idx, \
+ amvr_shift, cp)
+
+//8.5.5.8 Derivation process for constructed affine control point motion vector prediction candidates
+static int affine_mvp_const1(NeighbourContext* nctx,
+ const int lx, const int8_t ref_idx, const int amvr_shift,
+ Mv *cps, int *available)
+{
+ const NeighbourIdx tl[] = { B2, B3, A2 };
+ const NeighbourIdx tr[] = { B1, B0 };
+ const NeighbourIdx bl[] = { A1, A0 };
+
+ available[0] = AFFINE_MVP_CONSTRUCTED_CP(tl, cps + 0);
+ available[1] = AFFINE_MVP_CONSTRUCTED_CP(tr, cps + 1);
+ available[2] = AFFINE_MVP_CONSTRUCTED_CP(bl, cps + 2);
+ return available[0] && available[1];
+}
+
+//8.5.5.7 item 7
+static void affine_mvp_const2(const int idx, Mv *cps, const int num_cp)
+{
+ const Mv mv = cps[idx];
+ for (int j = 0; j < num_cp; j++)
+ cps[j] = mv;
+}
+
+//8.5.5.7 Derivation process for luma affine control point motion vector predictors
+static void affine_mvp(const VVCLocalContext *lc,
+ const int mvp_lx_flag, const int lx, const int8_t *ref_idx, const int amvr_shift,
+ MotionModelIdc motion_model_idc, Mv *cps)
+{
+ const NeighbourIdx ak[] = { A0, A1 };
+ const NeighbourIdx bk[] = { B0, B1, B2 };
+ const int num_cp = motion_model_idc + 1;
+ NeighbourContext nctx;
+ int available[MAX_CONTROL_POINTS];
+ int num_cands = 0;
+
+ init_neighbour_context(&nctx, lc);
+ //Ak
+ if (AFFINE_MVP_FROM_NBS(ak)) {
+ if (mvp_lx_flag == num_cands)
+ return;
+ num_cands++;
+ }
+ //Bk
+ if (AFFINE_MVP_FROM_NBS(bk)) {
+ if (mvp_lx_flag == num_cands)
+ return;
+ num_cands++;
+ }
+
+ //Const1
+ if (affine_mvp_const1(&nctx, lx, ref_idx[lx], amvr_shift, cps, available)) {
+ if (available[2] || motion_model_idc == MOTION_4_PARAMS_AFFINE) {
+ if (mvp_lx_flag == num_cands)
+ return;
+ num_cands++;
+ }
+ }
+
+ //Const2
+ for (int i = 2; i >= 0; i--) {
+ if (available[i]) {
+ if (mvp_lx_flag == num_cands) {
+ affine_mvp_const2(i, cps, num_cp);
+ return;
+ }
+ num_cands++;
+ }
+ }
+ if (temporal_luma_motion_vector(lc, ref_idx[lx], cps, lx, 1, 0)) {
+ if (mvp_lx_flag == num_cands) {
+ ff_vvc_round_mv(cps, amvr_shift, amvr_shift);
+ for (int i = 1; i < num_cp; i++)
+ cps[i] = cps[0];
+ return;
+ }
+ num_cands++;
+ }
+
+ //Zero Mv
+ memset(cps, 0, num_cp * sizeof(Mv));
+}
+
+void ff_vvc_affine_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi)
+{
+ const CodingUnit *cu = lc->cu;
+
+ mi->num_sb_x = cu->cb_width >> MIN_PU_LOG2;
+ mi->num_sb_y = cu->cb_height >> MIN_PU_LOG2;
+
+ ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+ if (mi->pred_flag != PF_L1)
+ affine_mvp(lc, mvp_lx_flag[L0], L0, mi->ref_idx, amvr_shift, mi->motion_model_idc, &mi->mv[L0][0]);
+ if (mi->pred_flag != PF_L0)
+ affine_mvp(lc, mvp_lx_flag[L1], L1, mi->ref_idx, amvr_shift, mi->motion_model_idc, &mi->mv[L1][0]);
+}
+
+//8.5.2.14 Rounding process for motion vectors
+void ff_vvc_round_mv(Mv *mv, const int lshift, const int rshift)
+{
+ if (rshift) {
+ const int offset = 1 << (rshift - 1);
+ mv->x = ((mv->x + offset - (mv->x >= 0)) >> rshift) << lshift;
+ mv->y = ((mv->y + offset - (mv->y >= 0)) >> rshift) << lshift;
+ } else {
+ mv->x = mv->x << lshift;
+ mv->y = mv->y << lshift;
+ }
+}
+
+void ff_vvc_clip_mv(Mv *mv)
+{
+ mv->x = av_clip(mv->x, -(1 << 17), (1 << 17) - 1);
+ mv->y = av_clip(mv->y, -(1 << 17), (1 << 17) - 1);
+}
+
+//8.5.2.1 Derivation process for motion vector components and reference indices
+static av_always_inline int is_greater_mer(const VVCFrameContext *fc, const int x0, const int y0, const int x0_br, const int y0_br)
+{
+ const uint8_t plevel = fc->ps.sps->log2_parallel_merge_level;
+
+ return x0_br >> plevel > x0 >> plevel &&
+ y0_br >> plevel > y0 >> plevel;
+}
+
+//8.5.2.16 Updating process for the history-based motion vector predictor candidate list
+void ff_vvc_update_hmvp(VVCLocalContext *lc, const MotionInfo *mi)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const MvField* tab_mvf = fc->tab.mvf;
+ EntryPoint* ep = lc->ep;
+ const MvField *mvf;
+ int i;
+
+ if (!is_greater_mer(fc, cu->x0, cu->y0, cu->x0 + cu->cb_width, cu->y0 + cu->cb_height))
+ return;
+ mvf = &TAB_MVF(cu->x0, cu->y0);
+
+ for (i = 0; i < ep->num_hmvp; i++) {
+ if (compare_mv_ref_idx(mvf, ep->hmvp + i)) {
+ ep->num_hmvp--;
+ break;
+ }
+ }
+ if (i == MAX_NUM_HMVP_CANDS) {
+ ep->num_hmvp--;
+ i = 0;
+ }
+
+ memmove(ep->hmvp + i, ep->hmvp + i + 1, (ep->num_hmvp - i) * sizeof(MvField));
+ ep->hmvp[ep->num_hmvp++] = *mvf;
+}
+
+MvField* ff_vvc_get_mvf(const VVCFrameContext *fc, const int x0, const int y0)
+{
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ MvField* tab_mvf = fc->tab.mvf;
+
+ return &TAB_MVF(x0, y0);
+}
diff --git a/libavcodec/vvc/vvc_mvs.h b/libavcodec/vvc/vvc_mvs.h
new file mode 100644
index 0000000000..6c46f9fdb2
--- /dev/null
+++ b/libavcodec/vvc/vvc_mvs.h
@@ -0,0 +1,46 @@
+/*
+ * VVC motion vector decoder
+ *
+ * Copyright (C) 2023 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVC_MVS_H
+#define AVCODEC_VVC_VVC_MVS_H
+
+#include "vvcdec.h"
+
+void ff_vvc_round_mv(Mv *mv, int lshift, int rshift);
+void ff_vvc_clip_mv(Mv *mv);
+void ff_vvc_mv_scale(Mv *dst, const Mv *src, int td, int tb);
+void ff_vvc_luma_mv_merge_mode(VVCLocalContext *lc, int merge_idx, int ciip_flag, MvField *mv);
+void ff_vvc_luma_mv_merge_gpm(VVCLocalContext *lc, const int merge_gpm_idx[2], MvField *mv);
+void ff_vvc_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo *mi);
+void ff_vvc_sb_mv_merge_mode(VVCLocalContext *lc, int merge_subblock_idx, PredictionUnit *pu);
+void ff_vvc_affine_mvp(VVCLocalContext *lc, const int *mvp_lx_flag, const int amvr_shift, MotionInfo* mi);
+void ff_vvc_store_sb_mvs(const VVCLocalContext *lc, PredictionUnit *pu);
+void ff_vvc_store_mv(const VVCLocalContext *lc, const MotionInfo *mi);
+void ff_vvc_store_mvf(const VVCLocalContext *lc, const MvField *mvf);
+void ff_vvc_store_gpm_mvf(const VVCLocalContext *lc, const PredictionUnit* pu);
+void ff_vvc_update_hmvp(VVCLocalContext *lc, const MotionInfo *mi);
+int ff_vvc_no_backward_pred_flag(const VVCLocalContext *lc);
+MvField* ff_vvc_get_mvf(const VVCFrameContext *fc, const int x0, const int y0);
+void ff_vvc_set_mvf(const VVCLocalContext *lc, const int x0, const int y0, const int w, const int h, const MvField *mvf);
+void ff_vvc_set_intra_mvf(const VVCLocalContext *lc);
+
+#endif //AVCODEC_VVC_VVC_MVS_H
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH v7 07/14] vvcdec: add inter prediction
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
` (4 preceding siblings ...)
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 06/14] vvcdec: add motion vector decoder Nuo Mi
@ 2023-12-10 15:58 ` Nuo Mi
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 08/14] vvcdec: add inv transform 1d Nuo Mi
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 15:58 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
---
libavcodec/vvc/Makefile | 1 +
libavcodec/vvc/vvc_inter.c | 939 ++++++++++++++++++++++++
libavcodec/vvc/vvc_inter.h | 42 ++
libavcodec/vvc/vvc_inter_template.c | 1023 +++++++++++++++++++++++++++
libavcodec/vvc/vvcdec.h | 5 +
libavcodec/vvc/vvcdsp.h | 170 +++++
6 files changed, 2180 insertions(+)
create mode 100644 libavcodec/vvc/vvc_inter.c
create mode 100644 libavcodec/vvc/vvc_inter.h
create mode 100644 libavcodec/vvc/vvc_inter_template.c
create mode 100644 libavcodec/vvc/vvcdsp.h
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index 912e9f516c..61b1ab39de 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -5,6 +5,7 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
vvc/vvc_cabac.o \
vvc/vvc_ctu.o \
vvc/vvc_data.o \
+ vvc/vvc_inter.o \
vvc/vvc_mvs.o \
vvc/vvc_ps.o \
vvc/vvc_refs.o \
diff --git a/libavcodec/vvc/vvc_inter.c b/libavcodec/vvc/vvc_inter.c
new file mode 100644
index 0000000000..c494089c6b
--- /dev/null
+++ b/libavcodec/vvc/vvc_inter.c
@@ -0,0 +1,939 @@
+/*
+ * VVC inter prediction
+ *
+ * Copyright (C) 2022 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "libavutil/frame.h"
+
+#include "vvc_data.h"
+#include "vvc_inter.h"
+#include "vvc_mvs.h"
+#include "vvc_refs.h"
+
+// +1 is enough, + 32 for asm alignment
+#define PROF_TEMP_OFFSET (MAX_PB_SIZE + 32)
+static const int bcw_w_lut[] = {4, 5, 3, 10, -2};
+
+static int emulated_edge(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride,
+ const int x_off, const int y_off, const int block_w, const int block_h, const int is_luma)
+{
+ const int extra_before = is_luma ? LUMA_EXTRA_BEFORE : CHROMA_EXTRA_BEFORE;
+ const int extra_after = is_luma ? LUMA_EXTRA_AFTER : CHROMA_EXTRA_AFTER;
+ const int extra = is_luma ? LUMA_EXTRA : CHROMA_EXTRA;
+ const int pic_width = is_luma ? fc->ps.pps->width : (fc->ps.pps->width >> fc->ps.sps->hshift[1]);
+ const int pic_height = is_luma ? fc->ps.pps->height : (fc->ps.pps->height >> fc->ps.sps->vshift[1]);
+
+ if (x_off < extra_before || y_off < extra_before ||
+ x_off >= pic_width - block_w - extra_after ||
+ y_off >= pic_height - block_h - extra_after) {
+ const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << fc->ps.sps->pixel_shift;
+ int offset = extra_before * *src_stride + (extra_before << fc->ps.sps->pixel_shift);
+ int buf_offset = extra_before * edge_emu_stride + (extra_before << fc->ps.sps->pixel_shift);
+
+ fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride,
+ block_w + extra, block_h + extra, x_off - extra_before, y_off - extra_before,
+ pic_width, pic_height);
+
+ *src = dst + buf_offset;
+ *src_stride = edge_emu_stride;
+ return 1;
+ }
+ return 0;
+}
+
+static void emulated_edge_dmvr(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride,
+ const int x_sb, const int y_sb, const int x_off, const int y_off, const int block_w, const int block_h, const int is_luma)
+{
+ const int extra_before = is_luma ? LUMA_EXTRA_BEFORE : CHROMA_EXTRA_BEFORE;
+ const int extra_after = is_luma ? LUMA_EXTRA_AFTER : CHROMA_EXTRA_AFTER;
+ const int extra = is_luma ? LUMA_EXTRA : CHROMA_EXTRA;
+ const int pic_width = is_luma ? fc->ps.pps->width : (fc->ps.pps->width >> fc->ps.sps->hshift[1]);
+ const int pic_height = is_luma ? fc->ps.pps->height : (fc->ps.pps->height >> fc->ps.sps->vshift[1]);
+
+ if (x_off < extra_before || y_off < extra_before ||
+ x_off >= pic_width - block_w - extra_after ||
+ y_off >= pic_height - block_h - extra_after||
+ (x_off != x_sb || y_off != y_sb)) {
+ const int ps = fc->ps.sps->pixel_shift;
+ const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << ps;
+ const int offset = extra_before * *src_stride + (extra_before << ps);
+ const int buf_offset = extra_before * edge_emu_stride + (extra_before << ps);
+
+ const int start_x = FFMIN(FFMAX(x_sb - extra_before, 0), pic_width - 1);
+ const int start_y = FFMIN(FFMAX(y_sb - extra_before, 0), pic_height - 1);
+ const int width = FFMAX(FFMIN(pic_width, x_sb + block_w + extra_after) - start_x, 1);
+ const int height = FFMAX(FFMIN(pic_height, y_sb + block_h + extra_after) - start_y, 1);
+
+ fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, block_w + extra, block_h + extra,
+ x_off - start_x - extra_before, y_off - start_y - extra_before, width, height);
+
+ *src = dst + buf_offset;
+ *src_stride = edge_emu_stride;
+ }
+}
+
+static void emulated_edge_bilinear(const VVCFrameContext *fc, uint8_t *dst, const uint8_t **src, ptrdiff_t *src_stride,
+ const int x_off, const int y_off, const int block_w, const int block_h)
+{
+ int pic_width = fc->ps.pps->width;
+ int pic_height = fc->ps.pps->height;
+
+ if (x_off < BILINEAR_EXTRA_BEFORE || y_off < BILINEAR_EXTRA_BEFORE ||
+ x_off >= pic_width - block_w - BILINEAR_EXTRA_AFTER ||
+ y_off >= pic_height - block_h - BILINEAR_EXTRA_AFTER) {
+ const ptrdiff_t edge_emu_stride = EDGE_EMU_BUFFER_STRIDE << fc->ps.sps->pixel_shift;
+ const int offset = BILINEAR_EXTRA_BEFORE * *src_stride + (BILINEAR_EXTRA_BEFORE << fc->ps.sps->pixel_shift);
+ const int buf_offset = BILINEAR_EXTRA_BEFORE * edge_emu_stride + (BILINEAR_EXTRA_BEFORE << fc->ps.sps->pixel_shift);
+
+ fc->vdsp.emulated_edge_mc(dst, *src - offset, edge_emu_stride, *src_stride, block_w + BILINEAR_EXTRA, block_h + BILINEAR_EXTRA,
+ x_off - BILINEAR_EXTRA_BEFORE, y_off - BILINEAR_EXTRA_BEFORE, pic_width, pic_height);
+
+ *src = dst + buf_offset;
+ *src_stride = edge_emu_stride;
+ }
+}
+
+
+#define EMULATED_EDGE_LUMA(dst, src, src_stride, x_off, y_off) \
+ emulated_edge(fc, dst, src, src_stride, x_off, y_off, block_w, block_h, 1)
+
+#define EMULATED_EDGE_CHROMA(dst, src, src_stride, x_off, y_off) \
+ emulated_edge(fc, dst, src, src_stride, x_off, y_off, block_w, block_h, 0)
+
+#define EMULATED_EDGE_DMVR_LUMA(dst, src, src_stride, x_sb, y_sb, x_off, y_off) \
+ emulated_edge_dmvr(fc, dst, src, src_stride, x_sb, y_sb, x_off, y_off, block_w, block_h, 1)
+
+#define EMULATED_EDGE_DMVR_CHROMA(dst, src, src_stride, x_sb, y_sb, x_off, y_off) \
+ emulated_edge_dmvr(fc, dst, src, src_stride, x_sb, y_sb, x_off, y_off, block_w, block_h, 0)
+
+#define EMULATED_EDGE_BILINEAR(dst, src, src_stride, x_off, y_off) \
+ emulated_edge_bilinear(fc, dst, src, src_stride, x_off, y_off, pred_w, pred_h)
+
+// part of 8.5.6.6 Weighted sample prediction process
+static int derive_weight_uni(int *denom, int *wx, int *ox,
+ const VVCLocalContext *lc, const MvField *mvf, const int c_idx)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCPPS *pps = fc->ps.pps;
+ const VVCSH *sh = &lc->sc->sh;
+ const int weight_flag = (IS_P(sh->r) && pps->r->pps_weighted_pred_flag) ||
+ (IS_B(sh->r) && pps->r->pps_weighted_bipred_flag);
+ if (weight_flag) {
+ const int lx = mvf->pred_flag - PF_L0;
+ const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt;
+
+ *denom = w->log2_denom[c_idx > 0];
+ *wx = w->weight[lx][c_idx][mvf->ref_idx[lx]];
+ *ox = w->offset[lx][c_idx][mvf->ref_idx[lx]];
+ }
+ return weight_flag;
+}
+
+// part of 8.5.6.6 Weighted sample prediction process
+static int derive_weight(int *denom, int *w0, int *w1, int *o0, int *o1,
+ const VVCLocalContext *lc, const MvField *mvf, const int c_idx, const int dmvr_flag)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCPPS *pps = fc->ps.pps;
+ const VVCSH *sh = &lc->sc->sh;
+ const int bcw_idx = mvf->bcw_idx;
+ const int weight_flag = (IS_P(sh->r) && pps->r->pps_weighted_pred_flag) ||
+ (IS_B(sh->r) && pps->r->pps_weighted_bipred_flag && !dmvr_flag);
+ if ((!weight_flag && !bcw_idx) || (bcw_idx && lc->cu->ciip_flag))
+ return 0;
+
+ if (bcw_idx) {
+ *denom = 2;
+ *w1 = bcw_w_lut[bcw_idx];
+ *w0 = 8 - *w1;
+ *o0 = *o1 = 0;
+ } else {
+ const VVCPPS *pps = fc->ps.pps;
+ const PredWeightTable *w = pps->r->pps_wp_info_in_ph_flag ? &fc->ps.ph.pwt : &sh->pwt;
+
+ *denom = w->log2_denom[c_idx > 0];
+ *w0 = w->weight[L0][c_idx][mvf->ref_idx[L0]];
+ *w1 = w->weight[L1][c_idx][mvf->ref_idx[L1]];
+ *o0 = w->offset[L0][c_idx][mvf->ref_idx[L0]];
+ *o1 = w->offset[L1][c_idx][mvf->ref_idx[L1]];
+ }
+ return 1;
+}
+
+static void luma_mc(VVCLocalContext *lc, int16_t *dst, const AVFrame *ref, const Mv *mv,
+ int x_off, int y_off, const int block_w, const int block_h)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const uint8_t *src = ref->data[0];
+ ptrdiff_t src_stride = ref->linesize[0];
+ const int idx = av_log2(block_w) - 1;
+ const int mx = mv->x & 0xf;
+ const int my = mv->y & 0xf;
+ const int8_t *hf = ff_vvc_inter_luma_filters[0][mx];
+ const int8_t *vf = ff_vvc_inter_luma_filters[0][my];
+
+ x_off += mv->x >> 4;
+ y_off += mv->y >> 4;
+ src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift);
+
+ EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off);
+
+ fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](dst, src, src_stride, block_h, hf, vf, block_w);
+}
+
+static void chroma_mc(VVCLocalContext *lc, int16_t *dst, const AVFrame *ref, const Mv *mv,
+ int x_off, int y_off, const int block_w, const int block_h, const int c_idx)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const uint8_t *src = ref->data[c_idx];
+ ptrdiff_t src_stride = ref->linesize[c_idx];
+ int hs = fc->ps.sps->hshift[c_idx];
+ int vs = fc->ps.sps->vshift[c_idx];
+ const int idx = av_log2(block_w) - 1;
+ const intptr_t mx = av_mod_uintp2(mv->x, 4 + hs) << (1 - hs);
+ const intptr_t my = av_mod_uintp2(mv->y, 4 + vs) << (1 - vs);
+ const int8_t *hf = ff_vvc_inter_chroma_filters[0][mx];
+ const int8_t *vf = ff_vvc_inter_chroma_filters[0][my];
+
+ x_off += mv->x >> (4 + hs);
+ y_off += mv->y >> (4 + vs);
+ src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift);
+
+ EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off);
+ fc->vvcdsp.inter.put[CHROMA][idx][!!my][!!mx](dst, src, src_stride, block_h, hf, vf, block_w);
+}
+
+static void luma_mc_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride,
+ const AVFrame *ref, const MvField *mvf, int x_off, int y_off, const int block_w, const int block_h,
+ const int hf_idx, const int vf_idx)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const int lx = mvf->pred_flag - PF_L0;
+ const Mv *mv = mvf->mv + lx;
+ const uint8_t *src = ref->data[0];
+ ptrdiff_t src_stride = ref->linesize[0];
+ const int idx = av_log2(block_w) - 1;
+ const int mx = mv->x & 0xf;
+ const int my = mv->y & 0xf;
+ const int8_t *hf = ff_vvc_inter_luma_filters[hf_idx][mx];
+ const int8_t *vf = ff_vvc_inter_luma_filters[vf_idx][my];
+ int denom, wx, ox;
+
+ x_off += mv->x >> 4;
+ y_off += mv->y >> 4;
+ src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift);
+
+ EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off);
+
+ if (derive_weight_uni(&denom, &wx, &ox, lc, mvf, LUMA)) {
+ fc->vvcdsp.inter.put_uni_w[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride,
+ block_h, denom, wx, ox, hf, vf, block_w);
+ } else {
+ fc->vvcdsp.inter.put_uni[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride,
+ block_h, hf, vf, block_w);
+ }
+}
+
+static void luma_mc_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride,
+ const AVFrame *ref0, const Mv *mv0, const int x_off, const int y_off, const int block_w, const int block_h,
+ const AVFrame *ref1, const Mv *mv1, const MvField *mvf, const int hf_idx, const int vf_idx,
+ const MvField *orig_mv, const int sb_bdof_flag)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const PredictionUnit *pu = &lc->cu->pu;
+ const int idx = av_log2(block_w) - 1;
+ const AVFrame *ref[] = { ref0, ref1 };
+ int16_t *tmp[] = { lc->tmp + sb_bdof_flag * PROF_TEMP_OFFSET, lc->tmp1 + sb_bdof_flag * PROF_TEMP_OFFSET };
+ int denom, w0, w1, o0, o1;
+ const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, pu->dmvr_flag);
+
+ for (int i = L0; i <= L1; i++) {
+ const Mv *mv = mvf->mv + i;
+ const int mx = mv->x & 0xf;
+ const int my = mv->y & 0xf;
+ const int ox = x_off + (mv->x >> 4);
+ const int oy = y_off + (mv->y >> 4);
+ ptrdiff_t src_stride = ref[i]->linesize[0];
+ const uint8_t *src = ref[i]->data[0] + oy * src_stride + (ox << fc->ps.sps->pixel_shift);
+ const int8_t *hf = ff_vvc_inter_luma_filters[hf_idx][mx];
+ const int8_t *vf = ff_vvc_inter_luma_filters[vf_idx][my];
+
+ if (pu->dmvr_flag) {
+ const int x_sb = x_off + (orig_mv->mv[i].x >> 4);
+ const int y_sb = y_off + (orig_mv->mv[i].y >> 4);
+
+ EMULATED_EDGE_DMVR_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_sb, y_sb, ox, oy);
+ } else {
+ EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, ox, oy);
+ }
+ fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](tmp[i], src, src_stride, block_h, hf, vf, block_w);
+ if (sb_bdof_flag)
+ fc->vvcdsp.inter.bdof_fetch_samples(tmp[i], src, src_stride, mx, my, block_w, block_h);
+ }
+
+ if (sb_bdof_flag)
+ fc->vvcdsp.inter.apply_bdof(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h);
+ else if (weight_flag)
+ fc->vvcdsp.inter.w_avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h, denom, w0, w1, o0, o1);
+ else
+ fc->vvcdsp.inter.avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h);
+}
+
+static void chroma_mc_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride,
+ const uint8_t *src, ptrdiff_t src_stride, int x_off, int y_off,
+ const int block_w, const int block_h, const MvField *mvf, const int c_idx,
+ const int hf_idx, const int vf_idx)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const int lx = mvf->pred_flag - PF_L0;
+ const int hs = fc->ps.sps->hshift[1];
+ const int vs = fc->ps.sps->vshift[1];
+ const int idx = av_log2(block_w) - 1;
+ const Mv *mv = &mvf->mv[lx];
+ const intptr_t mx = av_mod_uintp2(mv->x, 4 + hs) << (1 - hs);
+ const intptr_t my = av_mod_uintp2(mv->y, 4 + vs) << (1 - vs);
+ const int8_t *hf = ff_vvc_inter_chroma_filters[hf_idx][mx];
+ const int8_t *vf = ff_vvc_inter_chroma_filters[vf_idx][my];
+ int denom, wx, ox;
+
+ x_off += mv->x >> (4 + hs);
+ y_off += mv->y >> (4 + vs);
+ src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift);
+
+
+ EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off);
+ if (derive_weight_uni(&denom, &wx, &ox, lc, mvf, c_idx)) {
+ fc->vvcdsp.inter.put_uni_w[CHROMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride,
+ block_h, denom, wx, ox, hf, vf, block_w);
+ } else {
+ fc->vvcdsp.inter.put_uni[CHROMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride,
+ block_h, hf, vf, block_w);
+ }
+}
+
+static void chroma_mc_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride,
+ const AVFrame *ref0, const AVFrame *ref1, const int x_off, const int y_off,
+ const int block_w, const int block_h, const MvField *mvf, const int c_idx,
+ const int hf_idx, const int vf_idx, const MvField *orig_mv, const int dmvr_flag, const int ciip_flag)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const int hs = fc->ps.sps->hshift[1];
+ const int vs = fc->ps.sps->vshift[1];
+ const int idx = av_log2(block_w) - 1;
+ const AVFrame *ref[] = { ref0, ref1 };
+ int16_t *tmp[] = { lc->tmp, lc->tmp1 };
+ int denom, w0, w1, o0, o1;
+ const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, c_idx, dmvr_flag);
+
+ for (int i = L0; i <= L1; i++) {
+ const Mv *mv = mvf->mv + i;
+ const int mx = av_mod_uintp2(mv->x, 4 + hs) << (1 - hs);
+ const int my = av_mod_uintp2(mv->y, 4 + vs) << (1 - vs);
+ const int ox = x_off + (mv->x >> (4 + hs));
+ const int oy = y_off + (mv->y >> (4 + vs));
+ ptrdiff_t src_stride = ref[i]->linesize[c_idx];
+ const uint8_t *src = ref[i]->data[c_idx] + oy * src_stride + (ox << fc->ps.sps->pixel_shift);
+ const int8_t *hf = ff_vvc_inter_chroma_filters[hf_idx][mx];
+ const int8_t *vf = ff_vvc_inter_chroma_filters[vf_idx][my];
+ if (dmvr_flag) {
+ const int x_sb = x_off + (orig_mv->mv[i].x >> (4 + hs));
+ const int y_sb = y_off + (orig_mv->mv[i].y >> (4 + vs));
+ EMULATED_EDGE_DMVR_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_sb, y_sb, ox, oy);
+ } else {
+ EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, ox, oy);
+ }
+ fc->vvcdsp.inter.put[CHROMA][idx][!!my][!!mx](tmp[i], src, src_stride, block_h, hf, vf, block_w);
+ }
+ if (weight_flag)
+ fc->vvcdsp.inter.w_avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h, denom, w0, w1, o0, o1);
+ else
+ fc->vvcdsp.inter.avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h);
+}
+
+static void luma_prof_uni(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride,
+ const AVFrame *ref, const MvField *mvf, int x_off, int y_off, const int block_w, const int block_h,
+ const int cb_prof_flag, const int16_t *diff_mv_x, const int16_t *diff_mv_y)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const uint8_t *src = ref->data[0];
+ ptrdiff_t src_stride = ref->linesize[0];
+ uint16_t *prof_tmp = lc->tmp + PROF_TEMP_OFFSET;
+ const int idx = av_log2(block_w) - 1;
+ const int lx = mvf->pred_flag - PF_L0;
+ const Mv *mv = mvf->mv + lx;
+ const int mx = mv->x & 0xf;
+ const int my = mv->y & 0xf;
+ const int8_t *hf = ff_vvc_inter_luma_filters[2][mx];
+ const int8_t *vf = ff_vvc_inter_luma_filters[2][my];
+ int denom, wx, ox;
+ const int weight_flag = derive_weight_uni(&denom, &wx, &ox, lc, mvf, LUMA);
+
+ x_off += mv->x >> 4;
+ y_off += mv->y >> 4;
+ src += y_off * src_stride + (x_off << fc->ps.sps->pixel_shift);
+
+ EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off);
+ if (cb_prof_flag) {
+ fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](prof_tmp, src, src_stride, AFFINE_MIN_BLOCK_SIZE, hf, vf, AFFINE_MIN_BLOCK_SIZE);
+ fc->vvcdsp.inter.fetch_samples(prof_tmp, src, src_stride, mx, my);
+ if (!weight_flag)
+ fc->vvcdsp.inter.apply_prof_uni(dst, dst_stride, prof_tmp, diff_mv_x, diff_mv_y);
+ else
+ fc->vvcdsp.inter.apply_prof_uni_w(dst, dst_stride, prof_tmp, diff_mv_x, diff_mv_y, denom, wx, ox);
+ } else {
+ if (!weight_flag)
+ fc->vvcdsp.inter.put_uni[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, hf, vf, block_w);
+ else
+ fc->vvcdsp.inter.put_uni_w[LUMA][idx][!!my][!!mx](dst, dst_stride, src, src_stride, block_h, denom, wx, ox, hf, vf, block_w);
+ }
+}
+
+static void luma_prof_bi(VVCLocalContext *lc, uint8_t *dst, const ptrdiff_t dst_stride,
+ const AVFrame *ref0, const AVFrame *ref1, const MvField *mvf, const int x_off, const int y_off,
+ const int block_w, const int block_h)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const PredictionUnit *pu = &lc->cu->pu;
+ const AVFrame *ref[] = { ref0, ref1 };
+ int16_t *tmp[] = { lc->tmp, lc->tmp1 };
+ uint16_t *prof_tmp = lc->tmp2 + PROF_TEMP_OFFSET;
+ const int idx = av_log2(block_w) - 1;
+ int denom, w0, w1, o0, o1;
+ const int weight_flag = derive_weight(&denom, &w0, &w1, &o0, &o1, lc, mvf, LUMA, 0);
+
+ for (int i = L0; i <= L1; i++) {
+ const Mv *mv = mvf->mv + i;
+ const int mx = mv->x & 0xf;
+ const int my = mv->y & 0xf;
+ const int ox = x_off + (mv->x >> 4);
+ const int oy = y_off + (mv->y >> 4);
+ ptrdiff_t src_stride = ref[i]->linesize[0];
+ const uint8_t *src = ref[i]->data[0] + oy * src_stride + (ox << fc->ps.sps->pixel_shift);
+ const int8_t *hf = ff_vvc_inter_luma_filters[2][mx];
+ const int8_t *vf = ff_vvc_inter_luma_filters[2][my];
+
+ EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, ox, oy);
+ if (!pu->cb_prof_flag[i]) {
+ fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](tmp[i], src, src_stride, block_h, hf, vf, block_w);
+ } else {
+ fc->vvcdsp.inter.put[LUMA][idx][!!my][!!mx](prof_tmp, src, src_stride, AFFINE_MIN_BLOCK_SIZE, hf, vf, AFFINE_MIN_BLOCK_SIZE);
+ fc->vvcdsp.inter.fetch_samples(prof_tmp, src, src_stride, mx, my);
+ fc->vvcdsp.inter.apply_prof(tmp[i], prof_tmp, pu->diff_mv_x[i], pu->diff_mv_y[i]);
+ }
+ }
+
+ if (weight_flag)
+ fc->vvcdsp.inter.w_avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h, denom, w0, w1, o0, o1);
+ else
+ fc->vvcdsp.inter.avg(dst, dst_stride, tmp[L0], tmp[L1], block_w, block_h);
+}
+
+static int pred_get_refs(const VVCLocalContext *lc, VVCFrame *ref[2], const MvField *mv)
+{
+ const RefPicList *rpl = lc->sc->rpl;
+
+ for (int mask = PF_L0; mask <= PF_L1; mask++) {
+ if (mv->pred_flag & mask) {
+ const int lx = mask - PF_L0;
+ ref[lx] = rpl[lx].ref[mv->ref_idx[lx]];
+ if (!ref[lx])
+ return AVERROR_INVALIDDATA;
+ }
+ }
+ return 0;
+}
+
+#define POS(c_idx, x, y) \
+ &fc->frame->data[c_idx][((y) >> fc->ps.sps->vshift[c_idx]) * fc->frame->linesize[c_idx] + \
+ (((x) >> fc->ps.sps->hshift[c_idx]) << fc->ps.sps->pixel_shift)]
+
+static void pred_gpm_blk(VVCLocalContext *lc)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const PredictionUnit *pu = &cu->pu;
+
+ const uint8_t angle_idx = ff_vvc_gpm_angle_idx[pu->gpm_partition_idx];
+ const uint8_t weights_idx = ff_vvc_gpm_angle_to_weights_idx[angle_idx];
+ const int w = av_log2(cu->cb_width) - 3;
+ const int h = av_log2(cu->cb_height) - 3;
+ const uint8_t off_x = ff_vvc_gpm_weights_offset_x[pu->gpm_partition_idx][h][w];
+ const uint8_t off_y = ff_vvc_gpm_weights_offset_y[pu->gpm_partition_idx][h][w];
+ const uint8_t mirror_type = ff_vvc_gpm_angle_to_mirror[angle_idx];
+ const uint8_t *weights;
+
+ const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1;
+
+ int16_t *tmp[2] = {lc->tmp, lc->tmp1};
+
+ for (int c_idx = 0; c_idx < c_end; c_idx++) {
+ const int hs = fc->ps.sps->hshift[c_idx];
+ const int vs = fc->ps.sps->vshift[c_idx];
+ const int x = lc->cu->x0 >> hs;
+ const int y = lc->cu->y0 >> vs;
+ const int width = cu->cb_width >> hs;
+ const int height = cu->cb_height >> vs;
+ uint8_t *dst = POS(c_idx, lc->cu->x0, lc->cu->y0);
+ ptrdiff_t dst_stride = fc->frame->linesize[c_idx];
+
+ int step_x = 1 << hs;
+ int step_y = VVC_GPM_WEIGHT_SIZE << vs;
+ if (!mirror_type) {
+ weights = &ff_vvc_gpm_weights[weights_idx][off_y * VVC_GPM_WEIGHT_SIZE + off_x];
+ } else if (mirror_type == 1) {
+ step_x = -step_x;
+ weights = &ff_vvc_gpm_weights[weights_idx][off_y * VVC_GPM_WEIGHT_SIZE + VVC_GPM_WEIGHT_SIZE - 1- off_x];
+ } else {
+ step_y = -step_y;
+ weights = &ff_vvc_gpm_weights[weights_idx][(VVC_GPM_WEIGHT_SIZE - 1 - off_y) * VVC_GPM_WEIGHT_SIZE + off_x];
+ }
+
+ for (int i = 0; i < 2; i++) {
+ const MvField *mv = pu->gpm_mv + i;
+ const int lx = mv->pred_flag - PF_L0;
+ VVCFrame *ref = lc->sc->rpl[lx].ref[mv->ref_idx[lx]];
+ if (!ref)
+ return;
+ if (c_idx)
+ chroma_mc(lc, tmp[i], ref->frame, mv->mv + lx, x, y, width, height, c_idx);
+ else
+ luma_mc(lc, tmp[i], ref->frame, mv->mv + lx, x, y, width, height);
+ }
+ fc->vvcdsp.inter.put_gpm(dst, dst_stride, width, height, tmp[0], tmp[1], weights, step_x, step_y);
+ }
+ return;
+}
+
+static int ciip_derive_intra_weight(const VVCLocalContext *lc, const int x0, const int y0,
+ const int width, const int height)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int x0b = av_mod_uintp2(x0, sps->ctb_log2_size_y);
+ const int y0b = av_mod_uintp2(y0, sps->ctb_log2_size_y);
+ const int available_l = lc->ctb_left_flag || x0b;
+ const int available_u = lc->ctb_up_flag || y0b;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+
+ int w = 1;
+
+ if (available_u &&fc->tab.mvf[((y0 - 1) >> MIN_PU_LOG2) * min_pu_width + ((x0 - 1 + width)>> MIN_PU_LOG2)].pred_flag == PF_INTRA)
+ w++;
+
+ if (available_l && fc->tab.mvf[((y0 - 1 + height)>> MIN_PU_LOG2) * min_pu_width + ((x0 - 1) >> MIN_PU_LOG2)].pred_flag == PF_INTRA)
+ w++;
+
+ return w;
+}
+
+static void pred_regular_luma(VVCLocalContext *lc, const int hf_idx, const int vf_idx, const MvField *mv,
+ const int x0, const int y0, const int sbw, const int sbh, const MvField *orig_mv, const int sb_bdof_flag)
+{
+ const SliceContext *sc = lc->sc;
+ const VVCFrameContext *fc = lc->fc;
+ const int ciip_flag = lc->cu->ciip_flag;
+ uint8_t *dst = POS(0, x0, y0);
+ const ptrdiff_t dst_stride = fc->frame->linesize[0];
+ uint8_t *inter = ciip_flag ? (uint8_t *)lc->ciip_tmp1 : dst;
+ const ptrdiff_t inter_stride = ciip_flag ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst_stride;
+ VVCFrame *ref[2];
+
+ if (pred_get_refs(lc, ref, mv) < 0)
+ return;
+
+ if (mv->pred_flag != PF_BI) {
+ const int lx = mv->pred_flag - PF_L0;
+ luma_mc_uni(lc, inter, inter_stride, ref[lx]->frame,
+ mv, x0, y0, sbw, sbh, hf_idx, vf_idx);
+ } else {
+ luma_mc_bi(lc, inter, inter_stride, ref[0]->frame,
+ &mv->mv[0], x0, y0, sbw, sbh, ref[1]->frame, &mv->mv[1], mv,
+ hf_idx, vf_idx, orig_mv, sb_bdof_flag);
+ }
+
+ if (ciip_flag) {
+ const int intra_weight = ciip_derive_intra_weight(lc, x0, y0, sbw, sbh);
+ fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 0);
+ if (sc->sh.r->sh_lmcs_used_flag)
+ fc->vvcdsp.lmcs.filter(inter, inter_stride, sbw, sbh, fc->ps.lmcs.fwd_lut);
+ fc->vvcdsp.inter.put_ciip(dst, dst_stride, sbw, sbh, inter, inter_stride, intra_weight);
+
+ }
+}
+
+static void pred_regular_chroma(VVCLocalContext *lc, const MvField *mv,
+ const int x0, const int y0, const int sbw, const int sbh, const MvField *orig_mv, const int dmvr_flag)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const int hs = fc->ps.sps->hshift[1];
+ const int vs = fc->ps.sps->vshift[1];
+ const int x0_c = x0 >> hs;
+ const int y0_c = y0 >> vs;
+ const int w_c = sbw >> hs;
+ const int h_c = sbh >> vs;
+ const int do_ciip = lc->cu->ciip_flag && (w_c > 2);
+
+ uint8_t* dst1 = POS(1, x0, y0);
+ uint8_t* dst2 = POS(2, x0, y0);
+ const ptrdiff_t dst1_stride = fc->frame->linesize[1];
+ const ptrdiff_t dst2_stride = fc->frame->linesize[2];
+
+ uint8_t *inter1 = do_ciip ? (uint8_t *)lc->ciip_tmp1 : dst1;
+ const ptrdiff_t inter1_stride = do_ciip ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst1_stride;
+
+ uint8_t *inter2 = do_ciip ? (uint8_t *)lc->ciip_tmp2 : dst2;
+ const ptrdiff_t inter2_stride = do_ciip ? (MAX_PB_SIZE * sizeof(uint16_t)) : dst2_stride;
+
+ //fix me
+ const int hf_idx = 0;
+ const int vf_idx = 0;
+ VVCFrame *ref[2];
+
+ if (pred_get_refs(lc, ref, mv) < 0)
+ return;
+
+ if (mv->pred_flag != PF_BI) {
+ const int lx = mv->pred_flag - PF_L0;
+ if (!ref[lx])
+ return;
+
+ chroma_mc_uni(lc, inter1, inter1_stride, ref[lx]->frame->data[1], ref[lx]->frame->linesize[1],
+ x0_c, y0_c, w_c, h_c, mv, CB, hf_idx, vf_idx);
+ chroma_mc_uni(lc, inter2, inter2_stride, ref[lx]->frame->data[2], ref[lx]->frame->linesize[2],
+ x0_c, y0_c, w_c, h_c, mv, CR, hf_idx, vf_idx);
+ } else {
+ if (!ref[0] || !ref[1])
+ return;
+
+ chroma_mc_bi(lc, inter1, inter1_stride, ref[0]->frame, ref[1]->frame,
+ x0_c, y0_c, w_c, h_c, mv, CB, hf_idx, vf_idx, orig_mv, dmvr_flag, lc->cu->ciip_flag);
+
+ chroma_mc_bi(lc, inter2, inter2_stride, ref[0]->frame, ref[1]->frame,
+ x0_c, y0_c, w_c, h_c, mv, CR, hf_idx, vf_idx, orig_mv, dmvr_flag, lc->cu->ciip_flag);
+
+ }
+ if (do_ciip) {
+ const int intra_weight = ciip_derive_intra_weight(lc, x0, y0, sbw, sbh);
+ fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 1);
+ fc->vvcdsp.intra.intra_pred(lc, x0, y0, sbw, sbh, 2);
+ fc->vvcdsp.inter.put_ciip(dst1, dst1_stride, w_c, h_c, inter1, inter1_stride, intra_weight);
+ fc->vvcdsp.inter.put_ciip(dst2, dst2_stride, w_c, h_c, inter2, inter2_stride, intra_weight);
+
+ }
+}
+
+// 8.5.3.5 Parametric motion vector refinement process
+static int parametric_mv_refine(const int *sad, const int stride)
+{
+ const int sad_minus = sad[-stride];
+ const int sad_center = sad[0];
+ const int sad_plus = sad[stride];
+ int dmvc;
+ int denom = (( sad_minus + sad_plus) - (sad_center << 1 ) ) << 3;
+ if (!denom)
+ dmvc = 0;
+ else {
+ if (sad_minus == sad_center)
+ dmvc = -8;
+ else if (sad_plus == sad_center)
+ dmvc = 8;
+ else {
+ int num = ( sad_minus - sad_plus ) << 4;
+ int sign_num = 0;
+ int quotient = 0;
+ int counter = 3;
+ if (num < 0 ) {
+ num = - num;
+ sign_num = 1;
+ }
+ while (counter > 0) {
+ counter = counter - 1;
+ quotient = quotient << 1;
+ if ( num >= denom ) {
+ num = num - denom;
+ quotient = quotient + 1;
+ }
+ denom = (denom >> 1);
+ }
+ if (sign_num == 1 )
+ dmvc = -quotient;
+ else
+ dmvc = quotient;
+ }
+ }
+ return dmvc;
+}
+
+#define SAD_ARRAY_SIZE 5
+//8.5.3 Decoder-side motion vector refinement process
+static void dmvr_mv_refine(VVCLocalContext *lc, MvField *mvf, MvField *orig_mv, int *sb_bdof_flag,
+ const AVFrame *ref0, const AVFrame *ref1, const int x_off, const int y_off, const int block_w, const int block_h)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const int sr_range = 2;
+ const AVFrame *ref[] = { ref0, ref1 };
+ int16_t *tmp[] = { lc->tmp, lc->tmp1 };
+ int sad[SAD_ARRAY_SIZE][SAD_ARRAY_SIZE];
+ int min_dx, min_dy, min_sad, dx, dy;
+
+ *orig_mv = *mvf;
+ min_dx = min_dy = dx = dy = 2;
+
+ for (int i = L0; i <= L1; i++) {
+ const int pred_w = block_w + 2 * sr_range;
+ const int pred_h = block_h + 2 * sr_range;
+ const Mv *mv = mvf->mv + i;
+ const int mx = mv->x & 0xf;
+ const int my = mv->y & 0xf;
+ const int ox = x_off + (mv->x >> 4) - sr_range;
+ const int oy = y_off + (mv->y >> 4) - sr_range;
+ ptrdiff_t src_stride = ref[i]->linesize[LUMA];
+ const uint8_t *src = ref[i]->data[LUMA] + oy * src_stride + (ox << fc->ps.sps->pixel_shift);
+ EMULATED_EDGE_BILINEAR(lc->edge_emu_buffer, &src, &src_stride, ox, oy);
+ fc->vvcdsp.inter.dmvr[!!my][!!mx](tmp[i], src, src_stride, pred_h, mx, my, pred_w);
+ }
+
+ min_sad = fc->vvcdsp.inter.sad(tmp[L0], tmp[L1], dx, dy, block_w, block_h);
+ min_sad -= min_sad >> 2;
+ sad[dy][dx] = min_sad;
+
+ if (min_sad >= block_w * block_h) {
+ int dmv[2];
+ // 8.5.3.4 Array entry selection process
+ for (dy = 0; dy < SAD_ARRAY_SIZE; dy++) {
+ for (dx = 0; dx < SAD_ARRAY_SIZE; dx++) {
+ if (dx != sr_range || dy != sr_range) {
+ sad[dy][dx] = fc->vvcdsp.inter.sad(lc->tmp, lc->tmp1, dx, dy, block_w, block_h);
+ if (sad[dy][dx] < min_sad) {
+ min_sad = sad[dy][dx];
+ min_dx = dx;
+ min_dy = dy;
+ }
+ }
+ }
+ }
+ dmv[0] = (min_dx - sr_range) << 4;
+ dmv[1] = (min_dy - sr_range) << 4;
+ if (min_dx != 0 && min_dx != 4 && min_dy != 0 && min_dy != 4) {
+ dmv[0] += parametric_mv_refine(&sad[min_dy][min_dx], 1);
+ dmv[1] += parametric_mv_refine(&sad[min_dy][min_dx], SAD_ARRAY_SIZE);
+ }
+
+ for (int i = L0; i <= L1; i++) {
+ Mv *mv = mvf->mv + i;
+ mv->x += (1 - 2 * i) * dmv[0];
+ mv->y += (1 - 2 * i) * dmv[1];
+ ff_vvc_clip_mv(mv);
+ }
+ }
+ if (min_sad < 2 * block_w * block_h) {
+ *sb_bdof_flag = 0;
+ }
+}
+
+static void set_dmvr_info(VVCFrameContext *fc, const int x0, const int y0,
+ const int width, const int height, const MvField *mvf)
+
+{
+ const VVCPPS *pps = fc->ps.pps;
+
+ for (int y = y0; y < y0 + height; y += MIN_PU_SIZE) {
+ for (int x = x0; x < x0 + width; x += MIN_PU_SIZE) {
+ const int idx = pps->min_pu_width * (y >> MIN_PU_LOG2) + (x >> MIN_PU_LOG2);
+ fc->ref->tab_dmvr_mvf[idx] = *mvf;
+ }
+ }
+}
+
+static void fill_dmvr_info(const VVCFrameContext *fc, const int x0, const int y0,
+ const int width, const int height)
+{
+ const VVCPPS *pps = fc->ps.pps;
+ const int w = width >> MIN_PU_LOG2;
+
+ for (int y = y0 >> MIN_PU_LOG2; y < (y0 + height) >> MIN_PU_LOG2; y++) {
+ const int idx = pps->min_pu_width * y + (x0 >> MIN_PU_LOG2);
+ const MvField *mvf = fc->tab.mvf + idx;
+ MvField *dmvr_mvf = fc->ref->tab_dmvr_mvf + idx;
+ memcpy(dmvr_mvf, mvf, sizeof(MvField) * w);
+ }
+}
+
+static void derive_sb_mv(VVCLocalContext *lc, MvField *mv, MvField *orig_mv, int *sb_bdof_flag,
+ const int x0, const int y0, const int sbw, const int sbh)
+{
+ VVCFrameContext *fc = lc->fc;
+ const PredictionUnit *pu = &lc->cu->pu;
+
+ *orig_mv = *mv = *ff_vvc_get_mvf(fc, x0, y0);
+ if (pu->bdof_flag)
+ *sb_bdof_flag = 1;
+ if (pu->dmvr_flag) {
+ VVCFrame* ref[2];
+ if (pred_get_refs(lc, ref, mv) < 0)
+ return;
+ dmvr_mv_refine(lc, mv, orig_mv, sb_bdof_flag, ref[0]->frame, ref[1]->frame, x0, y0, sbw, sbh);
+ set_dmvr_info(fc, x0, y0, sbw, sbh, mv);
+ }
+}
+
+static void pred_regular_blk(VVCLocalContext *lc, const int skip_ciip)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ PredictionUnit *pu = &lc->cu->pu;
+ const MotionInfo *mi = &pu->mi;
+ MvField mv, orig_mv;
+ int sbw, sbh, sb_bdof_flag = 0;
+
+ if (cu->ciip_flag && skip_ciip)
+ return;
+
+ sbw = cu->cb_width / mi->num_sb_x;
+ sbh = cu->cb_height / mi->num_sb_y;
+
+ for (int sby = 0; sby < mi->num_sb_y; sby++) {
+ for (int sbx = 0; sbx < mi->num_sb_x; sbx++) {
+ const int x0 = cu->x0 + sbx * sbw;
+ const int y0 = cu->y0 + sby * sbh;
+
+ if (cu->ciip_flag)
+ ff_vvc_set_neighbour_available(lc, x0, y0, sbw, sbh);
+
+ derive_sb_mv(lc, &mv, &orig_mv, &sb_bdof_flag, x0, y0, sbw, sbh);
+ pred_regular_luma(lc, mi->hpel_if_idx, mi->hpel_if_idx, &mv, x0, y0, sbw, sbh, &orig_mv, sb_bdof_flag);
+ if (fc->ps.sps->r->sps_chroma_format_idc)
+ pred_regular_chroma(lc, &mv, x0, y0, sbw, sbh, &orig_mv, pu->dmvr_flag);
+ }
+ }
+}
+
+static void derive_affine_mvc(MvField *mvc, const VVCFrameContext *fc, const MvField *mv,
+ const int x0, const int y0, const int sbw, const int sbh)
+{
+ const int hs = fc->ps.sps->hshift[1];
+ const int vs = fc->ps.sps->vshift[1];
+ const MvField* mv2 = ff_vvc_get_mvf(fc, x0 + hs * sbw, y0 + vs * sbh);
+ *mvc = *mv;
+ mvc->mv[0].x += mv2->mv[0].x;
+ mvc->mv[0].y += mv2->mv[0].y;
+ mvc->mv[1].x += mv2->mv[1].x;
+ mvc->mv[1].y += mv2->mv[1].y;
+ ff_vvc_round_mv(mvc->mv + 0, 0, 1);
+ ff_vvc_round_mv(mvc->mv + 1, 0, 1);
+}
+
+static void pred_affine_blk(VVCLocalContext *lc)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const PredictionUnit *pu = &cu->pu;
+ const MotionInfo *mi = &pu->mi;
+ const int x0 = cu->x0;
+ const int y0 = cu->y0;
+ const int sbw = cu->cb_width / mi->num_sb_x;
+ const int sbh = cu->cb_height / mi->num_sb_y;
+ const int hs = fc->ps.sps->hshift[1];
+ const int vs = fc->ps.sps->vshift[1];
+
+ for (int sby = 0; sby < mi->num_sb_y; sby++) {
+ for (int sbx = 0; sbx < mi->num_sb_x; sbx++) {
+ const int x = x0 + sbx * sbw;
+ const int y = y0 + sby * sbh;
+
+ uint8_t *dst0 = POS(0, x, y);
+ const MvField *mv = ff_vvc_get_mvf(fc, x, y);
+ VVCFrame *ref[2];
+
+ if (pred_get_refs(lc, ref, mv) < 0)
+ return;
+
+ if (mi->pred_flag != PF_BI) {
+ const int lx = mi->pred_flag - PF_L0;
+ luma_prof_uni(lc, dst0, fc->frame->linesize[0], ref[lx]->frame,
+ mv, x, y, sbw, sbh, pu->cb_prof_flag[lx],
+ pu->diff_mv_x[lx], pu->diff_mv_y[lx]);
+ } else {
+ luma_prof_bi(lc, dst0, fc->frame->linesize[0], ref[0]->frame, ref[1]->frame,
+ mv, x, y, sbw, sbh);
+ }
+ if (fc->ps.sps->r->sps_chroma_format_idc) {
+ if (!av_mod_uintp2(sby, vs) && !av_mod_uintp2(sbx, hs)) {
+ MvField mvc;
+ derive_affine_mvc(&mvc, fc, mv, x, y, sbw, sbh);
+ pred_regular_chroma(lc, &mvc, x, y, sbw<<hs, sbh<<vs, NULL, 0);
+
+ }
+ }
+
+ }
+ }
+}
+
+static void predict_inter(VVCLocalContext *lc)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const PredictionUnit *pu = &cu->pu;
+
+ if (pu->merge_gpm_flag)
+ pred_gpm_blk(lc);
+ else if (pu->inter_affine_flag)
+ pred_affine_blk(lc);
+ else
+ pred_regular_blk(lc, 1); //intra block is not ready yet, skip ciip
+
+ if (!pu->dmvr_flag)
+ fill_dmvr_info(fc, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+ if (lc->sc->sh.r->sh_lmcs_used_flag && !cu->ciip_flag) {
+ uint8_t* dst0 = POS(0, cu->x0, cu->y0);
+ fc->vvcdsp.lmcs.filter(dst0, fc->frame->linesize[LUMA], cu->cb_width, cu->cb_height, fc->ps.lmcs.fwd_lut);
+ }
+}
+
+static int has_inter_luma(const CodingUnit *cu)
+{
+ return cu->pred_mode != MODE_INTRA && cu->pred_mode != MODE_PLT && cu->tree_type != DUAL_TREE_CHROMA;
+}
+
+int ff_vvc_predict_inter(VVCLocalContext *lc, const int rs)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CTU *ctu = fc->tab.ctus + rs;
+ CodingUnit *cu = ctu->cus;
+
+ while (cu) {
+ lc->cu = cu;
+ if (has_inter_luma(cu))
+ predict_inter(lc);
+ cu = cu->next;
+ }
+
+ return 0;
+}
+
+void ff_vvc_predict_ciip(VVCLocalContext *lc)
+{
+ av_assert0(lc->cu->ciip_flag);
+
+ //todo: refact out ciip from pred_regular_blk
+ pred_regular_blk(lc, 0);
+}
+
+#undef POS
diff --git a/libavcodec/vvc/vvc_inter.h b/libavcodec/vvc/vvc_inter.h
new file mode 100644
index 0000000000..61fc44b3a0
--- /dev/null
+++ b/libavcodec/vvc/vvc_inter.h
@@ -0,0 +1,42 @@
+/*
+ * VVC inter prediction
+ *
+ * Copyright (C) 2023 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVC_INTER_H
+#define AVCODEC_VVC_VVC_INTER_H
+
+#include "vvc_ctu.h"
+
+/**
+ * Loop entire CTU to predict all inter coding blocks
+ * @param lc local context for CTU
+ * @param rs raster order for the CTU
+ * @return AVERROR
+ */
+int ff_vvc_predict_inter(VVCLocalContext *lc, int rs);
+
+/**
+ * CIIP(Combined Inter-Intra Prediction) for a coding block
+ * @param lc local context for CTU
+ */
+void ff_vvc_predict_ciip(VVCLocalContext *lc);
+
+#endif // AVCODEC_VVC_VVC_INTER_H
diff --git a/libavcodec/vvc/vvc_inter_template.c b/libavcodec/vvc/vvc_inter_template.c
new file mode 100644
index 0000000000..b67b66a2dc
--- /dev/null
+++ b/libavcodec/vvc/vvc_inter_template.c
@@ -0,0 +1,1023 @@
+/*
+ * VVC inter prediction DSP
+ *
+ * Copyright (C) 2022 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+////////////////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////////////////
+static void FUNC(put_pixels)(int16_t *dst,
+ const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ const pixel *src = (const pixel *)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = src[x] << (14 - BIT_DEPTH);
+ src += src_stride;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+static void FUNC(put_uni_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride, const int height,
+ const int8_t *hf, const int8_t *vf, const int width)
+{
+ const pixel *src = (const pixel *)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+
+ for (int y = 0; y < height; y++) {
+ memcpy(dst, src, width * sizeof(pixel));
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(put_uni_w_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride, const int height,
+ const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf,
+ const int width)
+{
+ const pixel *src = (const pixel *)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+ const int ox = _ox * (1 << (BIT_DEPTH - 8));
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ const int v = (src[x] << (14 - BIT_DEPTH));
+ dst[x] = av_clip_pixel(((v * wx + offset) >> shift) + ox);
+ }
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////////////////
+#define LUMA_FILTER(src, stride) \
+ (filter[0] * src[x - 3 * stride] + \
+ filter[1] * src[x - 2 * stride] + \
+ filter[2] * src[x - stride] + \
+ filter[3] * src[x ] + \
+ filter[4] * src[x + stride] + \
+ filter[5] * src[x + 2 * stride] + \
+ filter[6] * src[x + 3 * stride] + \
+ filter[7] * src[x + 4 * stride])
+
+static void FUNC(put_luma_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ const pixel *src = (const pixel*)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+static void FUNC(put_luma_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ const pixel *src = (pixel*)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = vf;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+static void FUNC(put_luma_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE];
+ int16_t *tmp = tmp_array;
+ const pixel *src = (const pixel*)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+
+ src -= LUMA_EXTRA_BEFORE * src_stride;
+ for (int y = 0; y < height + LUMA_EXTRA; y++) {
+ for (int x = 0; x < width; x++)
+ tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ tmp += MAX_PB_SIZE;
+ }
+
+ tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE;
+ filter = vf;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6;
+ tmp += MAX_PB_SIZE;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+static void FUNC(put_uni_luma_h)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ const pixel *src = (const pixel*)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+ const int shift = 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ const int val = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8);
+ dst[x] = av_clip_pixel((val + offset) >> shift);
+ }
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(put_uni_luma_v)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+
+ const pixel *src = (const pixel*)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = vf;
+ const int shift = 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ const int val = LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8);
+ dst[x] = av_clip_pixel((val + offset) >> shift);
+ }
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(put_uni_luma_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE];
+ int16_t *tmp = tmp_array;
+ const pixel *src = (const pixel*)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+ const int shift = 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ src -= LUMA_EXTRA_BEFORE * src_stride;
+ for (int y = 0; y < height + LUMA_EXTRA; y++) {
+ for (int x = 0; x < width; x++)
+ tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ tmp += MAX_PB_SIZE;
+ }
+
+ tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE;
+ filter = vf;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ const int val = LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6;
+ dst[x] = av_clip_pixel((val + offset) >> shift);
+ }
+ tmp += MAX_PB_SIZE;
+ dst += dst_stride;
+ }
+
+}
+
+static void FUNC(put_uni_luma_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride, int height,
+ const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf,
+ const int width)
+{
+ const pixel *src = (const pixel*)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+ const int ox = _ox * (1 << (BIT_DEPTH - 8));
+ const int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = av_clip_pixel((((LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox);
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(put_uni_luma_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride, const int height,
+ const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf,
+ const int width)
+{
+ const pixel *src = (const pixel*)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = vf;
+ const int ox = _ox * (1 << (BIT_DEPTH - 8));
+ const int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = av_clip_pixel((((LUMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox);
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(put_uni_luma_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride, const int height, const int denom,
+ const int wx, const int _ox, const int8_t *hf, const int8_t *vf, const int width)
+{
+ int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE];
+ int16_t *tmp = tmp_array;
+ const pixel *src = (const pixel*)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+ const int ox = _ox * (1 << (BIT_DEPTH - 8));
+ const int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ src -= LUMA_EXTRA_BEFORE * src_stride;
+ for (int y = 0; y < height + LUMA_EXTRA; y++) {
+ for (int x = 0; x < width; x++)
+ tmp[x] = LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ tmp += MAX_PB_SIZE;
+ }
+
+ tmp = tmp_array + LUMA_EXTRA_BEFORE * MAX_PB_SIZE;
+ filter = vf;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = av_clip_pixel((((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox);
+ tmp += MAX_PB_SIZE;
+ dst += dst_stride;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////////////////
+#define CHROMA_FILTER(src, stride) \
+ (filter[0] * src[x - stride] + \
+ filter[1] * src[x] + \
+ filter[2] * src[x + stride] + \
+ filter[3] * src[x + 2 * stride])
+
+static void FUNC(put_chroma_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ const pixel *src = (const pixel *)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+static void FUNC(put_chroma_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ const pixel *src = (const pixel *)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = vf;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+static void FUNC(put_chroma_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE];
+ int16_t *tmp = tmp_array;
+ const pixel *src = (const pixel *)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+
+ src -= CHROMA_EXTRA_BEFORE * src_stride;
+
+ for (int y = 0; y < height + CHROMA_EXTRA; y++) {
+ for (int x = 0; x < width; x++)
+ tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ tmp += MAX_PB_SIZE;
+ }
+
+ tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE;
+ filter = vf;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6;
+ tmp += MAX_PB_SIZE;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+static void FUNC(put_uni_chroma_h)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ const pixel *src = (const pixel *)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+ const int shift = 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = av_clip_pixel(((CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) + offset) >> shift);
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(put_uni_chroma_v)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ const pixel *src = (const pixel *)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = vf;
+ const int shift = 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = av_clip_pixel(((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) + offset) >> shift);
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(put_uni_chroma_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const int8_t *hf, const int8_t *vf, const int width)
+{
+ int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE];
+ int16_t *tmp = tmp_array;
+ const pixel *src = (const pixel *)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+ const int shift = 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ src -= CHROMA_EXTRA_BEFORE * src_stride;
+
+ for (int y = 0; y < height + CHROMA_EXTRA; y++) {
+ for (int x = 0; x < width; x++)
+ tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ tmp += MAX_PB_SIZE;
+ }
+
+ tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE;
+ filter = vf;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + offset) >> shift);
+ tmp += MAX_PB_SIZE;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(put_uni_chroma_w_h)(uint8_t *_dst, ptrdiff_t _dst_stride,
+ const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox,
+ const int8_t *hf, const int8_t *vf, int width)
+{
+ const pixel *src = (const pixel *)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+ const int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ ox = ox * (1 << (BIT_DEPTH - 8));
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ dst[x] = av_clip_pixel((((CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox);
+ }
+ dst += dst_stride;
+ src += src_stride;
+ }
+}
+
+static void FUNC(put_uni_chroma_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const uint8_t *_src, const ptrdiff_t _src_stride, const int height,
+ const int denom, const int wx, const int _ox, const int8_t *hf, const int8_t *vf,
+ const int width)
+{
+ const pixel *src = (const pixel *)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = vf;
+ const int shift = denom + 14 - BIT_DEPTH;
+ const int ox = _ox * (1 << (BIT_DEPTH - 8));
+#if BIT_DEPTH < 14
+ int offset = 1 << (shift - 1);
+#else
+ int offset = 0;
+#endif
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ dst[x] = av_clip_pixel((((CHROMA_FILTER(src, src_stride) >> (BIT_DEPTH - 8)) * wx + offset) >> shift) + ox);
+ }
+ dst += dst_stride;
+ src += src_stride;
+ }
+}
+
+static void FUNC(put_uni_chroma_w_hv)(uint8_t *_dst, ptrdiff_t _dst_stride,
+ const uint8_t *_src, ptrdiff_t _src_stride, int height, int denom, int wx, int ox,
+ const int8_t *hf, const int8_t *vf, int width)
+{
+ int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE];
+ int16_t *tmp = tmp_array;
+ const pixel *src = (const pixel *)_src;
+ pixel *dst = (pixel *)_dst;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int8_t *filter = hf;
+ const int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+
+ src -= CHROMA_EXTRA_BEFORE * src_stride;
+
+ for (int y = 0; y < height + CHROMA_EXTRA; y++) {
+ for (int x = 0; x < width; x++)
+ tmp[x] = CHROMA_FILTER(src, 1) >> (BIT_DEPTH - 8);
+ src += src_stride;
+ tmp += MAX_PB_SIZE;
+ }
+
+ tmp = tmp_array + CHROMA_EXTRA_BEFORE * MAX_PB_SIZE;
+ filter = vf;
+
+ ox = ox * (1 << (BIT_DEPTH - 8));
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = av_clip_pixel((((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx + offset) >> shift) + ox);
+ tmp += MAX_PB_SIZE;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(avg)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const int16_t *src0, const int16_t *src1, const int width, const int height)
+{
+ pixel *dst = (pixel*)_dst;
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int shift = FFMAX(3, 15 - BIT_DEPTH);
+ const int offset = 1 << (shift - 1);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = av_clip_pixel((src0[x] + src1[x] + offset) >> shift);
+ src0 += MAX_PB_SIZE;
+ src1 += MAX_PB_SIZE;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(w_avg)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const int16_t *src0, const int16_t *src1, const int width, const int height,
+ const int denom, const int w0, const int w1, const int o0, const int o1)
+{
+ pixel *dst = (pixel*)_dst;
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int shift = denom + FFMAX(3, 15 - BIT_DEPTH);
+ const int offset = (((o0 + o1) << (BIT_DEPTH - 8)) + 1) << (shift - 1);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = av_clip_pixel((src0[x] * w0 + src1[x] * w1 + offset) >> shift);
+ src0 += MAX_PB_SIZE;
+ src1 += MAX_PB_SIZE;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(put_ciip)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const int width, const int height,
+ const uint8_t *_inter, const ptrdiff_t _inter_stride, const int intra_weight)
+{
+ pixel *dst = (pixel *)_dst;
+ pixel *inter = (pixel *)_inter;
+ const size_t dst_stride = _dst_stride / sizeof(pixel);
+ const size_t inter_stride = _inter_stride / sizeof(pixel);
+ const int inter_weight = 4 - intra_weight;
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = (dst[x] * intra_weight + inter[x] * inter_weight + 2) >> 2;
+ dst += dst_stride;
+ inter += inter_stride;
+ }
+}
+
+static void FUNC(put_gpm)(uint8_t *_dst, ptrdiff_t dst_stride,
+ const int width, const int height,
+ const int16_t *src0, const int16_t *src1,
+ const uint8_t *weights, const int step_x, const int step_y)
+{
+ const int shift = FFMAX(5, 17 - BIT_DEPTH);
+ const int offset = 1 << (shift - 1);
+ pixel *dst = (pixel *)_dst;
+
+ dst_stride /= sizeof(pixel);
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ const uint8_t w = weights[x * step_x];
+ dst[x] = av_clip_pixel((src0[x] * w + src1[x] * (8 - w) + offset) >> shift);
+ }
+ dst += dst_stride;
+ src0 += MAX_PB_SIZE;
+ src1 += MAX_PB_SIZE;
+ weights += step_y;
+ }
+}
+
+//8.5.6.3.3 Luma integer sample fetching process, add one extra pad line
+static void FUNC(bdof_fetch_samples)(int16_t *_dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int x_frac, const int y_frac, const int width, const int height)
+{
+ const int x_off = (x_frac >> 3) - 1;
+ const int y_off = (y_frac >> 3) - 1;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const pixel *src = (pixel*)_src + (x_off) + y_off * src_stride;
+ int16_t *dst = _dst - 1 - MAX_PB_SIZE;
+ const int shift = 14 - BIT_DEPTH;
+ const int bdof_width = width + 2 * BDOF_BORDER_EXT;
+
+ // top
+ for (int i = 0; i < bdof_width; i++)
+ dst[i] = src[i] << shift;
+
+ dst += MAX_PB_SIZE;
+ src += src_stride;
+
+ for (int i = 0; i < height; i++) {
+ dst[0] = src[0] << shift;
+ dst[1 + width] = src[1 + width] << shift;
+ dst += MAX_PB_SIZE;
+ src += src_stride;
+ }
+ for (int i = 0; i < bdof_width; i++)
+ dst[i] = src[i] << shift;
+}
+
+//8.5.6.3.3 Luma integer sample fetching process
+static void FUNC(fetch_samples)(int16_t *_dst, const uint8_t *_src, const ptrdiff_t _src_stride, const int x_frac, const int y_frac)
+{
+ FUNC(bdof_fetch_samples)(_dst, _src, _src_stride, x_frac, y_frac, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE);
+}
+
+static void FUNC(prof_grad_filter)(int16_t *_gradient_h, int16_t *_gradient_v, const ptrdiff_t gradient_stride,
+ const int16_t *_src, const ptrdiff_t src_stride, const int width, const int height, const int pad)
+{
+ const int shift = 6;
+ const int16_t *src = _src;
+ int16_t *gradient_h = _gradient_h + pad * (1 + gradient_stride);
+ int16_t *gradient_v = _gradient_v + pad * (1 + gradient_stride);
+
+ for (int y = 0; y < height; y++) {
+ const int16_t *p = src;
+ for (int x = 0; x < width; x++) {
+ gradient_h[x] = (p[1] >> shift) - (p[-1] >> shift);
+ gradient_v[x] = (p[src_stride] >> shift) - (p[-src_stride] >> shift);
+ p++;
+ }
+ gradient_h += gradient_stride;
+ gradient_v += gradient_stride;
+ src += src_stride;
+ }
+ if (pad) {
+ pad_int16(_gradient_h + 1 + gradient_stride, gradient_stride, width, height);
+ pad_int16(_gradient_v + 1 + gradient_stride, gradient_stride, width, height);
+ }
+}
+
+static void FUNC(apply_prof)(int16_t *dst, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y)
+{
+ const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit
+
+ int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE];
+ int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE];
+ FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0);
+
+ for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) {
+ for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) {
+ const int o = y * AFFINE_MIN_BLOCK_SIZE + x;
+ const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o];
+ const int val = src[x] + av_clip(di, -limit, limit - 1);
+ dst[x] = val;
+
+ }
+ src += MAX_PB_SIZE;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+static void FUNC(apply_prof_uni)(uint8_t *_dst, const ptrdiff_t _dst_stride, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y)
+{
+ const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit
+ pixel *dst = (pixel*)_dst;
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int shift = 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+ const int offset = 1 << (shift - 1);
+#else
+ const int offset = 0;
+#endif
+ int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE];
+ int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE];
+
+ FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0);
+
+ for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) {
+ for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) {
+ const int o = y * AFFINE_MIN_BLOCK_SIZE + x;
+ const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o];
+ const int val = src[x] + av_clip(di, -limit, limit - 1);
+ dst[x] = av_clip_pixel((val + offset) >> shift);
+
+ }
+ src += MAX_PB_SIZE;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(apply_prof_uni_w)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y,
+ const int denom, const int wx, const int _ox)
+{
+ const int limit = (1 << FFMAX(13, BIT_DEPTH + 1)); ///< dILimit
+ pixel *dst = (pixel*)_dst;
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ const int shift = denom + FFMAX(2, 14 - BIT_DEPTH);
+ const int offset = 1 << (shift - 1);
+ const int ox = _ox * (1 << (BIT_DEPTH - 8));
+ int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE];
+ int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE];
+
+ FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src, MAX_PB_SIZE, AFFINE_MIN_BLOCK_SIZE, AFFINE_MIN_BLOCK_SIZE, 0);
+
+ for (int y = 0; y < AFFINE_MIN_BLOCK_SIZE; y++) {
+ for (int x = 0; x < AFFINE_MIN_BLOCK_SIZE; x++) {
+ const int o = y * AFFINE_MIN_BLOCK_SIZE + x;
+ const int di = gradient_h[o] * diff_mv_x[o] + gradient_v[o] * diff_mv_y[o];
+ const int val = src[x] + av_clip(di, -limit, limit - 1);
+ dst[x] = av_clip_pixel(((val * wx + offset) >> shift) + ox);
+ }
+ src += MAX_PB_SIZE;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(derive_bdof_vx_vy)(const int16_t *_src0, const int16_t *_src1,
+ const int16_t **gradient_h, const int16_t **gradient_v, ptrdiff_t gradient_stride,
+ int* vx, int* vy)
+{
+ const int shift2 = 4;
+ const int shift3 = 1;
+ const int thres = 1 << 4;
+ int sgx2 = 0, sgy2 = 0, sgxgy = 0, sgxdi = 0, sgydi = 0;
+ const int16_t *src0 = _src0 - 1 - MAX_PB_SIZE;
+ const int16_t *src1 = _src1 - 1 - MAX_PB_SIZE;
+
+ for (int y = 0; y < BDOF_GRADIENT_SIZE; y++) {
+ for (int x = 0; x < BDOF_GRADIENT_SIZE; x++) {
+ const int diff = (src0[x] >> shift2) - (src1[x] >> shift2);
+ const int idx = gradient_stride * y + x;
+ const int temph = (gradient_h[0][idx] + gradient_h[1][idx]) >> shift3;
+ const int tempv = (gradient_v[0][idx] + gradient_v[1][idx]) >> shift3;
+ sgx2 += FFABS(temph);
+ sgy2 += FFABS(tempv);
+ sgxgy += VVC_SIGN(tempv) * temph;
+ sgxdi += -VVC_SIGN(temph) * diff;
+ sgydi += -VVC_SIGN(tempv) * diff;
+ }
+ src0 += MAX_PB_SIZE;
+ src1 += MAX_PB_SIZE;
+ }
+ *vx = sgx2 > 0 ? av_clip((sgxdi << 2) >> av_log2(sgx2) , -thres + 1, thres - 1) : 0;
+ *vy = sgy2 > 0 ? av_clip(((sgydi << 2) - ((*vx * sgxgy) >> 1)) >> av_log2(sgy2), -thres + 1, thres - 1) : 0;
+}
+
+static void FUNC(apply_bdof_min_block)(pixel* dst, const ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1,
+ const int16_t **gradient_h, const int16_t **gradient_v, const int vx, const int vy)
+{
+ const int shift4 = 15 - BIT_DEPTH;
+ const int offset4 = 1 << (shift4 - 1);
+
+ const int16_t* gh[] = { gradient_h[0] + 1 + BDOF_PADDED_SIZE, gradient_h[1] + 1 + BDOF_PADDED_SIZE };
+ const int16_t* gv[] = { gradient_v[0] + 1 + BDOF_PADDED_SIZE, gradient_v[1] + 1 + BDOF_PADDED_SIZE };
+
+ for (int y = 0; y < BDOF_BLOCK_SIZE; y++) {
+ for (int x = 0; x < BDOF_BLOCK_SIZE; x++) {
+ const int idx = y * BDOF_PADDED_SIZE + x;
+ const int bdof_offset = vx * (gh[0][idx] - gh[1][idx]) + vy * (gv[0][idx] - gv[1][idx]);
+ dst[x] = av_clip_pixel((src0[x] + offset4 + src1[x] + bdof_offset) >> shift4);
+ }
+ dst += dst_stride;
+ src0 += MAX_PB_SIZE;
+ src1 += MAX_PB_SIZE;
+ }
+}
+
+static void FUNC(apply_bdof)(uint8_t *_dst, const ptrdiff_t _dst_stride, int16_t *_src0, int16_t *_src1,
+ const int block_w, const int block_h)
+{
+ int16_t gradient_h[2][BDOF_PADDED_SIZE * BDOF_PADDED_SIZE];
+ int16_t gradient_v[2][BDOF_PADDED_SIZE * BDOF_PADDED_SIZE];
+ int vx, vy;
+ const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+ pixel* dst = (pixel*)_dst;
+
+ FUNC(prof_grad_filter)(gradient_h[0], gradient_v[0], BDOF_PADDED_SIZE,
+ _src0, MAX_PB_SIZE, block_w, block_h, 1);
+ pad_int16(_src0, MAX_PB_SIZE, block_w, block_h);
+ FUNC(prof_grad_filter)(gradient_h[1], gradient_v[1], BDOF_PADDED_SIZE,
+ _src1, MAX_PB_SIZE, block_w, block_h, 1);
+ pad_int16(_src1, MAX_PB_SIZE, block_w, block_h);
+
+ for (int y = 0; y < block_h; y += BDOF_BLOCK_SIZE) {
+ for (int x = 0; x < block_w; x += BDOF_BLOCK_SIZE) {
+ const int16_t* src0 = _src0 + y * MAX_PB_SIZE + x;
+ const int16_t* src1 = _src1 + y * MAX_PB_SIZE + x;
+ pixel *d = dst + x;
+ const int idx = BDOF_PADDED_SIZE * y + x;
+ const int16_t* gh[] = { gradient_h[0] + idx, gradient_h[1] + idx };
+ const int16_t* gv[] = { gradient_v[0] + idx, gradient_v[1] + idx };
+ FUNC(derive_bdof_vx_vy)(src0, src1, gh, gv, BDOF_PADDED_SIZE, &vx, &vy);
+ FUNC(apply_bdof_min_block)(d, dst_stride, src0, src1, gh, gv, vx, vy);
+ }
+ dst += BDOF_BLOCK_SIZE * dst_stride;
+ }
+}
+
+#define DMVR_FILTER(src, stride) \
+ (filter[0] * src[x] + \
+ filter[1] * src[x + stride])
+
+//8.5.3.2.2 Luma sample bilinear interpolation process
+static void FUNC(dmvr)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const intptr_t mx, const intptr_t my, const int width)
+{
+ const pixel *src = (const pixel *)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+#if BIT_DEPTH > 10
+ const int shift4 = BIT_DEPTH - 10;
+ const int offset4 = 1 << (shift4 - 1);
+ #define DMVR_SHIFT(s) (((s) + offset4) >> shift4)
+#else
+ #define DMVR_SHIFT(s) ((s) << (10 - BIT_DEPTH))
+#endif
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = DMVR_SHIFT(src[x]);
+ src += src_stride;
+ dst += MAX_PB_SIZE;
+ }
+#undef DMVR_SHIFT
+}
+
+//8.5.3.2.2 Luma sample bilinear interpolation process
+static void FUNC(dmvr_h)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const intptr_t mx, const intptr_t my, const int width)
+{
+ const pixel *src = (const pixel*)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[mx];
+ const int shift1 = BIT_DEPTH - 6;
+ const int offset1 = 1 << (shift1 - 1);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1;
+ src += src_stride;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+//8.5.3.2.2 Luma sample bilinear interpolation process
+static void FUNC(dmvr_v)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const intptr_t mx, const intptr_t my, const int width)
+{
+ const pixel *src = (pixel*)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[my];
+ const int shift1 = BIT_DEPTH - 6;
+ const int offset1 = 1 << (shift1 - 1);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = (DMVR_FILTER(src, src_stride) + offset1) >> shift1;
+ src += src_stride;
+ dst += MAX_PB_SIZE;
+ }
+
+}
+
+//8.5.3.2.2 Luma sample bilinear interpolation process
+static void FUNC(dmvr_hv)(int16_t *dst, const uint8_t *_src, const ptrdiff_t _src_stride,
+ const int height, const intptr_t mx, const intptr_t my, const int width)
+{
+ int16_t tmp_array[(MAX_PB_SIZE + BILINEAR_EXTRA) * MAX_PB_SIZE];
+ int16_t *tmp = tmp_array;
+ const pixel *src = (const pixel*)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ const int8_t *filter = ff_vvc_inter_luma_dmvr_filters[mx];
+ const int shift1 = BIT_DEPTH - 6;
+ const int offset1 = 1 << (shift1 - 1);
+ const int shift2 = 4;
+ const int offset2 = 1 << (shift2 - 1);
+
+ src -= BILINEAR_EXTRA_BEFORE * src_stride;
+ for (int y = 0; y < height + BILINEAR_EXTRA; y++) {
+ for (int x = 0; x < width; x++)
+ tmp[x] = (DMVR_FILTER(src, 1) + offset1) >> shift1;
+ src += src_stride;
+ tmp += MAX_PB_SIZE;
+ }
+
+ tmp = tmp_array + BILINEAR_EXTRA_BEFORE * MAX_PB_SIZE;
+ filter = ff_vvc_inter_luma_dmvr_filters[my];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = (DMVR_FILTER(tmp, MAX_PB_SIZE) + offset2) >> shift2;
+ tmp += MAX_PB_SIZE;
+ dst += MAX_PB_SIZE;
+ }
+}
+
+#define PEL_FUNC(dst, C, idx1, idx2, a) \
+ do { \
+ for (int w = 0; w < 7; w++) \
+ inter->dst[C][w][idx1][idx2] = FUNC(a); \
+ } while (0) \
+
+#define DIR_FUNCS(d, C, c) \
+ PEL_FUNC(put_##d, C, 0, 0, put_##d##_pixels); \
+ PEL_FUNC(put_##d, C, 0, 1, put_##d##_##c##_h); \
+ PEL_FUNC(put_##d, C, 1, 0, put_##d##_##c##_v); \
+ PEL_FUNC(put_##d, C, 1, 1, put_##d##_##c##_hv); \
+ PEL_FUNC(put_##d##_w, C, 0, 0, put_##d##_w_pixels); \
+ PEL_FUNC(put_##d##_w, C, 0, 1, put_##d##_##c##_w_h); \
+ PEL_FUNC(put_##d##_w, C, 1, 0, put_##d##_##c##_w_v); \
+ PEL_FUNC(put_##d##_w, C, 1, 1, put_##d##_##c##_w_hv);
+
+#define FUNCS(C, c) \
+ PEL_FUNC(put, C, 0, 0, put_pixels); \
+ PEL_FUNC(put, C, 0, 1, put_##c##_h); \
+ PEL_FUNC(put, C, 1, 0, put_##c##_v); \
+ PEL_FUNC(put, C, 1, 1, put_##c##_hv); \
+ DIR_FUNCS(uni, C, c); \
+
+static void FUNC(ff_vvc_inter_dsp_init)(VVCInterDSPContext *const inter)
+{
+ FUNCS(LUMA, luma);
+ FUNCS(CHROMA, chroma);
+
+ inter->avg = FUNC(avg);
+ inter->w_avg = FUNC(w_avg);
+
+ inter->dmvr[0][0] = FUNC(dmvr);
+ inter->dmvr[0][1] = FUNC(dmvr_h);
+ inter->dmvr[1][0] = FUNC(dmvr_v);
+ inter->dmvr[1][1] = FUNC(dmvr_hv);
+
+ inter->put_ciip = FUNC(put_ciip);
+ inter->put_gpm = FUNC(put_gpm);
+
+ inter->fetch_samples = FUNC(fetch_samples);
+ inter->bdof_fetch_samples = FUNC(bdof_fetch_samples);
+ inter->apply_prof = FUNC(apply_prof);
+ inter->apply_prof_uni = FUNC(apply_prof_uni);
+ inter->apply_prof_uni_w = FUNC(apply_prof_uni_w);
+ inter->apply_bdof = FUNC(apply_bdof);
+ inter->prof_grad_filter = FUNC(prof_grad_filter);
+ inter->sad = vvc_sad;
+}
+
+#undef FUNCS
+#undef PEL_FUNC
+#undef DMVR_FUNCS
diff --git a/libavcodec/vvc/vvcdec.h b/libavcodec/vvc/vvcdec.h
index 1ef71a4832..67d689d581 100644
--- a/libavcodec/vvc/vvcdec.h
+++ b/libavcodec/vvc/vvcdec.h
@@ -24,9 +24,11 @@
#ifndef AVCODEC_VVC_VVCDEC_H
#define AVCODEC_VVC_VVCDEC_H
+#include "libavcodec/videodsp.h"
#include "libavcodec/vvc.h"
#include "vvc_ps.h"
+#include "vvcdsp.h"
#define LUMA 0
#define CHROMA 1
@@ -104,6 +106,9 @@ typedef struct VVCFrameContext {
VVCFrame *ref;
+ VVCDSPContext vvcdsp;
+ VideoDSPContext vdsp;
+
struct VVCFrameThread *ft;
uint64_t decode_order;
diff --git a/libavcodec/vvc/vvcdsp.h b/libavcodec/vvc/vvcdsp.h
new file mode 100644
index 0000000000..b5a63c5833
--- /dev/null
+++ b/libavcodec/vvc/vvcdsp.h
@@ -0,0 +1,170 @@
+/*
+ * VVC DSP
+ *
+ * Copyright (C) 2021 Nuo Mi
+ *
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVCDSP_H
+#define AVCODEC_VVC_VVCDSP_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+enum TxType {
+ DCT2,
+ DST7,
+ DCT8,
+ N_TX_TYPE,
+};
+
+enum TxSize {
+ TX_SIZE_2,
+ TX_SIZE_4,
+ TX_SIZE_8,
+ TX_SIZE_16,
+ TX_SIZE_32,
+ TX_SIZE_64,
+ N_TX_SIZE,
+};
+
+typedef struct VVCInterDSPContext {
+ void (*put[2 /* luma, chroma */][7 /* log2(width) - 1 */][2 /* int, frac */][2 /* int, frac */])(
+ int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int height,
+ const int8_t *hf, const int8_t *vf, int width);
+
+ void (*put_uni[2 /* luma, chroma */][7 /* log2(width) - 1 */][2 /* int, frac */][2 /* int, frac */])(
+ uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, int height,
+ const int8_t *hf, const int8_t *vf, int width);
+
+ void (*put_uni_w[2 /* luma, chroma */][7 /* log2(width) - 1 */][2 /* int, frac */][2 /* int, frac */])(
+ uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride, int height,
+ int denom, int wx, int ox, const int8_t *hf, const int8_t *vf, int width);
+
+ void (*avg)(uint8_t *dst, ptrdiff_t dst_stride,
+ const int16_t *src0, const int16_t *src1, int width, int height);
+
+ void (*w_avg)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+ const int16_t *src0, const int16_t *src1, int width, int height,
+ int denom, int w0, int w1, int o0, int o1);
+
+ void (*put_ciip)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height,
+ const uint8_t *inter, ptrdiff_t inter_stride, int inter_weight);
+
+ void (*put_gpm)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height,
+ const int16_t *src0, const int16_t *src1,
+ const uint8_t *weights, int step_x, int step_y);
+
+ void (*fetch_samples)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int x_frac, int y_frac);
+ void (*bdof_fetch_samples)(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int x_frac, int y_frac,
+ int width, int height);
+
+ void (*prof_grad_filter)(int16_t *gradient_h, int16_t *gradient_v, const ptrdiff_t gradient_stride,
+ const int16_t *src, const ptrdiff_t src_stride, int width, int height, const int pad);
+ void (*apply_prof)(int16_t *dst, const int16_t *src, const int16_t *diff_mv_x, const int16_t *diff_mv_y);
+
+ void (*apply_prof_uni)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src,
+ const int16_t *diff_mv_x, const int16_t *diff_mv_y);
+ void (*apply_prof_uni_w)(uint8_t *dst, const ptrdiff_t dst_stride, const int16_t *src,
+ const int16_t *diff_mv_x, const int16_t *diff_mv_y, int denom, int wx, int ox);
+
+ void (*apply_bdof)(uint8_t *dst, ptrdiff_t dst_stride, int16_t *src0, int16_t *src1, int block_w, int block_h);
+
+ int (*sad)(const int16_t *src0, const int16_t *src1, int dx, int dy, int block_w, int block_h);
+ void (*dmvr[2][2])(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int height,
+ intptr_t mx, intptr_t my, int width);
+} VVCInterDSPContext;
+
+struct VVCLocalContext;
+
+typedef struct VVCIntraDSPContext {
+ void (*intra_cclm_pred)(const struct VVCLocalContext *lc, int x0, int y0, int w, int h);
+ void (*lmcs_scale_chroma)(struct VVCLocalContext *lc, int *dst, const int *coeff, int w, int h, int x0_cu, int y0_cu);
+ void (*intra_pred)(const struct VVCLocalContext *lc, int x0, int y0, int w, int h, int c_idx);
+ void (*pred_planar)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride);
+ void (*pred_mip)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride,
+ int mode_id, int is_transpose);
+ void (*pred_dc)(uint8_t *src, const uint8_t *top, const uint8_t *left, int w, int h, ptrdiff_t stride);
+ void (*pred_v)(uint8_t *src, const uint8_t *_top, int w, int h, ptrdiff_t stride);
+ void (*pred_h)(uint8_t *src, const uint8_t *_left, int w, int h, ptrdiff_t stride);
+ void (*pred_angular_v)(uint8_t *src, const uint8_t *_top, const uint8_t *_left,
+ int w, int h, ptrdiff_t stride, int c_idx, int mode, int ref_idx, int filter_flag, int need_pdpc);
+ void (*pred_angular_h)(uint8_t *src, const uint8_t *_top, const uint8_t *_left, int w, int h, ptrdiff_t stride,
+ int c_idx, int mode, int ref_idx, int filter_flag, int need_pdpc);
+} VVCIntraDSPContext;
+
+typedef struct VVCItxDSPContext {
+ void (*add_residual)(uint8_t *dst, const int *res, int width, int height, ptrdiff_t stride);
+ void (*add_residual_joint)(uint8_t *dst, const int *res, int width, int height, ptrdiff_t stride, int c_sign, int shift);
+ void (*pred_residual_joint)(int *buf, int width, int height, int c_sign, int shift);
+
+ void (*itx[N_TX_TYPE][N_TX_SIZE])(int *coeffs, ptrdiff_t step, size_t nz);
+ void (*transform_bdpcm)(int *coeffs, int width, int height, int vertical, int log2_transform_range);
+} VVCItxDSPContext;
+
+typedef struct VVCLMCSDSPContext {
+ void (*filter)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height, const uint8_t *lut);
+} VVCLMCSDSPContext;
+
+typedef struct VVCLFDSPContext {
+ int (*ladf_level[2 /* h, v */])(const uint8_t *pix, ptrdiff_t stride);
+
+ void (*filter_luma[2 /* h, v */])(uint8_t *pix, ptrdiff_t stride, const int32_t *beta, const int32_t *tc,
+ const uint8_t *no_p, const uint8_t *no_q, const uint8_t *max_len_p, const uint8_t *max_len_q, int hor_ctu_edge);
+ void (*filter_chroma[2 /* h, v */])(uint8_t *pix, ptrdiff_t stride, const int32_t *beta, const int32_t *tc,
+ const uint8_t *no_p, const uint8_t *no_q, const uint8_t *max_len_p, const uint8_t *max_len_q, int shift);
+} VVCLFDSPContext;
+
+struct SAOParams;
+typedef struct VVCSAODSPContext {
+ void (*band_filter[9])(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+ const int16_t *sao_offset_val, int sao_left_class, int width, int height);
+ /* implicit src_stride parameter has value of 2 * MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE */
+ void (*edge_filter[9])(uint8_t *dst /* align 16 */, const uint8_t *src /* align 32 */, ptrdiff_t dst_stride,
+ const int16_t *sao_offset_val, int sao_eo_class, int width, int height);
+ void (*edge_restore[2])(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+ const struct SAOParams *sao, const int *borders, int width, int height, int c_idx,
+ const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge);
+} VVCSAODSPContext;
+
+typedef struct VVCALFDSPContext {
+ void (*filter[2 /* luma, chroma */])(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride,
+ int width, int height, const int16_t *filter, const int16_t *clip, int vb_pos);
+ void (*filter_cc)(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *luma, ptrdiff_t luma_stride,
+ int width, int height, int hs, int vs, const int16_t *filter, int vb_pos);
+
+ void (*classify)(int *class_idx, int *transpose_idx, const uint8_t *src, ptrdiff_t src_stride, int width, int height,
+ int vb_pos, int *gradient_tmp);
+ void (*recon_coeff_and_clip)(int16_t *coeff, int16_t *clip, const int *class_idx, const int *transpose_idx, int size,
+ const int16_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt);
+} VVCALFDSPContext;
+
+typedef struct VVCDSPContext {
+ VVCInterDSPContext inter;
+ VVCIntraDSPContext intra;
+ VVCItxDSPContext itx;
+ VVCLMCSDSPContext lmcs;
+ VVCLFDSPContext lf;
+ VVCSAODSPContext sao;
+ VVCALFDSPContext alf;
+} VVCDSPContext;
+
+void ff_vvc_dsp_init(VVCDSPContext *hpc, int bit_depth);
+
+#endif /* AVCODEC_VVC_VVCDSP_H */
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH v7 08/14] vvcdec: add inv transform 1d
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
` (5 preceding siblings ...)
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 07/14] vvcdec: add inter prediction Nuo Mi
@ 2023-12-10 15:58 ` Nuo Mi
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 09/14] vvcdec: add intra prediction Nuo Mi
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 10/14] vvcdec: add LMCS, Deblocking, SAO, and ALF filters Nuo Mi
8 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 15:58 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
---
libavcodec/vvc/Makefile | 1 +
libavcodec/vvc/vvc_itx_1d.c | 708 ++++++++++++++++++++++++++++++++++++
libavcodec/vvc/vvc_itx_1d.h | 52 +++
3 files changed, 761 insertions(+)
create mode 100644 libavcodec/vvc/vvc_itx_1d.c
create mode 100644 libavcodec/vvc/vvc_itx_1d.h
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index 61b1ab39de..5796f9ad42 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -6,6 +6,7 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
vvc/vvc_ctu.o \
vvc/vvc_data.o \
vvc/vvc_inter.o \
+ vvc/vvc_itx_1d.o \
vvc/vvc_mvs.o \
vvc/vvc_ps.o \
vvc/vvc_refs.o \
diff --git a/libavcodec/vvc/vvc_itx_1d.c b/libavcodec/vvc/vvc_itx_1d.c
new file mode 100644
index 0000000000..01a50aad25
--- /dev/null
+++ b/libavcodec/vvc/vvc_itx_1d.c
@@ -0,0 +1,708 @@
+/*
+ * VVC 1D transform
+ *
+ * Copyright (C) 2023 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* The copyright in this software is being made available under the BSD
+ * License, included below. This software may be subject to other third party
+ * and contributor rights, including patent rights, and no such rights are
+ * granted under this license.
+ *
+ * Copyright (c) 2010-2021, ITU/ISO/IEC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* optimizaed with partial butterfly, see Hung C-Y, Landman P (1997)
+ Compact inverse discrete cosine transform circuit for MPEG video decoding.
+ */
+
+#include "vvc_data.h"
+#include "vvc_itx_1d.h"
+#include "libavutil/avutil.h"
+
+#define G2(m) ((nz > 2) ? (m) : 0)
+#define G4(m) ((nz > 4) ? (m) : 0)
+#define G8(m) ((nz > 8) ? (m) : 0)
+#define G16(m) ((nz > 16) ? (m) : 0)
+
+/*
+transmatrix[2][2] = {
+ { a, a },
+ { a, -a },
+}
+ */
+void ff_vvc_inv_dct2_2(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+
+ coeffs[0 * stride] = a * (x0 + x1);
+ coeffs[1 * stride] = a * (x0 - x1);
+}
+
+/*
+transmatrix[4][4] = {
+ { a, a, a, a},
+ { b, c, -c, -b},
+ { a, -a, -a, a},
+ { c, -b, b, -c},
+}
+ */
+void ff_vvc_inv_dct2_4(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64, b = 83, c = 36;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int E[2] = {
+ a * (x0 + G2(+x2)),
+ a * (x0 + G2(-x2)),
+ };
+ const int O[2] = {
+ b * x1 + G2(+c * x3),
+ c * x1 + G2(-b * x3),
+ };
+
+ coeffs[0 * stride] = E[0] + O[0];
+ coeffs[1 * stride] = E[1] + O[1];
+ coeffs[2 * stride] = E[1] - O[1];
+ coeffs[3 * stride] = E[0] - O[0];
+}
+
+/*
+transmatrix[8][8] = {
+ { a, a, a, a, a, a, a, a},
+ { d, e, f, g, -g, -f, -e, -d},
+ { b, c, -c, -b, -b, -c, c, b},
+ { e, -g, -d, -f, f, d, g, -e},
+ { a, -a, -a, a, a, -a, -a, a},
+ { f, -d, g, e, -e, -g, d, -f},
+ { c, -b, b, -c, -c, b, -b, c},
+ { g, -f, e, -d, d, -e, f, -g},
+}
+ */
+void ff_vvc_inv_dct2_8(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride];
+ const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride];
+ const int EE[2] = {
+ a * (x0 + G4(+x4)),
+ a * (x0 + G4(-x4)),
+ };
+ const int EO[2] = {
+ G2(b * x2) + G4(+c * x6),
+ G2(c * x2) + G4(-b * x6),
+ };
+ const int E[4] = {
+ EE[0] + EO[0], EE[1] + EO[1],
+ EE[1] - EO[1], EE[0] - EO[0],
+ };
+ const int O[4] = {
+ d * x1 + G2(+e * x3) + G4(+f * x5 + g * x7),
+ e * x1 + G2(-g * x3) + G4(-d * x5 - f * x7),
+ f * x1 + G2(-d * x3) + G4(+g * x5 + e * x7),
+ g * x1 + G2(-f * x3) + G4(+e * x5 - d * x7),
+ };
+
+ coeffs[0 * stride] = E[0] + O[0];
+ coeffs[1 * stride] = E[1] + O[1];
+ coeffs[2 * stride] = E[2] + O[2];
+ coeffs[3 * stride] = E[3] + O[3];
+ coeffs[4 * stride] = E[3] - O[3];
+ coeffs[5 * stride] = E[2] - O[2];
+ coeffs[6 * stride] = E[1] - O[1];
+ coeffs[7 * stride] = E[0] - O[0];
+}
+
+/*
+transmatrix[16][16] = {
+ { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a},
+ { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h},
+ { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d},
+ { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i},
+ { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b},
+ { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j},
+ { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e},
+ { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k},
+ { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a},
+ { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l},
+ { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f},
+ { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m},
+ { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c},
+ { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n},
+ { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g},
+ { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o},
+}
+ */
+void ff_vvc_inv_dct2_16(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90;
+ const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride];
+ const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride];
+ const int x8 = coeffs[8 * stride], x9 = coeffs[9 * stride];
+ const int x10 = coeffs[10 * stride], x11 = coeffs[11 * stride];
+ const int x12 = coeffs[12 * stride], x13 = coeffs[13 * stride];
+ const int x14 = coeffs[14 * stride], x15 = coeffs[15 * stride];
+ const int EEE[2] = {
+ a * (x0 + G8(+x8)),
+ a * (x0 + G8(-x8)),
+ };
+ const int EEO[2] = {
+ G4(b * x4) + G8(+c * x12),
+ G4(c * x4) + G8(-b * x12),
+ };
+ const int EE[4] = {
+ EEE[0] + EEO[0], EEE[1] + EEO[1],
+ EEE[1] - EEO[1], EEE[0] - EEO[0],
+ };
+ const int EO[4] = {
+ G2(d * x2) + G4(+e * x6) + G8(+f * x10 + g * x14),
+ G2(e * x2) + G4(-g * x6) + G8(-d * x10 - f * x14),
+ G2(f * x2) + G4(-d * x6) + G8(+g * x10 + e * x14),
+ G2(g * x2) + G4(-f * x6) + G8(+e * x10 - d * x14),
+ };
+ const int E[8] = {
+ EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3],
+ EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0],
+ };
+ const int O[8] = {
+ h * x1 + G2(+i * x3) + G4(+j * x5 + k * x7) + G8(+l * x9 + m * x11 + n * x13 + o * x15),
+ i * x1 + G2(+l * x3) + G4(+o * x5 - m * x7) + G8(-j * x9 - h * x11 - k * x13 - n * x15),
+ j * x1 + G2(+o * x3) + G4(-k * x5 - i * x7) + G8(-n * x9 + l * x11 + h * x13 + m * x15),
+ k * x1 + G2(-m * x3) + G4(-i * x5 + o * x7) + G8(+h * x9 + n * x11 - j * x13 - l * x15),
+ l * x1 + G2(-j * x3) + G4(-n * x5 + h * x7) + G8(-o * x9 - i * x11 + m * x13 + k * x15),
+ m * x1 + G2(-h * x3) + G4(+l * x5 + n * x7) + G8(-i * x9 + k * x11 + o * x13 - j * x15),
+ n * x1 + G2(-k * x3) + G4(+h * x5 - j * x7) + G8(+m * x9 + o * x11 - l * x13 + i * x15),
+ o * x1 + G2(-n * x3) + G4(+m * x5 - l * x7) + G8(+k * x9 - j * x11 + i * x13 - h * x15),
+ };
+
+ coeffs[0 * stride] = E[0] + O[0];
+ coeffs[1 * stride] = E[1] + O[1];
+ coeffs[2 * stride] = E[2] + O[2];
+ coeffs[3 * stride] = E[3] + O[3];
+ coeffs[4 * stride] = E[4] + O[4];
+ coeffs[5 * stride] = E[5] + O[5];
+ coeffs[6 * stride] = E[6] + O[6];
+ coeffs[7 * stride] = E[7] + O[7];
+ coeffs[8 * stride] = E[7] - O[7];
+ coeffs[9 * stride] = E[6] - O[6];
+ coeffs[10 * stride] = E[5] - O[5];
+ coeffs[11 * stride] = E[4] - O[4];
+ coeffs[12 * stride] = E[3] - O[3];
+ coeffs[13 * stride] = E[2] - O[2];
+ coeffs[14 * stride] = E[1] - O[1];
+ coeffs[15 * stride] = E[0] - O[0];
+}
+
+/*
+transMatrix[32][32] = {
+ { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a},
+ { p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, -E, -D, -C, -B, -A, -z, -y, -x, -w, -v, -u, -t, -s, -r, -q, -p},
+ { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h, -h, -i, -j, -k, -l, -m, -n, -o, o, n, m, l, k, j, i, h},
+ { q, t, w, z, C, -E, -B, -y, -v, -s, -p, -r, -u, -x, -A, -D, D, A, x, u, r, p, s, v, y, B, E, -C, -z, -w, -t, -q},
+ { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d, d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d},
+ { r, w, B, -D, -y, -t, -p, -u, -z, -E, A, v, q, s, x, C, -C, -x, -s, -q, -v, -A, E, z, u, p, t, y, D, -B, -w, -r},
+ { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i, -i, -l, -o, m, j, h, k, n, -n, -k, -h, -j, -m, o, l, i},
+ { s, z, -D, -w, -p, -v, -C, A, t, r, y, -E, -x, -q, -u, -B, B, u, q, x, E, -y, -r, -t, -A, C, v, p, w, D, -z, -s},
+ { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b},
+ { t, C, -y, -p, -x, D, u, s, B, -z, -q, -w, E, v, r, A, -A, -r, -v, -E, w, q, z, -B, -s, -u, -D, x, p, y, -C, -t},
+ { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j, -j, -o, k, i, n, -l, -h, -m, m, h, l, -n, -i, -k, o, j},
+ { u, -E, -t, -v, D, s, w, -C, -r, -x, B, q, y, -A, -p, -z, z, p, A, -y, -q, -B, x, r, C, -w, -s, -D, v, t, E, -u},
+ { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e, e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e},
+ { v, -B, -p, -C, u, w, -A, -q, -D, t, x, -z, -r, -E, s, y, -y, -s, E, r, z, -x, -t, D, q, A, -w, -u, C, p, B, -v},
+ { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k, -k, m, i, -o, -h, -n, j, l, -l, -j, n, h, o, -i, -m, k},
+ { w, -y, -u, A, s, -C, -q, E, p, D, -r, -B, t, z, -v, -x, x, v, -z, -t, B, r, -D, -p, -E, q, C, -s, -A, u, y, -w},
+ { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a},
+ { x, -v, -z, t, B, -r, -D, p, -E, -q, C, s, -A, -u, y, w, -w, -y, u, A, -s, -C, q, E, -p, D, r, -B, -t, z, v, -x},
+ { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l, -l, j, n, -h, o, i, -m, -k, k, m, -i, -o, h, -n, -j, l},
+ { y, -s, -E, r, -z, -x, t, D, -q, A, w, -u, -C, p, -B, -v, v, B, -p, C, u, -w, -A, q, -D, -t, x, z, -r, E, s, -y},
+ { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f, f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f},
+ { z, -p, A, y, -q, B, x, -r, C, w, -s, D, v, -t, E, u, -u, -E, t, -v, -D, s, -w, -C, r, -x, -B, q, -y, -A, p, -z},
+ { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m, -m, h, -l, -n, i, -k, -o, j, -j, o, k, -i, n, l, -h, m},
+ { A, -r, v, -E, -w, q, -z, -B, s, -u, D, x, -p, y, C, -t, t, -C, -y, p, -x, -D, u, -s, B, z, -q, w, E, -v, r, -A},
+ { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c},
+ { B, -u, q, -x, E, y, -r, t, -A, -C, v, -p, w, -D, -z, s, -s, z, D, -w, p, -v, C, A, -t, r, -y, -E, x, -q, u, -B},
+ { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n, -n, k, -h, j, -m, -o, l, -i, i, -l, o, m, -j, h, -k, n},
+ { C, -x, s, -q, v, -A, -E, z, -u, p, -t, y, -D, -B, w, -r, r, -w, B, D, -y, t, -p, u, -z, E, A, -v, q, -s, x, -C},
+ { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g, g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g},
+ { D, -A, x, -u, r, -p, s, -v, y, -B, E, C, -z, w, -t, q, -q, t, -w, z, -C, -E, B, -y, v, -s, p, -r, u, -x, A, -D},
+ { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o, -o, n, -m, l, -k, j, -i, h, -h, i, -j, k, -l, m, -n, o},
+ { E, -D, C, -B, A, -z, y, -x, w, -v, u, -t, s, -r, q, -p, p, -q, r, -s, t, -u, v, -w, x, -y, z, -A, B, -C, D, -E},
+}
+ */
+void ff_vvc_inv_dct2_32(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90;
+ const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9, p = 90;
+ const int q = 90, r = 88, s = 85, t = 82, u = 78, v = 73, w = 67, x = 61;
+ const int y = 54, z = 46, A = 38, B = 31, C = 22, D = 13, E_= 4;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride];
+ const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride];
+ const int x8 = coeffs[8 * stride], x9 = coeffs[9 * stride];
+ const int x10 = coeffs[10 * stride], x11 = coeffs[11 * stride];
+ const int x12 = coeffs[12 * stride], x13 = coeffs[13 * stride];
+ const int x14 = coeffs[14 * stride], x15 = coeffs[15 * stride];
+ const int x16 = coeffs[16 * stride], x17 = coeffs[17 * stride];
+ const int x18 = coeffs[18 * stride], x19 = coeffs[19 * stride];
+ const int x20 = coeffs[20 * stride], x21 = coeffs[21 * stride];
+ const int x22 = coeffs[22 * stride], x23 = coeffs[23 * stride];
+ const int x24 = coeffs[24 * stride], x25 = coeffs[25 * stride];
+ const int x26 = coeffs[26 * stride], x27 = coeffs[27 * stride];
+ const int x28 = coeffs[28 * stride], x29 = coeffs[29 * stride];
+ const int x30 = coeffs[30 * stride], x31 = coeffs[31 * stride];
+ const int EEEE[2] = {
+ a * (x0 + G16(+x16)),
+ a * (x0 + G16(-x16)),
+ };
+ const int EEEO[2] = {
+ G8(b * x8) + G16(+c * x24),
+ G8(c * x8) + G16(-b * x24),
+ };
+ const int EEE[4] = {
+ EEEE[0] + EEEO[0], EEEE[1] + EEEO[1],
+ EEEE[1] - EEEO[1], EEEE[0] - EEEO[0],
+ };
+ const int EEO[4] = {
+ G4(d * x4) + G8(+e * x12) + G16(+f * x20 + g * x28),
+ G4(e * x4) + G8(-g * x12) + G16(-d * x20 - f * x28),
+ G4(f * x4) + G8(-d * x12) + G16(+g * x20 + e * x28),
+ G4(g * x4) + G8(-f * x12) + G16(+e * x20 - d * x28),
+ };
+ const int EE[8] = {
+ EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3],
+ EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0],
+ };
+ const int EO[8] = {
+ G2(h * x2) + G4(+i * x6) + G8(+ j * x10 + k * x14) + G16(+l * x18 + m * x22 + n * x26 + o * x30),
+ G2(i * x2) + G4(+l * x6) + G8(+ o * x10 - m * x14) + G16(-j * x18 - h * x22 - k * x26 - n * x30),
+ G2(j * x2) + G4(+o * x6) + G8(- k * x10 - i * x14) + G16(-n * x18 + l * x22 + h * x26 + m * x30),
+ G2(k * x2) + G4(-m * x6) + G8(- i * x10 + o * x14) + G16(+h * x18 + n * x22 - j * x26 - l * x30),
+ G2(l * x2) + G4(-j * x6) + G8(- n * x10 + h * x14) + G16(-o * x18 - i * x22 + m * x26 + k * x30),
+ G2(m * x2) + G4(-h * x6) + G8(+ l * x10 + n * x14) + G16(-i * x18 + k * x22 + o * x26 - j * x30),
+ G2(n * x2) + G4(-k * x6) + G8(+ h * x10 - j * x14) + G16(+m * x18 + o * x22 - l * x26 + i * x30),
+ G2(o * x2) + G4(-n * x6) + G8(+ m * x10 - l * x14) + G16(+k * x18 - j * x22 + i * x26 - h * x30),
+ };
+ const int E[16] = {
+ EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7],
+ EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0],
+ };
+ const int O[16] = {
+ p * x1 + G2(+q * x3) + G4(+r * x5 + s * x7) + G8(+t * x9 + u * x11 + v * x13 + w * x15) + G16(+x * x17 + y * x19 + z * x21 + A * x23 + B * x25 + C * x27 + D * x29 + E_* x31),
+ q * x1 + G2(+t * x3) + G4(+w * x5 + z * x7) + G8(+C * x9 - E_* x11 - B * x13 - y * x15) + G16(-v * x17 - s * x19 - p * x21 - r * x23 - u * x25 - x * x27 - A * x29 - D * x31),
+ r * x1 + G2(+w * x3) + G4(+B * x5 - D * x7) + G8(-y * x9 - t * x11 - p * x13 - u * x15) + G16(-z * x17 - E_* x19 + A * x21 + v * x23 + q * x25 + s * x27 + x * x29 + C * x31),
+ s * x1 + G2(+z * x3) + G4(-D * x5 - w * x7) + G8(-p * x9 - v * x11 - C * x13 + A * x15) + G16(+t * x17 + r * x19 + y * x21 - E_* x23 - x * x25 - q * x27 - u * x29 - B * x31),
+ t * x1 + G2(+C * x3) + G4(-y * x5 - p * x7) + G8(-x * x9 + D * x11 + u * x13 + s * x15) + G16(+B * x17 - z * x19 - q * x21 - w * x23 + E_* x25 + v * x27 + r * x29 + A * x31),
+ u * x1 + G2(-E_* x3) + G4(-t * x5 - v * x7) + G8(+D * x9 + s * x11 + w * x13 - C * x15) + G16(-r * x17 - x * x19 + B * x21 + q * x23 + y * x25 - A * x27 - p * x29 - z * x31),
+ v * x1 + G2(-B * x3) + G4(-p * x5 - C * x7) + G8(+u * x9 + w * x11 - A * x13 - q * x15) + G16(-D * x17 + t * x19 + x * x21 - z * x23 - r * x25 - E_* x27 + s * x29 + y * x31),
+ w * x1 + G2(-y * x3) + G4(-u * x5 + A * x7) + G8(+s * x9 - C * x11 - q * x13 + E_* x15) + G16(+p * x17 + D * x19 - r * x21 - B * x23 + t * x25 + z * x27 - v * x29 - x * x31),
+ x * x1 + G2(-v * x3) + G4(-z * x5 + t * x7) + G8(+B * x9 - r * x11 - D * x13 + p * x15) + G16(-E_* x17 - q * x19 + C * x21 + s * x23 - A * x25 - u * x27 + y * x29 + w * x31),
+ y * x1 + G2(-s * x3) + G4(-E_* x5 + r * x7) + G8(-z * x9 - x * x11 + t * x13 + D * x15) + G16(-q * x17 + A * x19 + w * x21 - u * x23 - C * x25 + p * x27 - B * x29 - v * x31),
+ z * x1 + G2(-p * x3) + G4(+A * x5 + y * x7) + G8(-q * x9 + B * x11 + x * x13 - r * x15) + G16(+C * x17 + w * x19 - s * x21 + D * x23 + v * x25 - t * x27 + E_* x29 + u * x31),
+ A * x1 + G2(-r * x3) + G4(+v * x5 - E_* x7) + G8(-w * x9 + q * x11 - z * x13 - B * x15) + G16(+s * x17 - u * x19 + D * x21 + x * x23 - p * x25 + y * x27 + C * x29 - t * x31),
+ B * x1 + G2(-u * x3) + G4(+q * x5 - x * x7) + G8(+E_* x9 + y * x11 - r * x13 + t * x15) + G16(-A * x17 - C * x19 + v * x21 - p * x23 + w * x25 - D * x27 - z * x29 + s * x31),
+ C * x1 + G2(-x * x3) + G4(+s * x5 - q * x7) + G8(+v * x9 - A * x11 - E_* x13 + z * x15) + G16(-u * x17 + p * x19 - t * x21 + y * x23 - D * x25 - B * x27 + w * x29 - r * x31),
+ D * x1 + G2(-A * x3) + G4(+x * x5 - u * x7) + G8(+r * x9 - p * x11 + s * x13 - v * x15) + G16(+y * x17 - B * x19 + E_* x21 + C * x23 - z * x25 + w * x27 - t * x29 + q * x31),
+ E_* x1 + G2(-D * x3) + G4(+C * x5 - B * x7) + G8(+A * x9 - z * x11 + y * x13 - x * x15) + G16(+w * x17 - v * x19 + u * x21 - t * x23 + s * x25 - r * x27 + q * x29 - p * x31),
+ };
+
+ coeffs[0 * stride] = E[0] + O[0];
+ coeffs[1 * stride] = E[1] + O[1];
+ coeffs[2 * stride] = E[2] + O[2];
+ coeffs[3 * stride] = E[3] + O[3];
+ coeffs[4 * stride] = E[4] + O[4];
+ coeffs[5 * stride] = E[5] + O[5];
+ coeffs[6 * stride] = E[6] + O[6];
+ coeffs[7 * stride] = E[7] + O[7];
+ coeffs[8 * stride] = E[8] + O[8];
+ coeffs[9 * stride] = E[9] + O[9];
+ coeffs[10 * stride] = E[10] + O[10];
+ coeffs[11 * stride] = E[11] + O[11];
+ coeffs[12 * stride] = E[12] + O[12];
+ coeffs[13 * stride] = E[13] + O[13];
+ coeffs[14 * stride] = E[14] + O[14];
+ coeffs[15 * stride] = E[15] + O[15];
+ coeffs[16 * stride] = E[15] - O[15];
+ coeffs[17 * stride] = E[14] - O[14];
+ coeffs[18 * stride] = E[13] - O[13];
+ coeffs[19 * stride] = E[12] - O[12];
+ coeffs[20 * stride] = E[11] - O[11];
+ coeffs[21 * stride] = E[10] - O[10];
+ coeffs[22 * stride] = E[9] - O[9];
+ coeffs[23 * stride] = E[8] - O[8];
+ coeffs[24 * stride] = E[7] - O[7];
+ coeffs[25 * stride] = E[6] - O[6];
+ coeffs[26 * stride] = E[5] - O[5];
+ coeffs[27 * stride] = E[4] - O[4];
+ coeffs[28 * stride] = E[3] - O[3];
+ coeffs[29 * stride] = E[2] - O[2];
+ coeffs[30 * stride] = E[1] - O[1];
+ coeffs[31 * stride] = E[0] - O[0];
+}
+
+/*
+transMatrix[64][64] = {
+ { aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa },
+ { bf, bg, bh, bi, bj, bk, bl, bm, bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, -ck, -cj, -ci, -ch, -cg, -cf, -ce, -cd, -cc, -cb, -ca, -bz, -by, -bx, -bw, -bv, -bu, -bt, -bs, -br, -bq, -bp, -bo, -bn, -bm, -bl, -bk, -bj, -bi, -bh, -bg, -bf },
+ { ap, aq, ar, as, at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, -be, -bd, -bc, -bb, -ba, -az, -ay, -ax, -aw, -av, -au, -at, -as, -ar, -aq, -ap, -ap, -aq, -ar, -as, -at, -au, -av, -aw, -ax, -ay, -az, -ba, -bb, -bc, -bd, -be, be, bd, bc, bb, ba, az, ay, ax, aw, av, au, at, as, ar, aq, ap },
+ { bg, bj, bm, bp, bs, bv, by, cb, ce, ch, ck, -ci, -cf, -cc, -bz, -bw, -bt, -bq, -bn, -bk, -bh, -bf, -bi, -bl, -bo, -br, -bu, -bx, -ca, -cd, -cg, -cj, cj, cg, cd, ca, bx, bu, br, bo, bl, bi, bf, bh, bk, bn, bq, bt, bw, bz, cc, cf, ci, -ck, -ch, -ce, -cb, -by, -bv, -bs, -bp, -bm, -bj, -bg },
+ { ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah, ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah },
+ { bh, bm, br, bw, cb, cg, -ck, -cf, -ca, -bv, -bq, -bl, -bg, -bi, -bn, -bs, -bx, -cc, -ch, cj, ce, bz, bu, bp, bk, bf, bj, bo, bt, by, cd, ci, -ci, -cd, -by, -bt, -bo, -bj, -bf, -bk, -bp, -bu, -bz, -ce, -cj, ch, cc, bx, bs, bn, bi, bg, bl, bq, bv, ca, cf, ck, -cg, -cb, -bw, -br, -bm, -bh },
+ { aq, at, aw, az, bc, -be, -bb, -ay, -av, -as, -ap, -ar, -au, -ax, -ba, -bd, bd, ba, ax, au, ar, ap, as, av, ay, bb, be, -bc, -az, -aw, -at, -aq, -aq, -at, -aw, -az, -bc, be, bb, ay, av, as, ap, ar, au, ax, ba, bd, -bd, -ba, -ax, -au, -ar, -ap, -as, -av, -ay, -bb, -be, bc, az, aw, at, aq },
+ { bi, bp, bw, cd, ck, -ce, -bx, -bq, -bj, -bh, -bo, -bv, -cc, -cj, cf, by, br, bk, bg, bn, bu, cb, ci, -cg, -bz, -bs, -bl, -bf, -bm, -bt, -ca, -ch, ch, ca, bt, bm, bf, bl, bs, bz, cg, -ci, -cb, -bu, -bn, -bg, -bk, -br, -by, -cf, cj, cc, bv, bo, bh, bj, bq, bx, ce, -ck, -cd, -bw, -bp, -bi },
+ { ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad },
+ { bj, bs, cb, ck, -cc, -bt, -bk, -bi, -br, -ca, -cj, cd, bu, bl, bh, bq, bz, ci, -ce, -bv, -bm, -bg, -bp, -by, -ch, cf, bw, bn, bf, bo, bx, cg, -cg, -bx, -bo, -bf, -bn, -bw, -cf, ch, by, bp, bg, bm, bv, ce, -ci, -bz, -bq, -bh, -bl, -bu, -cd, cj, ca, br, bi, bk, bt, cc, -ck, -cb, -bs, -bj },
+ { ar, aw, bb, -bd, -ay, -at, -ap, -au, -az, -be, ba, av, aq, as, ax, bc, -bc, -ax, -as, -aq, -av, -ba, be, az, au, ap, at, ay, bd, -bb, -aw, -ar, -ar, -aw, -bb, bd, ay, at, ap, au, az, be, -ba, -av, -aq, -as, -ax, -bc, bc, ax, as, aq, av, ba, -be, -az, -au, -ap, -at, -ay, -bd, bb, aw, ar },
+ { bk, bv, cg, -ce, -bt, -bi, -bm, -bx, -ci, cc, br, bg, bo, bz, ck, -ca, -bp, -bf, -bq, -cb, cj, by, bn, bh, bs, cd, -ch, -bw, -bl, -bj, -bu, -cf, cf, bu, bj, bl, bw, ch, -cd, -bs, -bh, -bn, -by, -cj, cb, bq, bf, bp, ca, -ck, -bz, -bo, -bg, -br, -cc, ci, bx, bm, bi, bt, ce, -cg, -bv, -bk },
+ { ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai, ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai },
+ { bl, by, -ck, -bx, -bk, -bm, -bz, cj, bw, bj, bn, ca, -ci, -bv, -bi, -bo, -cb, ch, bu, bh, bp, cc, -cg, -bt, -bg, -bq, -cd, cf, bs, bf, br, ce, -ce, -br, -bf, -bs, -cf, cd, bq, bg, bt, cg, -cc, -bp, -bh, -bu, -ch, cb, bo, bi, bv, ci, -ca, -bn, -bj, -bw, -cj, bz, bm, bk, bx, ck, -by, -bl },
+ { as, az, -bd, -aw, -ap, -av, -bc, ba, at, ar, ay, -be, -ax, -aq, -au, -bb, bb, au, aq, ax, be, -ay, -ar, -at, -ba, bc, av, ap, aw, bd, -az, -as, -as, -az, bd, aw, ap, av, bc, -ba, -at, -ar, -ay, be, ax, aq, au, bb, -bb, -au, -aq, -ax, -be, ay, ar, at, ba, -bc, -av, -ap, -aw, -bd, az, as },
+ { bm, cb, -cf, -bq, -bi, -bx, cj, bu, bf, bt, ci, -by, -bj, -bp, -ce, cc, bn, bl, ca, -cg, -br, -bh, -bw, ck, bv, bg, bs, ch, -bz, -bk, -bo, -cd, cd, bo, bk, bz, -ch, -bs, -bg, -bv, -ck, bw, bh, br, cg, -ca, -bl, -bn, -cc, ce, bp, bj, by, -ci, -bt, -bf, -bu, -cj, bx, bi, bq, cf, -cb, -bm },
+ { ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab },
+ { bn, ce, -ca, -bj, -br, -ci, bw, bf, bv, -cj, -bs, -bi, -bz, cf, bo, bm, cd, -cb, -bk, -bq, -ch, bx, bg, bu, -ck, -bt, -bh, -by, cg, bp, bl, cc, -cc, -bl, -bp, -cg, by, bh, bt, ck, -bu, -bg, -bx, ch, bq, bk, cb, -cd, -bm, -bo, -cf, bz, bi, bs, cj, -bv, -bf, -bw, ci, br, bj, ca, -ce, -bn },
+ { at, bc, -ay, -ap, -ax, bd, au, as, bb, -az, -aq, -aw, be, av, ar, ba, -ba, -ar, -av, -be, aw, aq, az, -bb, -as, -au, -bd, ax, ap, ay, -bc, -at, -at, -bc, ay, ap, ax, -bd, -au, -as, -bb, az, aq, aw, -be, -av, -ar, -ba, ba, ar, av, be, -aw, -aq, -az, bb, as, au, bd, -ax, -ap, -ay, bc, at },
+ { bo, ch, -bv, -bh, -ca, cc, bj, bt, -cj, -bq, -bm, -cf, bx, bf, by, -ce, -bl, -br, -ck, bs, bk, cd, -bz, -bg, -bw, cg, bn, bp, ci, -bu, -bi, -cb, cb, bi, bu, -ci, -bp, -bn, -cg, bw, bg, bz, -cd, -bk, -bs, ck, br, bl, ce, -by, -bf, -bx, cf, bm, bq, cj, -bt, -bj, -cc, ca, bh, bv, -ch, -bo },
+ { aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj, aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj },
+ { bp, ck, -bq, -bo, -cj, br, bn, ci, -bs, -bm, -ch, bt, bl, cg, -bu, -bk, -cf, bv, bj, ce, -bw, -bi, -cd, bx, bh, cc, -by, -bg, -cb, bz, bf, ca, -ca, -bf, -bz, cb, bg, by, -cc, -bh, -bx, cd, bi, bw, -ce, -bj, -bv, cf, bk, bu, -cg, -bl, -bt, ch, bm, bs, -ci, -bn, -br, cj, bo, bq, -ck, -bp },
+ { au, -be, -at, -av, bd, as, aw, -bc, -ar, -ax, bb, aq, ay, -ba, -ap, -az, az, ap, ba, -ay, -aq, -bb, ax, ar, bc, -aw, -as, -bd, av, at, be, -au, -au, be, at, av, -bd, -as, -aw, bc, ar, ax, -bb, -aq, -ay, ba, ap, az, -az, -ap, -ba, ay, aq, bb, -ax, -ar, -bc, aw, as, bd, -av, -at, -be, au },
+ { bq, -ci, -bl, -bv, cd, bg, ca, -by, -bi, -cf, bt, bn, ck, -bo, -bs, cg, bj, bx, -cb, -bf, -cc, bw, bk, ch, -br, -bp, cj, bm, bu, -ce, -bh, -bz, bz, bh, ce, -bu, -bm, -cj, bp, br, -ch, -bk, -bw, cc, bf, cb, -bx, -bj, -cg, bs, bo, -ck, -bn, -bt, cf, bi, by, -ca, -bg, -cd, bv, bl, ci, -bq },
+ { ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae },
+ { br, -cf, -bg, -cc, bu, bo, -ci, -bj, -bz, bx, bl, ck, -bm, -bw, ca, bi, ch, -bp, -bt, cd, bf, ce, -bs, -bq, cg, bh, cb, -bv, -bn, cj, bk, by, -by, -bk, -cj, bn, bv, -cb, -bh, -cg, bq, bs, -ce, -bf, -cd, bt, bp, -ch, -bi, -ca, bw, bm, -ck, -bl, -bx, bz, bj, ci, -bo, -bu, cc, bg, cf, -br },
+ { av, -bb, -ap, -bc, au, aw, -ba, -aq, -bd, at, ax, -az, -ar, -be, as, ay, -ay, -as, be, ar, az, -ax, -at, bd, aq, ba, -aw, -au, bc, ap, bb, -av, -av, bb, ap, bc, -au, -aw, ba, aq, bd, -at, -ax, az, ar, be, -as, -ay, ay, as, -be, -ar, -az, ax, at, -bd, -aq, -ba, aw, au, -bc, -ap, -bb, av },
+ { bs, -cc, -bi, -cj, bl, bz, -bv, -bp, cf, bf, cg, -bo, -bw, by, bm, -ci, -bh, -cd, br, bt, -cb, -bj, -ck, bk, ca, -bu, -bq, ce, bg, ch, -bn, -bx, bx, bn, -ch, -bg, -ce, bq, bu, -ca, -bk, ck, bj, cb, -bt, -br, cd, bh, ci, -bm, -by, bw, bo, -cg, -bf, -cf, bp, bv, -bz, -bl, cj, bi, cc, -bs },
+ { ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak, ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak },
+ { bt, -bz, -bn, cf, bh, ck, -bi, -ce, bo, by, -bu, -bs, ca, bm, -cg, -bg, -cj, bj, cd, -bp, -bx, bv, br, -cb, -bl, ch, bf, ci, -bk, -cc, bq, bw, -bw, -bq, cc, bk, -ci, -bf, -ch, bl, cb, -br, -bv, bx, bp, -cd, -bj, cj, bg, cg, -bm, -ca, bs, bu, -by, -bo, ce, bi, -ck, -bh, -cf, bn, bz, -bt },
+ { aw, -ay, -au, ba, as, -bc, -aq, be, ap, bd, -ar, -bb, at, az, -av, -ax, ax, av, -az, -at, bb, ar, -bd, -ap, -be, aq, bc, -as, -ba, au, ay, -aw, -aw, ay, au, -ba, -as, bc, aq, -be, -ap, -bd, ar, bb, -at, -az, av, ax, -ax, -av, az, at, -bb, -ar, bd, ap, be, -aq, -bc, as, ba, -au, -ay, aw },
+ { bu, -bw, -bs, by, bq, -ca, -bo, cc, bm, -ce, -bk, cg, bi, -ci, -bg, ck, bf, cj, -bh, -ch, bj, cf, -bl, -cd, bn, cb, -bp, -bz, br, bx, -bt, -bv, bv, bt, -bx, -br, bz, bp, -cb, -bn, cd, bl, -cf, -bj, ch, bh, -cj, -bf, -ck, bg, ci, -bi, -cg, bk, ce, -bm, -cc, bo, ca, -bq, -by, bs, bw, -bu },
+ { aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa },
+ { bv, -bt, -bx, br, bz, -bp, -cb, bn, cd, -bl, -cf, bj, ch, -bh, -cj, bf, -ck, -bg, ci, bi, -cg, -bk, ce, bm, -cc, -bo, ca, bq, -by, -bs, bw, bu, -bu, -bw, bs, by, -bq, -ca, bo, cc, -bm, -ce, bk, cg, -bi, -ci, bg, ck, -bf, cj, bh, -ch, -bj, cf, bl, -cd, -bn, cb, bp, -bz, -br, bx, bt, -bv },
+ { ax, -av, -az, at, bb, -ar, -bd, ap, -be, -aq, bc, as, -ba, -au, ay, aw, -aw, -ay, au, ba, -as, -bc, aq, be, -ap, bd, ar, -bb, -at, az, av, -ax, -ax, av, az, -at, -bb, ar, bd, -ap, be, aq, -bc, -as, ba, au, -ay, -aw, aw, ay, -au, -ba, as, bc, -aq, -be, ap, -bd, -ar, bb, at, -az, -av, ax },
+ { bw, -bq, -cc, bk, ci, -bf, ch, bl, -cb, -br, bv, bx, -bp, -cd, bj, cj, -bg, cg, bm, -ca, -bs, bu, by, -bo, -ce, bi, ck, -bh, cf, bn, -bz, -bt, bt, bz, -bn, -cf, bh, -ck, -bi, ce, bo, -by, -bu, bs, ca, -bm, -cg, bg, -cj, -bj, cd, bp, -bx, -bv, br, cb, -bl, -ch, bf, -ci, -bk, cc, bq, -bw },
+ { al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al, al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al },
+ { bx, -bn, -ch, bg, -ce, -bq, bu, ca, -bk, -ck, bj, -cb, -bt, br, cd, -bh, ci, bm, -by, -bw, bo, cg, -bf, cf, bp, -bv, -bz, bl, cj, -bi, cc, bs, -bs, -cc, bi, -cj, -bl, bz, bv, -bp, -cf, bf, -cg, -bo, bw, by, -bm, -ci, bh, -cd, -br, bt, cb, -bj, ck, bk, -ca, -bu, bq, ce, -bg, ch, bn, -bx },
+ { ay, -as, -be, ar, -az, -ax, at, bd, -aq, ba, aw, -au, -bc, ap, -bb, -av, av, bb, -ap, bc, au, -aw, -ba, aq, -bd, -at, ax, az, -ar, be, as, -ay, -ay, as, be, -ar, az, ax, -at, -bd, aq, -ba, -aw, au, bc, -ap, bb, av, -av, -bb, ap, -bc, -au, aw, ba, -aq, bd, at, -ax, -az, ar, -be, -as, ay },
+ { by, -bk, cj, bn, -bv, -cb, bh, -cg, -bq, bs, ce, -bf, cd, bt, -bp, -ch, bi, -ca, -bw, bm, ck, -bl, bx, bz, -bj, ci, bo, -bu, -cc, bg, -cf, -br, br, cf, -bg, cc, bu, -bo, -ci, bj, -bz, -bx, bl, -ck, -bm, bw, ca, -bi, ch, bp, -bt, -cd, bf, -ce, -bs, bq, cg, -bh, cb, bv, -bn, -cj, bk, -by },
+ { af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af },
+ { bz, -bh, ce, bu, -bm, cj, bp, -br, -ch, bk, -bw, -cc, bf, -cb, -bx, bj, -cg, -bs, bo, ck, -bn, bt, cf, -bi, by, ca, -bg, cd, bv, -bl, ci, bq, -bq, -ci, bl, -bv, -cd, bg, -ca, -by, bi, -cf, -bt, bn, -ck, -bo, bs, cg, -bj, bx, cb, -bf, cc, bw, -bk, ch, br, -bp, -cj, bm, -bu, -ce, bh, -bz },
+ { az, -ap, ba, ay, -aq, bb, ax, -ar, bc, aw, -as, bd, av, -at, be, au, -au, -be, at, -av, -bd, as, -aw, -bc, ar, -ax, -bb, aq, -ay, -ba, ap, -az, -az, ap, -ba, -ay, aq, -bb, -ax, ar, -bc, -aw, as, -bd, -av, at, -be, -au, au, be, -at, av, bd, -as, aw, bc, -ar, ax, bb, -aq, ay, ba, -ap, az },
+ { ca, -bf, bz, cb, -bg, by, cc, -bh, bx, cd, -bi, bw, ce, -bj, bv, cf, -bk, bu, cg, -bl, bt, ch, -bm, bs, ci, -bn, br, cj, -bo, bq, ck, -bp, bp, -ck, -bq, bo, -cj, -br, bn, -ci, -bs, bm, -ch, -bt, bl, -cg, -bu, bk, -cf, -bv, bj, -ce, -bw, bi, -cd, -bx, bh, -cc, -by, bg, -cb, -bz, bf, -ca },
+ { am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am, am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am },
+ { cb, -bi, bu, ci, -bp, bn, -cg, -bw, bg, -bz, -cd, bk, -bs, -ck, br, -bl, ce, by, -bf, bx, cf, -bm, bq, -cj, -bt, bj, -cc, -ca, bh, -bv, -ch, bo, -bo, ch, bv, -bh, ca, cc, -bj, bt, cj, -bq, bm, -cf, -bx, bf, -by, -ce, bl, -br, ck, bs, -bk, cd, bz, -bg, bw, cg, -bn, bp, -ci, -bu, bi, -cb },
+ { ba, -ar, av, -be, -aw, aq, -az, -bb, as, -au, bd, ax, -ap, ay, bc, -at, at, -bc, -ay, ap, -ax, -bd, au, -as, bb, az, -aq, aw, be, -av, ar, -ba, -ba, ar, -av, be, aw, -aq, az, bb, -as, au, -bd, -ax, ap, -ay, -bc, at, -at, bc, ay, -ap, ax, bd, -au, as, -bb, -az, aq, -aw, -be, av, -ar, ba },
+ { cc, -bl, bp, -cg, -by, bh, -bt, ck, bu, -bg, bx, ch, -bq, bk, -cb, -cd, bm, -bo, cf, bz, -bi, bs, -cj, -bv, bf, -bw, -ci, br, -bj, ca, ce, -bn, bn, -ce, -ca, bj, -br, ci, bw, -bf, bv, cj, -bs, bi, -bz, -cf, bo, -bm, cd, cb, -bk, bq, -ch, -bx, bg, -bu, -ck, bt, -bh, by, cg, -bp, bl, -cc },
+ { ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac },
+ { cd, -bo, bk, -bz, -ch, bs, -bg, bv, -ck, -bw, bh, -br, cg, ca, -bl, bn, -cc, -ce, bp, -bj, by, ci, -bt, bf, -bu, cj, bx, -bi, bq, -cf, -cb, bm, -bm, cb, cf, -bq, bi, -bx, -cj, bu, -bf, bt, -ci, -by, bj, -bp, ce, cc, -bn, bl, -ca, -cg, br, -bh, bw, ck, -bv, bg, -bs, ch, bz, -bk, bo, -cd },
+ { bb, -au, aq, -ax, be, ay, -ar, at, -ba, -bc, av, -ap, aw, -bd, -az, as, -as, az, bd, -aw, ap, -av, bc, ba, -at, ar, -ay, -be, ax, -aq, au, -bb, -bb, au, -aq, ax, -be, -ay, ar, -at, ba, bc, -av, ap, -aw, bd, az, -as, as, -az, -bd, aw, -ap, av, -bc, -ba, at, -ar, ay, be, -ax, aq, -au, bb },
+ { ce, -br, bf, -bs, cf, cd, -bq, bg, -bt, cg, cc, -bp, bh, -bu, ch, cb, -bo, bi, -bv, ci, ca, -bn, bj, -bw, cj, bz, -bm, bk, -bx, ck, by, -bl, bl, -by, -ck, bx, -bk, bm, -bz, -cj, bw, -bj, bn, -ca, -ci, bv, -bi, bo, -cb, -ch, bu, -bh, bp, -cc, -cg, bt, -bg, bq, -cd, -cf, bs, -bf, br, -ce },
+ { an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an, an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an },
+ { cf, -bu, bj, -bl, bw, -ch, -cd, bs, -bh, bn, -by, cj, cb, -bq, bf, -bp, ca, ck, -bz, bo, -bg, br, -cc, -ci, bx, -bm, bi, -bt, ce, cg, -bv, bk, -bk, bv, -cg, -ce, bt, -bi, bm, -bx, ci, cc, -br, bg, -bo, bz, -ck, -ca, bp, -bf, bq, -cb, -cj, by, -bn, bh, -bs, cd, ch, -bw, bl, -bj, bu, -cf },
+ { bc, -ax, as, -aq, av, -ba, -be, az, -au, ap, -at, ay, -bd, -bb, aw, -ar, ar, -aw, bb, bd, -ay, at, -ap, au, -az, be, ba, -av, aq, -as, ax, -bc, -bc, ax, -as, aq, -av, ba, be, -az, au, -ap, at, -ay, bd, bb, -aw, ar, -ar, aw, -bb, -bd, ay, -at, ap, -au, az, -be, -ba, av, -aq, as, -ax, bc },
+ { cg, -bx, bo, -bf, bn, -bw, cf, ch, -by, bp, -bg, bm, -bv, ce, ci, -bz, bq, -bh, bl, -bu, cd, cj, -ca, br, -bi, bk, -bt, cc, ck, -cb, bs, -bj, bj, -bs, cb, -ck, -cc, bt, -bk, bi, -br, ca, -cj, -cd, bu, -bl, bh, -bq, bz, -ci, -ce, bv, -bm, bg, -bp, by, -ch, -cf, bw, -bn, bf, -bo, bx, -cg },
+ { ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag },
+ { ch, -ca, bt, -bm, bf, -bl, bs, -bz, cg, ci, -cb, bu, -bn, bg, -bk, br, -by, cf, cj, -cc, bv, -bo, bh, -bj, bq, -bx, ce, ck, -cd, bw, -bp, bi, -bi, bp, -bw, cd, -ck, -ce, bx, -bq, bj, -bh, bo, -bv, cc, -cj, -cf, by, -br, bk, -bg, bn, -bu, cb, -ci, -cg, bz, -bs, bl, -bf, bm, -bt, ca, -ch },
+ { bd, -ba, ax, -au, ar, -ap, as, -av, ay, -bb, be, bc, -az, aw, -at, aq, -aq, at, -aw, az, -bc, -be, bb, -ay, av, -as, ap, -ar, au, -ax, ba, -bd, -bd, ba, -ax, au, -ar, ap, -as, av, -ay, bb, -be, -bc, az, -aw, at, -aq, aq, -at, aw, -az, bc, be, -bb, ay, -av, as, -ap, ar, -au, ax, -ba, bd },
+ { ci, -cd, by, -bt, bo, -bj, bf, -bk, bp, -bu, bz, -ce, cj, ch, -cc, bx, -bs, bn, -bi, bg, -bl, bq, -bv, ca, -cf, ck, cg, -cb, bw, -br, bm, -bh, bh, -bm, br, -bw, cb, -cg, -ck, cf, -ca, bv, -bq, bl, -bg, bi, -bn, bs, -bx, cc, -ch, -cj, ce, -bz, bu, -bp, bk, -bf, bj, -bo, bt, -by, cd, -ci },
+ { ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao, ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao },
+ { cj, -cg, cd, -ca, bx, -bu, br, -bo, bl, -bi, bf, -bh, bk, -bn, bq, -bt, bw, -bz, cc, -cf, ci, ck, -ch, ce, -cb, by, -bv, bs, -bp, bm, -bj, bg, -bg, bj, -bm, bp, -bs, bv, -by, cb, -ce, ch, -ck, -ci, cf, -cc, bz, -bw, bt, -bq, bn, -bk, bh, -bf, bi, -bl, bo, -br, bu, -bx, ca, -cd, cg, -cj },
+ { be, -bd, bc, -bb, ba, -az, ay, -ax, aw, -av, au, -at, as, -ar, aq, -ap, ap, -aq, ar, -as, at, -au, av, -aw, ax, -ay, az, -ba, bb, -bc, bd, -be, -be, bd, -bc, bb, -ba, az, -ay, ax, -aw, av, -au, at, -as, ar, -aq, ap, -ap, aq, -ar, as, -at, au, -av, aw, -ax, ay, -az, ba, -bb, bc, -bd, be },
+ { ck, -cj, ci, -ch, cg, -cf, ce, -cd, cc, -cb, ca, -bz, by, -bx, bw, -bv, bu, -bt, bs, -br, bq, -bp, bo, -bn, bm, -bl, bk, -bj, bi, -bh, bg, -bf, bf, -bg, bh, -bi, bj, -bk, bl, -bm, bn, -bo, bp, -bq, br, -bs, bt, -bu, bv, -bw, bx, -by, bz, -ca, cb, -cc, cd, -ce, cf, -cg, ch, -ci, cj, -ck },
+}
+ */
+
+void ff_vvc_inv_dct2_64(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int aa = 64, ab = 83, ac = 36, ad = 89, ae = 75, af = 50, ag = 18, ah = 90;
+ const int ai = 87, aj = 80, ak = 70, al = 57, am = 43, an = 25, ao = 9, ap = 90;
+ const int aq = 90, ar = 88, as = 85, at = 82, au = 78, av = 73, aw = 67, ax = 61;
+ const int ay = 54, az = 46, ba = 38, bb = 31, bc = 22, bd = 13, be = 4, bf = 91;
+ const int bg = 90, bh = 90, bi = 90, bj = 88, bk = 87, bl = 86, bm = 84, bn = 83;
+ const int bo = 81, bp = 79, bq = 77, br = 73, bs = 71, bt = 69, bu = 65, bv = 62;
+ const int bw = 59, bx = 56, by = 52, bz = 48, ca = 44, cb = 41, cc = 37, cd = 33;
+ const int ce = 28, cf = 24, cg = 20, ch = 15, ci = 11, cj = 7, ck = 2;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride];
+ const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride];
+ const int x8 = coeffs[8 * stride], x9 = coeffs[9 * stride];
+ const int x10 = coeffs[10 * stride], x11 = coeffs[11 * stride];
+ const int x12 = coeffs[12 * stride], x13 = coeffs[13 * stride];
+ const int x14 = coeffs[14 * stride], x15 = coeffs[15 * stride];
+ const int x16 = coeffs[16 * stride], x17 = coeffs[17 * stride];
+ const int x18 = coeffs[18 * stride], x19 = coeffs[19 * stride];
+ const int x20 = coeffs[20 * stride], x21 = coeffs[21 * stride];
+ const int x22 = coeffs[22 * stride], x23 = coeffs[23 * stride];
+ const int x24 = coeffs[24 * stride], x25 = coeffs[25 * stride];
+ const int x26 = coeffs[26 * stride], x27 = coeffs[27 * stride];
+ const int x28 = coeffs[28 * stride], x29 = coeffs[29 * stride];
+ const int x30 = coeffs[30 * stride], x31 = coeffs[31 * stride];
+ //according to vvc specification, x31 to x63 are zeros
+ const int EEEEE[2] = {
+ aa * x0,
+ aa * x0,
+ };
+ const int EEEEO[2] = {
+ G16(ab * x16),
+ G16(ac * x16),
+ };
+ const int EEEE[4] = {
+ EEEEE[0] + EEEEO[0], EEEEE[1] + EEEEO[1],
+ EEEEE[1] - EEEEO[1], EEEEE[0] - EEEEO[0],
+ };
+ const int EEEO[4] = {
+ G8(ad * x8) + G16(+ae * x24),
+ G8(ae * x8) + G16(-ag * x24),
+ G8(af * x8) + G16(-ad * x24),
+ G8(ag * x8) + G16(-af * x24),
+ };
+ const int EEE[8] = {
+ EEEE[0] + EEEO[0], EEEE[1] + EEEO[1], EEEE[2] + EEEO[2], EEEE[3] + EEEO[3],
+ EEEE[3] - EEEO[3], EEEE[2] - EEEO[2], EEEE[1] - EEEO[1], EEEE[0] - EEEO[0],
+ };
+ const int EEO[8] = {
+ G4(ah * x4) + G8(+ai * x12) + G16(+aj * x20 + ak * x28),
+ G4(ai * x4) + G8(+al * x12) + G16(+ao * x20 - am * x28),
+ G4(aj * x4) + G8(+ao * x12) + G16(-ak * x20 - ai * x28),
+ G4(ak * x4) + G8(-am * x12) + G16(-ai * x20 + ao * x28),
+ G4(al * x4) + G8(-aj * x12) + G16(-an * x20 + ah * x28),
+ G4(am * x4) + G8(-ah * x12) + G16(+al * x20 + an * x28),
+ G4(an * x4) + G8(-ak * x12) + G16(+ah * x20 - aj * x28),
+ G4(ao * x4) + G8(-an * x12) + G16(+am * x20 - al * x28),
+ };
+ const int EE[16] = {
+ EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3], EEE[4] + EEO[4], EEE[5] + EEO[5], EEE[6] + EEO[6], EEE[7] + EEO[7],
+ EEE[7] - EEO[7], EEE[6] - EEO[6], EEE[5] - EEO[5], EEE[4] - EEO[4], EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0],
+ };
+ const int EO[16] = {
+ G2(ap * x2) + G4(+aq * x6) + G8(+ar * x10 + as * x14) + G16(+at * x18 + au * x22 + av * x26 + aw * x30),
+ G2(aq * x2) + G4(+at * x6) + G8(+aw * x10 + az * x14) + G16(+bc * x18 - be * x22 - bb * x26 - ay * x30),
+ G2(ar * x2) + G4(+aw * x6) + G8(+bb * x10 - bd * x14) + G16(-ay * x18 - at * x22 - ap * x26 - au * x30),
+ G2(as * x2) + G4(+az * x6) + G8(-bd * x10 - aw * x14) + G16(-ap * x18 - av * x22 - bc * x26 + ba * x30),
+ G2(at * x2) + G4(+bc * x6) + G8(-ay * x10 - ap * x14) + G16(-ax * x18 + bd * x22 + au * x26 + as * x30),
+ G2(au * x2) + G4(-be * x6) + G8(-at * x10 - av * x14) + G16(+bd * x18 + as * x22 + aw * x26 - bc * x30),
+ G2(av * x2) + G4(-bb * x6) + G8(-ap * x10 - bc * x14) + G16(+au * x18 + aw * x22 - ba * x26 - aq * x30),
+ G2(aw * x2) + G4(-ay * x6) + G8(-au * x10 + ba * x14) + G16(+as * x18 - bc * x22 - aq * x26 + be * x30),
+ G2(ax * x2) + G4(-av * x6) + G8(-az * x10 + at * x14) + G16(+bb * x18 - ar * x22 - bd * x26 + ap * x30),
+ G2(ay * x2) + G4(-as * x6) + G8(-be * x10 + ar * x14) + G16(-az * x18 - ax * x22 + at * x26 + bd * x30),
+ G2(az * x2) + G4(-ap * x6) + G8(+ba * x10 + ay * x14) + G16(-aq * x18 + bb * x22 + ax * x26 - ar * x30),
+ G2(ba * x2) + G4(-ar * x6) + G8(+av * x10 - be * x14) + G16(-aw * x18 + aq * x22 - az * x26 - bb * x30),
+ G2(bb * x2) + G4(-au * x6) + G8(+aq * x10 - ax * x14) + G16(+be * x18 + ay * x22 - ar * x26 + at * x30),
+ G2(bc * x2) + G4(-ax * x6) + G8(+as * x10 - aq * x14) + G16(+av * x18 - ba * x22 - be * x26 + az * x30),
+ G2(bd * x2) + G4(-ba * x6) + G8(+ax * x10 - au * x14) + G16(+ar * x18 - ap * x22 + as * x26 - av * x30),
+ G2(be * x2) + G4(-bd * x6) + G8(+bc * x10 - bb * x14) + G16(+ba * x18 - az * x22 + ay * x26 - ax * x30),
+ };
+ const int E[32] = {
+ EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7], EE[8] + EO[8], EE[9] + EO[9], EE[10] + EO[10], EE[11] + EO[11], EE[12] + EO[12], EE[13] + EO[13], EE[14] + EO[14], EE[15] + EO[15],
+ EE[15] - EO[15], EE[14] - EO[14], EE[13] - EO[13], EE[12] - EO[12], EE[11] - EO[11], EE[10] - EO[10], EE[9] - EO[9], EE[8] - EO[8], EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0],
+ };
+ const int O[32] = {
+ bf * x1 + G2(+bg * x3) + G4(+bh * x5 + bi * x7) + G8(+bj * x9 + bk * x11 + bl * x13 + bm * x15) + G16(+bn * x17 + bo * x19 + bp * x21 + bq * x23 + br * x25 + bs * x27 + bt * x29 + bu * x31),
+ bg * x1 + G2(+bj * x3) + G4(+bm * x5 + bp * x7) + G8(+bs * x9 + bv * x11 + by * x13 + cb * x15) + G16(+ce * x17 + ch * x19 + ck * x21 - ci * x23 + -cf * x25 - cc * x27 - bz * x29 - bw * x31),
+ bh * x1 + G2(+bm * x3) + G4(+br * x5 + bw * x7) + G8(+cb * x9 + cg * x11 - ck * x13 - cf * x15) + G16(-ca * x17 - bv * x19 - bq * x21 - bl * x23 + -bg * x25 - bi * x27 - bn * x29 - bs * x31),
+ bi * x1 + G2(+bp * x3) + G4(+bw * x5 + cd * x7) + G8(+ck * x9 - ce * x11 - bx * x13 - bq * x15) + G16(-bj * x17 - bh * x19 - bo * x21 - bv * x23 + -cc * x25 - cj * x27 + cf * x29 + by * x31),
+ bj * x1 + G2(+bs * x3) + G4(+cb * x5 + ck * x7) + G8(-cc * x9 - bt * x11 - bk * x13 - bi * x15) + G16(-br * x17 - ca * x19 - cj * x21 + cd * x23 + bu * x25 + bl * x27 + bh * x29 + bq * x31),
+ bk * x1 + G2(+bv * x3) + G4(+cg * x5 - ce * x7) + G8(-bt * x9 - bi * x11 - bm * x13 - bx * x15) + G16(-ci * x17 + cc * x19 + br * x21 + bg * x23 + bo * x25 + bz * x27 + ck * x29 - ca * x31),
+ bl * x1 + G2(+by * x3) + G4(-ck * x5 - bx * x7) + G8(-bk * x9 - bm * x11 - bz * x13 + cj * x15) + G16(+bw * x17 + bj * x19 + bn * x21 + ca * x23 + -ci * x25 - bv * x27 - bi * x29 - bo * x31),
+ bm * x1 + G2(+cb * x3) + G4(-cf * x5 - bq * x7) + G8(-bi * x9 - bx * x11 + cj * x13 + bu * x15) + G16(+bf * x17 + bt * x19 + ci * x21 - by * x23 + -bj * x25 - bp * x27 - ce * x29 + cc * x31),
+ bn * x1 + G2(+ce * x3) + G4(-ca * x5 - bj * x7) + G8(-br * x9 - ci * x11 + bw * x13 + bf * x15) + G16(+bv * x17 - cj * x19 - bs * x21 - bi * x23 + -bz * x25 + cf * x27 + bo * x29 + bm * x31),
+ bo * x1 + G2(+ch * x3) + G4(-bv * x5 - bh * x7) + G8(-ca * x9 + cc * x11 + bj * x13 + bt * x15) + G16(-cj * x17 - bq * x19 - bm * x21 - cf * x23 + bx * x25 + bf * x27 + by * x29 - ce * x31),
+ bp * x1 + G2(+ck * x3) + G4(-bq * x5 - bo * x7) + G8(-cj * x9 + br * x11 + bn * x13 + ci * x15) + G16(-bs * x17 - bm * x19 - ch * x21 + bt * x23 + bl * x25 + cg * x27 - bu * x29 - bk * x31),
+ bq * x1 + G2(-ci * x3) + G4(-bl * x5 - bv * x7) + G8(+cd * x9 + bg * x11 + ca * x13 - by * x15) + G16(-bi * x17 - cf * x19 + bt * x21 + bn * x23 + ck * x25 - bo * x27 - bs * x29 + cg * x31),
+ br * x1 + G2(-cf * x3) + G4(-bg * x5 - cc * x7) + G8(+bu * x9 + bo * x11 - ci * x13 - bj * x15) + G16(-bz * x17 + bx * x19 + bl * x21 + ck * x23 + -bm * x25 - bw * x27 + ca * x29 + bi * x31),
+ bs * x1 + G2(-cc * x3) + G4(-bi * x5 - cj * x7) + G8(+bl * x9 + bz * x11 - bv * x13 - bp * x15) + G16(+cf * x17 + bf * x19 + cg * x21 - bo * x23 + -bw * x25 + by * x27 + bm * x29 - ci * x31),
+ bt * x1 + G2(-bz * x3) + G4(-bn * x5 + cf * x7) + G8(+bh * x9 + ck * x11 - bi * x13 - ce * x15) + G16(+bo * x17 + by * x19 - bu * x21 - bs * x23 + ca * x25 + bm * x27 - cg * x29 - bg * x31),
+ bu * x1 + G2(-bw * x3) + G4(-bs * x5 + by * x7) + G8(+bq * x9 - ca * x11 - bo * x13 + cc * x15) + G16(+bm * x17 - ce * x19 - bk * x21 + cg * x23 + bi * x25 - ci * x27 - bg * x29 + ck * x31),
+ bv * x1 + G2(-bt * x3) + G4(-bx * x5 + br * x7) + G8(+bz * x9 - bp * x11 - cb * x13 + bn * x15) + G16(+cd * x17 - bl * x19 - cf * x21 + bj * x23 + ch * x25 - bh * x27 - cj * x29 + bf * x31),
+ bw * x1 + G2(-bq * x3) + G4(-cc * x5 + bk * x7) + G8(+ci * x9 - bf * x11 + ch * x13 + bl * x15) + G16(-cb * x17 - br * x19 + bv * x21 + bx * x23 + -bp * x25 - cd * x27 + bj * x29 + cj * x31),
+ bx * x1 + G2(-bn * x3) + G4(-ch * x5 + bg * x7) + G8(-ce * x9 - bq * x11 + bu * x13 + ca * x15) + G16(-bk * x17 - ck * x19 + bj * x21 - cb * x23 + -bt * x25 + br * x27 + cd * x29 - bh * x31),
+ by * x1 + G2(-bk * x3) + G4(+cj * x5 + bn * x7) + G8(-bv * x9 - cb * x11 + bh * x13 - cg * x15) + G16(-bq * x17 + bs * x19 + ce * x21 - bf * x23 + cd * x25 + bt * x27 - bp * x29 - ch * x31),
+ bz * x1 + G2(-bh * x3) + G4(+ce * x5 + bu * x7) + G8(-bm * x9 + cj * x11 + bp * x13 - br * x15) + G16(-ch * x17 + bk * x19 - bw * x21 - cc * x23 + bf * x25 - cb * x27 - bx * x29 + bj * x31),
+ ca * x1 + G2(-bf * x3) + G4(+bz * x5 + cb * x7) + G8(-bg * x9 + by * x11 + cc * x13 - bh * x15) + G16(+bx * x17 + cd * x19 - bi * x21 + bw * x23 + ce * x25 - bj * x27 + bv * x29 + cf * x31),
+ cb * x1 + G2(-bi * x3) + G4(+bu * x5 + ci * x7) + G8(-bp * x9 + bn * x11 - cg * x13 - bw * x15) + G16(+bg * x17 - bz * x19 - cd * x21 + bk * x23 + -bs * x25 - ck * x27 + br * x29 - bl * x31),
+ cc * x1 + G2(-bl * x3) + G4(+bp * x5 - cg * x7) + G8(-by * x9 + bh * x11 - bt * x13 + ck * x15) + G16(+bu * x17 - bg * x19 + bx * x21 + ch * x23 + -bq * x25 + bk * x27 - cb * x29 - cd * x31),
+ cd * x1 + G2(-bo * x3) + G4(+bk * x5 - bz * x7) + G8(-ch * x9 + bs * x11 - bg * x13 + bv * x15) + G16(-ck * x17 - bw * x19 + bh * x21 - br * x23 + cg * x25 + ca * x27 - bl * x29 + bn * x31),
+ ce * x1 + G2(-br * x3) + G4(+bf * x5 - bs * x7) + G8(+cf * x9 + cd * x11 - bq * x13 + bg * x15) + G16(-bt * x17 + cg * x19 + cc * x21 - bp * x23 + bh * x25 - bu * x27 + ch * x29 + cb * x31),
+ cf * x1 + G2(-bu * x3) + G4(+bj * x5 - bl * x7) + G8(+bw * x9 - ch * x11 - cd * x13 + bs * x15) + G16(-bh * x17 + bn * x19 - by * x21 + cj * x23 + cb * x25 - bq * x27 + bf * x29 - bp * x31),
+ cg * x1 + G2(-bx * x3) + G4(+bo * x5 - bf * x7) + G8(+bn * x9 - bw * x11 + cf * x13 + ch * x15) + G16(-by * x17 + bp * x19 - bg * x21 + bm * x23 + -bv * x25 + ce * x27 + ci * x29 - bz * x31),
+ ch * x1 + G2(-ca * x3) + G4(+bt * x5 - bm * x7) + G8(+bf * x9 - bl * x11 + bs * x13 - bz * x15) + G16(+cg * x17 + ci * x19 - cb * x21 + bu * x23 + -bn * x25 + bg * x27 - bk * x29 + br * x31),
+ ci * x1 + G2(-cd * x3) + G4(+by * x5 - bt * x7) + G8(+bo * x9 - bj * x11 + bf * x13 - bk * x15) + G16(+bp * x17 - bu * x19 + bz * x21 - ce * x23 + cj * x25 + ch * x27 - cc * x29 + bx * x31),
+ cj * x1 + G2(-cg * x3) + G4(+cd * x5 - ca * x7) + G8(+bx * x9 - bu * x11 + br * x13 - bo * x15) + G16(+bl * x17 - bi * x19 + bf * x21 - bh * x23 + bk * x25 - bn * x27 + bq * x29 - bt * x31),
+ ck * x1 + G2(-cj * x3) + G4(+ci * x5 - ch * x7) + G8(+cg * x9 - cf * x11 + ce * x13 - cd * x15) + G16(+cc * x17 - cb * x19 + ca * x21 - bz * x23 + by * x25 - bx * x27 + bw * x29 - bv * x31),
+ };
+ coeffs[0 * stride] = E[0 ] + O[0 ];
+ coeffs[1 * stride] = E[1 ] + O[1 ];
+ coeffs[2 * stride] = E[2 ] + O[2 ];
+ coeffs[3 * stride] = E[3 ] + O[3 ];
+ coeffs[4 * stride] = E[4 ] + O[4 ];
+ coeffs[5 * stride] = E[5 ] + O[5 ];
+ coeffs[6 * stride] = E[6 ] + O[6 ];
+ coeffs[7 * stride] = E[7 ] + O[7 ];
+ coeffs[8 * stride] = E[8 ] + O[8 ];
+ coeffs[9 * stride] = E[9 ] + O[9 ];
+ coeffs[10 * stride] = E[10] + O[10];
+ coeffs[11 * stride] = E[11] + O[11];
+ coeffs[12 * stride] = E[12] + O[12];
+ coeffs[13 * stride] = E[13] + O[13];
+ coeffs[14 * stride] = E[14] + O[14];
+ coeffs[15 * stride] = E[15] + O[15];
+ coeffs[16 * stride] = E[16] + O[16];
+ coeffs[17 * stride] = E[17] + O[17];
+ coeffs[18 * stride] = E[18] + O[18];
+ coeffs[19 * stride] = E[19] + O[19];
+ coeffs[20 * stride] = E[20] + O[20];
+ coeffs[21 * stride] = E[21] + O[21];
+ coeffs[22 * stride] = E[22] + O[22];
+ coeffs[23 * stride] = E[23] + O[23];
+ coeffs[24 * stride] = E[24] + O[24];
+ coeffs[25 * stride] = E[25] + O[25];
+ coeffs[26 * stride] = E[26] + O[26];
+ coeffs[27 * stride] = E[27] + O[27];
+ coeffs[28 * stride] = E[28] + O[28];
+ coeffs[29 * stride] = E[29] + O[29];
+ coeffs[30 * stride] = E[30] + O[30];
+ coeffs[31 * stride] = E[31] + O[31];
+ coeffs[32 * stride] = E[31] - O[31];
+ coeffs[33 * stride] = E[30] - O[30];
+ coeffs[34 * stride] = E[29] - O[29];
+ coeffs[35 * stride] = E[28] - O[28];
+ coeffs[36 * stride] = E[27] - O[27];
+ coeffs[37 * stride] = E[26] - O[26];
+ coeffs[38 * stride] = E[25] - O[25];
+ coeffs[39 * stride] = E[24] - O[24];
+ coeffs[40 * stride] = E[23] - O[23];
+ coeffs[41 * stride] = E[22] - O[22];
+ coeffs[42 * stride] = E[21] - O[21];
+ coeffs[43 * stride] = E[20] - O[20];
+ coeffs[44 * stride] = E[19] - O[19];
+ coeffs[45 * stride] = E[18] - O[18];
+ coeffs[46 * stride] = E[17] - O[17];
+ coeffs[47 * stride] = E[16] - O[16];
+ coeffs[48 * stride] = E[15] - O[15];
+ coeffs[49 * stride] = E[14] - O[14];
+ coeffs[50 * stride] = E[13] - O[13];
+ coeffs[51 * stride] = E[12] - O[12];
+ coeffs[52 * stride] = E[11] - O[11];
+ coeffs[53 * stride] = E[10] - O[10];
+ coeffs[54 * stride] = E[9] - O[9];
+ coeffs[55 * stride] = E[8] - O[8];
+ coeffs[56 * stride] = E[7] - O[7];
+ coeffs[57 * stride] = E[6] - O[6];
+ coeffs[58 * stride] = E[5] - O[5];
+ coeffs[59 * stride] = E[4] - O[4];
+ coeffs[60 * stride] = E[3] - O[3];
+ coeffs[61 * stride] = E[2] - O[2];
+ coeffs[62 * stride] = E[1] - O[1];
+ coeffs[63 * stride] = E[0] - O[0];
+};
+
+static void matrix_mul(int *coeffs, const ptrdiff_t stride, const int8_t* matrix, const int size, const size_t nz)
+{
+ //for dst7 and dct8, coeffs > 16 are zero out
+ int tmp[16];
+
+ for (int i = 0; i < nz; i++)
+ tmp[i] = coeffs[i * stride];
+
+ for (int i = 0; i < size; i++) {
+ int o = 0;
+
+ for (int j = 0; j < nz; j++)
+ o += tmp[j] * matrix[j * size];
+ *coeffs = o;
+ coeffs += stride;
+ matrix++;
+ }
+}
+
+static void inv_dct8(int *coeffs, const ptrdiff_t stride, const int8_t *matrix, const int size, const size_t nz)
+{
+ matrix_mul(coeffs, stride, matrix, size, nz);
+}
+
+#define DEFINE_INV_DCT8_1D(S) \
+void ff_vvc_inv_dct8_ ## S(int *coeffs, const ptrdiff_t stride, const size_t nz) \
+{ \
+ inv_dct8(coeffs, stride, &ff_vvc_dct8_##S##x##S[0][0], S, nz); \
+}
+
+DEFINE_INV_DCT8_1D( 4)
+DEFINE_INV_DCT8_1D( 8)
+DEFINE_INV_DCT8_1D(16)
+DEFINE_INV_DCT8_1D(32)
+
+static void inv_dst7(int *coeffs, const ptrdiff_t stride, const int8_t *matrix, const int size, const size_t nz)
+{
+ matrix_mul(coeffs, stride, matrix, size, nz);
+}
+
+#define DEFINE_INV_DST7_1D(S) \
+void ff_vvc_inv_dst7_ ## S(int *coeffs, const ptrdiff_t stride, const size_t nz) \
+{ \
+ inv_dst7(coeffs, stride, &ff_vvc_dst7_##S##x##S[0][0], S, nz); \
+}
+
+DEFINE_INV_DST7_1D( 4)
+DEFINE_INV_DST7_1D( 8)
+DEFINE_INV_DST7_1D(16)
+DEFINE_INV_DST7_1D(32)
+
+void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s,
+ int pred_mode_intra, int lfnst_idx, int log2_transform_range)
+{
+ int lfnst_tr_set_idx = pred_mode_intra < 0 ? 1 : ff_vvc_lfnst_tr_set_index[pred_mode_intra];
+ const int8_t *tr_mat = n_tr_s > 16 ? ff_vvc_lfnst_8x8[lfnst_tr_set_idx][lfnst_idx-1][0] : ff_vvc_lfnst_4x4[lfnst_tr_set_idx][lfnst_idx - 1][0];
+
+ for (int j = 0; j < n_tr_s; j++, tr_mat++) {
+ int t = 0;
+
+ for (int i = 0; i < no_zero_size; i++)
+ t += u[i] * tr_mat[i * n_tr_s];
+ v[j] = av_clip_intp2((t + 64) >> 7 , log2_transform_range);
+ }
+}
diff --git a/libavcodec/vvc/vvc_itx_1d.h b/libavcodec/vvc/vvc_itx_1d.h
new file mode 100644
index 0000000000..d9094f524b
--- /dev/null
+++ b/libavcodec/vvc/vvc_itx_1d.h
@@ -0,0 +1,52 @@
+/*
+ * VVC 1D transform
+ *
+ * Copyright (C) 2023 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVC_ITX_1D_H
+#define AVCODEC_VVC_VVC_ITX_1D_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define vvc_itx_1d_fn(name) \
+ void (name)(int *coeffs, ptrdiff_t stride, size_t nz)
+typedef vvc_itx_1d_fn(*vvc_itx_1d_fn);
+
+vvc_itx_1d_fn(ff_vvc_inv_dct2_2);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_4);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_8);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_16);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_32);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_64);
+vvc_itx_1d_fn(ff_vvc_inv_dst7_4);
+vvc_itx_1d_fn(ff_vvc_inv_dst7_8);
+vvc_itx_1d_fn(ff_vvc_inv_dst7_16);
+vvc_itx_1d_fn(ff_vvc_inv_dst7_32);
+vvc_itx_1d_fn(ff_vvc_inv_dct8_4);
+vvc_itx_1d_fn(ff_vvc_inv_dct8_8);
+vvc_itx_1d_fn(ff_vvc_inv_dct8_16);
+vvc_itx_1d_fn(ff_vvc_inv_dct8_32);
+
+
+void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s,
+ int pred_mode_intra, int lfnst_idx, int log2_transform_range);
+
+#endif // AVCODEC_VVC_VVC_ITX_1D_H
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH v7 09/14] vvcdec: add intra prediction
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
` (6 preceding siblings ...)
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 08/14] vvcdec: add inv transform 1d Nuo Mi
@ 2023-12-10 15:58 ` Nuo Mi
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 10/14] vvcdec: add LMCS, Deblocking, SAO, and ALF filters Nuo Mi
8 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 15:58 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
---
libavcodec/vvc/Makefile | 1 +
libavcodec/vvc/vvc_ctu.c | 50 ++
libavcodec/vvc/vvc_ctu.h | 2 +
libavcodec/vvc/vvc_intra.c | 797 +++++++++++++++++++++
libavcodec/vvc/vvc_intra.h | 49 ++
libavcodec/vvc/vvc_intra_template.c | 1015 +++++++++++++++++++++++++++
6 files changed, 1914 insertions(+)
create mode 100644 libavcodec/vvc/vvc_intra.c
create mode 100644 libavcodec/vvc/vvc_intra.h
create mode 100644 libavcodec/vvc/vvc_intra_template.c
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index 5796f9ad42..3b1ac72029 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -6,6 +6,7 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
vvc/vvc_ctu.o \
vvc/vvc_data.o \
vvc/vvc_inter.o \
+ vvc/vvc_intra.o \
vvc/vvc_itx_1d.o \
vvc/vvc_mvs.o \
vvc/vvc_ps.o \
diff --git a/libavcodec/vvc/vvc_ctu.c b/libavcodec/vvc/vvc_ctu.c
index 3d31b862ae..6138d2fc9f 100644
--- a/libavcodec/vvc/vvc_ctu.c
+++ b/libavcodec/vvc/vvc_ctu.c
@@ -20,10 +20,41 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include "libavcodec/refstruct.h"
+
#include "vvc_cabac.h"
#include "vvc_ctu.h"
#include "vvc_mvs.h"
+void ff_vvc_decode_neighbour(VVCLocalContext *lc, const int x_ctb, const int y_ctb,
+ const int rx, const int ry, const int rs)
+{
+ VVCFrameContext *fc = lc->fc;
+ const int ctb_size = fc->ps.sps->ctb_size_y;
+
+ lc->end_of_tiles_x = fc->ps.sps->width;
+ lc->end_of_tiles_y = fc->ps.sps->height;
+ if (fc->ps.pps->ctb_to_col_bd[rx] != fc->ps.pps->ctb_to_col_bd[rx + 1])
+ lc->end_of_tiles_x = FFMIN(x_ctb + ctb_size, lc->end_of_tiles_x);
+ if (fc->ps.pps->ctb_to_row_bd[ry] != fc->ps.pps->ctb_to_row_bd[ry + 1])
+ lc->end_of_tiles_y = FFMIN(y_ctb + ctb_size, lc->end_of_tiles_y);
+
+ lc->boundary_flags = 0;
+ if (rx > 0 && fc->ps.pps->ctb_to_col_bd[rx] != fc->ps.pps->ctb_to_col_bd[rx - 1])
+ lc->boundary_flags |= BOUNDARY_LEFT_TILE;
+ if (rx > 0 && fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - 1])
+ lc->boundary_flags |= BOUNDARY_LEFT_SLICE;
+ if (ry > 0 && fc->ps.pps->ctb_to_row_bd[ry] != fc->ps.pps->ctb_to_row_bd[ry - 1])
+ lc->boundary_flags |= BOUNDARY_UPPER_TILE;
+ if (ry > 0 && fc->tab.slice_idx[rs] != fc->tab.slice_idx[rs - fc->ps.pps->ctb_width])
+ lc->boundary_flags |= BOUNDARY_UPPER_SLICE;
+ lc->ctb_left_flag = rx > 0 && !(lc->boundary_flags & BOUNDARY_LEFT_TILE);
+ lc->ctb_up_flag = ry > 0 && !(lc->boundary_flags & BOUNDARY_UPPER_TILE) && !(lc->boundary_flags & BOUNDARY_UPPER_SLICE);
+ lc->ctb_up_right_flag = lc->ctb_up_flag && (fc->ps.pps->ctb_to_col_bd[rx] == fc->ps.pps->ctb_to_col_bd[rx + 1]) &&
+ (fc->ps.pps->ctb_to_row_bd[ry] == fc->ps.pps->ctb_to_row_bd[ry - 1]);
+ lc->ctb_up_left_flag = lc->ctb_left_flag && lc->ctb_up_flag;
+}
+
void ff_vvc_set_neighbour_available(VVCLocalContext *lc,
const int x0, const int y0, const int w, const int h)
{
@@ -39,6 +70,25 @@ void ff_vvc_set_neighbour_available(VVCLocalContext *lc,
lc->na.cand_up_right = lc->na.cand_up_right_sap && (x0 + w) < lc->end_of_tiles_x;
}
+void ff_vvc_ctu_free_cus(CTU *ctu)
+{
+ CodingUnit **cus = &ctu->cus;
+ while (*cus) {
+ CodingUnit *cu = *cus;
+ TransformUnit **head = &cu->tus.head;
+
+ *cus = cu->next;
+
+ while (*head) {
+ TransformUnit *tu = *head;
+ *head = tu->next;
+ ff_refstruct_unref(&tu);
+ }
+ cu->tus.tail = NULL;
+
+ ff_refstruct_unref(&cu);
+ }
+}
void ff_vvc_ep_init_stat_coeff(EntryPoint *ep,
const int bit_depth, const int persistent_rice_adaptation_enabled_flag)
diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h
index 82801b558e..577136b2e2 100644
--- a/libavcodec/vvc/vvc_ctu.h
+++ b/libavcodec/vvc/vvc_ctu.h
@@ -460,6 +460,8 @@ typedef struct ALFParams {
//utils
void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h);
+void ff_vvc_decode_neighbour(VVCLocalContext *lc, int x_ctb, int y_ctb, int rx, int ry, int rs);
+void ff_vvc_ctu_free_cus(CTU *ctu);
void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, int bit_depth, int persistent_rice_adaptation_enabled_flag);
#endif // AVCODEC_VVC_VVC_CTU_H
diff --git a/libavcodec/vvc/vvc_intra.c b/libavcodec/vvc/vvc_intra.c
new file mode 100644
index 0000000000..43de312a71
--- /dev/null
+++ b/libavcodec/vvc/vvc_intra.c
@@ -0,0 +1,797 @@
+/*
+ * VVC intra prediction
+ *
+ * Copyright (C) 2021 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "libavutil/frame.h"
+
+#include "vvc_data.h"
+#include "vvc_inter.h"
+#include "vvc_intra.h"
+#include "vvc_itx_1d.h"
+
+static int is_cclm(enum IntraPredMode mode)
+{
+ return mode == INTRA_LT_CCLM || mode == INTRA_L_CCLM || mode == INTRA_T_CCLM;
+}
+
+static int derive_ilfnst_pred_mode_intra(const VVCLocalContext *lc, const TransformBlock *tb)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const CodingUnit *cu = lc->cu;
+ const int x_tb = tb->x0 >> fc->ps.sps->min_cb_log2_size_y;
+ const int y_tb = tb->y0 >> fc->ps.sps->min_cb_log2_size_y;
+ const int x_c = (tb->x0 + (tb->tb_width << sps->hshift[1] >> 1) ) >> fc->ps.sps->min_cb_log2_size_y;
+ const int y_c = (tb->y0 + (tb->tb_height << sps->vshift[1] >> 1)) >> fc->ps.sps->min_cb_log2_size_y;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+ const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_tb, y_tb);
+ int pred_mode_intra = tb->c_idx == 0 ? cu->intra_pred_mode_y : cu->intra_pred_mode_c;
+ if (intra_mip_flag && !tb->c_idx) {
+ pred_mode_intra = INTRA_PLANAR;
+ } else if (is_cclm(pred_mode_intra)) {
+ int intra_mip_flag_c = SAMPLE_CTB(fc->tab.imf, x_c, y_c);
+ int cu_pred_mode = SAMPLE_CTB(fc->tab.cpm[0], x_c, y_c);
+ if (intra_mip_flag_c) {
+ pred_mode_intra = INTRA_PLANAR;
+ } else if (cu_pred_mode == MODE_IBC || cu_pred_mode == MODE_PLT) {
+ pred_mode_intra = INTRA_DC;
+ } else {
+ pred_mode_intra = SAMPLE_CTB(fc->tab.ipm, x_c, y_c);
+ }
+ }
+ pred_mode_intra = ff_vvc_wide_angle_mode_mapping(cu, tb->tb_width, tb->tb_height, tb->c_idx, pred_mode_intra);
+
+ return pred_mode_intra;
+}
+
+//8.7.4 Transformation process for scaled transform coefficients
+static void ilfnst_transform(const VVCLocalContext *lc, TransformBlock *tb)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const CodingUnit *cu = lc->cu;
+ const int w = tb->tb_width;
+ const int h = tb->tb_height;
+ const int n_lfnst_out_size = (w >= 8 && h >= 8) ? 48 : 16; ///< nLfnstOutSize
+ const int log2_lfnst_size = (w >= 8 && h >= 8) ? 3 : 2; ///< log2LfnstSize
+ const int n_lfnst_size = 1 << log2_lfnst_size; ///< nLfnstSize
+ const int non_zero_size = ((w == 8 && h == 8) || (w == 4 && h == 4)) ? 8 : 16; ///< nonZeroSize
+ const int pred_mode_intra = derive_ilfnst_pred_mode_intra(lc, tb);
+ const int transpose = pred_mode_intra > 34;
+ int u[16], v[48];
+
+ for (int x = 0; x < non_zero_size; x++) {
+ int xc = ff_vvc_diag_scan_x[2][2][x];
+ int yc = ff_vvc_diag_scan_y[2][2][x];
+ u[x] = tb->coeffs[w * yc + xc];
+ }
+ ff_vvc_inv_lfnst_1d(v, u, non_zero_size, n_lfnst_out_size, pred_mode_intra,
+ cu->lfnst_idx, sps->log2_transform_range);
+ if (transpose) {
+ int *dst = tb->coeffs;
+ const int *src = v;
+ if (n_lfnst_size == 4) {
+ for (int y = 0; y < 4; y++) {
+ dst[0] = src[0];
+ dst[1] = src[4];
+ dst[2] = src[8];
+ dst[3] = src[12];
+ src++;
+ dst += w;
+ }
+ } else {
+ for (int y = 0; y < 8; y++) {
+ dst[0] = src[0];
+ dst[1] = src[8];
+ dst[2] = src[16];
+ dst[3] = src[24];
+ if (y < 4) {
+ dst[4] = src[32];
+ dst[5] = src[36];
+ dst[6] = src[40];
+ dst[7] = src[44];
+ }
+ src++;
+ dst += w;
+ }
+ }
+
+ } else {
+ int *dst = tb->coeffs;
+ const int *src = v;
+ for (int y = 0; y < n_lfnst_size; y++) {
+ int size = (y < 4) ? n_lfnst_size : 4;
+ memcpy(dst, src, size * sizeof(int));
+ src += size;
+ dst += w;
+ }
+ }
+ tb->max_scan_x = n_lfnst_size - 1;
+ tb->max_scan_y = n_lfnst_size - 1;
+}
+
+//part of 8.7.4 Transformation process for scaled transform coefficients
+static void derive_transform_type(const VVCFrameContext *fc, const VVCLocalContext *lc, const TransformBlock *tb, enum TxType *trh, enum TxType *trv)
+{
+ const CodingUnit *cu = lc->cu;
+ static const enum TxType mts_to_trh[] = {DCT2, DST7, DCT8, DST7, DCT8};
+ static const enum TxType mts_to_trv[] = {DCT2, DST7, DST7, DCT8, DCT8};
+ const VVCSPS *sps = fc->ps.sps;
+ int implicit_mts_enabled = 0;
+ if (tb->c_idx || (cu->isp_split_type != ISP_NO_SPLIT && cu->lfnst_idx)) {
+ *trh = *trv = DCT2;
+ return;
+ }
+
+ if (sps->r->sps_mts_enabled_flag) {
+ if (cu->isp_split_type != ISP_NO_SPLIT ||
+ (cu->sbt_flag && FFMAX(tb->tb_width, tb->tb_height) <= 32) ||
+ (!sps->r->sps_explicit_mts_intra_enabled_flag && cu->pred_mode == MODE_INTRA &&
+ !cu->lfnst_idx && !cu->intra_mip_flag)) {
+ implicit_mts_enabled = 1;
+ }
+ }
+ if (implicit_mts_enabled) {
+ const int w = tb->tb_width;
+ const int h = tb->tb_height;
+ if (cu->sbt_flag) {
+ *trh = (cu->sbt_horizontal_flag || cu->sbt_pos_flag) ? DST7 : DCT8;
+ *trv = (!cu->sbt_horizontal_flag || cu->sbt_pos_flag) ? DST7 : DCT8;
+ } else {
+ *trh = (w >= 4 && w <= 16) ? DST7 : DCT2;
+ *trv = (h >= 4 && h <= 16) ? DST7 : DCT2;
+ }
+ return;
+ }
+ *trh = mts_to_trh[cu->mts_idx];
+ *trv = mts_to_trv[cu->mts_idx];
+}
+
+static void add_residual_for_joint_coding_chroma(VVCLocalContext *lc,
+ const TransformUnit *tu, TransformBlock *tb, const int chroma_scale)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const int c_sign = 1 - 2 * fc->ps.ph.r->ph_joint_cbcr_sign_flag;
+ const int shift = tu->coded_flag[1] ^ tu->coded_flag[2];
+ const int c_idx = 1 + tu->coded_flag[1];
+ const ptrdiff_t stride = fc->frame->linesize[c_idx];
+ const int hs = fc->ps.sps->hshift[c_idx];
+ const int vs = fc->ps.sps->vshift[c_idx];
+ uint8_t *dst = &fc->frame->data[c_idx][(tb->y0 >> vs) * stride +
+ ((tb->x0 >> hs) << fc->ps.sps->pixel_shift)];
+ if (chroma_scale) {
+ fc->vvcdsp.itx.pred_residual_joint(tb->coeffs, tb->tb_width, tb->tb_height, c_sign, shift);
+ fc->vvcdsp.intra.lmcs_scale_chroma(lc, tb->coeffs, tb->coeffs, tb->tb_width, tb->tb_height, cu->x0, cu->y0);
+ fc->vvcdsp.itx.add_residual(dst, tb->coeffs, tb->tb_width, tb->tb_height, stride);
+ } else {
+ fc->vvcdsp.itx.add_residual_joint(dst, tb->coeffs, tb->tb_width, tb->tb_height, stride, c_sign, shift);
+ }
+}
+
+static int add_reconstructed_area(VVCLocalContext *lc, const int ch_type, const int x0, const int y0, const int w, const int h)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const int hs = sps->hshift[ch_type];
+ const int vs = sps->vshift[ch_type];
+ ReconstructedArea *a;
+
+ if (lc->num_ras[ch_type] >= FF_ARRAY_ELEMS(lc->ras[ch_type]))
+ return AVERROR_INVALIDDATA;
+
+ a = &lc->ras[ch_type][lc->num_ras[ch_type]];
+ a->x = x0 >> hs;
+ a->y = y0 >> vs;
+ a->w = w >> hs;
+ a->h = h >> vs;
+ lc->num_ras[ch_type]++;
+
+ return 0;
+}
+
+static void add_tu_area(const TransformUnit *tu, int *x0, int *y0, int *w, int *h)
+{
+ *x0 = tu->x0;
+ *y0 = tu->y0;
+ *w = tu->width;
+ *h = tu->height;
+}
+
+#define MIN_ISP_PRED_WIDTH 4
+static int get_luma_predict_unit(const CodingUnit *cu, const TransformUnit *tu, const int idx, int *x0, int *y0, int *w, int *h)
+{
+ int has_luma = 1;
+ add_tu_area(tu, x0, y0, w, h);
+ if (cu->isp_split_type == ISP_VER_SPLIT && tu->width < MIN_ISP_PRED_WIDTH) {
+ *w = MIN_ISP_PRED_WIDTH;
+ has_luma = !(idx % (MIN_ISP_PRED_WIDTH / tu->width));
+ }
+ return has_luma;
+}
+
+static int get_chroma_predict_unit(const CodingUnit *cu, const TransformUnit *tu, const int idx, int *x0, int *y0, int *w, int *h)
+{
+ if (cu->isp_split_type == ISP_NO_SPLIT) {
+ add_tu_area(tu, x0, y0, w, h);
+ return 1;
+ }
+ if (idx == cu->num_intra_subpartitions - 1) {
+ *x0 = cu->x0;
+ *y0 = cu->y0;
+ *w = cu->cb_width;
+ *h = cu->cb_height;
+ return 1;
+ }
+ return 0;
+}
+
+//8.4.5.1 General decoding process for intra blocks
+static void predict_intra(VVCLocalContext *lc, const TransformUnit *tu, const int idx, const int target_ch_type)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const CodingUnit *cu = lc->cu;
+ const VVCTreeType tree_type = cu->tree_type;
+ int x0, y0, w, h;
+ if (cu->pred_mode != MODE_INTRA) {
+ add_reconstructed_area(lc, target_ch_type, tu->x0, tu->y0, tu->width, tu->height);
+ return;
+ }
+ if (!target_ch_type && tree_type != DUAL_TREE_CHROMA) {
+ if (get_luma_predict_unit(cu, tu, idx, &x0, &y0, &w, &h)) {
+ ff_vvc_set_neighbour_available(lc, x0, y0, w, h);
+ fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 0);
+ add_reconstructed_area(lc, 0, x0, y0, w, h);
+ }
+ }
+ if (target_ch_type && tree_type != DUAL_TREE_LUMA) {
+ if (get_chroma_predict_unit(cu, tu, idx, &x0, &y0, &w, &h)){
+ ff_vvc_set_neighbour_available(lc, x0, y0, w, h);
+ if (is_cclm(cu->intra_pred_mode_c)) {
+ fc->vvcdsp.intra.intra_cclm_pred(lc, x0, y0, w, h);
+ } else {
+ fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 1);
+ fc->vvcdsp.intra.intra_pred(lc, x0, y0, w, h, 2);
+ }
+ add_reconstructed_area(lc, 1, x0, y0, w, h);
+ }
+ }
+}
+
+static void scale_clip(int *coeff, const int nzw, const int w, const int h,
+ const int shift, const int log2_transform_range)
+{
+ const int add = 1 << (shift - 1);
+ for (int y = 0; y < h; y++) {
+ int *p = coeff + y * w;
+ for (int x = 0; x < nzw; x++) {
+ *p = av_clip_intp2((*p + add) >> shift, log2_transform_range);
+ p++;
+ }
+ memset(p, 0, sizeof(*p) * (w - nzw));
+ }
+}
+
+static void scale(int *out, const int *in, const int w, const int h, const int shift)
+{
+ const int add = 1 << (shift - 1);
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int *o = out + y * w + x;
+ const int *i = in + y * w + x;
+ *o = (*i + add) >> shift;
+ }
+ }
+}
+
+// part of 8.7.3 Scaling process for transform coefficients
+static void derive_qp(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const CodingUnit *cu = lc->cu;
+ int qp, qp_act_offset;
+
+ if (tb->c_idx == 0) {
+ //fix me
+ qp = cu->qp[LUMA] + sps->qp_bd_offset;
+ qp_act_offset = cu->act_enabled_flag ? -5 : 0;
+ } else {
+ const int is_jcbcr = tu->joint_cbcr_residual_flag && tu->coded_flag[CB] && tu->coded_flag[CR];
+ const int idx = is_jcbcr ? JCBCR : tb->c_idx;
+ qp = cu->qp[idx];
+ qp_act_offset = cu->act_enabled_flag ? 1 : 0;
+ }
+ if (tb->ts) {
+ const int qp_prime_ts_min = 4 + 6 * sps->r->sps_min_qp_prime_ts;
+
+ tb->qp = av_clip(qp + qp_act_offset, qp_prime_ts_min, 63 + sps->qp_bd_offset);
+ tb->rect_non_ts_flag = 0;
+ tb->bd_shift = 10;
+ } else {
+ const int log_sum = tb->log2_tb_width + tb->log2_tb_height;
+ const int rect_non_ts_flag = log_sum & 1;
+
+ tb->qp = av_clip(qp + qp_act_offset, 0, 63 + sps->qp_bd_offset);
+ tb->rect_non_ts_flag = rect_non_ts_flag;
+ tb->bd_shift = sps->bit_depth + rect_non_ts_flag + (log_sum / 2)
+ + 10 - sps->log2_transform_range + rsh->sh_dep_quant_used_flag;
+ }
+ tb->bd_offset = (1 << tb->bd_shift) >> 1;
+}
+
+//8.7.3 Scaling process for transform coefficients
+static av_always_inline int derive_scale(const TransformBlock *tb, const int sh_dep_quant_used_flag)
+{
+ static const uint8_t rem6[63 + 2 * 6 + 1] = {
+ 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2,
+ 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5,
+ 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3,
+ 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3
+ };
+
+ static const uint8_t div6[63 + 2 * 6 + 1] = {
+ 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3,
+ 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10,
+ 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12
+ };
+
+ const static int level_scale[2][6] = {
+ { 40, 45, 51, 57, 64, 72 },
+ { 57, 64, 72, 80, 90, 102 }
+ };
+ const int addin = sh_dep_quant_used_flag && !tb->ts;
+ const int qp = tb->qp + addin;
+
+ return level_scale[tb->rect_non_ts_flag][rem6[qp]] << div6[qp];
+}
+
+//8.7.3 Scaling process for transform coefficients
+static const uint8_t* derive_scale_m(const VVCLocalContext *lc, const TransformBlock *tb, uint8_t *scale_m)
+{
+ //Table 38 – Specification of the scaling matrix identifier variable id according to predMode, cIdx, nTbW, and nTbH
+ const int ids[2][3][6] = {
+ {
+ { 0, 2, 8, 14, 20, 26 },
+ { 0, 3, 9, 15, 21, 21 },
+ { 0, 4, 10, 16, 22, 22 }
+ },
+ {
+ { 0, 5, 11, 17, 23, 27 },
+ { 0, 6, 12, 18, 24, 24 },
+ { 1, 7, 13, 19, 25, 25 },
+ }
+ };
+ const VVCFrameParamSets *ps = &lc->fc->ps;
+ const VVCSPS *sps = ps->sps;
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const CodingUnit *cu = lc->cu;
+ const VVCScalingList *sl = ps->sl;
+ const int id = ids[cu->pred_mode != MODE_INTRA][tb->c_idx][FFMAX(tb->log2_tb_height, tb->log2_tb_width) - 1];
+ const int log2_matrix_size = (id < 2) ? 1 : (id < 8) ? 2 : 3;
+ uint8_t *p = scale_m;
+
+ av_assert0(!sps->r->sps_scaling_matrix_for_alternative_colour_space_disabled_flag);
+
+ if (!rsh->sh_explicit_scaling_list_used_flag || tb->ts ||
+ sps->r->sps_scaling_matrix_for_lfnst_disabled_flag && cu->apply_lfnst_flag[tb->c_idx])
+ return ff_vvc_default_scale_m;
+
+ if (!sl) {
+ av_log(lc->fc->log_ctx, AV_LOG_WARNING, "bug: no scaling list aps, id = %d", ps->ph.r->ph_scaling_list_aps_id);
+ return ff_vvc_default_scale_m;
+ }
+
+ for (int y = tb->min_scan_y; y <= tb->max_scan_y; y++) {
+ const int off = y << log2_matrix_size >> tb->log2_tb_height << log2_matrix_size;
+ const uint8_t *m = &sl->scaling_matrix_rec[id][off];
+
+ for (int x = tb->min_scan_x; x <= tb->max_scan_x; x++)
+ *p++ = m[x << log2_matrix_size >> tb->log2_tb_width];
+ }
+ if (id >= SL_START_16x16 && !tb->min_scan_x && !tb->min_scan_y)
+ *scale_m = sl->scaling_matrix_dc_rec[id - SL_START_16x16];
+
+ return scale_m;
+}
+
+//8.7.3 Scaling process for transform coefficients
+static av_always_inline int scale_coeff(const TransformBlock *tb, int coeff,
+ const int scale, const int scale_m, const int log2_transform_range)
+{
+ coeff = (coeff * scale * scale_m + tb->bd_offset) >> tb->bd_shift;
+ coeff = av_clip_intp2(coeff, log2_transform_range);
+ return coeff;
+}
+
+static void dequant(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb)
+{
+ uint8_t tmp[MAX_TB_SIZE * MAX_TB_SIZE];
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const uint8_t *scale_m = derive_scale_m(lc, tb, tmp);
+ int scale;
+
+ derive_qp(lc, tu, tb);
+ scale = derive_scale(tb, rsh->sh_dep_quant_used_flag);
+
+ for (int y = tb->min_scan_y; y <= tb->max_scan_y; y++) {
+ for (int x = tb->min_scan_x; x <= tb->max_scan_x; x++) {
+ int *coeff = tb->coeffs + y * tb->tb_width + x;
+
+ if (*coeff)
+ *coeff = scale_coeff(tb, *coeff, scale, *scale_m, sps->log2_transform_range);
+ scale_m++;
+ }
+ }
+}
+
+//transmatrix[0][0]
+#define DCT_A 64
+static void itx_2d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ const int w = tb->tb_width;
+ const int h = tb->tb_height;
+ const size_t nzw = tb->max_scan_x + 1;
+ const size_t nzh = tb->max_scan_y + 1;
+ const int shift[] = { 7, 5 + sps->log2_transform_range - sps->bit_depth };
+
+ if (w == h && nzw == 1 && nzh == 1 && trh == DCT2 && trv == DCT2) {
+ const int add[] = { 1 << (shift[0] - 1), 1 << (shift[1] - 1) };
+ const int t = (tb->coeffs[0] * DCT_A + add[0]) >> shift[0];
+ const int dc = (t * DCT_A + add[1]) >> shift[1];
+
+ for (int i = 0; i < w * h; i++)
+ tb->coeffs[i] = dc;
+
+ return;
+ }
+
+ for (int x = 0; x < nzw; x++)
+ fc->vvcdsp.itx.itx[trv][tb->log2_tb_height - 1](tb->coeffs + x, w, nzh);
+ scale_clip(tb->coeffs, nzw, w, h, shift[0], sps->log2_transform_range);
+
+ for (int y = 0; y < h; y++)
+ fc->vvcdsp.itx.itx[trh][tb->log2_tb_width - 1](tb->coeffs + y * w, 1, nzw);
+ scale(tb->coeffs, tb->coeffs, w, h, shift[1]);
+}
+
+static void itx_1d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ const int w = tb->tb_width;
+ const int h = tb->tb_height;
+ const size_t nzw = tb->max_scan_x + 1;
+ const size_t nzh = tb->max_scan_y + 1;
+
+ if ((w > 1 && nzw == 1 && trh == DCT2) || (h > 1 && nzh == 1 && trv == DCT2)) {
+ const int shift = 6 + sps->log2_transform_range - sps->bit_depth;
+ const int add = 1 << (shift - 1);
+ const int dc = (tb->coeffs[0] * DCT_A + add) >> shift;
+
+ for (int i = 0; i < w * h; i++)
+ tb->coeffs[i] = dc;
+
+ return;
+ }
+
+ if (w > 1)
+ fc->vvcdsp.itx.itx[trh][tb->log2_tb_width - 1](tb->coeffs, 1, nzw);
+ else
+ fc->vvcdsp.itx.itx[trv][tb->log2_tb_height - 1](tb->coeffs, 1, nzh);
+ scale(tb->coeffs, tb->coeffs, w, h, 6 + sps->log2_transform_range - sps->bit_depth);
+}
+
+static void transform_bdpcm(TransformBlock *tb, const VVCLocalContext *lc, const CodingUnit *cu)
+{
+ const VVCSPS *sps = lc->fc->ps.sps;
+ const IntraPredMode mode = tb->c_idx ? cu->intra_pred_mode_c : cu->intra_pred_mode_y;
+ const int vertical = mode == INTRA_VERT;
+ lc->fc->vvcdsp.itx.transform_bdpcm(tb->coeffs, tb->tb_width, tb->tb_height,
+ vertical, sps->log2_transform_range);
+ if (vertical)
+ tb->max_scan_y = tb->tb_height - 1;
+ else
+ tb->max_scan_x = tb->tb_width - 1;
+}
+
+static void itransform(VVCLocalContext *lc, TransformUnit *tu, const int tu_idx, const int target_ch_type)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const VVCSH *sh = &lc->sc->sh;
+ const CodingUnit *cu = lc->cu;
+ const int ps = fc->ps.sps->pixel_shift;
+ DECLARE_ALIGNED(32, int, temp)[MAX_TB_SIZE * MAX_TB_SIZE];
+
+ for (int i = 0; i < tu->nb_tbs; i++) {
+ TransformBlock *tb = &tu->tbs[i];
+ const int c_idx = tb->c_idx;
+ const int ch_type = c_idx > 0;
+
+ if (ch_type == target_ch_type && tb->has_coeffs) {
+ const int w = tb->tb_width;
+ const int h = tb->tb_height;
+ const int chroma_scale = ch_type && sh->r->sh_lmcs_used_flag && fc->ps.ph.r->ph_chroma_residual_scale_flag && (w * h > 4);
+ const ptrdiff_t stride = fc->frame->linesize[c_idx];
+ const int hs = sps->hshift[c_idx];
+ const int vs = sps->vshift[c_idx];
+ uint8_t *dst = &fc->frame->data[c_idx][(tb->y0 >> vs) * stride + ((tb->x0 >> hs) << ps)];
+
+ if (cu->bdpcm_flag[tb->c_idx])
+ transform_bdpcm(tb, lc, cu);
+ dequant(lc, tu, tb);
+ if (!tb->ts) {
+ enum TxType trh, trv;
+
+ if (cu->apply_lfnst_flag[c_idx])
+ ilfnst_transform(lc, tb);
+ derive_transform_type(fc, lc, tb, &trh, &trv);
+ if (w > 1 && h > 1)
+ itx_2d(fc, tb, trh, trv);
+ else
+ itx_1d(fc, tb, trh, trv);
+ }
+
+ if (chroma_scale)
+ fc->vvcdsp.intra.lmcs_scale_chroma(lc, temp, tb->coeffs, w, h, cu->x0, cu->y0);
+ fc->vvcdsp.itx.add_residual(dst, chroma_scale ? temp : tb->coeffs, w, h, stride);
+
+ if (tu->joint_cbcr_residual_flag && tb->c_idx)
+ add_residual_for_joint_coding_chroma(lc, tu, tb, chroma_scale);
+ }
+ }
+}
+
+static int reconstruct(VVCLocalContext *lc)
+{
+ VVCFrameContext *fc = lc->fc;
+ CodingUnit *cu = lc->cu;
+ const int start = cu->tree_type == DUAL_TREE_CHROMA;
+ const int end = fc->ps.sps->r->sps_chroma_format_idc && (cu->tree_type != DUAL_TREE_LUMA);
+
+ for (int ch_type = start; ch_type <= end; ch_type++) {
+ TransformUnit *tu = cu->tus.head;
+ for (int i = 0; tu; i++) {
+ predict_intra(lc, tu, i, ch_type);
+ itransform(lc, tu, i, ch_type);
+ tu = tu->next;
+ }
+ }
+ return 0;
+}
+
+int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int x_ctb = rx << sps->ctb_log2_size_y;
+ const int y_ctb = ry << sps->ctb_log2_size_y;
+ CTU *ctu = fc->tab.ctus + rs;
+ CodingUnit *cu = ctu->cus;
+ int ret = 0;
+
+ lc->num_ras[0] = lc->num_ras[1] = 0;
+ lc->lmcs.x_vpdu = -1;
+ lc->lmcs.y_vpdu = -1;
+ ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs);
+ while (cu) {
+ lc->cu = cu;
+
+ if (cu->ciip_flag)
+ ff_vvc_predict_ciip(lc);
+ if (cu->coded_flag) {
+ ret = reconstruct(lc);
+ } else {
+ add_reconstructed_area(lc, LUMA, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+ add_reconstructed_area(lc, CHROMA, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+ }
+ cu = cu->next;
+ }
+ ff_vvc_ctu_free_cus(ctu);
+ return ret;
+}
+
+int ff_vvc_get_mip_size_id(const int w, const int h)
+{
+ if (w == 4 && h == 4)
+ return 0;
+ if ((w == 4 || h == 4) || (w == 8 && h == 8))
+ return 1;
+ return 2;
+}
+
+int ff_vvc_nscale_derive(const int w, const int h, const int mode)
+{
+ int side_size, nscale;
+ av_assert0(mode < INTRA_LT_CCLM && !(mode > INTRA_HORZ && mode < INTRA_VERT));
+ if (mode == INTRA_PLANAR || mode == INTRA_DC ||
+ mode == INTRA_HORZ || mode == INTRA_VERT) {
+ nscale = (av_log2(w) + av_log2(h) - 2) >> 2;
+ } else {
+ const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode);
+ const int inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle);
+ if (mode >= INTRA_VERT)
+ side_size = h;
+ if (mode <= INTRA_HORZ)
+ side_size = w;
+ nscale = FFMIN(2, av_log2(side_size) - av_log2(3 * inv_angle - 2) + 8);
+ }
+ return nscale;
+}
+
+int ff_vvc_need_pdpc(const int w, const int h, const uint8_t bdpcm_flag, const int mode, const int ref_idx)
+{
+ av_assert0(mode < INTRA_LT_CCLM);
+ if ((w >= 4 && h >= 4) && !ref_idx && !bdpcm_flag) {
+ int nscale;
+ if (mode == INTRA_PLANAR || mode == INTRA_DC ||
+ mode == INTRA_HORZ || mode == INTRA_VERT)
+ return 1;
+ if (mode > INTRA_HORZ && mode < INTRA_VERT)
+ return 0;
+ nscale = ff_vvc_nscale_derive(w, h, mode);
+ return nscale >= 0;
+
+ }
+ return 0;
+}
+
+static const ReconstructedArea* get_reconstructed_area(const VVCLocalContext *lc, const int x, const int y, const int c_idx)
+{
+ const int ch_type = c_idx > 0;
+ for (int i = lc->num_ras[ch_type] - 1; i >= 0; i--) {
+ const ReconstructedArea* a = &lc->ras[ch_type][i];
+ const int r = (a->x + a->w);
+ const int b = (a->y + a->h);
+ if (a->x <= x && x < r && a->y <= y && y < b)
+ return a;
+
+ //it's too far away, no need check it;
+ if (x >= r && y >= b)
+ break;
+ }
+ return NULL;
+}
+
+int ff_vvc_get_top_available(const VVCLocalContext *lc, const int x, const int y, int target_size, const int c_idx)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int hs = sps->hshift[c_idx];
+ const int vs = sps->vshift[c_idx];
+ const int log2_ctb_size_v = sps->ctb_log2_size_y - vs;
+ const int end_of_ctb_x = ((lc->cu->x0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y;
+ const int y0b = av_mod_uintp2(y, log2_ctb_size_v);
+ const int max_x = FFMIN(fc->ps.pps->width, end_of_ctb_x) >> hs;
+ const ReconstructedArea *a;
+ int px = x;
+
+ if (!y0b) {
+ if (!lc->ctb_up_flag)
+ return 0;
+ target_size = FFMIN(target_size, (lc->end_of_tiles_x >> hs) - x);
+ if (sps->r->sps_entropy_coding_sync_enabled_flag)
+ target_size = FFMIN(target_size, (end_of_ctb_x >> hs) - x);
+ return target_size;
+ }
+
+ target_size = FFMAX(0, FFMIN(target_size, max_x - x));
+ while (target_size > 0 && (a = get_reconstructed_area(lc, px, y - 1, c_idx))) {
+ const int sz = FFMIN(target_size, a->x + a->w - px);
+ px += sz;
+ target_size -= sz;
+ }
+ return px - x;
+}
+
+int ff_vvc_get_left_available(const VVCLocalContext *lc, const int x, const int y, int target_size, const int c_idx)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int hs = sps->hshift[c_idx];
+ const int vs = sps->vshift[c_idx];
+ const int log2_ctb_size_h = sps->ctb_log2_size_y - hs;
+ const int x0b = av_mod_uintp2(x, log2_ctb_size_h);
+ const int end_of_ctb_y = ((lc->cu->y0 >> sps->ctb_log2_size_y) + 1) << sps->ctb_log2_size_y;
+ const int max_y = FFMIN(fc->ps.pps->height, end_of_ctb_y) >> vs;
+ const ReconstructedArea *a;
+ int py = y;
+
+ if (!x0b && !lc->ctb_left_flag)
+ return 0;
+
+ target_size = FFMAX(0, FFMIN(target_size, max_y - y));
+ if (!x0b)
+ return target_size;
+
+ while (target_size > 0 && (a = get_reconstructed_area(lc, x - 1, py, c_idx))) {
+ const int sz = FFMIN(target_size, a->y + a->h - py);
+ py += sz;
+ target_size -= sz;
+ }
+ return py - y;
+}
+
+static int less(const void *a, const void *b)
+{
+ return *(const int*)a - *(const int*)b;
+}
+
+int ff_vvc_ref_filter_flag_derive(const int mode)
+{
+ static const int modes[] = { -14, -12, -10, -6, INTRA_PLANAR, 2, 34, 66, 72, 76, 78, 80};
+ return bsearch(&mode, modes, FF_ARRAY_ELEMS(modes), sizeof(int), less) != NULL;
+}
+
+int ff_vvc_intra_pred_angle_derive(const int pred_mode)
+{
+ static const int angles[] = {
+ 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 23, 26, 29,
+ 32, 35, 39, 45, 51, 57, 64, 73, 86, 102, 128, 171, 256, 341, 512
+ };
+ int sign = 1, idx, intra_pred_angle;
+ if (pred_mode > INTRA_DIAG) {
+ idx = pred_mode - INTRA_VERT;
+ } else if (pred_mode > 0) {
+ idx = INTRA_HORZ - pred_mode;
+ } else {
+ idx = INTRA_HORZ - 2 - pred_mode;
+ }
+ if (idx < 0) {
+ idx = -idx;
+ sign = -1;
+ }
+ intra_pred_angle = sign * angles[idx];
+ return intra_pred_angle;
+}
+
+#define ROUND(f) (int)(f < 0 ? -(-f + 0.5) : (f + 0.5))
+int ff_vvc_intra_inv_angle_derive(const int intra_pred_angle)
+{
+ float inv_angle;
+ av_assert0(intra_pred_angle);
+ inv_angle = 32 * 512.0 / intra_pred_angle;
+ return ROUND(inv_angle);
+}
+
+//8.4.5.2.7 Wide angle intra prediction mode mapping proces
+int ff_vvc_wide_angle_mode_mapping(const CodingUnit *cu,
+ const int tb_width, const int tb_height, const int c_idx, int pred_mode_intra)
+{
+ int nw, nh, wh_ratio, min, max;
+
+ if (cu->isp_split_type == ISP_NO_SPLIT || c_idx) {
+ nw = tb_width;
+ nh = tb_height;
+ } else {
+ nw = cu->cb_width;
+ nh = cu->cb_height;
+ }
+ wh_ratio = FFABS(ff_log2(nw) - ff_log2(nh));
+ max = (wh_ratio > 1) ? (8 + 2 * wh_ratio) : 8;
+ min = (wh_ratio > 1) ? (60 - 2 * wh_ratio) : 60;
+
+ if (nw > nh && pred_mode_intra >=2 && pred_mode_intra < max)
+ pred_mode_intra += 65;
+ else if (nh > nw && pred_mode_intra <= 66 && pred_mode_intra > min)
+ pred_mode_intra -= 67;
+ return pred_mode_intra;
+}
diff --git a/libavcodec/vvc/vvc_intra.h b/libavcodec/vvc/vvc_intra.h
new file mode 100644
index 0000000000..6b674008f9
--- /dev/null
+++ b/libavcodec/vvc/vvc_intra.h
@@ -0,0 +1,49 @@
+/*
+ * VVC intra prediction
+ *
+ * Copyright (C) 2021 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef AVCODEC_VVC_VVC_INTRA_H
+#define AVCODEC_VVC_VVC_INTRA_H
+
+#include "vvc_ctu.h"
+
+/**
+ * reconstruct a CTU
+ * @param lc local context for CTU
+ * @param rs raster order for the CTU.
+ * @param rx raster order x for the CTU.
+ * @param ry raster order y for the CTU.
+ * @return AVERROR
+ */
+int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry);
+
+//utils for vvc_intra_template
+int ff_vvc_get_top_available(const VVCLocalContext *lc, int x0, int y0, int target_size, int c_idx);
+int ff_vvc_get_left_available(const VVCLocalContext *lc, int x0, int y0, int target_size, int c_idx);
+int ff_vvc_get_mip_size_id(int w, int h);
+int ff_vvc_need_pdpc(int w, int h, uint8_t bdpcm_flag, int mode, int ref_idx);
+int ff_vvc_nscale_derive(int w, int h, int mode);
+int ff_vvc_ref_filter_flag_derive(int mode);
+int ff_vvc_intra_pred_angle_derive(int pred_mode);
+int ff_vvc_intra_inv_angle_derive(int pred_mode);
+int ff_vvc_wide_angle_mode_mapping(const CodingUnit *cu,
+ int tb_width, int tb_height, int c_idx, int pred_mode_intra);
+
+#endif // AVCODEC_VVC_VVC_INTRA_H
diff --git a/libavcodec/vvc/vvc_intra_template.c b/libavcodec/vvc/vvc_intra_template.c
new file mode 100644
index 0000000000..9fb47549d5
--- /dev/null
+++ b/libavcodec/vvc/vvc_intra_template.c
@@ -0,0 +1,1015 @@
+/*
+ * VVC intra prediction DSP
+ *
+ * Copyright (C) 2021-2023 Nuomi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavcodec/bit_depth_template.c"
+
+#include "vvc_intra.h"
+
+#define POS(x, y) src[(x) + stride * (y)]
+
+static av_always_inline void FUNC(cclm_linear_pred)(VVCFrameContext *fc, const int x0, const int y0,
+ const int w, const int h, const pixel* pdsy, const int *a, const int *b, const int *k)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS - 1; i++) {
+ const int c_idx = i + 1;
+ const int x = x0 >> sps->hshift[c_idx];
+ const int y = y0 >> sps->vshift[c_idx];
+ const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel);
+ pixel *src = (pixel*)fc->frame->data[c_idx] + x + y * stride;
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ const int dsy = pdsy[y * w + x];
+ const int pred = ((dsy * a[i]) >> k[i]) + b[i];
+ POS(x, y) = CLIP(pred);
+ }
+ }
+ }
+}
+
+#define MAX_PICK_POS 4
+#define TOP 0
+#define LEFT 1
+
+static av_always_inline void FUNC(cclm_get_params_default)(int *a, int *b, int *k)
+{
+ for (int i = 0; i < 2; i++) {
+ a[i] = k[i] = 0;
+ b[i] = 1 << (BIT_DEPTH - 1);
+ }
+}
+
+static av_always_inline int FUNC(cclm_get_select_pos)(const VVCLocalContext *lc,
+ const int x, const int y, const int w, const int h, const int avail_t, const int avail_l,
+ int cnt[2], int pos[2][MAX_PICK_POS])
+{
+ const enum IntraPredMode mode = lc->cu->intra_pred_mode_c;
+ const int num_is4 = !avail_t || !avail_l || mode != INTRA_LT_CCLM;
+ int num_samp[2];
+
+ if (mode == INTRA_LT_CCLM) {
+ num_samp[TOP] = avail_t ? w : 0;
+ num_samp[LEFT] = avail_l ? h : 0;
+ } else {
+ num_samp[TOP] = (avail_t && mode == INTRA_T_CCLM) ? ff_vvc_get_top_available(lc, x, y, w + FFMIN(w, h), 1) : 0;
+ num_samp[LEFT] = (avail_l && mode == INTRA_L_CCLM) ? ff_vvc_get_left_available(lc, x, y, h + FFMIN(w, h), 1) : 0;
+ }
+ if (!num_samp[TOP] && !num_samp[LEFT]) {
+ return 0;
+ }
+ for (int i = TOP; i <= LEFT; i++) {
+ const int start = num_samp[i] >> (2 + num_is4);
+ const int step = FFMAX(1, num_samp[i] >> (1 + num_is4)) ;
+ cnt[i] = FFMIN(num_samp[i], (1 + num_is4) << 1);
+ for (int c = 0; c < cnt[i]; c++)
+ pos[i][c] = start + c * step;
+ }
+ return 1;
+}
+
+static av_always_inline void FUNC(cclm_select_luma_444)(const pixel *src, const int step,
+ const int cnt, const int pos[MAX_PICK_POS], pixel *sel_luma)
+{
+ for (int i = 0; i < cnt; i++)
+ sel_luma[i] = src[pos[i] * step];
+}
+
+static av_always_inline void FUNC(cclm_select_luma)(const VVCFrameContext *fc,
+ const int x0, const int y0, const int avail_t, const int avail_l, const int cnt[2], const int pos[2][MAX_PICK_POS],
+ pixel *sel_luma)
+{
+ const VVCSPS *sps = fc->ps.sps;
+
+ const int b_ctu_boundary = !av_mod_uintp2(y0, sps->ctb_log2_size_y);
+ const int hs = sps->hshift[1];
+ const int vs = sps->vshift[1];
+ const ptrdiff_t stride = fc->frame->linesize[0] / sizeof(pixel);
+
+ if (!hs && !vs) {
+ const pixel* src = (pixel*)fc->frame->data[0] + x0 + y0 * stride;
+ FUNC(cclm_select_luma_444)(src - avail_t * stride, 1, cnt[TOP], pos[TOP], sel_luma);
+ FUNC(cclm_select_luma_444)(src - avail_l, stride, cnt[LEFT], pos[LEFT], sel_luma + cnt[TOP]);
+ } else {
+ // top
+ if (vs && !b_ctu_boundary) {
+ const pixel *source = (pixel *)fc->frame->data[0] + x0 + (y0 - 2) * stride;
+ for (int i = 0; i < cnt[TOP]; i++) {
+ const int x = pos[TOP][i] << hs;
+ const pixel *src = source + x;
+ const int has_left = x || avail_l;
+ const pixel l = has_left ? POS(-1, 0) : POS(0, 0);
+ if (sps->r->sps_chroma_vertical_collocated_flag) {
+ sel_luma[i] = (POS(0, -1) + l + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3;
+ } else {
+ const pixel l1 = has_left ? POS(-1, 1) : POS(0, 1);
+ sel_luma[i] = (l + l1 + 2 * (POS(0, 0) + POS(0, 1)) + POS(1, 0) + POS(1, 1) + 4) >> 3;
+ }
+ }
+ } else {
+ const pixel *source = (pixel*)fc->frame->data[0] + x0 + (y0 - 1) * stride;
+ for (int i = 0; i < cnt[TOP]; i++) {
+ const int x = pos[TOP][i] << hs;
+ const pixel *src = source + x;
+ const int has_left = x || avail_l;
+ const pixel l = has_left ? POS(-1, 0) : POS(0, 0);
+ sel_luma[i] = (l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2;
+ }
+ }
+
+ // left
+ {
+ const pixel *left;
+ const pixel *source = (pixel *)fc->frame->data[0] + x0 + y0 * stride - (1 + hs) * avail_l;
+ left = source - avail_l;
+
+ for (int i = 0; i < cnt[LEFT]; i++) {
+ const int y = pos[LEFT][i] << vs;
+ const int offset = y * stride;
+ const pixel *l = left + offset;
+ const pixel *src = source + offset;
+ pixel pred;
+ if (!vs) {
+ pred = (*l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2;
+ } else {
+ if (sps->r->sps_chroma_vertical_collocated_flag) {
+ const int has_top = y || avail_t;
+ const pixel t = has_top ? POS(0, -1) : POS(0, 0);
+ pred = (*l + t + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3;
+ } else {
+ pred = (*l + *(l + stride) + 2 * POS(0, 0) + 2 * POS(0, 1) + POS(1, 0) + POS(1, 1) + 4) >> 3;
+ }
+ }
+ sel_luma[i + cnt[TOP]] = pred;
+ }
+ }
+ }
+}
+
+static av_always_inline void FUNC(cclm_select_chroma)(const VVCFrameContext *fc,
+ const int x, const int y, const int cnt[2], const int pos[2][MAX_PICK_POS],
+ pixel sel[][MAX_PICK_POS * 2])
+{
+ for (int c_idx = 1; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) {
+ const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel);
+
+ //top
+ const pixel *src = (pixel*)fc->frame->data[c_idx] + x + (y - 1)* stride;
+ for (int i = 0; i < cnt[TOP]; i++) {
+ sel[c_idx][i] = src[pos[TOP][i]];
+ }
+
+ //left
+ src = (pixel*)fc->frame->data[c_idx] + x - 1 + y * stride;
+ for (int i = 0; i < cnt[LEFT]; i++) {
+ sel[c_idx][i + cnt[TOP]] = src[pos[LEFT][i] * stride];
+ }
+ }
+}
+
+static av_always_inline int FUNC(cclm_select_samples)(const VVCLocalContext *lc,
+ const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l,
+ pixel sel[][MAX_PICK_POS * 2])
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int x = x0 >> sps->hshift[1];
+ const int y = y0 >> sps->vshift[1];
+ int cnt[2], pos[2][MAX_PICK_POS];
+
+ if (!FUNC(cclm_get_select_pos)(lc, x, y, w, h, avail_t, avail_l, cnt, pos))
+ return 0;
+
+ FUNC(cclm_select_luma)(fc, x0, y0, avail_t, avail_l, cnt, pos, sel[LUMA]);
+ FUNC(cclm_select_chroma)(fc, x, y, cnt, pos, sel);
+
+ if (cnt[TOP] + cnt[LEFT] == 2) {
+ for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) {
+ sel[c_idx][3] = sel[c_idx][0];
+ sel[c_idx][2] = sel[c_idx][1];
+ sel[c_idx][0] = sel[c_idx][1];
+ sel[c_idx][1] = sel[c_idx][3];
+ }
+ }
+ return 1;
+}
+
+static av_always_inline void FUNC(cclm_get_min_max)(
+ const pixel sel[][MAX_PICK_POS * 2], int *min, int *max)
+{
+ int min_grp_idx[] = { 0, 2 };
+ int max_grp_idx[] = { 1, 3 };
+
+ if (sel[LUMA][min_grp_idx[0]] > sel[LUMA][min_grp_idx[1]])
+ FFSWAP(int, min_grp_idx[0], min_grp_idx[1]);
+ if (sel[LUMA][max_grp_idx[0]] > sel[LUMA][max_grp_idx[1]])
+ FFSWAP(int, max_grp_idx[0], max_grp_idx[1]);
+ if (sel[LUMA][min_grp_idx[0]] > sel[LUMA][max_grp_idx[1]]) {
+ FFSWAP(int, min_grp_idx[0], max_grp_idx[0]);
+ FFSWAP(int, min_grp_idx[1], max_grp_idx[1]);
+ }
+ if (sel[LUMA][min_grp_idx[1]] > sel[LUMA][max_grp_idx[0]])
+ FFSWAP(int, min_grp_idx[1], max_grp_idx[0]);
+ for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) {
+ max[c_idx] = (sel[c_idx][max_grp_idx[0]] + sel[c_idx][max_grp_idx[1]] + 1) >> 1;
+ min[c_idx] = (sel[c_idx][min_grp_idx[0]] + sel[c_idx][min_grp_idx[1]] + 1) >> 1;
+ }
+}
+
+static av_always_inline void FUNC(cclm_get_params)(const VVCLocalContext *lc,
+ const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l,
+ int *a, int *b, int *k)
+{
+ pixel sel[VVC_MAX_SAMPLE_ARRAYS][MAX_PICK_POS * 2];
+ int max[VVC_MAX_SAMPLE_ARRAYS], min[VVC_MAX_SAMPLE_ARRAYS];
+ int diff;
+
+ if (!FUNC(cclm_select_samples)(lc, x0, y0, w, h, avail_t, avail_l, sel)) {
+ FUNC(cclm_get_params_default)(a, b, k);
+ return;
+ }
+
+ FUNC(cclm_get_min_max)(sel, min, max);
+
+ diff = max[LUMA] - min[LUMA];
+ if (diff == 0) {
+ for (int i = 0; i < 2; i++) {
+ a[i] = k[i] = 0;
+ b[i] = min[i + 1];
+ }
+ return;
+ }
+ for (int i = 0; i < 2; i++) {
+ const static int div_sig_table[] = {0, 7, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 1, 1, 0};
+ const int diffc = max[i + 1] - min[i + 1];
+ int x = av_log2(diff);
+ int y, v, sign, add;
+ const int norm_diff = ((diff << 4) >> x) & 15;
+ x += (norm_diff) ? 1 : 0;
+ y = abs(diffc) > 0 ? av_log2(abs(diffc)) + 1 : 0;
+ v = div_sig_table[norm_diff] | 8;
+ add = (1 << y >> 1);
+ a[i] = (diffc * v + add) >> y;
+ k[i] = FFMAX(1, 3 + x -y);
+ sign = a[i] < 0 ? -1 : (a[i] > 0);
+ a[i] = ((3 + x - y) < 1) ? sign * 15 : a[i];
+ b[i] = min[i + 1] - ((a[i] * min[0]) >> k[i]);
+ }
+
+}
+
+#undef TOP
+#undef LEFT
+
+static av_always_inline void FUNC(cclm_get_luma_rec_pixels)(const VVCFrameContext *fc,
+ const int x0, const int y0, const int w, const int h, const int avail_t, const int avail_l,
+ pixel *pdsy)
+{
+ const int hs = fc->ps.sps->hshift[1];
+ const int vs = fc->ps.sps->vshift[1];
+ const ptrdiff_t stride = fc->frame->linesize[0] / sizeof(pixel);
+ const pixel *source = (pixel*)fc->frame->data[0] + x0 + y0 * stride;
+ const pixel *left = source - avail_l;
+ const pixel *top = source - avail_t * stride;
+
+ const VVCSPS *sps = fc->ps.sps;
+ if (!hs && !vs) {
+ for (int i = 0; i < h; i++)
+ memcpy(pdsy + i * w, source + i * stride, w * sizeof(pixel));
+ return;
+ }
+ for (int i = 0; i < h; i++) {
+ const pixel *src = source;
+ const pixel *l = left;
+ const pixel *t = top;
+ if (!vs) {
+ for (int j = 0; j < w; j++) {
+ pixel pred = (*l + 2 * POS(0, 0) + POS(1, 0) + 2) >> 2;
+ pdsy[i * w + j] = pred;
+ src += 2;
+ l = src - 1;
+ }
+
+ } else {
+ if (sps->r->sps_chroma_vertical_collocated_flag) {
+ for (int j = 0; j < w; j++) {
+ pixel pred = (*l + *t + 4 * POS(0, 0) + POS(1, 0) + POS(0, 1) + 4) >> 3;
+ pdsy[i * w + j] = pred;
+ src += 2;
+ t += 2;
+ l = src - 1;
+ }
+ } else {
+ for (int j = 0; j < w; j++) {
+ pixel pred = (*l + *(l + stride) + 2 * POS(0, 0) + 2 * POS(0, 1) + POS(1, 0) + POS(1, 1) + 4) >> 3;
+
+ pdsy[i * w + j] = pred;
+ src += 2;
+ l = src - 1;
+ }
+ }
+ }
+ source += (stride << vs);
+ left += (stride << vs);
+ top = source - stride;
+ }
+}
+
+static av_always_inline void FUNC(cclm_pred_default)(VVCFrameContext *fc,
+ const int x, const int y, const int w, const int h, const int avail_t, const int avail_l)
+{
+ for (int c_idx = 1; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) {
+ const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel);
+ pixel *dst = (pixel*)fc->frame->data[c_idx] + x + y * stride;
+ for (int i = 0; i < h; i++) {
+ for (int j = 0; j < w; j++) {
+ dst[j] = 1 << (BIT_DEPTH - 1);
+ }
+ dst += stride;
+ }
+ }
+}
+
+//8.4.5.2.14 Specification of INTRA_LT_CCLM, INTRA_L_CCLM and INTRA_T_CCLM intra prediction mode
+static void FUNC(intra_cclm_pred)(const VVCLocalContext *lc, const int x0, const int y0,
+ const int width, const int height)
+{
+ VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int avail_t = ff_vvc_get_top_available(lc, x0, y0, 1, 0);
+ const int avail_l = ff_vvc_get_left_available(lc, x0, y0, 1, 0);
+ const int hs = sps->hshift[1];
+ const int vs = sps->vshift[1];
+ const int x = x0 >> hs;
+ const int y = y0 >> vs;
+ const int w = width >> hs;
+ const int h = height >> vs;
+ int a[2], b[2], k[2];
+
+ pixel dsy[MAX_TB_SIZE * MAX_TB_SIZE];
+ if (!avail_t && !avail_l) {
+ FUNC(cclm_pred_default)(fc, x, y, w, h, avail_t, avail_l);
+ return;
+ }
+ FUNC(cclm_get_luma_rec_pixels)(fc, x0, y0, w, h, avail_t, avail_l, dsy);
+ FUNC(cclm_get_params) (lc, x0, y0, w, h, avail_t, avail_l, a, b, k);
+ FUNC(cclm_linear_pred)(fc, x0, y0, w, h, dsy, a, b, k);
+}
+
+static int FUNC(lmcs_sum_samples)(const pixel *start, ptrdiff_t stride, const int avail, const int target_size)
+{
+ const int size = FFMIN(avail, target_size);
+ int sum = 0;
+ for (int i = 0; i < size; i++) {
+ sum += *start;
+ start += stride;
+ }
+ sum += *(start - stride) * (target_size - size);
+ return sum;
+}
+
+// 8.7.5.3 Picture reconstruction with luma dependent chroma residual scaling process for chroma samples
+static int FUNC(lmcs_derive_chroma_scale)(VVCLocalContext *lc, const int x0, const int y0)
+{
+ VVCFrameContext *fc = lc->fc;
+ const VVCLMCS *lmcs = &fc->ps.lmcs;
+ const int size_y = FFMIN(fc->ps.sps->ctb_size_y, 64);
+
+ const int x = x0 & ~(size_y - 1);
+ const int y = y0 & ~(size_y - 1);
+ if (lc->lmcs.x_vpdu != x || lc->lmcs.y_vpdu != y) {
+ int cnt = 0, luma = 0, i;
+ const pixel *src = (const pixel *)(fc->frame->data[LUMA] + y * fc->frame->linesize[LUMA] + (x << fc->ps.sps->pixel_shift));
+ const ptrdiff_t stride = fc->frame->linesize[LUMA] / sizeof(pixel);
+ const int avail_t = ff_vvc_get_top_available (lc, x, y, 1, 0);
+ const int avail_l = ff_vvc_get_left_available(lc, x, y, 1, 0);
+ if (avail_l) {
+ luma += FUNC(lmcs_sum_samples)(src - 1, stride, fc->ps.pps->height - y, size_y);
+ cnt = size_y;
+ }
+ if (avail_t) {
+ luma += FUNC(lmcs_sum_samples)(src - stride, 1, fc->ps.pps->width - x, size_y);
+ cnt += size_y;
+ }
+ if (cnt)
+ luma = (luma + (cnt >> 1)) >> av_log2(cnt);
+ else
+ luma = 1 << (BIT_DEPTH - 1);
+
+ for (i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) {
+ if (luma < lmcs->pivot[i + 1])
+ break;
+ }
+ i = FFMIN(i, LMCS_MAX_BIN_SIZE - 1);
+
+ lc->lmcs.chroma_scale = lmcs->chroma_scale_coeff[i];
+ lc->lmcs.x_vpdu = x;
+ lc->lmcs.y_vpdu = y;
+ }
+ return lc->lmcs.chroma_scale;
+}
+
+// 8.7.5.3 Picture reconstruction with luma dependent chroma residual scaling process for chroma samples
+static void FUNC(lmcs_scale_chroma)(VVCLocalContext *lc, int *dst, const int *coeff,
+ const int width, const int height, const int x0_cu, const int y0_cu)
+{
+ const int chroma_scale = FUNC(lmcs_derive_chroma_scale)(lc, x0_cu, y0_cu);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ const int c = av_clip_intp2(*coeff, BIT_DEPTH);
+
+ if (c > 0)
+ *dst = (c * chroma_scale + (1 << 10)) >> 11;
+ else
+ *dst = -((-c * chroma_scale + (1 << 10)) >> 11);
+ coeff++;
+ dst++;
+ }
+ }
+}
+
+static av_always_inline void FUNC(ref_filter)(const pixel *left, const pixel *top,
+ pixel *filtered_left, pixel *filtered_top, const int left_size, const int top_size,
+ const int unfilter_last_one)
+{
+ filtered_left[-1] = filtered_top[-1] = (left[0] + 2 * left[-1] + top[0] + 2 ) >> 2;
+ for (int i = 0; i < left_size - unfilter_last_one; i++) {
+ filtered_left[i] = (left[i- 1] + 2 * left[i] + left[i + 1] + 2) >> 2;
+ }
+ for (int i = 0; i < top_size - unfilter_last_one; i++) {
+ filtered_top[i] = (top[i-1] + 2 * top[i] + top[i + 1] + 2) >> 2;
+ }
+ if (unfilter_last_one) {
+ filtered_top[top_size - 1] = top[top_size - 1];
+ filtered_left[left_size - 1] = left[left_size - 1];
+ }
+}
+
+static av_always_inline void FUNC(prepare_intra_edge_params)(const VVCLocalContext *lc,
+ IntraEdgeParams* edge, const pixel *src, const ptrdiff_t stride,
+ const int x, int y, int w, int h, int c_idx, const int is_intra_mip,
+ const int mode, const int ref_idx, const int need_pdpc)
+{
+#define EXTEND(ptr, val, len) \
+do { \
+ for (i = 0; i < (len); i++) \
+ *(ptr + i) = val; \
+} while (0)
+ const CodingUnit *cu = lc->cu;
+ const int ref_filter_flag = is_intra_mip ? 0 : ff_vvc_ref_filter_flag_derive(mode);
+ const int filter_flag = !ref_idx && w * h > 32 && !c_idx &&
+ cu->isp_split_type == ISP_NO_SPLIT && ref_filter_flag;
+ int cand_up_left = lc->na.cand_up_left;
+ pixel *left = (pixel*)edge->left_array + MAX_TB_SIZE + 3;
+ pixel *top = (pixel*)edge->top_array + MAX_TB_SIZE + 3;
+ pixel *filtered_left = (pixel*)edge->filtered_left_array + MAX_TB_SIZE + 3;
+ pixel *filtered_top = (pixel*)edge->filtered_top_array + MAX_TB_SIZE + 3;
+ const int ref_line = ref_idx == 3 ? -4 : (-1 - ref_idx);
+ int left_size, top_size, unfilter_left_size, unfilter_top_size;
+ int left_available, top_available;
+ int refw, refh;
+ int intra_pred_angle, inv_angle;
+ int i;
+
+ if (is_intra_mip || mode == INTRA_PLANAR) {
+ left_size = h + 1;
+ top_size = w + 1;
+ unfilter_left_size = left_size + filter_flag;
+ unfilter_top_size = top_size + filter_flag;
+ } else if (mode == INTRA_DC) {
+ unfilter_left_size = left_size = h;
+ unfilter_top_size = top_size = w;
+ } else if (mode == INTRA_VERT) {
+ //we may need 1 pixel to predict the top left.
+ unfilter_left_size = left_size = need_pdpc ? h : 1;
+ unfilter_top_size = top_size = w;
+ } else if (mode == INTRA_HORZ) {
+ unfilter_left_size = left_size = h;
+ //even need_pdpc == 0, we may need 1 pixel to predict the top left.
+ unfilter_top_size = top_size = need_pdpc ? w : 1;
+ } else {
+ if (cu->isp_split_type == ISP_NO_SPLIT || c_idx) {
+ refw = w * 2;
+ refh = h * 2;
+ } else {
+ refw = cu->cb_width + w;
+ refh = cu->cb_height + h;
+ }
+ intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode);
+ inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle);
+ unfilter_top_size = top_size = refw;
+ unfilter_left_size = left_size = refh;
+ }
+
+ left_available = ff_vvc_get_left_available(lc, x, y, unfilter_left_size, c_idx);
+ for (i = 0; i < left_available; i++)
+ left[i] = POS(ref_line, i);
+
+ top_available = ff_vvc_get_top_available(lc, x, y, unfilter_top_size, c_idx);
+ memcpy(top, src + ref_line * stride, top_available * sizeof(pixel));
+
+ for (int i = -1; i >= ref_line; i--) {
+ if (cand_up_left) {
+ left[i] = POS(ref_line, i);
+ top[i] = POS(i, ref_line);
+ } else if (left_available) {
+ left[i] = top[i] = left[0];
+ } else if (top_available) {
+ left[i] = top[i] = top[0];
+ } else {
+ left[i] = top[i] = 1 << (BIT_DEPTH - 1);
+ }
+ }
+
+ EXTEND(top + top_available, top[top_available-1], unfilter_top_size - top_available);
+ EXTEND(left + left_available, left[left_available-1], unfilter_left_size - left_available);
+
+ if (ref_filter_flag) {
+ if (!ref_idx && w * h > 32 && !c_idx && cu->isp_split_type == ISP_NO_SPLIT ) {
+ const int unfilter_last_one = left_size == unfilter_left_size;
+ FUNC(ref_filter)(left, top, filtered_left, filtered_top, unfilter_left_size, unfilter_top_size, unfilter_last_one);
+ left = filtered_left;
+ top = filtered_top;
+ }
+ }
+ if (!is_intra_mip && mode != INTRA_PLANAR && mode != INTRA_DC) {
+ if (ref_filter_flag || ref_idx || cu->isp_split_type != ISP_NO_SPLIT) {
+ edge->filter_flag = 0;
+ } else {
+ const int min_dist_ver_hor = FFMIN(abs(mode - 50), abs(mode - 18));
+ const int intra_hor_ver_dist_thres[] = {24, 14, 2, 0, 0};
+ const int ntbs = (av_log2(w) + av_log2(h)) >> 1;
+ edge->filter_flag = min_dist_ver_hor > intra_hor_ver_dist_thres[ntbs - 2];
+ }
+
+ if (mode != INTRA_VERT && mode != INTRA_HORZ) {
+ if (mode >= INTRA_DIAG) {
+ if (intra_pred_angle < 0) {
+ pixel *p = top - (ref_idx + 1);
+ for (int x = -h; x < 0; x++) {
+ const int idx = -1 - ref_idx + FFMIN((x*inv_angle + 256) >> 9, h);
+ p[x] = left[idx];
+ }
+ } else {
+ for (int i = refw; i <= refw + FFMAX(1, w/h) * ref_idx + 1; i++)
+ top[i] = top[refw - 1];
+ }
+ } else {
+ if (intra_pred_angle < 0) {
+ pixel *p = left - (ref_idx + 1);
+ for (int x = -w; x < 0; x++) {
+ const int idx = -1 - ref_idx + FFMIN((x*inv_angle + 256) >> 9, w);
+ p[x] = top[idx];
+ }
+ } else {
+ for (int i = refh; i <= refh + FFMAX(1, h/w) * ref_idx + 1; i++)
+ left[i] = left[refh - 1];
+ }
+ }
+ }
+ }
+ edge->left = (uint8_t*)left;
+ edge->top = (uint8_t*)top;
+}
+
+//8.4.1 General decoding process for coding units coded in intra prediction mode
+static void FUNC(intra_pred)(const VVCLocalContext *lc, int x0, int y0,
+ const int width, const int height, int c_idx)
+{
+ VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const VVCPPS *pps = fc->ps.pps;
+ const CodingUnit *cu = lc->cu;
+ const int log2_min_cb_size = sps->min_cb_log2_size_y;
+ const int min_cb_width = pps->min_cb_width;
+ const int x_cb = x0 >> log2_min_cb_size;
+ const int y_cb = y0 >> log2_min_cb_size;
+
+ const int hshift = fc->ps.sps->hshift[c_idx];
+ const int vshift = fc->ps.sps->vshift[c_idx];
+ const int x = x0 >> hshift;
+ const int y = y0 >> vshift;
+ const int w = width >> hshift;
+ const int h = height >> vshift;
+ const ptrdiff_t stride = fc->frame->linesize[c_idx] / sizeof(pixel);
+
+ const int pred_mode = c_idx ? cu->intra_pred_mode_c : cu->intra_pred_mode_y;
+ const int mode = ff_vvc_wide_angle_mode_mapping(cu, w, h, c_idx, pred_mode);
+
+ const int intra_mip_flag = SAMPLE_CTB(fc->tab.imf, x_cb, y_cb);
+ const int is_intra_mip = intra_mip_flag && (!c_idx || cu->mip_chroma_direct_flag);
+ const int ref_idx = c_idx ? 0 : cu->intra_luma_ref_idx;
+ const int need_pdpc = ff_vvc_need_pdpc(w, h, cu->bdpcm_flag[c_idx], mode, ref_idx);
+
+
+ pixel *src = (pixel*)fc->frame->data[c_idx] + x + y * stride;
+ IntraEdgeParams edge;
+
+ FUNC(prepare_intra_edge_params)(lc, &edge, src, stride, x, y, w, h, c_idx, is_intra_mip, mode, ref_idx, need_pdpc);
+
+ if (is_intra_mip) {
+ int intra_mip_transposed_flag = SAMPLE_CTB(fc->tab.imtf, x_cb, y_cb);
+ int intra_mip_mode = SAMPLE_CTB(fc->tab.imm, x_cb, y_cb);
+
+ fc->vvcdsp.intra.pred_mip((uint8_t *)src, edge.top, edge.left,
+ w, h, stride, intra_mip_mode, intra_mip_transposed_flag);
+ } else if (mode == INTRA_PLANAR) {
+ fc->vvcdsp.intra.pred_planar((uint8_t *)src, edge.top, edge.left, w, h, stride);
+ } else if (mode == INTRA_DC) {
+ fc->vvcdsp.intra.pred_dc((uint8_t *)src, edge.top, edge.left, w, h, stride);
+ } else if (mode == INTRA_VERT) {
+ fc->vvcdsp.intra.pred_v((uint8_t *)src, edge.top, w, h, stride);
+ } else if (mode == INTRA_HORZ) {
+ fc->vvcdsp.intra.pred_h((uint8_t *)src, edge.left, w, h, stride);
+ } else {
+ if (mode >= INTRA_DIAG) {
+ fc->vvcdsp.intra.pred_angular_v((uint8_t *)src, edge.top, edge.left,
+ w, h, stride, c_idx, mode, ref_idx,
+ edge.filter_flag, need_pdpc);
+ } else {
+ fc->vvcdsp.intra.pred_angular_h((uint8_t *)src, edge.top, edge.left,
+ w, h, stride, c_idx, mode, ref_idx,
+ edge.filter_flag, need_pdpc);
+ }
+ }
+ if (need_pdpc) {
+ //8.4.5.2.15 Position-dependent intra prediction sample filtering process
+ if (!is_intra_mip && (mode == INTRA_PLANAR || mode == INTRA_DC ||
+ mode == INTRA_VERT || mode == INTRA_HORZ)) {
+ const int scale = (av_log2(w) + av_log2(h) - 2) >> 2;
+ const pixel *left = (pixel*)edge.left;
+ const pixel *top = (pixel*)edge.top;
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int l, t, wl, wt, pred;
+ pixel val;
+ if (mode == INTRA_PLANAR || mode == INTRA_DC) {
+ l = left[y];
+ t = top[x];
+ wl = 32 >> FFMIN((x << 1) >> scale, 31);
+ wt = 32 >> FFMIN((y << 1) >> scale, 31);
+ } else {
+ l = left[y] - left[-1] + POS(x,y);
+ t = top[x] - top[-1] + POS(x,y);
+ wl = (mode == INTRA_VERT) ? (32 >> FFMIN((x << 1) >> scale, 31)) : 0;
+ wt = (mode == INTRA_HORZ) ? (32 >> FFMIN((y << 1) >> scale, 31)) : 0;
+ }
+ val = POS(x, y);
+ pred = val + ((wl * (l - val) + wt * (t - val) + 32) >> 6);
+ POS(x, y) = CLIP(pred);
+ }
+ }
+ }
+ }
+}
+
+//8.4.5.2.11 Specification of INTRA_PLANAR intra prediction mode
+static av_always_inline void FUNC(pred_planar)(uint8_t *_src, const uint8_t *_top,
+ const uint8_t *_left, const int w, const int h, const ptrdiff_t stride)
+{
+ int x, y;
+ pixel *src = (pixel *)_src;
+ const pixel *top = (const pixel *)_top;
+ const pixel *left = (const pixel *)_left;
+ const int logw = av_log2(w);
+ const int logh = av_log2(h);
+ const int size = w * h;
+ const int shift = (logw + logh + 1);
+ for (y = 0; y < h; y++) {
+ for (x = 0; x < w; x++) {
+ const int pred_v = ((h - 1 - y) * top[x] + (y + 1) * left[h]) << logw;
+ const int pred_h = ((w - 1 - x) * left[y] + (x + 1) * top[w]) << logh;
+ const int pred = (pred_v + pred_h + size) >> shift;
+ POS(x, y) = pred;
+ }
+ }
+}
+
+//8.4.5.2.3 MIP boundary sample downsampling process
+static av_always_inline void FUNC(mip_downsampling)(int *reduced, const int boundary_size,
+ const pixel *ref, const int n_tb_s)
+{
+ const int b_dwn = n_tb_s / boundary_size;
+ const int log2 = av_log2(b_dwn);
+
+ if (boundary_size == n_tb_s) {
+ for (int i = 0; i < n_tb_s; i++)
+ reduced[i] = ref[i];
+ return;
+ }
+ for (int i = 0; i < boundary_size; i++) {
+ int r;
+ r = *ref++;
+ for (int j = 1; j < b_dwn; j++)
+ r += *ref++;
+ reduced[i] = (r + (1 << (log2 - 1))) >> log2;
+ }
+}
+
+static av_always_inline void FUNC(mip_reduced_pred)(pixel *src, const ptrdiff_t stride,
+ const int up_hor, const int up_ver, const int pred_size, const int *reduced, const int reduced_size,
+ const int ow, const int temp0, const uint8_t *matrix, int is_transposed)
+{
+ src = &POS(up_hor - 1, up_ver - 1);
+ for (int y = 0; y < pred_size; y++) {
+ for (int x = 0; x < pred_size; x++) {
+ int pred = 0;
+ for (int i = 0; i < reduced_size; i++)
+ pred += reduced[i] * matrix[i];
+ matrix += reduced_size;
+ pred = ((pred + ow) >> 6) + temp0;
+ pred = av_clip(pred, 0, (1<<BIT_DEPTH) - 1);
+ if (is_transposed)
+ POS(y * up_hor, x * up_ver) = pred;
+ else
+ POS(x * up_hor, y * up_ver) = pred;
+ }
+ }
+}
+
+static av_always_inline void FUNC(mip_upsampling_1d)(pixel *dst, const int dst_step, const int dst_stride, const int dst_height, const int factor,
+ const pixel *boundary, const int boundary_step, const int pred_size)
+{
+
+ for (int i = 0; i < dst_height; i++) {
+ const pixel *before = boundary;
+ const pixel *after = dst - dst_step;
+ pixel *d = dst;
+ for (int j = 0; j < pred_size; j++) {
+ after += dst_step * factor;
+ for (int k = 1; k < factor; k++) {
+ int mid = (factor - k) * (*before) + k * (*after);
+ *d = (mid + factor / 2) / factor;
+ d += dst_step;
+ }
+ before = after;
+ d += dst_step;
+ }
+ boundary += boundary_step;
+ dst += dst_stride;
+ }
+}
+
+//8.4.5.2.2 Matrix-based intra sample prediction
+static av_always_inline void FUNC(pred_mip)(uint8_t *_src, const uint8_t *_top,
+ const uint8_t *_left, const int w, const int h, const ptrdiff_t stride,
+ int mode_id, int is_transposed)
+{
+ pixel *src = (pixel *)_src;
+ const pixel *top = (const pixel *)_top;
+ const pixel *left = (const pixel *)_left;
+
+ const int size_id = ff_vvc_get_mip_size_id(w, h);
+ static const int boundary_sizes[] = {2, 4, 4};
+ static const int pred_sizes[] = {4, 4, 8};
+ const int boundary_size = boundary_sizes[size_id];
+ const int pred_size = pred_sizes[size_id];
+ const int in_size = 2 * boundary_size - ((size_id == 2) ? 1 : 0);
+ const uint8_t *matrix = ff_vvc_get_mip_matrix(size_id, mode_id);
+ const int up_hor = w / pred_size;
+ const int up_ver = h / pred_size;
+
+ int reduced[16];
+ int *red_t = reduced;
+ int *red_l = reduced + boundary_size;
+ int off = 1, ow = 0;
+ int temp0;
+
+ if (is_transposed) {
+ FFSWAP(int*, red_t, red_l);
+ }
+ FUNC(mip_downsampling)(red_t, boundary_size, top, w);
+ FUNC(mip_downsampling)(red_l, boundary_size, left, h);
+
+ temp0 = reduced[0];
+ if (size_id != 2) {
+ off = 0;
+ ow = (1 << (BIT_DEPTH - 1)) - temp0;
+ } else {
+ ow = reduced[1] - temp0;
+ }
+ reduced[0] = ow;
+ for (int i = 1; i < in_size; i++) {
+ reduced[i] = reduced[i + off] - temp0;
+ ow += reduced[i];
+ }
+ ow = 32 - 32 * ow;
+
+ FUNC(mip_reduced_pred)(src, stride, up_hor, up_ver, pred_size, reduced, in_size, ow, temp0, matrix, is_transposed);
+ if (up_hor > 1 || up_ver > 1) {
+ if (up_hor > 1)
+ FUNC(mip_upsampling_1d)(&POS(0, up_ver - 1), 1, up_ver * stride, pred_size, up_hor, left + up_ver - 1, up_ver, pred_size);
+ if (up_ver > 1)
+ FUNC(mip_upsampling_1d)(src, stride, 1, w, up_ver, top, 1, pred_size);
+ }
+}
+
+static av_always_inline pixel FUNC(pred_dc_val)(const pixel *top, const pixel *left,
+ const int w, const int h)
+{
+ pixel dc_val;
+ int sum = 0;
+ unsigned int offset = (w == h) ? (w << 1) : FFMAX(w, h);
+ const int shift = av_log2(offset);
+ offset >>= 1;
+ if (w >= h) {
+ for (int i = 0; i < w; i++)
+ sum += top[i];
+ }
+ if (w <= h) {
+ for (int i = 0; i < h; i++)
+ sum += left[i];
+ }
+ dc_val = (sum + offset) >> shift;
+ return dc_val;
+}
+
+//8.4.5.2.12 Specification of INTRA_DC intra prediction mode
+static av_always_inline void FUNC(pred_dc)(uint8_t *_src, const uint8_t *_top,
+ const uint8_t *_left, const int w, const int h, const ptrdiff_t stride)
+{
+ int x, y;
+ pixel *src = (pixel *)_src;
+ const pixel *top = (const pixel *)_top;
+ const pixel *left = (const pixel *)_left;
+ const pixel dc = FUNC(pred_dc_val)(top, left, w, h);
+ const pixel4 a = PIXEL_SPLAT_X4(dc);
+ for (y = 0; y < h; y++) {
+ pixel *s = src;
+ for (x = 0; x < w; x += 4) {
+ AV_WN4P(s, a);
+ s += 4;
+ }
+ src += stride;
+ }
+}
+
+static av_always_inline void FUNC(pred_v)(uint8_t *_src, const uint8_t *_top,
+ const int w, const int h, const ptrdiff_t stride)
+{
+ pixel *src = (pixel *)_src;
+ const pixel *top = (const pixel *)_top;
+ for (int y = 0; y < h; y++) {
+ memcpy(src, top, sizeof(pixel) * w);
+ src += stride;
+ }
+}
+
+static void FUNC(pred_h)(uint8_t *_src, const uint8_t *_left, const int w, const int h,
+ const ptrdiff_t stride)
+{
+ pixel *src = (pixel *)_src;
+ const pixel *left = (const pixel *)_left;
+ for (int y = 0; y < h; y++) {
+ const pixel4 a = PIXEL_SPLAT_X4(left[y]);
+ for (int x = 0; x < w; x += 4) {
+ AV_WN4P(&POS(x, y), a);
+ }
+ }
+}
+
+#define INTRA_LUMA_FILTER(p) CLIP((p[0] * f[0] + p[1] * f[1] + p[2] * f[2] + p[3] * f[3] + 32) >> 6)
+#define INTRA_CHROMA_FILTER(p) (((32 - fact) * p[1] + fact * p[2] + 16) >> 5)
+
+//8.4.5.2.13 Specification of INTRA_ANGULAR2..INTRA_ANGULAR66 intra prediction modes
+static void FUNC(pred_angular_v)(uint8_t *_src, const uint8_t *_top, const uint8_t *_left,
+ const int w, const int h, const ptrdiff_t stride, const int c_idx, const int mode,
+ const int ref_idx, const int filter_flag, const int need_pdpc)
+{
+ pixel *src = (pixel *)_src;
+ const pixel *left = (const pixel *)_left;
+ const pixel *top = (const pixel *)_top - (1 + ref_idx);
+ const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode);
+ int pos = (1 + ref_idx) * intra_pred_angle;
+ const int dp = intra_pred_angle;
+ const int is_luma = !c_idx;
+ int nscale, inv_angle;
+
+ if (need_pdpc) {
+ inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle);
+ nscale = ff_vvc_nscale_derive(w, h, mode);
+ }
+
+ for (int y = 0; y < h; y++) {
+ const int idx = (pos >> 5) + ref_idx;
+ const int fact = pos & 31;
+ if (!fact && (!is_luma || !filter_flag)) {
+ for (int x = 0; x < w; x++) {
+ const pixel *p = top + x + idx + 1;
+ POS(x, y) = *p;
+ }
+ } else {
+ if (!c_idx) {
+ const int8_t *f = ff_vvc_intra_luma_filter[filter_flag][fact];
+ for (int x = 0; x < w; x++) {
+ const pixel *p = top + x + idx;
+ POS(x, y) = INTRA_LUMA_FILTER(p);
+ }
+ } else {
+ for (int x = 0; x < w; x++) {
+ const pixel *p = top + x + idx;
+ POS(x, y) = INTRA_CHROMA_FILTER(p);
+ }
+ }
+ }
+ if (need_pdpc) {
+ int inv_angle_sum = 256 + inv_angle;
+ for (int x = 0; x < FFMIN(w, 3 << nscale); x++) {
+ const pixel l = left[y + (inv_angle_sum >> 9)];
+ const pixel val = POS(x, y);
+ const int wl = 32 >> ((x << 1) >> nscale);
+ const int pred = val + (((l - val) * wl + 32) >> 6);
+ POS(x, y) = CLIP(pred);
+ inv_angle_sum += inv_angle;
+ }
+ }
+ pos += dp;
+ }
+}
+
+//8.4.5.2.13 Specification of INTRA_ANGULAR2..INTRA_ANGULAR66 intra prediction modes
+static void FUNC(pred_angular_h)(uint8_t *_src, const uint8_t *_top, const uint8_t *_left,
+ const int w, const int h, const ptrdiff_t stride, const int c_idx, const int mode,
+ const int ref_idx, const int filter_flag, const int need_pdpc)
+{
+ pixel *src = (pixel *)_src;
+ const pixel *left = (const pixel *)_left - (1 + ref_idx);
+ const pixel *top = (const pixel *)_top;
+ const int is_luma = !c_idx;
+ const int intra_pred_angle = ff_vvc_intra_pred_angle_derive(mode);
+ const int dp = intra_pred_angle;
+ int nscale = 0, inv_angle, inv_angle_sum;
+
+ if (need_pdpc) {
+ inv_angle = ff_vvc_intra_inv_angle_derive(intra_pred_angle);
+ inv_angle_sum = 256 + inv_angle;
+ nscale = ff_vvc_nscale_derive(w, h, mode);
+ }
+
+ for (int y = 0; y < h; y++) {
+ int pos = (1 + ref_idx) * intra_pred_angle;
+ int wt;
+ if (need_pdpc)
+ wt = (32 >> ((y * 2) >> nscale));
+
+ for (int x = 0; x < w; x++) {
+ const int idx = (pos >> 5) + ref_idx;
+ const int fact = pos & 31;
+ const pixel *p = left + y + idx;
+ int pred;
+ if (!fact && (!is_luma || !filter_flag)) {
+ pred = p[1];
+ } else {
+ if (!c_idx) {
+ const int8_t *f = ff_vvc_intra_luma_filter[filter_flag][fact];
+ pred = INTRA_LUMA_FILTER(p);
+ } else {
+ pred = INTRA_CHROMA_FILTER(p);
+ }
+ }
+ if (need_pdpc) {
+ if (y < (3 << nscale)) {
+ const pixel t = top[x + (inv_angle_sum >> 9)];
+ pred = CLIP(pred + (((t - pred) * wt + 32) >> 6));
+ }
+ }
+ POS(x, y) = pred;
+ pos += dp;
+ }
+ if (need_pdpc)
+ inv_angle_sum += inv_angle;
+ }
+}
+
+static void FUNC(ff_vvc_intra_dsp_init)(VVCIntraDSPContext *const intra)
+{
+ intra->lmcs_scale_chroma = FUNC(lmcs_scale_chroma);
+ intra->intra_cclm_pred = FUNC(intra_cclm_pred);
+ intra->intra_pred = FUNC(intra_pred);
+ intra->pred_planar = FUNC(pred_planar);
+ intra->pred_mip = FUNC(pred_mip);
+ intra->pred_dc = FUNC(pred_dc);
+ intra->pred_v = FUNC(pred_v);
+ intra->pred_h = FUNC(pred_h);
+ intra->pred_angular_v = FUNC(pred_angular_v);
+ intra->pred_angular_h = FUNC(pred_angular_h);
+}
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH v7 10/14] vvcdec: add LMCS, Deblocking, SAO, and ALF filters
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
` (7 preceding siblings ...)
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 09/14] vvcdec: add intra prediction Nuo Mi
@ 2023-12-10 15:58 ` Nuo Mi
8 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 15:58 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
---
libavcodec/vvc/Makefile | 1 +
libavcodec/vvc/vvc_ctu.h | 1 +
libavcodec/vvc/vvc_filter.c | 1348 ++++++++++++++++++++++++++
libavcodec/vvc/vvc_filter.h | 71 ++
libavcodec/vvc/vvc_filter_template.c | 1135 ++++++++++++++++++++++
5 files changed, 2556 insertions(+)
create mode 100644 libavcodec/vvc/vvc_filter.c
create mode 100644 libavcodec/vvc/vvc_filter.h
create mode 100644 libavcodec/vvc/vvc_filter_template.c
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index 3b1ac72029..9e7fef7d38 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -5,6 +5,7 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
vvc/vvc_cabac.o \
vvc/vvc_ctu.o \
vvc/vvc_data.o \
+ vvc/vvc_filter.o \
vvc/vvc_inter.o \
vvc/vvc_intra.o \
vvc/vvc_itx_1d.o \
diff --git a/libavcodec/vvc/vvc_ctu.h b/libavcodec/vvc/vvc_ctu.h
index 577136b2e2..e9b157795c 100644
--- a/libavcodec/vvc/vvc_ctu.h
+++ b/libavcodec/vvc/vvc_ctu.h
@@ -462,6 +462,7 @@ typedef struct ALFParams {
void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0, int w, int h);
void ff_vvc_decode_neighbour(VVCLocalContext *lc, int x_ctb, int y_ctb, int rx, int ry, int rs);
void ff_vvc_ctu_free_cus(CTU *ctu);
+int ff_vvc_get_qPy(const VVCFrameContext *fc, int xc, int yc);
void ff_vvc_ep_init_stat_coeff(EntryPoint *ep, int bit_depth, int persistent_rice_adaptation_enabled_flag);
#endif // AVCODEC_VVC_VVC_CTU_H
diff --git a/libavcodec/vvc/vvc_filter.c b/libavcodec/vvc/vvc_filter.c
new file mode 100644
index 0000000000..a0eca7b85f
--- /dev/null
+++ b/libavcodec/vvc/vvc_filter.c
@@ -0,0 +1,1348 @@
+/*
+ * VVC filters
+ *
+ * Copyright (C) 2021 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "libavutil/frame.h"
+
+#include "vvc_ctu.h"
+#include "vvc_data.h"
+#include "vvc_filter.h"
+#include "vvc_refs.h"
+
+#define LEFT 0
+#define TOP 1
+#define RIGHT 2
+#define BOTTOM 3
+#define MAX_EDGES 4
+
+#define DEFAULT_INTRA_TC_OFFSET 2
+
+//Table 43 Derivation of threshold variables beta' and tc' from input Q
+static const uint16_t tctable[66] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 3, 4, 4, 4, 4, 5, 5, 5, 5, 7, 7, 8, 9, 10,
+ 10, 11, 13, 14, 15, 17, 19, 21, 24, 25, 29, 33, 36, 41, 45, 51,
+ 57, 64, 71, 80, 89, 100, 112, 125, 141, 157, 177, 198, 222, 250, 280, 314,
+ 352, 395,
+
+};
+
+//Table 43 Derivation of threshold variables beta' and tc' from input Q
+static const uint8_t betatable[64] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 22, 24,
+ 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56,
+ 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88,
+};
+
+
+static int get_qPc(const VVCFrameContext *fc, const int x0, const int y0, const int chroma)
+{
+ const int x = x0 >> MIN_TU_LOG2;
+ const int y = y0 >> MIN_TU_LOG2;
+ const int min_tu_width = fc->ps.pps->min_tu_width;
+ return fc->tab.qp[chroma][x + y * min_tu_width];
+}
+
+static void copy_ctb(uint8_t *dst, const uint8_t *src, const int width, const int height,
+ const ptrdiff_t dst_stride, const ptrdiff_t src_stride)
+{
+ for (int y = 0; y < height; y++) {
+ memcpy(dst, src, width);
+
+ dst += dst_stride;
+ src += src_stride;
+ }
+}
+
+static void copy_pixel(uint8_t *dst, const uint8_t *src, const int pixel_shift)
+{
+ if (pixel_shift)
+ *(uint16_t *)dst = *(uint16_t *)src;
+ else
+ *dst = *src;
+}
+
+static void copy_vert(uint8_t *dst, const uint8_t *src, const int pixel_shift, const int height,
+ const ptrdiff_t dst_stride, const ptrdiff_t src_stride)
+{
+ if (pixel_shift == 0) {
+ for (int i = 0; i < height; i++) {
+ *dst = *src;
+ dst += dst_stride;
+ src += src_stride;
+ }
+ } else {
+ for (int i = 0; i < height; i++) {
+ *(uint16_t *)dst = *(uint16_t *)src;
+ dst += dst_stride;
+ src += src_stride;
+ }
+ }
+}
+
+static void copy_ctb_to_hv(VVCFrameContext *fc, const uint8_t *src,
+ const ptrdiff_t src_stride, const int x, const int y, const int width, const int height,
+ const int c_idx, const int x_ctb, const int y_ctb, const int top)
+{
+ int ps = fc->ps.sps->pixel_shift;
+ int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx];
+ int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx];
+
+ if (top) {
+ /* top */
+ memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb) * w + x) << ps),
+ src, width << ps);
+ } else {
+ /* bottom */
+ memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 1) * w + x) << ps),
+ src + src_stride * (height - 1), width << ps);
+
+ /* copy vertical edges */
+ copy_vert(fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb) * h + y) << ps), src, ps, height, 1 << ps, src_stride);
+ copy_vert(fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb + 1) * h + y) << ps), src + ((width - 1) << ps), ps, height, 1 << ps, src_stride);
+ }
+}
+
+static void sao_copy_ctb_to_hv(VVCLocalContext *lc, const int rx, const int ry, const int top)
+{
+ VVCFrameContext *fc = lc->fc;
+ const int ctb_size_y = fc->ps.sps->ctb_size_y;
+ const int x0 = rx << fc->ps.sps->ctb_log2_size_y;
+ const int y0 = ry << fc->ps.sps->ctb_log2_size_y;
+
+ for (int c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) {
+ const int x = x0 >> fc->ps.sps->hshift[c_idx];
+ const int y = y0 >> fc->ps.sps->vshift[c_idx];
+ const ptrdiff_t src_stride = fc->frame->linesize[c_idx];
+ const int ctb_size_h = ctb_size_y >> fc->ps.sps->hshift[c_idx];
+ const int ctb_size_v = ctb_size_y >> fc->ps.sps->vshift[c_idx];
+ const int width = FFMIN(ctb_size_h, (fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]) - x);
+ const int height = FFMIN(ctb_size_v, (fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]) - y);
+ uint8_t *src = &fc->frame->data[c_idx][y * src_stride + (x << fc->ps.sps->pixel_shift)];
+ copy_ctb_to_hv(fc, src, src_stride, x, y, width, height, c_idx, rx, ry, top);
+ }
+
+}
+
+void ff_vvc_sao_copy_ctb_to_hv(VVCLocalContext *lc, const int rx, const int ry, const int last_row)
+{
+ if (ry)
+ sao_copy_ctb_to_hv(lc, rx, ry - 1, 0);
+
+ sao_copy_ctb_to_hv(lc, rx, ry, 1);
+
+ if (last_row)
+ sao_copy_ctb_to_hv(lc, rx, ry, 0);
+}
+
+void ff_vvc_sao_filter(VVCLocalContext *lc, int x, int y)
+{
+ VVCFrameContext *fc = lc->fc;
+ const int ctb_size_y = fc->ps.sps->ctb_size_y;
+ static const uint8_t sao_tab[16] = { 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8 };
+ int c_idx;
+ int edges[4]; // 0 left 1 top 2 right 3 bottom
+ int x_ctb = x >> fc->ps.sps->ctb_log2_size_y;
+ int y_ctb = y >> fc->ps.sps->ctb_log2_size_y;
+ SAOParams *sao = &CTB(fc->tab.sao, x_ctb, y_ctb);
+ // flags indicating unfilterable edges
+ uint8_t vert_edge[] = { 0, 0 };
+ uint8_t horiz_edge[] = { 0, 0 };
+ uint8_t diag_edge[] = { 0, 0, 0, 0 };
+ uint8_t lfase = fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag;
+ uint8_t no_tile_filter = fc->ps.pps->r->num_tiles_in_pic > 1 &&
+ !fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag;
+ uint8_t restore = no_tile_filter || !lfase;
+ uint8_t left_tile_edge = 0;
+ uint8_t right_tile_edge = 0;
+ uint8_t up_tile_edge = 0;
+ uint8_t bottom_tile_edge = 0;
+
+ edges[LEFT] = x_ctb == 0;
+ edges[TOP] = y_ctb == 0;
+ edges[RIGHT] = x_ctb == fc->ps.pps->ctb_width - 1;
+ edges[BOTTOM] = y_ctb == fc->ps.pps->ctb_height - 1;
+
+ if (restore) {
+ if (!edges[LEFT]) {
+ left_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_col_bd[x_ctb] == x_ctb;
+ vert_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb)) || left_tile_edge;
+ }
+ if (!edges[RIGHT]) {
+ right_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_col_bd[x_ctb] != fc->ps.pps->ctb_to_col_bd[x_ctb + 1];
+ vert_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb)) || right_tile_edge;
+ }
+ if (!edges[TOP]) {
+ up_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_row_bd[y_ctb] == y_ctb;
+ horiz_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb - 1)) || up_tile_edge;
+ }
+ if (!edges[BOTTOM]) {
+ bottom_tile_edge = no_tile_filter && fc->ps.pps->ctb_to_row_bd[y_ctb] != fc->ps.pps->ctb_to_row_bd[y_ctb + 1];
+ horiz_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb + 1)) || bottom_tile_edge;
+ }
+ if (!edges[LEFT] && !edges[TOP]) {
+ diag_edge[0] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb - 1)) || left_tile_edge || up_tile_edge;
+ }
+ if (!edges[TOP] && !edges[RIGHT]) {
+ diag_edge[1] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb - 1)) || right_tile_edge || up_tile_edge;
+ }
+ if (!edges[RIGHT] && !edges[BOTTOM]) {
+ diag_edge[2] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb + 1)) || right_tile_edge || bottom_tile_edge;
+ }
+ if (!edges[LEFT] && !edges[BOTTOM]) {
+ diag_edge[3] = (!lfase && CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb - 1, y_ctb + 1)) || left_tile_edge || bottom_tile_edge;
+ }
+ }
+
+ for (c_idx = 0; c_idx < (fc->ps.sps->r->sps_chroma_format_idc ? 3 : 1); c_idx++) {
+ int x0 = x >> fc->ps.sps->hshift[c_idx];
+ int y0 = y >> fc->ps.sps->vshift[c_idx];
+ ptrdiff_t src_stride = fc->frame->linesize[c_idx];
+ int ctb_size_h = ctb_size_y >> fc->ps.sps->hshift[c_idx];
+ int ctb_size_v = ctb_size_y >> fc->ps.sps->vshift[c_idx];
+ int width = FFMIN(ctb_size_h, (fc->ps.sps->width >> fc->ps.sps->hshift[c_idx]) - x0);
+ int height = FFMIN(ctb_size_v, (fc->ps.sps->height >> fc->ps.sps->vshift[c_idx]) - y0);
+ int tab = sao_tab[(FFALIGN(width, 8) >> 3) - 1];
+ uint8_t *src = &fc->frame->data[c_idx][y0 * src_stride + (x0 << fc->ps.sps->pixel_shift)];
+ ptrdiff_t dst_stride;
+ uint8_t *dst;
+
+ switch (sao->type_idx[c_idx]) {
+ case SAO_BAND:
+ fc->vvcdsp.sao.band_filter[tab](src, src, src_stride, src_stride,
+ sao->offset_val[c_idx], sao->band_position[c_idx], width, height);
+ break;
+ case SAO_EDGE:
+ {
+ int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx];
+ int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx];
+ int sh = fc->ps.sps->pixel_shift;
+
+ dst_stride = 2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE;
+ dst = lc->sao_buffer + dst_stride + AV_INPUT_BUFFER_PADDING_SIZE;
+
+ if (!edges[TOP]) {
+ int left = 1 - edges[LEFT];
+ int right = 1 - edges[RIGHT];
+ const uint8_t *src1;
+ uint8_t *dst1;
+ int pos;
+
+ dst1 = dst - dst_stride - (left << sh);
+ src1 = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb - 1) * w + x0 - left) << sh);
+ pos = 0;
+ if (left) {
+ copy_pixel(dst1, src1, sh);
+ pos += (1 << sh);
+ }
+ memcpy(dst1 + pos, src1 + pos, width << sh);
+ if (right) {
+ pos += width << sh;
+ copy_pixel(dst1 + pos, src1 + pos, sh);
+ }
+ }
+ if (!edges[BOTTOM]) {
+ int left = 1 - edges[LEFT];
+ int right = 1 - edges[RIGHT];
+ const uint8_t *src1;
+ uint8_t *dst1;
+ int pos;
+
+ dst1 = dst + height * dst_stride - (left << sh);
+ src1 = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 2) * w + x0 - left) << sh);
+ pos = 0;
+ if (left) {
+ copy_pixel(dst1, src1, sh);
+ pos += (1 << sh);
+ }
+ memcpy(dst1 + pos, src1 + pos, width << sh);
+ if (right) {
+ pos += width << sh;
+ copy_pixel(dst1 + pos, src1 + pos, sh);
+ }
+ }
+ if (!edges[LEFT]) {
+ copy_vert(dst - (1 << sh),
+ fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb - 1) * h + y0) << sh),
+ sh, height, dst_stride, 1 << sh);
+ }
+ if (!edges[RIGHT]) {
+ copy_vert(dst + (width << sh),
+ fc->tab.sao_pixel_buffer_v[c_idx] + (((2 * x_ctb + 2) * h + y0) << sh),
+ sh, height, dst_stride, 1 << sh);
+ }
+
+ copy_ctb(dst, src, width << sh, height, dst_stride, src_stride);
+ fc->vvcdsp.sao.edge_filter[tab](src, dst, src_stride, sao->offset_val[c_idx],
+ sao->eo_class[c_idx], width, height);
+ fc->vvcdsp.sao.edge_restore[restore](src, dst,
+ src_stride, dst_stride,
+ sao,
+ edges, width,
+ height, c_idx,
+ vert_edge,
+ horiz_edge,
+ diag_edge);
+ break;
+ }
+ }
+ }
+}
+
+#define TAB_BS(t, x, y) (t)[((y) >> 2) * (fc->tab.bs_width) + ((x) >> 2)]
+#define TAB_MAX_LEN(t, x, y) (t)[((y) >> 2) * (fc->tab.bs_width) + ((x) >> 2)]
+
+//8 samples a time
+#define DEBLOCK_STEP 8
+#define LUMA_GRID 4
+#define CHROMA_GRID 8
+
+static int boundary_strength(const VVCLocalContext *lc, MvField *curr, MvField *neigh,
+ const RefPicList *neigh_rpl)
+{
+ RefPicList *rpl = lc->sc->rpl;
+ if (curr->pred_flag == PF_BI && neigh->pred_flag == PF_BI) {
+ // same L0 and L1
+ if (rpl[0].list[curr->ref_idx[0]] == neigh_rpl[0].list[neigh->ref_idx[0]] &&
+ rpl[0].list[curr->ref_idx[0]] == rpl[1].list[curr->ref_idx[1]] &&
+ neigh_rpl[0].list[neigh->ref_idx[0]] == neigh_rpl[1].list[neigh->ref_idx[1]]) {
+ if ((FFABS(neigh->mv[0].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[0].y) >= 8 ||
+ FFABS(neigh->mv[1].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[1].y) >= 8) &&
+ (FFABS(neigh->mv[1].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[0].y) >= 8 ||
+ FFABS(neigh->mv[0].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[1].y) >= 8))
+ return 1;
+ else
+ return 0;
+ } else if (neigh_rpl[0].list[neigh->ref_idx[0]] == rpl[0].list[curr->ref_idx[0]] &&
+ neigh_rpl[1].list[neigh->ref_idx[1]] == rpl[1].list[curr->ref_idx[1]]) {
+ if (FFABS(neigh->mv[0].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[0].y) >= 8 ||
+ FFABS(neigh->mv[1].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[1].y) >= 8)
+ return 1;
+ else
+ return 0;
+ } else if (neigh_rpl[1].list[neigh->ref_idx[1]] == rpl[0].list[curr->ref_idx[0]] &&
+ neigh_rpl[0].list[neigh->ref_idx[0]] == rpl[1].list[curr->ref_idx[1]]) {
+ if (FFABS(neigh->mv[1].x - curr->mv[0].x) >= 8 || FFABS(neigh->mv[1].y - curr->mv[0].y) >= 8 ||
+ FFABS(neigh->mv[0].x - curr->mv[1].x) >= 8 || FFABS(neigh->mv[0].y - curr->mv[1].y) >= 8)
+ return 1;
+ else
+ return 0;
+ } else {
+ return 1;
+ }
+ } else if ((curr->pred_flag != PF_BI) && (neigh->pred_flag != PF_BI)){ // 1 MV
+ Mv A, B;
+ int ref_A, ref_B;
+
+ if (curr->pred_flag & 1) {
+ A = curr->mv[0];
+ ref_A = rpl[0].list[curr->ref_idx[0]];
+ } else {
+ A = curr->mv[1];
+ ref_A = rpl[1].list[curr->ref_idx[1]];
+ }
+
+ if (neigh->pred_flag & 1) {
+ B = neigh->mv[0];
+ ref_B = neigh_rpl[0].list[neigh->ref_idx[0]];
+ } else {
+ B = neigh->mv[1];
+ ref_B = neigh_rpl[1].list[neigh->ref_idx[1]];
+ }
+
+ if (ref_A == ref_B) {
+ if (FFABS(A.x - B.x) >= 8 || FFABS(A.y - B.y) >= 8)
+ return 1;
+ else
+ return 0;
+ } else
+ return 1;
+ }
+
+ return 1;
+}
+
+//part of 8.8.3.3 Derivation process of transform block boundary
+static void derive_max_filter_length_luma(const VVCFrameContext *fc, const int qx, const int qy,
+ const int is_intra, const int has_subblock, const int vertical, uint8_t *max_len_p, uint8_t *max_len_q)
+{
+ const int px = vertical ? qx - 1 : qx;
+ const int py = !vertical ? qy - 1 : qy;
+ const uint8_t *tb_size = vertical ? fc->tab.tb_width[LUMA] : fc->tab.tb_height[LUMA];
+ const int size_p = tb_size[(py >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (px >> MIN_TU_LOG2)];
+ const int size_q = tb_size[(qy >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (qx >> MIN_TU_LOG2)];
+ const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y;
+ const int off_p = (py >> min_cb_log2) * fc->ps.pps->min_cb_width + (px >> min_cb_log2);
+ if (size_p <= 4 || size_q <= 4) {
+ *max_len_p = *max_len_q = 1;
+ } else {
+ *max_len_p = *max_len_q = 3;
+ if (size_p >= 32)
+ *max_len_p = 7;
+ if (size_q >= 32)
+ *max_len_q = 7;
+ }
+ if (has_subblock)
+ *max_len_q = FFMIN(5, *max_len_q);
+ if (fc->tab.msf[off_p] || fc->tab.iaf[off_p])
+ *max_len_p = FFMIN(5, *max_len_p);
+}
+
+static void deblock_subblock_bs_vertical(const VVCLocalContext *lc,
+ const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height)
+{
+ const VVCFrameContext *fc = lc->fc;
+ MvField *tab_mvf = fc->tab.mvf;
+ RefPicList *rpl = lc->sc->rpl;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const int log2_min_pu_size = MIN_PU_LOG2;
+ uint8_t max_len_p, max_len_q;
+ int bs, i, j;
+
+ // bs for TU internal vertical PU boundaries
+ for (j = 0; j < height; j += 4) {
+ int y_pu = (y0 + j) >> log2_min_pu_size;
+
+ for (i = 8 - ((x0 - cb_x) % 8); i < width; i += 8) {
+ int xp_pu = (x0 + i - 1) >> log2_min_pu_size;
+ int xq_pu = (x0 + i) >> log2_min_pu_size;
+ MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu];
+ MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu];
+ const int x = x0 + i;
+ const int y = y0 + j;
+
+ bs = boundary_strength(lc, curr, left, rpl);
+ TAB_BS(fc->tab.vertical_bs[LUMA], x, y) = bs;
+
+
+ max_len_p = max_len_q = 0;
+ if (i == 4 || i == width - 4)
+ max_len_p = max_len_q = 1;
+ else if (i == 8 || i == width - 8)
+ max_len_p = max_len_q = 2;
+ else
+ max_len_p = max_len_q = 3;
+
+ TAB_MAX_LEN(fc->tab.vertical_p, x, y) = max_len_p;
+ TAB_MAX_LEN(fc->tab.vertical_q, x, y) = max_len_q;
+ }
+ }
+}
+
+static void deblock_subblock_bs_horizontal(const VVCLocalContext *lc,
+ const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height)
+{
+ const VVCFrameContext *fc = lc->fc;
+ MvField* tab_mvf = fc->tab.mvf;
+ RefPicList* rpl = lc->sc->rpl;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const int log2_min_pu_size = MIN_PU_LOG2;
+ uint8_t max_len_p, max_len_q;
+ int bs, i, j;
+
+ // bs for TU internal horizontal PU boundaries
+ for (j = 8 - ((y0 - cb_y) % 8); j < height; j += 8) {
+ int yp_pu = (y0 + j - 1) >> log2_min_pu_size;
+ int yq_pu = (y0 + j) >> log2_min_pu_size;
+
+ for (i = 0; i < width; i += 4) {
+ int x_pu = (x0 + i) >> log2_min_pu_size;
+ MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu];
+ MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu];
+ const int x = x0 + i;
+ const int y = y0 + j;
+
+ bs = boundary_strength(lc, curr, top, rpl);
+ TAB_BS(fc->tab.horizontal_bs[LUMA], x, y) = bs;
+
+ //fixme:
+ //edgeTbFlags[ x − sbW ][ y ] is equal to 1
+ //edgeTbFlags[ x + sbW ][ y ] is equal to 1
+ max_len_p = max_len_q = 0;
+ if (j == 4 || j == height - 4)
+ max_len_p = max_len_q = 1;
+ else if (j == 8 || j == height - 8)
+ max_len_p = max_len_q = 2;
+ else
+ max_len_p = max_len_q = 3;
+ TAB_MAX_LEN(fc->tab.horizontal_p, x, y) = max_len_p;
+ TAB_MAX_LEN(fc->tab.horizontal_q, x, y) = max_len_q;
+ }
+ }
+
+}
+
+static void deblock_bs_luma_vertical(const VVCLocalContext *lc,
+ const int x0, const int y0, const int width, const int height)
+{
+ const VVCFrameContext *fc = lc->fc;
+ MvField *tab_mvf = fc->tab.mvf;
+ const int log2_min_pu_size = MIN_PU_LOG2;
+ const int log2_min_tu_size = MIN_TU_LOG2;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const int min_tu_width = fc->ps.pps->min_tu_width;
+ const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+ int is_intra = tab_mvf[(y0 >> log2_min_pu_size) * min_pu_width +
+ (x0 >> log2_min_pu_size)].pred_flag == PF_INTRA;
+ int boundary_left;
+ int has_vertical_sb = 0;
+ uint8_t max_len_p, max_len_q;
+
+ const int off_q = (y0 >> min_cb_log2) * min_cb_width + (x0 >> min_cb_log2);
+ const int cb_x = fc->tab.cb_pos_x[LUMA][off_q];
+ const int cb_y = fc->tab.cb_pos_y[LUMA][off_q];
+ const int cb_width = fc->tab.cb_width[LUMA][off_q];
+
+ if (!is_intra) {
+ if (fc->tab.msf[off_q] || fc->tab.iaf[off_q])
+ has_vertical_sb = cb_width > 8;
+ }
+
+ // bs for vertical TU boundaries
+ boundary_left = x0 > 0 && !(x0 & 3);
+ if (boundary_left &&
+ ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag &&
+ lc->boundary_flags & BOUNDARY_LEFT_SLICE &&
+ (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) ||
+ (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag &&
+ lc->boundary_flags & BOUNDARY_LEFT_TILE &&
+ (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0)))
+ boundary_left = 0;
+
+ if (boundary_left) {
+ const RefPicList *rpl_left =
+ (lc->boundary_flags & BOUNDARY_LEFT_SLICE) ? ff_vvc_get_ref_list(fc, fc->ref, x0 - 1, y0) : lc->sc->rpl;
+ int xp_pu = (x0 - 1) >> log2_min_pu_size;
+ int xq_pu = x0 >> log2_min_pu_size;
+ int xp_tu = (x0 - 1) >> log2_min_tu_size;
+ int xq_tu = x0 >> log2_min_tu_size;
+
+ for (int i = 0; i < height; i += 4) {
+ int bs;
+ const int off_x = cb_x - x0;
+ int y_pu = (y0 + i) >> log2_min_pu_size;
+ int y_tu = (y0 + i) >> log2_min_tu_size;
+ MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu];
+ MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu];
+ uint8_t left_cbf_luma = fc->tab.tu_coded_flag[LUMA][y_tu * min_tu_width + xp_tu];
+ uint8_t curr_cbf_luma = fc->tab.tu_coded_flag[LUMA][y_tu * min_tu_width + xq_tu];
+ uint8_t pcmf = fc->tab.pcmf[LUMA][y_tu * min_tu_width + xp_tu] &&
+ fc->tab.pcmf[LUMA][y_tu * min_tu_width + xq_tu];
+
+ if (pcmf)
+ bs = 0;
+ else if (curr->pred_flag == PF_INTRA || left->pred_flag == PF_INTRA || curr->ciip_flag || left->ciip_flag)
+ bs = 2;
+ else if (curr_cbf_luma || left_cbf_luma)
+ bs = 1;
+ else if (off_x && ((off_x % 8) || !has_vertical_sb))
+ bs = 0; ////inside a cu, not aligned to 8 or with no subblocks
+ else
+ bs = boundary_strength(lc, curr, left, rpl_left);
+
+ TAB_BS(fc->tab.vertical_bs[LUMA], x0, (y0 + i)) = bs;
+
+ derive_max_filter_length_luma(fc, x0, y0 + i, is_intra, has_vertical_sb, 1, &max_len_p, &max_len_q);
+ TAB_MAX_LEN(fc->tab.vertical_p, x0, y0 + i) = max_len_p;
+ TAB_MAX_LEN(fc->tab.vertical_q, x0, y0 + i) = max_len_q;
+ }
+ }
+
+ if (!is_intra) {
+ if (fc->tab.msf[off_q] || fc->tab.iaf[off_q])
+ deblock_subblock_bs_vertical(lc, cb_x, cb_y, x0, y0, width, height);
+ }
+
+}
+static void deblock_bs_luma_horizontal(const VVCLocalContext *lc,
+ const int x0, const int y0, const int width, const int height)
+{
+ const VVCFrameContext *fc = lc->fc;
+ MvField *tab_mvf = fc->tab.mvf;
+ const int log2_min_pu_size = MIN_PU_LOG2;
+ const int log2_min_tu_size = MIN_TU_LOG2;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const int min_tu_width = fc->ps.pps->min_tu_width;
+ const int min_cb_log2 = fc->ps.sps->min_cb_log2_size_y;
+ const int min_cb_width = fc->ps.pps->min_cb_width;
+ int is_intra = tab_mvf[(y0 >> log2_min_pu_size) * min_pu_width +
+ (x0 >> log2_min_pu_size)].pred_flag == PF_INTRA;
+ int boundary_upper;
+ int has_horizontal_sb = 0;
+ uint8_t max_len_p, max_len_q;
+
+ const int off_q = (y0 >> min_cb_log2) * min_cb_width + (x0 >> min_cb_log2);
+ const int cb_x = fc->tab.cb_pos_x[LUMA][off_q];
+ const int cb_y = fc->tab.cb_pos_y[LUMA][off_q];
+ const int cb_height = fc->tab.cb_height[LUMA][off_q];
+
+ if (!is_intra) {
+ if (fc->tab.msf[off_q] || fc->tab.iaf[off_q])
+ has_horizontal_sb = cb_height > 8;
+ }
+
+ boundary_upper = y0 > 0 && !(y0 & 3);
+ if (boundary_upper &&
+ ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag &&
+ lc->boundary_flags & BOUNDARY_UPPER_SLICE &&
+ (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) ||
+ (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag &&
+ lc->boundary_flags & BOUNDARY_UPPER_TILE &&
+ (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0)))
+ boundary_upper = 0;
+
+ if (boundary_upper) {
+ const RefPicList *rpl_top =
+ (lc->boundary_flags & BOUNDARY_UPPER_SLICE) ? ff_vvc_get_ref_list(fc, fc->ref, x0, y0 - 1) : lc->sc->rpl;
+ int yp_pu = (y0 - 1) >> log2_min_pu_size;
+ int yq_pu = y0 >> log2_min_pu_size;
+ int yp_tu = (y0 - 1) >> log2_min_tu_size;
+ int yq_tu = y0 >> log2_min_tu_size;
+
+ for (int i = 0; i < width; i += 4) {
+ int bs;
+ const int off_y = y0 - cb_y;
+ int x_pu = (x0 + i) >> log2_min_pu_size;
+ int x_tu = (x0 + i) >> log2_min_tu_size;
+ MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu];
+ MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu];
+ uint8_t top_cbf_luma = fc->tab.tu_coded_flag[LUMA][yp_tu * min_tu_width + x_tu];
+ uint8_t curr_cbf_luma = fc->tab.tu_coded_flag[LUMA][yq_tu * min_tu_width + x_tu];
+ const uint8_t pcmf = fc->tab.pcmf[LUMA][yp_tu * min_tu_width + x_tu] &&
+ fc->tab.pcmf[LUMA][yq_tu * min_tu_width + x_tu];
+
+ if (pcmf)
+ bs = 0;
+ else if (curr->pred_flag == PF_INTRA || top->pred_flag == PF_INTRA || curr->ciip_flag || top->ciip_flag)
+ bs = 2;
+ else if (curr_cbf_luma || top_cbf_luma)
+ bs = 1;
+ else if (off_y && ((off_y % 8) || !has_horizontal_sb))
+ bs = 0; //inside a cu, not aligned to 8 or with no subblocks
+ else
+ bs = boundary_strength(lc, curr, top, rpl_top);
+
+ TAB_BS(fc->tab.horizontal_bs[LUMA], x0 + i, y0) = bs;
+
+ derive_max_filter_length_luma(fc, x0 + i, y0, is_intra, has_horizontal_sb, 0, &max_len_p, &max_len_q);
+ TAB_MAX_LEN(fc->tab.horizontal_p, x0 + i, y0) = max_len_p;
+ TAB_MAX_LEN(fc->tab.horizontal_q, x0 + i, y0) = max_len_q;
+ }
+ }
+
+ if (!is_intra) {
+ if (fc->tab.msf[off_q] || fc->tab.iaf[off_q])
+ deblock_subblock_bs_horizontal(lc, cb_x, cb_y, x0, y0, width, height);
+ }
+}
+
+static void deblock_bs_chroma_vertical(const VVCLocalContext *lc,
+ const int x0, const int y0, const int width, const int height)
+{
+ const VVCFrameContext *fc = lc->fc;
+ MvField *tab_mvf = fc->tab.mvf;
+ const int log2_min_pu_size = MIN_PU_LOG2;
+ const int log2_min_tu_size = MIN_PU_LOG2;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const int min_tu_width = fc->ps.pps->min_tu_width;
+ int boundary_left, i;
+
+ // bs for vertical TU boundaries
+ boundary_left = x0 > 0 && !(x0 & ((CHROMA_GRID << fc->ps.sps->hshift[1]) - 1));
+ if (boundary_left &&
+ ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag &&
+ lc->boundary_flags & BOUNDARY_LEFT_SLICE &&
+ (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) ||
+ (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag &&
+ lc->boundary_flags & BOUNDARY_LEFT_TILE &&
+ (x0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0)))
+ boundary_left = 0;
+
+ if (boundary_left) {
+ int xp_pu = (x0 - 1) >> log2_min_pu_size;
+ int xq_pu = x0 >> log2_min_pu_size;
+ int xp_tu = (x0 - 1) >> log2_min_tu_size;
+ int xq_tu = x0 >> log2_min_tu_size;
+
+ for (i = 0; i < height; i += 2) {
+ int y_pu = (y0 + i) >> log2_min_pu_size;
+ int y_tu = (y0 + i) >> log2_min_tu_size;
+ MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu];
+ MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu];
+ const int left_tu = y_tu * min_tu_width + xp_tu;
+ const int curr_tu = y_tu * min_tu_width + xq_tu;
+ const uint8_t pcmf = fc->tab.pcmf[CHROMA][left_tu] && fc->tab.pcmf[CHROMA][curr_tu];
+
+ for (int c = CB; c <= CR; c++) {
+ uint8_t cbf = fc->tab.tu_coded_flag[c][left_tu] |
+ fc->tab.tu_coded_flag[c][curr_tu] |
+ fc->tab.tu_joint_cbcr_residual_flag[left_tu] |
+ fc->tab.tu_joint_cbcr_residual_flag[curr_tu];
+ int bs = 0;
+
+ if (pcmf)
+ bs = 0;
+ else if (curr->pred_flag == PF_INTRA || left->pred_flag == PF_INTRA || curr->ciip_flag || left->ciip_flag)
+ bs = 2;
+ else if (cbf)
+ bs = 1;
+ TAB_BS(fc->tab.vertical_bs[c], x0, (y0 + i)) = bs;
+ }
+ }
+ }
+}
+
+static void deblock_bs_chroma_horizontal(const VVCLocalContext *lc,
+ const int x0, const int y0, const int width, const int height)
+{
+ const VVCFrameContext *fc = lc->fc;
+ MvField *tab_mvf = fc->tab.mvf;
+ const int log2_min_pu_size = MIN_PU_LOG2;
+ const int log2_min_tu_size = MIN_PU_LOG2;
+ const int min_pu_width = fc->ps.pps->min_pu_width;
+ const int min_tu_width = fc->ps.pps->min_tu_width;
+ int boundary_upper;
+
+ boundary_upper = y0 > 0 && !(y0 & ((CHROMA_GRID << fc->ps.sps->vshift[1]) - 1));
+ if (boundary_upper &&
+ ((!fc->ps.pps->r->pps_loop_filter_across_slices_enabled_flag &&
+ lc->boundary_flags & BOUNDARY_UPPER_SLICE &&
+ (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0) ||
+ (!fc->ps.pps->r->pps_loop_filter_across_tiles_enabled_flag &&
+ lc->boundary_flags & BOUNDARY_UPPER_TILE &&
+ (y0 % (1 << fc->ps.sps->ctb_log2_size_y)) == 0)))
+ boundary_upper = 0;
+
+ if (boundary_upper) {
+ int yp_pu = (y0 - 1) >> log2_min_pu_size;
+ int yq_pu = y0 >> log2_min_pu_size;
+ int yp_tu = (y0 - 1) >> log2_min_tu_size;
+ int yq_tu = y0 >> log2_min_tu_size;
+
+ for (int i = 0; i < width; i += 2) {
+ int x_pu = (x0 + i) >> log2_min_pu_size;
+ int x_tu = (x0 + i) >> log2_min_tu_size;
+ MvField *top = &tab_mvf[yp_pu * min_pu_width + x_pu];
+ MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu];
+ const int top_tu = yp_tu * min_tu_width + x_tu;
+ const int curr_tu = yq_tu * min_tu_width + x_tu;
+ const uint8_t pcmf = fc->tab.pcmf[CHROMA][top_tu] && fc->tab.pcmf[CHROMA][curr_tu];
+
+ for (int c = CB; c <= CR; c++) {
+ uint8_t cbf = fc->tab.tu_coded_flag[c][top_tu] |
+ fc->tab.tu_coded_flag[c][curr_tu] |
+ fc->tab.tu_joint_cbcr_residual_flag[top_tu] |
+ fc->tab.tu_joint_cbcr_residual_flag[curr_tu];
+ int bs = 0;
+
+ if (pcmf)
+ bs = 0;
+ else if (curr->pred_flag == PF_INTRA || top->pred_flag == PF_INTRA || curr->ciip_flag || top->ciip_flag)
+ bs = 2;
+ else if (cbf)
+ bs = 1;
+ TAB_BS(fc->tab.horizontal_bs[c], x0 + i, y0) = bs;
+ }
+ }
+ }
+}
+
+typedef void (*deblock_bs_fn)(const VVCLocalContext *lc, const int x0, const int y0,
+ const int width, const int height);
+
+static void deblock_bs(const VVCLocalContext *lc, const int x0, const int y0, const int vertical)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const VVCPPS *pps = fc->ps.pps;
+ const int ctb_size = sps->ctb_size_y;
+ const int x_end = FFMIN(x0 + ctb_size, pps->width) >> MIN_TU_LOG2;
+ const int y_end = FFMIN(y0 + ctb_size, pps->height) >> MIN_TU_LOG2;
+ deblock_bs_fn deblock_bs[2][2] = {
+ { deblock_bs_luma_horizontal, deblock_bs_chroma_horizontal },
+ { deblock_bs_luma_vertical, deblock_bs_chroma_vertical }
+ };
+
+ for (int is_chroma = 0; is_chroma <= 1; is_chroma++) {
+ const int hs = sps->hshift[is_chroma];
+ const int vs = sps->vshift[is_chroma];
+ for (int y = y0 >> MIN_TU_LOG2; y < y_end; y++) {
+ for (int x = x0 >> MIN_TU_LOG2; x < x_end; x++) {
+ const int off = y * fc->ps.pps->min_tu_width + x;
+ if ((fc->tab.tb_pos_x0[is_chroma][off] >> MIN_TU_LOG2) == x && (fc->tab.tb_pos_y0[is_chroma][off] >> MIN_TU_LOG2) == y) {
+ deblock_bs[vertical][is_chroma](lc, x << MIN_TU_LOG2, y << MIN_TU_LOG2,
+ fc->tab.tb_width[is_chroma][off] << hs, fc->tab.tb_height[is_chroma][off] << vs);
+ }
+ }
+ }
+ }
+}
+
+//part of 8.8.3.3 Derivation process of transform block boundary
+static void max_filter_length_luma(const VVCFrameContext *fc, const int qx, const int qy,
+ const int vertical, uint8_t *max_len_p, uint8_t *max_len_q)
+{
+ const uint8_t *tab_len_p = vertical ? fc->tab.vertical_p : fc->tab.horizontal_p;
+ const uint8_t *tab_len_q = vertical ? fc->tab.vertical_q : fc->tab.horizontal_q;
+ *max_len_p = TAB_MAX_LEN(tab_len_p, qx, qy);
+ *max_len_q = TAB_MAX_LEN(tab_len_q, qx, qy);
+}
+
+//part of 8.8.3.3 Derivation process of transform block boundary
+static void max_filter_length_chroma(const VVCFrameContext *fc, const int qx, const int qy,
+ const int vertical, const int horizontal_ctu_edge, const int bs, uint8_t *max_len_p, uint8_t *max_len_q)
+{
+ const int px = vertical ? qx - 1 : qx;
+ const int py = !vertical ? qy - 1 : qy;
+ const uint8_t *tb_size = vertical ? fc->tab.tb_width[CHROMA] : fc->tab.tb_height[CHROMA];
+
+ const int size_p = tb_size[(py >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (px >> MIN_TU_LOG2)];
+ const int size_q = tb_size[(qy >> MIN_TU_LOG2) * fc->ps.pps->min_tu_width + (qx >> MIN_TU_LOG2)];
+ if (size_p >= 8 && size_q >= 8) {
+ *max_len_p = *max_len_q = 3;
+ if (horizontal_ctu_edge)
+ *max_len_p = 1;
+ } else {
+ //part of 8.8.3.6.4 Decision process for chroma block edges
+ *max_len_p = *max_len_q = (bs == 2);
+ }
+}
+
+static void max_filter_length(const VVCFrameContext *fc, const int qx, const int qy,
+ const int c_idx, const int vertical, const int horizontal_ctu_edge, const int bs, uint8_t *max_len_p, uint8_t *max_len_q)
+{
+ if (!c_idx)
+ max_filter_length_luma(fc, qx, qy, vertical, max_len_p, max_len_q);
+ else
+ max_filter_length_chroma(fc, qx, qy, vertical, horizontal_ctu_edge, bs, max_len_p, max_len_q);
+}
+
+#define TC_CALC(qp, bs) \
+ tctable[av_clip((qp) + DEFAULT_INTRA_TC_OFFSET * ((bs) - 1) + \
+ (tc_offset & -2), \
+ 0, MAX_QP + DEFAULT_INTRA_TC_OFFSET)]
+
+// part of 8.8.3.6.2 Decision process for luma block edges
+static int get_qp_y(const VVCFrameContext *fc, const uint8_t *src, const int x, const int y, const int vertical)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ const int qp = (ff_vvc_get_qPy(fc, x - vertical, y - !vertical) + ff_vvc_get_qPy(fc, x, y) + 1) >> 1;
+ int qp_offset = 0;
+ int level;
+
+ if (!sps->r->sps_ladf_enabled_flag)
+ return qp;
+
+ level = fc->vvcdsp.lf.ladf_level[vertical](src, fc->frame->linesize[LUMA]);
+ qp_offset = sps->r->sps_ladf_lowest_interval_qp_offset;
+ for (int i = 0; i < sps->num_ladf_intervals - 1 && level > sps->ladf_interval_lower_bound[i + 1]; i++)
+ qp_offset = sps->r->sps_ladf_qp_offset[i];
+
+ return qp + qp_offset;
+}
+
+// part of 8.8.3.6.2 Decision process for luma block edges
+static int get_qp_c(const VVCFrameContext *fc, const int x, const int y, const int c_idx, const int vertical)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ return (get_qPc(fc, x - vertical, y - !vertical, c_idx) + get_qPc(fc, x, y, c_idx) - 2 * sps->qp_bd_offset + 1) >> 1;
+}
+
+static int get_qp(const VVCFrameContext *fc, const uint8_t *src, const int x, const int y, const int c_idx, const int vertical)
+{
+ if (!c_idx)
+ return get_qp_y(fc, src, x, y, vertical);
+ return get_qp_c(fc, x, y, c_idx, vertical);
+}
+
+void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0)
+{
+ VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int c_end = sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1;
+ uint8_t *src;
+ int x, y, qp;
+
+ //not use this yet, may needed by plt.
+ const uint8_t no_p[4] = { 0 };
+ const uint8_t no_q[4] = { 0 } ;
+
+ int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y;
+ int x_end, y_end;
+ int ctb_size = 1 << ctb_log2_size_y;
+ int ctb = (x0 >> ctb_log2_size_y) +
+ (y0 >> ctb_log2_size_y) * fc->ps.pps->ctb_width;
+ DBParams *params = fc->tab.deblock + ctb;
+
+ deblock_bs(lc, x0, y0, 1);
+
+ x_end = x0 + ctb_size;
+ if (x_end > fc->ps.sps->width)
+ x_end = fc->ps.sps->width;
+ y_end = y0 + ctb_size;
+ if (y_end > fc->ps.sps->height)
+ y_end = fc->ps.sps->height;
+
+ for (int c_idx = 0; c_idx < c_end; c_idx++) {
+ const int hs = sps->hshift[c_idx];
+ const int vs = sps->vshift[c_idx];
+ const int grid = c_idx ? (CHROMA_GRID << hs) : LUMA_GRID;
+ const int tc_offset = params->tc_offset[c_idx];
+ const int beta_offset = params->beta_offset[c_idx];
+
+ for (y = y0; y < y_end; y += (DEBLOCK_STEP << vs)) {
+ for (x = x0 ? x0 : grid; x < x_end; x += grid) {
+ int32_t bs[4], beta[4], tc[4], all_zero_bs = 1;
+ uint8_t max_len_p[4], max_len_q[4];
+
+ for (int i = 0; i < DEBLOCK_STEP >> (2 - vs); i++) {
+ const int dy = i << 2;
+ bs[i] = (y + dy < y_end) ? TAB_BS(fc->tab.vertical_bs[c_idx], x, y + dy) : 0;
+ if (bs[i]) {
+ src = &fc->frame->data[c_idx][((y + dy) >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)];
+ qp = get_qp(fc, src, x, y + dy, c_idx, 1);
+
+ beta[i] = betatable[av_clip(qp + beta_offset, 0, MAX_QP)];
+
+ max_filter_length(fc, x, y + dy, c_idx, 1, 0, bs[i], &max_len_p[i], &max_len_q[i]);
+ all_zero_bs = 0;
+ }
+ tc[i] = bs[i] ? TC_CALC(qp, bs[i]) : 0;
+ }
+
+ if (!all_zero_bs) {
+ src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)];
+ if (!c_idx) {
+ fc->vvcdsp.lf.filter_luma[1](src, fc->frame->linesize[c_idx],
+ beta, tc, no_p, no_q, max_len_p, max_len_q, 0);
+ } else {
+ fc->vvcdsp.lf.filter_chroma[1](src, fc->frame->linesize[c_idx],
+ beta, tc, no_p, no_q, max_len_p, max_len_q, vs);
+ }
+ }
+ }
+ }
+ }
+}
+
+void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0)
+{
+ VVCFrameContext *fc = lc->fc;
+ const VVCSPS *sps = fc->ps.sps;
+ const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1;
+ uint8_t* src;
+ int x, y, qp;
+
+ //not use this yet, may needed by plt.
+ const uint8_t no_p[4] = { 0 };
+ const uint8_t no_q[4] = { 0 } ;
+
+ int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y;
+ int x_end, y_end;
+ int ctb_size = 1 << ctb_log2_size_y;
+ int ctb = (x0 >> ctb_log2_size_y) +
+ (y0 >> ctb_log2_size_y) * fc->ps.pps->ctb_width;
+
+ deblock_bs(lc, x0, y0, 0);
+
+ x_end = x0 + ctb_size;
+ if (x_end > fc->ps.sps->width)
+ x_end = fc->ps.sps->width;
+ y_end = y0 + ctb_size;
+ if (y_end > fc->ps.sps->height)
+ y_end = fc->ps.sps->height;
+
+ for (int c_idx = 0; c_idx < c_end; c_idx++) {
+ const int hs = sps->hshift[c_idx];
+ const int vs = sps->vshift[c_idx];
+ const int grid = c_idx ? (CHROMA_GRID << vs) : LUMA_GRID;
+
+ for (y = y0; y < y_end; y += grid) {
+ const uint8_t horizontal_ctu_edge = !(y % fc->ps.sps->ctb_size_y);
+ if (!y)
+ continue;
+
+ for (x = x0 ? x0: 0; x < x_end; x += (DEBLOCK_STEP << hs)) {
+ int32_t bs[4], beta[4], tc[4], all_zero_bs = 1;
+ uint8_t max_len_p[4], max_len_q[4];
+
+ for (int i = 0; i < DEBLOCK_STEP >> (2 - hs); i++) {
+ const int dx = i << 2;
+ const DBParams *params = fc->tab.deblock + ctb - (x + dx < x0);
+ const int beta_offset = params->beta_offset[c_idx];
+ const int tc_offset = params->tc_offset[c_idx];
+
+ bs[i] = (x + dx < x_end) ? TAB_BS(fc->tab.horizontal_bs[c_idx], x + dx, y) : 0;
+ if (bs[i]) {
+ src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + (((x + dx)>> hs) << fc->ps.sps->pixel_shift)];
+ qp = get_qp(fc, src, x + dx, y, c_idx, 0);
+
+ beta[i] = betatable[av_clip(qp + beta_offset, 0, MAX_QP)];
+
+ max_filter_length(fc, x + dx, y, c_idx, 0, horizontal_ctu_edge, bs[i], &max_len_p[i], &max_len_q[i]);
+ all_zero_bs = 0;
+ }
+ tc[i] = bs[i] ? TC_CALC(qp, bs[i]) : 0;
+ }
+ if (!all_zero_bs) {
+ src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)];
+ if (!c_idx) {
+ fc->vvcdsp.lf.filter_luma[0](src, fc->frame->linesize[c_idx],
+ beta, tc, no_p, no_q, max_len_p, max_len_q, horizontal_ctu_edge);
+ } else {
+ fc->vvcdsp.lf.filter_chroma[0](src, fc->frame->linesize[c_idx],
+ beta, tc, no_p, no_q, max_len_p, max_len_q, hs);
+ }
+ }
+ }
+ }
+ }
+}
+
+static void alf_copy_border(uint8_t *dst, const uint8_t *src,
+ const int pixel_shift, int width, const int height, const ptrdiff_t dst_stride, const ptrdiff_t src_stride)
+{
+ width <<= pixel_shift;
+ for (int i = 0; i < height; i++) {
+ memcpy(dst, src, width);
+ dst += dst_stride;
+ src += src_stride;
+ }
+}
+
+static void alf_extend_vert(uint8_t *_dst, const uint8_t *_src,
+ const int pixel_shift, const int width, const int height, ptrdiff_t stride)
+{
+ if (pixel_shift == 0) {
+ for (int i = 0; i < height; i++) {
+ memset(_dst, *_src, width);
+ _src += stride;
+ _dst += stride;
+ }
+ } else {
+ const uint16_t *src = (const uint16_t *)_src;
+ uint16_t *dst = (uint16_t *)_dst;
+ stride >>= pixel_shift;
+
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++)
+ dst[j] = *src;
+ src += stride;
+ dst += stride;
+ }
+ }
+}
+
+static void alf_extend_horz(uint8_t *dst, const uint8_t *src,
+ const int pixel_shift, int width, const int height, const ptrdiff_t stride)
+{
+ width <<= pixel_shift;
+ for (int i = 0; i < height; i++) {
+ memcpy(dst, src, width);
+ dst += stride;
+ }
+}
+
+static void alf_copy_ctb_to_hv(VVCFrameContext *fc, const uint8_t *src, const ptrdiff_t src_stride,
+ const int x, const int y, const int width, const int height, const int x_ctb, const int y_ctb, const int c_idx)
+{
+ const int ps = fc->ps.sps->pixel_shift;
+ const int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx];
+ const int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx];
+ const int border_pixels = (c_idx == 0) ? ALF_BORDER_LUMA : ALF_BORDER_CHROMA;
+ const int offset_h[] = { 0, height - border_pixels };
+ const int offset_v[] = { 0, width - border_pixels };
+
+ /* copy horizontal edges */
+ for (int i = 0; i < FF_ARRAY_ELEMS(offset_h); i++) {
+ alf_copy_border(fc->tab.alf_pixel_buffer_h[c_idx][i] + ((border_pixels * y_ctb * w + x)<< ps),
+ src + offset_h[i] * src_stride, ps, width, border_pixels, w << ps, src_stride);
+ }
+ /* copy vertical edges */
+ for (int i = 0; i < FF_ARRAY_ELEMS(offset_v); i++) {
+ alf_copy_border(fc->tab.alf_pixel_buffer_v[c_idx][i] + ((h * x_ctb + y) * (border_pixels << ps)),
+ src + (offset_v[i] << ps), ps, border_pixels, height, border_pixels << ps, src_stride);
+ }
+}
+
+static void alf_fill_border_h(uint8_t *dst, const ptrdiff_t dst_stride, const uint8_t *src, const ptrdiff_t src_stride,
+ const uint8_t *border, const int width, const int border_pixels, const int ps, const int edge)
+{
+ if (edge)
+ alf_extend_horz(dst, border, ps, width, border_pixels, dst_stride);
+ else
+ alf_copy_border(dst, src, ps, width, border_pixels, dst_stride, src_stride);
+}
+
+static void alf_fill_border_v(uint8_t *dst, const ptrdiff_t dst_stride, const uint8_t *src,
+ const uint8_t *border, const int border_pixels, const int height, const int pixel_shift, const int *edges, const int edge)
+{
+ const ptrdiff_t src_stride = (border_pixels << pixel_shift);
+
+ if (edge) {
+ alf_extend_vert(dst, border, pixel_shift, border_pixels, height + 2 * border_pixels, dst_stride);
+ return;
+ }
+
+ //left/right
+ alf_copy_border(dst + dst_stride * border_pixels * edges[TOP], src + src_stride * border_pixels * edges[TOP],
+ pixel_shift, border_pixels, height + (!edges[TOP] + !edges[BOTTOM]) * border_pixels, dst_stride, src_stride);
+
+ //top left/right
+ if (edges[TOP])
+ alf_extend_horz(dst, dst + dst_stride * border_pixels, pixel_shift, border_pixels, border_pixels, dst_stride);
+
+ //bottom left/right
+ if (edges[BOTTOM]) {
+ dst += dst_stride * (border_pixels + height);
+ alf_extend_horz(dst, dst - dst_stride, pixel_shift, border_pixels, border_pixels, dst_stride);
+ }
+}
+
+static void alf_prepare_buffer(VVCFrameContext *fc, uint8_t *_dst, const uint8_t *_src, const int x, const int y,
+ const int x_ctb, const int y_ctb, const int width, const int height, const ptrdiff_t dst_stride, const ptrdiff_t src_stride,
+ const int c_idx, const int *edges)
+{
+ const int ps = fc->ps.sps->pixel_shift;
+ const int w = fc->ps.sps->width >> fc->ps.sps->hshift[c_idx];
+ const int h = fc->ps.sps->height >> fc->ps.sps->vshift[c_idx];
+ const int border_pixels = c_idx == 0 ? ALF_BORDER_LUMA : ALF_BORDER_CHROMA;
+ uint8_t *dst, *src;
+
+ copy_ctb(_dst, _src, width << ps, height, dst_stride, src_stride);
+
+ //top
+ src = fc->tab.alf_pixel_buffer_h[c_idx][1] + (((border_pixels * (y_ctb - 1)) * w + x) << ps);
+ dst = _dst - border_pixels * dst_stride;
+ alf_fill_border_h(dst, dst_stride, src, w << ps, _dst, width, border_pixels, ps, edges[TOP]);
+
+ //bottom
+ src = fc->tab.alf_pixel_buffer_h[c_idx][0] + ((border_pixels * (y_ctb + 1) * w + x) << ps);
+ dst = _dst + height * dst_stride;
+ alf_fill_border_h(dst, dst_stride, src, w << ps, _dst + (height - 1) * dst_stride, width, border_pixels, ps, edges[BOTTOM]);
+
+
+ //left
+ src = fc->tab.alf_pixel_buffer_v[c_idx][1] + (h * (x_ctb - 1) + y - border_pixels) * (border_pixels << ps);
+ dst = _dst - (border_pixels << ps) - border_pixels * dst_stride;
+ alf_fill_border_v(dst, dst_stride, src, dst + (border_pixels << ps), border_pixels, height, ps, edges, edges[LEFT]);
+
+ //right
+ src = fc->tab.alf_pixel_buffer_v[c_idx][0] + (h * (x_ctb + 1) + y - border_pixels) * (border_pixels << ps);
+ dst = _dst + (width << ps) - border_pixels * dst_stride;
+ alf_fill_border_v(dst, dst_stride, src, dst - (1 << ps), border_pixels, height, ps, edges, edges[RIGHT]);
+}
+
+#define ALF_MAX_BLOCKS_IN_CTU (MAX_CTU_SIZE * MAX_CTU_SIZE / ALF_BLOCK_SIZE / ALF_BLOCK_SIZE)
+#define ALF_MAX_FILTER_SIZE (ALF_MAX_BLOCKS_IN_CTU * ALF_NUM_COEFF_LUMA)
+
+static void alf_get_coeff_and_clip(VVCLocalContext *lc, int16_t *coeff, int16_t *clip,
+ const uint8_t *src, ptrdiff_t src_stride, int width, int height, int vb_pos, ALFParams *alf)
+{
+ const VVCFrameContext *fc = lc->fc;
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ uint8_t fixed_clip_set[ALF_NUM_FILTERS_LUMA][ALF_NUM_COEFF_LUMA] = { { 0 } };
+ const int16_t *coeff_set;
+ const uint8_t *clip_idx_set;
+ const uint8_t *class_to_filt;
+ const int size = width * height / ALF_BLOCK_SIZE / ALF_BLOCK_SIZE;
+ int class_idx[ALF_MAX_BLOCKS_IN_CTU];
+ int transpose_idx[ALF_MAX_BLOCKS_IN_CTU];
+
+ if (alf->ctb_filt_set_idx_y < 16) {
+ coeff_set = &ff_vvc_alf_fix_filt_coeff[0][0];
+ clip_idx_set = &fixed_clip_set[0][0];
+ class_to_filt = ff_vvc_alf_class_to_filt_map[alf->ctb_filt_set_idx_y];
+ } else {
+ const int id = rsh->sh_alf_aps_id_luma[alf->ctb_filt_set_idx_y - 16];
+ const VVCALF *aps = fc->ps.alf_list[id];
+ coeff_set = &aps->luma_coeff[0][0];
+ clip_idx_set = &aps->luma_clip_idx[0][0];
+ class_to_filt = ff_vvc_alf_aps_class_to_filt_map;
+ }
+ fc->vvcdsp.alf.classify(class_idx, transpose_idx, src, src_stride, width, height,
+ vb_pos, lc->alf_gradient_tmp);
+ fc->vvcdsp.alf.recon_coeff_and_clip(coeff, clip, class_idx, transpose_idx, size,
+ coeff_set, clip_idx_set, class_to_filt);
+}
+
+static void alf_filter_luma(VVCLocalContext *lc, uint8_t *dst, const uint8_t *src,
+ const ptrdiff_t dst_stride, const ptrdiff_t src_stride, const int x0, const int y0,
+ const int width, const int height, const int _vb_pos, ALFParams *alf)
+{
+ const VVCFrameContext *fc = lc->fc;
+ int vb_pos = _vb_pos - y0;
+ int16_t *coeff = (int16_t*)lc->tmp;
+ int16_t *clip = (int16_t *)lc->tmp1;
+
+ av_assert0(ALF_MAX_FILTER_SIZE <= sizeof(lc->tmp));
+ av_assert0(ALF_MAX_FILTER_SIZE * sizeof(int16_t) <= sizeof(lc->tmp1));
+
+ alf_get_coeff_and_clip(lc, coeff, clip, src, src_stride, width, height, vb_pos, alf);
+ fc->vvcdsp.alf.filter[LUMA](dst, dst_stride, src, src_stride, width, height, coeff, clip, vb_pos);
+}
+
+static int alf_clip_from_idx(const VVCFrameContext *fc, const int idx)
+{
+ const VVCSPS *sps = fc->ps.sps;
+ const int offset[] = {0, 3, 5, 7};
+
+ return 1 << (sps->bit_depth - offset[idx]);
+}
+
+static void alf_filter_chroma(VVCLocalContext *lc, uint8_t *dst, const uint8_t *src,
+ const ptrdiff_t dst_stride, const ptrdiff_t src_stride, const int c_idx,
+ const int width, const int height, const int vb_pos, ALFParams *alf)
+{
+ VVCFrameContext *fc = lc->fc;
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const VVCALF *aps = fc->ps.alf_list[rsh->sh_alf_aps_id_chroma];
+ const int idx = alf->alf_ctb_filter_alt_idx[c_idx - 1];
+ const int16_t *coeff = aps->chroma_coeff[idx];
+ int16_t clip[ALF_NUM_COEFF_CHROMA];
+
+ for (int i = 0; i < ALF_NUM_COEFF_CHROMA; i++)
+ clip[i] = alf_clip_from_idx(fc, aps->chroma_clip_idx[idx][i]);
+
+ fc->vvcdsp.alf.filter[CHROMA](dst, dst_stride, src, src_stride, width, height, coeff, clip, vb_pos);
+}
+
+static void alf_filter_cc(VVCLocalContext *lc, uint8_t *dst, const uint8_t *luma,
+ const ptrdiff_t dst_stride, const ptrdiff_t luma_stride, const int c_idx,
+ const int width, const int height, const int hs, const int vs, const int vb_pos, ALFParams *alf)
+{
+ VVCFrameContext *fc = lc->fc;
+ const H266RawSliceHeader *rsh = lc->sc->sh.r;
+ const int idx = c_idx - 1;
+ const int cc_aps_id = c_idx == CB ? rsh->sh_alf_cc_cb_aps_id : rsh->sh_alf_cc_cr_aps_id;
+ const VVCALF *aps = fc->ps.alf_list[cc_aps_id];
+
+ if (aps) {
+ const int16_t *coeff = aps->cc_coeff[idx][alf->ctb_cc_idc[idx] - 1];
+
+ fc->vvcdsp.alf.filter_cc(dst, dst_stride, luma, luma_stride, width, height, hs, vs, coeff, vb_pos);
+ }
+}
+
+void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext* lc, const int x0, const int y0)
+{
+ VVCFrameContext *fc = lc->fc;
+ const int x_ctb = x0 >> fc->ps.sps->ctb_log2_size_y;
+ const int y_ctb = y0 >> fc->ps.sps->ctb_log2_size_y;
+ const int ctb_size_y = fc->ps.sps->ctb_size_y;
+ const int ps = fc->ps.sps->pixel_shift;
+ const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1;
+
+ for (int c_idx = 0; c_idx < c_end; c_idx++) {
+ const int hs = fc->ps.sps->hshift[c_idx];
+ const int vs = fc->ps.sps->vshift[c_idx];
+ const int x = x0 >> hs;
+ const int y = y0 >> vs;
+ const int width = FFMIN(fc->ps.sps->width - x0, ctb_size_y) >> hs;
+ const int height = FFMIN(fc->ps.sps->height - y0, ctb_size_y) >> vs;
+
+ const int src_stride = fc->frame->linesize[c_idx];
+ uint8_t* src = &fc->frame->data[c_idx][y * src_stride + (x << ps)];
+
+ alf_copy_ctb_to_hv(fc, src, src_stride, x, y, width, height, x_ctb, y_ctb, c_idx);
+ }
+}
+
+void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0)
+{
+ VVCFrameContext *fc = lc->fc;
+ const VVCPPS *pps = fc->ps.pps;
+ const int x_ctb = x0 >> fc->ps.sps->ctb_log2_size_y;
+ const int y_ctb = y0 >> fc->ps.sps->ctb_log2_size_y;
+ const int ctb_size_y = fc->ps.sps->ctb_size_y;
+ const int ps = fc->ps.sps->pixel_shift;
+ const int padded_stride = EDGE_EMU_BUFFER_STRIDE << ps;
+ const int padded_offset = padded_stride * ALF_PADDING_SIZE + (ALF_PADDING_SIZE << ps);
+ const int c_end = fc->ps.sps->r->sps_chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1;
+ ALFParams *alf = &CTB(fc->tab.alf, x_ctb, y_ctb);
+ int edges[MAX_EDGES] = { x_ctb == 0, y_ctb == 0, x_ctb == pps->ctb_width - 1, y_ctb == pps->ctb_height - 1 };
+
+ if (!pps->r->pps_loop_filter_across_tiles_enabled_flag) {
+ edges[LEFT] = edges[LEFT] || (lc->boundary_flags & BOUNDARY_LEFT_TILE);
+ edges[TOP] = edges[TOP] || (lc->boundary_flags & BOUNDARY_UPPER_TILE);
+ edges[RIGHT] = edges[RIGHT] || pps->ctb_to_col_bd[x_ctb] != pps->ctb_to_col_bd[x_ctb + 1];
+ edges[BOTTOM] = edges[BOTTOM] || pps->ctb_to_row_bd[y_ctb] != pps->ctb_to_row_bd[y_ctb + 1];
+ }
+
+ if (!pps->r->pps_loop_filter_across_slices_enabled_flag) {
+ edges[LEFT] = edges[LEFT] || (lc->boundary_flags & BOUNDARY_LEFT_SLICE);
+ edges[TOP] = edges[TOP] || (lc->boundary_flags & BOUNDARY_UPPER_SLICE);
+ edges[RIGHT] = edges[RIGHT] || CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb + 1, y_ctb);
+ edges[BOTTOM] = edges[BOTTOM] || CTB(fc->tab.slice_idx, x_ctb, y_ctb) != CTB(fc->tab.slice_idx, x_ctb, y_ctb + 1);
+ }
+
+ for (int c_idx = 0; c_idx < c_end; c_idx++) {
+ const int hs = fc->ps.sps->hshift[c_idx];
+ const int vs = fc->ps.sps->vshift[c_idx];
+ const int ctb_size_h = ctb_size_y >> hs;
+ const int ctb_size_v = ctb_size_y >> vs;
+ const int x = x0 >> hs;
+ const int y = y0 >> vs;
+ const int pic_width = fc->ps.sps->width >> hs;
+ const int pic_height = fc->ps.sps->height >> vs;
+ const int width = FFMIN(pic_width - x, ctb_size_h);
+ const int height = FFMIN(pic_height - y, ctb_size_v);
+ const int src_stride = fc->frame->linesize[c_idx];
+ uint8_t *src = &fc->frame->data[c_idx][y * src_stride + (x << ps)];
+ uint8_t *padded;
+
+ if (alf->ctb_flag[c_idx] || (!c_idx && (alf->ctb_cc_idc[0] || alf->ctb_cc_idc[1]))) {
+ padded = (c_idx ? lc->alf_buffer_chroma : lc->alf_buffer_luma) + padded_offset;
+ alf_prepare_buffer(fc, padded, src, x, y, x_ctb, y_ctb, width, height,
+ padded_stride, src_stride, c_idx, edges);
+ }
+ if (alf->ctb_flag[c_idx]) {
+ if (!c_idx) {
+ alf_filter_luma(lc, src, padded, src_stride, padded_stride, x, y,
+ width, height, y + ctb_size_v - ALF_VB_POS_ABOVE_LUMA, alf);
+ } else {
+ alf_filter_chroma(lc, src, padded, src_stride, padded_stride, c_idx,
+ width, height, ctb_size_v - ALF_VB_POS_ABOVE_CHROMA, alf);
+ }
+ }
+ if (c_idx && alf->ctb_cc_idc[c_idx - 1]) {
+ padded = lc->alf_buffer_luma + padded_offset;
+ alf_filter_cc(lc, src, padded, src_stride, padded_stride, c_idx,
+ width, height, hs, vs, (ctb_size_v << vs) - ALF_VB_POS_ABOVE_LUMA, alf);
+ }
+
+ alf->applied[c_idx] = 1;
+ }
+}
+
+
+void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x, const int y)
+{
+ const SliceContext *sc = lc->sc;
+ const VVCFrameContext *fc = lc->fc;
+ const int ctb_size = fc->ps.sps->ctb_size_y;
+ const int width = FFMIN(fc->ps.pps->width - x, ctb_size);
+ const int height = FFMIN(fc->ps.pps->height - y, ctb_size);
+ uint8_t *data = fc->frame->data[LUMA] + y * fc->frame->linesize[LUMA] + (x << fc->ps.sps->pixel_shift);
+ if (sc->sh.r->sh_lmcs_used_flag)
+ fc->vvcdsp.lmcs.filter(data, fc->frame->linesize[LUMA], width, height, fc->ps.lmcs.inv_lut);
+}
diff --git a/libavcodec/vvc/vvc_filter.h b/libavcodec/vvc/vvc_filter.h
new file mode 100644
index 0000000000..2ae4c33e2d
--- /dev/null
+++ b/libavcodec/vvc/vvc_filter.h
@@ -0,0 +1,71 @@
+/*
+ * VVC filters
+ *
+ * Copyright (C) 2022 Nuo Mi
+ *
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef AVCODEC_VVC_VVC_FILTER_H
+#define AVCODEC_VVC_VVC_FILTER_H
+
+#include "vvcdec.h"
+
+/**
+ * lmcs filter for the CTU
+ * @param lc local context for CTU
+ * @param x0 x position for the CTU
+ * @param y0 y position for the CTU
+ */
+void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x0, const int y0);
+
+/**
+ * vertical deblock filter for the CTU
+ * @param lc local context for CTU
+ * @param x0 x position for the CTU
+ * @param y0 y position for the CTU
+ */
+void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0);
+
+/**
+ * horizontal deblock filter for the CTU
+ * @param lc local context for CTU
+ * @param x0 x position for the CTU
+ * @param y0 y position for the CTU
+ */
+void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0);
+
+/**
+ * sao filter for the CTU
+ * @param lc local context for CTU
+ * @param x0 x position for the CTU
+ * @param y0 y position for the CTU
+ */
+void ff_vvc_sao_filter(VVCLocalContext *lc, const int x0, const int y0);
+
+void ff_vvc_sao_copy_ctb_to_hv(VVCLocalContext* lc, int rx, int ry, int last_row);
+void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext* lc, int x0, int y0);
+
+/**
+ * alf filter for the CTU
+ * @param lc local context for CTU
+ * @param x0 x position for the CTU
+ * @param y0 y position for the CTU
+ */
+void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0);
+
+#endif // AVCODEC_VVC_VVC_CTU_H
diff --git a/libavcodec/vvc/vvc_filter_template.c b/libavcodec/vvc/vvc_filter_template.c
new file mode 100644
index 0000000000..a4f1792ec4
--- /dev/null
+++ b/libavcodec/vvc/vvc_filter_template.c
@@ -0,0 +1,1135 @@
+/*
+ * VVC filters DSP
+ *
+ * Copyright (C) 2022 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+static void FUNC(lmcs_filter_luma)(uint8_t *_dst, ptrdiff_t dst_stride, const int width, const int height, const uint8_t *_lut)
+{
+ const pixel *lut = (const pixel *)_lut;
+ pixel *dst = (pixel*)_dst;
+ dst_stride /= sizeof(pixel);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++)
+ dst[x] = lut[dst[x]];
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(sao_band_filter)(uint8_t *_dst, const uint8_t *_src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+ const int16_t *sao_offset_val, const int sao_left_class, const int width, const int height)
+{
+ pixel *dst = (pixel *)_dst;
+ const pixel *src = (pixel *)_src;
+ int offset_table[32] = { 0 };
+ int k, y, x;
+ int shift = BIT_DEPTH - 5;
+
+ dst_stride /= sizeof(pixel);
+ src_stride /= sizeof(pixel);
+
+ for (k = 0; k < 4; k++)
+ offset_table[(k + sao_left_class) & 31] = sao_offset_val[k + 1];
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++)
+ dst[x] = av_clip_pixel(src[x] + offset_table[src[x] >> shift]);
+ dst += dst_stride;
+ src += src_stride;
+ }
+}
+
+#define CMP(a, b) (((a) > (b)) - ((a) < (b)))
+
+static void FUNC(sao_edge_filter)(uint8_t *_dst, const uint8_t *_src, ptrdiff_t dst_stride,
+ const int16_t *sao_offset_val, const int eo, const int width, const int height)
+{
+ static const uint8_t edge_idx[] = { 1, 2, 0, 3, 4 };
+ static const int8_t pos[4][2][2] = {
+ { { -1, 0 }, { 1, 0 } }, // horizontal
+ { { 0, -1 }, { 0, 1 } }, // vertical
+ { { -1, -1 }, { 1, 1 } }, // 45 degree
+ { { 1, -1 }, { -1, 1 } }, // 135 degree
+ };
+ pixel *dst = (pixel *)_dst;
+ const pixel *src = (pixel *)_src;
+ int a_stride, b_stride;
+ int x, y;
+ ptrdiff_t src_stride = (2 * MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE) / sizeof(pixel);
+ dst_stride /= sizeof(pixel);
+
+ a_stride = pos[eo][0][0] + pos[eo][0][1] * src_stride;
+ b_stride = pos[eo][1][0] + pos[eo][1][1] * src_stride;
+ for (y = 0; y < height; y++) {
+ for (x = 0; x < width; x++) {
+ int diff0 = CMP(src[x], src[x + a_stride]);
+ int diff1 = CMP(src[x], src[x + b_stride]);
+ int offset_val = edge_idx[2 + diff0 + diff1];
+ dst[x] = av_clip_pixel(src[x] + sao_offset_val[offset_val]);
+ }
+ src += src_stride;
+ dst += dst_stride;
+ }
+}
+
+static void FUNC(sao_edge_restore_0)(uint8_t *_dst, const uint8_t *_src,
+ ptrdiff_t dst_stride, ptrdiff_t src_stride, const SAOParams *sao,
+ const int *borders, const int _width, const int _height, const int c_idx,
+ const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge)
+{
+ int x, y;
+ pixel *dst = (pixel *)_dst;
+ const pixel *src = (pixel *)_src;
+ const int16_t *sao_offset_val = sao->offset_val[c_idx];
+ const int sao_eo_class = sao->eo_class[c_idx];
+ int init_x = 0, width = _width, height = _height;
+
+ dst_stride /= sizeof(pixel);
+ src_stride /= sizeof(pixel);
+
+ if (sao_eo_class != SAO_EO_VERT) {
+ if (borders[0]) {
+ int offset_val = sao_offset_val[0];
+ for (y = 0; y < height; y++) {
+ dst[y * dst_stride] = av_clip_pixel(src[y * src_stride] + offset_val);
+ }
+ init_x = 1;
+ }
+ if (borders[2]) {
+ int offset_val = sao_offset_val[0];
+ int offset = width - 1;
+ for (x = 0; x < height; x++) {
+ dst[x * dst_stride + offset] = av_clip_pixel(src[x * src_stride + offset] + offset_val);
+ }
+ width--;
+ }
+ }
+ if (sao_eo_class != SAO_EO_HORIZ) {
+ if (borders[1]) {
+ int offset_val = sao_offset_val[0];
+ for (x = init_x; x < width; x++)
+ dst[x] = av_clip_pixel(src[x] + offset_val);
+ }
+ if (borders[3]) {
+ int offset_val = sao_offset_val[0];
+ ptrdiff_t y_dst_stride = dst_stride * (height - 1);
+ ptrdiff_t y_src_stride = src_stride * (height - 1);
+ for (x = init_x; x < width; x++)
+ dst[x + y_dst_stride] = av_clip_pixel(src[x + y_src_stride] + offset_val);
+ height--;
+ }
+ }
+}
+
+static void FUNC(sao_edge_restore_1)(uint8_t *_dst, const uint8_t *_src,
+ ptrdiff_t dst_stride, ptrdiff_t src_stride, const SAOParams *sao,
+ const int *borders, const int _width, const int _height, const int c_idx,
+ const uint8_t *vert_edge, const uint8_t *horiz_edge, const uint8_t *diag_edge)
+{
+ int x, y;
+ pixel *dst = (pixel *)_dst;
+ const pixel *src = (pixel *)_src;
+ const int16_t *sao_offset_val = sao->offset_val[c_idx];
+ const int sao_eo_class = sao->eo_class[c_idx];
+ int init_x = 0, init_y = 0, width = _width, height = _height;
+
+ dst_stride /= sizeof(pixel);
+ src_stride /= sizeof(pixel);
+
+ if (sao_eo_class != SAO_EO_VERT) {
+ if (borders[0]) {
+ int offset_val = sao_offset_val[0];
+ for (y = 0; y < height; y++) {
+ dst[y * dst_stride] = av_clip_pixel(src[y * src_stride] + offset_val);
+ }
+ init_x = 1;
+ }
+ if (borders[2]) {
+ int offset_val = sao_offset_val[0];
+ int offset = width - 1;
+ for (x = 0; x < height; x++) {
+ dst[x * dst_stride + offset] = av_clip_pixel(src[x * src_stride + offset] + offset_val);
+ }
+ width--;
+ }
+ }
+ if (sao_eo_class != SAO_EO_HORIZ) {
+ if (borders[1]) {
+ int offset_val = sao_offset_val[0];
+ for (x = init_x; x < width; x++)
+ dst[x] = av_clip_pixel(src[x] + offset_val);
+ init_y = 1;
+ }
+ if (borders[3]) {
+ int offset_val = sao_offset_val[0];
+ ptrdiff_t y_dst_stride = dst_stride * (height - 1);
+ ptrdiff_t y_src_stride = src_stride * (height - 1);
+ for (x = init_x; x < width; x++)
+ dst[x + y_dst_stride] = av_clip_pixel(src[x + y_src_stride] + offset_val);
+ height--;
+ }
+ }
+
+ {
+ int save_upper_left = !diag_edge[0] && sao_eo_class == SAO_EO_135D && !borders[0] && !borders[1];
+ int save_upper_right = !diag_edge[1] && sao_eo_class == SAO_EO_45D && !borders[1] && !borders[2];
+ int save_lower_right = !diag_edge[2] && sao_eo_class == SAO_EO_135D && !borders[2] && !borders[3];
+ int save_lower_left = !diag_edge[3] && sao_eo_class == SAO_EO_45D && !borders[0] && !borders[3];
+
+ // Restore pixels that can't be modified
+ if (vert_edge[0] && sao_eo_class != SAO_EO_VERT) {
+ for (y = init_y + save_upper_left; y < height - save_lower_left; y++)
+ dst[y * dst_stride] = src[y * src_stride];
+ }
+ if (vert_edge[1] && sao_eo_class != SAO_EO_VERT) {
+ for (y = init_y + save_upper_right; y < height - save_lower_right; y++)
+ dst[y * dst_stride + width - 1] = src[y * src_stride + width - 1];
+ }
+
+ if (horiz_edge[0] && sao_eo_class != SAO_EO_HORIZ) {
+ for (x = init_x + save_upper_left; x < width - save_upper_right; x++)
+ dst[x] = src[x];
+ }
+ if (horiz_edge[1] && sao_eo_class != SAO_EO_HORIZ) {
+ for (x = init_x + save_lower_left; x < width - save_lower_right; x++)
+ dst[(height - 1) * dst_stride + x] = src[(height - 1) * src_stride + x];
+ }
+ if (diag_edge[0] && sao_eo_class == SAO_EO_135D)
+ dst[0] = src[0];
+ if (diag_edge[1] && sao_eo_class == SAO_EO_45D)
+ dst[width - 1] = src[width - 1];
+ if (diag_edge[2] && sao_eo_class == SAO_EO_135D)
+ dst[dst_stride * (height - 1) + width - 1] = src[src_stride * (height - 1) + width - 1];
+ if (diag_edge[3] && sao_eo_class == SAO_EO_45D)
+ dst[dst_stride * (height - 1)] = src[src_stride * (height - 1)];
+
+ }
+}
+
+#undef CMP
+
+static av_always_inline int16_t FUNC(alf_clip)(pixel curr, pixel v0, pixel v1, int16_t clip)
+{
+ return av_clip(v0 - curr, -clip, clip) + av_clip(v1 - curr, -clip, clip);
+}
+
+static void FUNC(alf_filter_luma)(uint8_t *_dst, ptrdiff_t dst_stride, const uint8_t *_src, ptrdiff_t src_stride,
+ const int width, const int height, const int16_t *filter, const int16_t *clip, const int vb_pos)
+{
+ const pixel *src = (pixel *)_src;
+ const int shift = 7;
+ const int offset = 1 << ( shift - 1 );
+ const int vb_above = vb_pos - 4;
+ const int vb_below = vb_pos + 3;
+
+ dst_stride /= sizeof(pixel);
+ src_stride /= sizeof(pixel);
+
+ for (int y = 0; y < height; y += ALF_BLOCK_SIZE) {
+ for (int x = 0; x < width; x += ALF_BLOCK_SIZE) {
+ const pixel *s0 = src + y * src_stride + x;
+ const pixel *s1 = s0 + src_stride;
+ const pixel *s2 = s0 - src_stride;
+ const pixel *s3 = s1 + src_stride;
+ const pixel *s4 = s2 - src_stride;
+ const pixel *s5 = s3 + src_stride;
+ const pixel *s6 = s4 - src_stride;
+
+ for (int i = 0; i < ALF_BLOCK_SIZE; i++) {
+ pixel *dst = (pixel *)_dst + (y + i) * dst_stride + x;
+
+ const pixel *p0 = s0 + i * src_stride;
+ const pixel *p1 = s1 + i * src_stride;
+ const pixel *p2 = s2 + i * src_stride;
+ const pixel *p3 = s3 + i * src_stride;
+ const pixel *p4 = s4 + i * src_stride;
+ const pixel *p5 = s5 + i * src_stride;
+ const pixel *p6 = s6 + i * src_stride;
+
+ const int is_near_vb_above = (y + i < vb_pos) && (y + i >= vb_pos - 1);
+ const int is_near_vb_below = (y + i >= vb_pos) && (y + i <= vb_pos);
+ const int is_near_vb = is_near_vb_above || is_near_vb_below;
+
+ if ((y + i < vb_pos) && ((y + i) >= vb_above)) {
+ p1 = (y + i == vb_pos - 1) ? p0 : p1;
+ p3 = (y + i >= vb_pos - 2) ? p1 : p3;
+ p5 = (y + i >= vb_pos - 3) ? p3 : p5;
+
+ p2 = (y + i == vb_pos - 1) ? p0 : p2;
+ p4 = (y + i >= vb_pos - 2) ? p2 : p4;
+ p6 = (y + i >= vb_pos - 3) ? p4 : p6;
+ } else if ((y + i >= vb_pos) && ((y + i) <= vb_below)) {
+ p2 = (y + i == vb_pos ) ? p0 : p2;
+ p4 = (y + i <= vb_pos + 1) ? p2 : p4;
+ p6 = (y + i <= vb_pos + 2) ? p4 : p6;
+
+ p1 = (y + i == vb_pos ) ? p0 : p1;
+ p3 = (y + i <= vb_pos + 1) ? p1 : p3;
+ p5 = (y + i <= vb_pos + 2) ? p3 : p5;
+ }
+
+ for (int j = 0; j < ALF_BLOCK_SIZE; j++) {
+ int sum = 0;
+ const pixel curr = *p0;
+
+ sum += filter[0] * FUNC(alf_clip)(curr, p5[+0], p6[+0], clip[0]);
+ sum += filter[1] * FUNC(alf_clip)(curr, p3[+1], p4[-1], clip[1]);
+ sum += filter[2] * FUNC(alf_clip)(curr, p3[+0], p4[+0], clip[2]);
+ sum += filter[3] * FUNC(alf_clip)(curr, p3[-1], p4[+1], clip[3]);
+ sum += filter[4] * FUNC(alf_clip)(curr, p1[+2], p2[-2], clip[4]);
+ sum += filter[5] * FUNC(alf_clip)(curr, p1[+1], p2[-1], clip[5]);
+ sum += filter[6] * FUNC(alf_clip)(curr, p1[+0], p2[+0], clip[6]);
+ sum += filter[7] * FUNC(alf_clip)(curr, p1[-1], p2[+1], clip[7]);
+ sum += filter[8] * FUNC(alf_clip)(curr, p1[-2], p2[+2], clip[8]);
+ sum += filter[9] * FUNC(alf_clip)(curr, p0[+3], p0[-3], clip[9]);
+ sum += filter[10] * FUNC(alf_clip)(curr, p0[+2], p0[-2], clip[10]);
+ sum += filter[11] * FUNC(alf_clip)(curr, p0[+1], p0[-1], clip[11]);
+
+ if (!is_near_vb)
+ sum = (sum + offset) >> shift;
+ else
+ sum = (sum + (1 << ((shift + 3) - 1))) >> (shift + 3);
+ sum += curr;
+ dst[j] = CLIP(sum);
+
+ p0++;
+ p1++;
+ p2++;
+ p3++;
+ p4++;
+ p5++;
+ p6++;
+ }
+ }
+ filter += ALF_NUM_COEFF_LUMA;
+ clip += ALF_NUM_COEFF_LUMA;
+ }
+ }
+}
+
+static void FUNC(alf_filter_chroma)(uint8_t* _dst, ptrdiff_t dst_stride, const uint8_t* _src, ptrdiff_t src_stride,
+ const int width, const int height, const int16_t* filter, const int16_t* clip, const int vb_pos)
+{
+ const pixel *src = (pixel *)_src;
+ const int shift = 7;
+ const int offset = 1 << ( shift - 1 );
+ const int vb_above = vb_pos - 2;
+ const int vb_below = vb_pos + 1;
+
+ dst_stride /= sizeof(pixel);
+ src_stride /= sizeof(pixel);
+
+ for (int y = 0; y < height; y += ALF_BLOCK_SIZE) {
+ for (int x = 0; x < width; x += ALF_BLOCK_SIZE) {
+ const pixel *s0 = src + y * src_stride + x;
+ const pixel *s1 = s0 + src_stride;
+ const pixel *s2 = s0 - src_stride;
+ const pixel *s3 = s1 + src_stride;
+ const pixel *s4 = s2 - src_stride;
+ const pixel *s5 = s3 + src_stride;
+ const pixel *s6 = s4 - src_stride;
+
+ for (int i = 0; i < ALF_BLOCK_SIZE; i++) {
+ pixel *dst = (pixel *)_dst + (y + i) * dst_stride + x;
+
+ const pixel *p0 = s0 + i * src_stride;
+ const pixel *p1 = s1 + i * src_stride;
+ const pixel *p2 = s2 + i * src_stride;
+ const pixel *p3 = s3 + i * src_stride;
+ const pixel *p4 = s4 + i * src_stride;
+ const pixel *p5 = s5 + i * src_stride;
+ const pixel *p6 = s6 + i * src_stride;
+
+ const int is_near_vb_above = (y + i < vb_pos) && (y + i >= vb_pos - 1);
+ const int is_near_vb_below = (y + i >= vb_pos) && (y + i <= vb_pos);
+ const int is_near_vb = is_near_vb_above || is_near_vb_below;
+
+ if ((y + i < vb_pos) && ((y + i) >= vb_above)) {
+ p1 = (y + i == vb_pos - 1) ? p0 : p1;
+ p3 = (y + i >= vb_pos - 2) ? p1 : p3;
+ p5 = (y + i >= vb_pos - 3) ? p3 : p5;
+
+ p2 = (y + i == vb_pos - 1) ? p0 : p2;
+ p4 = (y + i >= vb_pos - 2) ? p2 : p4;
+ p6 = (y + i >= vb_pos - 3) ? p4 : p6;
+ } else if ((y + i >= vb_pos) && ((y + i) <= vb_below)) {
+ p2 = (y + i == vb_pos ) ? p0 : p2;
+ p4 = (y + i <= vb_pos + 1) ? p2 : p4;
+ p6 = (y + i <= vb_pos + 2) ? p4 : p6;
+
+ p1 = (y + i == vb_pos ) ? p0 : p1;
+ p3 = (y + i <= vb_pos + 1) ? p1 : p3;
+ p5 = (y + i <= vb_pos + 2) ? p3 : p5;
+ }
+
+ for (int j = 0; j < ALF_BLOCK_SIZE; j++) {
+ int sum = 0;
+ const pixel curr = *p0;
+
+ sum += filter[0] * FUNC(alf_clip)(curr, p3[+0], p4[+0], clip[0]);
+ sum += filter[1] * FUNC(alf_clip)(curr, p1[+1], p2[-1], clip[1]);
+ sum += filter[2] * FUNC(alf_clip)(curr, p1[+0], p2[+0], clip[2]);
+ sum += filter[3] * FUNC(alf_clip)(curr, p1[-1], p2[+1], clip[3]);
+ sum += filter[4] * FUNC(alf_clip)(curr, p0[+2], p0[-2], clip[4]);
+ sum += filter[5] * FUNC(alf_clip)(curr, p0[+1], p0[-1], clip[5]);
+
+ if (!is_near_vb)
+ sum = (sum + offset) >> shift;
+ else
+ sum = (sum + (1 << ((shift + 3) - 1))) >> (shift + 3);
+ sum += curr;
+ dst[j] = CLIP(sum);
+
+ p0++;
+ p1++;
+ p2++;
+ p3++;
+ p4++;
+ p5++;
+ p6++;
+ }
+ }
+ }
+ }
+}
+
+static void FUNC(alf_filter_cc)(uint8_t *_dst, ptrdiff_t dst_stride, const uint8_t *_luma, const ptrdiff_t luma_stride,
+ const int width, const int height, const int hs, const int vs, const int16_t *filter, const int vb_pos)
+{
+ const ptrdiff_t stride = luma_stride / sizeof(pixel);
+
+ dst_stride /= sizeof(pixel);
+
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int sum = 0;
+ pixel *dst = (pixel *)_dst + y * dst_stride + x;
+ const pixel *src = (pixel *)_luma + (y << vs) * stride + (x << hs);
+
+ const pixel *s0 = src - stride;
+ const pixel *s1 = src;
+ const pixel *s2 = src + stride;
+ const pixel *s3 = src + 2 * stride;
+
+ const int pos = y << vs;
+ if (!vs && (pos == vb_pos || pos == vb_pos + 1))
+ continue;
+
+ if (pos == (vb_pos - 2) || pos == (vb_pos + 1))
+ s3 = s2;
+ else if (pos == (vb_pos - 1) || pos == vb_pos)
+ s3 = s2 = s0 = s1;
+
+
+ sum += filter[0] * (*s0 - *src);
+ sum += filter[1] * (*(s1 - 1) - *src);
+ sum += filter[2] * (*(s1 + 1) - *src);
+ sum += filter[3] * (*(s2 - 1) - *src);
+ sum += filter[4] * (*s2 - *src);
+ sum += filter[5] * (*(s2 + 1) - *src);
+ sum += filter[6] * (*s3 - *src);
+ sum = av_clip((sum + 64) >> 7, -(1 << (BIT_DEPTH - 1)), (1 << (BIT_DEPTH - 1)) - 1);
+ sum += *dst;
+ *dst = av_clip_pixel(sum);
+ }
+ }
+}
+
+#define ALF_DIR_VERT 0
+#define ALF_DIR_HORZ 1
+#define ALF_DIR_DIGA0 2
+#define ALF_DIR_DIGA1 3
+
+static void FUNC(alf_get_idx)(int *class_idx, int *transpose_idx, const int *sum, const int ac)
+{
+ static const int arg_var[] = {0, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4 };
+
+ int hv0, hv1, dir_hv, d0, d1, dir_d, hvd1, hvd0, sum_hv, dir1;
+
+ dir_hv = sum[ALF_DIR_VERT] <= sum[ALF_DIR_HORZ];
+ hv1 = FFMAX(sum[ALF_DIR_VERT], sum[ALF_DIR_HORZ]);
+ hv0 = FFMIN(sum[ALF_DIR_VERT], sum[ALF_DIR_HORZ]);
+
+ dir_d = sum[ALF_DIR_DIGA0] <= sum[ALF_DIR_DIGA1];
+ d1 = FFMAX(sum[ALF_DIR_DIGA0], sum[ALF_DIR_DIGA1]);
+ d0 = FFMIN(sum[ALF_DIR_DIGA0], sum[ALF_DIR_DIGA1]);
+
+ //promote to avoid overflow
+ dir1 = (uint64_t)d1 * hv0 <= (uint64_t)hv1 * d0;
+ hvd1 = dir1 ? hv1 : d1;
+ hvd0 = dir1 ? hv0 : d0;
+
+ sum_hv = sum[ALF_DIR_HORZ] + sum[ALF_DIR_VERT];
+ *class_idx = arg_var[av_clip_uintp2(sum_hv * ac >> (BIT_DEPTH - 1), 4)];
+ if (hvd1 * 2 > 9 * hvd0)
+ *class_idx += ((dir1 << 1) + 2) * 5;
+ else if (hvd1 > 2 * hvd0)
+ *class_idx += ((dir1 << 1) + 1) * 5;
+
+ *transpose_idx = dir_d * 2 + dir_hv;
+}
+
+static void FUNC(alf_classify)(int *class_idx, int *transpose_idx,
+ const uint8_t *_src, const ptrdiff_t _src_stride, const int width, const int height,
+ const int vb_pos, int *gradient_tmp)
+{
+ int *grad;
+
+ const int h = height + ALF_GRADIENT_BORDER * 2;
+ const int w = width + ALF_GRADIENT_BORDER * 2;
+ const int size = (ALF_BLOCK_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP;
+ const int gstride = (w / ALF_GRADIENT_STEP) * ALF_NUM_DIR;
+
+ const pixel *src = (const pixel *)_src;
+ const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+ src -= (ALF_GRADIENT_BORDER + 1) * src_stride + ALF_GRADIENT_BORDER;
+
+ grad = gradient_tmp;
+ for (int y = 0; y < h; y += ALF_GRADIENT_STEP) {
+ const pixel *s0 = src + y * src_stride;
+ const pixel *s1 = s0 + src_stride;
+ const pixel *s2 = s1 + src_stride;
+ const pixel *s3 = s2 + src_stride;
+
+ if (y == vb_pos) //above
+ s3 = s2;
+ else if (y == vb_pos + ALF_GRADIENT_BORDER)
+ s0 = s1;
+
+ for (int x = 0; x < w; x += ALF_GRADIENT_STEP) {
+ //two points a time
+ const pixel *a0 = s0 + x;
+ const pixel *p0 = s1 + x;
+ const pixel *b0 = s2 + x;
+ const int val0 = (*p0) << 1;
+
+ const pixel *a1 = s1 + x + 1;
+ const pixel *p1 = s2 + x + 1;
+ const pixel *b1 = s3 + x + 1;
+ const int val1 = (*p1) << 1;
+
+ grad[ALF_DIR_VERT] = FFABS(val0 - *a0 - *b0) + FFABS(val1 - *a1 - *b1);
+ grad[ALF_DIR_HORZ] = FFABS(val0 - *(p0 - 1) - *(p0 + 1)) + FFABS(val1 - *(p1 - 1) - *(p1 + 1));
+ grad[ALF_DIR_DIGA0] = FFABS(val0 - *(a0 - 1) - *(b0 + 1)) + FFABS(val1 - *(a1 - 1) - *(b1 + 1));
+ grad[ALF_DIR_DIGA1] = FFABS(val0 - *(a0 + 1) - *(b0 - 1)) + FFABS(val1 - *(a1 + 1) - *(b1 - 1));
+ grad += ALF_NUM_DIR;
+ }
+ }
+
+ for (int y = 0; y < height ; y += ALF_BLOCK_SIZE ) {
+ int start = 0;
+ int end = (ALF_BLOCK_SIZE + ALF_GRADIENT_BORDER * 2) / ALF_GRADIENT_STEP;
+ int ac = 2;
+ if (y + ALF_BLOCK_SIZE == vb_pos) {
+ end -= ALF_GRADIENT_BORDER / ALF_GRADIENT_STEP;
+ ac = 3;
+ } else if (y == vb_pos) {
+ start += ALF_GRADIENT_BORDER / ALF_GRADIENT_STEP;
+ ac = 3;
+ }
+ for (int x = 0; x < width; x += ALF_BLOCK_SIZE) {
+ const int xg = x / ALF_GRADIENT_STEP;
+ const int yg = y / ALF_GRADIENT_STEP;
+ int sum[ALF_NUM_DIR] = { 0 };
+
+ grad = gradient_tmp + (yg + start) * gstride + xg * ALF_NUM_DIR;
+ //todo: optimize this loop
+ for (int i = start; i < end; i++) {
+ for (int j = 0; j < size; j++) {
+ sum[ALF_DIR_VERT] += grad[ALF_DIR_VERT];
+ sum[ALF_DIR_HORZ] += grad[ALF_DIR_HORZ];
+ sum[ALF_DIR_DIGA0] += grad[ALF_DIR_DIGA0];
+ sum[ALF_DIR_DIGA1] += grad[ALF_DIR_DIGA1];
+ grad += ALF_NUM_DIR;
+ }
+ grad += gstride - size * ALF_NUM_DIR;
+ }
+ FUNC(alf_get_idx)(class_idx, transpose_idx, sum, ac);
+
+ class_idx++;
+ transpose_idx++;
+ }
+ }
+
+}
+
+static void FUNC(alf_recon_coeff_and_clip)(int16_t *coeff, int16_t *clip,
+ const int *class_idx, const int *transpose_idx, const int size,
+ const int16_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt)
+{
+ const static int index[][ALF_NUM_COEFF_LUMA] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
+ { 9, 4, 10, 8, 1, 5, 11, 7, 3, 0, 2, 6 },
+ { 0, 3, 2, 1, 8, 7, 6, 5, 4, 9, 10, 11 },
+ { 9, 8, 10, 4, 3, 7, 11, 5, 1, 0, 2, 6 },
+ };
+
+ const int16_t clip_set[] = {
+ 1 << BIT_DEPTH, 1 << (BIT_DEPTH - 3), 1 << (BIT_DEPTH - 5), 1 << (BIT_DEPTH - 7)
+ };
+
+ for (int i = 0; i < size; i++) {
+ const int16_t *src_coeff = coeff_set + class_to_filt[class_idx[i]] * ALF_NUM_COEFF_LUMA;
+ const uint8_t *clip_idx = clip_idx_set + class_idx[i] * ALF_NUM_COEFF_LUMA;
+
+ for (int j = 0; j < ALF_NUM_COEFF_LUMA; j++) {
+ const int idx = index[transpose_idx[i]][j];
+ *coeff++ = src_coeff[idx];
+ *clip++ = clip_set[clip_idx[idx]];
+ }
+ }
+}
+
+#undef ALF_DIR_HORZ
+#undef ALF_DIR_VERT
+#undef ALF_DIR_DIGA0
+#undef ALF_DIR_DIGA1
+
+// line zero
+#define P7 pix[-8 * xstride]
+#define P6 pix[-7 * xstride]
+#define P5 pix[-6 * xstride]
+#define P4 pix[-5 * xstride]
+#define P3 pix[-4 * xstride]
+#define P2 pix[-3 * xstride]
+#define P1 pix[-2 * xstride]
+#define P0 pix[-1 * xstride]
+#define Q0 pix[0 * xstride]
+#define Q1 pix[1 * xstride]
+#define Q2 pix[2 * xstride]
+#define Q3 pix[3 * xstride]
+#define Q4 pix[4 * xstride]
+#define Q5 pix[5 * xstride]
+#define Q6 pix[6 * xstride]
+#define Q7 pix[7 * xstride]
+#define P(x) pix[(-(x)-1) * xstride]
+#define Q(x) pix[(x) * xstride]
+
+// line three. used only for deblocking decision
+#define TP7 pix[-8 * xstride + 3 * ystride]
+#define TP6 pix[-7 * xstride + 3 * ystride]
+#define TP5 pix[-6 * xstride + 3 * ystride]
+#define TP4 pix[-5 * xstride + 3 * ystride]
+#define TP3 pix[-4 * xstride + 3 * ystride]
+#define TP2 pix[-3 * xstride + 3 * ystride]
+#define TP1 pix[-2 * xstride + 3 * ystride]
+#define TP0 pix[-1 * xstride + 3 * ystride]
+#define TQ0 pix[0 * xstride + 3 * ystride]
+#define TQ1 pix[1 * xstride + 3 * ystride]
+#define TQ2 pix[2 * xstride + 3 * ystride]
+#define TQ3 pix[3 * xstride + 3 * ystride]
+#define TQ4 pix[4 * xstride + 3 * ystride]
+#define TQ5 pix[5 * xstride + 3 * ystride]
+#define TQ6 pix[6 * xstride + 3 * ystride]
+#define TQ7 pix[7 * xstride + 3 * ystride]
+#define TP(x) pix[(-(x)-1) * xstride + 3 * ystride]
+#define TQ(x) pix[(x) * xstride + 3 * ystride]
+
+#define FP3 pix[-4 * xstride + 1 * ystride]
+#define FP2 pix[-3 * xstride + 1 * ystride]
+#define FP1 pix[-2 * xstride + 1 * ystride]
+#define FP0 pix[-1 * xstride + 1 * ystride]
+#define FQ0 pix[0 * xstride + 1 * ystride]
+#define FQ1 pix[1 * xstride + 1 * ystride]
+#define FQ2 pix[2 * xstride + 1 * ystride]
+#define FQ3 pix[3 * xstride + 1 * ystride]
+
+static void FUNC(loop_filter_luma_large)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, const int32_t tc,
+ const uint8_t no_p, const uint8_t no_q, const uint8_t max_len_p, const uint8_t max_len_q)
+{
+ for (int d = 0; d < 4; d++) {
+ const int p6 = P6;
+ const int p5 = P5;
+ const int p4 = P4;
+ const int p3 = P3;
+ const int p2 = P2;
+ const int p1 = P1;
+ const int p0 = P0;
+ const int q0 = Q0;
+ const int q1 = Q1;
+ const int q2 = Q2;
+ const int q3 = Q3;
+ const int q4 = Q4;
+ const int q5 = Q5;
+ const int q6 = Q6;
+ int m;
+ if (max_len_p == 5 && max_len_q == 5)
+ m = (p4 + p3 + 2 * (p2 + p1 + p0 + q0 + q1 + q2) + q3 + q4 + 8) >> 4;
+ else if (max_len_p == max_len_q)
+ m = (p6 + p5 + p4 + p3 + p2 + p1 + 2 * (p0 + q0) + q1 + q2 + q3 + q4 + q5 + q6 + 8) >> 4;
+ else if (max_len_p + max_len_q == 12)
+ m = (p5 + p4 + p3 + p2 + 2 * (p1 + p0 + q0 + q1) + q2 + q3 + q4 + q5 + 8) >> 4;
+ else if (max_len_p + max_len_q == 8)
+ m = (p3 + p2 + p1 + p0 + q0 + q1 + q2 + q3 + 4) >> 3;
+ else if (max_len_q == 7)
+ m = (2 * (p2 + p1 + p0 + q0) + p0 + p1 + q1 + q2 + q3 + q4 + q5 + q6 + 8) >> 4;
+ else
+ m = (p6 + p5 + p4 + p3 + p2 + p1 + 2 * (q2 + q1 + q0 + p0) + q0 + q1 + 8) >> 4;
+ if (!no_p) {
+ const int refp = (P(max_len_p) + P(max_len_p - 1) + 1) >> 1;
+ if (max_len_p == 3) {
+ P0 = p0 + av_clip(((m * 53 + refp * 11 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1));
+ P1 = p1 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p1, -(tc * 4 >> 1), (tc * 4 >> 1));
+ P2 = p2 + av_clip(((m * 11 + refp * 53 + 32) >> 6) - p2, -(tc * 2 >> 1), (tc * 2 >> 1));
+ } else if (max_len_p == 5) {
+ P0 = p0 + av_clip(((m * 58 + refp * 6 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1));
+ P1 = p1 + av_clip(((m * 45 + refp * 19 + 32) >> 6) - p1, -(tc * 5 >> 1), (tc * 5 >> 1));
+ P2 = p2 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p2, -(tc * 4 >> 1), (tc * 4 >> 1));
+ P3 = p3 + av_clip(((m * 19 + refp * 45 + 32) >> 6) - p3, -(tc * 3 >> 1), (tc * 3 >> 1));
+ P4 = p4 + av_clip(((m * 6 + refp * 58 + 32) >> 6) - p4, -(tc * 2 >> 1), (tc * 2 >> 1));
+ } else {
+ P0 = p0 + av_clip(((m * 59 + refp * 5 + 32) >> 6) - p0, -(tc * 6 >> 1), (tc * 6 >> 1));
+ P1 = p1 + av_clip(((m * 50 + refp * 14 + 32) >> 6) - p1, -(tc * 5 >> 1), (tc * 5 >> 1));
+ P2 = p2 + av_clip(((m * 41 + refp * 23 + 32) >> 6) - p2, -(tc * 4 >> 1), (tc * 4 >> 1));
+ P3 = p3 + av_clip(((m * 32 + refp * 32 + 32) >> 6) - p3, -(tc * 3 >> 1), (tc * 3 >> 1));
+ P4 = p4 + av_clip(((m * 23 + refp * 41 + 32) >> 6) - p4, -(tc * 2 >> 1), (tc * 2 >> 1));
+ P5 = p5 + av_clip(((m * 14 + refp * 50 + 32) >> 6) - p5, -(tc * 1 >> 1), (tc * 1 >> 1));
+ P6 = p6 + av_clip(((m * 5 + refp * 59 + 32) >> 6) - p6, -(tc * 1 >> 1), (tc * 1 >> 1));
+ }
+ }
+ if (!no_q) {
+ const int refq = (Q(max_len_q) + Q(max_len_q - 1) + 1) >> 1;
+ if (max_len_q == 3) {
+ Q0 = q0 + av_clip(((m * 53 + refq * 11 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1));
+ Q1 = q1 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q1, -(tc * 4 >> 1), (tc * 4 >> 1));
+ Q2 = q2 + av_clip(((m * 11 + refq * 53 + 32) >> 6) - q2, -(tc * 2 >> 1), (tc * 2 >> 1));
+ } else if (max_len_q == 5) {
+ Q0 = q0 + av_clip(((m * 58 + refq * 6 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1));
+ Q1 = q1 + av_clip(((m * 45 + refq * 19 + 32) >> 6) - q1, -(tc * 5 >> 1), (tc * 5 >> 1));
+ Q2 = q2 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q2, -(tc * 4 >> 1), (tc * 4 >> 1));
+ Q3 = q3 + av_clip(((m * 19 + refq * 45 + 32) >> 6) - q3, -(tc * 3 >> 1), (tc * 3 >> 1));
+ Q4 = q4 + av_clip(((m * 6 + refq * 58 + 32) >> 6) - q4, -(tc * 2 >> 1), (tc * 2 >> 1));
+ } else {
+ Q0 = q0 + av_clip(((m * 59 + refq * 5 + 32) >> 6) - q0, -(tc * 6 >> 1), (tc * 6 >> 1));
+ Q1 = q1 + av_clip(((m * 50 + refq * 14 + 32) >> 6) - q1, -(tc * 5 >> 1), (tc * 5 >> 1));
+ Q2 = q2 + av_clip(((m * 41 + refq * 23 + 32) >> 6) - q2, -(tc * 4 >> 1), (tc * 4 >> 1));
+ Q3 = q3 + av_clip(((m * 32 + refq * 32 + 32) >> 6) - q3, -(tc * 3 >> 1), (tc * 3 >> 1));
+ Q4 = q4 + av_clip(((m * 23 + refq * 41 + 32) >> 6) - q4, -(tc * 2 >> 1), (tc * 2 >> 1));
+ Q5 = q5 + av_clip(((m * 14 + refq * 50 + 32) >> 6) - q5, -(tc * 1 >> 1), (tc * 1 >> 1));
+ Q6 = q6 + av_clip(((m * 5 + refq * 59 + 32) >> 6) - q6, -(tc * 1 >> 1), (tc * 1 >> 1));
+ }
+
+ }
+ pix += ystride;
+ }
+}
+
+static void FUNC(loop_filter_luma_strong)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride, const int32_t tc,
+ const uint8_t no_p, const uint8_t no_q)
+{
+ const int tc2 = tc << 1;
+ const int tc3 = tc * 3;
+ for (int d = 0; d < 4; d++) {
+ const int p3 = P3;
+ const int p2 = P2;
+ const int p1 = P1;
+ const int p0 = P0;
+ const int q0 = Q0;
+ const int q1 = Q1;
+ const int q2 = Q2;
+ const int q3 = Q3;
+ if (!no_p) {
+ P0 = p0 + av_clip(((p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3) - p0, -tc3, tc3);
+ P1 = p1 + av_clip(((p2 + p1 + p0 + q0 + 2) >> 2) - p1, -tc2, tc2);
+ P2 = p2 + av_clip(((2 * p3 + 3 * p2 + p1 + p0 + q0 + 4) >> 3) - p2, -tc, tc);
+ }
+ if (!no_q) {
+ Q0 = q0 + av_clip(((p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3) - q0, -tc3, tc3);
+ Q1 = q1 + av_clip(((p0 + q0 + q1 + q2 + 2) >> 2) - q1, -tc2, tc2);
+ Q2 = q2 + av_clip(((2 * q3 + 3 * q2 + q1 + q0 + p0 + 4) >> 3) - q2, -tc, tc);
+ }
+ pix += ystride;
+ }
+}
+
+static void FUNC(loop_filter_luma_weak)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride,
+ const int32_t tc, const int32_t beta, const uint8_t no_p, const uint8_t no_q, const int nd_p, const int nd_q)
+{
+ const int tc_2 = tc >> 1;
+ for (int d = 0; d < 4; d++) {
+ const int p2 = P2;
+ const int p1 = P1;
+ const int p0 = P0;
+ const int q0 = Q0;
+ const int q1 = Q1;
+ const int q2 = Q2;
+ int delta0 = (9 * (q0 - p0) - 3 * (q1 - p1) + 8) >> 4;
+ if (abs(delta0) < 10 * tc) {
+ delta0 = av_clip(delta0, -tc, tc);
+ if (!no_p)
+ P0 = av_clip_pixel(p0 + delta0);
+ if (!no_q)
+ Q0 = av_clip_pixel(q0 - delta0);
+ if (!no_p && nd_p > 1) {
+ const int deltap1 = av_clip((((p2 + p0 + 1) >> 1) - p1 + delta0) >> 1, -tc_2, tc_2);
+ P1 = av_clip_pixel(p1 + deltap1);
+ }
+ if (!no_q && nd_q > 1) {
+ const int deltaq1 = av_clip((((q2 + q0 + 1) >> 1) - q1 - delta0) >> 1, -tc_2, tc_2);
+ Q1 = av_clip_pixel(q1 + deltaq1);
+ }
+ }
+ pix += ystride;
+ }
+
+}
+
+static void FUNC(vvc_loop_filter_luma)(uint8_t* _pix, ptrdiff_t _xstride, ptrdiff_t _ystride,
+ const int32_t *_beta, const int32_t *_tc, const uint8_t *_no_p, const uint8_t *_no_q,
+ const uint8_t *_max_len_p, const uint8_t *_max_len_q, int hor_ctu_edge)
+{
+ const ptrdiff_t xstride = _xstride / sizeof(pixel);
+ const ptrdiff_t ystride = _ystride / sizeof(pixel);
+
+ for (int i = 0; i < 2; i++) {
+ pixel* pix = (pixel*)_pix + i * 4 * ystride;
+ const int dp0 = abs(P2 - 2 * P1 + P0);
+ const int dq0 = abs(Q2 - 2 * Q1 + Q0);
+ const int dp3 = abs(TP2 - 2 * TP1 + TP0);
+ const int dq3 = abs(TQ2 - 2 * TQ1 + TQ0);
+ const int d0 = dp0 + dq0;
+ const int d3 = dp3 + dq3;
+#if BIT_DEPTH < 10
+ const int tc = (_tc[i] + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH);
+#else
+ const int tc = _tc[i] << (BIT_DEPTH - 10);
+#endif
+ const int tc25 = ((tc * 5 + 1) >> 1);
+
+ const int no_p = _no_p[i];
+ const int no_q = _no_q[i];
+
+ int max_len_p = _max_len_p[i];
+ int max_len_q = _max_len_q[i];
+
+ const int large_p = (max_len_p > 3 && !hor_ctu_edge);
+ const int large_q = max_len_q > 3;
+ const int beta = _beta[i] << BIT_DEPTH - 8;
+
+ const int beta_3 = beta >> 3;
+ const int beta_2 = beta >> 2;
+
+ if (!tc)
+ continue;
+
+ if (large_p || large_q) {
+ const int dp0l = large_p ? ((dp0 + abs(P5 - 2 * P4 + P3) + 1) >> 1) : dp0;
+ const int dq0l = large_q ? ((dq0 + abs(Q5 - 2 * Q4 + Q3) + 1) >> 1) : dq0;
+ const int dp3l = large_p ? ((dp3 + abs(TP5 - 2 * TP4 + TP3) + 1) >> 1) : dp3;
+ const int dq3l = large_q ? ((dq3 + abs(TQ5 - 2 * TQ4 + TQ3) + 1) >> 1) : dq3;
+ const int d0l = dp0l + dq0l;
+ const int d3l = dp3l + dq3l;
+ const int beta53 = beta * 3 >> 5;
+ const int beta_4 = beta >> 4;
+ max_len_p = large_p ? max_len_p : 3;
+ max_len_q = large_q ? max_len_q : 3;
+
+ if (d0l + d3l < beta) {
+ const int sp0l = abs(P3 - P0) + (max_len_p == 7 ? abs(P7 - P6 - P5 + P4) : 0);
+ const int sq0l = abs(Q0 - Q3) + (max_len_q == 7 ? abs(Q4 - Q5 - Q6 + Q7) : 0);
+ const int sp3l = abs(TP3 - TP0) + (max_len_p == 7 ? abs(TP7 - TP6 - TP5 + TP4) : 0);
+ const int sq3l = abs(TQ0 - TQ3) + (max_len_q == 7 ? abs(TQ4 - TQ5 - TQ6 + TQ7) : 0);
+ const int sp0 = large_p ? ((sp0l + abs(P3 - P(max_len_p)) + 1) >> 1) : sp0l;
+ const int sp3 = large_p ? ((sp3l + abs(TP3 - TP(max_len_p)) + 1) >> 1) : sp3l;
+ const int sq0 = large_q ? ((sq0l + abs(Q3 - Q(max_len_q)) + 1) >> 1) : sq0l;
+ const int sq3 = large_q ? ((sq3l + abs(TQ3 - TQ(max_len_q)) + 1) >> 1) : sq3l;
+ if (sp0 + sq0 < beta53 && abs(P0 - Q0) < tc25 &&
+ sp3 + sq3 < beta53 && abs(TP0 - TQ0) < tc25 &&
+ (d0l << 1) < beta_4 && (d3l << 1) < beta_4) {
+ FUNC(loop_filter_luma_large)(pix, xstride, ystride, tc, no_p, no_q, max_len_p, max_len_q);
+ continue;
+ }
+ }
+ }
+ if (d0 + d3 < beta) {
+ if (max_len_p > 2 && max_len_q > 2 &&
+ abs(P3 - P0) + abs(Q3 - Q0) < beta_3 && abs(P0 - Q0) < tc25 &&
+ abs(TP3 - TP0) + abs(TQ3 - TQ0) < beta_3 && abs(TP0 - TQ0) < tc25 &&
+ (d0 << 1) < beta_2 && (d3 << 1) < beta_2) {
+ FUNC(loop_filter_luma_strong)(pix, xstride, ystride, tc, no_p, no_q);
+ } else { // weak filtering
+ int nd_p = 1;
+ int nd_q = 1;
+ if (max_len_p > 1 && max_len_q > 1) {
+ if (dp0 + dp3 < ((beta + (beta >> 1)) >> 3))
+ nd_p = 2;
+ if (dq0 + dq3 < ((beta + (beta >> 1)) >> 3))
+ nd_q = 2;
+ }
+ FUNC(loop_filter_luma_weak)(pix, xstride, ystride, tc, beta, no_p, no_q, nd_p, nd_q);
+ }
+ }
+ }
+}
+
+static void FUNC(loop_filter_chroma_strong)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride,
+ const int size, const int32_t tc, const uint8_t no_p, const uint8_t no_q)
+{
+ for (int d = 0; d < size; d++) {
+ const int p3 = P3;
+ const int p2 = P2;
+ const int p1 = P1;
+ const int p0 = P0;
+ const int q0 = Q0;
+ const int q1 = Q1;
+ const int q2 = Q2;
+ const int q3 = Q3;
+ if (!no_p) {
+ P0 = av_clip((p3 + p2 + p1 + 2 * p0 + q0 + q1 + q2 + 4) >> 3, p0 - tc, p0 + tc);
+ P1 = av_clip((2 * p3 + p2 + 2 * p1 + p0 + q0 + q1 + 4) >> 3, p1 - tc, p1 + tc);
+ P2 = av_clip((3 * p3 + 2 * p2 + p1 + p0 + q0 + 4) >> 3, p2 - tc, p2 + tc );
+ }
+ if (!no_q) {
+ Q0 = av_clip((p2 + p1 + p0 + 2 * q0 + q1 + q2 + q3 + 4) >> 3, q0 - tc, q0 + tc);
+ Q1 = av_clip((p1 + p0 + q0 + 2 * q1 + q2 + 2 * q3 + 4) >> 3, q1 - tc, q1 + tc);
+ Q2 = av_clip((p0 + q0 + q1 + 2 * q2 + 3 * q3 + 4) >> 3, q2 - tc, q2 + tc);
+ }
+ pix += ystride;
+ }
+}
+
+static void FUNC(loop_filter_chroma_strong_one_side)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride,
+ const int size, const int32_t tc, const uint8_t no_p, const uint8_t no_q)
+{
+ for (int d = 0; d < size; d++) {
+ const int p1 = P1;
+ const int p0 = P0;
+ const int q0 = Q0;
+ const int q1 = Q1;
+ const int q2 = Q2;
+ const int q3 = Q3;
+ if (!no_p) {
+ P0 = av_clip((3 * p1 + 2 * p0 + q0 + q1 + q2 + 4) >> 3, p0 - tc, p0 + tc);
+ }
+ if (!no_q) {
+ Q0 = av_clip((2 * p1 + p0 + 2 * q0 + q1 + q2 + q3 + 4) >> 3, q0 - tc, q0 + tc);
+ Q1 = av_clip((p1 + p0 + q0 + 2 * q1 + q2 + 2 * q3 + 4) >> 3, q1 - tc, q1 + tc);
+ Q2 = av_clip((p0 + q0 + q1 + 2 * q2 + 3 * q3 + 4) >> 3, q2 - tc, q2 + tc);
+ }
+ pix += ystride;
+ }
+}
+
+static void FUNC(loop_filter_chroma_weak)(pixel *pix, const ptrdiff_t xstride, const ptrdiff_t ystride,
+ const int size, const int32_t tc, const uint8_t no_p, const uint8_t no_q)
+{
+ for (int d = 0; d < size; d++) {
+ int delta0;
+ const int p1 = P1;
+ const int p0 = P0;
+ const int q0 = Q0;
+ const int q1 = Q1;
+ delta0 = av_clip((((q0 - p0) * 4) + p1 - q1 + 4) >> 3, -tc, tc);
+ if (!no_p)
+ P0 = av_clip_pixel(p0 + delta0);
+ if (!no_q)
+ Q0 = av_clip_pixel(q0 - delta0);
+ pix += ystride;
+ }
+}
+
+static void FUNC(vvc_loop_filter_chroma)(uint8_t *_pix, const ptrdiff_t _xstride, const ptrdiff_t _ystride,
+ const int32_t *_beta, const int32_t *_tc, const uint8_t *_no_p, const uint8_t *_no_q,
+ const uint8_t *_max_len_p, const uint8_t *_max_len_q, const int shift)
+{
+ const ptrdiff_t xstride = _xstride / sizeof(pixel);
+ const ptrdiff_t ystride = _ystride / sizeof(pixel);
+ const int size = shift ? 2 : 4;
+ const int end = 8 / size; // 8 samples a loop
+
+ for (int i = 0; i < end; i++) {
+ pixel *pix = (pixel *)_pix + i * size * ystride;
+ const uint8_t no_p = _no_p[i];
+ const uint8_t no_q = _no_q[i];
+ const int beta = _beta[i] << (BIT_DEPTH - 8);
+ const int beta_3 = beta >> 3;
+ const int beta_2 = beta >> 2;
+
+#if BIT_DEPTH < 10
+ const int tc = (_tc[i] + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH);
+#else
+ const int tc = _tc[i] << (BIT_DEPTH - 10);
+#endif
+ const int tc25 = ((tc * 5 + 1) >> 1);
+
+ uint8_t max_len_p = _max_len_p[i];
+ uint8_t max_len_q = _max_len_q[i];
+
+ if (!max_len_p || !max_len_q || !tc)
+ continue;
+
+ if (max_len_q == 3){
+ const int p1n = shift ? FP1 : TP1;
+ const int p2n = max_len_p == 1 ? p1n : (shift ? FP2 : TP2);
+ const int p0n = shift ? FP0 : TP0;
+ const int q0n = shift ? FQ0 : TQ0;
+ const int q1n = shift ? FQ1 : TQ1;
+ const int q2n = shift ? FQ2 : TQ2;
+ const int p3 = max_len_p == 1 ? P1 : P3;
+ const int p2 = max_len_p == 1 ? P1 : P2;
+ const int p1 = P1;
+ const int p0 = P0;
+ const int dp0 = abs(p2 - 2 * p1 + p0);
+ const int dq0 = abs(Q2 - 2 * Q1 + Q0);
+
+ const int dp1 = abs(p2n - 2 * p1n + p0n);
+ const int dq1 = abs(q2n - 2 * q1n + q0n);
+ const int d0 = dp0 + dq0;
+ const int d1 = dp1 + dq1;
+
+ if (d0 + d1 < beta) {
+ const int p3n = max_len_p == 1 ? p1n : (shift ? FP3 : TP3);
+ const int q3n = shift ? FQ3 : TQ3;
+ const int dsam0 = (d0 << 1) < beta_2 && (abs(p3 - p0) + abs(Q0 - Q3) < beta_3) &&
+ abs(p0 - Q0) < tc25;
+ const int dsam1 = (d1 << 1) < beta_2 && (abs(p3n - p0n) + abs(q0n - q3n) < beta_3) &&
+ abs(p0n - q0n) < tc25;
+ if (!dsam0 || !dsam1)
+ max_len_p = max_len_q = 1;
+ } else {
+ max_len_p = max_len_q = 1;
+ }
+ }
+
+ if (max_len_p == 3 && max_len_q == 3)
+ FUNC(loop_filter_chroma_strong)(pix, xstride, ystride, size, tc, no_p, no_q);
+ else if (max_len_q == 3)
+ FUNC(loop_filter_chroma_strong_one_side)(pix, xstride, ystride, size, tc, no_p, no_q);
+ else
+ FUNC(loop_filter_chroma_weak)(pix, xstride, ystride, size, tc, no_p, no_q);
+ }
+}
+
+static void FUNC(vvc_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride,
+ const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q,
+ const uint8_t *max_len_p, const uint8_t *max_len_q, int shift)
+{
+ FUNC(vvc_loop_filter_chroma)(pix, stride, sizeof(pixel), beta, tc,
+ no_p, no_q, max_len_p, max_len_q, shift);
+}
+
+static void FUNC(vvc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride,
+ const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q,
+ const uint8_t *max_len_p, const uint8_t *max_len_q, int shift)
+{
+ FUNC(vvc_loop_filter_chroma)(pix, sizeof(pixel), stride, beta, tc,
+ no_p, no_q, max_len_p, max_len_q, shift);
+}
+
+static void FUNC(vvc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride,
+ const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q,
+ const uint8_t *max_len_p, const uint8_t *max_len_q, int hor_ctu_edge)
+{
+ FUNC(vvc_loop_filter_luma)(pix, stride, sizeof(pixel), beta, tc,
+ no_p, no_q, max_len_p, max_len_q, hor_ctu_edge);
+}
+
+static void FUNC(vvc_v_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride,
+ const int32_t *beta, const int32_t *tc, const uint8_t *no_p, const uint8_t *no_q,
+ const uint8_t *max_len_p, const uint8_t *max_len_q, int hor_ctu_edge)
+{
+ FUNC(vvc_loop_filter_luma)(pix, sizeof(pixel), stride, beta, tc,
+ no_p, no_q, max_len_p, max_len_q, hor_ctu_edge);
+}
+
+static int FUNC(vvc_loop_ladf_level)(const uint8_t *_pix, const ptrdiff_t _xstride, const ptrdiff_t _ystride)
+{
+ const pixel *pix = (pixel *)_pix;
+ const ptrdiff_t xstride = _xstride / sizeof(pixel);
+ const ptrdiff_t ystride = _ystride / sizeof(pixel);
+ return (P0 + TP0 + Q0 + TQ0) >> 2;
+}
+
+static int FUNC(vvc_h_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride)
+{
+ return FUNC(vvc_loop_ladf_level)(pix, stride, sizeof(pixel));
+}
+
+static int FUNC(vvc_v_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride)
+{
+ return FUNC(vvc_loop_ladf_level)(pix, sizeof(pixel), stride);
+}
+
+#undef P7
+#undef P6
+#undef P5
+#undef P4
+#undef P3
+#undef P2
+#undef P1
+#undef P0
+#undef Q0
+#undef Q1
+#undef Q2
+#undef Q3
+#undef Q4
+#undef Q5
+#undef Q6
+#undef Q7
+
+#undef TP7
+#undef TP6
+#undef TP5
+#undef TP4
+#undef TP3
+#undef TP2
+#undef TP1
+#undef TP0
+#undef TQ0
+#undef TQ1
+#undef TQ2
+#undef TQ3
+#undef TQ4
+#undef TQ5
+#undef TQ6
+#undef TQ7
+
+static void FUNC(ff_vvc_lmcs_dsp_init)(VVCLMCSDSPContext *const lmcs)
+{
+ lmcs->filter = FUNC(lmcs_filter_luma);
+}
+
+static void FUNC(ff_vvc_lf_dsp_init)(VVCLFDSPContext *const lf)
+{
+ lf->ladf_level[0] = FUNC(vvc_h_loop_ladf_level);
+ lf->ladf_level[1] = FUNC(vvc_v_loop_ladf_level);
+ lf->filter_luma[0] = FUNC(vvc_h_loop_filter_luma);
+ lf->filter_luma[1] = FUNC(vvc_v_loop_filter_luma);
+ lf->filter_chroma[0] = FUNC(vvc_h_loop_filter_chroma);
+ lf->filter_chroma[1] = FUNC(vvc_v_loop_filter_chroma);
+}
+
+static void FUNC(ff_vvc_sao_dsp_init)(VVCSAODSPContext *const sao)
+{
+ for (int i = 0; i < FF_ARRAY_ELEMS(sao->band_filter); i++)
+ sao->band_filter[i] = FUNC(sao_band_filter);
+ for (int i = 0; i < FF_ARRAY_ELEMS(sao->edge_filter); i++)
+ sao->edge_filter[i] = FUNC(sao_edge_filter);
+ sao->edge_restore[0] = FUNC(sao_edge_restore_0);
+ sao->edge_restore[1] = FUNC(sao_edge_restore_1);
+}
+
+static void FUNC(ff_vvc_alf_dsp_init)(VVCALFDSPContext *const alf)
+{
+ alf->filter[LUMA] = FUNC(alf_filter_luma);
+ alf->filter[CHROMA] = FUNC(alf_filter_chroma);
+ alf->filter_cc = FUNC(alf_filter_cc);
+ alf->classify = FUNC(alf_classify);
+ alf->recon_coeff_and_clip = FUNC(alf_recon_coeff_and_clip);
+}
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
* [FFmpeg-devel] [PATCH v7 08/14] vvcdec: add inv transform 1d
[not found] <20231210125308.1838-1-nuomi2021@gmail.com>
@ 2023-12-10 12:53 ` Nuo Mi
0 siblings, 0 replies; 10+ messages in thread
From: Nuo Mi @ 2023-12-10 12:53 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Nuo Mi
---
libavcodec/vvc/Makefile | 1 +
libavcodec/vvc/vvc_itx_1d.c | 708 ++++++++++++++++++++++++++++++++++++
libavcodec/vvc/vvc_itx_1d.h | 52 +++
3 files changed, 761 insertions(+)
create mode 100644 libavcodec/vvc/vvc_itx_1d.c
create mode 100644 libavcodec/vvc/vvc_itx_1d.h
diff --git a/libavcodec/vvc/Makefile b/libavcodec/vvc/Makefile
index 61b1ab39de..5796f9ad42 100644
--- a/libavcodec/vvc/Makefile
+++ b/libavcodec/vvc/Makefile
@@ -6,6 +6,7 @@ OBJS-$(CONFIG_VVC_DECODER) += vvc/vvcdec.o \
vvc/vvc_ctu.o \
vvc/vvc_data.o \
vvc/vvc_inter.o \
+ vvc/vvc_itx_1d.o \
vvc/vvc_mvs.o \
vvc/vvc_ps.o \
vvc/vvc_refs.o \
diff --git a/libavcodec/vvc/vvc_itx_1d.c b/libavcodec/vvc/vvc_itx_1d.c
new file mode 100644
index 0000000000..01a50aad25
--- /dev/null
+++ b/libavcodec/vvc/vvc_itx_1d.c
@@ -0,0 +1,708 @@
+/*
+ * VVC 1D transform
+ *
+ * Copyright (C) 2023 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* The copyright in this software is being made available under the BSD
+ * License, included below. This software may be subject to other third party
+ * and contributor rights, including patent rights, and no such rights are
+ * granted under this license.
+ *
+ * Copyright (c) 2010-2021, ITU/ISO/IEC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
+ * be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* optimizaed with partial butterfly, see Hung C-Y, Landman P (1997)
+ Compact inverse discrete cosine transform circuit for MPEG video decoding.
+ */
+
+#include "vvc_data.h"
+#include "vvc_itx_1d.h"
+#include "libavutil/avutil.h"
+
+#define G2(m) ((nz > 2) ? (m) : 0)
+#define G4(m) ((nz > 4) ? (m) : 0)
+#define G8(m) ((nz > 8) ? (m) : 0)
+#define G16(m) ((nz > 16) ? (m) : 0)
+
+/*
+transmatrix[2][2] = {
+ { a, a },
+ { a, -a },
+}
+ */
+void ff_vvc_inv_dct2_2(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+
+ coeffs[0 * stride] = a * (x0 + x1);
+ coeffs[1 * stride] = a * (x0 - x1);
+}
+
+/*
+transmatrix[4][4] = {
+ { a, a, a, a},
+ { b, c, -c, -b},
+ { a, -a, -a, a},
+ { c, -b, b, -c},
+}
+ */
+void ff_vvc_inv_dct2_4(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64, b = 83, c = 36;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int E[2] = {
+ a * (x0 + G2(+x2)),
+ a * (x0 + G2(-x2)),
+ };
+ const int O[2] = {
+ b * x1 + G2(+c * x3),
+ c * x1 + G2(-b * x3),
+ };
+
+ coeffs[0 * stride] = E[0] + O[0];
+ coeffs[1 * stride] = E[1] + O[1];
+ coeffs[2 * stride] = E[1] - O[1];
+ coeffs[3 * stride] = E[0] - O[0];
+}
+
+/*
+transmatrix[8][8] = {
+ { a, a, a, a, a, a, a, a},
+ { d, e, f, g, -g, -f, -e, -d},
+ { b, c, -c, -b, -b, -c, c, b},
+ { e, -g, -d, -f, f, d, g, -e},
+ { a, -a, -a, a, a, -a, -a, a},
+ { f, -d, g, e, -e, -g, d, -f},
+ { c, -b, b, -c, -c, b, -b, c},
+ { g, -f, e, -d, d, -e, f, -g},
+}
+ */
+void ff_vvc_inv_dct2_8(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride];
+ const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride];
+ const int EE[2] = {
+ a * (x0 + G4(+x4)),
+ a * (x0 + G4(-x4)),
+ };
+ const int EO[2] = {
+ G2(b * x2) + G4(+c * x6),
+ G2(c * x2) + G4(-b * x6),
+ };
+ const int E[4] = {
+ EE[0] + EO[0], EE[1] + EO[1],
+ EE[1] - EO[1], EE[0] - EO[0],
+ };
+ const int O[4] = {
+ d * x1 + G2(+e * x3) + G4(+f * x5 + g * x7),
+ e * x1 + G2(-g * x3) + G4(-d * x5 - f * x7),
+ f * x1 + G2(-d * x3) + G4(+g * x5 + e * x7),
+ g * x1 + G2(-f * x3) + G4(+e * x5 - d * x7),
+ };
+
+ coeffs[0 * stride] = E[0] + O[0];
+ coeffs[1 * stride] = E[1] + O[1];
+ coeffs[2 * stride] = E[2] + O[2];
+ coeffs[3 * stride] = E[3] + O[3];
+ coeffs[4 * stride] = E[3] - O[3];
+ coeffs[5 * stride] = E[2] - O[2];
+ coeffs[6 * stride] = E[1] - O[1];
+ coeffs[7 * stride] = E[0] - O[0];
+}
+
+/*
+transmatrix[16][16] = {
+ { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a},
+ { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h},
+ { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d},
+ { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i},
+ { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b},
+ { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j},
+ { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e},
+ { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k},
+ { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a},
+ { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l},
+ { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f},
+ { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m},
+ { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c},
+ { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n},
+ { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g},
+ { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o},
+}
+ */
+void ff_vvc_inv_dct2_16(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90;
+ const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride];
+ const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride];
+ const int x8 = coeffs[8 * stride], x9 = coeffs[9 * stride];
+ const int x10 = coeffs[10 * stride], x11 = coeffs[11 * stride];
+ const int x12 = coeffs[12 * stride], x13 = coeffs[13 * stride];
+ const int x14 = coeffs[14 * stride], x15 = coeffs[15 * stride];
+ const int EEE[2] = {
+ a * (x0 + G8(+x8)),
+ a * (x0 + G8(-x8)),
+ };
+ const int EEO[2] = {
+ G4(b * x4) + G8(+c * x12),
+ G4(c * x4) + G8(-b * x12),
+ };
+ const int EE[4] = {
+ EEE[0] + EEO[0], EEE[1] + EEO[1],
+ EEE[1] - EEO[1], EEE[0] - EEO[0],
+ };
+ const int EO[4] = {
+ G2(d * x2) + G4(+e * x6) + G8(+f * x10 + g * x14),
+ G2(e * x2) + G4(-g * x6) + G8(-d * x10 - f * x14),
+ G2(f * x2) + G4(-d * x6) + G8(+g * x10 + e * x14),
+ G2(g * x2) + G4(-f * x6) + G8(+e * x10 - d * x14),
+ };
+ const int E[8] = {
+ EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3],
+ EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0],
+ };
+ const int O[8] = {
+ h * x1 + G2(+i * x3) + G4(+j * x5 + k * x7) + G8(+l * x9 + m * x11 + n * x13 + o * x15),
+ i * x1 + G2(+l * x3) + G4(+o * x5 - m * x7) + G8(-j * x9 - h * x11 - k * x13 - n * x15),
+ j * x1 + G2(+o * x3) + G4(-k * x5 - i * x7) + G8(-n * x9 + l * x11 + h * x13 + m * x15),
+ k * x1 + G2(-m * x3) + G4(-i * x5 + o * x7) + G8(+h * x9 + n * x11 - j * x13 - l * x15),
+ l * x1 + G2(-j * x3) + G4(-n * x5 + h * x7) + G8(-o * x9 - i * x11 + m * x13 + k * x15),
+ m * x1 + G2(-h * x3) + G4(+l * x5 + n * x7) + G8(-i * x9 + k * x11 + o * x13 - j * x15),
+ n * x1 + G2(-k * x3) + G4(+h * x5 - j * x7) + G8(+m * x9 + o * x11 - l * x13 + i * x15),
+ o * x1 + G2(-n * x3) + G4(+m * x5 - l * x7) + G8(+k * x9 - j * x11 + i * x13 - h * x15),
+ };
+
+ coeffs[0 * stride] = E[0] + O[0];
+ coeffs[1 * stride] = E[1] + O[1];
+ coeffs[2 * stride] = E[2] + O[2];
+ coeffs[3 * stride] = E[3] + O[3];
+ coeffs[4 * stride] = E[4] + O[4];
+ coeffs[5 * stride] = E[5] + O[5];
+ coeffs[6 * stride] = E[6] + O[6];
+ coeffs[7 * stride] = E[7] + O[7];
+ coeffs[8 * stride] = E[7] - O[7];
+ coeffs[9 * stride] = E[6] - O[6];
+ coeffs[10 * stride] = E[5] - O[5];
+ coeffs[11 * stride] = E[4] - O[4];
+ coeffs[12 * stride] = E[3] - O[3];
+ coeffs[13 * stride] = E[2] - O[2];
+ coeffs[14 * stride] = E[1] - O[1];
+ coeffs[15 * stride] = E[0] - O[0];
+}
+
+/*
+transMatrix[32][32] = {
+ { a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a, a},
+ { p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, -E, -D, -C, -B, -A, -z, -y, -x, -w, -v, -u, -t, -s, -r, -q, -p},
+ { h, i, j, k, l, m, n, o, -o, -n, -m, -l, -k, -j, -i, -h, -h, -i, -j, -k, -l, -m, -n, -o, o, n, m, l, k, j, i, h},
+ { q, t, w, z, C, -E, -B, -y, -v, -s, -p, -r, -u, -x, -A, -D, D, A, x, u, r, p, s, v, y, B, E, -C, -z, -w, -t, -q},
+ { d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d, d, e, f, g, -g, -f, -e, -d, -d, -e, -f, -g, g, f, e, d},
+ { r, w, B, -D, -y, -t, -p, -u, -z, -E, A, v, q, s, x, C, -C, -x, -s, -q, -v, -A, E, z, u, p, t, y, D, -B, -w, -r},
+ { i, l, o, -m, -j, -h, -k, -n, n, k, h, j, m, -o, -l, -i, -i, -l, -o, m, j, h, k, n, -n, -k, -h, -j, -m, o, l, i},
+ { s, z, -D, -w, -p, -v, -C, A, t, r, y, -E, -x, -q, -u, -B, B, u, q, x, E, -y, -r, -t, -A, C, v, p, w, D, -z, -s},
+ { b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b, b, c, -c, -b, -b, -c, c, b},
+ { t, C, -y, -p, -x, D, u, s, B, -z, -q, -w, E, v, r, A, -A, -r, -v, -E, w, q, z, -B, -s, -u, -D, x, p, y, -C, -t},
+ { j, o, -k, -i, -n, l, h, m, -m, -h, -l, n, i, k, -o, -j, -j, -o, k, i, n, -l, -h, -m, m, h, l, -n, -i, -k, o, j},
+ { u, -E, -t, -v, D, s, w, -C, -r, -x, B, q, y, -A, -p, -z, z, p, A, -y, -q, -B, x, r, C, -w, -s, -D, v, t, E, -u},
+ { e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e, e, -g, -d, -f, f, d, g, -e, -e, g, d, f, -f, -d, -g, e},
+ { v, -B, -p, -C, u, w, -A, -q, -D, t, x, -z, -r, -E, s, y, -y, -s, E, r, z, -x, -t, D, q, A, -w, -u, C, p, B, -v},
+ { k, -m, -i, o, h, n, -j, -l, l, j, -n, -h, -o, i, m, -k, -k, m, i, -o, -h, -n, j, l, -l, -j, n, h, o, -i, -m, k},
+ { w, -y, -u, A, s, -C, -q, E, p, D, -r, -B, t, z, -v, -x, x, v, -z, -t, B, r, -D, -p, -E, q, C, -s, -A, u, y, -w},
+ { a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a, a, -a, -a, a},
+ { x, -v, -z, t, B, -r, -D, p, -E, -q, C, s, -A, -u, y, w, -w, -y, u, A, -s, -C, q, E, -p, D, r, -B, -t, z, v, -x},
+ { l, -j, -n, h, -o, -i, m, k, -k, -m, i, o, -h, n, j, -l, -l, j, n, -h, o, i, -m, -k, k, m, -i, -o, h, -n, -j, l},
+ { y, -s, -E, r, -z, -x, t, D, -q, A, w, -u, -C, p, -B, -v, v, B, -p, C, u, -w, -A, q, -D, -t, x, z, -r, E, s, -y},
+ { f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f, f, -d, g, e, -e, -g, d, -f, -f, d, -g, -e, e, g, -d, f},
+ { z, -p, A, y, -q, B, x, -r, C, w, -s, D, v, -t, E, u, -u, -E, t, -v, -D, s, -w, -C, r, -x, -B, q, -y, -A, p, -z},
+ { m, -h, l, n, -i, k, o, -j, j, -o, -k, i, -n, -l, h, -m, -m, h, -l, -n, i, -k, -o, j, -j, o, k, -i, n, l, -h, m},
+ { A, -r, v, -E, -w, q, -z, -B, s, -u, D, x, -p, y, C, -t, t, -C, -y, p, -x, -D, u, -s, B, z, -q, w, E, -v, r, -A},
+ { c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c, c, -b, b, -c, -c, b, -b, c},
+ { B, -u, q, -x, E, y, -r, t, -A, -C, v, -p, w, -D, -z, s, -s, z, D, -w, p, -v, C, A, -t, r, -y, -E, x, -q, u, -B},
+ { n, -k, h, -j, m, o, -l, i, -i, l, -o, -m, j, -h, k, -n, -n, k, -h, j, -m, -o, l, -i, i, -l, o, m, -j, h, -k, n},
+ { C, -x, s, -q, v, -A, -E, z, -u, p, -t, y, -D, -B, w, -r, r, -w, B, D, -y, t, -p, u, -z, E, A, -v, q, -s, x, -C},
+ { g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g, g, -f, e, -d, d, -e, f, -g, -g, f, -e, d, -d, e, -f, g},
+ { D, -A, x, -u, r, -p, s, -v, y, -B, E, C, -z, w, -t, q, -q, t, -w, z, -C, -E, B, -y, v, -s, p, -r, u, -x, A, -D},
+ { o, -n, m, -l, k, -j, i, -h, h, -i, j, -k, l, -m, n, -o, -o, n, -m, l, -k, j, -i, h, -h, i, -j, k, -l, m, -n, o},
+ { E, -D, C, -B, A, -z, y, -x, w, -v, u, -t, s, -r, q, -p, p, -q, r, -s, t, -u, v, -w, x, -y, z, -A, B, -C, D, -E},
+}
+ */
+void ff_vvc_inv_dct2_32(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int a = 64, b = 83, c = 36, d = 89, e = 75, f = 50, g = 18, h = 90;
+ const int i = 87, j = 80, k = 70, l = 57, m = 43, n = 25, o = 9, p = 90;
+ const int q = 90, r = 88, s = 85, t = 82, u = 78, v = 73, w = 67, x = 61;
+ const int y = 54, z = 46, A = 38, B = 31, C = 22, D = 13, E_= 4;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride];
+ const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride];
+ const int x8 = coeffs[8 * stride], x9 = coeffs[9 * stride];
+ const int x10 = coeffs[10 * stride], x11 = coeffs[11 * stride];
+ const int x12 = coeffs[12 * stride], x13 = coeffs[13 * stride];
+ const int x14 = coeffs[14 * stride], x15 = coeffs[15 * stride];
+ const int x16 = coeffs[16 * stride], x17 = coeffs[17 * stride];
+ const int x18 = coeffs[18 * stride], x19 = coeffs[19 * stride];
+ const int x20 = coeffs[20 * stride], x21 = coeffs[21 * stride];
+ const int x22 = coeffs[22 * stride], x23 = coeffs[23 * stride];
+ const int x24 = coeffs[24 * stride], x25 = coeffs[25 * stride];
+ const int x26 = coeffs[26 * stride], x27 = coeffs[27 * stride];
+ const int x28 = coeffs[28 * stride], x29 = coeffs[29 * stride];
+ const int x30 = coeffs[30 * stride], x31 = coeffs[31 * stride];
+ const int EEEE[2] = {
+ a * (x0 + G16(+x16)),
+ a * (x0 + G16(-x16)),
+ };
+ const int EEEO[2] = {
+ G8(b * x8) + G16(+c * x24),
+ G8(c * x8) + G16(-b * x24),
+ };
+ const int EEE[4] = {
+ EEEE[0] + EEEO[0], EEEE[1] + EEEO[1],
+ EEEE[1] - EEEO[1], EEEE[0] - EEEO[0],
+ };
+ const int EEO[4] = {
+ G4(d * x4) + G8(+e * x12) + G16(+f * x20 + g * x28),
+ G4(e * x4) + G8(-g * x12) + G16(-d * x20 - f * x28),
+ G4(f * x4) + G8(-d * x12) + G16(+g * x20 + e * x28),
+ G4(g * x4) + G8(-f * x12) + G16(+e * x20 - d * x28),
+ };
+ const int EE[8] = {
+ EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3],
+ EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0],
+ };
+ const int EO[8] = {
+ G2(h * x2) + G4(+i * x6) + G8(+ j * x10 + k * x14) + G16(+l * x18 + m * x22 + n * x26 + o * x30),
+ G2(i * x2) + G4(+l * x6) + G8(+ o * x10 - m * x14) + G16(-j * x18 - h * x22 - k * x26 - n * x30),
+ G2(j * x2) + G4(+o * x6) + G8(- k * x10 - i * x14) + G16(-n * x18 + l * x22 + h * x26 + m * x30),
+ G2(k * x2) + G4(-m * x6) + G8(- i * x10 + o * x14) + G16(+h * x18 + n * x22 - j * x26 - l * x30),
+ G2(l * x2) + G4(-j * x6) + G8(- n * x10 + h * x14) + G16(-o * x18 - i * x22 + m * x26 + k * x30),
+ G2(m * x2) + G4(-h * x6) + G8(+ l * x10 + n * x14) + G16(-i * x18 + k * x22 + o * x26 - j * x30),
+ G2(n * x2) + G4(-k * x6) + G8(+ h * x10 - j * x14) + G16(+m * x18 + o * x22 - l * x26 + i * x30),
+ G2(o * x2) + G4(-n * x6) + G8(+ m * x10 - l * x14) + G16(+k * x18 - j * x22 + i * x26 - h * x30),
+ };
+ const int E[16] = {
+ EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7],
+ EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0],
+ };
+ const int O[16] = {
+ p * x1 + G2(+q * x3) + G4(+r * x5 + s * x7) + G8(+t * x9 + u * x11 + v * x13 + w * x15) + G16(+x * x17 + y * x19 + z * x21 + A * x23 + B * x25 + C * x27 + D * x29 + E_* x31),
+ q * x1 + G2(+t * x3) + G4(+w * x5 + z * x7) + G8(+C * x9 - E_* x11 - B * x13 - y * x15) + G16(-v * x17 - s * x19 - p * x21 - r * x23 - u * x25 - x * x27 - A * x29 - D * x31),
+ r * x1 + G2(+w * x3) + G4(+B * x5 - D * x7) + G8(-y * x9 - t * x11 - p * x13 - u * x15) + G16(-z * x17 - E_* x19 + A * x21 + v * x23 + q * x25 + s * x27 + x * x29 + C * x31),
+ s * x1 + G2(+z * x3) + G4(-D * x5 - w * x7) + G8(-p * x9 - v * x11 - C * x13 + A * x15) + G16(+t * x17 + r * x19 + y * x21 - E_* x23 - x * x25 - q * x27 - u * x29 - B * x31),
+ t * x1 + G2(+C * x3) + G4(-y * x5 - p * x7) + G8(-x * x9 + D * x11 + u * x13 + s * x15) + G16(+B * x17 - z * x19 - q * x21 - w * x23 + E_* x25 + v * x27 + r * x29 + A * x31),
+ u * x1 + G2(-E_* x3) + G4(-t * x5 - v * x7) + G8(+D * x9 + s * x11 + w * x13 - C * x15) + G16(-r * x17 - x * x19 + B * x21 + q * x23 + y * x25 - A * x27 - p * x29 - z * x31),
+ v * x1 + G2(-B * x3) + G4(-p * x5 - C * x7) + G8(+u * x9 + w * x11 - A * x13 - q * x15) + G16(-D * x17 + t * x19 + x * x21 - z * x23 - r * x25 - E_* x27 + s * x29 + y * x31),
+ w * x1 + G2(-y * x3) + G4(-u * x5 + A * x7) + G8(+s * x9 - C * x11 - q * x13 + E_* x15) + G16(+p * x17 + D * x19 - r * x21 - B * x23 + t * x25 + z * x27 - v * x29 - x * x31),
+ x * x1 + G2(-v * x3) + G4(-z * x5 + t * x7) + G8(+B * x9 - r * x11 - D * x13 + p * x15) + G16(-E_* x17 - q * x19 + C * x21 + s * x23 - A * x25 - u * x27 + y * x29 + w * x31),
+ y * x1 + G2(-s * x3) + G4(-E_* x5 + r * x7) + G8(-z * x9 - x * x11 + t * x13 + D * x15) + G16(-q * x17 + A * x19 + w * x21 - u * x23 - C * x25 + p * x27 - B * x29 - v * x31),
+ z * x1 + G2(-p * x3) + G4(+A * x5 + y * x7) + G8(-q * x9 + B * x11 + x * x13 - r * x15) + G16(+C * x17 + w * x19 - s * x21 + D * x23 + v * x25 - t * x27 + E_* x29 + u * x31),
+ A * x1 + G2(-r * x3) + G4(+v * x5 - E_* x7) + G8(-w * x9 + q * x11 - z * x13 - B * x15) + G16(+s * x17 - u * x19 + D * x21 + x * x23 - p * x25 + y * x27 + C * x29 - t * x31),
+ B * x1 + G2(-u * x3) + G4(+q * x5 - x * x7) + G8(+E_* x9 + y * x11 - r * x13 + t * x15) + G16(-A * x17 - C * x19 + v * x21 - p * x23 + w * x25 - D * x27 - z * x29 + s * x31),
+ C * x1 + G2(-x * x3) + G4(+s * x5 - q * x7) + G8(+v * x9 - A * x11 - E_* x13 + z * x15) + G16(-u * x17 + p * x19 - t * x21 + y * x23 - D * x25 - B * x27 + w * x29 - r * x31),
+ D * x1 + G2(-A * x3) + G4(+x * x5 - u * x7) + G8(+r * x9 - p * x11 + s * x13 - v * x15) + G16(+y * x17 - B * x19 + E_* x21 + C * x23 - z * x25 + w * x27 - t * x29 + q * x31),
+ E_* x1 + G2(-D * x3) + G4(+C * x5 - B * x7) + G8(+A * x9 - z * x11 + y * x13 - x * x15) + G16(+w * x17 - v * x19 + u * x21 - t * x23 + s * x25 - r * x27 + q * x29 - p * x31),
+ };
+
+ coeffs[0 * stride] = E[0] + O[0];
+ coeffs[1 * stride] = E[1] + O[1];
+ coeffs[2 * stride] = E[2] + O[2];
+ coeffs[3 * stride] = E[3] + O[3];
+ coeffs[4 * stride] = E[4] + O[4];
+ coeffs[5 * stride] = E[5] + O[5];
+ coeffs[6 * stride] = E[6] + O[6];
+ coeffs[7 * stride] = E[7] + O[7];
+ coeffs[8 * stride] = E[8] + O[8];
+ coeffs[9 * stride] = E[9] + O[9];
+ coeffs[10 * stride] = E[10] + O[10];
+ coeffs[11 * stride] = E[11] + O[11];
+ coeffs[12 * stride] = E[12] + O[12];
+ coeffs[13 * stride] = E[13] + O[13];
+ coeffs[14 * stride] = E[14] + O[14];
+ coeffs[15 * stride] = E[15] + O[15];
+ coeffs[16 * stride] = E[15] - O[15];
+ coeffs[17 * stride] = E[14] - O[14];
+ coeffs[18 * stride] = E[13] - O[13];
+ coeffs[19 * stride] = E[12] - O[12];
+ coeffs[20 * stride] = E[11] - O[11];
+ coeffs[21 * stride] = E[10] - O[10];
+ coeffs[22 * stride] = E[9] - O[9];
+ coeffs[23 * stride] = E[8] - O[8];
+ coeffs[24 * stride] = E[7] - O[7];
+ coeffs[25 * stride] = E[6] - O[6];
+ coeffs[26 * stride] = E[5] - O[5];
+ coeffs[27 * stride] = E[4] - O[4];
+ coeffs[28 * stride] = E[3] - O[3];
+ coeffs[29 * stride] = E[2] - O[2];
+ coeffs[30 * stride] = E[1] - O[1];
+ coeffs[31 * stride] = E[0] - O[0];
+}
+
+/*
+transMatrix[64][64] = {
+ { aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa, aa },
+ { bf, bg, bh, bi, bj, bk, bl, bm, bn, bo, bp, bq, br, bs, bt, bu, bv, bw, bx, by, bz, ca, cb, cc, cd, ce, cf, cg, ch, ci, cj, ck, -ck, -cj, -ci, -ch, -cg, -cf, -ce, -cd, -cc, -cb, -ca, -bz, -by, -bx, -bw, -bv, -bu, -bt, -bs, -br, -bq, -bp, -bo, -bn, -bm, -bl, -bk, -bj, -bi, -bh, -bg, -bf },
+ { ap, aq, ar, as, at, au, av, aw, ax, ay, az, ba, bb, bc, bd, be, -be, -bd, -bc, -bb, -ba, -az, -ay, -ax, -aw, -av, -au, -at, -as, -ar, -aq, -ap, -ap, -aq, -ar, -as, -at, -au, -av, -aw, -ax, -ay, -az, -ba, -bb, -bc, -bd, -be, be, bd, bc, bb, ba, az, ay, ax, aw, av, au, at, as, ar, aq, ap },
+ { bg, bj, bm, bp, bs, bv, by, cb, ce, ch, ck, -ci, -cf, -cc, -bz, -bw, -bt, -bq, -bn, -bk, -bh, -bf, -bi, -bl, -bo, -br, -bu, -bx, -ca, -cd, -cg, -cj, cj, cg, cd, ca, bx, bu, br, bo, bl, bi, bf, bh, bk, bn, bq, bt, bw, bz, cc, cf, ci, -ck, -ch, -ce, -cb, -by, -bv, -bs, -bp, -bm, -bj, -bg },
+ { ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah, ah, ai, aj, ak, al, am, an, ao, -ao, -an, -am, -al, -ak, -aj, -ai, -ah, -ah, -ai, -aj, -ak, -al, -am, -an, -ao, ao, an, am, al, ak, aj, ai, ah },
+ { bh, bm, br, bw, cb, cg, -ck, -cf, -ca, -bv, -bq, -bl, -bg, -bi, -bn, -bs, -bx, -cc, -ch, cj, ce, bz, bu, bp, bk, bf, bj, bo, bt, by, cd, ci, -ci, -cd, -by, -bt, -bo, -bj, -bf, -bk, -bp, -bu, -bz, -ce, -cj, ch, cc, bx, bs, bn, bi, bg, bl, bq, bv, ca, cf, ck, -cg, -cb, -bw, -br, -bm, -bh },
+ { aq, at, aw, az, bc, -be, -bb, -ay, -av, -as, -ap, -ar, -au, -ax, -ba, -bd, bd, ba, ax, au, ar, ap, as, av, ay, bb, be, -bc, -az, -aw, -at, -aq, -aq, -at, -aw, -az, -bc, be, bb, ay, av, as, ap, ar, au, ax, ba, bd, -bd, -ba, -ax, -au, -ar, -ap, -as, -av, -ay, -bb, -be, bc, az, aw, at, aq },
+ { bi, bp, bw, cd, ck, -ce, -bx, -bq, -bj, -bh, -bo, -bv, -cc, -cj, cf, by, br, bk, bg, bn, bu, cb, ci, -cg, -bz, -bs, -bl, -bf, -bm, -bt, -ca, -ch, ch, ca, bt, bm, bf, bl, bs, bz, cg, -ci, -cb, -bu, -bn, -bg, -bk, -br, -by, -cf, cj, cc, bv, bo, bh, bj, bq, bx, ce, -ck, -cd, -bw, -bp, -bi },
+ { ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad, ad, ae, af, ag, -ag, -af, -ae, -ad, -ad, -ae, -af, -ag, ag, af, ae, ad },
+ { bj, bs, cb, ck, -cc, -bt, -bk, -bi, -br, -ca, -cj, cd, bu, bl, bh, bq, bz, ci, -ce, -bv, -bm, -bg, -bp, -by, -ch, cf, bw, bn, bf, bo, bx, cg, -cg, -bx, -bo, -bf, -bn, -bw, -cf, ch, by, bp, bg, bm, bv, ce, -ci, -bz, -bq, -bh, -bl, -bu, -cd, cj, ca, br, bi, bk, bt, cc, -ck, -cb, -bs, -bj },
+ { ar, aw, bb, -bd, -ay, -at, -ap, -au, -az, -be, ba, av, aq, as, ax, bc, -bc, -ax, -as, -aq, -av, -ba, be, az, au, ap, at, ay, bd, -bb, -aw, -ar, -ar, -aw, -bb, bd, ay, at, ap, au, az, be, -ba, -av, -aq, -as, -ax, -bc, bc, ax, as, aq, av, ba, -be, -az, -au, -ap, -at, -ay, -bd, bb, aw, ar },
+ { bk, bv, cg, -ce, -bt, -bi, -bm, -bx, -ci, cc, br, bg, bo, bz, ck, -ca, -bp, -bf, -bq, -cb, cj, by, bn, bh, bs, cd, -ch, -bw, -bl, -bj, -bu, -cf, cf, bu, bj, bl, bw, ch, -cd, -bs, -bh, -bn, -by, -cj, cb, bq, bf, bp, ca, -ck, -bz, -bo, -bg, -br, -cc, ci, bx, bm, bi, bt, ce, -cg, -bv, -bk },
+ { ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai, ai, al, ao, -am, -aj, -ah, -ak, -an, an, ak, ah, aj, am, -ao, -al, -ai, -ai, -al, -ao, am, aj, ah, ak, an, -an, -ak, -ah, -aj, -am, ao, al, ai },
+ { bl, by, -ck, -bx, -bk, -bm, -bz, cj, bw, bj, bn, ca, -ci, -bv, -bi, -bo, -cb, ch, bu, bh, bp, cc, -cg, -bt, -bg, -bq, -cd, cf, bs, bf, br, ce, -ce, -br, -bf, -bs, -cf, cd, bq, bg, bt, cg, -cc, -bp, -bh, -bu, -ch, cb, bo, bi, bv, ci, -ca, -bn, -bj, -bw, -cj, bz, bm, bk, bx, ck, -by, -bl },
+ { as, az, -bd, -aw, -ap, -av, -bc, ba, at, ar, ay, -be, -ax, -aq, -au, -bb, bb, au, aq, ax, be, -ay, -ar, -at, -ba, bc, av, ap, aw, bd, -az, -as, -as, -az, bd, aw, ap, av, bc, -ba, -at, -ar, -ay, be, ax, aq, au, bb, -bb, -au, -aq, -ax, -be, ay, ar, at, ba, -bc, -av, -ap, -aw, -bd, az, as },
+ { bm, cb, -cf, -bq, -bi, -bx, cj, bu, bf, bt, ci, -by, -bj, -bp, -ce, cc, bn, bl, ca, -cg, -br, -bh, -bw, ck, bv, bg, bs, ch, -bz, -bk, -bo, -cd, cd, bo, bk, bz, -ch, -bs, -bg, -bv, -ck, bw, bh, br, cg, -ca, -bl, -bn, -cc, ce, bp, bj, by, -ci, -bt, -bf, -bu, -cj, bx, bi, bq, cf, -cb, -bm },
+ { ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab, ab, ac, -ac, -ab, -ab, -ac, ac, ab },
+ { bn, ce, -ca, -bj, -br, -ci, bw, bf, bv, -cj, -bs, -bi, -bz, cf, bo, bm, cd, -cb, -bk, -bq, -ch, bx, bg, bu, -ck, -bt, -bh, -by, cg, bp, bl, cc, -cc, -bl, -bp, -cg, by, bh, bt, ck, -bu, -bg, -bx, ch, bq, bk, cb, -cd, -bm, -bo, -cf, bz, bi, bs, cj, -bv, -bf, -bw, ci, br, bj, ca, -ce, -bn },
+ { at, bc, -ay, -ap, -ax, bd, au, as, bb, -az, -aq, -aw, be, av, ar, ba, -ba, -ar, -av, -be, aw, aq, az, -bb, -as, -au, -bd, ax, ap, ay, -bc, -at, -at, -bc, ay, ap, ax, -bd, -au, -as, -bb, az, aq, aw, -be, -av, -ar, -ba, ba, ar, av, be, -aw, -aq, -az, bb, as, au, bd, -ax, -ap, -ay, bc, at },
+ { bo, ch, -bv, -bh, -ca, cc, bj, bt, -cj, -bq, -bm, -cf, bx, bf, by, -ce, -bl, -br, -ck, bs, bk, cd, -bz, -bg, -bw, cg, bn, bp, ci, -bu, -bi, -cb, cb, bi, bu, -ci, -bp, -bn, -cg, bw, bg, bz, -cd, -bk, -bs, ck, br, bl, ce, -by, -bf, -bx, cf, bm, bq, cj, -bt, -bj, -cc, ca, bh, bv, -ch, -bo },
+ { aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj, aj, ao, -ak, -ai, -an, al, ah, am, -am, -ah, -al, an, ai, ak, -ao, -aj, -aj, -ao, ak, ai, an, -al, -ah, -am, am, ah, al, -an, -ai, -ak, ao, aj },
+ { bp, ck, -bq, -bo, -cj, br, bn, ci, -bs, -bm, -ch, bt, bl, cg, -bu, -bk, -cf, bv, bj, ce, -bw, -bi, -cd, bx, bh, cc, -by, -bg, -cb, bz, bf, ca, -ca, -bf, -bz, cb, bg, by, -cc, -bh, -bx, cd, bi, bw, -ce, -bj, -bv, cf, bk, bu, -cg, -bl, -bt, ch, bm, bs, -ci, -bn, -br, cj, bo, bq, -ck, -bp },
+ { au, -be, -at, -av, bd, as, aw, -bc, -ar, -ax, bb, aq, ay, -ba, -ap, -az, az, ap, ba, -ay, -aq, -bb, ax, ar, bc, -aw, -as, -bd, av, at, be, -au, -au, be, at, av, -bd, -as, -aw, bc, ar, ax, -bb, -aq, -ay, ba, ap, az, -az, -ap, -ba, ay, aq, bb, -ax, -ar, -bc, aw, as, bd, -av, -at, -be, au },
+ { bq, -ci, -bl, -bv, cd, bg, ca, -by, -bi, -cf, bt, bn, ck, -bo, -bs, cg, bj, bx, -cb, -bf, -cc, bw, bk, ch, -br, -bp, cj, bm, bu, -ce, -bh, -bz, bz, bh, ce, -bu, -bm, -cj, bp, br, -ch, -bk, -bw, cc, bf, cb, -bx, -bj, -cg, bs, bo, -ck, -bn, -bt, cf, bi, by, -ca, -bg, -cd, bv, bl, ci, -bq },
+ { ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae, ae, -ag, -ad, -af, af, ad, ag, -ae, -ae, ag, ad, af, -af, -ad, -ag, ae },
+ { br, -cf, -bg, -cc, bu, bo, -ci, -bj, -bz, bx, bl, ck, -bm, -bw, ca, bi, ch, -bp, -bt, cd, bf, ce, -bs, -bq, cg, bh, cb, -bv, -bn, cj, bk, by, -by, -bk, -cj, bn, bv, -cb, -bh, -cg, bq, bs, -ce, -bf, -cd, bt, bp, -ch, -bi, -ca, bw, bm, -ck, -bl, -bx, bz, bj, ci, -bo, -bu, cc, bg, cf, -br },
+ { av, -bb, -ap, -bc, au, aw, -ba, -aq, -bd, at, ax, -az, -ar, -be, as, ay, -ay, -as, be, ar, az, -ax, -at, bd, aq, ba, -aw, -au, bc, ap, bb, -av, -av, bb, ap, bc, -au, -aw, ba, aq, bd, -at, -ax, az, ar, be, -as, -ay, ay, as, -be, -ar, -az, ax, at, -bd, -aq, -ba, aw, au, -bc, -ap, -bb, av },
+ { bs, -cc, -bi, -cj, bl, bz, -bv, -bp, cf, bf, cg, -bo, -bw, by, bm, -ci, -bh, -cd, br, bt, -cb, -bj, -ck, bk, ca, -bu, -bq, ce, bg, ch, -bn, -bx, bx, bn, -ch, -bg, -ce, bq, bu, -ca, -bk, ck, bj, cb, -bt, -br, cd, bh, ci, -bm, -by, bw, bo, -cg, -bf, -cf, bp, bv, -bz, -bl, cj, bi, cc, -bs },
+ { ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak, ak, -am, -ai, ao, ah, an, -aj, -al, al, aj, -an, -ah, -ao, ai, am, -ak, -ak, am, ai, -ao, -ah, -an, aj, al, -al, -aj, an, ah, ao, -ai, -am, ak },
+ { bt, -bz, -bn, cf, bh, ck, -bi, -ce, bo, by, -bu, -bs, ca, bm, -cg, -bg, -cj, bj, cd, -bp, -bx, bv, br, -cb, -bl, ch, bf, ci, -bk, -cc, bq, bw, -bw, -bq, cc, bk, -ci, -bf, -ch, bl, cb, -br, -bv, bx, bp, -cd, -bj, cj, bg, cg, -bm, -ca, bs, bu, -by, -bo, ce, bi, -ck, -bh, -cf, bn, bz, -bt },
+ { aw, -ay, -au, ba, as, -bc, -aq, be, ap, bd, -ar, -bb, at, az, -av, -ax, ax, av, -az, -at, bb, ar, -bd, -ap, -be, aq, bc, -as, -ba, au, ay, -aw, -aw, ay, au, -ba, -as, bc, aq, -be, -ap, -bd, ar, bb, -at, -az, av, ax, -ax, -av, az, at, -bb, -ar, bd, ap, be, -aq, -bc, as, ba, -au, -ay, aw },
+ { bu, -bw, -bs, by, bq, -ca, -bo, cc, bm, -ce, -bk, cg, bi, -ci, -bg, ck, bf, cj, -bh, -ch, bj, cf, -bl, -cd, bn, cb, -bp, -bz, br, bx, -bt, -bv, bv, bt, -bx, -br, bz, bp, -cb, -bn, cd, bl, -cf, -bj, ch, bh, -cj, -bf, -ck, bg, ci, -bi, -cg, bk, ce, -bm, -cc, bo, ca, -bq, -by, bs, bw, -bu },
+ { aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa, aa, -aa, -aa, aa },
+ { bv, -bt, -bx, br, bz, -bp, -cb, bn, cd, -bl, -cf, bj, ch, -bh, -cj, bf, -ck, -bg, ci, bi, -cg, -bk, ce, bm, -cc, -bo, ca, bq, -by, -bs, bw, bu, -bu, -bw, bs, by, -bq, -ca, bo, cc, -bm, -ce, bk, cg, -bi, -ci, bg, ck, -bf, cj, bh, -ch, -bj, cf, bl, -cd, -bn, cb, bp, -bz, -br, bx, bt, -bv },
+ { ax, -av, -az, at, bb, -ar, -bd, ap, -be, -aq, bc, as, -ba, -au, ay, aw, -aw, -ay, au, ba, -as, -bc, aq, be, -ap, bd, ar, -bb, -at, az, av, -ax, -ax, av, az, -at, -bb, ar, bd, -ap, be, aq, -bc, -as, ba, au, -ay, -aw, aw, ay, -au, -ba, as, bc, -aq, -be, ap, -bd, -ar, bb, at, -az, -av, ax },
+ { bw, -bq, -cc, bk, ci, -bf, ch, bl, -cb, -br, bv, bx, -bp, -cd, bj, cj, -bg, cg, bm, -ca, -bs, bu, by, -bo, -ce, bi, ck, -bh, cf, bn, -bz, -bt, bt, bz, -bn, -cf, bh, -ck, -bi, ce, bo, -by, -bu, bs, ca, -bm, -cg, bg, -cj, -bj, cd, bp, -bx, -bv, br, cb, -bl, -ch, bf, -ci, -bk, cc, bq, -bw },
+ { al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al, al, -aj, -an, ah, -ao, -ai, am, ak, -ak, -am, ai, ao, -ah, an, aj, -al, -al, aj, an, -ah, ao, ai, -am, -ak, ak, am, -ai, -ao, ah, -an, -aj, al },
+ { bx, -bn, -ch, bg, -ce, -bq, bu, ca, -bk, -ck, bj, -cb, -bt, br, cd, -bh, ci, bm, -by, -bw, bo, cg, -bf, cf, bp, -bv, -bz, bl, cj, -bi, cc, bs, -bs, -cc, bi, -cj, -bl, bz, bv, -bp, -cf, bf, -cg, -bo, bw, by, -bm, -ci, bh, -cd, -br, bt, cb, -bj, ck, bk, -ca, -bu, bq, ce, -bg, ch, bn, -bx },
+ { ay, -as, -be, ar, -az, -ax, at, bd, -aq, ba, aw, -au, -bc, ap, -bb, -av, av, bb, -ap, bc, au, -aw, -ba, aq, -bd, -at, ax, az, -ar, be, as, -ay, -ay, as, be, -ar, az, ax, -at, -bd, aq, -ba, -aw, au, bc, -ap, bb, av, -av, -bb, ap, -bc, -au, aw, ba, -aq, bd, at, -ax, -az, ar, -be, -as, ay },
+ { by, -bk, cj, bn, -bv, -cb, bh, -cg, -bq, bs, ce, -bf, cd, bt, -bp, -ch, bi, -ca, -bw, bm, ck, -bl, bx, bz, -bj, ci, bo, -bu, -cc, bg, -cf, -br, br, cf, -bg, cc, bu, -bo, -ci, bj, -bz, -bx, bl, -ck, -bm, bw, ca, -bi, ch, bp, -bt, -cd, bf, -ce, -bs, bq, cg, -bh, cb, bv, -bn, -cj, bk, -by },
+ { af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af, af, -ad, ag, ae, -ae, -ag, ad, -af, -af, ad, -ag, -ae, ae, ag, -ad, af },
+ { bz, -bh, ce, bu, -bm, cj, bp, -br, -ch, bk, -bw, -cc, bf, -cb, -bx, bj, -cg, -bs, bo, ck, -bn, bt, cf, -bi, by, ca, -bg, cd, bv, -bl, ci, bq, -bq, -ci, bl, -bv, -cd, bg, -ca, -by, bi, -cf, -bt, bn, -ck, -bo, bs, cg, -bj, bx, cb, -bf, cc, bw, -bk, ch, br, -bp, -cj, bm, -bu, -ce, bh, -bz },
+ { az, -ap, ba, ay, -aq, bb, ax, -ar, bc, aw, -as, bd, av, -at, be, au, -au, -be, at, -av, -bd, as, -aw, -bc, ar, -ax, -bb, aq, -ay, -ba, ap, -az, -az, ap, -ba, -ay, aq, -bb, -ax, ar, -bc, -aw, as, -bd, -av, at, -be, -au, au, be, -at, av, bd, -as, aw, bc, -ar, ax, bb, -aq, ay, ba, -ap, az },
+ { ca, -bf, bz, cb, -bg, by, cc, -bh, bx, cd, -bi, bw, ce, -bj, bv, cf, -bk, bu, cg, -bl, bt, ch, -bm, bs, ci, -bn, br, cj, -bo, bq, ck, -bp, bp, -ck, -bq, bo, -cj, -br, bn, -ci, -bs, bm, -ch, -bt, bl, -cg, -bu, bk, -cf, -bv, bj, -ce, -bw, bi, -cd, -bx, bh, -cc, -by, bg, -cb, -bz, bf, -ca },
+ { am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am, am, -ah, al, an, -ai, ak, ao, -aj, aj, -ao, -ak, ai, -an, -al, ah, -am, -am, ah, -al, -an, ai, -ak, -ao, aj, -aj, ao, ak, -ai, an, al, -ah, am },
+ { cb, -bi, bu, ci, -bp, bn, -cg, -bw, bg, -bz, -cd, bk, -bs, -ck, br, -bl, ce, by, -bf, bx, cf, -bm, bq, -cj, -bt, bj, -cc, -ca, bh, -bv, -ch, bo, -bo, ch, bv, -bh, ca, cc, -bj, bt, cj, -bq, bm, -cf, -bx, bf, -by, -ce, bl, -br, ck, bs, -bk, cd, bz, -bg, bw, cg, -bn, bp, -ci, -bu, bi, -cb },
+ { ba, -ar, av, -be, -aw, aq, -az, -bb, as, -au, bd, ax, -ap, ay, bc, -at, at, -bc, -ay, ap, -ax, -bd, au, -as, bb, az, -aq, aw, be, -av, ar, -ba, -ba, ar, -av, be, aw, -aq, az, bb, -as, au, -bd, -ax, ap, -ay, -bc, at, -at, bc, ay, -ap, ax, bd, -au, as, -bb, -az, aq, -aw, -be, av, -ar, ba },
+ { cc, -bl, bp, -cg, -by, bh, -bt, ck, bu, -bg, bx, ch, -bq, bk, -cb, -cd, bm, -bo, cf, bz, -bi, bs, -cj, -bv, bf, -bw, -ci, br, -bj, ca, ce, -bn, bn, -ce, -ca, bj, -br, ci, bw, -bf, bv, cj, -bs, bi, -bz, -cf, bo, -bm, cd, cb, -bk, bq, -ch, -bx, bg, -bu, -ck, bt, -bh, by, cg, -bp, bl, -cc },
+ { ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac, ac, -ab, ab, -ac, -ac, ab, -ab, ac },
+ { cd, -bo, bk, -bz, -ch, bs, -bg, bv, -ck, -bw, bh, -br, cg, ca, -bl, bn, -cc, -ce, bp, -bj, by, ci, -bt, bf, -bu, cj, bx, -bi, bq, -cf, -cb, bm, -bm, cb, cf, -bq, bi, -bx, -cj, bu, -bf, bt, -ci, -by, bj, -bp, ce, cc, -bn, bl, -ca, -cg, br, -bh, bw, ck, -bv, bg, -bs, ch, bz, -bk, bo, -cd },
+ { bb, -au, aq, -ax, be, ay, -ar, at, -ba, -bc, av, -ap, aw, -bd, -az, as, -as, az, bd, -aw, ap, -av, bc, ba, -at, ar, -ay, -be, ax, -aq, au, -bb, -bb, au, -aq, ax, -be, -ay, ar, -at, ba, bc, -av, ap, -aw, bd, az, -as, as, -az, -bd, aw, -ap, av, -bc, -ba, at, -ar, ay, be, -ax, aq, -au, bb },
+ { ce, -br, bf, -bs, cf, cd, -bq, bg, -bt, cg, cc, -bp, bh, -bu, ch, cb, -bo, bi, -bv, ci, ca, -bn, bj, -bw, cj, bz, -bm, bk, -bx, ck, by, -bl, bl, -by, -ck, bx, -bk, bm, -bz, -cj, bw, -bj, bn, -ca, -ci, bv, -bi, bo, -cb, -ch, bu, -bh, bp, -cc, -cg, bt, -bg, bq, -cd, -cf, bs, -bf, br, -ce },
+ { an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an, an, -ak, ah, -aj, am, ao, -al, ai, -ai, al, -ao, -am, aj, -ah, ak, -an, -an, ak, -ah, aj, -am, -ao, al, -ai, ai, -al, ao, am, -aj, ah, -ak, an },
+ { cf, -bu, bj, -bl, bw, -ch, -cd, bs, -bh, bn, -by, cj, cb, -bq, bf, -bp, ca, ck, -bz, bo, -bg, br, -cc, -ci, bx, -bm, bi, -bt, ce, cg, -bv, bk, -bk, bv, -cg, -ce, bt, -bi, bm, -bx, ci, cc, -br, bg, -bo, bz, -ck, -ca, bp, -bf, bq, -cb, -cj, by, -bn, bh, -bs, cd, ch, -bw, bl, -bj, bu, -cf },
+ { bc, -ax, as, -aq, av, -ba, -be, az, -au, ap, -at, ay, -bd, -bb, aw, -ar, ar, -aw, bb, bd, -ay, at, -ap, au, -az, be, ba, -av, aq, -as, ax, -bc, -bc, ax, -as, aq, -av, ba, be, -az, au, -ap, at, -ay, bd, bb, -aw, ar, -ar, aw, -bb, -bd, ay, -at, ap, -au, az, -be, -ba, av, -aq, as, -ax, bc },
+ { cg, -bx, bo, -bf, bn, -bw, cf, ch, -by, bp, -bg, bm, -bv, ce, ci, -bz, bq, -bh, bl, -bu, cd, cj, -ca, br, -bi, bk, -bt, cc, ck, -cb, bs, -bj, bj, -bs, cb, -ck, -cc, bt, -bk, bi, -br, ca, -cj, -cd, bu, -bl, bh, -bq, bz, -ci, -ce, bv, -bm, bg, -bp, by, -ch, -cf, bw, -bn, bf, -bo, bx, -cg },
+ { ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag, ag, -af, ae, -ad, ad, -ae, af, -ag, -ag, af, -ae, ad, -ad, ae, -af, ag },
+ { ch, -ca, bt, -bm, bf, -bl, bs, -bz, cg, ci, -cb, bu, -bn, bg, -bk, br, -by, cf, cj, -cc, bv, -bo, bh, -bj, bq, -bx, ce, ck, -cd, bw, -bp, bi, -bi, bp, -bw, cd, -ck, -ce, bx, -bq, bj, -bh, bo, -bv, cc, -cj, -cf, by, -br, bk, -bg, bn, -bu, cb, -ci, -cg, bz, -bs, bl, -bf, bm, -bt, ca, -ch },
+ { bd, -ba, ax, -au, ar, -ap, as, -av, ay, -bb, be, bc, -az, aw, -at, aq, -aq, at, -aw, az, -bc, -be, bb, -ay, av, -as, ap, -ar, au, -ax, ba, -bd, -bd, ba, -ax, au, -ar, ap, -as, av, -ay, bb, -be, -bc, az, -aw, at, -aq, aq, -at, aw, -az, bc, be, -bb, ay, -av, as, -ap, ar, -au, ax, -ba, bd },
+ { ci, -cd, by, -bt, bo, -bj, bf, -bk, bp, -bu, bz, -ce, cj, ch, -cc, bx, -bs, bn, -bi, bg, -bl, bq, -bv, ca, -cf, ck, cg, -cb, bw, -br, bm, -bh, bh, -bm, br, -bw, cb, -cg, -ck, cf, -ca, bv, -bq, bl, -bg, bi, -bn, bs, -bx, cc, -ch, -cj, ce, -bz, bu, -bp, bk, -bf, bj, -bo, bt, -by, cd, -ci },
+ { ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao, ao, -an, am, -al, ak, -aj, ai, -ah, ah, -ai, aj, -ak, al, -am, an, -ao, -ao, an, -am, al, -ak, aj, -ai, ah, -ah, ai, -aj, ak, -al, am, -an, ao },
+ { cj, -cg, cd, -ca, bx, -bu, br, -bo, bl, -bi, bf, -bh, bk, -bn, bq, -bt, bw, -bz, cc, -cf, ci, ck, -ch, ce, -cb, by, -bv, bs, -bp, bm, -bj, bg, -bg, bj, -bm, bp, -bs, bv, -by, cb, -ce, ch, -ck, -ci, cf, -cc, bz, -bw, bt, -bq, bn, -bk, bh, -bf, bi, -bl, bo, -br, bu, -bx, ca, -cd, cg, -cj },
+ { be, -bd, bc, -bb, ba, -az, ay, -ax, aw, -av, au, -at, as, -ar, aq, -ap, ap, -aq, ar, -as, at, -au, av, -aw, ax, -ay, az, -ba, bb, -bc, bd, -be, -be, bd, -bc, bb, -ba, az, -ay, ax, -aw, av, -au, at, -as, ar, -aq, ap, -ap, aq, -ar, as, -at, au, -av, aw, -ax, ay, -az, ba, -bb, bc, -bd, be },
+ { ck, -cj, ci, -ch, cg, -cf, ce, -cd, cc, -cb, ca, -bz, by, -bx, bw, -bv, bu, -bt, bs, -br, bq, -bp, bo, -bn, bm, -bl, bk, -bj, bi, -bh, bg, -bf, bf, -bg, bh, -bi, bj, -bk, bl, -bm, bn, -bo, bp, -bq, br, -bs, bt, -bu, bv, -bw, bx, -by, bz, -ca, cb, -cc, cd, -ce, cf, -cg, ch, -ci, cj, -ck },
+}
+ */
+
+void ff_vvc_inv_dct2_64(int *coeffs, const ptrdiff_t stride, const size_t nz)
+{
+ const int aa = 64, ab = 83, ac = 36, ad = 89, ae = 75, af = 50, ag = 18, ah = 90;
+ const int ai = 87, aj = 80, ak = 70, al = 57, am = 43, an = 25, ao = 9, ap = 90;
+ const int aq = 90, ar = 88, as = 85, at = 82, au = 78, av = 73, aw = 67, ax = 61;
+ const int ay = 54, az = 46, ba = 38, bb = 31, bc = 22, bd = 13, be = 4, bf = 91;
+ const int bg = 90, bh = 90, bi = 90, bj = 88, bk = 87, bl = 86, bm = 84, bn = 83;
+ const int bo = 81, bp = 79, bq = 77, br = 73, bs = 71, bt = 69, bu = 65, bv = 62;
+ const int bw = 59, bx = 56, by = 52, bz = 48, ca = 44, cb = 41, cc = 37, cd = 33;
+ const int ce = 28, cf = 24, cg = 20, ch = 15, ci = 11, cj = 7, ck = 2;
+ const int x0 = coeffs[0 * stride], x1 = coeffs[1 * stride];
+ const int x2 = coeffs[2 * stride], x3 = coeffs[3 * stride];
+ const int x4 = coeffs[4 * stride], x5 = coeffs[5 * stride];
+ const int x6 = coeffs[6 * stride], x7 = coeffs[7 * stride];
+ const int x8 = coeffs[8 * stride], x9 = coeffs[9 * stride];
+ const int x10 = coeffs[10 * stride], x11 = coeffs[11 * stride];
+ const int x12 = coeffs[12 * stride], x13 = coeffs[13 * stride];
+ const int x14 = coeffs[14 * stride], x15 = coeffs[15 * stride];
+ const int x16 = coeffs[16 * stride], x17 = coeffs[17 * stride];
+ const int x18 = coeffs[18 * stride], x19 = coeffs[19 * stride];
+ const int x20 = coeffs[20 * stride], x21 = coeffs[21 * stride];
+ const int x22 = coeffs[22 * stride], x23 = coeffs[23 * stride];
+ const int x24 = coeffs[24 * stride], x25 = coeffs[25 * stride];
+ const int x26 = coeffs[26 * stride], x27 = coeffs[27 * stride];
+ const int x28 = coeffs[28 * stride], x29 = coeffs[29 * stride];
+ const int x30 = coeffs[30 * stride], x31 = coeffs[31 * stride];
+ //according to vvc specification, x31 to x63 are zeros
+ const int EEEEE[2] = {
+ aa * x0,
+ aa * x0,
+ };
+ const int EEEEO[2] = {
+ G16(ab * x16),
+ G16(ac * x16),
+ };
+ const int EEEE[4] = {
+ EEEEE[0] + EEEEO[0], EEEEE[1] + EEEEO[1],
+ EEEEE[1] - EEEEO[1], EEEEE[0] - EEEEO[0],
+ };
+ const int EEEO[4] = {
+ G8(ad * x8) + G16(+ae * x24),
+ G8(ae * x8) + G16(-ag * x24),
+ G8(af * x8) + G16(-ad * x24),
+ G8(ag * x8) + G16(-af * x24),
+ };
+ const int EEE[8] = {
+ EEEE[0] + EEEO[0], EEEE[1] + EEEO[1], EEEE[2] + EEEO[2], EEEE[3] + EEEO[3],
+ EEEE[3] - EEEO[3], EEEE[2] - EEEO[2], EEEE[1] - EEEO[1], EEEE[0] - EEEO[0],
+ };
+ const int EEO[8] = {
+ G4(ah * x4) + G8(+ai * x12) + G16(+aj * x20 + ak * x28),
+ G4(ai * x4) + G8(+al * x12) + G16(+ao * x20 - am * x28),
+ G4(aj * x4) + G8(+ao * x12) + G16(-ak * x20 - ai * x28),
+ G4(ak * x4) + G8(-am * x12) + G16(-ai * x20 + ao * x28),
+ G4(al * x4) + G8(-aj * x12) + G16(-an * x20 + ah * x28),
+ G4(am * x4) + G8(-ah * x12) + G16(+al * x20 + an * x28),
+ G4(an * x4) + G8(-ak * x12) + G16(+ah * x20 - aj * x28),
+ G4(ao * x4) + G8(-an * x12) + G16(+am * x20 - al * x28),
+ };
+ const int EE[16] = {
+ EEE[0] + EEO[0], EEE[1] + EEO[1], EEE[2] + EEO[2], EEE[3] + EEO[3], EEE[4] + EEO[4], EEE[5] + EEO[5], EEE[6] + EEO[6], EEE[7] + EEO[7],
+ EEE[7] - EEO[7], EEE[6] - EEO[6], EEE[5] - EEO[5], EEE[4] - EEO[4], EEE[3] - EEO[3], EEE[2] - EEO[2], EEE[1] - EEO[1], EEE[0] - EEO[0],
+ };
+ const int EO[16] = {
+ G2(ap * x2) + G4(+aq * x6) + G8(+ar * x10 + as * x14) + G16(+at * x18 + au * x22 + av * x26 + aw * x30),
+ G2(aq * x2) + G4(+at * x6) + G8(+aw * x10 + az * x14) + G16(+bc * x18 - be * x22 - bb * x26 - ay * x30),
+ G2(ar * x2) + G4(+aw * x6) + G8(+bb * x10 - bd * x14) + G16(-ay * x18 - at * x22 - ap * x26 - au * x30),
+ G2(as * x2) + G4(+az * x6) + G8(-bd * x10 - aw * x14) + G16(-ap * x18 - av * x22 - bc * x26 + ba * x30),
+ G2(at * x2) + G4(+bc * x6) + G8(-ay * x10 - ap * x14) + G16(-ax * x18 + bd * x22 + au * x26 + as * x30),
+ G2(au * x2) + G4(-be * x6) + G8(-at * x10 - av * x14) + G16(+bd * x18 + as * x22 + aw * x26 - bc * x30),
+ G2(av * x2) + G4(-bb * x6) + G8(-ap * x10 - bc * x14) + G16(+au * x18 + aw * x22 - ba * x26 - aq * x30),
+ G2(aw * x2) + G4(-ay * x6) + G8(-au * x10 + ba * x14) + G16(+as * x18 - bc * x22 - aq * x26 + be * x30),
+ G2(ax * x2) + G4(-av * x6) + G8(-az * x10 + at * x14) + G16(+bb * x18 - ar * x22 - bd * x26 + ap * x30),
+ G2(ay * x2) + G4(-as * x6) + G8(-be * x10 + ar * x14) + G16(-az * x18 - ax * x22 + at * x26 + bd * x30),
+ G2(az * x2) + G4(-ap * x6) + G8(+ba * x10 + ay * x14) + G16(-aq * x18 + bb * x22 + ax * x26 - ar * x30),
+ G2(ba * x2) + G4(-ar * x6) + G8(+av * x10 - be * x14) + G16(-aw * x18 + aq * x22 - az * x26 - bb * x30),
+ G2(bb * x2) + G4(-au * x6) + G8(+aq * x10 - ax * x14) + G16(+be * x18 + ay * x22 - ar * x26 + at * x30),
+ G2(bc * x2) + G4(-ax * x6) + G8(+as * x10 - aq * x14) + G16(+av * x18 - ba * x22 - be * x26 + az * x30),
+ G2(bd * x2) + G4(-ba * x6) + G8(+ax * x10 - au * x14) + G16(+ar * x18 - ap * x22 + as * x26 - av * x30),
+ G2(be * x2) + G4(-bd * x6) + G8(+bc * x10 - bb * x14) + G16(+ba * x18 - az * x22 + ay * x26 - ax * x30),
+ };
+ const int E[32] = {
+ EE[0] + EO[0], EE[1] + EO[1], EE[2] + EO[2], EE[3] + EO[3], EE[4] + EO[4], EE[5] + EO[5], EE[6] + EO[6], EE[7] + EO[7], EE[8] + EO[8], EE[9] + EO[9], EE[10] + EO[10], EE[11] + EO[11], EE[12] + EO[12], EE[13] + EO[13], EE[14] + EO[14], EE[15] + EO[15],
+ EE[15] - EO[15], EE[14] - EO[14], EE[13] - EO[13], EE[12] - EO[12], EE[11] - EO[11], EE[10] - EO[10], EE[9] - EO[9], EE[8] - EO[8], EE[7] - EO[7], EE[6] - EO[6], EE[5] - EO[5], EE[4] - EO[4], EE[3] - EO[3], EE[2] - EO[2], EE[1] - EO[1], EE[0] - EO[0],
+ };
+ const int O[32] = {
+ bf * x1 + G2(+bg * x3) + G4(+bh * x5 + bi * x7) + G8(+bj * x9 + bk * x11 + bl * x13 + bm * x15) + G16(+bn * x17 + bo * x19 + bp * x21 + bq * x23 + br * x25 + bs * x27 + bt * x29 + bu * x31),
+ bg * x1 + G2(+bj * x3) + G4(+bm * x5 + bp * x7) + G8(+bs * x9 + bv * x11 + by * x13 + cb * x15) + G16(+ce * x17 + ch * x19 + ck * x21 - ci * x23 + -cf * x25 - cc * x27 - bz * x29 - bw * x31),
+ bh * x1 + G2(+bm * x3) + G4(+br * x5 + bw * x7) + G8(+cb * x9 + cg * x11 - ck * x13 - cf * x15) + G16(-ca * x17 - bv * x19 - bq * x21 - bl * x23 + -bg * x25 - bi * x27 - bn * x29 - bs * x31),
+ bi * x1 + G2(+bp * x3) + G4(+bw * x5 + cd * x7) + G8(+ck * x9 - ce * x11 - bx * x13 - bq * x15) + G16(-bj * x17 - bh * x19 - bo * x21 - bv * x23 + -cc * x25 - cj * x27 + cf * x29 + by * x31),
+ bj * x1 + G2(+bs * x3) + G4(+cb * x5 + ck * x7) + G8(-cc * x9 - bt * x11 - bk * x13 - bi * x15) + G16(-br * x17 - ca * x19 - cj * x21 + cd * x23 + bu * x25 + bl * x27 + bh * x29 + bq * x31),
+ bk * x1 + G2(+bv * x3) + G4(+cg * x5 - ce * x7) + G8(-bt * x9 - bi * x11 - bm * x13 - bx * x15) + G16(-ci * x17 + cc * x19 + br * x21 + bg * x23 + bo * x25 + bz * x27 + ck * x29 - ca * x31),
+ bl * x1 + G2(+by * x3) + G4(-ck * x5 - bx * x7) + G8(-bk * x9 - bm * x11 - bz * x13 + cj * x15) + G16(+bw * x17 + bj * x19 + bn * x21 + ca * x23 + -ci * x25 - bv * x27 - bi * x29 - bo * x31),
+ bm * x1 + G2(+cb * x3) + G4(-cf * x5 - bq * x7) + G8(-bi * x9 - bx * x11 + cj * x13 + bu * x15) + G16(+bf * x17 + bt * x19 + ci * x21 - by * x23 + -bj * x25 - bp * x27 - ce * x29 + cc * x31),
+ bn * x1 + G2(+ce * x3) + G4(-ca * x5 - bj * x7) + G8(-br * x9 - ci * x11 + bw * x13 + bf * x15) + G16(+bv * x17 - cj * x19 - bs * x21 - bi * x23 + -bz * x25 + cf * x27 + bo * x29 + bm * x31),
+ bo * x1 + G2(+ch * x3) + G4(-bv * x5 - bh * x7) + G8(-ca * x9 + cc * x11 + bj * x13 + bt * x15) + G16(-cj * x17 - bq * x19 - bm * x21 - cf * x23 + bx * x25 + bf * x27 + by * x29 - ce * x31),
+ bp * x1 + G2(+ck * x3) + G4(-bq * x5 - bo * x7) + G8(-cj * x9 + br * x11 + bn * x13 + ci * x15) + G16(-bs * x17 - bm * x19 - ch * x21 + bt * x23 + bl * x25 + cg * x27 - bu * x29 - bk * x31),
+ bq * x1 + G2(-ci * x3) + G4(-bl * x5 - bv * x7) + G8(+cd * x9 + bg * x11 + ca * x13 - by * x15) + G16(-bi * x17 - cf * x19 + bt * x21 + bn * x23 + ck * x25 - bo * x27 - bs * x29 + cg * x31),
+ br * x1 + G2(-cf * x3) + G4(-bg * x5 - cc * x7) + G8(+bu * x9 + bo * x11 - ci * x13 - bj * x15) + G16(-bz * x17 + bx * x19 + bl * x21 + ck * x23 + -bm * x25 - bw * x27 + ca * x29 + bi * x31),
+ bs * x1 + G2(-cc * x3) + G4(-bi * x5 - cj * x7) + G8(+bl * x9 + bz * x11 - bv * x13 - bp * x15) + G16(+cf * x17 + bf * x19 + cg * x21 - bo * x23 + -bw * x25 + by * x27 + bm * x29 - ci * x31),
+ bt * x1 + G2(-bz * x3) + G4(-bn * x5 + cf * x7) + G8(+bh * x9 + ck * x11 - bi * x13 - ce * x15) + G16(+bo * x17 + by * x19 - bu * x21 - bs * x23 + ca * x25 + bm * x27 - cg * x29 - bg * x31),
+ bu * x1 + G2(-bw * x3) + G4(-bs * x5 + by * x7) + G8(+bq * x9 - ca * x11 - bo * x13 + cc * x15) + G16(+bm * x17 - ce * x19 - bk * x21 + cg * x23 + bi * x25 - ci * x27 - bg * x29 + ck * x31),
+ bv * x1 + G2(-bt * x3) + G4(-bx * x5 + br * x7) + G8(+bz * x9 - bp * x11 - cb * x13 + bn * x15) + G16(+cd * x17 - bl * x19 - cf * x21 + bj * x23 + ch * x25 - bh * x27 - cj * x29 + bf * x31),
+ bw * x1 + G2(-bq * x3) + G4(-cc * x5 + bk * x7) + G8(+ci * x9 - bf * x11 + ch * x13 + bl * x15) + G16(-cb * x17 - br * x19 + bv * x21 + bx * x23 + -bp * x25 - cd * x27 + bj * x29 + cj * x31),
+ bx * x1 + G2(-bn * x3) + G4(-ch * x5 + bg * x7) + G8(-ce * x9 - bq * x11 + bu * x13 + ca * x15) + G16(-bk * x17 - ck * x19 + bj * x21 - cb * x23 + -bt * x25 + br * x27 + cd * x29 - bh * x31),
+ by * x1 + G2(-bk * x3) + G4(+cj * x5 + bn * x7) + G8(-bv * x9 - cb * x11 + bh * x13 - cg * x15) + G16(-bq * x17 + bs * x19 + ce * x21 - bf * x23 + cd * x25 + bt * x27 - bp * x29 - ch * x31),
+ bz * x1 + G2(-bh * x3) + G4(+ce * x5 + bu * x7) + G8(-bm * x9 + cj * x11 + bp * x13 - br * x15) + G16(-ch * x17 + bk * x19 - bw * x21 - cc * x23 + bf * x25 - cb * x27 - bx * x29 + bj * x31),
+ ca * x1 + G2(-bf * x3) + G4(+bz * x5 + cb * x7) + G8(-bg * x9 + by * x11 + cc * x13 - bh * x15) + G16(+bx * x17 + cd * x19 - bi * x21 + bw * x23 + ce * x25 - bj * x27 + bv * x29 + cf * x31),
+ cb * x1 + G2(-bi * x3) + G4(+bu * x5 + ci * x7) + G8(-bp * x9 + bn * x11 - cg * x13 - bw * x15) + G16(+bg * x17 - bz * x19 - cd * x21 + bk * x23 + -bs * x25 - ck * x27 + br * x29 - bl * x31),
+ cc * x1 + G2(-bl * x3) + G4(+bp * x5 - cg * x7) + G8(-by * x9 + bh * x11 - bt * x13 + ck * x15) + G16(+bu * x17 - bg * x19 + bx * x21 + ch * x23 + -bq * x25 + bk * x27 - cb * x29 - cd * x31),
+ cd * x1 + G2(-bo * x3) + G4(+bk * x5 - bz * x7) + G8(-ch * x9 + bs * x11 - bg * x13 + bv * x15) + G16(-ck * x17 - bw * x19 + bh * x21 - br * x23 + cg * x25 + ca * x27 - bl * x29 + bn * x31),
+ ce * x1 + G2(-br * x3) + G4(+bf * x5 - bs * x7) + G8(+cf * x9 + cd * x11 - bq * x13 + bg * x15) + G16(-bt * x17 + cg * x19 + cc * x21 - bp * x23 + bh * x25 - bu * x27 + ch * x29 + cb * x31),
+ cf * x1 + G2(-bu * x3) + G4(+bj * x5 - bl * x7) + G8(+bw * x9 - ch * x11 - cd * x13 + bs * x15) + G16(-bh * x17 + bn * x19 - by * x21 + cj * x23 + cb * x25 - bq * x27 + bf * x29 - bp * x31),
+ cg * x1 + G2(-bx * x3) + G4(+bo * x5 - bf * x7) + G8(+bn * x9 - bw * x11 + cf * x13 + ch * x15) + G16(-by * x17 + bp * x19 - bg * x21 + bm * x23 + -bv * x25 + ce * x27 + ci * x29 - bz * x31),
+ ch * x1 + G2(-ca * x3) + G4(+bt * x5 - bm * x7) + G8(+bf * x9 - bl * x11 + bs * x13 - bz * x15) + G16(+cg * x17 + ci * x19 - cb * x21 + bu * x23 + -bn * x25 + bg * x27 - bk * x29 + br * x31),
+ ci * x1 + G2(-cd * x3) + G4(+by * x5 - bt * x7) + G8(+bo * x9 - bj * x11 + bf * x13 - bk * x15) + G16(+bp * x17 - bu * x19 + bz * x21 - ce * x23 + cj * x25 + ch * x27 - cc * x29 + bx * x31),
+ cj * x1 + G2(-cg * x3) + G4(+cd * x5 - ca * x7) + G8(+bx * x9 - bu * x11 + br * x13 - bo * x15) + G16(+bl * x17 - bi * x19 + bf * x21 - bh * x23 + bk * x25 - bn * x27 + bq * x29 - bt * x31),
+ ck * x1 + G2(-cj * x3) + G4(+ci * x5 - ch * x7) + G8(+cg * x9 - cf * x11 + ce * x13 - cd * x15) + G16(+cc * x17 - cb * x19 + ca * x21 - bz * x23 + by * x25 - bx * x27 + bw * x29 - bv * x31),
+ };
+ coeffs[0 * stride] = E[0 ] + O[0 ];
+ coeffs[1 * stride] = E[1 ] + O[1 ];
+ coeffs[2 * stride] = E[2 ] + O[2 ];
+ coeffs[3 * stride] = E[3 ] + O[3 ];
+ coeffs[4 * stride] = E[4 ] + O[4 ];
+ coeffs[5 * stride] = E[5 ] + O[5 ];
+ coeffs[6 * stride] = E[6 ] + O[6 ];
+ coeffs[7 * stride] = E[7 ] + O[7 ];
+ coeffs[8 * stride] = E[8 ] + O[8 ];
+ coeffs[9 * stride] = E[9 ] + O[9 ];
+ coeffs[10 * stride] = E[10] + O[10];
+ coeffs[11 * stride] = E[11] + O[11];
+ coeffs[12 * stride] = E[12] + O[12];
+ coeffs[13 * stride] = E[13] + O[13];
+ coeffs[14 * stride] = E[14] + O[14];
+ coeffs[15 * stride] = E[15] + O[15];
+ coeffs[16 * stride] = E[16] + O[16];
+ coeffs[17 * stride] = E[17] + O[17];
+ coeffs[18 * stride] = E[18] + O[18];
+ coeffs[19 * stride] = E[19] + O[19];
+ coeffs[20 * stride] = E[20] + O[20];
+ coeffs[21 * stride] = E[21] + O[21];
+ coeffs[22 * stride] = E[22] + O[22];
+ coeffs[23 * stride] = E[23] + O[23];
+ coeffs[24 * stride] = E[24] + O[24];
+ coeffs[25 * stride] = E[25] + O[25];
+ coeffs[26 * stride] = E[26] + O[26];
+ coeffs[27 * stride] = E[27] + O[27];
+ coeffs[28 * stride] = E[28] + O[28];
+ coeffs[29 * stride] = E[29] + O[29];
+ coeffs[30 * stride] = E[30] + O[30];
+ coeffs[31 * stride] = E[31] + O[31];
+ coeffs[32 * stride] = E[31] - O[31];
+ coeffs[33 * stride] = E[30] - O[30];
+ coeffs[34 * stride] = E[29] - O[29];
+ coeffs[35 * stride] = E[28] - O[28];
+ coeffs[36 * stride] = E[27] - O[27];
+ coeffs[37 * stride] = E[26] - O[26];
+ coeffs[38 * stride] = E[25] - O[25];
+ coeffs[39 * stride] = E[24] - O[24];
+ coeffs[40 * stride] = E[23] - O[23];
+ coeffs[41 * stride] = E[22] - O[22];
+ coeffs[42 * stride] = E[21] - O[21];
+ coeffs[43 * stride] = E[20] - O[20];
+ coeffs[44 * stride] = E[19] - O[19];
+ coeffs[45 * stride] = E[18] - O[18];
+ coeffs[46 * stride] = E[17] - O[17];
+ coeffs[47 * stride] = E[16] - O[16];
+ coeffs[48 * stride] = E[15] - O[15];
+ coeffs[49 * stride] = E[14] - O[14];
+ coeffs[50 * stride] = E[13] - O[13];
+ coeffs[51 * stride] = E[12] - O[12];
+ coeffs[52 * stride] = E[11] - O[11];
+ coeffs[53 * stride] = E[10] - O[10];
+ coeffs[54 * stride] = E[9] - O[9];
+ coeffs[55 * stride] = E[8] - O[8];
+ coeffs[56 * stride] = E[7] - O[7];
+ coeffs[57 * stride] = E[6] - O[6];
+ coeffs[58 * stride] = E[5] - O[5];
+ coeffs[59 * stride] = E[4] - O[4];
+ coeffs[60 * stride] = E[3] - O[3];
+ coeffs[61 * stride] = E[2] - O[2];
+ coeffs[62 * stride] = E[1] - O[1];
+ coeffs[63 * stride] = E[0] - O[0];
+};
+
+static void matrix_mul(int *coeffs, const ptrdiff_t stride, const int8_t* matrix, const int size, const size_t nz)
+{
+ //for dst7 and dct8, coeffs > 16 are zero out
+ int tmp[16];
+
+ for (int i = 0; i < nz; i++)
+ tmp[i] = coeffs[i * stride];
+
+ for (int i = 0; i < size; i++) {
+ int o = 0;
+
+ for (int j = 0; j < nz; j++)
+ o += tmp[j] * matrix[j * size];
+ *coeffs = o;
+ coeffs += stride;
+ matrix++;
+ }
+}
+
+static void inv_dct8(int *coeffs, const ptrdiff_t stride, const int8_t *matrix, const int size, const size_t nz)
+{
+ matrix_mul(coeffs, stride, matrix, size, nz);
+}
+
+#define DEFINE_INV_DCT8_1D(S) \
+void ff_vvc_inv_dct8_ ## S(int *coeffs, const ptrdiff_t stride, const size_t nz) \
+{ \
+ inv_dct8(coeffs, stride, &ff_vvc_dct8_##S##x##S[0][0], S, nz); \
+}
+
+DEFINE_INV_DCT8_1D( 4)
+DEFINE_INV_DCT8_1D( 8)
+DEFINE_INV_DCT8_1D(16)
+DEFINE_INV_DCT8_1D(32)
+
+static void inv_dst7(int *coeffs, const ptrdiff_t stride, const int8_t *matrix, const int size, const size_t nz)
+{
+ matrix_mul(coeffs, stride, matrix, size, nz);
+}
+
+#define DEFINE_INV_DST7_1D(S) \
+void ff_vvc_inv_dst7_ ## S(int *coeffs, const ptrdiff_t stride, const size_t nz) \
+{ \
+ inv_dst7(coeffs, stride, &ff_vvc_dst7_##S##x##S[0][0], S, nz); \
+}
+
+DEFINE_INV_DST7_1D( 4)
+DEFINE_INV_DST7_1D( 8)
+DEFINE_INV_DST7_1D(16)
+DEFINE_INV_DST7_1D(32)
+
+void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s,
+ int pred_mode_intra, int lfnst_idx, int log2_transform_range)
+{
+ int lfnst_tr_set_idx = pred_mode_intra < 0 ? 1 : ff_vvc_lfnst_tr_set_index[pred_mode_intra];
+ const int8_t *tr_mat = n_tr_s > 16 ? ff_vvc_lfnst_8x8[lfnst_tr_set_idx][lfnst_idx-1][0] : ff_vvc_lfnst_4x4[lfnst_tr_set_idx][lfnst_idx - 1][0];
+
+ for (int j = 0; j < n_tr_s; j++, tr_mat++) {
+ int t = 0;
+
+ for (int i = 0; i < no_zero_size; i++)
+ t += u[i] * tr_mat[i * n_tr_s];
+ v[j] = av_clip_intp2((t + 64) >> 7 , log2_transform_range);
+ }
+}
diff --git a/libavcodec/vvc/vvc_itx_1d.h b/libavcodec/vvc/vvc_itx_1d.h
new file mode 100644
index 0000000000..d9094f524b
--- /dev/null
+++ b/libavcodec/vvc/vvc_itx_1d.h
@@ -0,0 +1,52 @@
+/*
+ * VVC 1D transform
+ *
+ * Copyright (C) 2023 Nuo Mi
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VVC_VVC_ITX_1D_H
+#define AVCODEC_VVC_VVC_ITX_1D_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define vvc_itx_1d_fn(name) \
+ void (name)(int *coeffs, ptrdiff_t stride, size_t nz)
+typedef vvc_itx_1d_fn(*vvc_itx_1d_fn);
+
+vvc_itx_1d_fn(ff_vvc_inv_dct2_2);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_4);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_8);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_16);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_32);
+vvc_itx_1d_fn(ff_vvc_inv_dct2_64);
+vvc_itx_1d_fn(ff_vvc_inv_dst7_4);
+vvc_itx_1d_fn(ff_vvc_inv_dst7_8);
+vvc_itx_1d_fn(ff_vvc_inv_dst7_16);
+vvc_itx_1d_fn(ff_vvc_inv_dst7_32);
+vvc_itx_1d_fn(ff_vvc_inv_dct8_4);
+vvc_itx_1d_fn(ff_vvc_inv_dct8_8);
+vvc_itx_1d_fn(ff_vvc_inv_dct8_16);
+vvc_itx_1d_fn(ff_vvc_inv_dct8_32);
+
+
+void ff_vvc_inv_lfnst_1d(int *v, const int *u, int no_zero_size, int n_tr_s,
+ int pred_mode_intra, int lfnst_idx, int log2_transform_range);
+
+#endif // AVCODEC_VVC_VVC_ITX_1D_H
--
2.25.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2023-12-10 16:13 UTC | newest]
Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20231210155807.4583-1-nuomi2021@gmail.com>
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 02/14] vvcdec: add vvc_data Nuo Mi
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 03/14] vvcdec: add parameter parser for sps, pps, ph, sh Nuo Mi
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 04/14] vvcdec: add cabac decoder Nuo Mi
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 05/14] vvcdec: add reference management Nuo Mi
2023-12-10 15:57 ` [FFmpeg-devel] [PATCH v7 06/14] vvcdec: add motion vector decoder Nuo Mi
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 07/14] vvcdec: add inter prediction Nuo Mi
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 08/14] vvcdec: add inv transform 1d Nuo Mi
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 09/14] vvcdec: add intra prediction Nuo Mi
2023-12-10 15:58 ` [FFmpeg-devel] [PATCH v7 10/14] vvcdec: add LMCS, Deblocking, SAO, and ALF filters Nuo Mi
[not found] <20231210125308.1838-1-nuomi2021@gmail.com>
2023-12-10 12:53 ` [FFmpeg-devel] [PATCH v7 08/14] vvcdec: add inv transform 1d Nuo Mi
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git