Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [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 06/14] vvcdec: add motion vector decoder
       [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_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

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 06/14] vvcdec: add motion vector decoder 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