From 3cb136dc5fc70d65f9918453a842439323e81908 Mon Sep 17 00:00:00 2001
From: Nuo Mi <nuomi2021@gmail.com>
Date: Sat, 24 Dec 2022 14:05:02 +0800
Subject: [PATCH] avcodec: add vvc decoder

---
 configure                     |    1 +
 libavcodec/Makefile           |    4 +
 libavcodec/allcodecs.c        |    1 +
 libavcodec/executor.c         |  192 ++
 libavcodec/executor.h         |   53 +
 libavcodec/vvc_cabac.c        | 2407 ++++++++++++++++++++++
 libavcodec/vvc_cabac.h        |  123 ++
 libavcodec/vvc_ctu.c          | 2385 ++++++++++++++++++++++
 libavcodec/vvc_ctu.h          |  357 ++++
 libavcodec/vvc_data.c         | 3610 +++++++++++++++++++++++++++++++++
 libavcodec/vvc_data.h         |   66 +
 libavcodec/vvc_filter.c       | 1462 +++++++++++++
 libavcodec/vvc_inter.c        | 1348 ++++++++++++
 libavcodec/vvc_itx_1d.c       |  208 ++
 libavcodec/vvc_itx_1d.h       |   49 +
 libavcodec/vvc_mvs.c          | 1861 +++++++++++++++++
 libavcodec/vvc_mvs.h          |   49 +
 libavcodec/vvc_parser.c       |    4 +-
 libavcodec/vvc_ps.c           | 3328 ++++++++++++++++++++++++++++++
 libavcodec/vvc_ps.h           |  808 ++++++++
 libavcodec/vvc_refs.c         |  496 +++++
 libavcodec/vvc_thread.c       |  749 +++++++
 libavcodec/vvc_thread.h       |   77 +
 libavcodec/vvcdec.c           | 1252 ++++++++++++
 libavcodec/vvcdec.h           |  405 ++++
 libavcodec/vvcdsp.c           |  452 +++++
 libavcodec/vvcdsp.h           |  223 ++
 libavcodec/vvcdsp_template.c  | 2724 +++++++++++++++++++++++++
 libavcodec/vvcpred.c          |  321 +++
 libavcodec/vvcpred.h          |   67 +
 libavcodec/vvcpred_template.c | 1006 +++++++++
 31 files changed, 26086 insertions(+), 2 deletions(-)
 create mode 100644 libavcodec/executor.c
 create mode 100644 libavcodec/executor.h
 create mode 100644 libavcodec/vvc_cabac.c
 create mode 100644 libavcodec/vvc_cabac.h
 create mode 100644 libavcodec/vvc_ctu.c
 create mode 100644 libavcodec/vvc_ctu.h
 create mode 100644 libavcodec/vvc_data.c
 create mode 100644 libavcodec/vvc_data.h
 create mode 100644 libavcodec/vvc_filter.c
 create mode 100644 libavcodec/vvc_inter.c
 create mode 100644 libavcodec/vvc_itx_1d.c
 create mode 100644 libavcodec/vvc_itx_1d.h
 create mode 100644 libavcodec/vvc_mvs.c
 create mode 100644 libavcodec/vvc_mvs.h
 create mode 100644 libavcodec/vvc_ps.c
 create mode 100644 libavcodec/vvc_ps.h
 create mode 100644 libavcodec/vvc_refs.c
 create mode 100644 libavcodec/vvc_thread.c
 create mode 100644 libavcodec/vvc_thread.h
 create mode 100644 libavcodec/vvcdec.c
 create mode 100644 libavcodec/vvcdec.h
 create mode 100644 libavcodec/vvcdsp.c
 create mode 100644 libavcodec/vvcdsp.h
 create mode 100644 libavcodec/vvcdsp_template.c
 create mode 100644 libavcodec/vvcpred.c
 create mode 100644 libavcodec/vvcpred.h
 create mode 100644 libavcodec/vvcpred_template.c

diff --git a/configure b/configure
index c5de9a1114..cc5c75f1c9 100755
--- a/configure
+++ b/configure
@@ -2968,6 +2968,7 @@ vp6f_decoder_select="vp6_decoder"
 vp7_decoder_select="h264pred videodsp vp8dsp"
 vp8_decoder_select="h264pred videodsp vp8dsp"
 vp9_decoder_select="videodsp vp9_parser vp9_superframe_split_bsf"
+vvc_decoder_select="cabac golomb videodsp"
 wcmv_decoder_select="inflate_wrapper"
 webp_decoder_select="vp8_decoder exif"
 wmalossless_decoder_select="llauddsp"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index dab09f483a..d6e693cff1 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -425,6 +425,10 @@ OBJS-$(CONFIG_HEVC_DECODER)            += hevcdec.o hevc_mvs.o \
                                           hevc_cabac.o hevc_refs.o hevcpred.o    \
                                           hevcdsp.o hevc_filter.o hevc_data.o \
                                           h274.o
+OBJS-$(CONFIG_VVC_DECODER)             += vvcdec.o vvc_ps.o vvc_thread.o vvc_cabac.o \
+                                          vvc_ctu.o vvcdsp.o vvc_itx_1d.o vvcpred.o \
+                                          vvc_inter.o vvc_mvs.o vvc_refs.o vvc_filter.o\
+                                          executor.o vvc_data.o
 OBJS-$(CONFIG_HEVC_AMF_ENCODER)        += amfenc_hevc.o
 OBJS-$(CONFIG_HEVC_CUVID_DECODER)      += cuviddec.o
 OBJS-$(CONFIG_HEVC_MEDIACODEC_DECODER) += mediacodecdec.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index b009848a44..16255a5794 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -383,6 +383,7 @@ extern const FFCodec ff_vp9_rkmpp_decoder;
 extern const FFCodec ff_vp9_v4l2m2m_decoder;
 extern const FFCodec ff_vqa_decoder;
 extern const FFCodec ff_vqc_decoder;
+extern const FFCodec ff_vvc_decoder;
 extern const FFCodec ff_wbmp_decoder;
 extern const FFCodec ff_wbmp_encoder;
 extern const FFCodec ff_webp_decoder;
diff --git a/libavcodec/executor.c b/libavcodec/executor.c
new file mode 100644
index 0000000000..af596d43f1
--- /dev/null
+++ b/libavcodec/executor.c
@@ -0,0 +1,192 @@
+/*
+ * VVC video 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
+ */
+#include "libavutil/avutil.h"
+#include "libavutil/thread.h"
+#include "executor.h"
+
+typedef struct ThreadInfo {
+    int idx;
+    Executor *e;
+    pthread_t thread;
+} ThreadInfo;
+
+struct Executor {
+    TaskCallbacks cb;
+    ThreadInfo *threads;
+    uint8_t *local_contexts;
+    int thread_count;
+
+    pthread_mutex_t lock;
+    pthread_cond_t cond;
+    int die;
+    Task *tasks;
+};
+
+static void remove_task(Task **prev, Task *t)
+{
+    *prev  = t->next;
+    t->next = NULL;
+}
+
+static void add_task(Task **prev, Task *t)
+{
+    t->next = *prev;
+    *prev   = t;
+}
+
+static void *executor_worker_task(void *data)
+{
+    ThreadInfo *ti = (ThreadInfo*)data;
+    Executor *e = ti->e;
+    void *lc       = e->local_contexts + ti->idx * e->cb.local_context_size;
+    Task **prev;
+    TaskCallbacks *cb = &e->cb;
+
+    pthread_mutex_lock(&e->lock);
+    while (1) {
+        Task* t = NULL;
+        if (e->die) break;
+
+        for (prev = &e->tasks; *prev; prev = &(*prev)->next) {
+            if (cb->ready(*prev, cb->user_data)) {
+                t = *prev;
+                break;
+            }
+        }
+        if (t) {
+            //found one task
+            remove_task(prev, t);
+            pthread_mutex_unlock(&e->lock);
+            cb->run(t, lc, cb->user_data);
+            pthread_mutex_lock(&e->lock);
+        } else {
+            //no task in one loop
+            pthread_cond_wait(&e->cond, &e->lock);
+        }
+    }
+    pthread_mutex_unlock(&e->lock);
+    return NULL;
+}
+
+Executor* ff_executor_alloc(const TaskCallbacks *cb, int thread_count)
+{
+    Executor *e;
+    int i, j, ret;
+    if (!cb || !cb->user_data || !cb->ready || !cb->run || !cb->priority_higher)
+        return NULL;
+    e = av_calloc(1, sizeof(*e));
+    if (!e)
+        return NULL;
+    e->cb = *cb;
+
+    e->local_contexts = av_malloc(thread_count * e->cb.local_context_size);
+    if (!e->local_contexts)
+        goto free_executor;
+
+    e->threads = av_calloc(thread_count, sizeof(*e->threads));
+    if (!e->threads)
+        goto free_contexts;
+    for (i = 0; i < thread_count; i++) {
+        ThreadInfo *ti = e->threads + i;
+        ti->e = e;
+        ti->idx = i;
+    }
+
+    ret = pthread_mutex_init(&e->lock, NULL);
+    if (ret)
+        goto free_threads;
+
+    ret = pthread_cond_init(&e->cond, NULL);
+    if (ret)
+        goto destroy_lock;
+
+    for (i = 0; i < thread_count; i++) {
+        ThreadInfo *ti = e->threads + i;
+        ret = pthread_create(&ti->thread, NULL, executor_worker_task, ti);
+        if (ret)
+            goto join_threads;
+    }
+    e->thread_count = thread_count;
+    return e;
+
+join_threads:
+    pthread_mutex_lock(&e->lock);
+    e->die = 1;
+    pthread_cond_broadcast(&e->cond);
+    pthread_mutex_unlock(&e->lock);
+    for (j = 0; j < i; j++)
+        pthread_join(e->threads[j].thread, NULL);
+    pthread_cond_destroy(&e->cond);
+destroy_lock:
+    pthread_mutex_destroy(&e->lock);
+free_threads:
+    av_free(e->threads);
+free_contexts:
+    av_free(e->local_contexts);
+free_executor:
+    free(e);
+    return NULL;
+}
+
+void ff_executor_free(Executor **executor)
+{
+    Executor *e;
+    if (!executor || !*executor)
+        return;
+    e = *executor;
+
+    //singal die
+    pthread_mutex_lock(&e->lock);
+    e->die = 1;
+    pthread_cond_broadcast(&e->cond);
+    pthread_mutex_unlock(&e->lock);
+
+    for (int i = 0; i < e->thread_count; i++)
+        pthread_join(e->threads[i].thread, NULL);
+    pthread_cond_destroy(&e->cond);
+    pthread_mutex_destroy(&e->lock);
+
+    av_free(e->threads);
+    av_free(e->local_contexts);
+
+    av_freep(executor);
+}
+
+void ff_executor_execute(Executor *e, Task *t)
+{
+    TaskCallbacks *cb = &e->cb;
+    Task **prev;
+
+    pthread_mutex_lock(&e->lock);
+    for (prev = &e->tasks; *prev && cb->priority_higher(*prev, t); prev = &(*prev)->next)
+        /* nothing */;
+    add_task(prev, t);
+    pthread_cond_signal(&e->cond);
+    pthread_mutex_unlock(&e->lock);
+}
+
+void ff_executor_wakeup(Executor *e)
+{
+    pthread_mutex_lock(&e->lock);
+    pthread_cond_broadcast(&e->cond);
+    pthread_mutex_unlock(&e->lock);
+}
diff --git a/libavcodec/executor.h b/libavcodec/executor.h
new file mode 100644
index 0000000000..0370629741
--- /dev/null
+++ b/libavcodec/executor.h
@@ -0,0 +1,53 @@
+/*
+ * VVC video 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_EXECUTOR_H
+#define AVCODEC_EXECUTOR_H
+
+typedef struct Executor Executor;
+typedef struct Task Task;
+
+struct Task {
+    Task *next;
+};
+
+typedef struct TaskCallbacks {
+    void *user_data;
+
+    size_t local_context_size;
+
+    // return 1 if a's priority > b's priority
+    int (*priority_higher)(const Task *a, const Task *b);
+
+    // task is ready for run
+    int (*ready)(const Task *t, void *user_data);
+
+    // run the task
+    int (*run)(Task *t, void *local_context, void *user_data);
+} TaskCallbacks;
+
+Executor* ff_executor_alloc(const TaskCallbacks *callbacks, int thread_count);
+void ff_executor_free(Executor **e);
+void ff_executor_execute(Executor *e, Task *t);
+void ff_executor_wakeup(Executor *e);
+
+#endif
diff --git a/libavcodec/vvc_cabac.c b/libavcodec/vvc_cabac.c
new file mode 100644
index 0000000000..15f45a30d6
--- /dev/null
+++ b/libavcodec/vvc_cabac.c
@@ -0,0 +1,2407 @@
+/*
+ * 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 "libavutil/attributes.h"
+#include "libavutil/common.h"
+#include "cabac_functions.h"
+#include "vvc.h"
+#include "vvc_cabac.h"
+#include "vvc_ctu.h"
+#include "vvc_data.h"
+#include "vvc_itx_1d.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              = 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
+         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
+         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
+         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
+         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 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 VVCSH *sh = &lc->sc->sh;
+    const int qp    = av_clip_uintp2(sh->slice_qp_y, 6);
+    int init_type   = 2 - sh->slice_type;
+
+    av_assert0(VVC_CONTEXTS == SYNTAX_ELEMENT_LAST);
+
+    if (sh->cabac_init_flag && sh->slice_type != VVC_SLICE_TYPE_I)
+        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 ctb_addr,
+    const int ctb_addr_x, const int ctb_addr_y)
+{
+    int ret = 0;
+    const VVCPPS *pps               = lc->fc->ps.pps;
+    const int first_ctb_in_slice    = !ctb_addr;
+    const int first_ctb_in_tile     = ctb_addr_x == pps->ctb_to_col_bd[ctb_addr_x] && ctb_addr_y == pps->ctb_to_row_bd[ctb_addr_y];
+
+    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)
+
+static av_always_inline int vvc_get_cabac_bypass(CABACContext *c)
+{
+    return get_cabac_bypass(c);
+}
+
+//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) | vvc_get_cabac_bypass(&lc->ep->cc);
+    if (v >= u) {
+        v = (v << 1) | vvc_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) && vvc_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) + vvc_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 (!vvc_get_cabac_bypass(&lc->ep->cc))
+        return SAO_BAND;
+    return SAO_EDGE;
+}
+
+int ff_vvc_sao_band_position_decode(VVCLocalContext *lc)
+{
+    int i;
+    int value = vvc_get_cabac_bypass(&lc->ep->cc);
+
+    for (i = 0; i < 4; i++)
+        value = (value << 1) | vvc_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 && vvc_get_cabac_bypass(&lc->ep->cc))
+        i++;
+    return i;
+}
+
+int ff_vvc_sao_offset_sign_decode(VVCLocalContext *lc)
+{
+    return vvc_get_cabac_bypass(&lc->ep->cc);
+}
+
+int ff_vvc_sao_eo_class_decode(VVCLocalContext *lc)
+{
+    int ret = vvc_get_cabac_bypass(&lc->ep->cc) << 1;
+    ret    |= vvc_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.alf.num_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 && vvc_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_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 vvc_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 && vvc_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 vvc_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 (vvc_get_cabac_bypass(&lc->ep->cc) << 1) | vvc_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 && vvc_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 && vvc_get_cabac_bypass(&lc->ep->cc); i++)
+        /* nothing */;
+    return i;
+}
+
+static int mmvd_direction_idx_decode(VVCLocalContext *lc)
+{
+    return (vvc_get_cabac_bypass(&lc->ep->cc) << 1) | vvc_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 && vvc_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) | vvc_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 && vvc_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 && vvc_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 vvc_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 && vvc_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 && vvc_get_cabac_bypass(&lc->ep->cc); k++)
+        /* nothing */;
+    i = (1 << k) - 1;
+    v = 0;
+    while (k--)
+        v = (v << 1) + vvc_get_cabac_bypass(&lc->ep->cc);
+    v += i;
+
+    return v + 5;
+}
+
+int ff_vvc_cu_qp_delta_sign_flag(VVCLocalContext *lc)
+{
+    return vvc_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->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)
+{
+    int i;
+    const int length = (last_significant_coeff_y_prefix >> 1) - 1;
+    int value = vvc_get_cabac_bypass(&lc->ep->cc);
+
+    for (i = 1; i < length; i++)
+        value = (value << 1) | vvc_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)
+{
+    int loc_sum = 0;
+    level += w * yc + xc;
+    if (xc < w - 1) {
+        loc_sum += level[1];
+        if (xc < w - 2)
+            loc_sum += level[2];
+        if (yc < h - 1)
+            loc_sum += level[w + 1];
+    }
+    if (yc < h - 1) {
+        loc_sum += level[w];
+        if (yc < h - 2)
+            loc_sum += level[w << 1];
+    }
+    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);
+        const int loc_sum_abs_pass1 = get_local_sum(rc->abs_level_pass1, tb->tb_width, tb->tb_height, xc, yc);
+        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 TransformBlock *tb = rc->tb;
+    const int w = rc->width_in_sbs;
+    const int h = rc->height_in_sbs;
+    int inc;
+
+    if (tb->ts) {
+        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 TransformBlock *tb = rc->tb;
+    int inc;
+
+    if (tb->ts) {
+        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);
+
+        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(const ResidualCoding* rc, const int xc, const int yc, const int base_level)
+{
+    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;
+
+    loc_sum_abs = get_local_sum(rc->abs_level, tb->tb_width, tb->tb_height, xc, yc);
+    loc_sum_abs = av_clip_uintp2(loc_sum_abs - base_level * 5, 5);
+    return rice_params[loc_sum_abs];
+}
+
+static int abs_decode(VVCLocalContext *lc, const int c_rice_param)
+{
+    const int MAX_BIN = 6;
+    int prefix = 0;
+    int suffix = 0;
+    int i;
+
+    while (prefix < MAX_BIN && vvc_get_cabac_bypass(&lc->ep->cc))
+        prefix++;
+    if (prefix < MAX_BIN) {
+        for (i = 0; i < c_rice_param; i++) {
+            suffix = (suffix << 1) | vvc_get_cabac_bypass(&lc->ep->cc);
+        }
+    } else {
+        suffix = limited_kth_order_egk_decode(&lc->ep->cc, c_rice_param + 1, 11, 15);
+    }
+    return suffix + (prefix << c_rice_param);
+}
+
+static int abs_remainder_decode(VVCLocalContext *lc, const ResidualCoding* rc, const int xc, const int yc)
+{
+    const int c_rice_param = abs_get_rice_param(rc, xc, yc, 4);
+    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 int c_rice_param = 1;
+    const int rem = abs_decode(lc, c_rice_param);
+    return rem;
+}
+
+static int coeff_sign_flag_decode(VVCLocalContext *lc)
+{
+    return vvc_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 abs_level_decode(VVCLocalContext *lc, const ResidualCoding *rc, const int xc, const int yc)
+{
+    const int c_rice_param = abs_get_rice_param(rc, xc, yc, 0);
+    int abs_dec_level =  abs_decode(lc, c_rice_param);
+    const int zero_pos = (rc->qstate < 2 ? 1 : 2) << c_rice_param;
+    if (abs_dec_level != zero_pos) {
+        if (abs_dec_level < zero_pos)
+            abs_dec_level++;
+    } else {
+        abs_dec_level = 0;
+    }
+    return abs_dec_level;
+}
+
+static void init_residual_coding(ResidualCoding *rc, const int log2_zo_tb_width, const int log2_zo_tb_height, TransformBlock *tb)
+{
+    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->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;
+
+    if (!tb->ts) {
+        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(&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 VVCSH *sh     = &lc->sc->sh;
+    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 (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])
+            *abs_level += abs_remainder_decode(lc, rc, xc, yc) << 1;
+    }
+    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;
+
+        *abs_level = abs_level_decode(lc, rc, xc, yc);
+        if (*abs_level > 0) {
+            if (last_sig_scan_pos_sb == -1)
+                last_sig_scan_pos_sb = n;
+            first_sig_scan_pos_sb = n;
+        }
+        if (sh->dep_quant_used_flag)
+            rc->qstate = qstate_translate_table[rc->qstate][*abs_level & 1];
+    }
+    sig_hidden_flag = sh->sign_data_hiding_used_flag &&
+        (last_sig_scan_pos_sb - first_sig_scan_pos_sb > 3 ? 1 : 0);
+
+    if (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 (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 (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 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;
+    }
+    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->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->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(&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 VVCSH *sh     = &lc->sc->sh;
+    const int ts        = !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_cabac.h b/libavcodec/vvc_cabac.h
new file mode 100644
index 0000000000..ccf246dc72
--- /dev/null
+++ b/libavcodec/vvc_cabac.h
@@ -0,0 +1,123 @@
+/*
+ * 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_CABAC_H
+#define AVCODEC_VVC_CABAC_H
+
+#include "vvc_ctu.h"
+
+int ff_vvc_cabac_init(VVCLocalContext *lc, int ctb_addr, int ctb_addr_x, int ctb_addr_y);
+
+//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_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_CABAC_H
diff --git a/libavcodec/vvc_ctu.c b/libavcodec/vvc_ctu.c
new file mode 100644
index 0000000000..b1dc880a22
--- /dev/null
+++ b/libavcodec/vvc_ctu.c
@@ -0,0 +1,2385 @@
+/*
+ * VVC CTU 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
+ */
+
+#include "vvc_cabac.h"
+#include "vvc_ctu.h"
+#include "vvc_data.h"
+#include "vvc_itx_1d.h"
+#include "vvc_mvs.h"
+
+#define PROF_TEMP_SIZE (PROF_BLOCK_SIZE) * sizeof(int16_t)
+
+#define TAB_MSM(fc, depth, x, y) fc->tab.msm[(depth)][((y) >> 5) * fc->ps.pps->width32 + ((x) >> 5)]
+#define TAB_ISPMF(fc, x, y) fc->tab.ispmf[((y) >> 6) * fc->ps.pps->width64 + ((x) >> 6)]
+
+typedef enum VVCModeType {
+    MODE_TYPE_ALL,
+    MODE_TYPE_INTER,
+    MODE_TYPE_INTRA,
+} VVCModeType;
+
+static void set_tb_pos(const VVCFrameContext *fc, const TransformBlock *tb)
+{
+    const int x_tb      = tb->x0 >> MIN_TB_LOG2;
+    const int y_tb      = tb->y0 >> MIN_TB_LOG2;
+    const int hs        = fc->ps.sps->hshift[tb->c_idx];
+    const int vs        = fc->ps.sps->vshift[tb->c_idx];
+    const int is_chroma = tb->c_idx != 0;
+    const int width     = FFMAX(1, tb->tb_width >> (MIN_TB_LOG2 - hs));
+    const int end       = y_tb + FFMAX(1, tb->tb_height >> (MIN_TB_LOG2 - vs));
+
+    for (int y = y_tb; y < end; y++) {
+        const int off = y * fc->ps.pps->min_tb_width + x_tb;
+        for (int i = 0; i < width; i++) {
+            fc->tab.tb_pos_x0[is_chroma][off + i] = tb->x0;
+            fc->tab.tb_pos_y0[is_chroma][off + i] = tb->y0;
+        }
+        memset(fc->tab.tb_width [is_chroma] + off, tb->tb_width,  width);
+        memset(fc->tab.tb_height[is_chroma] + off, tb->tb_height, width);
+    }
+}
+
+
+static void set_tb_tab(uint8_t *tab, uint8_t v, const VVCFrameContext *fc,
+    const TransformBlock *tb)
+{
+    const int width  = tb->tb_width  << fc->ps.sps->hshift[tb->c_idx];
+    const int height = tb->tb_height << fc->ps.sps->vshift[tb->c_idx];
+
+    for (int h = 0; h < height; h += MIN_TB_SIZE) {
+        const int y = (tb->y0 + h) >> MIN_TB_LOG2;
+        const int off = y * fc->ps.pps->min_tb_width + (tb->x0 >> MIN_TB_LOG2);
+        const int w = FFMAX(1, width >> MIN_TB_LOG2);
+        memset(tab + off, v, w);
+    }
+}
+
+// 8.7.1 Derivation process for quantization parameters
+static int get_qp_y_pred(const VVCLocalContext *lc)
+{
+    const VVCFrameContext *fc     = lc->fc;
+    const VVCSPS *sps       = fc->ps.sps;
+    const VVCPPS *pps       = fc->ps.pps;
+    const CodingUnit *cu    = lc->cu;
+    const int ctb_log2_size = sps->ctb_log2_size_y;
+    const int ctb_size_mask = (1 << ctb_log2_size) - 1;
+    const int xQg           = lc->parse.cu_qg_top_left_x;
+    const int yQg           = lc->parse.cu_qg_top_left_y;
+    const int min_cb_width  = fc->ps.pps->min_cb_width;
+    const int x_cb          = cu->x0 >> sps->min_cb_log2_size_y;
+    const int y_cb          = cu->y0 >> sps->min_cb_log2_size_y;
+    const int x_ctb         = cu->x0 >> ctb_log2_size;
+    const int y_ctb         = cu->y0 >> ctb_log2_size;
+    const int in_same_ctb_a = ((xQg - 1) >> ctb_log2_size) == x_ctb && (yQg >> ctb_log2_size) == y_ctb;
+    const int in_same_ctb_b = (xQg >> ctb_log2_size) == x_ctb && ((yQg - 1) >> ctb_log2_size) == y_ctb;
+    int qPy_pred, qPy_a, qPy_b;
+
+    if (lc->na.cand_up) {
+        const int first_qg_in_ctu = !(xQg & ctb_size_mask) &&  !(yQg & ctb_size_mask);
+        const int qPy_up          = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width];
+        if (first_qg_in_ctu && pps->ctb_to_col_bd[xQg >> ctb_log2_size] == xQg)
+            return qPy_up;
+    }
+
+    // qPy_pred
+    qPy_pred = lc->ep->is_first_qg ? lc->sc->sh.slice_qp_y : lc->ep->qp_y;
+
+    // qPy_b
+    if (!lc->na.cand_up || !in_same_ctb_b)
+        qPy_b = qPy_pred;
+    else
+        qPy_b = fc->tab.qp[LUMA][x_cb + (y_cb - 1) * min_cb_width];
+
+    // qPy_a
+    if (!lc->na.cand_left || !in_same_ctb_a)
+        qPy_a = qPy_pred;
+    else
+        qPy_a = fc->tab.qp[LUMA][(x_cb - 1) + y_cb * min_cb_width];
+
+    av_assert2(qPy_a >= -fc->ps.sps->qp_bd_offset && qPy_a < 63);
+    av_assert2(qPy_b >= -fc->ps.sps->qp_bd_offset && qPy_b < 63);
+
+    return (qPy_a + qPy_b + 1) >> 1;
+}
+
+static int set_qp_y(VVCLocalContext *lc, const int x0, const int y0, const int has_qp_delta)
+{
+    const VVCSPS *sps   = lc->fc->ps.sps;
+    EntryPoint *ep      = lc->ep;
+    CodingUnit *cu      = lc->cu;
+    int cu_qp_delta     = 0;
+
+    if (!lc->fc->ps.pps->cu_qp_delta_enabled_flag) {
+        ep->qp_y = lc->sc->sh.slice_qp_y;
+    } else if (ep->is_first_qg || (lc->parse.cu_qg_top_left_x == x0 && lc->parse.cu_qg_top_left_y == y0)) {
+        ep->qp_y = get_qp_y_pred(lc);
+        ep->is_first_qg = 0;
+    }
+
+    if (has_qp_delta) {
+        const int cu_qp_delta_abs = ff_vvc_cu_qp_delta_abs(lc);
+
+        if (cu_qp_delta_abs)
+            cu_qp_delta = ff_vvc_cu_qp_delta_sign_flag(lc) ? -cu_qp_delta_abs : cu_qp_delta_abs;
+        if (cu_qp_delta > (31 + sps->qp_bd_offset / 2) || cu_qp_delta < -(32 + sps->qp_bd_offset / 2))
+            return AVERROR_INVALIDDATA;
+        lc->parse.is_cu_qp_delta_coded = 1;
+
+        if (cu_qp_delta) {
+            int off = sps->qp_bd_offset;
+            ep->qp_y = FFUMOD(ep->qp_y + cu_qp_delta + 64 + 2 * off, 64 + off) - off;
+        }
+    }
+
+    ff_vvc_set_cb_tab(lc, lc->fc->tab.qp[LUMA], ep->qp_y);
+    cu->qp[LUMA] = ep->qp_y;
+
+    return 0;
+}
+
+static void set_qp_c_tab(const VVCLocalContext *lc, const TransformUnit *tu, const TransformBlock *tb)
+{
+    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;
+
+    set_tb_tab(lc->fc->tab.qp[tb->c_idx], lc->cu->qp[idx], lc->fc, tb);
+}
+
+static void set_qp_c(VVCLocalContext *lc)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    const VVCSPS *sps           = fc->ps.sps;
+    const VVCPPS *pps           = fc->ps.pps;
+    const VVCSH *sh             = &lc->sc->sh;
+    CodingUnit *cu              = lc->cu;
+    const int x_center          = cu->x0 + cu->cb_width  / 2;
+    const int y_center          = cu->y0 + cu->cb_height / 2;
+    const int single_tree       = cu->tree_type == SINGLE_TREE;
+    const int qp_luma           = (single_tree ? lc->ep->qp_y : ff_vvc_get_qPy(fc, x_center, y_center)) + sps->qp_bd_offset;
+    const int qp_chroma         = av_clip(qp_luma, 0, MAX_QP + sps->qp_bd_offset);
+    int qp;
+
+    for (int i = CB - 1; i < CR + sps->joint_cbcr_enabled_flag; i++) {
+        qp = sps->chroma_qp_table[i][qp_chroma];
+        qp = qp + pps->chroma_qp_offset[i] + sh->chroma_qp_offset[i] + lc->parse.chroma_qp_offset[i];
+        qp = av_clip(qp, -sps->qp_bd_offset, MAX_QP) + sps->qp_bd_offset;
+        cu->qp[i + 1] = qp;
+    }
+}
+
+static TransformUnit* add_tu(CodingUnit *cu, const int x0, const int y0, const int tu_width, const int tu_height)
+{
+    TransformUnit *tu;
+
+    if (cu->num_tus >= FF_ARRAY_ELEMS(cu->tus))
+        return NULL;
+
+    tu = &cu->tus[cu->num_tus];
+    tu->x0 = x0;
+    tu->y0 = y0;
+    tu->width = tu_width;
+    tu->height = tu_height;
+    tu->joint_cbcr_residual_flag = 0;
+    memset(tu->coded_flag, 0, sizeof(tu->coded_flag));
+    tu->nb_tbs = 0;
+    cu->num_tus++;
+
+    return tu;
+}
+
+static TransformBlock* add_tb(TransformUnit *tu, VVCLocalContext *lc,
+    const int x0, const int y0, const int tb_width, const int tb_height, const int c_idx)
+{
+    TransformBlock *tb;
+
+    tb = &tu->tbs[tu->nb_tbs++];
+    tb->has_coeffs = 0;
+    tb->x0 = x0;
+    tb->y0 = y0;
+    tb->tb_width  = tb_width;
+    tb->tb_height = tb_height;
+    tb->log2_tb_width  = log2(tb_width);
+    tb->log2_tb_height = log2(tb_height);
+
+    tb->max_scan_x = tb->max_scan_y = 0;
+    tb->min_scan_x = tb->min_scan_y = 0;
+
+    tb->c_idx = c_idx;
+    tb->ts = 0;
+    tb->coeffs = lc->coeffs;
+    lc->coeffs += tb_width * tb_height;
+    return tb;
+}
+
+static uint8_t tu_y_coded_flag_decode(VVCLocalContext *lc, const int is_sbt_not_coded,
+    const int sub_tu_index, const int is_isp, const int is_chroma_coded)
+{
+    uint8_t tu_y_coded_flag = 0;
+    const VVCSPS *sps       = lc->fc->ps.sps;
+    CodingUnit *cu          = lc->cu;
+
+    if (!is_sbt_not_coded) {
+        int has_y_coded_flag = sub_tu_index < cu->num_intra_subpartitions - 1 || !lc->parse.infer_tu_cbf_luma;
+        if (!is_isp) {
+            const int is_large = cu->cb_width > sps->max_tb_size_y || cu->cb_height > sps->max_tb_size_y;
+            has_y_coded_flag = (cu->pred_mode == MODE_INTRA && !cu->act_enabled_flag) || is_chroma_coded || is_large;
+        }
+        tu_y_coded_flag = has_y_coded_flag ? ff_vvc_tu_y_coded_flag(lc) : 1;
+    }
+    if (is_isp)
+        lc->parse.infer_tu_cbf_luma = lc->parse.infer_tu_cbf_luma && !tu_y_coded_flag;
+    return tu_y_coded_flag;
+}
+
+static void chroma_qp_offset_decode(VVCLocalContext *lc, const int is_128, const int is_chroma_coded)
+{
+    const VVCPPS *pps   = lc->fc->ps.pps;
+    const VVCSH *sh     = &lc->sc->sh;
+
+    if ((is_128 || is_chroma_coded) &&
+        sh->cu_chroma_qp_offset_enabled_flag && !lc->parse.is_cu_chroma_qp_offset_coded) {
+        const int cu_chroma_qp_offset_flag = ff_vvc_cu_chroma_qp_offset_flag(lc);
+        if (cu_chroma_qp_offset_flag) {
+            int cu_chroma_qp_offset_idx = 0;
+            if (pps->chroma_qp_offset_list_len_minus1 > 0)
+                cu_chroma_qp_offset_idx = ff_vvc_cu_chroma_qp_offset_idx(lc);
+            for (int i = CB - 1; i < JCBCR; i++)
+                lc->parse.chroma_qp_offset[i] = pps->chroma_qp_offset_list[cu_chroma_qp_offset_idx][i];
+        } else {
+            memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset));
+        }
+        lc->parse.is_cu_chroma_qp_offset_coded = 1;
+    }
+}
+
+static int hls_transform_unit(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int sub_tu_index, int ch_type)
+{
+    VVCFrameContext *fc = lc->fc;
+    const VVCSPS *sps   = fc->ps.sps;
+    const VVCPPS *pps   = fc->ps.pps;
+    CodingUnit *cu      = lc->cu;
+    TransformUnit *tu   = add_tu(cu, x0, y0, tu_width, tu_height);
+    const int min_cb_width      = pps->min_cb_width;
+    const VVCTreeType tree_type = cu->tree_type;
+    const int is_128            = cu->cb_width > 64 || cu->cb_height > 64;
+    const int is_isp            = cu->isp_split_type != ISP_NO_SPLIT;
+    const int is_isp_last_tu    = is_isp && (sub_tu_index == cu->num_intra_subpartitions - 1);
+    const int is_sbt_not_coded  = cu->sbt_flag &&
+        ((sub_tu_index == 0 && cu->sbt_pos_flag) || (sub_tu_index == 1 && !cu->sbt_pos_flag));
+    const int chroma_available  = tree_type != DUAL_TREE_LUMA && sps->chroma_format_idc &&
+        (!is_isp || is_isp_last_tu);
+    int ret, xc, yc, wc, hc, is_chroma_coded;
+
+    if (!tu)
+        return AVERROR_INVALIDDATA;
+
+    if (tree_type == SINGLE_TREE && is_isp_last_tu) {
+        const int x_cu = x0 >> fc->ps.sps->min_cb_log2_size_y;
+        const int y_cu = y0 >> fc->ps.sps->min_cb_log2_size_y;
+        xc = SAMPLE_CTB(fc->tab.cb_pos_x[ch_type],  x_cu, y_cu);
+        yc = SAMPLE_CTB(fc->tab.cb_pos_y[ch_type],  x_cu, y_cu);
+        wc = SAMPLE_CTB(fc->tab.cb_width[ch_type],  x_cu, y_cu);
+        hc = SAMPLE_CTB(fc->tab.cb_height[ch_type], x_cu, y_cu);
+    } else {
+        xc = x0, yc = y0, wc = tu_width, hc = tu_height;
+    }
+
+    if (chroma_available && !is_sbt_not_coded) {
+        tu->coded_flag[CB] = ff_vvc_tu_cb_coded_flag(lc);
+        tu->coded_flag[CR] = ff_vvc_tu_cr_coded_flag(lc, tu->coded_flag[CB]);
+    }
+
+    is_chroma_coded = chroma_available && (tu->coded_flag[CB] || tu->coded_flag[CR]);
+
+    if (tree_type != DUAL_TREE_CHROMA) {
+        int has_qp_delta;
+        tu->coded_flag[LUMA] = tu_y_coded_flag_decode(lc, is_sbt_not_coded, sub_tu_index, is_isp, is_chroma_coded);
+        has_qp_delta = (is_128 || tu->coded_flag[LUMA] || is_chroma_coded) &&
+            pps->cu_qp_delta_enabled_flag && !lc->parse.is_cu_qp_delta_coded;
+        ret = set_qp_y(lc, x0, y0, has_qp_delta);
+        if (ret < 0)
+            return ret;
+        add_tb(tu, lc, x0, y0, tu_width, tu_height, LUMA);
+    }
+    if (tree_type != DUAL_TREE_LUMA) {
+        chroma_qp_offset_decode(lc, is_128, is_chroma_coded);
+        if (chroma_available) {
+            const int hs = sps->hshift[CHROMA];
+            const int vs = sps->vshift[CHROMA];
+            add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CB);
+            add_tb(tu, lc, xc, yc, wc >> hs, hc >> vs, CR);
+        }
+    }
+    if (sps->joint_cbcr_enabled_flag && ((cu->pred_mode == MODE_INTRA &&
+        (tu->coded_flag[CB] || tu->coded_flag[CR])) ||
+        (tu->coded_flag[CB] && tu->coded_flag[CR])) &&
+        chroma_available) {
+        tu->joint_cbcr_residual_flag = ff_vvc_tu_joint_cbcr_residual_flag(lc, tu->coded_flag[1], tu->coded_flag[2]);
+    }
+
+    for (int i = 0; i < tu->nb_tbs; i++) {
+        TransformBlock *tb  = &tu->tbs[i];
+        const int is_chroma = tb->c_idx != LUMA;
+        tb->has_coeffs = tu->coded_flag[tb->c_idx];
+        if (tb->has_coeffs && is_chroma)
+            tb->has_coeffs = tb->c_idx == CB ? 1 : !(tu->coded_flag[CB] && tu->joint_cbcr_residual_flag);
+        if (tb->has_coeffs) {
+            tb->ts = cu->bdpcm_flag[i];
+            if (sps->transform_skip_enabled_flag && !cu->bdpcm_flag[i] &&
+                tb->tb_width <= sps->max_ts_size && tb->tb_height <= sps->max_ts_size &&
+                !cu->sbt_flag && (is_chroma || !is_isp)) {
+                tb->ts = ff_vvc_transform_skip_flag(lc, is_chroma);
+            }
+            ret = ff_vvc_residual_coding(lc, tb);
+            if (ret < 0)
+                return ret;
+            set_tb_tab(fc->tab.tu_coded_flag[tb->c_idx], tu->coded_flag[tb->c_idx], fc, tb);
+        }
+        if (tb->c_idx != CR)
+            set_tb_pos(fc, tb);
+        if (tb->c_idx == CB)
+            set_tb_tab(fc->tab.tu_joint_cbcr_residual_flag, tu->joint_cbcr_residual_flag, fc, tb);
+    }
+
+    return 0;
+}
+
+static int hls_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height, int ch_type)
+{
+    const CodingUnit *cu = lc->cu;
+    const VVCSPS *sps = lc->fc->ps.sps;
+    int ret;
+
+    lc->parse.infer_tu_cbf_luma = 1;
+    if (cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag) {
+        if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) {
+            const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height;
+            const int trafo_width  =  ver_split_first ? (tu_width  / 2) : tu_width;
+            const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height;
+
+            #define TRANSFORM_TREE(x, y) do {                                           \
+                ret = hls_transform_tree(lc, x, y, trafo_width, trafo_height, ch_type);  \
+                if (ret < 0)                                                            \
+                    return ret;                                                         \
+            } while (0)
+
+            TRANSFORM_TREE(x0, y0);
+            if (ver_split_first)
+                TRANSFORM_TREE(x0 + trafo_width, y0);
+            else
+                TRANSFORM_TREE(x0, y0 + trafo_height);
+
+        } else {
+            ret = hls_transform_unit(lc, x0, y0, tu_width, tu_height, 0, ch_type);
+            if (ret < 0)
+                return ret;
+
+        }
+    } else if (cu->sbt_flag) {
+        if (!cu->sbt_horizontal_flag) {
+            #define TRANSFORM_UNIT(x, width, idx) do {                              \
+                ret = hls_transform_unit(lc, x, y0, width, tu_height, idx, ch_type); \
+                if (ret < 0)                                                        \
+                    return ret;                                                     \
+            } while (0)
+
+            const int trafo_width = tu_width * lc->parse.sbt_num_fourths_tb0 / 4;
+            TRANSFORM_UNIT(x0, trafo_width, 0);
+            TRANSFORM_UNIT(x0 + trafo_width, tu_width - trafo_width, 1);
+
+            #undef TRANSFORM_UNIT
+        } else {
+            #define TRANSFORM_UNIT(y, height, idx) do {                             \
+                ret = hls_transform_unit(lc, x0, y, tu_width, height, idx, ch_type); \
+                if (ret < 0)                                                        \
+                    return ret;                                                     \
+            } while (0)
+
+            const int trafo_height = tu_height * lc->parse.sbt_num_fourths_tb0 / 4;
+            TRANSFORM_UNIT(y0, trafo_height, 0);
+            TRANSFORM_UNIT(y0 + trafo_height, tu_height - trafo_height, 1);
+
+            #undef TRANSFORM_UNIT
+        }
+    } else if (cu->isp_split_type == ISP_HOR_SPLIT) {
+        const int trafo_height = tu_height / cu->num_intra_subpartitions;
+        for (int i = 0; i < cu->num_intra_subpartitions; i++) {
+            ret = hls_transform_unit(lc, x0, y0 + trafo_height * i, tu_width, trafo_height, i, 0);
+            if (ret < 0)
+                return ret;
+        }
+    } else if (cu->isp_split_type == ISP_VER_SPLIT) {
+        const int trafo_width = tu_width / cu->num_intra_subpartitions;
+        for (int i = 0; i < cu->num_intra_subpartitions; i++) {
+            ret = hls_transform_unit(lc, x0 + trafo_width * i , y0, trafo_width, tu_height, i, 0);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
+    return 0;
+}
+
+static int skipped_transform_tree(VVCLocalContext *lc, int x0, int y0,int tu_width, int tu_height)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    const VVCSPS *sps           = fc->ps.sps;
+
+    if (tu_width > sps->max_tb_size_y || tu_height > sps->max_tb_size_y) {
+        const int ver_split_first = tu_width > sps->max_tb_size_y && tu_width > tu_height;
+        const int trafo_width  =  ver_split_first ? (tu_width  / 2) : tu_width;
+        const int trafo_height = !ver_split_first ? (tu_height / 2) : tu_height;
+
+        #define SKIPPED_TRANSFORM_TREE(x, y) do {                                   \
+            int ret = skipped_transform_tree(lc, x, y, trafo_width, trafo_height);  \
+            if (ret < 0)                                                            \
+                return ret;                                                         \
+        } while (0)
+
+        SKIPPED_TRANSFORM_TREE(x0, y0);
+        if (ver_split_first)
+            SKIPPED_TRANSFORM_TREE(x0 + trafo_width, y0);
+        else
+            SKIPPED_TRANSFORM_TREE(x0, y0 + trafo_height);
+    } else {
+        TransformUnit *tu = add_tu(lc->cu, x0, y0, tu_width, tu_height);
+        const int c_end = sps->chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : (LUMA + 1);
+        if (!tu)
+            return AVERROR_INVALIDDATA;
+        for (int i = LUMA; i < c_end; i++) {
+            TransformBlock *tb = add_tb(tu, lc, x0, y0, tu_width >> sps->hshift[i], tu_height >> sps->vshift[i], i);
+            if (i != CR)
+                set_tb_pos(fc, tb);
+        }
+    }
+
+    return 0;
+}
+
+//6.4.1 Allowed quad split process
+//6.4.2 Allowed binary split process
+//6.4.3 Allowed ternary split process
+static void can_split(const VVCLocalContext *lc, int x0, int y0,int cb_width, int cb_height,
+     int mtt_depth, int depth_offset, int part_idx, VVCSplitMode last_split_mode,
+     VVCTreeType tree_type, VVCModeType mode_type, VVCAllowedSplit* split)
+{
+    int min_qt_size, max_bt_size, max_tt_size, max_mtt_depth;
+    const VVCFrameContext *fc   = lc->fc;
+    const VVCSH *sh             = &lc->sc->sh;
+    const VVCSPS *sps           = fc->ps.sps;
+    const VVCPPS *pps           = fc->ps.pps;
+    const int chroma            = tree_type == DUAL_TREE_CHROMA;
+    int min_cb_size_y           = sps->min_cb_size_y;
+    int *qt                     = &split->qt;
+    int *btv                    = &split->btv;
+    int *bth                    = &split->bth;
+    int *ttv                    = &split->ttv;
+    int *tth                    = &split->tth;
+
+    *qt = *bth = *btv = *tth = *ttv = 1;
+
+    if (mtt_depth)
+        *qt = 0;
+
+    min_qt_size = sh->min_qt_size[chroma];
+    if (cb_width <= min_qt_size)
+        *qt = 0;
+
+    if (chroma) {
+        int chroma_area = (cb_width >> sps->hshift[1]) * (cb_height >> sps->vshift[1]);
+        int chroma_width = cb_width >> sps->hshift[1];
+
+        if (chroma_width == 8)
+            *ttv = 0;
+        else if (chroma_width <= 4) {
+            if (chroma_width == 4)
+                *btv = 0;
+            *qt = 0;
+        }
+        if (mode_type == MODE_TYPE_INTRA)
+            *qt = *btv = *bth = *ttv = *tth = 0;
+        if (chroma_area <= 32) {
+            *ttv = *tth = 0;
+            if (chroma_area <= 16)
+                *btv = *bth = 0;
+        }
+    }
+    max_bt_size = sh->max_bt_size[chroma];
+    max_tt_size = sh->max_tt_size[chroma];
+    max_mtt_depth = sh->max_mtt_depth[chroma] + depth_offset;
+
+    if (mode_type == MODE_TYPE_INTER) {
+        int area = cb_width * cb_height;
+        if (area == 32)
+            *btv = *bth = 0;
+        else if (area == 64)
+            *ttv = *tth = 0;
+    }
+    if (cb_width <= 2 * min_cb_size_y) {
+        *ttv = 0;
+        if (cb_width <= min_cb_size_y)
+            *btv = 0;
+    }
+    if (cb_height <= 2 * min_cb_size_y) {
+        *tth = 0;
+        if (cb_height <= min_cb_size_y)
+            *bth = 0;
+    }
+    if (cb_width > max_bt_size || cb_height > max_bt_size)
+        *btv = *bth = 0;
+    max_tt_size = FFMIN(64, max_tt_size);
+    if (cb_width > max_tt_size || cb_height > max_tt_size)
+        *ttv = *tth = 0;
+    if (mtt_depth >= max_mtt_depth)
+        *btv = *bth = *ttv = *tth = 0;
+    if (x0 + cb_width > pps->width) {
+        *ttv = *tth = 0;
+        if (cb_height > 64)
+            *btv = 0;
+        if (y0 + cb_height <= pps->height)
+            *bth = 0;
+        else if (cb_width > min_qt_size)
+            *btv = *bth = 0;
+    }
+    if (y0 + cb_height > pps->height) {
+        *btv = *ttv = *tth = 0;
+        if (cb_width > 64)
+            *bth = 0;
+    }
+    if (mtt_depth > 0 && part_idx  == 1)  {
+        if (last_split_mode == SPLIT_TT_VER)
+            *btv = 0;
+        else if (last_split_mode == SPLIT_TT_HOR)
+            *bth = 0;
+    }
+    if (cb_width <= 64 && cb_height > 64)
+        *btv = 0;
+    if (cb_width > 64 && cb_height <= 64)
+        *bth = 0;
+}
+
+static int get_num_intra_subpartitions(enum IspType isp_split_type, int cb_width, int cb_height)
+{
+    if (isp_split_type == ISP_NO_SPLIT)
+        return 1;
+    if ((cb_width == 4 && cb_height == 8) || (cb_width == 8 && cb_height == 4))
+        return 2;
+    return 4;
+}
+
+static int get_cclm_enabled(const VVCLocalContext *lc, const int x0, const int y0)
+{
+    const VVCFrameContext *fc = lc->fc;
+    const VVCSPS *sps   = fc->ps.sps;
+    int enabled = 0;
+
+    if (!sps->cclm_enabled_flag)
+        return 0;
+    if (!sps->qtbtt_dual_tree_intra_flag || !IS_I(&lc->sc->sh) || sps->ctb_log2_size_y < 6)
+        return 1;
+    else {
+        const int x64 = x0 >> 6 << 6;
+        const int y64 = y0 >> 6 << 6;
+        const int y32 = y0 >> 5 << 5;
+        const int x64_cu = x64 >> fc->ps.sps->min_cb_log2_size_y;
+        const int y64_cu = y64 >> fc->ps.sps->min_cb_log2_size_y;
+        const int y32_cu = y32 >> fc->ps.sps->min_cb_log2_size_y;
+        const int min_cb_width = fc->ps.pps->min_cb_width;
+        const int depth = SAMPLE_CTB(fc->tab.cqt_depth[1], x64_cu, y64_cu);
+        const int min_depth = fc->ps.sps->ctb_log2_size_y - 6;
+        const VVCSplitMode msm64 = (VVCSplitMode)TAB_MSM(fc, 0, x64, y64);
+        const VVCSplitMode msm32 = (VVCSplitMode)TAB_MSM(fc, 1, x64, y32);
+
+        enabled = SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y64_cu) == 64 &&
+            SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y64_cu) == 64;
+        enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR &&
+            SAMPLE_CTB(fc->tab.cb_width[1], x64_cu, y32_cu) == 64 &&
+            SAMPLE_CTB(fc->tab.cb_height[1], x64_cu, y32_cu) == 32;
+        enabled |= depth > min_depth;
+        enabled |= depth == min_depth && msm64 == SPLIT_BT_HOR && msm32 == SPLIT_BT_VER;
+
+        if (enabled) {
+            const int w = SAMPLE_CTB(fc->tab.cb_width[0], x64_cu, y64_cu);
+            const int h = SAMPLE_CTB(fc->tab.cb_height[0], x64_cu, y64_cu);
+            const int depth0 = SAMPLE_CTB(fc->tab.cqt_depth[0], x64_cu, y64_cu);
+            if ((w == 64 && h == 64 && TAB_ISPMF(fc, x64, y64)) ||
+                ((w < 64 || h < 64) && depth0 == min_depth))
+                return 0;
+        }
+
+    }
+
+    return enabled;
+}
+
+static int less(const void *a, const void *b)
+{
+    return *(const int*)a - *(const int*)b;
+}
+
+//8.4.2 Derivation process for luma intra prediction mode
+static enum IntraPredMode luma_intra_pred_mode(VVCLocalContext* lc, const int intra_subpartitions_mode_flag)
+{
+    VVCFrameContext *fc     = lc->fc;
+    CodingUnit *cu          = lc->cu;
+    const int x0            = cu->x0;
+    const int y0            = cu->y0;
+    enum IntraPredMode pred;
+    int intra_luma_not_planar_flag = 1;
+    int intra_luma_mpm_remainder = 0;
+    int intra_luma_mpm_flag = 1;
+    int intra_luma_mpm_idx = 0;
+
+    if (!cu->intra_luma_ref_idx)
+        intra_luma_mpm_flag = ff_vvc_intra_luma_mpm_flag(lc);
+    if (intra_luma_mpm_flag) {
+        if (!cu->intra_luma_ref_idx)
+            intra_luma_not_planar_flag = ff_vvc_intra_luma_not_planar_flag(lc, intra_subpartitions_mode_flag);
+        if (intra_luma_not_planar_flag)
+            intra_luma_mpm_idx = ff_vvc_intra_luma_mpm_idx(lc);
+    } else {
+        intra_luma_mpm_remainder = ff_vvc_intra_luma_mpm_remainder(lc);
+    }
+
+    if (!intra_luma_not_planar_flag) {
+        pred = INTRA_PLANAR;
+    } else if (cu->bdpcm_flag[0]) {
+        pred = cu->bdpcm_dir[0] ? INTRA_VERT : INTRA_HORZ;
+    } else {
+        const VVCSPS *sps       = fc->ps.sps;
+        const int x_a           = (x0 - 1) >> sps->min_cb_log2_size_y;
+        const int y_a           = (y0 + cu->cb_height - 1) >> sps->min_cb_log2_size_y;
+        const int x_b           = (x0 + cu->cb_width - 1) >> sps->min_cb_log2_size_y;
+        const int y_b           = (y0 - 1) >> sps->min_cb_log2_size_y;
+        int min_cb_width        = fc->ps.pps->min_cb_width;
+        int x0b                 = av_mod_uintp2(x0, sps->ctb_log2_size_y);
+        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;
+
+        int a, b, cand[5];
+
+       if (!available_l || (SAMPLE_CTB(fc->tab.cpm[0], x_a, y_a) != MODE_INTRA) ||
+            SAMPLE_CTB(fc->tab.imf, x_a, y_a)) {
+            a = INTRA_PLANAR;
+        } else {
+            a = SAMPLE_CTB(fc->tab.ipm, x_a, y_a);
+        }
+
+        if (!available_u || (SAMPLE_CTB(fc->tab.cpm[0], x_b, y_b) != MODE_INTRA) ||
+            SAMPLE_CTB(fc->tab.imf, x_b, y_b) || !y0b) {
+            b = INTRA_PLANAR;
+        } else {
+            b = SAMPLE_CTB(fc->tab.ipm, x_b, y_b);
+        }
+
+        if (a == b && a > INTRA_DC) {
+            cand[0] = a;
+            cand[1] = 2 + ((a + 61) % 64);
+            cand[2] = 2 + ((a -  1) % 64);
+            cand[3] = 2 + ((a + 60) % 64);
+            cand[4] = 2 + (a % 64);
+        } else {
+            const int minab = FFMIN(a, b);
+            const int maxab = FFMAX(a, b);
+            if (a > INTRA_DC && b > INTRA_DC) {
+                const int diff = maxab - minab;
+                cand[0] = a;
+                cand[1] = b;
+                if (diff == 1) {
+                    cand[2] = 2 + ((minab + 61) % 64);
+                    cand[3] = 2 + ((maxab - 1) % 64);
+                    cand[4] = 2 + ((minab + 60) % 64);
+                } else if (diff >= 62) {
+                    cand[2] = 2 + ((minab - 1) % 64);
+                    cand[3] = 2 + ((maxab + 61) % 64);
+                    cand[4] = 2 + (minab % 64);
+                } else if (diff == 2) {
+                    cand[2] = 2 + ((minab - 1) % 64);
+                    cand[3] = 2 + ((minab + 61) % 64);
+                    cand[4] = 2 + ((maxab - 1) % 64);
+                } else {
+                    cand[2] = 2 + ((minab + 61) % 64);
+                    cand[3] = 2 + ((minab - 1) % 64);
+                    cand[4] = 2 + ((maxab + 61) % 64);
+                }
+            } else if (a > INTRA_DC || b > INTRA_DC) {
+                cand[0] = maxab;
+                cand[1] = 2 + ((maxab + 61 ) % 64);
+                cand[2] = 2 + ((maxab - 1) % 64);
+                cand[3] = 2 + ((maxab + 60 ) % 64);
+                cand[4] = 2 + (maxab % 64);
+            } else {
+                cand[0] = INTRA_DC;
+                cand[1] = INTRA_VERT;
+                cand[2] = INTRA_HORZ;
+                cand[3] = INTRA_VERT - 4;
+                cand[4] = INTRA_VERT + 4;
+            }
+        }
+        if (intra_luma_mpm_flag) {
+            pred = cand[intra_luma_mpm_idx];
+        } else {
+            qsort(cand, FF_ARRAY_ELEMS(cand), sizeof(cand[0]), less);
+            pred = intra_luma_mpm_remainder + 1;
+            for (int i = 0; i < FF_ARRAY_ELEMS(cand); i++) {
+                if (pred >= cand[i])
+                    pred++;
+            }
+        }
+    }
+    ff_vvc_set_cb_tab(lc, fc->tab.ipm, pred);
+    return pred;
+}
+
+static int lfnst_idx_decode(VVCLocalContext *lc)
+{
+    CodingUnit  *cu             = lc->cu;
+    const VVCTreeType tree_type = cu->tree_type;
+    const VVCSPS *sps           = lc->fc->ps.sps;
+    const int cb_width          = cu->cb_width;
+    const int cb_height         = cu->cb_height;
+    int lfnst_width, lfnst_height, min_lfnst;
+    int lfnst_idx = 0;
+
+    memset(cu->apply_lfnst_flag, 0, sizeof(cu->apply_lfnst_flag));
+
+    if (!sps->lfnst_enabled_flag || cu->pred_mode != MODE_INTRA || FFMAX(cb_width, cb_height) > sps->max_tb_size_y)
+        return 0;
+
+    for (int i = 0; i < cu->num_tus; i++) {
+        const TransformUnit  *tu  = &cu->tus[i];
+        for (int j = 0; j < tu->nb_tbs; j++) {
+            const TransformBlock *tb = tu->tbs + j;
+            if (tu->coded_flag[tb->c_idx] && tb->ts)
+                return 0;
+        }
+    }
+
+    if (tree_type == DUAL_TREE_CHROMA) {
+        lfnst_width  = cb_width  >> sps->hshift[1];
+        lfnst_height = cb_height >> sps->vshift[1];
+    } else {
+        const int vs = cu->isp_split_type == ISP_VER_SPLIT;
+        const int hs = cu->isp_split_type == ISP_HOR_SPLIT;
+        lfnst_width = vs ? cb_width / cu->num_intra_subpartitions : cb_width;
+        lfnst_height = hs ? cb_height / cu->num_intra_subpartitions : cb_height;
+    }
+    min_lfnst = FFMIN(lfnst_width, lfnst_height);
+    if (tree_type != DUAL_TREE_CHROMA && cu->intra_mip_flag && min_lfnst < 16)
+        return 0;
+
+    if (min_lfnst >= 4) {
+        if ((cu->isp_split_type != ISP_NO_SPLIT || !lc->parse.lfnst_dc_only) && lc->parse.lfnst_zero_out_sig_coeff_flag)
+            lfnst_idx = ff_vvc_lfnst_idx(lc, tree_type != SINGLE_TREE);
+    }
+
+    if (lfnst_idx) {
+        cu->apply_lfnst_flag[LUMA] = tree_type != DUAL_TREE_CHROMA;
+        cu->apply_lfnst_flag[CB] = cu->apply_lfnst_flag[CR] = tree_type == DUAL_TREE_CHROMA;
+    }
+
+    return lfnst_idx;
+}
+
+static MtsIdx mts_idx_decode(VVCLocalContext *lc)
+{
+    const CodingUnit *cu    = lc->cu;
+    const VVCSPS     *sps   = lc->fc->ps.sps;
+    const int cb_width      = cu->cb_width;
+    const int cb_height     = cu->cb_height;
+    const uint8_t transform_skip_flag = cu->tus[0].tbs[0].ts; //fix me
+    int mts_idx = MTS_DCT2_DCT2;
+    if (cu->tree_type != DUAL_TREE_CHROMA && !cu->lfnst_idx &&
+        !transform_skip_flag && FFMAX(cb_width, cb_height) <= 32 &&
+        cu->isp_split_type == ISP_NO_SPLIT && !cu->sbt_flag &&
+        lc->parse.mts_zero_out_sig_coeff_flag && !lc->parse.mts_dc_only) {
+        if ((cu->pred_mode == MODE_INTER && sps->explicit_mts_inter_enabled_flag) ||
+            (cu->pred_mode == MODE_INTRA && sps->explicit_mts_intra_enabled_flag)) {
+            mts_idx = ff_vvc_mts_idx(lc);
+        }
+    }
+
+    return mts_idx;
+}
+
+static int is_cclm(enum IntraPredMode mode)
+{
+    return mode == INTRA_LT_CCLM || mode == INTRA_L_CCLM || mode == INTRA_T_CCLM;
+}
+
+//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;
+}
+
+static enum IntraPredMode derive_center_luma_intra_pred_mode(const VVCFrameContext *fc, const VVCSPS *sps, const VVCPPS *pps, const CodingUnit *cu)
+{
+    const int x_center            = (cu->x0 + cu->cb_width / 2) >> sps->min_cb_log2_size_y;
+    const int y_center            = (cu->y0 + cu->cb_height / 2) >> sps->min_cb_log2_size_y;
+    const int min_cb_width        = pps->min_cb_width;
+    const int intra_mip_flag      = SAMPLE_CTB(fc->tab.imf, x_center, y_center);
+    const int cu_pred_mode        = SAMPLE_CTB(fc->tab.cpm[0], x_center, y_center);
+    const int intra_pred_mode_y   = SAMPLE_CTB(fc->tab.ipm, x_center, y_center);
+
+    if (intra_mip_flag)
+        return INTRA_PLANAR;
+    if (cu_pred_mode == MODE_IBC || cu_pred_mode == MODE_PLT)
+        return INTRA_DC;
+    return intra_pred_mode_y;
+}
+
+static void derive_chroma_intra_pred_mode(VVCLocalContext *lc,
+    const int cclm_mode_flag, const int cclm_mode_idx, const int intra_chroma_pred_mode)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    CodingUnit *cu              = lc->cu;
+    const VVCSPS *sps           = fc->ps.sps;
+    const VVCPPS *pps           = fc->ps.pps;
+    const int x_cb              = cu->x0 >> sps->min_cb_log2_size_y;
+    const int y_cb              = cu->y0 >> sps->min_cb_log2_size_y;
+    const int min_cb_width      = pps->min_cb_width;
+    const int intra_mip_flag    = SAMPLE_CTB(fc->tab.imf, x_cb, y_cb);
+    enum IntraPredMode luma_intra_pred_mode = SAMPLE_CTB(fc->tab.ipm, x_cb, y_cb);
+
+    if (cu->tree_type == SINGLE_TREE && sps->chroma_format_idc == CHROMA_FORMAT_444 &&
+        intra_chroma_pred_mode == 4 && intra_mip_flag) {
+        cu->mip_chroma_direct_flag = 1;
+        cu->intra_pred_mode_c = luma_intra_pred_mode;
+        return;
+    }
+    luma_intra_pred_mode = derive_center_luma_intra_pred_mode(fc, sps, pps, cu);
+
+    cu->mip_chroma_direct_flag = 0;
+    if (cu->act_enabled_flag) {
+        cu->intra_pred_mode_c = luma_intra_pred_mode;
+        return;
+    }
+    if (cu->bdpcm_flag[1]){
+        cu->intra_pred_mode_c = cu->bdpcm_dir[1] ? INTRA_VERT : INTRA_HORZ;
+        return;
+    }
+    if (cclm_mode_flag) {
+        cu->intra_pred_mode_c = INTRA_LT_CCLM + cclm_mode_idx;
+    } else if (intra_chroma_pred_mode == 4){
+        cu->intra_pred_mode_c = luma_intra_pred_mode;
+    } else {
+        const static IntraPredMode pred_mode_c[][4 + 1] = {
+            {INTRA_VDIAG, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR, INTRA_PLANAR},
+            {INTRA_VERT,  INTRA_VDIAG,  INTRA_VERT,   INTRA_VERT,   INTRA_VERT},
+            {INTRA_HORZ,  INTRA_HORZ,   INTRA_VDIAG,  INTRA_HORZ,   INTRA_HORZ},
+            {INTRA_DC,    INTRA_DC,     INTRA_DC,     INTRA_VDIAG,  INTRA_DC},
+        };
+        const int modes[4] = {INTRA_PLANAR, INTRA_VERT, INTRA_HORZ, INTRA_DC};
+        int idx;
+
+        for (idx = 0; idx < FF_ARRAY_ELEMS(modes); idx++) {
+            if (modes[idx] == luma_intra_pred_mode)
+                break;
+        }
+        cu->intra_pred_mode_c = pred_mode_c[intra_chroma_pred_mode][idx];
+    }
+    if (sps->chroma_format_idc == CHROMA_FORMAT_422 && cu->intra_pred_mode_c <= INTRA_VDIAG) {
+        const static int mode_map_422[INTRA_VDIAG + 1] = {
+             0,  1, 61, 62, 63, 64, 65, 66,  2,  3,  5,  6,  8, 10, 12, 13,
+            14, 16, 18, 20, 22, 23, 24, 26, 28, 30, 31, 33, 34, 35, 36, 37,
+            38, 39, 40, 41, 41, 42, 43, 43, 44, 44, 45, 45, 46, 47, 48, 48,
+            49, 49, 50, 51, 51, 52, 52, 53, 54, 55, 55, 56, 56, 57, 57, 58,
+            59, 59, 60,
+        };
+        cu->intra_pred_mode_c = mode_map_422[cu->intra_pred_mode_c];
+    }
+}
+
+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 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);
+    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->mts_enabled_flag) {
+        if (cu->isp_split_type != ISP_NO_SPLIT ||
+            (cu->sbt_flag && FFMAX(tb->tb_width, tb->tb_height) <= 32) ||
+            (!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(const 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->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.pred_residual_joint(tb->coeffs, tb->tb_width, tb->tb_height, c_sign, shift);
+        fc->hpc.lmcs_scale_chroma(lc, tb->coeffs, tb->coeffs, tb->tb_width, tb->tb_height, cu->x0, cu->y0);
+        fc->vvcdsp.add_residual(dst, tb->coeffs, tb->tb_width, tb->tb_height, stride);
+    } else {
+        fc->vvcdsp.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->hpc.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->hpc.intra_cclm_pred(lc, x0, y0, w, h);
+            } else {
+                fc->hpc.intra_pred(lc, x0, y0, w, h, 1);
+                fc->hpc.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 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_int16((*p + add) >> shift);
+            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 VVCSH *sh             = &lc->sc->sh;
+    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->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) - 5 + 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 VVCSH *sh             = &lc->sc->sh;
+    const CodingUnit *cu        = lc->cu;
+    const AVBufferRef *ref;
+    const VVCScalingList *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->scaling_matrix_for_alternative_colour_space_disabled_flag);
+
+    if (!sh->explicit_scaling_list_used_flag || tb->ts ||
+        sps->scaling_matrix_for_lfnst_disabled_flag && cu->apply_lfnst_flag[tb->c_idx])
+        return ff_vvc_default_scale_m;
+
+    ref = ps->scaling_list[ps->ph->scaling_list_aps_id];
+    if (!ref || !ref->data) {
+        av_log(lc->fc->avctx, AV_LOG_WARNING, "bug: no scaling list aps, id = %d", ps->ph->scaling_list_aps_id);
+        return ff_vvc_default_scale_m;
+    }
+
+    sl = (const VVCScalingList *)ref->data;
+    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)
+{
+    coeff = (coeff * scale * scale_m + tb->bd_offset) >> tb->bd_shift;
+    coeff = av_clip(coeff, -(1<<15), (1<<15) - 1);
+    return coeff;
+}
+
+static void dequant(const VVCLocalContext *lc, const TransformUnit *tu, TransformBlock *tb)
+{
+    uint8_t tmp[MAX_TB_SIZE * MAX_TB_SIZE];
+    const VVCSH *sh         = &lc->sc->sh;
+    const uint8_t *scale_m  = derive_scale_m(lc, tb, tmp);
+    int scale;
+
+    derive_qp(lc, tu, tb);
+    scale = derive_scale(tb, 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);
+            scale_m++;
+        }
+    }
+}
+
+static void itx_2d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv, int *temp)
+{
+    const VVCSPS *sps   = fc->ps.sps;
+    const int w         = tb->tb_width;
+    const int h         = tb->tb_height;
+    const int nzw       = tb->max_scan_x + 1;
+
+    for (int x = 0; x < nzw; x++)
+        fc->vvcdsp.itx[trv][tb->log2_tb_height - 1](temp + x, w, tb->coeffs + x, w);
+    scale_clip(temp, nzw, w, h, 7);
+
+    for (int y = 0; y < h; y++)
+        fc->vvcdsp.itx[trh][tb->log2_tb_width - 1](tb->coeffs + y * w, 1, temp + y * w, 1);
+    scale(tb->coeffs, tb->coeffs, w, h, 20 - sps->bit_depth);
+}
+
+static void itx_1d(const VVCFrameContext *fc, TransformBlock *tb, const enum TxType trh, const enum TxType trv, int  *temp)
+{
+    const VVCSPS *sps   = fc->ps.sps;
+    const int w         = tb->tb_width;
+    const int h         = tb->tb_height;
+
+    if (w > 1)
+        fc->vvcdsp.itx[trh][tb->log2_tb_width - 1](temp, 1, tb->coeffs, 1);
+    else
+        fc->vvcdsp.itx[trv][tb->log2_tb_height - 1](temp, 1, tb->coeffs, 1);
+    scale(tb->coeffs, temp, w, h, 21 - sps->bit_depth);
+}
+
+static void itransform(const 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->lmcs_used_flag && fc->ps.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)];
+
+            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, temp);
+                else
+                    itx_1d(fc, tb, trh, trv, temp);
+            }
+
+            if (chroma_scale)
+                fc->hpc.lmcs_scale_chroma(lc, temp, tb->coeffs, w, h, cu->x0, cu->y0);
+            fc->vvcdsp.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->chroma_format_idc && (cu->tree_type != DUAL_TREE_LUMA);
+
+    for (int ch_type = start; ch_type <= end; ch_type++) {
+        for (int i = 0; i < cu->num_tus; i++) {
+            TransformUnit *tu = &cu->tus[i];
+
+            predict_intra(lc, tu, i, ch_type);
+            itransform(lc, tu, i, ch_type);
+        }
+    }
+    return 0;
+}
+
+static void intra_luma_pred_modes(VVCLocalContext *lc, const int cu_act_enabled_flag)
+{
+    VVCFrameContext *fc                   = lc->fc;
+    const VVCSPS *sps               = fc->ps.sps;
+    const VVCPPS *pps               = fc->ps.pps;
+    CodingUnit *cu                  = lc->cu;
+    const int log2_min_cb_size      = sps->min_cb_log2_size_y;
+    const int x0                    = cu->x0;
+    const int y0                    = cu->y0;
+    const int x_cb                  = x0 >> log2_min_cb_size;
+    const int y_cb                  = y0 >> log2_min_cb_size;
+    const int cb_width              = cu->cb_width;
+    const int cb_height             = cu->cb_height;
+
+    cu->intra_luma_ref_idx  = 0;
+    if (sps->mip_enabled_flag)
+        cu->intra_mip_flag = ff_vvc_intra_mip_flag(lc, fc->tab.imf);
+    if (cu->intra_mip_flag) {
+        int intra_mip_transposed_flag = ff_vvc_intra_mip_transposed_flag(lc);
+        int intra_mip_mode = ff_vvc_intra_mip_mode(lc);
+
+        int x = y_cb * pps->min_cb_width + x_cb;
+        for (int y = 0; y < (cb_height>>log2_min_cb_size); y++) {
+            int width = cb_width>>log2_min_cb_size;
+            memset(&fc->tab.imf[x],  cu->intra_mip_flag, width);
+            memset(&fc->tab.ipm[x], intra_mip_mode, width);
+            fc->tab.imtf[x] = intra_mip_transposed_flag;
+            fc->tab.imm[x]  = intra_mip_mode;
+            x += pps->min_cb_width;
+        }
+        cu->intra_pred_mode_y = intra_mip_mode;
+    } else {
+        const int min_tb_size_y = 1 << 2;
+        int intra_subpartitions_mode_flag = 0;
+        if (sps->mrl_enabled_flag && ((y0 % sps->ctb_size_y) > 0))
+            cu->intra_luma_ref_idx = ff_vvc_intra_luma_ref_idx(lc);
+        if (sps->isp_enabled_flag && !cu->intra_luma_ref_idx &&
+            (cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) &&
+            (cb_width * cb_height > min_tb_size_y * min_tb_size_y) &&
+            !cu_act_enabled_flag)
+            intra_subpartitions_mode_flag = ff_vvc_intra_subpartitions_mode_flag(lc);
+        if (!(x0 & 63) && !(y0 & 63))
+            TAB_ISPMF(fc, x0, y0) = intra_subpartitions_mode_flag;
+        cu->isp_split_type = ff_vvc_isp_split_type(lc, intra_subpartitions_mode_flag);
+        cu->num_intra_subpartitions = get_num_intra_subpartitions(cu->isp_split_type, cb_width, cb_height);
+        cu->intra_pred_mode_y = luma_intra_pred_mode(lc, intra_subpartitions_mode_flag);
+    }
+}
+
+static void intra_chroma_pred_modes(VVCLocalContext *lc, int x0, int y0)
+{
+    const int cclm_enabled = get_cclm_enabled(lc, x0, y0);
+    int cclm_mode_flag = 0;
+    int cclm_mode_idx = 0;
+    int intra_chroma_pred_mode = 0;
+
+    if (cclm_enabled)
+        cclm_mode_flag = ff_vvc_cclm_mode_flag(lc);
+
+    if (cclm_mode_flag)
+        cclm_mode_idx = ff_vvc_cclm_mode_idx(lc);
+    else
+        intra_chroma_pred_mode = ff_vvc_intra_chroma_pred_mode(lc);
+    derive_chroma_intra_pred_mode(lc, cclm_mode_flag, cclm_mode_idx, intra_chroma_pred_mode);
+}
+
+static PredMode pred_mode_decode(VVCLocalContext *lc,
+                                 const VVCTreeType tree_type,
+                                 const VVCModeType mode_type)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    CodingUnit *cu              = lc->cu;
+    const VVCSPS *sps           = fc->ps.sps;
+    const VVCSH *sh             = &lc->sc->sh;
+    const int ch_type = tree_type == DUAL_TREE_CHROMA ? 1 : 0;
+    const int is_4x4 = cu->cb_width == 4 && cu->cb_height == 4;
+    int pred_mode_flag;
+    int pred_mode_ibc_flag;
+    PredMode pred_mode;
+
+    cu->skip_flag = 0;
+    if (!IS_I(sh) || sps->ibc_enabled_flag) {
+        const int is_128 = cu->cb_width == 128 || cu->cb_height == 128;
+        if (tree_type != DUAL_TREE_CHROMA &&
+            ((!is_4x4 && mode_type != MODE_TYPE_INTRA) ||
+            (sps->ibc_enabled_flag && !is_128))) {
+            cu->skip_flag = ff_vvc_cu_skip_flag(lc, fc->tab.skip);
+        }
+
+        if (is_4x4 || mode_type == MODE_TYPE_INTRA || IS_I(sh)) {
+            pred_mode_flag = 1;
+        } else if (mode_type == MODE_TYPE_INTER || cu->skip_flag) {
+            pred_mode_flag = 0;
+        } else  {
+            pred_mode_flag = ff_vvc_pred_mode_flag(lc, ch_type);
+        }
+        pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER;
+
+        if (((IS_I(sh) && !cu->skip_flag) ||
+            (!IS_I(sh) && (pred_mode != MODE_INTRA ||
+            ((is_4x4 || mode_type == MODE_TYPE_INTRA) && !cu->skip_flag)))) &&
+            !is_128 && mode_type != MODE_TYPE_INTER && sps->ibc_enabled_flag &&
+            tree_type != DUAL_TREE_CHROMA) {
+            pred_mode_ibc_flag = ff_vvc_pred_mode_ibc_flag(lc, ch_type);
+        } else if (cu->skip_flag && (is_4x4 || mode_type == MODE_TYPE_INTRA)) {
+            pred_mode_ibc_flag = 1;
+        } else if (is_128 || mode_type == MODE_TYPE_INTER || tree_type == DUAL_TREE_CHROMA) {
+            pred_mode_ibc_flag = 0;
+        } else {
+            pred_mode_ibc_flag = (IS_I(sh)) ? sps->ibc_enabled_flag : 0;
+        }
+        if (pred_mode_ibc_flag)
+            pred_mode = MODE_IBC;
+    } else {
+        pred_mode_flag = is_4x4 || mode_type == MODE_TYPE_INTRA ||
+            mode_type != MODE_TYPE_INTER || IS_I(sh);
+        pred_mode = pred_mode_flag ? MODE_INTRA : MODE_INTER;
+    }
+    return pred_mode;
+}
+
+void ff_vvc_set_cb_tab(const VVCLocalContext *lc, uint8_t *tab, const uint8_t v)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    const VVCPPS *pps           = fc->ps.pps;
+    const CodingUnit *cu        = lc->cu;
+    const int log2_min_cb_size  = fc->ps.sps->min_cb_log2_size_y;
+    const int x_cb              = cu->x0 >> log2_min_cb_size;
+    const int y_cb              = cu->y0 >> log2_min_cb_size;
+    const int cb_width          = cu->cb_width;
+    const int cb_height         = cu->cb_height;
+    int x                       = y_cb * pps->min_cb_width + x_cb;
+
+    for (int y = 0; y < (cb_height >> log2_min_cb_size); y++) {
+        const int width = cb_width >> log2_min_cb_size;
+
+        memset(&tab[x], v, width);
+        x += pps->min_cb_width;
+    }
+}
+
+static void sbt_info(VVCLocalContext *lc, const VVCSPS *sps)
+{
+    CodingUnit *cu      = lc->cu;
+    const int cb_width  = cu->cb_width;
+    const int cb_height = cu->cb_height;
+
+    if (cu->pred_mode == MODE_INTER && sps->sbt_enabled_flag && !cu->ciip_flag
+        && cb_width <= sps->max_tb_size_y && cb_height <= sps->max_tb_size_y) {
+        const int sbt_ver_h = cb_width  >= 8;
+        const int sbt_hor_h = cb_height >= 8;
+        cu->sbt_flag = 0;
+        if (sbt_ver_h || sbt_hor_h)
+            cu->sbt_flag = ff_vvc_sbt_flag(lc);
+        if (cu->sbt_flag) {
+            const int sbt_ver_q = cb_width  >= 16;
+            const int sbt_hor_q = cb_height >= 16;
+            int cu_sbt_quad_flag = 0;
+
+            if ((sbt_ver_h || sbt_hor_h) && (sbt_ver_q || sbt_hor_q))
+                cu_sbt_quad_flag = ff_vvc_sbt_quad_flag(lc);
+            if (cu_sbt_quad_flag) {
+                cu->sbt_horizontal_flag = sbt_hor_q;
+                if (sbt_ver_q && sbt_hor_q)
+                    cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc);
+            } else {
+                cu->sbt_horizontal_flag = sbt_hor_h;
+                if (sbt_ver_h && sbt_hor_h)
+                    cu->sbt_horizontal_flag = ff_vvc_sbt_horizontal_flag(lc);
+            }
+            cu->sbt_pos_flag = ff_vvc_sbt_pos_flag(lc);
+
+            {
+                const int sbt_min = cu_sbt_quad_flag ? 1 : 2;
+                lc->parse.sbt_num_fourths_tb0 = cu->sbt_pos_flag ? (4 - sbt_min) : sbt_min;
+            }
+        }
+    }
+}
+
+static int skipped_transform_tree_unit(VVCLocalContext *lc)
+{
+    const CodingUnit *cu = lc->cu;
+    int ret;
+
+    set_qp_y(lc, cu->x0, cu->y0, 0);
+    set_qp_c(lc);
+    ret = skipped_transform_tree(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+    if (ret < 0)
+        return ret;
+    return 0;
+}
+
+static void set_cb_pos(const VVCFrameContext *fc, const CodingUnit *cu)
+{
+    const VVCSPS *sps           = fc->ps.sps;
+    const VVCPPS *pps           = fc->ps.pps;
+    const int log2_min_cb_size  = sps->min_cb_log2_size_y;
+    const int x_cb              = cu->x0 >> log2_min_cb_size;
+    const int y_cb              = cu->y0 >> log2_min_cb_size;
+    const int ch_type           = cu->ch_type;
+    int x, y;
+
+    x = y_cb * pps->min_cb_width + x_cb;
+    for (y = 0; y < (cu->cb_height >> log2_min_cb_size); y++) {
+        const int width = cu->cb_width >> log2_min_cb_size;
+
+        for (int i = 0; i < width; i++) {
+            fc->tab.cb_pos_x[ch_type][x + i] = cu->x0;
+            fc->tab.cb_pos_y[ch_type][x + i] = cu->y0;
+        }
+        memset(&fc->tab.cb_width[ch_type][x], cu->cb_width, width);
+        memset(&fc->tab.cb_height[ch_type][x], cu->cb_height, width);
+        memset(&fc->tab.cqt_depth[ch_type][x], cu->cqt_depth, width);
+
+        x += pps->min_cb_width;
+    }
+}
+
+static CodingUnit* add_cu(VVCLocalContext *lc, const int x0, const int y0,
+    const int cb_width, const int cb_height, const int cqt_depth, const VVCTreeType tree_type)
+{
+    VVCFrameContext *fc = lc->fc;
+    const int ch_type   = tree_type == DUAL_TREE_CHROMA ? 1 : 0;
+    const VVCSPS *sps   = fc->ps.sps;
+    const VVCPPS *pps   = fc->ps.pps;
+    const int rx        = x0 >> sps->ctb_log2_size_y;
+    const int ry        = y0 >> sps->ctb_log2_size_y;
+    CTU *ctu            = fc->tab.ctus + ry * pps->ctb_width + rx;
+    CodingUnit *cu;
+
+    AVBufferRef *buf = av_buffer_pool_get(fc->cu_pool);
+    if (!buf)
+        return NULL;
+    cu = (CodingUnit *)buf->data;
+    cu->next = NULL;
+    cu->buf = buf;
+
+    if (ctu->cus)
+        lc->cu->next = cu;
+    else
+        ctu->cus = cu;
+    lc->cu = cu;
+
+    memset(&cu->pu, 0, sizeof(cu->pu));
+
+    lc->parse.prev_tu_cbf_y = 0;
+
+    cu->sbt_flag = 0;
+    cu->act_enabled_flag = 0;
+
+    cu->tree_type = tree_type;
+    cu->x0 = x0;
+    cu->y0 = y0;
+    cu->cb_width = cb_width;
+    cu->cb_height = cb_height;
+    cu->ch_type = ch_type;
+    cu->cqt_depth = cqt_depth;
+    cu->num_tus = 0;
+    cu->bdpcm_flag[LUMA] = cu->bdpcm_flag[CB] = cu->bdpcm_flag[CR] = 0;
+    cu->isp_split_type = ISP_NO_SPLIT;
+    cu->intra_mip_flag = 0;
+    cu->ciip_flag = 0;
+    cu->coded_flag = 1;
+    cu->num_intra_subpartitions = 1;
+
+    set_cb_pos(fc, cu);
+    return cu;
+}
+
+static int hls_coding_unit(VVCLocalContext *lc, int x0, int y0, int cb_width, int cb_height,
+    int cqt_depth, const VVCTreeType tree_type, VVCModeType mode_type)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    const VVCSPS *sps           = fc->ps.sps;
+    const VVCSH *sh             = &lc->sc->sh;
+    const int hs                = sps->hshift[CHROMA];
+    const int vs                = sps->vshift[CHROMA];
+    const int is_128            = cb_width > 64 || cb_height > 64;
+    int pred_mode_plt_flag = 0;
+    int cu_act_enabled_flag = 0;
+    int ret;
+
+    CodingUnit *cu = add_cu(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type);
+
+    if (!cu)
+        return AVERROR(ENOMEM);
+
+    ff_vvc_set_neighbour_available(lc, cu->x0, cu->y0, cu->cb_width, cu->cb_height);
+
+    if (IS_I(sh) && is_128)
+        mode_type = MODE_TYPE_INTRA;
+    cu->pred_mode = pred_mode_decode(lc, tree_type, mode_type);
+
+    if (cu->pred_mode == MODE_INTRA && sps->palette_enabled_flag && !is_128 && !cu->skip_flag &&
+        mode_type != MODE_TYPE_INTER && ((cb_width * cb_height) >
+        (tree_type != DUAL_TREE_CHROMA ? 16 : (16 << hs << vs))) &&
+        (mode_type != MODE_TYPE_INTRA || tree_type != DUAL_TREE_CHROMA)) {
+        pred_mode_plt_flag = ff_vvc_pred_mode_plt_flag(lc);
+        if (pred_mode_plt_flag)
+            return AVERROR_PATCHWELCOME;
+    }
+    if (cu->pred_mode == MODE_INTRA && sps->act_enabled_flag && tree_type == SINGLE_TREE)
+        av_assert0(0 && "fix me");
+    if (cu->pred_mode == MODE_INTRA || cu->pred_mode == MODE_PLT) {
+        if (tree_type == SINGLE_TREE || tree_type == DUAL_TREE_LUMA) {
+            if (pred_mode_plt_flag) {
+                return AVERROR_PATCHWELCOME;
+            } else {
+                int intra_bdpcm_luma_flag = 0;
+                if (intra_bdpcm_luma_flag) {
+                    av_assert0(0 && "fix me");
+                } else {
+                    intra_luma_pred_modes(lc, cu_act_enabled_flag);
+                }
+            }
+            ff_vvc_set_intra_mvf(lc);
+        }
+        if ((tree_type == SINGLE_TREE || tree_type == DUAL_TREE_CHROMA) && sps->chroma_format_idc) {
+            if (pred_mode_plt_flag && tree_type == DUAL_TREE_CHROMA)
+                return AVERROR_PATCHWELCOME;
+            else if (!pred_mode_plt_flag) {
+                if (!cu->act_enabled_flag) {
+                    if ((cb_width  >> hs) <= sps->max_ts_size &&
+                        (cb_height >> vs) <= sps->max_ts_size &&
+                        sps->bdpcm_enabled_flag) {
+                        av_assert0(0 && "fix me");
+                        //cu->bdpcm_flag[1] = cu->bdpcm_flag[2] =
+                    }
+                    if (cu->bdpcm_flag[1])
+                        av_assert0(0 && "fix me");
+                    else {
+                        intra_chroma_pred_modes(lc, x0, y0);
+                    }
+
+                }
+            }
+        }
+    } else if (tree_type != DUAL_TREE_CHROMA) { /* MODE_INTER or MODE_IBC */
+        ff_vvc_inter_data(lc);
+    }
+    if (cu->pred_mode != MODE_INTRA && !pred_mode_plt_flag && !lc->cu->pu.general_merge_flag)
+        cu->coded_flag = ff_vvc_cu_coded_flag(lc);
+    else
+        cu->coded_flag = !(cu->skip_flag || pred_mode_plt_flag);
+
+    if (cu->coded_flag) {
+        sbt_info(lc, sps);
+        if (sps->act_enabled_flag && cu->pred_mode != MODE_INTRA && tree_type == SINGLE_TREE)
+            av_assert0(0 && "fix me");
+        lc->parse.lfnst_dc_only = 1;
+        lc->parse.lfnst_zero_out_sig_coeff_flag = 1;
+        lc->parse.mts_dc_only = 1;
+        lc->parse.mts_zero_out_sig_coeff_flag = 1;
+        ret = hls_transform_tree(lc, x0, y0, cb_width, cb_height, cu->ch_type);
+        if (ret < 0)
+            return ret;
+        cu->lfnst_idx = lfnst_idx_decode(lc);
+        cu->mts_idx = mts_idx_decode(lc);
+        set_qp_c(lc);
+        if (ret < 0)
+            return ret;
+    } else {
+        av_assert0(tree_type == SINGLE_TREE);
+        ret = skipped_transform_tree_unit(lc);
+        if (ret < 0)
+            return ret;
+    }
+    ff_vvc_set_cb_tab(lc, fc->tab.cpm[cu->ch_type], cu->pred_mode);
+    if (tree_type != DUAL_TREE_CHROMA)
+        ff_vvc_set_cb_tab(lc, fc->tab.skip, cu->skip_flag);
+    if (tree_type != DUAL_TREE_LUMA && sps->chroma_format_idc) {
+        for (int i = 0; i < cu->num_tus; i++) {
+            const TransformUnit *tu = cu->tus + i;
+            for (int j = 0; j < tu->nb_tbs; j++) {
+                const TransformBlock *tb = tu->tbs + j;
+                if (tb->c_idx != LUMA)
+                    set_qp_c_tab(lc, tu, tb);
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int derive_mode_type_condition(const VVCLocalContext *lc,
+    const VVCSplitMode split, const int cb_width, const int cb_height, const VVCModeType mode_type_curr)
+{
+    const VVCSH *sh     = &lc->sc->sh;
+    const VVCSPS *sps   = lc->fc->ps.sps;
+    const int area      = cb_width * cb_height;
+
+    if ((IS_I(sh) && sps->qtbtt_dual_tree_intra_flag) ||
+        mode_type_curr != MODE_TYPE_ALL || !sps->chroma_format_idc ||
+        sps->chroma_format_idc == CHROMA_FORMAT_444)
+        return 0;
+    if ((area == 64 && (split == SPLIT_QT || split == SPLIT_TT_HOR || split == SPLIT_TT_VER)) ||
+        (area == 32 &&  (split == SPLIT_BT_HOR || split == SPLIT_BT_VER)))
+        return 1;
+    if ((area == 64 && (split == SPLIT_BT_HOR || split == SPLIT_BT_VER) && sps->chroma_format_idc == CHROMA_FORMAT_420) ||
+        (area == 128 && (split == SPLIT_TT_HOR || split == SPLIT_TT_VER) && sps->chroma_format_idc == CHROMA_FORMAT_420) ||
+        (cb_width == 8 && split == SPLIT_BT_VER) || (cb_width == 16 && split == SPLIT_TT_VER))
+        return 1 + !IS_I(sh);
+
+    return 0;
+}
+
+static VVCModeType mode_type_decode(VVCLocalContext *lc, const int x0, const int y0,
+    const int cb_width, const int cb_height, const VVCSplitMode split, const int ch_type,
+    const VVCModeType mode_type_curr)
+{
+    VVCModeType mode_type;
+    const int mode_type_condition = derive_mode_type_condition(lc, split, cb_width, cb_height, mode_type_curr);
+
+    if (mode_type_condition == 1)
+        mode_type = MODE_TYPE_INTRA;
+    else if (mode_type_condition == 2) {
+        mode_type = ff_vvc_non_inter_flag(lc, x0, y0, ch_type) ? MODE_TYPE_INTRA : MODE_TYPE_INTER;
+    } else {
+        mode_type = mode_type_curr;
+    }
+
+    return mode_type;
+}
+
+static int hls_coding_tree(VVCLocalContext *lc,
+    int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c,
+    int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx,
+    VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr);
+
+static int coding_tree_btv(VVCLocalContext *lc,
+    int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c,
+    int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset,
+    VVCTreeType tree_type, VVCModeType mode_type)
+{
+#define CODING_TREE(x, idx) do { \
+    ret = hls_coding_tree(lc, x, y0, cb_width / 2, cb_height, \
+        qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \
+        depth_offset, idx, SPLIT_BT_VER, tree_type, mode_type); \
+    if (ret < 0) \
+        return ret; \
+} while (0);
+
+    const VVCPPS *pps = lc->fc->ps.pps;
+    const int x1 = x0 + cb_width / 2;
+    int ret = 0;
+
+    depth_offset += (x0 + cb_width > pps->width) ? 1 : 0;
+    CODING_TREE(x0, 0);
+    if (x1 < pps->width)
+        CODING_TREE(x1, 1);
+
+    return 0;
+
+#undef CODING_TREE
+}
+
+static int coding_tree_bth(VVCLocalContext *lc,
+    int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c,
+    int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset,
+    VVCTreeType tree_type, VVCModeType mode_type)
+{
+#define CODING_TREE(y, idx) do { \
+        ret = hls_coding_tree(lc, x0, y, cb_width , cb_height / 2, \
+            qg_on_y, qg_on_c, cb_sub_div + 1, cqt_depth, mtt_depth + 1, \
+            depth_offset, idx, SPLIT_BT_HOR, tree_type, mode_type); \
+        if (ret < 0) \
+            return ret; \
+    } while (0);
+
+    const VVCPPS *pps = lc->fc->ps.pps;
+    const int y1 = y0 + (cb_height / 2);
+    int ret = 0;
+
+    depth_offset += (y0 + cb_height > pps->height) ? 1 : 0;
+    CODING_TREE(y0, 0);
+    if (y1 < pps->height)
+        CODING_TREE(y1, 1);
+
+    return 0;
+
+#undef CODING_TREE
+}
+
+static int coding_tree_ttv(VVCLocalContext *lc,
+    int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c,
+    int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset,
+    VVCTreeType tree_type, VVCModeType mode_type)
+{
+#define CODING_TREE(x, w, sub_div, idx) do { \
+        ret = hls_coding_tree(lc, x, y0, w, cb_height, \
+            qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \
+            depth_offset, idx, SPLIT_TT_VER, tree_type, mode_type); \
+        if (ret < 0) \
+            return ret; \
+    } while (0);
+
+    const VVCSH *sh = &lc->sc->sh;
+    const int x1    = x0 + cb_width / 4;
+    const int x2    = x0 + cb_width * 3 / 4;
+    int ret;
+
+    qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv);
+    qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv);
+
+    CODING_TREE(x0, cb_width / 4, cb_sub_div + 2, 0);
+    CODING_TREE(x1, cb_width / 2, cb_sub_div + 1, 1);
+    CODING_TREE(x2, cb_width / 4, cb_sub_div + 2, 2);
+
+    return 0;
+
+#undef CODING_TREE
+}
+
+static int coding_tree_tth(VVCLocalContext *lc,
+    int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c,
+    int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset,
+    VVCTreeType tree_type, VVCModeType mode_type)
+{
+#define CODING_TREE(y, h, sub_div, idx) do { \
+        ret = hls_coding_tree(lc, x0, y, cb_width, h, \
+            qg_on_y, qg_on_c, sub_div, cqt_depth, mtt_depth + 1, \
+            depth_offset, idx, SPLIT_TT_HOR, tree_type, mode_type); \
+        if (ret < 0) \
+            return ret; \
+    } while (0);
+
+    const VVCSH *sh = &lc->sc->sh;
+    const int y1    = y0 + (cb_height / 4);
+    const int y2    = y0 + (3 * cb_height / 4);
+    int ret;
+
+    qg_on_y = qg_on_y && (cb_sub_div + 2 <= sh->cu_qp_delta_subdiv);
+    qg_on_c = qg_on_c && (cb_sub_div + 2 <= sh->cu_chroma_qp_offset_subdiv);
+
+    CODING_TREE(y0, cb_height / 4, cb_sub_div + 2, 0);
+    CODING_TREE(y1, cb_height / 2, cb_sub_div + 1, 1);
+    CODING_TREE(y2, cb_height / 4, cb_sub_div + 2, 2);
+
+    return 0;
+
+#undef CODING_TREE
+}
+
+static int coding_tree_qt(VVCLocalContext *lc,
+    int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c,
+    int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset,
+    VVCTreeType tree_type, VVCModeType mode_type)
+{
+#define CODING_TREE(x, y, idx) do { \
+        ret = hls_coding_tree(lc, x, y, cb_width / 2, cb_height / 2, \
+            qg_on_y, qg_on_c, cb_sub_div + 2, cqt_depth + 1, 0, 0, \
+            idx, SPLIT_QT, tree_type, mode_type); \
+        if (ret < 0) \
+            return ret; \
+    } while (0);
+
+    const VVCPPS *pps = lc->fc->ps.pps;
+    const int x1 = x0 + cb_width / 2;
+    const int y1 = y0 + cb_height / 2;
+    int ret = 0;
+
+    CODING_TREE(x0, y0, 0);
+    if (x1 < pps->width)
+        CODING_TREE(x1, y0, 1);
+    if (y1 < pps->height)
+        CODING_TREE(x0, y1, 2);
+    if (x1 < pps->width &&
+        y1 < pps->height)
+        CODING_TREE(x1, y1, 3);
+
+    return 0;
+
+#undef CODING_TREE
+}
+
+typedef int (*coding_tree_fn)(VVCLocalContext *lc,
+    int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c,
+    int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset,
+    VVCTreeType tree_type, VVCModeType mode_type);
+
+const static coding_tree_fn coding_tree[] = {
+    coding_tree_tth,
+    coding_tree_bth,
+    coding_tree_ttv,
+    coding_tree_btv,
+    coding_tree_qt,
+};
+
+static int hls_coding_tree(VVCLocalContext *lc,
+    int x0, int y0, int cb_width, int cb_height, int qg_on_y, int qg_on_c,
+    int cb_sub_div, int cqt_depth, int mtt_depth, int depth_offset, int part_idx,
+    VVCSplitMode last_split_mode, VVCTreeType tree_type_curr, VVCModeType mode_type_curr)
+{
+    VVCFrameContext *fc = lc->fc;
+    const VVCPPS *pps   = fc->ps.pps;
+    const VVCSH *sh     = &lc->sc->sh;
+    const int ch_type   = tree_type_curr == DUAL_TREE_CHROMA;
+    int ret;
+    VVCAllowedSplit allowed;
+
+    if (pps->cu_qp_delta_enabled_flag && qg_on_y && cb_sub_div <= sh->cu_qp_delta_subdiv) {
+        lc->parse.is_cu_qp_delta_coded = 0;
+        lc->parse.cu_qg_top_left_x = x0;
+        lc->parse.cu_qg_top_left_y = y0;
+    }
+    if (sh->cu_chroma_qp_offset_enabled_flag && qg_on_c &&
+        cb_sub_div <= sh->cu_chroma_qp_offset_subdiv) {
+        lc->parse.is_cu_chroma_qp_offset_coded = 0;
+        memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset));
+    }
+
+    can_split(lc, x0, y0, cb_width, cb_height, mtt_depth, depth_offset, part_idx,
+        last_split_mode, tree_type_curr, mode_type_curr, &allowed);
+    if (ff_vvc_split_cu_flag(lc, x0, y0, cb_width, cb_height, ch_type, &allowed)) {
+        VVCSplitMode split      = ff_vvc_split_mode(lc, x0, y0, cb_width, cb_height, cqt_depth, mtt_depth, ch_type, &allowed);
+        VVCModeType mode_type   = mode_type_decode(lc, x0, y0, cb_width, cb_height, split, ch_type, mode_type_curr);
+
+        VVCTreeType tree_type   = (mode_type == MODE_TYPE_INTRA) ? DUAL_TREE_LUMA : tree_type_curr;
+
+        if (split != SPLIT_QT) {
+            if (!(x0 & 31) && !(y0 & 31) && mtt_depth <= 1)
+                TAB_MSM(fc, mtt_depth, x0, y0) = split;
+        }
+        ret = coding_tree[split - 1](lc, x0, y0, cb_width, cb_height, qg_on_y, qg_on_c,
+            cb_sub_div, cqt_depth, mtt_depth, depth_offset, tree_type, mode_type);
+        if (ret < 0)
+            return ret;
+        if (mode_type_curr == MODE_TYPE_ALL && mode_type == MODE_TYPE_INTRA) {
+            ret = hls_coding_tree(lc, x0, y0, cb_width, cb_height, 0, qg_on_c, cb_sub_div,
+                cqt_depth, mtt_depth, 0, 0, split, DUAL_TREE_CHROMA, mode_type);
+            if (ret < 0)
+                return ret;
+        }
+    } else {
+        ret = hls_coding_unit(lc, x0, y0, cb_width, cb_height, cqt_depth, tree_type_curr, mode_type_curr);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
+static int dual_tree_implicit_qt_split(VVCLocalContext *lc,
+    const int x0, const int y0, const int cb_size, const int cqt_depth)
+{
+    const VVCSH *sh     = &lc->sc->sh;
+    const VVCPPS *pps   = lc->fc->ps.pps;
+    const int cb_subdiv = 2 * cqt_depth;
+    int ret;
+
+    if (cb_size > 64) {
+        #define DUAL_TREE(x, y) do {                                                \
+            ret = dual_tree_implicit_qt_split(lc, x, y, cb_size / 2, cqt_depth + 1); \
+            if (ret < 0)                                                            \
+                return ret;                                                         \
+        } while (0)
+
+        const int x1 = x0 + (cb_size / 2);
+        const int y1 = y0 + (cb_size / 2);
+        if (pps->cu_qp_delta_enabled_flag && cb_subdiv <= sh->cu_qp_delta_subdiv) {
+            lc->parse.is_cu_qp_delta_coded = 0;
+            lc->parse.cu_qg_top_left_x = x0;
+            lc->parse.cu_qg_top_left_y = y0;
+        }
+        if (sh->cu_chroma_qp_offset_enabled_flag && cb_subdiv <= sh->cu_chroma_qp_offset_subdiv) {
+            lc->parse.is_cu_chroma_qp_offset_coded = 0;
+            memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset));
+        }
+        DUAL_TREE(x0, y0);
+        if (x1 < pps->width)
+            DUAL_TREE(x1, y0);
+        if (y1 < pps->height)
+            DUAL_TREE(x0, y1);
+        if (x1 < pps->width && y1 < pps->height)
+            DUAL_TREE(x1, y1);
+    #undef DUAL_TREE
+    } else {
+        #define CODING_TREE(tree_type) do {                                             \
+            const int qg_on_y = tree_type == DUAL_TREE_LUMA;                            \
+            ret = hls_coding_tree(lc, x0, y0, cb_size, cb_size, qg_on_y, !qg_on_y,           \
+                 cb_subdiv, cqt_depth, 0, 0, 0, SPLIT_NONE, tree_type, MODE_TYPE_ALL);  \
+            if (ret < 0)                                                                \
+                return ret;                                                             \
+        } while (0)
+        CODING_TREE(DUAL_TREE_LUMA);
+        CODING_TREE(DUAL_TREE_CHROMA);
+        #undef CODING_TREE
+    }
+    return 0;
+}
+
+#define SET_SAO(elem, value)                            \
+do {                                                    \
+    if (!sao_merge_up_flag && !sao_merge_left_flag)     \
+        sao->elem = value;                              \
+    else if (sao_merge_left_flag)                       \
+        sao->elem = CTB(fc->tab.sao, rx-1, ry).elem;         \
+    else if (sao_merge_up_flag)                         \
+        sao->elem = CTB(fc->tab.sao, rx, ry-1).elem;         \
+    else                                                \
+        sao->elem = 0;                                  \
+} while (0)
+
+static void hls_sao(VVCLocalContext *lc, const int rx, const int ry)
+{
+    VVCFrameContext *fc     = lc->fc;
+    const VVCSH *sh         = &lc->sc->sh;
+    int sao_merge_left_flag = 0;
+    int sao_merge_up_flag   = 0;
+    SAOParams *sao          = &CTB(fc->tab.sao, rx, ry);
+    int c_idx, i;
+
+    if (sh->sao_used_flag[0] || sh->sao_used_flag[1]) {
+        if (rx > 0) {
+            if (lc->ctb_left_flag)
+                sao_merge_left_flag = ff_vvc_sao_merge_flag_decode(lc);
+        }
+        if (ry > 0 && !sao_merge_left_flag) {
+            if (lc->ctb_up_flag)
+                sao_merge_up_flag = ff_vvc_sao_merge_flag_decode(lc);
+        }
+    }
+
+    for (c_idx = 0; c_idx < (fc->ps.sps->chroma_format_idc ? 3 : 1); c_idx++) {
+        if (!sh->sao_used_flag[c_idx]) {
+            sao->type_idx[c_idx] = SAO_NOT_APPLIED;
+            continue;
+        }
+
+        if (c_idx == 2) {
+            sao->type_idx[2] = sao->type_idx[1];
+            sao->eo_class[2] = sao->eo_class[1];
+        } else {
+            SET_SAO(type_idx[c_idx], ff_vvc_sao_type_idx_decode(lc));
+        }
+
+        if (sao->type_idx[c_idx] == SAO_NOT_APPLIED)
+            continue;
+
+        for (i = 0; i < 4; i++)
+            SET_SAO(offset_abs[c_idx][i], ff_vvc_sao_offset_abs_decode(lc));
+
+        if (sao->type_idx[c_idx] == SAO_BAND) {
+            for (i = 0; i < 4; i++) {
+                if (sao->offset_abs[c_idx][i]) {
+                    SET_SAO(offset_sign[c_idx][i],
+                            ff_vvc_sao_offset_sign_decode(lc));
+                } else {
+                    sao->offset_sign[c_idx][i] = 0;
+                }
+            }
+            SET_SAO(band_position[c_idx], ff_vvc_sao_band_position_decode(lc));
+        } else if (c_idx != 2) {
+            SET_SAO(eo_class[c_idx], ff_vvc_sao_eo_class_decode(lc));
+        }
+
+        // Inferred parameters
+        sao->offset_val[c_idx][0] = 0;
+        for (i = 0; i < 4; i++) {
+            sao->offset_val[c_idx][i + 1] = sao->offset_abs[c_idx][i];
+            if (sao->type_idx[c_idx] == SAO_EDGE) {
+                if (i > 1)
+                    sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1];
+            } else if (sao->offset_sign[c_idx][i]) {
+                sao->offset_val[c_idx][i + 1] = -sao->offset_val[c_idx][i + 1];
+            }
+            sao->offset_val[c_idx][i + 1] *= 1 << (fc->ps.sps->bit_depth - FFMIN(10, fc->ps.sps->bit_depth));
+        }
+    }
+}
+
+static void alf_params(VVCLocalContext *lc, const int rx, const int ry)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    const VVCSH *sh             = &lc->sc->sh;
+    ALFParams *alf              = &CTB(fc->tab.alf, rx, ry);
+
+    alf->ctb_flag[LUMA] = alf->ctb_flag[CB] = alf->ctb_flag[CR] = 0;
+    if (sh->alf.enabled_flag[LUMA]) {
+        alf->ctb_flag[LUMA] = ff_vvc_alf_ctb_flag(lc, rx, ry, LUMA);
+        if (alf->ctb_flag[LUMA]) {
+            int alf_use_aps_flag = 0;
+            if (sh->alf.num_aps_ids_luma > 0) {
+                alf_use_aps_flag = ff_vvc_alf_use_aps_flag(lc);
+            }
+            if (alf_use_aps_flag) {
+                alf->ctb_filt_set_idx_y = 16;
+                if (sh->alf.num_aps_ids_luma > 1)
+                    alf->ctb_filt_set_idx_y += ff_vvc_alf_luma_prev_filter_idx(lc);
+            } else {
+                alf->ctb_filt_set_idx_y = ff_vvc_alf_luma_fixed_filter_idx(lc);
+            }
+        }
+        for (int c_idx = CB; c_idx <= CR; c_idx++) {
+            if (sh->alf.enabled_flag[c_idx]) {
+                const VVCALF *aps = (VVCALF*)fc->ps.alf_list[sh->alf.aps_id_chroma]->data;
+                alf->ctb_flag[c_idx] = ff_vvc_alf_ctb_flag(lc, rx, ry, c_idx);
+                alf->alf_ctb_filter_alt_idx[c_idx - 1] = 0;
+                if (alf->ctb_flag[c_idx] && aps->num_chroma_filters > 1)
+                    alf->alf_ctb_filter_alt_idx[c_idx - 1] = ff_vvc_alf_ctb_filter_alt_idx(lc, c_idx, aps->num_chroma_filters);
+            }
+        }
+    }
+    for (int i = 0; i < 2; i++) {
+        alf->ctb_cc_idc[i] = 0;
+        if (sh->alf.cc_enabled_flag[i]) {
+            const VVCALF *aps = (VVCALF*)fc->ps.alf_list[sh->alf.cc_aps_id[i]]->data;
+            alf->ctb_cc_idc[i] = ff_vvc_alf_ctb_cc_idc(lc, rx, ry, i, aps->cc_filters_signalled[i]);
+        }
+    }
+}
+
+static void deblock_params(VVCLocalContext *lc, const int rx, const int ry)
+{
+    VVCFrameContext *fc = lc->fc;
+    const VVCSH *sh     = &lc->sc->sh;
+    CTB(fc->tab.deblock, rx, ry) = sh->deblock;
+}
+
+static int hls_coding_tree_unit(VVCLocalContext *lc,  int x0, int y0)
+{
+    int ret = 0;
+    const VVCFrameContext *fc   = lc->fc;
+    const VVCSPS *sps           = fc->ps.sps;
+    const VVCSH *sh             = &lc->sc->sh;
+    const unsigned int ctb_size = sps->ctb_size_y;
+
+    memset(lc->parse.chroma_qp_offset, 0, sizeof(lc->parse.chroma_qp_offset));
+
+    hls_sao(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y);
+    alf_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y);
+    deblock_params(lc, x0 >> sps->ctb_log2_size_y, y0 >> sps->ctb_log2_size_y);
+
+    if (IS_I(sh) && sps->qtbtt_dual_tree_intra_flag)
+        ret = dual_tree_implicit_qt_split(lc, x0, y0, ctb_size, 0);
+    else
+        ret = hls_coding_tree(lc, x0, y0, ctb_size, ctb_size,
+            1, 1, 0, 0, 0, 0, 0, SPLIT_NONE, SINGLE_TREE, MODE_TYPE_ALL);
+    if (ret < 0)
+        return ret;
+
+    return 0;
+}
+
+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;
+}
+
+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->pred_mode != MODE_INTRA && cu->pred_mode != MODE_PLT && cu->tree_type != DUAL_TREE_CHROMA) {
+            ff_vvc_predict_inter(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_coding_tree_unit(VVCLocalContext *lc, const int ctb_addr, const int rs, const int rx, const int ry)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    const VVCSPS *sps           = fc->ps.sps;
+    const VVCPPS *pps           = fc->ps.pps;
+    const VVCSH *sh             = &lc->sc->sh;
+    const int x_ctb             = rx << sps->ctb_log2_size_y;
+    const int y_ctb             = ry << sps->ctb_log2_size_y;
+    const int ctb_size          = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y;
+    EntryPoint* ep              = lc->ep;
+    int ret;
+
+    if (rx == pps->ctb_to_col_bd[rx]) {
+        //fix me for ibc
+        ep->num_hmvp = 0;
+        ep->is_first_qg = ry == pps->ctb_to_row_bd[ry] || !ctb_addr;
+    }
+
+    lc->coeffs = fc->tab.coeffs + (ry * pps->ctb_width + rx) * ctb_size * VVC_MAX_SAMPLE_ARRAYS;
+
+    ff_vvc_cabac_init(lc, ctb_addr, rx, ry);
+    fc->tab.slice_idx[rs] = lc->sc->slice_idx;
+    ff_vvc_decode_neighbour(lc, x_ctb, y_ctb, rx, ry, rs);
+    ret = hls_coding_tree_unit(lc, x_ctb, y_ctb);
+    if (ret < 0)
+        return ret;
+
+    if (rx == pps->ctb_to_col_bd[rx + 1] - 1) {
+        if (ctb_addr == sh->num_ctus_in_curr_slice - 1) {
+            const int end_of_slice_one_bit = ff_vvc_end_of_slice_flag_decode(lc);
+            if (!end_of_slice_one_bit)
+                return AVERROR_INVALIDDATA;
+        } else {
+            if (ry == pps->ctb_to_row_bd[ry + 1] - 1) {
+                const int end_of_tile_one_bit = ff_vvc_end_of_tile_one_bit(lc);
+                if (!end_of_tile_one_bit)
+                    return AVERROR_INVALIDDATA;
+            } else {
+                if (fc->ps.sps->entropy_coding_sync_enabled_flag) {
+                    const int end_of_subset_one_bit = ff_vvc_end_of_subset_one_bit(lc);
+                    if (!end_of_subset_one_bit)
+                        return AVERROR_INVALIDDATA;
+                }
+            }
+        }
+    }
+    return 0;
+}
diff --git a/libavcodec/vvc_ctu.h b/libavcodec/vvc_ctu.h
new file mode 100644
index 0000000000..ef60f9c7c1
--- /dev/null
+++ b/libavcodec/vvc_ctu.h
@@ -0,0 +1,357 @@
+/*
+ * VVC CTU 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_CTU_H
+#define AVCODEC_VVC_CTU_H
+
+#include "vvcdec.h"
+
+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];
+} 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_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;
+
+    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;
+
+struct CodingUnit;
+typedef struct CodingUnit {
+    AVBufferRef *buf;
+
+    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;
+
+    // Inferred parameters
+    enum IspType isp_split_type;            ///< IntraSubPartitionsSplitType
+
+    enum PredMode pred_mode;                ///< PredMode
+
+    //inter
+    uint8_t ciip_flag;
+
+    // Inferred parameters
+    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[3];                      ///< BdpcmFlag
+    int bdpcm_dir[3];                       ///< BdpcmDir
+
+    int apply_lfnst_flag[VVC_MAX_SAMPLE_ARRAYS];    ///< ApplyLfnstFlag[]
+
+    TransformUnit tus[MAX_TUS_IN_CU];
+    int num_tus;
+
+    int8_t qp[4];                         ///< QpY, Qp′Cb, Qp′Cr, Qp′CbCr
+
+    PredictionUnit pu;
+
+    struct CodingUnit *next;
+} CodingUnit;
+
+struct CTU {
+    CodingUnit *cus;
+};
+
+typedef struct ReconstructedArea {
+    int x;
+    int y;
+    int w;
+    int h;
+} ReconstructedArea;
+
+// 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
+
+    VVCCabacState cabac_state[VVC_CONTEXTS];
+    CABACContext cc;
+
+    VVCTask *parse_task;
+    int ctu_addr_last;
+
+    uint8_t is_first_qg;
+    MvField hmvp[MAX_NUM_HMVP_CANDS];               ///< HmvpCandList
+    int     num_hmvp;                               ///< NumHmvpCand
+} EntryPoint;
+
+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, 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];
+
+    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;
+} ;
+
+typedef struct VVCAllowedSplit {
+    int qt;
+    int btv;
+    int bth;
+    int ttv;
+    int tth;
+} VVCAllowedSplit;
+
+void ff_vvc_decode_neighbour(VVCLocalContext *lc, int x_ctb, int y_ctb, int rx, int ry, int rs);
+int ff_vvc_coding_tree_unit(VVCLocalContext *lc, int ctb_addr, int rs, int rx, int ry);
+int ff_vvc_reconstruct(VVCLocalContext *lc, const int rs, const int rx, const int ry);
+
+int ff_vvc_inter_data(VVCLocalContext *lc);
+int ff_vvc_predict_inter(VVCLocalContext *lc);
+void ff_vvc_set_cb_tab(const VVCLocalContext *lc, uint8_t *tab, uint8_t v);
+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_CTU_H
diff --git a/libavcodec/vvc_data.c b/libavcodec/vvc_data.c
new file mode 100644
index 0000000000..d2d4e959e1
--- /dev/null
+++ b/libavcodec/vvc_data.c
@@ -0,0 +1,3610 @@
+/*
+ * 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];
+}
+
+const int8_t ff_vvc_dct2[DCT2_MAX_SIZE][DCT2_MAX_SIZE] = {
+    {
+         64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,
+         64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,
+         64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,
+         64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,  64,
+    },
+    {
+         91,  90,  90,  90,  88,  87,  86,  84,  83,  81,  79,  77,  73,  71,  69,  65,
+         62,  59,  56,  52,  48,  44,  41,  37,  33,  28,  24,  20,  15,  11,   7,   2,
+         -2,  -7, -11, -15, -20, -24, -28, -33, -37, -41, -44, -48, -52, -56, -59, -62,
+        -65, -69, -71, -73, -77, -79, -81, -83, -84, -86, -87, -88, -90, -90, -90, -91,
+    },
+    {
+         90,  90,  88,  85,  82,  78,  73,  67,  61,  54,  46,  38,  31,  22,  13,   4,
+         -4, -13, -22, -31, -38, -46, -54, -61, -67, -73, -78, -82, -85, -88, -90, -90,
+        -90, -90, -88, -85, -82, -78, -73, -67, -61, -54, -46, -38, -31, -22, -13,  -4,
+          4,  13,  22,  31,  38,  46,  54,  61,  67,  73,  78,  82,  85,  88,  90,  90,
+    },
+    {
+         90,  88,  84,  79,  71,  62,  52,  41,  28,  15,   2, -11, -24, -37, -48, -59,
+        -69, -77, -83, -87, -90, -91, -90, -86, -81, -73, -65, -56, -44, -33, -20,  -7,
+          7,  20,  33,  44,  56,  65,  73,  81,  86,  90,  91,  90,  87,  83,  77,  69,
+         59,  48,  37,  24,  11,  -2, -15, -28, -41, -52, -62, -71, -79, -84, -88, -90,
+    },
+    {
+         90,  87,  80,  70,  57,  43,  25,   9,  -9, -25, -43, -57, -70, -80, -87, -90,
+        -90, -87, -80, -70, -57, -43, -25,  -9,   9,  25,  43,  57,  70,  80,  87,  90,
+         90,  87,  80,  70,  57,  43,  25,   9,  -9, -25, -43, -57, -70, -80, -87, -90,
+        -90, -87, -80, -70, -57, -43, -25,  -9,   9,  25,  43,  57,  70,  80,  87,  90,
+    },
+    {
+         90,  84,  73,  59,  41,  20,  -2, -24, -44, -62, -77, -86, -90, -90, -83, -71,
+        -56, -37, -15,   7,  28,  48,  65,  79,  87,  91,  88,  81,  69,  52,  33,  11,
+        -11, -33, -52, -69, -81, -88, -91, -87, -79, -65, -48, -28,  -7,  15,  37,  56,
+         71,  83,  90,  90,  86,  77,  62,  44,  24,   2, -20, -41, -59, -73, -84, -90,
+    },
+    {
+         90,  82,  67,  46,  22,  -4, -31, -54, -73, -85, -90, -88, -78, -61, -38, -13,
+         13,  38,  61,  78,  88,  90,  85,  73,  54,  31,   4, -22, -46, -67, -82, -90,
+        -90, -82, -67, -46, -22,   4,  31,  54,  73,  85,  90,  88,  78,  61,  38,  13,
+        -13, -38, -61, -78, -88, -90, -85, -73, -54, -31,  -4,  22,  46,  67,  82,  90,
+    },
+    {
+         90,  79,  59,  33,   2, -28, -56, -77, -88, -90, -81, -62, -37,  -7,  24,  52,
+         73,  87,  90,  83,  65,  41,  11, -20, -48, -71, -86, -91, -84, -69, -44, -15,
+         15,  44,  69,  84,  91,  86,  71,  48,  20, -11, -41, -65, -83, -90, -87, -73,
+        -52, -24,   7,  37,  62,  81,  90,  88,  77,  56,  28,  -2, -33, -59, -79, -90,
+    },
+    {
+         89,  75,  50,  18, -18, -50, -75, -89, -89, -75, -50, -18,  18,  50,  75,  89,
+         89,  75,  50,  18, -18, -50, -75, -89, -89, -75, -50, -18,  18,  50,  75,  89,
+         89,  75,  50,  18, -18, -50, -75, -89, -89, -75, -50, -18,  18,  50,  75,  89,
+         89,  75,  50,  18, -18, -50, -75, -89, -89, -75, -50, -18,  18,  50,  75,  89,
+    },
+    {
+         88,  71,  41,   2, -37, -69, -87, -90, -73, -44,  -7,  33,  65,  86,  90,  77,
+         48,  11, -28, -62, -84, -90, -79, -52, -15,  24,  59,  83,  91,  81,  56,  20,
+        -20, -56, -81, -91, -83, -59, -24,  15,  52,  79,  90,  84,  62,  28, -11, -48,
+        -77, -90, -86, -65, -33,   7,  44,  73,  90,  87,  69,  37,  -2, -41, -71, -88,
+    },
+    {
+         88,  67,  31, -13, -54, -82, -90, -78, -46,  -4,  38,  73,  90,  85,  61,  22,
+        -22, -61, -85, -90, -73, -38,   4,  46,  78,  90,  82,  54,  13, -31, -67, -88,
+        -88, -67, -31,  13,  54,  82,  90,  78,  46,   4, -38, -73, -90, -85, -61, -22,
+         22,  61,  85,  90,  73,  38,  -4, -46, -78, -90, -82, -54, -13,  31,  67,  88,
+    },
+    {
+         87,  62,  20, -28, -69, -90, -84, -56, -11,  37,  73,  90,  81,  48,   2, -44,
+        -79, -91, -77, -41,   7,  52,  83,  90,  71,  33, -15, -59, -86, -88, -65, -24,
+         24,  65,  88,  86,  59,  15, -33, -71, -90, -83, -52,  -7,  41,  77,  91,  79,
+         44,  -2, -48, -81, -90, -73, -37,  11,  56,  84,  90,  69,  28, -20, -62, -87,
+    },
+    {
+         87,  57,   9, -43, -80, -90, -70, -25,  25,  70,  90,  80,  43,  -9, -57, -87,
+        -87, -57,  -9,  43,  80,  90,  70,  25, -25, -70, -90, -80, -43,   9,  57,  87,
+         87,  57,   9, -43, -80, -90, -70, -25,  25,  70,  90,  80,  43,  -9, -57, -87,
+        -87, -57,  -9,  43,  80,  90,  70,  25, -25, -70, -90, -80, -43,   9,  57,  87,
+    },
+    {
+         86,  52,  -2, -56, -87, -84, -48,   7,  59,  88,  83,  44, -11, -62, -90, -81,
+        -41,  15,  65,  90,  79,  37, -20, -69, -90, -77, -33,  24,  71,  91,  73,  28,
+        -28, -73, -91, -71, -24,  33,  77,  90,  69,  20, -37, -79, -90, -65, -15,  41,
+         81,  90,  62,  11, -44, -83, -88, -59,  -7,  48,  84,  87,  56,   2, -52, -86,
+    },
+    {
+         85,  46, -13, -67, -90, -73, -22,  38,  82,  88,  54,  -4, -61, -90, -78, -31,
+         31,  78,  90,  61,   4, -54, -88, -82, -38,  22,  73,  90,  67,  13, -46, -85,
+        -85, -46,  13,  67,  90,  73,  22, -38, -82, -88, -54,   4,  61,  90,  78,  31,
+        -31, -78, -90, -61,  -4,  54,  88,  82,  38, -22, -73, -90, -67, -13,  46,  85,
+    },
+    {
+         84,  41, -24, -77, -90, -56,   7,  65,  91,  69,  11, -52, -88, -79, -28,  37,
+         83,  86,  44, -20, -73, -90, -59,   2,  62,  90,  71,  15, -48, -87, -81, -33,
+         33,  81,  87,  48, -15, -71, -90, -62,  -2,  59,  90,  73,  20, -44, -86, -83,
+        -37,  28,  79,  88,  52, -11, -69, -91, -65,  -7,  56,  90,  77,  24, -41, -84,
+    },
+    {
+         83,  36, -36, -83, -83, -36,  36,  83,  83,  36, -36, -83, -83, -36,  36,  83,
+         83,  36, -36, -83, -83, -36,  36,  83,  83,  36, -36, -83, -83, -36,  36,  83,
+         83,  36, -36, -83, -83, -36,  36,  83,  83,  36, -36, -83, -83, -36,  36,  83,
+         83,  36, -36, -83, -83, -36,  36,  83,  83,  36, -36, -83, -83, -36,  36,  83,
+    },
+    {
+         83,  28, -44, -88, -73, -11,  59,  91,  62,  -7, -71, -90, -48,  24,  81,  84,
+         33, -41, -87, -77, -15,  56,  90,  65,  -2, -69, -90, -52,  20,  79,  86,  37,
+        -37, -86, -79, -20,  52,  90,  69,   2, -65, -90, -56,  15,  77,  87,  41, -33,
+        -84, -81, -24,  48,  90,  71,   7, -62, -91, -59,  11,  73,  88,  44, -28, -83,
+    },
+    {
+         82,  22, -54, -90, -61,  13,  78,  85,  31, -46, -90, -67,   4,  73,  88,  38,
+        -38, -88, -73,  -4,  67,  90,  46, -31, -85, -78, -13,  61,  90,  54, -22, -82,
+        -82, -22,  54,  90,  61, -13, -78, -85, -31,  46,  90,  67,  -4, -73, -88, -38,
+         38,  88,  73,   4, -67, -90, -46,  31,  85,  78,  13, -61, -90, -54,  22,  82,
+    },
+    {
+         81,  15, -62, -90, -44,  37,  88,  69,  -7, -77, -84, -24,  56,  91,  52, -28,
+        -86, -73,  -2,  71,  87,  33, -48, -90, -59,  20,  83,  79,  11, -65, -90, -41,
+         41,  90,  65, -11, -79, -83, -20,  59,  90,  48, -33, -87, -71,   2,  73,  86,
+         28, -52, -91, -56,  24,  84,  77,   7, -69, -88, -37,  44,  90,  62, -15, -81,
+    },
+    {
+         80,   9, -70, -87, -25,  57,  90,  43, -43, -90, -57,  25,  87,  70,  -9, -80,
+        -80,  -9,  70,  87,  25, -57, -90, -43,  43,  90,  57, -25, -87, -70,   9,  80,
+         80,   9, -70, -87, -25,  57,  90,  43, -43, -90, -57,  25,  87,  70,  -9, -80,
+        -80,  -9,  70,  87,  25, -57, -90, -43,  43,  90,  57, -25, -87, -70,   9,  80,
+    },
+    {
+         79,   2, -77, -81,  -7,  73,  83,  11, -71, -84, -15,  69,  86,  20, -65, -87,
+        -24,  62,  88,  28, -59, -90, -33,  56,  90,  37, -52, -90, -41,  48,  91,  44,
+        -44, -91, -48,  41,  90,  52, -37, -90, -56,  33,  90,  59, -28, -88, -62,  24,
+         87,  65, -20, -86, -69,  15,  84,  71, -11, -83, -73,   7,  81,  77,  -2, -79,
+    },
+    {
+         78,  -4, -82, -73,  13,  85,  67, -22, -88, -61,  31,  90,  54, -38, -90, -46,
+         46,  90,  38, -54, -90, -31,  61,  88,  22, -67, -85, -13,  73,  82,   4, -78,
+        -78,   4,  82,  73, -13, -85, -67,  22,  88,  61, -31, -90, -54,  38,  90,  46,
+        -46, -90, -38,  54,  90,  31, -61, -88, -22,  67,  85,  13, -73, -82,  -4,  78,
+    },
+    {
+         77, -11, -86, -62,  33,  90,  44, -52, -90, -24,  69,  83,   2, -81, -71,  20,
+         88,  56, -41, -91, -37,  59,  87,  15, -73, -79,   7,  84,  65, -28, -90, -48,
+         48,  90,  28, -65, -84,  -7,  79,  73, -15, -87, -59,  37,  91,  41, -56, -88,
+        -20,  71,  81,  -2, -83, -69,  24,  90,  52, -44, -90, -33,  62,  86,  11, -77,
+    },
+    {
+         75, -18, -89, -50,  50,  89,  18, -75, -75,  18,  89,  50, -50, -89, -18,  75,
+         75, -18, -89, -50,  50,  89,  18, -75, -75,  18,  89,  50, -50, -89, -18,  75,
+         75, -18, -89, -50,  50,  89,  18, -75, -75,  18,  89,  50, -50, -89, -18,  75,
+         75, -18, -89, -50,  50,  89,  18, -75, -75,  18,  89,  50, -50, -89, -18,  75,
+    },
+    {
+         73, -24, -90, -37,  65,  81, -11, -88, -48,  56,  86,   2, -84, -59,  44,  90,
+         15, -79, -69,  33,  91,  28, -71, -77,  20,  90,  41, -62, -83,   7,  87,  52,
+        -52, -87,  -7,  83,  62, -41, -90, -20,  77,  71, -28, -91, -33,  69,  79, -15,
+        -90, -44,  59,  84,  -2, -86, -56,  48,  88,  11, -81, -65,  37,  90,  24, -73,
+    },
+    {
+         73, -31, -90, -22,  78,  67, -38, -90, -13,  82,  61, -46, -88,  -4,  85,  54,
+        -54, -85,   4,  88,  46, -61, -82,  13,  90,  38, -67, -78,  22,  90,  31, -73,
+        -73,  31,  90,  22, -78, -67,  38,  90,  13, -82, -61,  46,  88,   4, -85, -54,
+         54,  85,  -4, -88, -46,  61,  82, -13, -90, -38,  67,  78, -22, -90, -31,  73,
+    },
+    {
+         71, -37, -90,  -7,  86,  48, -62, -79,  24,  91,  20, -81, -59,  52,  84, -11,
+        -90, -33,  73,  69, -41, -88,  -2,  87,  44, -65, -77,  28,  90,  15, -83, -56,
+         56,  83, -15, -90, -28,  77,  65, -44, -87,   2,  88,  41, -69, -73,  33,  90,
+         11, -84, -52,  59,  81, -20, -91, -24,  79,  62, -48, -86,   7,  90,  37, -71,
+    },
+    {
+         70, -43, -87,   9,  90,  25, -80, -57,  57,  80, -25, -90,  -9,  87,  43, -70,
+        -70,  43,  87,  -9, -90, -25,  80,  57, -57, -80,  25,  90,   9, -87, -43,  70,
+         70, -43, -87,   9,  90,  25, -80, -57,  57,  80, -25, -90,  -9,  87,  43, -70,
+        -70,  43,  87,  -9, -90, -25,  80,  57, -57, -80,  25,  90,   9, -87, -43,  70,
+    },
+    {
+         69, -48, -83,  24,  90,   2, -90, -28,  81,  52, -65, -71,  44,  84, -20, -90,
+         -7,  88,  33, -79, -56,  62,  73, -41, -86,  15,  91,  11, -87, -37,  77,  59,
+        -59, -77,  37,  87, -11, -91, -15,  86,  41, -73, -62,  56,  79, -33, -88,   7,
+         90,  20, -84, -44,  71,  65, -52, -81,  28,  90,  -2, -90, -24,  83,  48, -69,
+    },
+    {
+         67, -54, -78,  38,  85, -22, -90,   4,  90,  13, -88, -31,  82,  46, -73, -61,
+         61,  73, -46, -82,  31,  88, -13, -90,  -4,  90,  22, -85, -38,  78,  54, -67,
+        -67,  54,  78, -38, -85,  22,  90,  -4, -90, -13,  88,  31, -82, -46,  73,  61,
+        -61, -73,  46,  82, -31, -88,  13,  90,   4, -90, -22,  85,  38, -78, -54,  67,
+    },
+    {
+         65, -59, -71,  52,  77, -44, -81,  37,  84, -28, -87,  20,  90, -11, -90,   2,
+         91,   7, -90, -15,  88,  24, -86, -33,  83,  41, -79, -48,  73,  56, -69, -62,
+         62,  69, -56, -73,  48,  79, -41, -83,  33,  86, -24, -88,  15,  90,  -7, -91,
+         -2,  90,  11, -90, -20,  87,  28, -84, -37,  81,  44, -77, -52,  71,  59, -65,
+    },
+    {
+         64, -64, -64,  64,  64, -64, -64,  64,  64, -64, -64,  64,  64, -64, -64,  64,
+         64, -64, -64,  64,  64, -64, -64,  64,  64, -64, -64,  64,  64, -64, -64,  64,
+         64, -64, -64,  64,  64, -64, -64,  64,  64, -64, -64,  64,  64, -64, -64,  64,
+         64, -64, -64,  64,  64, -64, -64,  64,  64, -64, -64,  64,  64, -64, -64,  64,
+    },
+    {
+         62, -69, -56,  73,  48, -79, -41,  83,  33, -86, -24,  88,  15, -90,  -7,  91,
+         -2, -90,  11,  90, -20, -87,  28,  84, -37, -81,  44,  77, -52, -71,  59,  65,
+        -65, -59,  71,  52, -77, -44,  81,  37, -84, -28,  87,  20, -90, -11,  90,   2,
+        -91,   7,  90, -15, -88,  24,  86, -33, -83,  41,  79, -48, -73,  56,  69, -62,
+    },
+    {
+         61, -73, -46,  82,  31, -88, -13,  90,  -4, -90,  22,  85, -38, -78,  54,  67,
+        -67, -54,  78,  38, -85, -22,  90,   4, -90,  13,  88, -31, -82,  46,  73, -61,
+        -61,  73,  46, -82, -31,  88,  13, -90,   4,  90, -22, -85,  38,  78, -54, -67,
+         67,  54, -78, -38,  85,  22, -90,  -4,  90, -13, -88,  31,  82, -46, -73,  61,
+    },
+    {
+         59, -77, -37,  87,  11, -91,  15,  86, -41, -73,  62,  56, -79, -33,  88,   7,
+        -90,  20,  84, -44, -71,  65,  52, -81, -28,  90,   2, -90,  24,  83, -48, -69,
+         69,  48, -83, -24,  90,  -2, -90,  28,  81, -52, -65,  71,  44, -84, -20,  90,
+         -7, -88,  33,  79, -56, -62,  73,  41, -86, -15,  91, -11, -87,  37,  77, -59,
+    },
+    {
+         57, -80, -25,  90,  -9, -87,  43,  70, -70, -43,  87,   9, -90,  25,  80, -57,
+        -57,  80,  25, -90,   9,  87, -43, -70,  70,  43, -87,  -9,  90, -25, -80,  57,
+         57, -80, -25,  90,  -9, -87,  43,  70, -70, -43,  87,   9, -90,  25,  80, -57,
+        -57,  80,  25, -90,   9,  87, -43, -70,  70,  43, -87,  -9,  90, -25, -80,  57,
+    },
+    {
+         56, -83, -15,  90, -28, -77,  65,  44, -87,  -2,  88, -41, -69,  73,  33, -90,
+         11,  84, -52, -59,  81,  20, -91,  24,  79, -62, -48,  86,   7, -90,  37,  71,
+        -71, -37,  90,  -7, -86,  48,  62, -79, -24,  91, -20, -81,  59,  52, -84, -11,
+         90, -33, -73,  69,  41, -88,   2,  87, -44, -65,  77,  28, -90,  15,  83, -56,
+    },
+    {
+         54, -85,  -4,  88, -46, -61,  82,  13, -90,  38,  67, -78, -22,  90, -31, -73,
+         73,  31, -90,  22,  78, -67, -38,  90, -13, -82,  61,  46, -88,   4,  85, -54,
+        -54,  85,   4, -88,  46,  61, -82, -13,  90, -38, -67,  78,  22, -90,  31,  73,
+        -73, -31,  90, -22, -78,  67,  38, -90,  13,  82, -61, -46,  88,  -4, -85,  54,
+    },
+    {
+         52, -87,   7,  83, -62, -41,  90, -20, -77,  71,  28, -91,  33,  69, -79, -15,
+         90, -44, -59,  84,   2, -86,  56,  48, -88,  11,  81, -65, -37,  90, -24, -73,
+         73,  24, -90,  37,  65, -81, -11,  88, -48, -56,  86,  -2, -84,  59,  44, -90,
+         15,  79, -69, -33,  91, -28, -71,  77,  20, -90,  41,  62, -83,  -7,  87, -52,
+    },
+    {
+         50, -89,  18,  75, -75, -18,  89, -50, -50,  89, -18, -75,  75,  18, -89,  50,
+         50, -89,  18,  75, -75, -18,  89, -50, -50,  89, -18, -75,  75,  18, -89,  50,
+         50, -89,  18,  75, -75, -18,  89, -50, -50,  89, -18, -75,  75,  18, -89,  50,
+         50, -89,  18,  75, -75, -18,  89, -50, -50,  89, -18, -75,  75,  18, -89,  50,
+    },
+    {
+         48, -90,  28,  65, -84,   7,  79, -73, -15,  87, -59, -37,  91, -41, -56,  88,
+        -20, -71,  81,   2, -83,  69,  24, -90,  52,  44, -90,  33,  62, -86,  11,  77,
+        -77, -11,  86, -62, -33,  90, -44, -52,  90, -24, -69,  83,  -2, -81,  71,  20,
+        -88,  56,  41, -91,  37,  59, -87,  15,  73, -79,  -7,  84, -65, -28,  90, -48,
+    },
+    {
+         46, -90,  38,  54, -90,  31,  61, -88,  22,  67, -85,  13,  73, -82,   4,  78,
+        -78,  -4,  82, -73, -13,  85, -67, -22,  88, -61, -31,  90, -54, -38,  90, -46,
+        -46,  90, -38, -54,  90, -31, -61,  88, -22, -67,  85, -13, -73,  82,  -4, -78,
+         78,   4, -82,  73,  13, -85,  67,  22, -88,  61,  31, -90,  54,  38, -90,  46,
+    },
+    {
+         44, -91,  48,  41, -90,  52,  37, -90,  56,  33, -90,  59,  28, -88,  62,  24,
+        -87,  65,  20, -86,  69,  15, -84,  71,  11, -83,  73,   7, -81,  77,   2, -79,
+         79,  -2, -77,  81,  -7, -73,  83, -11, -71,  84, -15, -69,  86, -20, -65,  87,
+        -24, -62,  88, -28, -59,  90, -33, -56,  90, -37, -52,  90, -41, -48,  91, -44,
+    },
+    {
+         43, -90,  57,  25, -87,  70,   9, -80,  80,  -9, -70,  87, -25, -57,  90, -43,
+        -43,  90, -57, -25,  87, -70,  -9,  80, -80,   9,  70, -87,  25,  57, -90,  43,
+         43, -90,  57,  25, -87,  70,   9, -80,  80,  -9, -70,  87, -25, -57,  90, -43,
+        -43,  90, -57, -25,  87, -70,  -9,  80, -80,   9,  70, -87,  25,  57, -90,  43,
+    },
+    {
+         41, -90,  65,  11, -79,  83, -20, -59,  90, -48, -33,  87, -71,  -2,  73, -86,
+         28,  52, -91,  56,  24, -84,  77,  -7, -69,  88, -37, -44,  90, -62, -15,  81,
+        -81,  15,  62, -90,  44,  37, -88,  69,   7, -77,  84, -24, -56,  91, -52, -28,
+         86, -73,   2,  71, -87,  33,  48, -90,  59,  20, -83,  79, -11, -65,  90, -41,
+    },
+    {
+         38, -88,  73,  -4, -67,  90, -46, -31,  85, -78,  13,  61, -90,  54,  22, -82,
+         82, -22, -54,  90, -61, -13,  78, -85,  31,  46, -90,  67,   4, -73,  88, -38,
+        -38,  88, -73,   4,  67, -90,  46,  31, -85,  78, -13, -61,  90, -54, -22,  82,
+        -82,  22,  54, -90,  61,  13, -78,  85, -31, -46,  90, -67,  -4,  73, -88,  38,
+    },
+    {
+         37, -86,  79, -20, -52,  90, -69,   2,  65, -90,  56,  15, -77,  87, -41, -33,
+         84, -81,  24,  48, -90,  71,  -7, -62,  91, -59, -11,  73, -88,  44,  28, -83,
+         83, -28, -44,  88, -73,  11,  59, -91,  62,   7, -71,  90, -48, -24,  81, -84,
+         33,  41, -87,  77, -15, -56,  90, -65,  -2,  69, -90,  52,  20, -79,  86, -37,
+    },
+    {
+         36, -83,  83, -36, -36,  83, -83,  36,  36, -83,  83, -36, -36,  83, -83,  36,
+         36, -83,  83, -36, -36,  83, -83,  36,  36, -83,  83, -36, -36,  83, -83,  36,
+         36, -83,  83, -36, -36,  83, -83,  36,  36, -83,  83, -36, -36,  83, -83,  36,
+         36, -83,  83, -36, -36,  83, -83,  36,  36, -83,  83, -36, -36,  83, -83,  36,
+    },
+    {
+         33, -81,  87, -48, -15,  71, -90,  62,  -2, -59,  90, -73,  20,  44, -86,  83,
+        -37, -28,  79, -88,  52,  11, -69,  91, -65,   7,  56, -90,  77, -24, -41,  84,
+        -84,  41,  24, -77,  90, -56,  -7,  65, -91,  69, -11, -52,  88, -79,  28,  37,
+        -83,  86, -44, -20,  73, -90,  59,   2, -62,  90, -71,  15,  48, -87,  81, -33,
+    },
+    {
+         31, -78,  90, -61,   4,  54, -88,  82, -38, -22,  73, -90,  67, -13, -46,  85,
+        -85,  46,  13, -67,  90, -73,  22,  38, -82,  88, -54,  -4,  61, -90,  78, -31,
+        -31,  78, -90,  61,  -4, -54,  88, -82,  38,  22, -73,  90, -67,  13,  46, -85,
+         85, -46, -13,  67, -90,  73, -22, -38,  82, -88,  54,   4, -61,  90, -78,  31,
+    },
+    {
+         28, -73,  91, -71,  24,  33, -77,  90, -69,  20,  37, -79,  90, -65,  15,  41,
+        -81,  90, -62,  11,  44, -83,  88, -59,   7,  48, -84,  87, -56,   2,  52, -86,
+         86, -52,  -2,  56, -87,  84, -48,  -7,  59, -88,  83, -44, -11,  62, -90,  81,
+        -41, -15,  65, -90,  79, -37, -20,  69, -90,  77, -33, -24,  71, -91,  73, -28,
+    },
+    {
+         25, -70,  90, -80,  43,   9, -57,  87, -87,  57,  -9, -43,  80, -90,  70, -25,
+        -25,  70, -90,  80, -43,  -9,  57, -87,  87, -57,   9,  43, -80,  90, -70,  25,
+         25, -70,  90, -80,  43,   9, -57,  87, -87,  57,  -9, -43,  80, -90,  70, -25,
+        -25,  70, -90,  80, -43,  -9,  57, -87,  87, -57,   9,  43, -80,  90, -70,  25,
+    },
+    {
+         24, -65,  88, -86,  59, -15, -33,  71, -90,  83, -52,   7,  41, -77,  91, -79,
+         44,   2, -48,  81, -90,  73, -37, -11,  56, -84,  90, -69,  28,  20, -62,  87,
+        -87,  62, -20, -28,  69, -90,  84, -56,  11,  37, -73,  90, -81,  48,  -2, -44,
+         79, -91,  77, -41,  -7,  52, -83,  90, -71,  33,  15, -59,  86, -88,  65, -24,
+    },
+    {
+         22, -61,  85, -90,  73, -38,  -4,  46, -78,  90, -82,  54, -13, -31,  67, -88,
+         88, -67,  31,  13, -54,  82, -90,  78, -46,   4,  38, -73,  90, -85,  61, -22,
+        -22,  61, -85,  90, -73,  38,   4, -46,  78, -90,  82, -54,  13,  31, -67,  88,
+        -88,  67, -31, -13,  54, -82,  90, -78,  46,  -4, -38,  73, -90,  85, -61,  22,
+    },
+    {
+         20, -56,  81, -91,  83, -59,  24,  15, -52,  79, -90,  84, -62,  28,  11, -48,
+         77, -90,  86, -65,  33,   7, -44,  73, -90,  87, -69,  37,   2, -41,  71, -88,
+         88, -71,  41,  -2, -37,  69, -87,  90, -73,  44,  -7, -33,  65, -86,  90, -77,
+         48, -11, -28,  62, -84,  90, -79,  52, -15, -24,  59, -83,  91, -81,  56, -20,
+    },
+    {
+         18, -50,  75, -89,  89, -75,  50, -18, -18,  50, -75,  89, -89,  75, -50,  18,
+         18, -50,  75, -89,  89, -75,  50, -18, -18,  50, -75,  89, -89,  75, -50,  18,
+         18, -50,  75, -89,  89, -75,  50, -18, -18,  50, -75,  89, -89,  75, -50,  18,
+         18, -50,  75, -89,  89, -75,  50, -18, -18,  50, -75,  89, -89,  75, -50,  18,
+    },
+    {
+         15, -44,  69, -84,  91, -86,  71, -48,  20,  11, -41,  65, -83,  90, -87,  73,
+        -52,  24,   7, -37,  62, -81,  90, -88,  77, -56,  28,   2, -33,  59, -79,  90,
+        -90,  79, -59,  33,  -2, -28,  56, -77,  88, -90,  81, -62,  37,  -7, -24,  52,
+        -73,  87, -90,  83, -65,  41, -11, -20,  48, -71,  86, -91,  84, -69,  44, -15,
+    },
+    {
+         13, -38,  61, -78,  88, -90,  85, -73,  54, -31,   4,  22, -46,  67, -82,  90,
+        -90,  82, -67,  46, -22,  -4,  31, -54,  73, -85,  90, -88,  78, -61,  38, -13,
+        -13,  38, -61,  78, -88,  90, -85,  73, -54,  31,  -4, -22,  46, -67,  82, -90,
+         90, -82,  67, -46,  22,   4, -31,  54, -73,  85, -90,  88, -78,  61, -38,  13,
+    },
+    {
+         11, -33,  52, -69,  81, -88,  91, -87,  79, -65,  48, -28,   7,  15, -37,  56,
+        -71,  83, -90,  90, -86,  77, -62,  44, -24,   2,  20, -41,  59, -73,  84, -90,
+         90, -84,  73, -59,  41, -20,  -2,  24, -44,  62, -77,  86, -90,  90, -83,  71,
+        -56,  37, -15,  -7,  28, -48,  65, -79,  87, -91,  88, -81,  69, -52,  33, -11,
+    },
+    {
+          9, -25,  43, -57,  70, -80,  87, -90,  90, -87,  80, -70,  57, -43,  25,  -9,
+         -9,  25, -43,  57, -70,  80, -87,  90, -90,  87, -80,  70, -57,  43, -25,   9,
+          9, -25,  43, -57,  70, -80,  87, -90,  90, -87,  80, -70,  57, -43,  25,  -9,
+         -9,  25, -43,  57, -70,  80, -87,  90, -90,  87, -80,  70, -57,  43, -25,   9,
+    },
+    {
+          7, -20,  33, -44,  56, -65,  73, -81,  86, -90,  91, -90,  87, -83,  77, -69,
+         59, -48,  37, -24,  11,   2, -15,  28, -41,  52, -62,  71, -79,  84, -88,  90,
+        -90,  88, -84,  79, -71,  62, -52,  41, -28,  15,  -2, -11,  24, -37,  48, -59,
+         69, -77,  83, -87,  90, -91,  90, -86,  81, -73,  65, -56,  44, -33,  20,  -7,
+    },
+    {
+          4, -13,  22, -31,  38, -46,  54, -61,  67, -73,  78, -82,  85, -88,  90, -90,
+         90, -90,  88, -85,  82, -78,  73, -67,  61, -54,  46, -38,  31, -22,  13,  -4,
+         -4,  13, -22,  31, -38,  46, -54,  61, -67,  73, -78,  82, -85,  88, -90,  90,
+        -90,  90, -88,  85, -82,  78, -73,  67, -61,  54, -46,  38, -31,  22, -13,   4,
+    },
+    {
+          2,  -7,  11, -15,  20, -24,  28, -33,  37, -41,  44, -48,  52, -56,  59, -62,
+         65, -69,  71, -73,  77, -79,  81, -83,  84, -86,  87, -88,  90, -90,  90, -91,
+         91, -90,  90, -90,  88, -87,  86, -84,  83, -81,  79, -77,  73, -71,  69, -65,
+         62, -59,  56, -52,  48, -44,  41, -37,  33, -28,  24, -20,  15, -11,   7,  -2,
+    },
+};
+
+// 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 int8_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 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[32] = {
+      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_data.h b/libavcodec/vvc_data.h
new file mode 100644
index 0000000000..c7bfcc4498
--- /dev/null
+++ b/libavcodec/vvc_data.h
@@ -0,0 +1,66 @@
+/*
+ * VVC shared data 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_DATA_H
+#define AVCODEC_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];
+
+#define DCT2_MAX_SIZE 64
+extern const int8_t ff_vvc_dct2[DCT2_MAX_SIZE][DCT2_MAX_SIZE];
+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_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 int8_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_DATA_H */
diff --git a/libavcodec/vvc_filter.c b/libavcodec/vvc_filter.c
new file mode 100644
index 0000000000..df409b0b61
--- /dev/null
+++ b/libavcodec/vvc_filter.c
@@ -0,0 +1,1462 @@
+/*
+ * VVC video 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 "libavutil/common.h"
+#include "libavutil/internal.h"
+
+#include "cabac_functions.h"
+#include "vvc_ctu.h"
+#include "vvc_data.h"
+
+//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,
+};
+
+int ff_vvc_get_qPy(const VVCFrameContext *fc, int xc, int yc)
+{
+    int min_cb_log2_size_y  = fc->ps.sps->min_cb_log2_size_y;
+    int x                 = xc >> min_cb_log2_size_y;
+    int y                 = yc >> min_cb_log2_size_y;
+    return fc->tab.qp[LUMA][x + y * fc->ps.pps->min_cb_width];
+}
+
+static int get_qPc(const VVCFrameContext *fc, const int x0, const int y0, const int chroma)
+{
+    const int x             = x0 >> MIN_TB_LOG2;
+    const int y             = y0 >> MIN_TB_LOG2;
+    const int min_tb_width  = fc->ps.pps->min_tb_width;
+    return fc->tab.qp[chroma][x + y * min_tb_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)
+{
+    int i, j;
+
+    if (((intptr_t)dst | (intptr_t)src | dst_stride | src_stride) & 15) {
+        for (i = 0; i < height; i++) {
+            for (j = 0; j < width; j+=8)
+                AV_COPY64U(dst+j, src+j);
+            dst += dst_stride;
+            src += src_stride;
+        }
+    } else {
+        for (i = 0; i < height; i++) {
+            for (j = 0; j < width; j+=16)
+                AV_COPY128(dst+j, src+j);
+            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)
+{
+    int i;
+    if (pixel_shift == 0) {
+        for (i = 0; i < height; i++) {
+            *dst = *src;
+            dst += dst_stride;
+            src += src_stride;
+        }
+    } else {
+        for (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)
+{
+    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];
+
+    /* copy horizontal edges */
+    memcpy(fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb) * w + x) << ps),
+        src, width << ps);
+    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 restore_tqb_pixels(VVCFrameContext *fc,
+                               uint8_t *src1, const uint8_t *dst1,
+                               ptrdiff_t src_stride, ptrdiff_t dst_stride,
+                               int x0, int y0, int width, int height, int c_idx)
+{
+#if 0
+    if ( fc->ps.pps->transquant_bypass_enable_flag ||
+            (fc->ps.sps->pcm.loop_filter_disable_flag && fc->ps.sps->pcm_enabled_flag)) {
+        int x, y;
+        int min_pu_size  = 1 << fc->ps.sps->log2_min_pu_size;
+        int hshift       = fc->ps.sps->hshift[c_idx];
+        int vshift       = fc->ps.sps->vshift[c_idx];
+        int x_min        = ((x0         ) >> fc->ps.sps->log2_min_pu_size);
+        int y_min        = ((y0         ) >> fc->ps.sps->log2_min_pu_size);
+        int x_max        = ((x0 + width ) >> fc->ps.sps->log2_min_pu_size);
+        int y_max        = ((y0 + height) >> fc->ps.sps->log2_min_pu_size);
+        int len          = (min_pu_size >> hshift) << fc->ps.sps->pixel_shift;
+        for (y = y_min; y < y_max; y++) {
+            for (x = x_min; x < x_max; x++) {
+                if (fc->is_pcm[y * fc->ps.sps->min_pu_width + x]) {
+                    int n;
+                    uint8_t *src = src1 + (((y << fc->ps.sps->log2_min_pu_size) - y0) >> vshift) * src_stride + ((((x << fc->ps.sps->log2_min_pu_size) - x0) >> hshift) << fc->ps.sps->pixel_shift);
+                    const uint8_t *dst = dst1 + (((y << fc->ps.sps->log2_min_pu_size) - y0) >> vshift) * dst_stride + ((((x << fc->ps.sps->log2_min_pu_size) - x0) >> hshift) << fc->ps.sps->pixel_shift);
+                    for (n = 0; n < (min_pu_size >> vshift); n++) {
+                        memcpy(src, dst, len);
+                        src += src_stride;
+                        dst += dst_stride;
+                    }
+                }
+            }
+        }
+    }
+#endif
+}
+
+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->loop_filter_across_slices_enabled_flag;
+    uint8_t no_tile_filter   = fc->ps.pps->num_tiles_in_pic > 1 &&
+                               !fc->ps.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[0]   = x_ctb == 0;
+    edges[1]   = y_ctb == 0;
+    edges[2]   = x_ctb == fc->ps.pps->ctb_width  - 1;
+    edges[3]   = y_ctb == fc->ps.pps->ctb_height - 1;
+
+    if (restore) {
+        if (!edges[0]) {
+            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[2]) {
+            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[1]) {
+            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[3]) {
+            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[0] && !edges[1]) {
+            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[1] && !edges[2]) {
+            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[2] && !edges[3]) {
+            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[0] && !edges[3]) {
+            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->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:
+            copy_CTB_to_hv(fc, src, src_stride, x0, y0, width, height, c_idx, x_ctb, y_ctb);
+#if 0
+            if (fc->ps.pps->transquant_bypass_enable_flag ||
+                (fc->ps.sps->pcm.loop_filter_disable_flag && fc->ps.sps->pcm_enabled_flag)) {
+                dst = lc->edge_emu_buffer;
+                dst_stride = 2*MAX_PB_SIZE;
+                copy_CTB(dst, src, width << fc->ps.sps->pixel_shift, height, dst_stride, src_stride);
+                fc->vvcdsp.sao_band_filter[tab](src, dst, src_stride, dst_stride,
+                                                sao->offset_val[c_idx], sao->band_position[c_idx],
+                                                width, height);
+                restore_tqb_pixels(fc, src, dst, src_stride, dst_stride,
+                                   x, y, width, height, c_idx);
+            } else {
+                fc->vvcdsp.sao_band_filter[tab](src, src, src_stride, src_stride,
+                                                sao->offset_val[c_idx], sao->band_position[c_idx],
+                                                width, height);
+            }
+#endif
+            fc->vvcdsp.sao_band_filter[tab](src, src, src_stride, src_stride,
+                sao->offset_val[c_idx], sao->band_position[c_idx],
+                width, height);
+
+            sao->type_idx[c_idx] = SAO_APPLIED;
+            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 left_edge = edges[0];
+            int top_edge = edges[1];
+            int right_edge = edges[2];
+            int bottom_edge = edges[3];
+            int sh = fc->ps.sps->pixel_shift;
+            int left_pixels, right_pixels;
+
+            dst_stride = 2*MAX_PB_SIZE + AV_INPUT_BUFFER_PADDING_SIZE;
+            dst = lc->sao_buffer + dst_stride + AV_INPUT_BUFFER_PADDING_SIZE;
+
+            if (!top_edge) {
+                int left = 1 - left_edge;
+                int right = 1 - right_edge;
+                const uint8_t *src1[2];
+                uint8_t *dst1;
+                int src_idx, pos;
+
+                dst1 = dst - dst_stride - (left << sh);
+                src1[0] = src - src_stride - (left << sh);
+                src1[1] = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb - 1) * w + x0 - left) << sh);
+                pos = 0;
+                if (left) {
+                    src_idx = (CTB(fc->tab.sao, x_ctb-1, y_ctb-1).type_idx[c_idx] ==
+                               SAO_APPLIED);
+                    copy_pixel(dst1, src1[src_idx], sh);
+                    pos += (1 << sh);
+                }
+                src_idx = (CTB(fc->tab.sao, x_ctb, y_ctb-1).type_idx[c_idx] ==
+                           SAO_APPLIED);
+                memcpy(dst1 + pos, src1[src_idx] + pos, width << sh);
+                if (right) {
+                    pos += width << sh;
+                    src_idx = (CTB(fc->tab.sao, x_ctb+1, y_ctb-1).type_idx[c_idx] ==
+                               SAO_APPLIED);
+                    copy_pixel(dst1 + pos, src1[src_idx] + pos, sh);
+                }
+            }
+            if (!bottom_edge) {
+                int left = 1 - left_edge;
+                int right = 1 - right_edge;
+                const uint8_t *src1[2];
+                uint8_t *dst1;
+                int src_idx, pos;
+
+                dst1 = dst + height * dst_stride - (left << sh);
+                src1[0] = src + height * src_stride - (left << sh);
+                src1[1] = fc->tab.sao_pixel_buffer_h[c_idx] + (((2 * y_ctb + 2) * w + x0 - left) << sh);
+                pos = 0;
+                if (left) {
+                    src_idx = (CTB(fc->tab.sao, x_ctb-1, y_ctb+1).type_idx[c_idx] ==
+                               SAO_APPLIED);
+                    copy_pixel(dst1, src1[src_idx], sh);
+                    pos += (1 << sh);
+                }
+                src_idx = (CTB(fc->tab.sao, x_ctb, y_ctb+1).type_idx[c_idx] ==
+                           SAO_APPLIED);
+                memcpy(dst1 + pos, src1[src_idx] + pos, width << sh);
+                if (right) {
+                    pos += width << sh;
+                    src_idx = (CTB(fc->tab.sao, x_ctb+1, y_ctb+1).type_idx[c_idx] ==
+                               SAO_APPLIED);
+                    copy_pixel(dst1 + pos, src1[src_idx] + pos, sh);
+                }
+            }
+            left_pixels = 0;
+            if (!left_edge) {
+                if (CTB(fc->tab.sao, x_ctb-1, y_ctb).type_idx[c_idx] == SAO_APPLIED) {
+                    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);
+                } else {
+                    left_pixels = 1;
+                }
+            }
+            right_pixels = 0;
+            if (!right_edge) {
+                if (CTB(fc->tab.sao, x_ctb+1, y_ctb).type_idx[c_idx] == SAO_APPLIED) {
+                    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);
+                } else {
+                    right_pixels = 1;
+                }
+            }
+
+            copy_CTB(dst - (left_pixels << sh),
+                     src - (left_pixels << sh),
+                     (width + left_pixels + right_pixels) << sh,
+                     height, dst_stride, src_stride);
+
+            copy_CTB_to_hv(fc, src, src_stride, x0, y0, width, height, c_idx,
+                           x_ctb, y_ctb);
+            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);
+            restore_tqb_pixels(fc, src, dst, src_stride, dst_stride,
+                               x, y, width, height, c_idx);
+            sao->type_idx[c_idx] = SAO_APPLIED;
+            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)]
+#define DEBLOCK_STEP            4
+#define MAX_FILTER_LEN          8
+#define LUMA_GRID               4
+#define CHROMA_GRID             8
+
+static int boundary_strength(const VVCFrameContext *fc, MvField *curr, MvField *neigh,
+                             const RefPicList *neigh_refPicList)
+{
+    if (curr->pred_flag == PF_BI &&  neigh->pred_flag == PF_BI) {
+        // same L0 and L1
+        if (fc->ref->refPicList[0].list[curr->ref_idx[0]] == neigh_refPicList[0].list[neigh->ref_idx[0]]  &&
+            fc->ref->refPicList[0].list[curr->ref_idx[0]] == fc->ref->refPicList[1].list[curr->ref_idx[1]] &&
+            neigh_refPicList[0].list[neigh->ref_idx[0]] == neigh_refPicList[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_refPicList[0].list[neigh->ref_idx[0]] == fc->ref->refPicList[0].list[curr->ref_idx[0]] &&
+                   neigh_refPicList[1].list[neigh->ref_idx[1]] == fc->ref->refPicList[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_refPicList[1].list[neigh->ref_idx[1]] == fc->ref->refPicList[0].list[curr->ref_idx[0]] &&
+                   neigh_refPicList[0].list[neigh->ref_idx[0]] == fc->ref->refPicList[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 = fc->ref->refPicList[0].list[curr->ref_idx[0]];
+        } else {
+            A     = curr->mv[1];
+            ref_A = fc->ref->refPicList[1].list[curr->ref_idx[1]];
+        }
+
+        if (neigh->pred_flag & 1) {
+            B     = neigh->mv[0];
+            ref_B = neigh_refPicList[0].list[neigh->ref_idx[0]];
+        } else {
+            B     = neigh->mv[1];
+            ref_B = neigh_refPicList[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_TB_LOG2) * fc->ps.pps->min_tb_width + (px >> MIN_TB_LOG2)];
+    const int size_q = tb_size[(qy >> MIN_TB_LOG2) * fc->ps.pps->min_tb_width + (qx >> MIN_TB_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 vvc_deblock_subblock_bs_vertical(const VVCFrameContext  *fc,
+    const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height)
+{
+    MvField* tab_mvf            = fc->ref->tab_mvf;
+    RefPicList* rpl             = fc->ref->refPicList;
+    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(fc, 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 vvc_deblock_subblock_bs_horizontal(const VVCFrameContext  *fc,
+    const int cb_x, const int cb_y, const int x0, const int y0, const int width, const int height)
+{
+    MvField* tab_mvf            = fc->ref->tab_mvf;
+    RefPicList* rpl             = fc->ref->refPicList;
+    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(fc, 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 vvc_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->ref->tab_mvf;
+    const int log2_min_pu_size = MIN_PU_LOG2;
+    const int log2_min_tu_size = MIN_TB_LOG2;
+    const int min_pu_width     = fc->ps.pps->min_pu_width;
+    const int min_tu_width     = fc->ps.pps->min_tb_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 i, bs, 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->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->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) :
+                                fc->ref->refPicList;
+        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 += 4) {
+            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[0][y_tu * min_tu_width + xp_tu];
+            uint8_t curr_cbf_luma = fc->tab.tu_coded_flag[0][y_tu * min_tu_width + xq_tu];
+
+            bs = 0;
+            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(fc, 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])
+            vvc_deblock_subblock_bs_vertical(fc, cb_x, cb_y, x0, y0, width, height);
+    }
+
+}
+static void vvc_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->ref->tab_mvf;
+    const int log2_min_pu_size = MIN_PU_LOG2;
+    const int log2_min_tu_size = MIN_TB_LOG2;
+    const int min_pu_width     = fc->ps.pps->min_pu_width;
+    const int min_tu_width     = fc->ps.pps->min_tb_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 i, bs, 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->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->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) :
+                                fc->ref->refPicList;
+        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 (i = 0; i < width; i += 4) {
+            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[0][yp_tu * min_tu_width + x_tu];
+            uint8_t curr_cbf_luma = fc->tab.tu_coded_flag[0][yq_tu * min_tu_width + x_tu];
+
+            bs = 0;
+            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(fc, 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])
+            vvc_deblock_subblock_bs_horizontal(fc, cb_x, cb_y, x0, y0, width, height);
+    }
+}
+
+static void vvc_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->ref->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_tb_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->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->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;
+
+            for (int c = 1; c < 3; 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 (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 vvc_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->ref->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_tb_width;
+    int boundary_upper;
+    int i;
+
+    boundary_upper = y0 > 0 && !(y0 & ((CHROMA_GRID << fc->ps.sps->vshift[1]) - 1));
+    if (boundary_upper &&
+        ((!fc->ps.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->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 (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;
+
+            for (int c = 1; c < 3; 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 (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 vvc_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_TB_LOG2;
+    const int y_end     = FFMIN(y0 + ctb_size, pps->height) >> MIN_TB_LOG2;
+    deblock_bs_fn deblock_bs[2][2] = {
+        { vvc_deblock_bs_luma_horizontal, vvc_deblock_bs_chroma_horizontal },
+        { vvc_deblock_bs_luma_vertical,   vvc_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_TB_LOG2; y < y_end; y++) {
+            for (int x = x0 >> MIN_TB_LOG2; x < x_end; x++) {
+                const int off = y * fc->ps.pps->min_tb_width + x;
+                if ((fc->tab.tb_pos_x0[is_chroma][off] >> MIN_TB_LOG2) == x && (fc->tab.tb_pos_y0[is_chroma][off] >> MIN_TB_LOG2) == y) {
+                    deblock_bs[vertical][is_chroma](lc, x << MIN_TB_LOG2, y << MIN_TB_LOG2,
+                        fc->tab.tb_width[is_chroma][off] << hs, fc->tab.tb_height[is_chroma][off] << vs);
+                }
+            }
+        }
+    }
+}
+
+static int get_pcm(VVCFrameContext *fc, int x, int y)
+{
+    return 0;
+#if 0
+    int log2_min_pu_size = fc->ps.sps->log2_min_pu_size;
+    int x_pu, y_pu;
+
+
+    if (x < 0 || y < 0)
+        return 2;
+
+    x_pu = x >> log2_min_pu_size;
+    y_pu = y >> log2_min_pu_size;
+
+    if (x_pu >= fc->ps.sps->min_pu_width || y_pu >= fc->ps.sps->min_pu_height)
+        return 2;
+    return fc->is_pcm[y_pu * fc->ps.sps->min_pu_width + x_pu];
+#endif
+}
+
+//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_TB_LOG2) * fc->ps.pps->min_tb_width + (px >> MIN_TB_LOG2)];
+    const int size_q = tb_size[(qy >> MIN_TB_LOG2) * fc->ps.pps->min_tb_width + (qx >> MIN_TB_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->ladf_enabled_flag)
+        return qp;
+
+    level = (vertical ? fc->vvcdsp.vvc_v_loop_ladf_level : fc->vvcdsp.vvc_h_loop_ladf_level)(src, fc->frame->linesize[LUMA]);
+    qp_offset = 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->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     = fc->ps.sps->chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1;
+    uint8_t *src;
+    int x, y, beta, tc, qp;
+    uint8_t no_p = 0;
+    uint8_t no_q = 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;
+
+    //fixme
+    int pcmf = 0;
+#if 0
+    int pcmf = (fc->ps.sps->pcm_enabled_flag &&
+        fc->ps.sps->pcm.loop_filter_disable_flag) ||
+        fc->ps.pps->transquant_bypass_enable_flag;
+#endif
+    vvc_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) {
+            for (x = x0 ? x0 : grid; x < x_end; x += grid) {
+                const int bs = TAB_BS(fc->tab.vertical_bs[c_idx], x, y);
+                if (bs) {
+                    uint8_t max_len_p, max_len_q;
+
+                    src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)];
+                    qp = get_qp(fc, src, x, y, c_idx, 1);
+
+                    beta = betatable[av_clip(qp + beta_offset, 0, MAX_QP)];
+
+                    tc = TC_CALC(qp, bs);
+
+                    max_filter_length(fc, x, y, c_idx, 1, 0, bs, &max_len_p, &max_len_q);
+
+                    if (pcmf) {
+                        no_p = get_pcm(fc, x - 1, y);
+                        no_q = get_pcm(fc, x, y);
+                        if (!c_idx) {
+                            fc->vvcdsp.vvc_v_loop_filter_luma_c(src,
+                                fc->frame->linesize[c_idx], beta, tc, no_p, no_q, max_len_p, max_len_q);
+                        } else {
+                            fc->vvcdsp.vvc_v_loop_filter_chroma_c(src,
+                                fc->frame->linesize[c_idx], beta,tc, no_p, no_q, vs, max_len_p, max_len_q);
+                        }
+                    } else {
+                        if (!c_idx) {
+                            fc->vvcdsp.vvc_v_loop_filter_luma(src,
+                                fc->frame->linesize[c_idx], beta, tc, no_p, no_q, max_len_p, max_len_q);
+                        } else {
+                            fc->vvcdsp.vvc_v_loop_filter_chroma(src,
+                                fc->frame->linesize[c_idx], beta, tc, no_p, no_q, vs, max_len_p, max_len_q);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+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->chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1;
+    uint8_t* src;
+    int x, y, beta, tc, qp;
+    uint8_t no_p = 0;
+    uint8_t no_q = 0;
+
+    int ctb_log2_size_y = fc->ps.sps->ctb_log2_size_y;
+    int x_end, x_end2, 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;
+    int tc_offset, beta_offset;
+    //fixme
+    int pcmf = 0;
+#if 0
+    int pcmf = (fc->ps.sps->pcm_enabled_flag &&
+        fc->ps.sps->pcm.loop_filter_disable_flag) ||
+        fc->ps.pps->transquant_bypass_enable_flag;
+#endif
+
+    vvc_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;
+
+        x_end2 = x_end == fc->ps.sps->width ? x_end : x_end - (MAX_FILTER_LEN << hs);
+
+        for (y = y0; y < y_end; y += grid) {
+            if (!y)
+                continue;
+
+            // horizontal filtering luma
+            for (x = x0 ? x0 - (MAX_FILTER_LEN << hs) : 0; x < x_end2; x += DEBLOCK_STEP) {
+                const int bs = TAB_BS(fc->tab.horizontal_bs[c_idx], x, y);
+                if (bs) {
+                    const DBParams  *params = fc->tab.deblock + ctb - (x < x0);
+                    const int horizontal_ctu_edge = !(y % fc->ps.sps->ctb_size_y);
+                    uint8_t max_len_p, max_len_q;
+
+                    src = &fc->frame->data[c_idx][(y >> vs) * fc->frame->linesize[c_idx] + ((x >> hs) << fc->ps.sps->pixel_shift)];
+                    qp = get_qp(fc, src, x, y, c_idx, 0);
+
+                    tc_offset = params->tc_offset[c_idx];
+                    beta_offset = params->beta_offset[c_idx];
+
+                    beta = betatable[av_clip(qp + beta_offset, 0, MAX_QP)];
+                    tc = TC_CALC(qp, bs);
+
+                    max_filter_length(fc, x, y, c_idx, 0, horizontal_ctu_edge, bs, &max_len_p, &max_len_q);
+
+                    if (pcmf) {
+                        no_p = get_pcm(fc, x, y - 1);
+                        no_q = get_pcm(fc, x, y);
+                        if (!c_idx) {
+                            fc->vvcdsp.vvc_h_loop_filter_luma_c(src, fc->frame->linesize[c_idx],
+                                beta, tc, no_p, no_q, max_len_p, max_len_q, horizontal_ctu_edge);
+                        } else {
+                            fc->vvcdsp.vvc_h_loop_filter_chroma_c(src, fc->frame->linesize[c_idx],
+                                beta, tc, no_p, no_q, hs, max_len_p, max_len_q);
+                        }
+                    } else {
+                        if (!c_idx) {
+                            fc->vvcdsp.vvc_h_loop_filter_luma(src, fc->frame->linesize[c_idx],
+                                beta, tc, no_p, no_q, max_len_p, max_len_q, horizontal_ctu_edge);
+                        } else {
+                            fc->vvcdsp.vvc_h_loop_filter_chroma(src, fc->frame->linesize[c_idx], beta,
+                                tc, no_p, no_q, hs, max_len_p, max_len_q);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+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);
+    }
+}
+
+#define LEFT        0
+#define TOP         1
+#define RIGHT       2
+#define BOTTOM      3
+#define MAX_EDGES   4
+
+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_BLOCKS_IN_SUBBLOCK      (ALF_SUBBLOCK_SIZE / ALF_BLOCK_SIZE)
+#define ALF_SUBBLOCK_FILTER_SIZE    (ALF_BLOCKS_IN_SUBBLOCK * ALF_BLOCKS_IN_SUBBLOCK * ALF_NUM_COEFF_LUMA)
+
+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;
+    const VVCSH *sh             = &lc->sc->sh;
+    const int ps                = fc->ps.sps->pixel_shift;
+    const int no_vb_height      = y0 + height > vb_pos ? vb_pos - y0 - ALF_VB_POS_ABOVE_LUMA : height;
+    int8_t coeff[ALF_SUBBLOCK_FILTER_SIZE];
+    int16_t clip[ALF_SUBBLOCK_FILTER_SIZE];
+    uint8_t fixed_clip_set[ALF_NUM_FILTERS_LUMA * ALF_NUM_COEFF_LUMA] = { 0 };
+    const int8_t  *coeff_set;
+    const uint8_t *clip_idx_set;
+    const uint8_t *class_to_filt;
+
+    if (alf->ctb_filt_set_idx_y < 16) {
+        coeff_set = &ff_vvc_alf_fix_filt_coeff[0][0];
+        clip_idx_set = fixed_clip_set;
+        class_to_filt = ff_vvc_alf_class_to_filt_map[alf->ctb_filt_set_idx_y];
+        //av_assert0(0 && "fixme");
+    } else {
+        const int id = sh->alf.aps_id_luma[alf->ctb_filt_set_idx_y - 16];
+        const VVCALF *aps = (VVCALF *)fc->ps.alf_list[id]->data;
+        coeff_set    = aps->luma_coeff;
+        clip_idx_set = aps->luma_clip_idx;
+        class_to_filt = ff_vvc_alf_aps_class_to_filt_map;
+    }
+
+    for (int y = 0; y < no_vb_height; y += ALF_SUBBLOCK_SIZE) {
+        for (int x = 0; x < width; x += ALF_SUBBLOCK_SIZE) {
+            const uint8_t *src = _src + y * src_stride + (x << ps);
+            uint8_t *dst = _dst + y * dst_stride + (x << ps);
+            const int w = FFMIN(width  - x, ALF_SUBBLOCK_SIZE);
+            const int h = FFMIN(no_vb_height - y, ALF_SUBBLOCK_SIZE);
+
+            fc->vvcdsp.alf_get_coeff_and_clip(src, src_stride, x0 + x, y0 + y, w, h,
+                vb_pos, coeff_set, clip_idx_set, class_to_filt, coeff, clip);
+
+            fc->vvcdsp.alf_filter_luma(dst, src, dst_stride, src_stride, w, h, coeff, clip);
+        }
+    }
+
+    if (height > no_vb_height) {
+        const int y = no_vb_height;
+        for (int x = 0; x < width; x += ALF_SUBBLOCK_SIZE) {
+            const uint8_t *src = _src + y * src_stride + (x << ps);
+            uint8_t *dst = _dst + y * dst_stride + (x << ps);
+            const int w = FFMIN(width  - x, ALF_SUBBLOCK_SIZE);
+            const int h = height - y;
+
+            fc->vvcdsp.alf_get_coeff_and_clip(src, src_stride, x0 + x, y0 + y, w, h, vb_pos, coeff_set, clip_idx_set, class_to_filt, coeff, clip);
+
+            fc->vvcdsp.alf_filter_luma_vb(dst, src, dst_stride, src_stride, w, h, coeff, clip, vb_pos - y0 - y);
+        }
+    }
+}
+
+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 VVCSH *sh         = &lc->sc->sh;
+    const VVCALF *aps       = (VVCALF *)fc->ps.alf_list[sh->alf.aps_id_chroma]->data;
+    const int off           = alf->alf_ctb_filter_alt_idx[c_idx - 1] * ALF_NUM_COEFF_CHROMA;
+    const int8_t *coeff     = aps->chroma_coeff + off;
+    const int no_vb_height  = height > vb_pos ? vb_pos - ALF_VB_POS_ABOVE_CHROMA : height;
+    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[off + i]);
+
+    fc->vvcdsp.alf_filter_chroma(dst, src, dst_stride, src_stride, width, no_vb_height, coeff, clip);
+    if (no_vb_height < height) {
+        dst += dst_stride * no_vb_height;
+        src += src_stride * no_vb_height;
+        fc->vvcdsp.alf_filter_chroma_vb(dst, src, dst_stride, src_stride, width, height - no_vb_height, coeff, clip, vb_pos - no_vb_height);
+    }
+}
+
+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 VVCSH *sh         = &lc->sc->sh;
+    const int idx           = c_idx - 1;
+    AVBufferRef *aps_buf    = fc->ps.alf_list[sh->alf.cc_aps_id[idx]];
+
+    if (aps_buf) {
+        const VVCALF *aps   = (VVCALF *)aps_buf->data;
+        const int off       = (alf->ctb_cc_idc[idx] - 1)* ALF_NUM_COEFF_CC;
+        const int8_t *coeff = aps->cc_coeff[idx] + off;
+
+        fc->vvcdsp.alf_filter_cc(dst, luma, dst_stride, 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->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 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->chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1;
+    ALFParams *alf          = &CTB(fc->tab.alf, x_ctb, y_ctb);
+    int edges[4];  // 0 left 1 top 2 right 3 bottom
+
+    edges[0]   = x_ctb == 0;
+    edges[1]   = y_ctb == 0;
+    edges[2]   = x_ctb == fc->ps.pps->ctb_width  - 1;
+    edges[3]   = y_ctb == fc->ps.pps->ctb_height - 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.lmcs_used_flag)
+        fc->vvcdsp.lmcs_filter_luma(data, fc->frame->linesize[LUMA], width, height, fc->ps.ph->lmcs_inv_lut);
+}
diff --git a/libavcodec/vvc_inter.c b/libavcodec/vvc_inter.c
new file mode 100644
index 0000000000..f2b5b334eb
--- /dev/null
+++ b/libavcodec/vvc_inter.c
@@ -0,0 +1,1348 @@
+/*
+ * VVC inter predict
+ *
+ * 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_cabac.h"
+#include "vvc_ctu.h"
+#include "vvc_data.h"
+#include "vvc_mvs.h"
+#include "vvc_thread.h"
+
+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, 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;
+        int offset     = BILINEAR_EXTRA_BEFORE * *src_stride      + (BILINEAR_EXTRA_BEFORE << fc->ps.sps->pixel_shift);
+        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)
+
+static void luma_mc(VVCLocalContext *lc, int16_t *dst, AVFrame *ref, const Mv *mv,
+    int x_off, int y_off, const int block_w, const int block_h)
+{
+    VVCFrameContext *fc  = lc->fc;
+    const uint8_t *src   = ref->data[0];
+    ptrdiff_t src_stride = ref->linesize[0];
+    int mx               = mv->x & 0xf;
+    int my               = mv->y & 0xf;
+
+    x_off += mv->x >> 4;
+    y_off += mv->y >> 4;
+    src   += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift));
+
+    EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off);
+
+    fc->vvcdsp.put_vvc_luma[!!my][!!mx](dst, src, src_stride, block_h, mx, my, block_w, 0, 0);
+}
+
+static void chroma_mc(VVCLocalContext *lc, int16_t *dst, AVFrame *ref, const Mv *mv,
+    int x_off, int y_off, const int block_w, const int block_h, const int c_idx)
+{
+    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];
+
+    intptr_t mx  = av_mod_uintp2(mv->x, 4 + hs);
+    intptr_t my  = av_mod_uintp2(mv->y, 4 + vs);
+    intptr_t _mx = mx << (1 - hs);
+    intptr_t _my = my << (1 - vs);
+
+    x_off += mv->x >> (4 + hs);
+    y_off += mv->y >> (4 + vs);
+    src  += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift));
+
+    EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off);
+    fc->vvcdsp.put_vvc_chroma[!!my][!!mx](dst, src, src_stride, block_h, _mx, _my, block_w, 0, 0);
+}
+
+static void luma_mc_uni(VVCLocalContext *lc, uint8_t *_dst, ptrdiff_t dst_stride,
+    AVFrame *ref, const Mv *mv, int x_off, int y_off, const int block_w, const int block_h,
+    const int luma_weight, const int luma_offset, const int hf_idx, const int vf_idx)
+{
+    VVCFrameContext *fc     = lc->fc;
+    const uint8_t *src      = ref->data[0];
+    uint8_t *dst            = _dst;
+    ptrdiff_t src_stride    = ref->linesize[0];
+    int mx                  = mv->x & 0xf;
+    int my                  = mv->y & 0xf;
+    int weight_flag         = (IS_P(&lc->sc->sh) && fc->ps.pps->weighted_pred_flag) ||
+                              (IS_B(&lc->sc->sh) && fc->ps.pps->weighted_bipred_flag);
+
+    x_off += mv->x >> 4;
+    y_off += mv->y >> 4;
+    src   += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift));
+
+    EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off);
+
+
+
+    if (!weight_flag) {
+        fc->vvcdsp.put_vvc_luma_uni[!!my][!!mx](dst, dst_stride, src, src_stride,
+                                                      block_h, mx, my, block_w,
+                                                      hf_idx, vf_idx);
+    } else {
+        av_assert0(0 && "fixme");
+#if 0
+        fc->vvcdsp.put_vvc_qpel_uni_w[idx][!!my][!!mx](dst, dst_stride, src, src_stride,
+                                                        block_h, fc->sh.luma_log2_weight_denom,
+                                                        luma_weight, luma_offset, mx, my, block_w);
+#endif
+    }
+}
+
+static void luma_bdof(VVCLocalContext *lc, uint8_t *dst, ptrdiff_t dst_stride,
+    const uint8_t *_src0, const ptrdiff_t src0stride, const int mx0, const int my0,
+    const uint8_t *_src1, const ptrdiff_t src1stride, const int mx1, const int my1,
+    const int block_w, const int block_h, const int hf_idx, const int vf_idx)
+{
+    const VVCFrameContext *fc = lc->fc;
+    int16_t *tmp0       = lc->tmp  + 1 + MAX_PB_SIZE;
+    int16_t *tmp1       = lc->tmp1 + 1 + MAX_PB_SIZE;
+
+    fc->vvcdsp.put_vvc_luma[!!my0][!!mx0](tmp0, _src0, src0stride,
+                                         block_h, mx0, my0, block_w, hf_idx, vf_idx);
+    fc->vvcdsp.bdof_fetch_samples(tmp0, _src0, src0stride, mx0, my0, block_w, block_h);
+
+    fc->vvcdsp.put_vvc_luma[!!my1][!!mx1](tmp1, _src1, src1stride,
+                                         block_h, mx1, my1, block_w, hf_idx, vf_idx);
+    fc->vvcdsp.bdof_fetch_samples(tmp1, _src1, src1stride, mx1, my1, block_w, block_h);
+    fc->vvcdsp.apply_bdof(dst, dst_stride, tmp0, tmp1, block_w, block_h);
+}
+
+ static void luma_mc_bi(VVCLocalContext *lc, uint8_t *dst, ptrdiff_t dst_stride,
+    AVFrame *ref0, const Mv *mv0, const int x_off, const int y_off, const int block_w, const int block_h,
+    AVFrame *ref1, const Mv *mv1, const MvField *current_mv, const int hf_idx, const int vf_idx,
+    const MvField *orig_mv, const int dmvr_flag, const int sb_bdof_flag, const int ciip_flag)
+{
+    VVCFrameContext *fc  = lc->fc;
+    ptrdiff_t src0stride = ref0->linesize[0];
+    ptrdiff_t src1stride = ref1->linesize[0];
+    int mx0              = mv0->x & 0xf;
+    int my0              = mv0->y & 0xf;
+    int mx1              = mv1->x & 0xf;
+    int my1              = mv1->y & 0xf;
+    int weight_flag      = (IS_P(&lc->sc->sh) && fc->ps.pps->weighted_pred_flag) ||
+                           (IS_B(&lc->sc->sh) && fc->ps.pps->weighted_bipred_flag);
+    int bcw_idx          = current_mv->bcw_idx;
+    int x_off0           = x_off + (mv0->x >> 4);
+    int y_off0           = y_off + (mv0->y >> 4);
+    int x_off1           = x_off + (mv1->x >> 4);
+    int y_off1           = y_off + (mv1->y >> 4);
+
+    const uint8_t *src0  = ref0->data[0] + y_off0 * src0stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift);
+    const uint8_t *src1  = ref1->data[0] + y_off1 * src1stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift);
+
+    if (dmvr_flag) {
+        int x_sb0 = x_off + (orig_mv->mv[L0].x >> 4);
+        int y_sb0 = y_off + (orig_mv->mv[L0].y >> 4);
+        int x_sb1 = x_off + (orig_mv->mv[L1].x >> 4);
+        int y_sb1 = y_off + (orig_mv->mv[L1].y >> 4);
+        EMULATED_EDGE_DMVR_LUMA(lc->edge_emu_buffer,  &src0, &src0stride, x_sb0, y_sb0, x_off0, y_off0);
+        EMULATED_EDGE_DMVR_LUMA(lc->edge_emu_buffer2, &src1, &src1stride, x_sb1, y_sb1, x_off1, y_off1);
+    } else {
+        EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src0, &src0stride, x_off0, y_off0);
+        EMULATED_EDGE_LUMA(lc->edge_emu_buffer2, &src1, &src1stride, x_off1, y_off1);
+    }
+    if (sb_bdof_flag) {
+        luma_bdof(lc, dst, dst_stride, src0, src0stride, mx0, my0, src1, src1stride, mx1, my1,
+            block_w, block_h, hf_idx, vf_idx);
+    } else {
+        fc->vvcdsp.put_vvc_luma[!!my0][!!mx0](lc->tmp, src0, src0stride,
+                                             block_h, mx0, my0, block_w, hf_idx, vf_idx);
+        if (!weight_flag && !bcw_idx || ciip_flag)
+            fc->vvcdsp.put_vvc_luma_bi[!!my1][!!mx1](dst, dst_stride, src1, src1stride, lc->tmp,
+                                                           block_h, mx1, my1, block_w, hf_idx, vf_idx);
+        else {
+            int denom, w0, w1, o0, o1;
+            if (bcw_idx) {
+                denom = 2;
+                w1 = bcw_w_lut[bcw_idx];
+                w0 = 8 - w1;
+                o0 = o1 = 0;
+            } else {
+                av_assert0(0);
+#if 0
+                denom = fc->shluma_log2_weight_denom;
+                w0 = fc->sh.luma_weight_l0[current_mv->ref_idx[0]];
+                w1 = fc->sh.luma_weight_l1[current_mv->ref_idx[1]];
+                o0 = fc->sh.luma_offset_l0[current_mv->ref_idx[0]];
+                o1 = fc->sh.luma_offset_l1[current_mv->ref_idx[1]];
+#endif
+            }
+            fc->vvcdsp.put_vvc_luma_bi_w[!!my1][!!mx1](dst, dst_stride, src1, src1stride, lc->tmp,
+                                                             block_h, denom, w0, w1, o0, o1,
+                                                             mx1, my1, block_w, hf_idx, vf_idx);
+        }
+    }
+
+}
+
+static void chroma_mc_uni(VVCLocalContext *lc, uint8_t *dst, ptrdiff_t dst_stride,
+    const uint8_t *src, ptrdiff_t src_stride, const int reflist,
+    int x_off, int y_off, const int block_w, const int block_h, const MvField *current_mv,
+    const int chroma_weight, const int chroma_offset, const int hf_idx, const int vf_idx)
+{
+    VVCFrameContext *fc  = lc->fc;
+    int hshift           = fc->ps.sps->hshift[1];
+    int vshift           = fc->ps.sps->vshift[1];
+    const Mv *mv         = &current_mv->mv[reflist];
+    int weight_flag      = (IS_P(&lc->sc->sh) && fc->ps.pps->weighted_pred_flag) ||
+                           (IS_B(&lc->sc->sh) && fc->ps.pps->weighted_bipred_flag);
+    intptr_t mx          = av_mod_uintp2(mv->x, 4 + hshift);
+    intptr_t my          = av_mod_uintp2(mv->y, 4 + vshift);
+    intptr_t _mx         = mx << (1 - hshift);
+    intptr_t _my         = my << (1 - vshift);
+
+    x_off += mv->x >> (4 + hshift);
+    y_off += mv->y >> (4 + vshift);
+    src  += y_off * src_stride + (x_off * (1 << fc->ps.sps->pixel_shift));
+
+    EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src, &src_stride, x_off, y_off);
+    if (!weight_flag)
+        fc->vvcdsp.put_vvc_chroma_uni[!!my][!!mx](dst, dst_stride, src, src_stride,
+            block_h, _mx, _my, block_w, hf_idx, vf_idx);
+    else {
+        av_assert0(0 && "fixme");
+        /*
+        fc->vvcdsp.put_vvc_epel_uni_w[idx][!!my][!!mx](dst, dst_stride, src, src_stride,
+                                                        block_h, fc->sh.chroma_log2_weight_denom,
+                                                        chroma_weight, chroma_offset, _mx, _my, block_w);
+        */
+    }
+}
+
+static void chroma_mc_bi(VVCLocalContext *lc, uint8_t *dst, ptrdiff_t dst_stride,
+    AVFrame *ref0, AVFrame *ref1, const int x_off, const int y_off, const int block_w, const int block_h,
+    const MvField *current_mv, const int cidx, const int hf_idx, const int vf_idx,
+    const MvField *orig_mv, const int dmvr_flag, const int ciip_flag)
+{
+    VVCFrameContext *fc  = lc->fc;
+    const uint8_t *src0  = ref0->data[cidx+1];
+    const uint8_t *src1  = ref1->data[cidx+1];
+    ptrdiff_t src0stride = ref0->linesize[cidx+1];
+    ptrdiff_t src1stride = ref1->linesize[cidx+1];
+    int weight_flag      = (IS_P(&lc->sc->sh) && fc->ps.pps->weighted_pred_flag) ||
+                           (IS_B(&lc->sc->sh) && fc->ps.pps->weighted_bipred_flag);
+    int bcw_idx          = current_mv->bcw_idx;
+    const Mv *mv0        = &current_mv->mv[0];
+    const Mv *mv1        = &current_mv->mv[1];
+    int hshift           = fc->ps.sps->hshift[1];
+    int vshift           = fc->ps.sps->vshift[1];
+
+    intptr_t mx0 = av_mod_uintp2(mv0->x, 4 + hshift);
+    intptr_t my0 = av_mod_uintp2(mv0->y, 4 + vshift);
+    intptr_t mx1 = av_mod_uintp2(mv1->x, 4 + hshift);
+    intptr_t my1 = av_mod_uintp2(mv1->y, 4 + vshift);
+    intptr_t _mx0 = mx0 << (1 - hshift);
+    intptr_t _my0 = my0 << (1 - vshift);
+    intptr_t _mx1 = mx1 << (1 - hshift);
+    intptr_t _my1 = my1 << (1 - vshift);
+
+    int x_off0 = x_off + (mv0->x >> (4 + hshift));
+    int y_off0 = y_off + (mv0->y >> (4 + vshift));
+    int x_off1 = x_off + (mv1->x >> (4 + hshift));
+    int y_off1 = y_off + (mv1->y >> (4 + vshift));
+    src0  += y_off0 * src0stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift);
+    src1  += y_off1 * src1stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift);
+
+    if (dmvr_flag) {
+        int x_sb0 = x_off + (orig_mv->mv[L0].x >> (4 + hshift));
+        int y_sb0 = y_off + (orig_mv->mv[L0].y >> (4 + vshift));
+        int x_sb1 = x_off + (orig_mv->mv[L1].x >> (4 + hshift));
+        int y_sb1 = y_off + (orig_mv->mv[L1].y >> (4 + vshift));
+        EMULATED_EDGE_DMVR_CHROMA(lc->edge_emu_buffer,  &src0, &src0stride, x_sb0, y_sb0, x_off0, y_off0);
+        EMULATED_EDGE_DMVR_CHROMA(lc->edge_emu_buffer2, &src1, &src1stride, x_sb1, y_sb1, x_off1, y_off1);
+    } else {
+        EMULATED_EDGE_CHROMA(lc->edge_emu_buffer, &src0, &src0stride, x_off0, y_off0);
+        EMULATED_EDGE_CHROMA(lc->edge_emu_buffer2, &src1, &src1stride, x_off1, y_off1);
+    }
+
+    fc->vvcdsp.put_vvc_chroma[!!my0][!!mx0](lc->tmp, src0, src0stride,
+                                                block_h, _mx0, _my0, block_w, hf_idx, vf_idx);
+    if (!weight_flag && !bcw_idx || ciip_flag)
+        fc->vvcdsp.put_vvc_chroma_bi[!!my1][!!mx1](dst, dst_stride,
+                                                       src1, src1stride, lc->tmp,
+                                                       block_h, _mx1, _my1, block_w, hf_idx, vf_idx);
+    else {
+        int denom, w0, w1, o0, o1;
+        if (bcw_idx) {
+            denom = 2;
+            w1 = bcw_w_lut[bcw_idx];
+            w0 = 8 - w1;
+            o0 = o1 = 0;
+        } else {
+            av_assert0(0);
+#if 0
+            denom = fc->sh.chroma_log2_weight_denom;
+            w0 = fc->sh.chroma_weight_l0[current_mv->ref_idx[0]][cidx];
+            w1 = fc->sh.chroma_weight_l1[current_mv->ref_idx[1]][cidx];
+            o0 = fc->sh.chroma_offset_l0[current_mv->ref_idx[0]][cidx];
+            o1 = fc->sh.chroma_offset_l1[current_mv->ref_idx[1]][cidx];
+#endif
+        }
+        fc->vvcdsp.put_vvc_chroma_bi_w[!!my1][!!mx1](dst, dst_stride,
+                                                         src1, src1stride, lc->tmp,
+                                                         block_h,
+                                                         denom, w0, w1, o0, o1,
+                                                         _mx1, _my1, block_w, hf_idx, vf_idx);
+    }
+}
+
+static void luma_prof_uni(VVCLocalContext *lc, uint8_t *_dst, ptrdiff_t dst_stride,
+    AVFrame *ref, const Mv *mv, 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];
+    uint8_t *dst                = _dst;
+    uint16_t *prof_tmp          = lc->tmp + 1 + MAX_PB_SIZE;
+    ptrdiff_t src_stride        = ref->linesize[0];
+    int mx                      = mv->x & 0xf;
+    int my                      = mv->y & 0xf;
+
+    x_off += mv->x >> 4;
+    y_off += mv->y >> 4;
+    src   += y_off * src_stride + (x_off * (1 << 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.put_vvc_luma[!!my][!!mx](prof_tmp, src, src_stride, AFFINE_MIN_BLOCK_SIZE, mx, my, AFFINE_MIN_BLOCK_SIZE, 2, 2);
+        fc->vvcdsp.fetch_samples(prof_tmp, src, src_stride, mx, my);
+        fc->vvcdsp.apply_prof_uni(dst, dst_stride, prof_tmp, diff_mv_x, diff_mv_y);
+    } else {
+        fc->vvcdsp.put_vvc_luma_uni[!!my][!!mx](dst, dst_stride, src, src_stride, block_h, mx, my, block_w, 2, 2);
+    }
+}
+
+static void get_luma_weight(const VVCFrameContext *fc, const int bcw_idx,
+    int *denom, int *w0, int *w1, int *o0, int *o1)
+{
+    if (bcw_idx) {
+        *denom = 2;
+        *w1 = bcw_w_lut[bcw_idx];
+        *w0 = 8 - *w1;
+        *o0 = *o1 = 0;
+    } else {
+        av_assert0(0);
+#if 0
+        denom = fc->shluma_log2_weight_denom;
+        w0 = fc->sh.luma_weight_l0[current_mv->ref_idx[0]];
+        w1 = fc->sh.luma_weight_l1[current_mv->ref_idx[1]];
+        o0 = fc->sh.luma_offset_l0[current_mv->ref_idx[0]];
+        o1 = fc->sh.luma_offset_l1[current_mv->ref_idx[1]];
+#endif
+    }
+
+}
+
+static void luma_prof_bi(VVCLocalContext *lc, uint8_t *dst, ptrdiff_t dst_stride,
+    AVFrame *ref0, AVFrame *ref1, const MvField *current_mv, 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;
+    ptrdiff_t src0stride = ref0->linesize[0];
+    ptrdiff_t src1stride = ref1->linesize[0];
+    const int bcw_idx    = current_mv->bcw_idx;
+    const Mv *mv0        = current_mv->mv + L0;
+    const Mv *mv1        = current_mv->mv + L1;
+    int mx0              = mv0->x & 0xf;
+    int my0              = mv0->y & 0xf;
+    int mx1              = mv1->x & 0xf;
+    int my1              = mv1->y & 0xf;
+    int weight_flag      = (IS_P(&lc->sc->sh) && fc->ps.pps->weighted_pred_flag) ||
+                           (IS_B(&lc->sc->sh) && fc->ps.pps->weighted_bipred_flag) || bcw_idx;
+    int x_off0           = x_off + (mv0->x >> 4);
+    int y_off0           = y_off + (mv0->y >> 4);
+    int x_off1           = x_off + (mv1->x >> 4);
+    int y_off1           = y_off + (mv1->y >> 4);
+
+    int denom, w0, w1, o0, o1;
+
+    const uint8_t *src0  = ref0->data[0] + y_off0 * src0stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift);
+    const uint8_t *src1  = ref1->data[0] + y_off1 * src1stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift);
+
+    uint16_t *prof_tmp   = lc->tmp1 + 1 + MAX_PB_SIZE;
+
+    EMULATED_EDGE_LUMA(lc->edge_emu_buffer, &src0, &src0stride, x_off0, y_off0);
+    EMULATED_EDGE_LUMA(lc->edge_emu_buffer2, &src1, &src1stride, x_off1, y_off1);
+
+    if (!pu->cb_prof_flag[L0]) {
+        fc->vvcdsp.put_vvc_luma[!!my0][!!mx0](lc->tmp, src0, src0stride,
+            block_h, mx0, my0, block_w, 2, 2);
+    } else{
+        fc->vvcdsp.put_vvc_luma[!!my0][!!mx0](prof_tmp, src0, src0stride, AFFINE_MIN_BLOCK_SIZE, mx0, my0, AFFINE_MIN_BLOCK_SIZE, 2, 2);
+        fc->vvcdsp.fetch_samples(prof_tmp, src0, src0stride, mx0, my0);
+        fc->vvcdsp.apply_prof(lc->tmp, prof_tmp, pu->diff_mv_x[L0], pu->diff_mv_y[L0]);
+    }
+    if (!pu->cb_prof_flag[L1]) {
+        if (!weight_flag) {
+            fc->vvcdsp.put_vvc_luma_bi[!!my1][!!mx1](dst, dst_stride, src1, src1stride, lc->tmp,
+                block_h, mx1, my1, block_w, 2, 2);
+        } else {
+            get_luma_weight(fc, bcw_idx, &denom, &w0, &w1, &o0, &o1);
+            fc->vvcdsp.put_vvc_luma_bi_w[!!my1][!!mx1](dst, dst_stride, src1, src1stride, lc->tmp,
+                block_h, denom, w0, w1, o0, o1,  mx1, my1, block_w, 2, 2);
+        }
+    } else {
+        fc->vvcdsp.put_vvc_luma[!!my1][!!mx1](prof_tmp, src1, src1stride, AFFINE_MIN_BLOCK_SIZE, mx1, my1, AFFINE_MIN_BLOCK_SIZE, 2, 2);
+        fc->vvcdsp.fetch_samples(prof_tmp, src1, src1stride, mx1, my1);
+
+        if (!weight_flag) {
+            fc->vvcdsp.apply_prof_bi(dst, dst_stride,  lc->tmp, prof_tmp, pu->diff_mv_x[L1], pu->diff_mv_y[L1]);
+        } else {
+            get_luma_weight(fc, bcw_idx, &denom, &w0, &w1, &o0, &o1);
+            fc->vvcdsp.apply_prof_bi_w(dst, dst_stride, lc->tmp, prof_tmp, pu->diff_mv_x[L1], pu->diff_mv_y[L1], denom, w0, w1, o0, o1);
+        }
+    }
+
+}
+
+//8.5.2.7 Derivation process for merge motion vector difference
+static void derive_mmvd(const VVCFrameContext *fc, const Mv *mmvd_offset, MvField *mvf)
+{
+    Mv mmvd[2];
+
+    if (mvf->pred_flag == PF_BI) {
+        const RefPicList *refPicList = fc->ref->refPicList;
+        const int poc = fc->ps.ph->poc;
+        const int diff[] = {
+            poc - refPicList[0].list[mvf->ref_idx[0]],
+            poc - refPicList[1].list[mvf->ref_idx[1]]
+        };
+        const int sign = FFSIGN(diff[0]) != FFSIGN(diff[1]);
+        if (diff[0] == diff[1]) {
+            mmvd[1] = mmvd[0] = *mmvd_offset;
+        } else {
+            const int i = FFABS(diff[0]) < FFABS(diff[1]);
+            const int o = !i;
+            mmvd[i] = *mmvd_offset;
+            if (!refPicList[0].isLongTerm[mvf->ref_idx[0]] && !refPicList[1].isLongTerm[mvf->ref_idx[1]]) {
+                ff_vvc_mv_scale(&mmvd[o], mmvd_offset, diff[i], diff[o]);
+            } else {
+                mmvd[o].x = sign ? -mmvd[i].x : mmvd[i].x;
+                mmvd[o].y = sign ? -mmvd[i].y : mmvd[i].y;
+            }
+        }
+        mvf->mv[0].x += mmvd[0].x;
+        mvf->mv[0].y += mmvd[0].y;
+        mvf->mv[1].x += mmvd[1].x;
+        mvf->mv[1].y += mmvd[1].y;
+    } else {
+        const int idx = mvf->pred_flag - PF_L0;
+        mvf->mv[idx].x += mmvd_offset->x;
+        mvf->mv[idx].y += mmvd_offset->y;
+    }
+
+}
+
+static void mvf_to_mi(const MvField *mvf, MotionInfo *mi)
+{
+    mi->pred_flag = mvf->pred_flag;
+    mi->bcw_idx = mvf->bcw_idx;
+    mi->hpel_if_idx = mvf->hpel_if_idx;
+    for (int i = 0; i < 2; i++) {
+        PredFlag mask = i + 1;
+        if (mvf->pred_flag & mask) {
+            mi->mv[i][0] = mvf->mv[i];
+            mi->ref_idx[i] = mvf->ref_idx[i];
+        }
+    }
+}
+
+static void mv_merge_refine_pred_flag(MvField *mvf, const int width, const int height)
+{
+    if (mvf->pred_flag == PF_BI && (width + height) == 12) {
+        mvf->pred_flag = PF_L0;
+        mvf->bcw_idx   = 0;
+    }
+}
+
+static int hls_merge_data(VVCLocalContext *lc)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    const VVCPH  *ph            = fc->ps.ph;
+    const VVCSPS *sps           = fc->ps.sps;
+    const VVCSH *sh             = &lc->sc->sh;
+    CodingUnit *cu              = lc->cu;
+    PredictionUnit *pu          = &cu->pu;
+    MotionInfo *mi              = &pu->mi;
+    const int cb_width          = cu->cb_width;
+    const int cb_height         = cu->cb_height;
+    MvField mvf;
+    int merge_idx = 0;
+
+    pu->merge_gpm_flag = 0;
+    mi->num_sb_x = mi->num_sb_y = 1;
+    if (cu->pred_mode == MODE_IBC) {
+        av_assert0(0 && "fix me");
+        return 0;
+    } else {
+        if (ph->max_num_subblock_merge_cand > 0 && cb_width >= 8 && cb_height >= 8) {
+            pu->merge_subblock_flag = ff_vvc_merge_subblock_flag(lc);
+        }
+        if (pu->merge_subblock_flag) {
+            int merge_subblock_idx = 0;
+            ff_vvc_set_cb_tab(lc, fc->tab.msf, pu->merge_subblock_flag);
+            if (ph->max_num_subblock_merge_cand > 1) {
+                merge_subblock_idx = ff_vvc_merge_subblock_idx(lc, ph->max_num_subblock_merge_cand);
+            }
+            ff_vvc_sb_mv_merge_mode(lc, merge_subblock_idx, pu);
+        } else {
+            int regular_merge_flag = 1;
+            const int is_128 = cb_width == 128 || cb_height == 128;
+            const int ciip_avaiable = sps->ciip_enabled_flag &&
+                !cu->skip_flag && (cb_width * cb_height >= 64);
+            const int gpm_avaiable  = sps->gpm_enabled_flag && IS_B(sh) &&
+                (cb_width >= 8) && (cb_height >=8) &&
+                (cb_width < 8 * cb_height) && (cb_height < 8 *cb_width);
+            if (!is_128 &&  (ciip_avaiable || gpm_avaiable)) {
+                regular_merge_flag = ff_vvc_regular_merge_flag(lc, cu->skip_flag);
+            }
+            if (regular_merge_flag) {
+                Mv mmvd_offset;
+                if (sps->mmvd_enabled_flag) {
+                    pu->mmvd_merge_flag = ff_vvc_mmvd_merge_flag(lc);
+                }
+                if (pu->mmvd_merge_flag) {
+                    int mmvd_cand_flag = 0;
+                    if (sps->max_num_merge_cand > 1) {
+                        mmvd_cand_flag = ff_vvc_mmvd_cand_flag(lc);
+                    }
+                    ff_vvc_mmvd_offset_coding(lc, &mmvd_offset, ph->mmvd_fullpel_only_flag);
+                    merge_idx = mmvd_cand_flag;
+                } else if (sps->max_num_merge_cand > 1){
+                    merge_idx = ff_vvc_merge_idx(lc);
+                }
+                ff_vvc_luma_mv_merge_mode(lc, merge_idx, 0, &mvf);
+                if (pu->mmvd_merge_flag)
+                    derive_mmvd(fc, &mmvd_offset, &mvf);
+                mv_merge_refine_pred_flag(&mvf, cb_width, cb_height);
+                ff_vvc_store_mvf(lc, &mvf);
+                mvf_to_mi(&mvf, mi);
+            } else {
+
+                if (ciip_avaiable && gpm_avaiable) {
+                    cu->ciip_flag = ff_vvc_ciip_flag(lc);
+                } else {
+                    cu->ciip_flag = sps->ciip_enabled_flag && !cu->skip_flag &&
+                        !is_128 && (cb_width * cb_height >= 64);
+                }
+                if (cu->ciip_flag && sps->max_num_merge_cand > 1) {
+                    merge_idx = ff_vvc_merge_idx(lc);
+                }
+                if (!cu->ciip_flag) {
+                    int merge_gpm_idx[2];
+
+                    pu->merge_gpm_flag = 1;
+                    pu->gpm_partition_idx = ff_vvc_merge_gpm_partition_idx(lc);
+                    merge_gpm_idx[0] = ff_vvc_merge_gpm_idx(lc, 0);
+                    merge_gpm_idx[1] = 0;
+                    if (sps->max_num_gpm_merge_cand > 2)
+                        merge_gpm_idx[1] = ff_vvc_merge_gpm_idx(lc, 1);
+
+                    ff_vvc_luma_mv_merge_gpm(lc, merge_gpm_idx, pu->gpm_mv);
+                    ff_vvc_store_gpm_mvf(lc, pu);
+                } else {
+                    ff_vvc_luma_mv_merge_mode(lc, merge_idx, 1, &mvf);
+                    mv_merge_refine_pred_flag(&mvf, cb_width, cb_height);
+                    ff_vvc_store_mvf(lc, &mvf);
+                    mvf_to_mi(&mvf, mi);
+                    cu->intra_pred_mode_y   = cu->intra_pred_mode_c = INTRA_PLANAR;
+                    cu->intra_luma_ref_idx  = 0;
+                    cu->intra_mip_flag      = 0;
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+static void hls_mvd_coding(VVCLocalContext *lc, Mv* mvd)
+{
+    int16_t mv[2];
+    int i;
+
+    for (i = 0; i < 2; i++) {
+        mv[i] = ff_vvc_abs_mvd_greater0_flag(lc);
+    }
+    for (i = 0; i < 2; i++) {
+        if (mv[i])
+            mv[i] += ff_vvc_abs_mvd_greater1_flag(lc);
+    }
+    for (i = 0; i < 2; i++) {
+        if (mv[i] > 0) {
+            if (mv[i] == 2)
+                mv[i] += ff_vvc_abs_mvd_minus2(lc);
+            mv[i] = (1 - 2 * ff_vvc_mvd_sign_flag(lc)) * mv[i];
+        }
+    }
+    mvd->x = mv[0];
+    mvd->y = mv[1];
+}
+
+static int bcw_idx_decode(VVCLocalContext *lc, const MotionInfo *mi, const int cb_width, const int cb_height)
+{
+    VVCFrameContext *fc = lc->fc;
+    const VVCSPS *sps = fc->ps.sps;
+    const VVCPH  *ph = fc->ps.ph;
+    const PredWeightTable *w = &ph->pred_weight_table;
+    int bcw_idx = 0;
+    if (sps->bcw_enabled_flag && mi->pred_flag == PF_BI &&
+        !w->luma_weight_l0_flag[mi->ref_idx[0]] &&
+        !w->luma_weight_l1_flag[mi->ref_idx[1]] &&
+        !w->chroma_weight_l0_flag[mi->ref_idx[0]] &&
+        !w->chroma_weight_l1_flag[mi->ref_idx[1]] &&
+        cb_width * cb_height >= 256) {
+        bcw_idx = ff_vvc_bcw_idx(lc, ff_vvc_no_backward_pred_flag(fc));
+
+    }
+    return bcw_idx;
+}
+
+static int8_t ref_idx_decode(VVCLocalContext *lc, const VVCSH *sh,
+    const int sym_mvd_flag, const int lx)
+{
+    int ref_idx = 0;
+    if (sh->nb_refs[lx] > 1 && !sym_mvd_flag)
+        ref_idx = ff_vvc_ref_idx_lx(lc, sh->nb_refs[lx]);
+    else if (sym_mvd_flag)
+        ref_idx = sh->ref_idx_sym[lx];
+    return ref_idx;
+}
+
+static int mvp_data(VVCLocalContext *lc)
+{
+    VVCFrameContext *fc     = lc->fc;
+    const CodingUnit *cu    = lc->cu;
+    PredictionUnit *pu      = &lc->cu->pu;
+    const VVCSPS *sps       = fc->ps.sps;
+    const VVCPH *ph         = fc->ps.ph;
+    const VVCSH *sh         = &lc->sc->sh;
+    MotionInfo *mi          = &pu->mi;
+    const int cb_width      = cu->cb_width;
+    const int cb_height     = cu->cb_height;
+
+    int mvp_lx_flag[2] = {0};
+    int cu_affine_type_flag = 0;
+    int i, num_cp_mv;
+    int amvr_enabled, has_no_zero_mvd = 0, amvr_shift;
+
+    Mv mvds[2][MAX_CONTROL_POINTS];
+
+    mi->pred_flag = ff_vvc_pred_flag(lc, IS_B(sh));
+    if (sps->affine_enabled_flag && cb_width >= 16 && cb_height >= 16) {
+        pu->inter_affine_flag = ff_vvc_inter_affine_flag(lc);
+        ff_vvc_set_cb_tab(lc, fc->tab.iaf, pu->inter_affine_flag);
+        if (sps->six_param_affine_enabled_flag && pu->inter_affine_flag) {
+            cu_affine_type_flag = ff_vvc_cu_affine_type_flag(lc);
+        }
+    }
+    mi->motion_model_idc = pu->inter_affine_flag + cu_affine_type_flag;
+    num_cp_mv = mi->motion_model_idc + 1;
+
+    if (sps->smvd_enabled_flag && !ph->mvd_l1_zero_flag &&
+        mi->pred_flag == PF_BI && !pu->inter_affine_flag &&
+        sh->ref_idx_sym[0] > -1 && sh->ref_idx_sym[1] > -1)
+        pu->sym_mvd_flag = ff_vvc_sym_mvd_flag(lc);
+
+    for (int i = L0; i <= L1; i++) {
+        const PredFlag pred_flag = PF_L0 + !i;
+        if (mi->pred_flag != pred_flag) {
+            mi->ref_idx[i] = ref_idx_decode(lc, sh, pu->sym_mvd_flag, i);
+            if (i == L1 && ph->mvd_l1_zero_flag && mi->pred_flag == PF_BI) {
+                for (int j = 0; j < num_cp_mv; j++)
+                    AV_ZERO64(&mvds[i][j]);
+            } else {
+                Mv *mvd0 = &mvds[i][0];
+                if (i == L1 && pu->sym_mvd_flag) {
+                    mvd0->x = -mvds[L0][0].x;
+                    mvd0->y = -mvds[L0][0].y;
+                }
+                else
+                    hls_mvd_coding(lc, mvd0);
+                has_no_zero_mvd |= (mvd0->x || mvd0->y);
+                for (int j = 1; j < num_cp_mv; j++) {
+                    Mv *mvd = &mvds[i][j];
+                    hls_mvd_coding(lc, mvd);
+                    mvd->x += mvd0->x;
+                    mvd->y += mvd0->y;
+                    has_no_zero_mvd |= (mvd->x || mvd->y);
+                }
+            }
+            mvp_lx_flag[i] = ff_vvc_mvp_lx_flag(lc);
+        }
+    }
+
+    amvr_enabled = mi->motion_model_idc == MOTION_TRANSLATION ?
+        sps->amvr_enabled_flag : sps->affine_amvr_enabled_flag;
+    amvr_enabled &= has_no_zero_mvd;
+
+    amvr_shift = ff_vvc_amvr_shift(lc, pu->inter_affine_flag, cu->pred_mode, amvr_enabled);
+
+    mi->hpel_if_idx = amvr_shift == 3;
+    mi->bcw_idx = bcw_idx_decode(lc, mi, cb_width, cb_height);
+
+    if (mi->motion_model_idc)
+        ff_vvc_affine_mvp(lc, mvp_lx_flag, amvr_shift, mi);
+    else
+        ff_vvc_mvp(lc, mvp_lx_flag, amvr_shift, mi);
+
+    for (i = 0; i < 2; i++) {
+        PredFlag lx = i + 1;
+        if (mi->pred_flag & lx) {
+            for (int j = 0; j < num_cp_mv; j++) {
+                    const Mv *mvd = &mvds[i][j];
+                    mi->mv[i][j].x += mvd->x << amvr_shift;
+                    mi->mv[i][j].y += mvd->y << amvr_shift;
+            }
+        }
+    }
+    if (mi->motion_model_idc)
+        ff_vvc_store_sb_mvs(lc, pu);
+    else
+        ff_vvc_store_mv(lc, &pu->mi);
+
+
+    return 0;
+}
+
+static void vvc_await_progress(const VVCFrameContext *fc, VVCFrame *ref,
+    const Mv *mv, int y0, int height)
+{
+    //todo: check why we need magic number 9
+    int y = FFMAX(0, (mv->y >> 4) + y0 + height + 9);
+
+    ff_vvc_await_progress(ref, y);
+}
+
+static int pred_await_progress(const VVCFrameContext *fc, VVCFrame *ref[2],
+    const MvField *mv, int y0, int height)
+{
+    for (int mask = PF_L0; mask <= PF_L1; mask++) {
+        if (mv->pred_flag & mask) {
+            const int lx = mask - PF_L0;
+            ref[lx] = fc->ref->refPicList[lx].ref[mv->ref_idx[lx]];
+            if (!ref[lx])
+                return AVERROR_INVALIDDATA;
+            vvc_await_progress(fc, ref[lx], mv->mv + lx, y0, height);
+        }
+    }
+    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->chroma_format_idc ? 3 : 1;
+
+    int16_t *tmp[2] = {lc->tmp, lc->tmp1};
+    const ptrdiff_t tmp_stride = MAX_PB_SIZE;
+
+    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 = fc->ref->refPicList[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 {
+                vvc_await_progress(fc, ref, mv->mv + lx, y, height);
+                luma_mc(lc, tmp[i], ref->frame, mv->mv + lx, x, y, width, height);
+            }
+        }
+        fc->vvcdsp.put_vvc_gpm(dst, dst_stride, width, height, tmp[0], tmp[1], tmp_stride, 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)
+{
+    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->ref->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->ref->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 dmvr_flag, const int sb_bdof_flag)
+{
+    const SliceContext *sc  = lc->sc;
+    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_await_progress(fc, ref, mv, y0, sbh) < 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->mv + lx, x0, y0, sbw, sbh, 0, 0, hf_idx, vf_idx);
+
+        //                        fc->sh.luma_weight_l0[current_mv->ref_idx[0]],
+        //                        fc->sh.luma_offset_l0[current_mv->ref_idx[0]]);
+    } 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, dmvr_flag, sb_bdof_flag, ciip_flag);
+
+    }
+    if (ciip_flag) {
+        const int intra_weight = ciip_derive_intra_weight(lc, x0, y0, sbw, sbh);
+        fc->hpc.intra_pred(lc, x0, y0, sbw, sbh, 0);
+        if (sc->sh.lmcs_used_flag)
+            fc->vvcdsp.lmcs_filter_luma(inter, inter_stride, sbw, sbh, fc->ps.ph->lmcs_fwd_lut);
+        fc->vvcdsp.put_vvc_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)
+{
+    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;
+    if (mv->pred_flag != PF_BI) {
+        const int lx = mv->pred_flag - PF_L0;
+        VVCFrame* ref = fc->ref->refPicList[lx].ref[mv->ref_idx[lx]];
+        if (!ref)
+            return;
+        chroma_mc_uni(lc, inter1, inter1_stride, ref->frame->data[1], ref->frame->linesize[1],
+            lx, x0_c, y0_c, w_c, h_c, mv, 0, 0, hf_idx, vf_idx);
+        //                              fc->sh.chroma_weight_l0[current_mv->ref_idx[0]][0], fc->sh.chroma_offset_l0[current_mv->ref_idx[0]][0]);
+        chroma_mc_uni(lc, inter2, inter2_stride, ref->frame->data[2], ref->frame->linesize[2],
+            lx, x0_c, y0_c, w_c, h_c, mv, 0, 0, hf_idx, vf_idx);
+        //                              fc->sh.chroma_weight_l0[current_mv->ref_idx[0]][1], fc->sh.chroma_offset_l0[current_mv->ref_idx[0]][1]);
+    } else {
+        VVCFrame* ref0 = fc->ref->refPicList[0].ref[mv->ref_idx[0]];
+        VVCFrame* ref1 = fc->ref->refPicList[1].ref[mv->ref_idx[1]];
+        if (!ref0 || !ref1)
+            return;
+        chroma_mc_bi(lc, inter1, inter1_stride, ref0->frame, ref1->frame,
+            x0_c, y0_c, w_c, h_c, mv, 0, hf_idx, vf_idx, orig_mv, dmvr_flag, lc->cu->ciip_flag);
+
+        chroma_mc_bi(lc, inter2, inter2_stride, ref0->frame, ref1->frame,
+            x0_c, y0_c, w_c, h_c, mv, 1, 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->hpc.intra_pred(lc, x0, y0, sbw, sbh, 1);
+        fc->hpc.intra_pred(lc, x0, y0, sbw, sbh, 2);
+        fc->vvcdsp.put_vvc_ciip(dst1, dst1_stride, w_c, h_c, inter1, inter1_stride, intra_weight);
+        fc->vvcdsp.put_vvc_ciip(dst2, dst2_stride, w_c, h_c, inter2, inter2_stride, intra_weight);
+
+    }
+}
+
+// derive bdofFlag from 8.5.6 Decoding process for inter blocks
+// derive dmvr from 8.5.1 General decoding process for coding units coded in inter prediction mode
+static void derive_dmvr_bdof_flag(VVCLocalContext *lc, const PredictionUnit* pu, int *dmvr_flag, int *bdof_flag)
+{
+    const VVCFrameContext *fc = lc->fc;
+    const VVCPH *ph = fc->ps.ph;
+    const int poc = ph->poc;
+    const RefPicList *rpl0 = fc->ref->refPicList + L0;
+    const RefPicList *rpl1 = fc->ref->refPicList + L1;
+    const int8_t *ref_idx = pu->mi.ref_idx;
+    const MotionInfo *mi = &pu->mi;
+    const CodingUnit *cu = lc->cu;
+    const PredWeightTable *w = &ph->pred_weight_table;
+
+    *dmvr_flag = 0;
+    *bdof_flag = 0;
+
+    if (mi->pred_flag == PF_BI &&
+        (poc - rpl0->list[ref_idx[L0]] == rpl1->list[ref_idx[L1]] - poc) &&
+        !rpl0->isLongTerm[ref_idx[L0]] && !rpl1->isLongTerm[ref_idx[L1]] &&
+        !cu->ciip_flag &&
+        !mi->bcw_idx &&
+        !w->luma_weight_l0[mi->ref_idx[L0]] && !w->luma_weight_l1[mi->ref_idx[L1]] &&
+        !w->chroma_weight_l0[mi->ref_idx[L0]] && !w->chroma_weight_l1[mi->ref_idx[L1]] &&
+        cu->cb_width >= 8 && cu->cb_height >= 8 &&
+        (cu->cb_width * cu->cb_height >= 128)) {
+        // fixme: for RprConstraintsActiveFlag
+        if (!ph->bdof_disabled_flag &&
+            mi->motion_model_idc == MOTION_TRANSLATION &&
+            !pu->merge_subblock_flag &&
+            !pu->sym_mvd_flag)
+            *bdof_flag = 1;
+        if (!ph->dmvr_disabled_flag &&
+            pu->general_merge_flag &&
+            !pu->mmvd_merge_flag)
+            *dmvr_flag = 1;
+
+    }
+}
+
+// 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, AVFrame *ref0, AVFrame *ref1,
+    int x_off, int y_off, int block_w, int block_h, MvField *mv, MvField *orig_mv, int *sb_bdof_flag)
+{
+    const VVCFrameContext *fc  = lc->fc;
+    ptrdiff_t src0stride = ref0->linesize[0];
+    ptrdiff_t src1stride = ref1->linesize[0];
+    Mv *mv0 = mv->mv + L0;
+    Mv *mv1 = mv->mv + L1;
+    const int sr_range   = 2;
+    int mx0              = mv0->x & 0xf;
+    int my0              = mv0->y & 0xf;
+    int mx1              = mv1->x & 0xf;
+    int my1              = mv1->y & 0xf;
+    int x_off0           = x_off + (mv0->x >> 4) - sr_range;
+    int y_off0           = y_off + (mv0->y >> 4) - sr_range;
+    int x_off1           = x_off + (mv1->x >> 4) - sr_range;
+    int y_off1           = y_off + (mv1->y >> 4) - sr_range;
+    int pred_w           = block_w + 2 * sr_range;
+    int pred_h           = block_h + 2 * sr_range;
+
+    uint8_t *src0  = ref0->data[0] + y_off0 * src0stride + (int)((unsigned)x_off0 << fc->ps.sps->pixel_shift);
+    uint8_t *src1  = ref1->data[0] + y_off1 * src1stride + (int)((unsigned)x_off1 << fc->ps.sps->pixel_shift);
+
+    int sad[SAD_ARRAY_SIZE][SAD_ARRAY_SIZE];
+    int min_dx, min_dy, min_sad, dx, dy;
+
+    *orig_mv = *mv;
+    min_dx = min_dy = dx = dy = 2;
+
+    EMULATED_EDGE_BILINEAR(lc->edge_emu_buffer, &src0, &src0stride, x_off0, y_off0);
+    EMULATED_EDGE_BILINEAR(lc->edge_emu_buffer2, &src1, &src1stride, x_off1, y_off1);
+    fc->vvcdsp.dmvr_vvc_luma[!!my0][!!mx0](lc->tmp, src0, src0stride, pred_h, mx0, my0, pred_w);
+    fc->vvcdsp.dmvr_vvc_luma[!!my1][!!mx1](lc->tmp1, src1, src1stride, pred_h, mx1, my1, pred_w);
+
+    min_sad = fc->vvcdsp.vvc_sad(lc->tmp, lc->tmp1, 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.vvc_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);
+        }
+        mv0->x += dmv[0];
+        mv0->y += dmv[1];
+        mv1->x += -dmv[0];
+        mv1->y += -dmv[1];
+        ff_vvc_clip_mv(mv0);
+        ff_vvc_clip_mv(mv1);
+    }
+    if (min_sad < 2 * block_w * block_h) {
+        *sb_bdof_flag = 0;
+    }
+}
+static void derive_sb_mv(VVCLocalContext *lc, const int x0, const int y0, const int sbw, const int sbh,
+    const int dmvr_flag, const int bdof_flag,  MvField *mv, MvField *orig_mv, int *sb_bdof_flag)
+{
+    VVCFrameContext *fc = lc->fc;
+
+    *orig_mv = *mv = *ff_vvc_get_mvf(fc, x0, y0);
+    if (bdof_flag)
+        *sb_bdof_flag = 1;
+    if (dmvr_flag) {
+        VVCFrame* ref[2];
+        if (pred_await_progress(fc, ref, mv, y0, sbh) < 0)
+            return;
+        dmvr_mv_refine(lc, ref[0]->frame, ref[1]->frame, x0, y0, sbw, sbh, mv, orig_mv, sb_bdof_flag);
+        ff_vvc_set_dmvr_info(fc, x0, y0, sbw, sbh, mv);
+    }
+}
+
+static void pred_regular_blk(VVCLocalContext *lc)
+{
+    VVCFrameContext *fc         = lc->fc;
+    CodingUnit *cu              = lc->cu;
+    const PredictionUnit *pu    = &cu->pu;
+    const MotionInfo *mi        = &pu->mi;
+    MvField mv, orig_mv;
+    int sbw, sbh, num_sb_x, num_sb_y, sb_bdof_flag = 0;
+    int dmvr_flag, bdof_flag;
+
+    derive_dmvr_bdof_flag(lc, pu, &dmvr_flag, &bdof_flag);
+    num_sb_x = mi->num_sb_x;
+    num_sb_y = mi->num_sb_y;
+    if (dmvr_flag || bdof_flag) {
+        num_sb_x = (cu->cb_width > 16) ? (cu->cb_width >> 4) : 1;
+        num_sb_y = (cu->cb_height > 16) ? (cu->cb_height >> 4) : 1;
+    }
+    sbw = cu->cb_width / num_sb_x;
+    sbh = cu->cb_height / num_sb_y;
+
+    for (int sby = 0; sby < num_sb_y; sby++) {
+        for (int sbx = 0; sbx < 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, x0, y0, sbw, sbh, dmvr_flag, bdof_flag, &mv, &orig_mv, &sb_bdof_flag);
+            pred_regular_luma(lc, mi->hpel_if_idx, mi->hpel_if_idx, &mv, x0, y0, sbw, sbh, &orig_mv, dmvr_flag, sb_bdof_flag);
+            if (fc->ps.sps->chroma_format_idc)
+                pred_regular_chroma(lc, &mv, x0, y0, sbw, sbh, &orig_mv, dmvr_flag);
+        }
+    }
+}
+
+static void derive_affine_mvc(const VVCFrameContext *fc, const MvField *mv,
+    const int x0, const int y0, const int sbw, const int sbh, MvField *mvc)
+{
+    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_await_progress(fc, ref, mv, y, sbh) < 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->mv + lx, 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->chroma_format_idc) {
+                if (!av_mod_uintp2(sby, vs) && !av_mod_uintp2(sbx, hs)) {
+                    MvField mvc;
+                    derive_affine_mvc(fc, mv, x, y, sbw, sbh, &mvc);
+                    pred_regular_chroma(lc, &mvc, x, y, sbw<<hs, sbh<<vs, NULL, 0);
+
+                }
+            }
+
+        }
+    }
+}
+
+int ff_vvc_inter_data(VVCLocalContext *lc)
+{
+    const CodingUnit *cu        = lc->cu;
+    PredictionUnit *pu          = &lc->cu->pu;
+    MotionInfo *mi              = &pu->mi;
+    int ret                     = 0;
+
+    pu->general_merge_flag = 1;
+    if (!cu->skip_flag)
+        pu->general_merge_flag = ff_vvc_general_merge_flag(lc);
+
+    if (pu->general_merge_flag) {
+        hls_merge_data(lc);
+    } else if (cu->pred_mode == MODE_IBC){
+        av_assert0(0 && "fixme");
+    } else {
+        ret = mvp_data(lc);
+    }
+    if (!pu->merge_gpm_flag && !pu->inter_affine_flag && !pu->merge_subblock_flag)
+        ff_vvc_update_hmvp(lc, mi);
+    return ret;
+}
+
+int ff_vvc_predict_inter(VVCLocalContext *lc)
+{
+    const VVCFrameContext *fc   = lc->fc;
+    const CodingUnit *cu        = lc->cu;
+    const PredictionUnit *pu    = &cu->pu;
+    int ret                     = 0;
+
+    if (pu->merge_gpm_flag) {
+        pred_gpm_blk(lc);
+    } else if (pu->inter_affine_flag){
+        pred_affine_blk(lc);
+    } else {
+        pred_regular_blk(lc);
+    }
+    if (lc->sc->sh.lmcs_used_flag && !cu->ciip_flag) {
+        uint8_t* dst0 = POS(0, cu->x0, cu->y0);
+        fc->vvcdsp.lmcs_filter_luma(dst0, fc->frame->linesize[LUMA], cu->cb_width, cu->cb_height, fc->ps.ph->lmcs_fwd_lut);
+    }
+    return ret;
+}
+#undef POS
diff --git a/libavcodec/vvc_itx_1d.c b/libavcodec/vvc_itx_1d.c
new file mode 100644
index 0000000000..dbdd246379
--- /dev/null
+++ b/libavcodec/vvc_itx_1d.c
@@ -0,0 +1,208 @@
+/*
+ * 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
+ */
+
+/* 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"
+
+static void prepare_odd(int *o, int size, const int* x, ptrdiff_t stride)
+{
+    const int step = DCT2_MAX_SIZE / (size * 2);
+
+    for (int i = 0; i < size; i++) {
+        o[i] = 0;
+        for (int j = 0; j < size; j++)
+            o[i] += x[(1 + 2 * j) * stride] * ff_vvc_dct2[(1 + 2*j) * step][i];
+    }
+}
+
+static void decomposition(int *out, ptrdiff_t stride, const int *even, const int *odd, int half)
+{
+    int size = half << 1;
+
+    //start from back, so we can do in place decomposition
+    for (int i = 0; i < half; i++)
+          out[(size - i - 1) * stride] = even[i] - odd[i];
+    for (int i = 0; i < half; i++)
+          out[i * stride] = even[i] + odd[i];
+}
+
+/*
+transMatrix[2][2] =
+      { a,  a }
+      { a, -a }
+ */
+
+void ff_vvc_inv_dct2_2(int *out, ptrdiff_t out_stride,
+    const int *in, ptrdiff_t in_stride)
+{
+    const int a = 64;
+    const int x0 = in[0 * in_stride], x1 = in[1 * in_stride];
+
+    out[0 * out_stride] = a * (x0 + x1);
+    out[1 * out_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 *out, ptrdiff_t out_stride,
+    const int *in, ptrdiff_t in_stride)
+{
+    const int a = 64, b = 83, c = 36;
+    const int x0 = in[0 * in_stride], x1 = in[1 * in_stride];
+    const int x2 = in[2 * in_stride], x3 = in[3 * in_stride];
+    const int e[2] = {
+        a * (x0 + x2),
+        a * (x0 - x2),
+    };
+    const int o[2] = {
+        b * x1 + c * x3,
+        c * x1 - b * x3,
+    };
+
+    decomposition(out, out_stride, e, o, 2);
+}
+
+static void inv_dct2(int *out, const int size, const ptrdiff_t out_stride,
+    const int *in, const ptrdiff_t in_stride, int* e, int* o)
+{
+      int half = size >> 1;
+
+      if (size == 4) {
+            ff_vvc_inv_dct2_4(out, out_stride, in, in_stride);
+            return;
+      }
+      inv_dct2(e, half, 1, in, in_stride << 1, e, o);
+      prepare_odd(o, half, in, in_stride);
+      decomposition(out, out_stride, e, o, half);
+}
+
+#define DEFINE_INV_DCT2_1D(S)                                                                  \
+void ff_vvc_inv_dct2_ ## S(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) \
+{                                                                                              \
+    int o[S>>1], e[S>>1];                                                                      \
+    inv_dct2(out, S, out_stride, in, in_stride, e, o);                                         \
+}
+
+static void matrix_mul(int *out, const ptrdiff_t out_stride, const int *in, const ptrdiff_t in_stride, const int8_t* matrix, const int size)
+{
+    for (int i = 0; i < size; i++) {
+         int o = 0;
+
+         for (int j = 0; j < size; j++)
+              o += in[j * in_stride] * matrix[j * size];
+         *out = o;
+         out += out_stride;
+         matrix++;
+    }
+}
+
+DEFINE_INV_DCT2_1D( 8);
+DEFINE_INV_DCT2_1D(16);
+DEFINE_INV_DCT2_1D(32);
+DEFINE_INV_DCT2_1D(64);
+
+static void inv_dct8(int *out, const ptrdiff_t out_stride, const int *in, const ptrdiff_t in_stride, const int8_t *matrix, const int size)
+{
+    matrix_mul(out, out_stride, in, in_stride, matrix, size);
+}
+
+#define DEFINE_INV_DCT8_1D(S)                                                                  \
+void ff_vvc_inv_dct8_ ## S(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) \
+{                                                                                              \
+    inv_dct8(out, out_stride, in, in_stride, &ff_vvc_dct8_##S##x##S[0][0], S);                 \
+}
+
+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 *out, const ptrdiff_t out_stride, const int *in, const ptrdiff_t in_stride, const int8_t* matrix, const int size)
+{
+     matrix_mul(out, out_stride, in, in_stride, matrix, size);
+}
+
+#define DEFINE_INV_DST7_1D(S)                                                                  \
+void ff_vvc_inv_dst7_ ## S(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride) \
+{                                                                                              \
+    inv_dst7(out, out_stride, in, in_stride, &ff_vvc_dst7_##S##x##S[0][0], S);                 \
+}
+
+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 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((t + 64) >> 7 , -(1 << 15), (1 << 15) - 1);
+     }
+}
diff --git a/libavcodec/vvc_itx_1d.h b/libavcodec/vvc_itx_1d.h
new file mode 100644
index 0000000000..abda322606
--- /dev/null
+++ b/libavcodec/vvc_itx_1d.h
@@ -0,0 +1,49 @@
+/*
+ * VVC 1D transform
+ *
+ * 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_ITX_1D_H
+#define AVCODEC_VVC_ITX_1D_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+#define vvc_itx_1d_fn(name) \
+    void (name)(int *out, ptrdiff_t out_stride, const int *in, ptrdiff_t in_stride)
+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);
+
+#endif /* AVCODEC_VVC_TRANSFORM_H */
diff --git a/libavcodec/vvc_mvs.c b/libavcodec/vvc_mvs.c
new file mode 100644
index 0000000000..c5e51d6549
--- /dev/null
+++ b/libavcodec/vvc_mvs.c
@@ -0,0 +1,1861 @@
+/*
+ * VVC video decoder
+ *
+ * Copyright (C) 2021 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.h"
+#include "vvc_ctu.h"
+#include "vvc_data.h"
+#include "vvc_thread.h"
+#include "vvc_mvs.h"
+
+#define IS_SAME_MV(a, b) (AV_RN64A(a) == AV_RN64A(b))
+
+void ff_vvc_set_neighbour_available(VVCLocalContext *lc, int x0, int y0,
+                                     int nPbW, int nPbH)
+{
+    const int log2_ctb_size = lc->fc->ps.sps->ctb_log2_size_y;
+    int x0b = av_mod_uintp2(x0, log2_ctb_size);
+    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 + nPbW == 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 + nPbW) < lc->end_of_tiles_x;
+}
+
+//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 VVCFrameContext *fc)
+{
+    int check_diffpicount = 0;
+    int i, j;
+    const RefPicList *refPicList = fc->ref->refPicList;
+
+    for (j = 0; j < 2; j++) {
+        for (i = 0; i < refPicList[j].nb_refs; i++) {
+            if (refPicList[j].list[i] > 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;
+    RefPicList* refPicList      = fc->ref->refPicList;
+
+    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(fc) && (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(fc) && (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(fc)) {
+                if (X == 0)
+                    return CHECK_MVSET(0);
+                else
+                    return CHECK_MVSET(1);
+            } else {
+                if (lc->sc->sh.collocated_list)
+                    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->temporal_mvp_enabled_flag || (cu->cb_width * cu->cb_height <= 32))
+        return 0;
+
+    tab_mvf = ref->tab_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;
+        ff_vvc_await_progress(ref, y);
+        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;
+            ff_vvc_await_progress(ref, y);
+            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->ref->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->ref->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->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->ref->tab_mvf;
+    const int min_pu_width      = fc->ps.pps->min_pu_width;
+
+    if (!n->checked) {
+        n->checked = 1;
+        n->available = !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->ref->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->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 = (lc->sc->sh.slice_type == VVC_SLICE_TYPE_B) ?
+            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 VVCSH *sh     = &lc->sc->sh;
+    const int nb_refs   = (sh->slice_type == VVC_SLICE_TYPE_P) ?
+        sh->rpls[0].num_ref_entries : FFMIN(sh->rpls[0].num_ref_entries, sh->rpls[1].num_ref_entries);
+    int zero_idx        = 0;
+
+    while (num_cands < sps->max_num_merge_cand) {
+        MvField *cand = cand_list + num_cands;
+
+        cand->pred_flag    = PF_L0 + ((sh->slice_type == VVC_SLICE_TYPE_B) << 1);
+        AV_ZERO64(cand->mv + 0);
+        AV_ZERO64(cand->mv + 1);
+        cand->ref_idx[0]   = zero_idx < nb_refs ? zero_idx : 0;
+        cand->ref_idx[1]   = zero_idx < nb_refs ? 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, lc->sc->sh.slice_type == VVC_SLICE_TYPE_B)) {
+        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->ref->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->ref->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->ref->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_mvf;
+    int colPic                  = ref->poc;
+    int X                       = 0;
+
+    sb_clip_location(fc, x_ctb, y_ctb, temp_mv, &x, &y);
+
+    ff_vvc_await_progress(ref, y);
+
+    temp_col    = TAB_MVF(x, y);
+    mvLXCol     = mv + 0;
+    *pred_flag = DERIVE_TEMPORAL_COLOCATED_MVS(1);
+    if (sh->slice_type == VVC_SLICE_TYPE_B) {
+        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 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 == fc->ref->refPicList[0].list[a1->ref_idx[0]])
+            *temp_mv = a1->mv[0];
+        else if ((a1->pred_flag & PF_L1) && colPic == fc->ref->refPicList[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->temporal_mvp_enabled_flag ||
+        !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 + ((lc->sc->sh.slice_type == VVC_SLICE_TYPE_B) << 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->six_param_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->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.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->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 VVCFrameContext *fc,
+    const int x_cand, const int y_cand, const int lx, const int8_t *ref_idx,
+    Mv *mv)
+{
+
+    const int min_pu_width          = fc->ps.pps->min_pu_width;
+    const MvField* tab_mvf          = fc->ref->tab_mvf;
+    const MvField *mvf              = &TAB_MVF(x_cand, y_cand);
+    const RefPicList* refPicList    = fc->ref->refPicList;
+    const PredFlag maskx = lx + 1;
+    const int poc = refPicList[lx].list[ref_idx[lx]];
+    int available = 0;
+
+    if ((mvf->pred_flag & maskx) &&  refPicList[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) &&  refPicList[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->ref->tab_mvf;
+        const MvField *mvf = &TAB_MVF(x_nb, y_nb);
+        RefPicList* refPicList = fc->ref->refPicList;
+        const PredFlag maskx = lx + 1;
+        const int poc = refPicList[lx].list[ref_idx[lx]];
+
+        if ((mvf->pred_flag & maskx) &&  refPicList[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) &&  refPicList[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 VVCFrameContext *fc   = lc->fc;
+    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(fc, 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 VVCFrameContext *fc       = lc->fc;
+    const EntryPoint* ep            = lc->ep;
+    const RefPicList* refPicList    = fc->ref->refPicList;
+    const int poc                   = refPicList[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 == refPicList[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 VVCFrameContext *fc       = ctx->lc->fc;
+    const MvField *tab_mvf          = fc->ref->tab_mvf;
+    const int min_pu_width          = fc->ps.pps->min_pu_width;
+    const RefPicList* refPicList    = fc->ref->refPicList;
+    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 = refPicList[lx].list[ref_idx];
+            if ((mvf->pred_flag & maskx) &&  refPicList[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) &&  refPicList[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->ref->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->ref->tab_mvf;
+
+    return &TAB_MVF(x0, y0);
+}
+
+void ff_vvc_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) {
+            DMVRInfo *di = &fc->tab.dmvr[pps->min_pu_width * (y >> MIN_PU_LOG2) + (x >> MIN_PU_LOG2)];
+            di->dmvr_enabled = 1;
+            if (mvf->pred_flag & PF_L0)
+                di->mv[L0] = mvf->mv[L0];
+            if (mvf->pred_flag & PF_L1)
+                di->mv[L1] = mvf->mv[L1];
+        }
+    }
+}
+
+void ff_vvc_apply_dmvr_info_ctb(VVCFrameContext *fc, const int x0, const int y0)
+{
+    const VVCPPS *pps   = fc->ps.pps;
+    const int ctb_size  = fc->ps.sps->ctb_size_y;
+    const int x_end     = FFMIN(x0 + ctb_size, pps->width);
+    const int y_end     = FFMIN(y0 + ctb_size, pps->height);
+
+    for (int y = y0; y < y_end; y += MIN_PU_SIZE) {
+        for (int x = x0; x < x_end; x += MIN_PU_SIZE) {
+            const int off = pps->min_pu_width * (y >> MIN_PU_LOG2) + (x >> MIN_PU_LOG2);
+            const DMVRInfo *di = &fc->tab.dmvr[off];
+            if (di->dmvr_enabled) {
+                MvField *mvf = &fc->ref->tab_mvf[off];
+                if (mvf->pred_flag & PF_L0)
+                    mvf->mv[L0] = di->mv[L0];
+                if (mvf->pred_flag & PF_L1)
+                    mvf->mv[L1] = di->mv[L1];
+            }
+        }
+    }
+}
diff --git a/libavcodec/vvc_mvs.h b/libavcodec/vvc_mvs.h
new file mode 100644
index 0000000000..1778a05868
--- /dev/null
+++ b/libavcodec/vvc_mvs.h
@@ -0,0 +1,49 @@
+/*
+ * VVC mvs decoder 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_MVS_H
+#define AVCODEC_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_set_neighbour_available(VVCLocalContext *lc, int x0, int y0,            int nPbW, int nPbH);
+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 VVCFrameContext *fc);
+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);
+void ff_vvc_set_dmvr_info(VVCFrameContext *fc, int x0, const int y0, int width, int height, const MvField *mvf);
+void ff_vvc_apply_dmvr_info_ctb(VVCFrameContext *fc, int x0, const int y0);
+
+#endif //AVCODEC_VVC_MVS_H
diff --git a/libavcodec/vvc_parser.c b/libavcodec/vvc_parser.c
index b10b3a5914..40ee6016b7 100644
--- a/libavcodec/vvc_parser.c
+++ b/libavcodec/vvc_parser.c
@@ -212,7 +212,7 @@ static int set_parser_ctx(AVCodecParserContext *ctx, AVCodecContext *avctx,
         h266_sub_width_c[sps->sps_chroma_format_idc];
     ctx->height       = pps->pps_pic_height_in_luma_samples -
         (pps->pps_conf_win_top_offset + pps->pps_conf_win_bottom_offset) *
-        h266_sub_height_c[sps->sps_chroma_format_idc];;
+        h266_sub_height_c[sps->sps_chroma_format_idc];
     ctx->pict_type    = pu->pic_type;
     ctx->format       = get_format(sps);
 
@@ -226,7 +226,7 @@ static int set_parser_ctx(AVCodecParserContext *ctx, AVCodecContext *avctx,
     }
     avctx->pix_fmt = ctx->format;
 
-    if(sps->sps_ptl_dpb_hrd_params_present_flag && sps->sps_timing_hrd_params_present_flag) {
+    if (sps->sps_ptl_dpb_hrd_params_present_flag && sps->sps_timing_hrd_params_present_flag) {
         num = sps->sps_general_timing_hrd_parameters.num_units_in_tick;
         den = sps->sps_general_timing_hrd_parameters.time_scale;
     } else {
diff --git a/libavcodec/vvc_ps.c b/libavcodec/vvc_ps.c
new file mode 100644
index 0000000000..55b5fdd01f
--- /dev/null
+++ b/libavcodec/vvc_ps.c
@@ -0,0 +1,3328 @@
+/*
+ * VVC Parameter Set decoding
+ *
+ * Copyright (C) 2021 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 "libavutil/imgutils.h"
+#include "golomb.h"
+#include "vvc_data.h"
+#include "vvc_ps.h"
+#include "vvcdec.h"
+
+#define u(b, f, min, max) do { \
+    f = get_bits(gb, b); \
+    if ((f < (min)) || (f > (max))) { \
+        av_log(log_ctx, AV_LOG_ERROR, \
+               #f " %d should in range [%d, %d]", f, (min), (max)); \
+    }\
+} while (0)
+
+#define uep(f, plus, min, max) do { \
+    f = get_ue_golomb_long(gb) + plus; \
+    if (((f) < (min)) || ((f) > (max))) {\
+        av_log(log_ctx, AV_LOG_ERROR, \
+               #f " %d should in range [%d, %d]", f, (min), (max)); \
+        return AVERROR_INVALIDDATA; \
+    } \
+} while (0)
+
+#define ue(f, max) do { \
+    f = get_ue_golomb_long(gb); \
+    if ((f) > (max)) {\
+        av_log(log_ctx, AV_LOG_ERROR, \
+               #f " %d should < %d", f, (max)); \
+        return AVERROR_INVALIDDATA; \
+    } \
+} while (0)
+
+#define sep(f, plus, min, max) do {\
+    f = get_se_golomb(gb) + plus; \
+    if ((f < (min)) || (f > (max))) {\
+        av_log(log_ctx, AV_LOG_ERROR, \
+               #f " %d should in range [%d, %d]", f, (min), (max)); \
+        return AVERROR_INVALIDDATA; \
+    } \
+} while (0)
+
+#define se(f, min, max) do {\
+    f = get_se_golomb(gb); \
+    if ((f < (min)) || (f > (max))) {\
+        av_log(log_ctx, AV_LOG_ERROR, \
+               #f " %d should in range [%d, %d]", f, (min), (max)); \
+        return AVERROR_INVALIDDATA; \
+    } \
+} while (0)
+
+typedef struct VVCLMCS {
+    int min_bin_idx;
+    int max_bin_idx;
+    int delta_cw[LMCS_MAX_BIN_SIZE];
+    int delta_crs;
+} VVCLMCS;
+
+static av_always_inline unsigned int vvc_ceil(unsigned int v, unsigned int align)
+{
+    return (((v) + (align) - 1) / (align));
+}
+
+static void remove_pps(VVCParamSets *s, int id)
+{
+    av_buffer_unref(&s->pps_list[id]);
+}
+
+static void remove_sps(VVCParamSets *s, int id)
+{
+    if (s->sps_list[id]) {
+        /* drop all PPS that depend on this SPS */
+        for (int i = 0; i < FF_ARRAY_ELEMS(s->pps_list); i++)
+            if (s->pps_list[i] && ((VVCPPS*)s->pps_list[i]->data)->seq_parameter_set_id == id)
+                remove_pps(s, i);
+    }
+    av_buffer_unref(&s->sps_list[id]);
+}
+
+static int gci_parse(GeneralConstraintsInfo *gci, GetBitContext *gb, void *log_ctx)
+{
+    int i;
+
+    gci->present_flag = get_bits1(gb);
+    if (gci->present_flag) {
+        unsigned int num_reserved_bits;
+        /* general */
+        gci->intra_only_constraint_flag = get_bits1(gb);
+        gci->all_layers_independent_constraint_flag = get_bits1(gb);
+        gci->one_au_only_constraint_flag = get_bits1(gb);
+
+        /* picture format */
+        gci->sixteen_minus_max_bitdepth_constraint_idc = get_bits(gb, 4);
+        if (gci->sixteen_minus_max_bitdepth_constraint_idc > 8U) {
+            av_log(log_ctx, AV_LOG_ERROR,
+                   "sixteen_minus_max_bitdepth_constraint_idc  %d is invalid\n",
+                   gci->sixteen_minus_max_bitdepth_constraint_idc);
+            return AVERROR_INVALIDDATA;
+        }
+        gci-> three_minus_max_chroma_format_constraint_idc = get_bits(gb, 2);
+
+        /* NAL unit type related */
+        gci->no_mixed_nalu_types_in_pic_constraint_flag = get_bits1(gb);
+        gci->no_trail_constraint_flag = get_bits1(gb);
+        gci->no_stsa_constraint_flag = get_bits1(gb);
+        gci->no_rasl_constraint_flag = get_bits1(gb);
+        gci->no_radl_constraint_flag = get_bits1(gb);
+        gci->no_idr_constraint_flag = get_bits1(gb);
+        gci->no_cra_constraint_flag = get_bits1(gb);
+        gci->no_gdr_constraint_flag = get_bits1(gb);
+        gci->no_aps_constraint_flag = get_bits1(gb);
+        gci->no_idr_rpl_constraint_flag = get_bits1(gb);
+
+        /* tile, slice, subpicture partitioning */
+        gci->one_tile_per_pic_constraint_flag = get_bits1(gb);
+        gci->pic_header_in_slice_header_constraint_flag = get_bits1(gb);
+        gci->one_slice_per_pic_constraint_flag = get_bits1(gb);
+        gci->no_rectangular_slice_constraint_flag = get_bits1(gb);
+        gci->one_slice_per_subpic_constraint_flag = get_bits1(gb);
+        gci->no_subpic_info_constraint_flag = get_bits1(gb);
+
+        /* CTU and block partitioning */
+        gci->three_minus_max_log2_ctu_size_constraint_idc = get_bits(gb, 2);
+        gci->no_partition_constraints_override_constraint_flag = get_bits1(gb);
+        gci->no_mtt_constraint_flag = get_bits1(gb);
+        gci->no_qtbtt_dual_tree_intra_constraint_flag = get_bits1(gb);
+
+        /* intra */
+        gci->no_palette_constraint_flag = get_bits1(gb);
+        gci->no_ibc_constraint_flag = get_bits1(gb);
+        gci->no_isp_constraint_flag = get_bits1(gb);
+        gci->no_mrl_constraint_flag = get_bits1(gb);
+        gci->no_mip_constraint_flag = get_bits1(gb);
+        gci->no_cclm_constraint_flag = get_bits1(gb);
+
+        /* inter */
+        gci->no_ref_pic_resampling_constraint_flag = get_bits1(gb);
+        gci->no_res_change_in_clvs_constraint_flag = get_bits1(gb);
+        gci->no_weighted_prediction_constraint_flag = get_bits1(gb);
+        gci->no_ref_wraparound_constraint_flag = get_bits1(gb);
+        gci->no_temporal_mvp_constraint_flag = get_bits1(gb);
+        gci->no_sbtmvp_constraint_flag = get_bits1(gb);
+        gci->no_amvr_constraint_flag = get_bits1(gb);
+        gci->no_bdof_constraint_flag = get_bits1(gb);
+        gci->no_smvd_constraint_flag = get_bits1(gb);
+        gci->no_dmvr_constraint_flag = get_bits1(gb);
+        gci->no_mmvd_constraint_flag = get_bits1(gb);
+        gci->no_affine_motion_constraint_flag = get_bits1(gb);
+        gci->no_prof_constraint_flag = get_bits1(gb);
+        gci->no_bcw_constraint_flag = get_bits1(gb);
+        gci->no_ciip_constraint_flag = get_bits1(gb);
+        gci->no_gpm_constraint_flag = get_bits1(gb);
+
+        /* transform, quantization, residual */
+        gci->no_luma_transform_size_64_constraint_flag = get_bits1(gb);
+        gci->no_transform_skip_constraint_flag = get_bits1(gb);
+        gci->no_bdpcm_constraint_flag = get_bits1(gb);
+        gci->no_mts_constraint_flag = get_bits1(gb);
+        gci->no_lfnst_constraint_flag = get_bits1(gb);
+        gci->no_joint_cbcr_constraint_flag = get_bits1(gb);
+        gci->no_sbt_constraint_flag = get_bits1(gb);
+        gci->no_act_constraint_flag = get_bits1(gb);
+        gci->no_explicit_scaling_list_constraint_flag = get_bits1(gb);
+        gci->no_dep_quant_constraint_flag = get_bits1(gb);
+        gci->no_sign_data_hiding_constraint_flag = get_bits1(gb);
+        gci->no_cu_qp_delta_constraint_flag = get_bits1(gb);
+        gci->no_chroma_qp_offset_constraint_flag = get_bits1(gb);
+
+        /* loop filter */
+        gci->no_sao_constraint_flag = get_bits1(gb);
+        gci->no_alf_constraint_flag = get_bits1(gb);
+        gci->no_ccalf_constraint_flag = get_bits1(gb);
+        gci->no_lmcs_constraint_flag = get_bits1(gb);
+        gci->no_ladf_constraint_flag = get_bits1(gb);
+        gci->no_virtual_boundaries_constraint_flag = get_bits1(gb);
+        num_reserved_bits = get_bits(gb, 8);
+        for (i = 0; i < num_reserved_bits; i++) {
+            skip_bits1(gb);          //reserved_zero_bit[i]
+        }
+    }
+    align_get_bits(gb);
+    return 0;
+}
+
+static int ptl_parse(PTL *ptl, const int profile_tier_present_flag,
+    const int max_num_sub_layers_minus1, GetBitContext *gb, void *log_ctx)
+{
+    int ret, i;
+
+    if (profile_tier_present_flag) {
+        ptl->general_profile_idc = get_bits(gb, 7);
+        ptl->general_tier_flag = get_bits1(gb);
+    }
+    ptl->general_level_idc = get_bits(gb, 8);
+    ptl->frame_only_constraint_flag = get_bits1(gb);
+    ptl->multilayer_enabled_flag = get_bits1(gb);
+    if (profile_tier_present_flag) {
+        ret = gci_parse(&ptl->gci, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    }
+
+    for (i = max_num_sub_layers_minus1 - 1; i >= 0; i--)
+        ptl->sublayer_level_present_flag[i] = get_bits1(gb);
+
+    align_get_bits(gb);
+
+    for (i = max_num_sub_layers_minus1 - 1; i >= 0; i--) {
+        if (ptl->sublayer_level_present_flag[i])
+            ptl->sublayer_level_idc[i] = get_bits(gb, 8);
+    }
+
+    if (profile_tier_present_flag) {
+        ptl->num_sub_profiles = get_bits(gb, 8);
+        for (i = 0; i < ptl->num_sub_profiles; i++)
+            ptl->general_sub_profile_idc[i] = get_bits(gb, 32);
+    }
+    return 0;
+}
+
+static int map_pixel_format(VVCSPS *sps, void *log_ctx)
+{
+    const AVPixFmtDescriptor *desc;
+    switch (sps->bit_depth) {
+    case 8:
+        if (sps->chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY8;
+        if (sps->chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P;
+        if (sps->chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P;
+        if (sps->chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P;
+       break;
+    case 10:
+        if (sps->chroma_format_idc == 0) sps->pix_fmt = AV_PIX_FMT_GRAY10;
+        if (sps->chroma_format_idc == 1) sps->pix_fmt = AV_PIX_FMT_YUV420P10;
+        if (sps->chroma_format_idc == 2) sps->pix_fmt = AV_PIX_FMT_YUV422P10;
+        if (sps->chroma_format_idc == 3) sps->pix_fmt = AV_PIX_FMT_YUV444P10;
+        break;
+    default:
+        av_log(log_ctx, AV_LOG_ERROR,
+               "The following bit-depths are currently specified: 8, 10 bits, "
+               "chroma_format_idc is %d, depth is %d\n",
+               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_parse_pic_resampling(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    sps->ref_pic_resampling_enabled_flag = get_bits1(gb);
+    if (sps->ref_pic_resampling_enabled_flag)
+        sps->res_change_in_clvs_allowed_flag = get_bits1(gb);
+    if (sps->res_change_in_clvs_allowed_flag) {
+        av_log(log_ctx, AV_LOG_ERROR, "res_change_in_clvs_allowed_flag is not supported yet.\n");
+        return AVERROR_PATCHWELCOME;
+    }
+
+    return 0;
+}
+
+static void win_parse(VVCWindow* win, const VVCSPS *sps, GetBitContext *gb)
+{
+    win->left_offset   = get_ue_golomb_long(gb) << sps->hshift[1];
+    win->right_offset  = get_ue_golomb_long(gb) << sps->hshift[1];
+    win->top_offset    = get_ue_golomb_long(gb) << sps->vshift[1];
+    win->bottom_offset = get_ue_golomb_long(gb) << sps->vshift[1];
+}
+
+static void sps_parse_conf_win(VVCSPS *sps, GetBitContext *gb, AVCodecContext *avctx)
+{
+    if (get_bits1(gb)) { // sps_conformance_window_flag
+        win_parse(&sps->conf_win, sps, gb);
+
+        if (avctx->flags2 & AV_CODEC_FLAG2_IGNORE_CROP) {
+            av_log(avctx, AV_LOG_DEBUG,
+                   "discarding sps conformance window, "
+                   "original values are l:%u r:%u t:%u b:%u\n",
+                   sps->conf_win.left_offset,
+                   sps->conf_win.right_offset,
+                   sps->conf_win.top_offset,
+                   sps->conf_win.bottom_offset);
+
+            sps->conf_win.left_offset   =
+            sps->conf_win.right_offset  =
+            sps->conf_win.top_offset    =
+            sps->conf_win.bottom_offset = 0;
+        }
+        sps->output_window = sps->conf_win;
+    }
+}
+
+static void pps_parse_conf_win(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb)
+{
+    if (get_bits1(gb)) { // pps_conformance_window_flag
+        win_parse(&pps->conf_win, sps, gb);
+    } else if (pps->width  == sps->width && pps->height == sps->height) {
+        pps->conf_win = sps->conf_win;
+    }
+}
+
+static void scaling_win_parse(VVCWindow* win, const VVCSPS *sps, GetBitContext *gb)
+{
+    win->left_offset   = get_se_golomb(gb) << sps->hshift[1];
+    win->right_offset  = get_se_golomb(gb) << sps->hshift[1];
+    win->top_offset    = get_se_golomb(gb) << sps->vshift[1];
+    win->bottom_offset = get_se_golomb(gb) << sps->vshift[1];
+}
+
+static int pps_scaling_win_parse(VVCPPS *pps, const VVCSPS *sps,
+    GetBitContext *gb, void *log_ctx)
+{
+    if (get_bits1(gb)) { //pps_scaling_window_explicit_signalling_flag
+        if (!sps->ref_pic_resampling_enabled_flag) {
+            av_log(log_ctx, AV_LOG_ERROR,
+                "Invalid data: sps_ref_pic_resampling_enabled_flag is false, "
+                "but pps_scaling_window_explicit_signalling_flag is true.\n");
+            return AVERROR_INVALIDDATA;
+        }
+        scaling_win_parse(&pps->scaling_win, sps, gb);
+     } else {
+        pps->scaling_win = pps->conf_win;
+    }
+    return 0;
+}
+
+static int sps_parse_subpic(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    int i;
+    unsigned int ctb_size_y = sps->ctb_size_y;
+    unsigned int tmp_width_val =
+        vvc_ceil(sps->width, ctb_size_y);
+    unsigned int tmp_height_val =
+        vvc_ceil(sps->height, ctb_size_y);
+
+    sps->subpic_info_present_flag = get_bits1(gb);
+    if (sps->subpic_info_present_flag) {
+        sps->num_subpics = get_ue_golomb_long(gb) + 1;
+        if (sps->num_subpics > VVC_MAX_SLICES) {
+            av_log(log_ctx, AV_LOG_ERROR, "num_subpics out of range: %d\n",
+                   sps->num_subpics);
+            return AVERROR_INVALIDDATA;
+        }
+        if (sps->num_subpics > 1) {
+            const int wlen = av_ceil_log2(tmp_width_val);
+            const int hlen = av_ceil_log2(tmp_height_val);
+            int sps_subpic_same_size_flag;
+
+            sps->independent_subpics_flag = get_bits1(gb);
+            sps_subpic_same_size_flag = get_bits1(gb);
+            if (sps->width > ctb_size_y)
+                sps->subpic_width[0]  = get_bits(gb, wlen) + 1;
+            else
+                sps->subpic_width[0] = tmp_width_val;
+            if (sps->height > ctb_size_y)
+                sps->subpic_height[0] = get_bits(gb, hlen) + 1;
+            else
+                sps->subpic_height[0] = tmp_height_val;
+            if (!sps->independent_subpics_flag) {
+                sps->subpic_treated_as_pic_flag[0] = get_bits1(gb);
+                sps->loop_filter_across_subpic_enabled_flag[0] = get_bits1(gb);
+            } else {
+                sps->subpic_treated_as_pic_flag[0] = 1;
+                sps->loop_filter_across_subpic_enabled_flag[0] = 1;
+            }
+            for (i = 1; i < sps->num_subpics; i++) {
+                if (!sps_subpic_same_size_flag) {
+                    if (sps->width > ctb_size_y)
+                        sps->subpic_ctu_top_left_x[i] = get_bits(gb, wlen);
+                    if (sps->height > ctb_size_y)
+                        sps->subpic_ctu_top_left_y[i] = get_bits(gb, hlen);
+                    if (i < sps->num_subpics - 1 &&
+                        sps->width > ctb_size_y) {
+                        sps->subpic_width[i]  = get_bits(gb, wlen) + 1;
+                    } else {
+                        sps->subpic_width[i]  =
+                            tmp_width_val - sps->subpic_ctu_top_left_x[i];
+                    }
+                    if (i < sps->num_subpics - 1 &&
+                        sps->height > ctb_size_y) {
+                        sps->subpic_height[i] = get_bits(gb, hlen) + 1;
+                    } else   {
+                        sps->subpic_height[i] =
+                            tmp_height_val - sps->subpic_ctu_top_left_y[i];
+                    }
+                } else {
+                    int num_subpic_cols =
+                        tmp_width_val / sps->subpic_width[0];
+                    sps->subpic_ctu_top_left_x[i] =
+                        (i % num_subpic_cols) * sps->subpic_width[0];
+                    sps->subpic_ctu_top_left_y[i] =
+                        (i / num_subpic_cols) * sps->subpic_height[0];
+                    sps->subpic_width[i]  = sps->subpic_width[0];
+                    sps->subpic_height[i] = sps->subpic_height[0];
+                }
+                if (!sps->independent_subpics_flag) {
+                    sps->subpic_treated_as_pic_flag[i] = get_bits1(gb);
+                    sps->loop_filter_across_subpic_enabled_flag[i] = get_bits1(gb);
+                } else {
+                    sps->subpic_treated_as_pic_flag[i] = 1;
+                }
+            }
+            sps->subpic_id_len = get_ue_golomb_long(gb) + 1;
+
+            if (sps->subpic_id_len > 16U ||
+                ((1 << sps->subpic_id_len) < sps->num_subpics)) {
+                av_log(log_ctx, AV_LOG_ERROR,
+                    "subpic_id_len(%d) is invalid\n",
+                    sps->subpic_id_len);
+                return AVERROR_INVALIDDATA;
+            }
+            sps->subpic_id_mapping_explicitly_signalled_flag = get_bits1(gb);
+            if (sps->subpic_id_mapping_explicitly_signalled_flag) {
+                sps->subpic_id_mapping_present_flag = get_bits1(gb);
+                if (sps->subpic_id_mapping_present_flag) {
+                    for (i = 0; i < sps->num_subpics; i++) {
+                        sps->subpic_id[i] = get_bits(gb, sps->subpic_id_len);
+                    }
+                }
+            }
+        } else {
+            sps->subpic_ctu_top_left_x[0] = 0;
+            sps->subpic_ctu_top_left_y[0] = 0;
+            sps->subpic_width[0] = tmp_width_val;
+            sps->subpic_height[0] = tmp_height_val;
+        }
+    } else {
+        sps->num_subpics = 1;
+        sps->independent_subpics_flag = 1;
+        sps->subpic_width[0] = tmp_width_val;
+        sps->subpic_height[0] = tmp_height_val;
+    }
+
+    return 0;
+}
+
+static int sps_parse_bit_depth(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    sps->bit_depth = get_ue_golomb_long(gb) + 8;
+    if (sps->bit_depth > 10)
+        return AVERROR_PATCHWELCOME;
+
+    sps->qp_bd_offset = 6 * (sps->bit_depth - 8);
+    return map_pixel_format(sps, log_ctx);
+}
+
+static int sps_parse_poc(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    sps->log2_max_pic_order_cnt_lsb = get_bits(gb, 4) + 4;
+    if (sps->log2_max_pic_order_cnt_lsb > 16U) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "log2_max_pic_order_cnt_lsb %d > 16 is invalid",
+               sps->log2_max_pic_order_cnt_lsb);
+        return AVERROR_INVALIDDATA;
+    }
+    sps->max_pic_order_cnt_lsb = 1 << sps->log2_max_pic_order_cnt_lsb;
+
+    sps->poc_msb_cycle_flag = get_bits1(gb);
+    if (sps->poc_msb_cycle_flag)
+        uep(sps->poc_msb_cycle_len, 1, 1, 32 - sps->log2_max_pic_order_cnt_lsb);
+
+    return 0;
+}
+
+static int sps_parse_extra_bytes(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    u(2, sps->num_extra_ph_bytes, 0, 2);
+    for (int i = 0; i < (sps->num_extra_ph_bytes * 8); i++)
+        sps->extra_ph_bit_present_flag[i] = get_bits1(gb);
+
+    u(2, sps->num_extra_sh_bytes, 0, 2);
+    for (int i = 0; i < (sps->num_extra_sh_bytes * 8); i++)
+        sps->extra_sh_bit_present_flag[i] = get_bits1(gb);
+
+    return 0;
+}
+
+static void dpb_parameters_parse(DpbParameters *dpb, const uint8_t max_sublayers_minus1,
+    const uint8_t sublayer_info_flag, GetBitContext *gb)
+{
+    int i = (sublayer_info_flag ? 0 : max_sublayers_minus1);
+    for (/* nothing */; i <= max_sublayers_minus1; i++) {
+        dpb->max_dec_pic_buffering[i] = get_ue_golomb_long(gb) + 1;
+        dpb->max_num_reorder_pics[i]  = get_ue_golomb_long(gb);
+        dpb->max_latency_increase[i]  = get_ue_golomb_long(gb) - 1;
+    }
+}
+
+static int partition_constraints_parse(PartitionConstraints *pc, const VVCSPS *sps,
+    GetBitContext *gb, void *log_ctx)
+{
+    const int max = FFMIN(6, sps->ctb_log2_size_y);
+
+    ue(pc->log2_diff_min_qt_min_cb, max - sps->min_cb_log2_size_y);
+    ue(pc->max_mtt_hierarchy_depth, 2 * (sps->ctb_log2_size_y - sps->min_cb_log2_size_y));
+    if (pc->max_mtt_hierarchy_depth) {
+        const int min_qt_log2_size = pc->log2_diff_min_qt_min_cb + sps->min_cb_log2_size_y;
+        ue(pc->log2_diff_max_bt_min_qt, sps->ctb_log2_size_y - min_qt_log2_size);
+        ue(pc->log2_diff_max_tt_min_qt, FFMIN(6, sps->ctb_log2_size_y) - min_qt_log2_size);
+    }
+
+    return 0;
+}
+
+static int sps_parse_partition_constraints(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    int ret = 0;
+
+    sps->partition_constraints_override_enabled_flag = get_bits1(gb);
+
+    ret = partition_constraints_parse(&sps->intra_slice_luma, sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    if (sps->chroma_format_idc != 0) {
+        sps->qtbtt_dual_tree_intra_flag = get_bits1(gb);
+        if (sps->qtbtt_dual_tree_intra_flag) {
+            ret = partition_constraints_parse(&sps->intra_slice_chroma, sps, gb, log_ctx);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
+    ret = partition_constraints_parse(&sps->inter_slice, sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    return ret;
+}
+
+static int rpls_parse(VVCRefPicListStruct *rpls, GetBitContext *gb,
+    const uint8_t list_idx, const uint8_t rpls_idx, const VVCSPS *sps, void *log_ctx)
+{
+    int i, j;
+
+    rpls->num_ltrp_entries = 0;
+    ue(rpls->num_ref_entries, VVC_MAX_REF_ENTRIES);
+    if (sps->long_term_ref_pics_flag &&
+        rpls_idx < sps->num_ref_pic_lists[list_idx] &&
+        rpls->num_ref_entries > 0)
+        rpls->ltrp_in_header_flag = get_bits1(gb);
+    if (sps->long_term_ref_pics_flag &&
+        rpls_idx == sps->num_ref_pic_lists[list_idx])
+        rpls->ltrp_in_header_flag = 1;
+    for (i = 0, j = 0; i < rpls->num_ref_entries; i++) {
+        VVCRefPicListStructEntry *entry = &rpls->entries[i];
+        if (sps->inter_layer_prediction_enabled_flag)
+            entry->inter_layer_ref_pic_flag = get_bits1(gb);
+
+        if (!entry->inter_layer_ref_pic_flag) {
+            if (sps->long_term_ref_pics_flag)
+                entry->st_ref_pic_flag = get_bits1(gb);
+            else
+                entry->st_ref_pic_flag = 1;
+            if (entry->st_ref_pic_flag) {
+                int abs_delta_poc_st, strp_entry_sign_flag = 0;
+                ue(abs_delta_poc_st, (1<<15) - 1);
+                if (!((sps->weighted_pred_flag ||
+                    sps->weighted_bipred_flag) && i != 0))
+                    abs_delta_poc_st++;
+                if (abs_delta_poc_st > 0)
+                    strp_entry_sign_flag = get_bits1(gb);
+                entry->delta_poc_val_st = (1 - 2 * strp_entry_sign_flag) * abs_delta_poc_st;
+            } else {
+                if (!rpls->ltrp_in_header_flag) {
+                    uint8_t bits = sps->log2_max_pic_order_cnt_lsb;
+                    entry->lt_poc = get_bits(gb, bits);
+                    j++;
+                }
+                rpls->num_ltrp_entries++;
+            }
+        } else {
+            return AVERROR_PATCHWELCOME;
+        }
+    }
+
+    return 0;
+}
+
+static int sps_parse_chroma_qp_table(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    const int sps_same_qp_table_for_chroma_flag = get_bits1(gb);
+    const int num_qp_tables = sps_same_qp_table_for_chroma_flag ?
+        1 : (sps->joint_cbcr_enabled_flag ? 3 : 2);
+
+    for (int i = 0; i < num_qp_tables; i++) {
+        int qp_table_start_minus26, num_points_in_qp_table_minus1;
+        int num_points_in_qp_table;
+        int 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;
+
+        se(qp_table_start_minus26, -26 - sps->qp_bd_offset, 36);
+        ue(num_points_in_qp_table_minus1, 36 - qp_table_start_minus26);
+        num_points_in_qp_table = num_points_in_qp_table_minus1 + 1;
+
+        qp_out[0] = qp_in[0] = qp_table_start_minus26 + 26;
+        for (int j = 0; j < num_points_in_qp_table; j++ ) {
+            const uint8_t max = 0xff;
+            unsigned int delta_qp_diff_val, delta_qp_in_val_minus1;
+
+            ue(delta_qp_in_val_minus1, max);
+            ue(delta_qp_diff_val, max);
+            delta_qp_in[j] = delta_qp_in_val_minus1 + 1;
+            qp_in[j+1] = qp_in[j] + delta_qp_in[j];
+            qp_out[j+1] = qp_out[j] + (delta_qp_in_val_minus1 ^ delta_qp_diff_val);
+        }
+        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 (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 int sps_parse_transform(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    int ret = 0;
+
+    sps->transform_skip_enabled_flag = get_bits1(gb);
+    if (sps->transform_skip_enabled_flag) {
+        sps->max_ts_size = 1 << (get_ue_golomb_long(gb) + 2);
+        if (sps->max_ts_size > 32) {
+            av_log(log_ctx, AV_LOG_ERROR,
+               "sps_log2_transform_skip_max_size_minus2 > 3 is invalid");
+            return AVERROR_INVALIDDATA;
+        }
+        sps->bdpcm_enabled_flag = get_bits1(gb);
+    }
+
+    sps->mts_enabled_flag = get_bits1(gb);
+    if (sps->mts_enabled_flag) {
+        sps->explicit_mts_intra_enabled_flag = get_bits1(gb);
+        sps->explicit_mts_inter_enabled_flag = get_bits1(gb);
+    }
+
+    sps->lfnst_enabled_flag = get_bits1(gb);
+
+    if (sps->chroma_format_idc != 0) {
+        sps->joint_cbcr_enabled_flag = get_bits1(gb);
+        ret = sps_parse_chroma_qp_table(sps, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    }
+
+    return ret;
+}
+
+static void sps_parse_filter(VVCSPS *sps, GetBitContext *gb)
+{
+    sps->sao_enabled_flag = get_bits1(gb);
+
+    sps->alf_enabled_flag = get_bits1(gb);
+    if (sps->alf_enabled_flag && sps->chroma_format_idc)
+        sps->ccalf_enabled_flag = get_bits1(gb);
+
+    sps->lmcs_enabled_flag = get_bits1(gb);
+}
+
+static int sps_parse_rpls(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    int sps_rpl1_same_as_rpl0_flag;
+
+    sps->idr_rpl_present_flag = get_bits1(gb);
+    sps_rpl1_same_as_rpl0_flag = get_bits1(gb);
+    for (int i = 0; i < (sps_rpl1_same_as_rpl0_flag ? 1 : 2); i++) {
+        ue(sps->num_ref_pic_lists[i], VVC_MAX_REF_PIC_LISTS);
+        for (int j = 0; j < sps->num_ref_pic_lists[i]; j++) {
+            int ret = rpls_parse(&sps->ref_pic_list_struct[i][j], gb, i, j, sps, log_ctx);
+            if (ret < 0)
+                return ret;
+        }
+    }
+
+    if (sps_rpl1_same_as_rpl0_flag) {
+        sps->num_ref_pic_lists[1] = sps->num_ref_pic_lists[0];
+        for (int j = 0; j < sps->num_ref_pic_lists[0]; j++)
+            memcpy(&sps->ref_pic_list_struct[1][j],
+                   &sps->ref_pic_list_struct[0][j],
+                   sizeof(sps->ref_pic_list_struct[0][j]));
+    }
+
+    return 0;
+}
+
+static int sps_parse_max_num_gpm_merge_cand(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    sps->max_num_gpm_merge_cand = 0;
+    if (sps->max_num_merge_cand >= 2) {
+        sps->gpm_enabled_flag = get_bits1(gb);
+        if (sps->gpm_enabled_flag) {
+            sps->max_num_gpm_merge_cand = 2;
+            if (sps->max_num_merge_cand >= 3) {
+                uint8_t sps_max_num_merge_cand_minus_max_num_gpm_cand;
+                ue(sps_max_num_merge_cand_minus_max_num_gpm_cand, sps->max_num_merge_cand - 2);
+                sps->max_num_gpm_merge_cand = sps->max_num_merge_cand - sps_max_num_merge_cand_minus_max_num_gpm_cand;
+            }
+        }
+    }
+
+    return 0;
+}
+
+static int sps_parse_inter(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    int ret, sps_six_minus_max_num_merge_cand;
+
+    sps->weighted_pred_flag = get_bits1(gb);
+    sps->weighted_bipred_flag = get_bits1(gb);
+    sps->long_term_ref_pics_flag = get_bits1(gb);
+    if (sps->video_parameter_set_id > 0)
+        sps->inter_layer_prediction_enabled_flag = get_bits1(gb);
+
+    ret = sps_parse_rpls(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    sps->ref_wraparound_enabled_flag = get_bits1(gb);
+
+    sps->temporal_mvp_enabled_flag = get_bits1(gb);
+    if (sps->temporal_mvp_enabled_flag)
+        sps->sbtmvp_enabled_flag = get_bits1(gb);
+
+    sps->amvr_enabled_flag = get_bits1(gb);
+    sps->bdof_enabled_flag = get_bits1(gb);
+    if (sps->bdof_enabled_flag)
+        sps->bdof_control_present_in_ph_flag = get_bits1(gb);
+
+    sps->smvd_enabled_flag = get_bits1(gb);
+    sps->dmvr_enabled_flag = get_bits1(gb);
+    if (sps->dmvr_enabled_flag)
+        sps->dmvr_control_present_in_ph_flag = get_bits1(gb);
+
+    sps->mmvd_enabled_flag = get_bits1(gb);
+    if (sps->mmvd_enabled_flag)
+        sps->mmvd_fullpel_only_enabled_flag = get_bits1(gb);
+
+    ue(sps_six_minus_max_num_merge_cand, 5);
+    sps->max_num_merge_cand = 6 - sps_six_minus_max_num_merge_cand;
+
+    sps->sbt_enabled_flag = get_bits1(gb);
+
+    sps->affine_enabled_flag = get_bits1(gb);
+    if (sps->affine_enabled_flag) {
+        ue(sps->five_minus_max_num_subblock_merge_cand,
+           5 - sps->sbtmvp_enabled_flag);
+        sps->six_param_affine_enabled_flag = get_bits1(gb);
+        if (sps->amvr_enabled_flag)
+            sps->affine_amvr_enabled_flag = get_bits1(gb);
+        sps->affine_prof_enabled_flag = get_bits1(gb);
+        if (sps->affine_prof_enabled_flag)
+            sps->prof_control_present_in_ph_flag = get_bits1(gb);
+    }
+
+    sps->bcw_enabled_flag = get_bits1(gb);
+    sps->ciip_enabled_flag = get_bits1(gb);
+
+    ret = sps_parse_max_num_gpm_merge_cand(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    uep(sps->log2_parallel_merge_level, 2, 2, sps->ctb_log2_size_y);
+
+    return 0;
+}
+
+static int sps_parse_intra(VVCSPS *sps, const int sps_max_luma_transform_size_64_flag,
+    GetBitContext *gb, void *log_ctx)
+{
+    sps->isp_enabled_flag = get_bits1(gb);
+    sps->mrl_enabled_flag = get_bits1(gb);
+    sps->mip_enabled_flag = get_bits1(gb);
+
+    if (sps->chroma_format_idc != 0)
+        sps->cclm_enabled_flag = get_bits1(gb);
+    if (sps->chroma_format_idc == 1) {
+        sps->chroma_horizontal_collocated_flag = get_bits1(gb);
+        sps->chroma_vertical_collocated_flag = get_bits1(gb);
+    } else {
+        sps->chroma_horizontal_collocated_flag = 1;
+        sps->chroma_vertical_collocated_flag = 1;
+    }
+
+    sps->palette_enabled_flag = get_bits1(gb);
+    if (sps->chroma_format_idc == 3 &&
+        !sps_max_luma_transform_size_64_flag)
+        sps->act_enabled_flag = get_bits1(gb);
+    if (sps->transform_skip_enabled_flag || sps->palette_enabled_flag)
+        ue(sps->min_qp_prime_ts, 8);
+
+    sps->ibc_enabled_flag = get_bits1(gb);
+    if (sps->ibc_enabled_flag) {
+        uint8_t six_minus_max_num_ibc_merge_cand;
+        ue(six_minus_max_num_ibc_merge_cand, 5);
+        sps->max_num_ibc_merge_cand = 6 - six_minus_max_num_ibc_merge_cand;
+    }
+
+    return 0;
+}
+
+static int sps_parse_ladf(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    sps->ladf_enabled_flag = get_bits1(gb);
+    if (sps->ladf_enabled_flag) {
+        sps->num_ladf_intervals = get_bits(gb, 2) + 2;
+        se(sps->ladf_lowest_interval_qp_offset, -63, 63);
+
+        sps->ladf_interval_lower_bound[0] = 0;
+        for (int i = 0; i < sps->num_ladf_intervals - 1; i++) {
+            int sps_ladf_delta_threshold_minus1;
+
+            se(sps->ladf_qp_offset[i], -63, 63);
+
+            ue(sps_ladf_delta_threshold_minus1, (2 << sps->bit_depth) - 3);
+            sps->ladf_interval_lower_bound[i + 1] = sps->ladf_interval_lower_bound[i] + sps_ladf_delta_threshold_minus1 + 1;
+        }
+    }
+
+    return 0;
+}
+
+static void sps_parse_dequant(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    sps->explicit_scaling_list_enabled_flag = get_bits1(gb);
+    if (sps->lfnst_enabled_flag && sps->explicit_scaling_list_enabled_flag)
+        sps->scaling_matrix_for_lfnst_disabled_flag = get_bits1(gb);
+
+    if (sps->act_enabled_flag && sps->explicit_scaling_list_enabled_flag)
+        sps->scaling_matrix_for_alternative_colour_space_disabled_flag = get_bits1(gb);
+
+    if (sps->scaling_matrix_for_alternative_colour_space_disabled_flag)
+        sps->scaling_matrix_designated_colour_space_flag = get_bits1(gb);
+
+    sps->dep_quant_enabled_flag = get_bits1(gb);
+    sps->sign_data_hiding_enabled_flag = get_bits1(gb);
+}
+
+static int virtual_boundaries_parse(VirtualBoundaries *vbs, const int width, const int height,
+    GetBitContext *gb, void *log_ctx)
+{
+    vbs->virtual_boundaries_present_flag = get_bits1(gb);
+    if (vbs->virtual_boundaries_present_flag) {
+        ue(vbs->num_ver_virtual_boundaries, width <= 8 ? 0 : 3);
+        for (int i = 0; i < vbs->num_ver_virtual_boundaries; i++)
+            ue(vbs->virtual_boundary_pos_x_minus1[i], (width + 7) / 8 - 2);
+        ue(vbs->num_hor_virtual_boundaries, height <= 8 ? 0 : 3);
+        for (int i = 0; i < vbs->num_hor_virtual_boundaries; i++)
+            ue(vbs->virtual_boundary_pos_y_minus1[i], (height + 7) / 8 - 2);
+    }
+
+    return 0;
+}
+
+static int sps_parse_virtual_boundaries(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    sps->virtual_boundaries_enabled_flag = get_bits1(gb);
+    if (sps->virtual_boundaries_enabled_flag) {
+        const int ret = virtual_boundaries_parse(&sps->vbs, sps->width, sps->height, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
+// 7.3.5.1 General timing and HRD parameters syntax
+static int general_timing_hrd_parameters_parse(GeneralTimingHrdParameters *hrd, GetBitContext *gb, void *log_ctx)
+{
+    skip_bits_long(gb, 32);     ///< num_units_in_tick
+    skip_bits_long(gb, 32);     ///< time_scale
+    hrd->general_nal_hrd_params_present_flag = get_bits1(gb);
+    hrd->general_vcl_hrd_params_present_flag = get_bits1(gb);
+    if (hrd->general_nal_hrd_params_present_flag || hrd->general_vcl_hrd_params_present_flag) {
+        skip_bits1(gb);         ///< general_same_pic_timing_in_all_ols_flag
+        hrd->general_du_hrd_params_present_flag             = get_bits1(gb);
+        if (hrd->general_du_hrd_params_present_flag)
+            skip_bits(gb, 8);   ///< tick_divisor_minus2
+        skip_bits(gb, 4);       ///< bit_rate_scale
+        skip_bits(gb, 4);       ///< cpb_size_scale
+        if (hrd->general_du_hrd_params_present_flag)
+            skip_bits(gb, 4);   ///< cpb_size_du_scale
+        uep(hrd->hrd_cpb_cnt, 1, 1, 32);
+    }
+    return 0;
+}
+
+static int ols_timing_hrd_parameters_parse(const GeneralTimingHrdParameters *hrd,
+    const uint8_t first_sublayer, const uint8_t last_sublayer, GetBitContext *gb, void *log_ctx)
+{
+    const int hrd_params_present_flag = hrd->general_nal_hrd_params_present_flag ||
+            hrd->general_vcl_hrd_params_present_flag;
+    int elemental_duration_in_tc_minus1;
+    for (uint8_t i = first_sublayer; i < last_sublayer; i++) {
+        int fixed_pic_rate_within_cvs_flag = 0;
+        if (!get_bits1(gb))         ///< fixed_pic_rate_general_flag
+            fixed_pic_rate_within_cvs_flag = get_bits1(gb);
+        if (fixed_pic_rate_within_cvs_flag)
+            ue(elemental_duration_in_tc_minus1, 2047);
+        else if (hrd_params_present_flag && hrd->hrd_cpb_cnt == 1)
+            skip_bits1(gb);          ///< low_delay_hrd_flag
+
+        if (hrd_params_present_flag) {
+            int bit_rate_value_minus1, cpb_size_value_minus1;
+            int cpb_size_du_value_minus1, bit_rate_du_value_minus1;
+            for (int j = 0; j < hrd->hrd_cpb_cnt; j++) {
+                ue(bit_rate_value_minus1, UINT32_MAX - 1);
+                ue(cpb_size_value_minus1, UINT32_MAX - 1);
+                if (hrd->general_du_hrd_params_present_flag) {
+                    ue(cpb_size_du_value_minus1, UINT32_MAX - 1);
+                    ue(bit_rate_du_value_minus1, UINT32_MAX - 1);
+                }
+                skip_bits1(gb);     ///< cbr_flag
+            }
+        }
+    }
+    return 0;
+}
+
+static int sps_parse_hrd(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    int ret;
+    sps->timing_hrd_params_present_flag = get_bits1(gb);
+    if (sps->timing_hrd_params_present_flag) {
+        uint8_t first_sublayer = 0 ;
+        GeneralTimingHrdParameters *hrd = &sps->general_timing_hrd_parameters;
+        ret = general_timing_hrd_parameters_parse(hrd, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+        if (sps->max_sublayers > 1 && get_bits1(gb))        ///< sps_sublayer_cpb_params_present_flag
+            first_sublayer = sps->max_sublayers - 1;
+        ret = ols_timing_hrd_parameters_parse(hrd, first_sublayer, sps->max_sublayers, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    }
+    return 0;
+}
+
+static int sps_parse_vui(VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    VUI *vui = &sps->vui;
+
+    if (get_bits1(gb)) {         //vui_parameters_present_flag
+        uep(vui->payload_size, 1, 1, 1024);
+        align_get_bits(gb);
+        //fixme
+    } else {
+        vui->colour_primaries = 2;
+        vui->transfer_characteristics = 2;
+        vui->matrix_coeffs =  2;
+        vui->chroma_sample_loc_type_frame = 6;
+        vui->chroma_sample_loc_type_top_field = 6;
+        vui->chroma_sample_loc_type_bottom_field = 6;
+    }
+
+    return 0;
+}
+
+static int sps_parse(VVCSPS *sps, GetBitContext *gb, unsigned int *sps_id,
+    int apply_defdispwin, AVBufferRef **vps_list, AVCodecContext *avctx, int nuh_layer_id)
+{
+    int sps_ptl_dpb_hrd_params_present_flag;
+    int ret = 0, sps_max_luma_transform_size_64_flag = 0;
+    const VVCVPS *vps;
+    void *log_ctx = avctx;
+
+    // Coded parameters
+
+    *sps_id = get_bits(gb, 4);
+
+    sps->video_parameter_set_id = get_bits(gb, 4);
+    if (vps_list && !vps_list[sps->video_parameter_set_id]) {
+        if (!sps->video_parameter_set_id) {
+            VVCVPS *vps0;
+            AVBufferRef* buf = av_buffer_allocz(sizeof(*vps));
+            if (!buf)
+                return AVERROR(ENOMEM);
+            vps_list[0] = buf;
+            vps0 = (VVCVPS *)buf->data;
+            vps0->max_layers = 1;
+            vps0->independent_layer_flag[0] = 1;
+            vps0->layer_id[0] = nuh_layer_id;
+        } else {
+            av_log(log_ctx, AV_LOG_ERROR, "VPS %d does not exist\n",
+                sps->video_parameter_set_id);
+            return AVERROR_INVALIDDATA;
+        }
+    }
+    vps = (VVCVPS*)vps_list[sps->video_parameter_set_id]->data;
+
+    sps->max_sublayers = get_bits(gb, 3) + 1;
+    if (sps->max_sublayers > VVC_MAX_SUBLAYERS) {
+        av_log(log_ctx, AV_LOG_ERROR, "sps_max_sublayers out of range: %d\n",
+               sps->max_sublayers);
+        return AVERROR_INVALIDDATA;
+    }
+
+    sps->chroma_format_idc = get_bits(gb, 2);
+
+    sps->ctb_log2_size_y  = get_bits(gb, 2) + 5;
+    if (sps->ctb_log2_size_y == 8) {
+        av_log(log_ctx, AV_LOG_ERROR, "sps_log2_ctu_size_minus5 can't be 3\n");
+        return AVERROR_INVALIDDATA;
+    }
+    sps->ctb_size_y = 1 << sps->ctb_log2_size_y;
+
+    sps_ptl_dpb_hrd_params_present_flag = get_bits1(gb);
+    if (sps_ptl_dpb_hrd_params_present_flag) {
+        ret = ptl_parse(&sps->ptl, 1, sps->max_sublayers - 1, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    }
+    skip_bits1(gb);           ///< sps_gdr_enabled_flag
+
+    ret = sps_parse_pic_resampling(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    ue(sps->width, VVC_MAX_WIDTH);
+    ue(sps->height, VVC_MAX_HEIGHT);
+    if (!sps->width || !sps->height)
+        return AVERROR_INVALIDDATA;
+
+    sps_parse_conf_win(sps, gb, avctx);
+
+    ret = sps_parse_subpic(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    ret = sps_parse_bit_depth(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    sps->entropy_coding_sync_enabled_flag = get_bits1(gb);
+    sps->entry_point_offsets_present_flag = get_bits1(gb);
+
+    ret = sps_parse_poc(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    ret = sps_parse_extra_bytes(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    if (sps_ptl_dpb_hrd_params_present_flag) {
+        const int sps_sublayer_dpb_params_flag = sps->max_sublayers > 1 ? get_bits1(gb) : 0;
+        dpb_parameters_parse(&sps->dpb, sps->max_sublayers - 1, sps_sublayer_dpb_params_flag, gb);
+    }
+
+    uep(sps->min_cb_log2_size_y, 2, 2, FFMIN(6, sps->ctb_log2_size_y));
+    sps->min_cb_size_y = 1 << sps->min_cb_log2_size_y;
+
+    ret = sps_parse_partition_constraints(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    if (sps->ctb_size_y > 32)
+        sps_max_luma_transform_size_64_flag = get_bits1(gb);
+    sps->max_tb_size_y = 1 << (sps_max_luma_transform_size_64_flag ? 6 : 5);
+
+    ret = sps_parse_transform(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    sps_parse_filter(sps, gb);
+
+    ret = sps_parse_inter(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    ret = sps_parse_intra(sps, sps_max_luma_transform_size_64_flag, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    ret = sps_parse_ladf(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    sps_parse_dequant(sps, gb, log_ctx);
+
+    ret = sps_parse_virtual_boundaries(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    if (sps_ptl_dpb_hrd_params_present_flag) {
+        ret = sps_parse_hrd(sps, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    }
+
+    sps->field_seq_flag = get_bits1(gb);
+
+    ret = sps_parse_vui(sps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    skip_bits1(gb);                 ///< sps_extension_flag
+
+    if (get_bits_left(gb) < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Overread SPS by %d bits\n", -get_bits_left(gb));
+        return AVERROR_INVALIDDATA;
+    }
+
+    return 0;
+}
+
+int ff_vvc_decode_sps(VVCParamSets *ps, GetBitContext *gb,
+    int apply_defdispwin, int nuh_layer_id, AVCodecContext *avctx)
+{
+    VVCSPS *sps;
+    AVBufferRef *sps_buf = av_buffer_allocz(sizeof(*sps));
+    unsigned int sps_id;
+    int ret;
+    ptrdiff_t nal_size;
+
+    if (!sps_buf)
+        return AVERROR(ENOMEM);
+    sps = (VVCSPS*)sps_buf->data;
+
+    av_log(avctx, AV_LOG_DEBUG, "Decoding SPS\n");
+
+    nal_size = gb->buffer_end - gb->buffer;
+    if (nal_size > sizeof(sps->data)) {
+        av_log(avctx, AV_LOG_WARNING, "Truncating likely oversized SPS "
+               "(%"PTRDIFF_SPECIFIER" > %"SIZE_SPECIFIER")\n",
+               nal_size, sizeof(sps->data));
+        sps->data_size = sizeof(sps->data);
+    } else {
+        sps->data_size = nal_size;
+    }
+    memcpy(sps->data, gb->buffer, sps->data_size);
+
+    ret = sps_parse(sps, gb, &sps_id, apply_defdispwin, ps->vps_list, avctx, nuh_layer_id);
+    if (ret < 0) {
+        av_buffer_unref(&sps_buf);
+        return ret;
+    }
+
+    if (avctx->debug & FF_DEBUG_BITSTREAM) {
+        av_log(avctx, AV_LOG_DEBUG,
+               "Parsed SPS: id %d; coded wxh: %dx%d; "
+               "cropped wxh: %dx%d; pix_fmt: %s.\n",
+               sps_id, sps->width, sps->height,
+               sps->width - (sps->output_window.left_offset + sps->output_window.right_offset),
+               sps->height - (sps->output_window.top_offset + sps->output_window.bottom_offset),
+               av_get_pix_fmt_name(sps->pix_fmt));
+    }
+
+    /* check if this is a repeat of an already parsed SPS, then keep the
+     * original one.
+     * otherwise drop all PPSes that depend on it */
+    if (ps->sps_list[sps_id] &&
+        !memcmp(ps->sps_list[sps_id]->data, sps_buf->data, sps_buf->size)) {
+        av_buffer_unref(&sps_buf);
+    } else {
+        remove_sps(ps, sps_id);
+        ps->sps_list[sps_id] = sps_buf;
+    }
+    return 0;
+}
+
+static void vvc_pps_free(void *opaque, uint8_t *data)
+{
+    VVCPPS *pps = (VVCPPS *)data;
+
+    av_freep(&pps->column_width);
+    av_freep(&pps->row_height);
+    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);
+    av_freep(&pps);
+}
+
+static int pps_parse_width_height(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    const int divisor = FFMAX(sps->min_cb_size_y, 8);
+
+    ue(pps->width,  sps->width);
+    ue(pps->height, sps->height);
+
+    if (!pps->width || !pps->height)
+        return AVERROR_INVALIDDATA;
+
+    if (pps->width % divisor || pps->height % divisor) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Invalid dimensions: %ux%u not divisible "
+               "by %u, MinCbSizeY = %u.\n", pps->width,
+               pps->height, divisor, sps->min_cb_size_y);
+        return AVERROR_INVALIDDATA;
+    }
+    if (!sps->res_change_in_clvs_allowed_flag &&
+        (pps->width != sps->width || pps->height != sps->height)) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Resoltuion change is not allowed, "
+               "in max resolution (%ux%u) mismatched with pps(%ux%u).\n",
+               sps->width, sps->height,
+               pps->width, pps->height);
+        return AVERROR_INVALIDDATA;
+    }
+    return 0;
+}
+
+static unsigned int* pps_parse_tile_sizes(uint16_t *num, unsigned int num_exp,
+    unsigned int max_num, unsigned int max_size, GetBitContext *gb, void *log_ctx)
+{
+    unsigned int i, exp_tile_size = 0;
+    unsigned int unified_size, remaining_size;
+    unsigned int *sizes, *p;
+    sizes = av_malloc_array(num_exp, sizeof(sizes));
+    if (!sizes)
+        return NULL;
+    for (i = 0; i < num_exp; i++) {
+        sizes[i] = get_ue_golomb_long(gb) + 1;
+        if (exp_tile_size + sizes[i] > max_size) {
+            goto err;
+        }
+        exp_tile_size += sizes[i];
+    }
+    remaining_size = max_size - exp_tile_size;
+    unified_size = (i == 0 ? max_size : sizes[i-1]);
+    *num = i + vvc_ceil(remaining_size, unified_size);
+    if (*num > max_num) {
+        av_log(log_ctx, AV_LOG_ERROR, "num(%d) large than %d.\n", *num, max_num);
+        return NULL;
+    }
+    p = av_realloc_array(sizes, *num, sizeof(*sizes));
+    if (!p)
+        goto err;
+    sizes = p;
+    while (remaining_size > unified_size) {
+        sizes[i] = unified_size;
+        remaining_size -= unified_size;
+        i++;
+    }
+    if (remaining_size > 0) {
+        sizes[i] = remaining_size;
+    }
+    return sizes;
+err:
+    av_free(sizes);
+    return NULL;
+}
+
+static inline unsigned int* pps_setup_bd(unsigned int *sizes, int num_sizes)
+{
+    unsigned int *bd = av_malloc_array(num_sizes + 1, sizeof(*bd));
+    if (!bd)
+        return NULL;
+    *bd= 0;
+    for (int i = 0; i < num_sizes; i++) {
+        bd[i+1] = bd[i] + sizes[i];
+    }
+    return bd;
+}
+
+static int pps_setup(VVCPPS *pps, const VVCSPS *sps)
+{
+    int ret = AVERROR(ENOMEM), tile_x = 0, tile_y = 0;
+
+    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->ctb_to_col_bd || !pps->ctb_to_row_bd)
+        goto err;
+
+    for (int ctb_addr_x = 0; ctb_addr_x < pps->ctb_width; ctb_addr_x++) {
+        if (ctb_addr_x == pps->col_bd[tile_x + 1])
+            tile_x++;
+        pps->ctb_to_col_bd[ctb_addr_x] = pps->col_bd[tile_x];
+    }
+    pps->ctb_to_col_bd[pps->ctb_width] = pps->ctb_width;
+
+    for (int ctb_addr_y = 0; ctb_addr_y < pps->ctb_height; ctb_addr_y++) {
+        if (ctb_addr_y == pps->row_bd[tile_y + 1])
+            tile_y++;
+        pps->ctb_to_row_bd[ctb_addr_y] = pps->row_bd[tile_y];
+    }
+    pps->ctb_to_row_bd[pps->ctb_height] = pps->ctb_height;
+
+     return 0;
+err:
+    return ret;
+}
+
+static int pps_parse_subpic_id(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    int pps_subpic_id_mapping_present_flag;
+    uint8_t subpic_id_len;
+
+    pps->no_pic_partition_flag = get_bits1(gb);
+    if (sps->num_subpics > 1 || pps->mixed_nalu_types_in_pic_flag)  {
+        if (pps->no_pic_partition_flag) {
+            av_log(log_ctx, AV_LOG_ERROR,
+                   "no_pic_partition_flag should not true");
+            return AVERROR_INVALIDDATA;
+        }
+    }
+
+    pps_subpic_id_mapping_present_flag = get_bits1(gb);
+    if (pps_subpic_id_mapping_present_flag) {
+        if (!pps->no_pic_partition_flag) {
+            const uint16_t num_subpics = get_ue_golomb_long(gb) + 1;
+            if (num_subpics != sps->num_subpics) {
+                av_log(log_ctx, AV_LOG_ERROR,
+                    "pps_num_subpics %u should equals to "
+                    "sps_num_subpics %u .\n", num_subpics, sps->num_subpics);
+                return AVERROR_INVALIDDATA;
+            }
+        }
+
+        subpic_id_len = get_ue_golomb_long(gb) + 1;
+        if (subpic_id_len != sps->subpic_id_len) {
+            av_log(log_ctx, AV_LOG_ERROR,
+                "pps_subpic_id_len %u should equals to "
+                "sps_subpic_id_len %u .\n", subpic_id_len, sps->subpic_id_len);
+            return AVERROR_INVALIDDATA;
+        }
+
+    }
+    for (int i = 0; i < sps->num_subpics; i++) {
+        if (sps->subpic_id_mapping_explicitly_signalled_flag) {
+            if (pps_subpic_id_mapping_present_flag)
+                pps->subpic_id[i] = get_bits(gb, sps->subpic_id_len);
+            else
+                pps->subpic_id[i] = sps->subpic_id[i];
+        } else {
+            pps->subpic_id[i] = i;
+        }
+    }
+    return 0;
+}
+
+static int pps_parse_chroma_tool_offsets(VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    uint8_t pps_joint_cbcr_qp_offset_present_flag;
+
+    pps->chroma_tool_offsets_present_flag = get_bits1(gb);
+    if (!pps->chroma_tool_offsets_present_flag)
+        return 0;
+
+    se(pps->chroma_qp_offset[CB - 1], -12, 12);
+    se(pps->chroma_qp_offset[CR - 1], -12, 12);
+
+    pps_joint_cbcr_qp_offset_present_flag = get_bits1(gb);
+    if (pps_joint_cbcr_qp_offset_present_flag)
+        se(pps->chroma_qp_offset[JCBCR - 1], -12, 12);
+    pps->slice_chroma_qp_offsets_present_flag = get_bits1(gb);
+    pps->cu_chroma_qp_offset_list_enabled_flag = get_bits1(gb);
+    if (pps->cu_chroma_qp_offset_list_enabled_flag) {
+        ue(pps->chroma_qp_offset_list_len_minus1, 5);
+        for (int i = 0; i <= pps->chroma_qp_offset_list_len_minus1; i++) {
+            for (int j = CB - 1; j < CR + pps_joint_cbcr_qp_offset_present_flag; j++)
+                se(pps->chroma_qp_offset_list[i][j], -12, 12);
+        }
+    }
+    return 0;
+}
+
+static int deblock_parse(DBParams *deblock, const VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    int beta_offset_div2, tc_offset_div2;
+    for (int c_idx = 0; c_idx < VVC_MAX_SAMPLE_ARRAYS; c_idx++) {
+        if (!c_idx || pps->chroma_tool_offsets_present_flag) {
+            se(beta_offset_div2, -12, 12);
+            se(tc_offset_div2, -12, 12);
+        }
+        deblock->beta_offset[c_idx] = beta_offset_div2 << 1;
+        deblock->tc_offset[c_idx] = tc_offset_div2 << 1;
+    }
+    return 0;
+}
+static int pps_parse_deblocking_control(VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    if (get_bits1(gb)) { //pps_deblocking_filter_control_present_flag
+        pps->deblocking_filter_override_enabled_flag = get_bits1(gb);
+        pps->deblocking_filter_disabled_flag = get_bits1(gb);
+        if (!pps->no_pic_partition_flag &&
+            pps->deblocking_filter_override_enabled_flag)
+            pps->dbf_info_in_ph_flag = get_bits1(gb);
+        if (!pps->deblocking_filter_disabled_flag)
+            return deblock_parse(&pps->deblock, pps, gb, log_ctx);
+    }
+    return 0;
+}
+
+static int pps_parse_num_tiles_in_pic(VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    unsigned int num_exp_tile_columns, num_exp_tile_rows;
+
+    uep(num_exp_tile_columns, 1, 1,
+        FFMIN(pps->ctb_width, VVC_MAX_TILE_COLUMNS));
+    uep(num_exp_tile_rows, 1, 1,
+       FFMIN(pps->ctb_height, VVC_MAX_TILE_ROWS));
+
+    pps->column_width = pps_parse_tile_sizes(&pps->num_tile_columns,
+        num_exp_tile_columns, VVC_MAX_TILE_COLUMNS, pps->ctb_width,
+        gb, log_ctx);
+    if (!pps->column_width)
+        return AVERROR(ENOMEM);
+
+    pps->row_height = pps_parse_tile_sizes(&pps->num_tile_rows,
+        num_exp_tile_rows, VVC_MAX_TILE_ROWS, pps->ctb_height,
+        gb, log_ctx);
+    if (!pps->row_height)
+        return AVERROR(ENOMEM);
+
+    pps->num_tiles_in_pic =
+        pps->num_tile_columns * pps->num_tile_rows;
+    if (pps->num_tiles_in_pic > VVC_MAX_TILES_PER_AU) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "NumTilesInPic(%d) large than %d.\n",
+               pps->num_tiles_in_pic, VVC_MAX_TILES_PER_AU);
+        return AVERROR_INVALIDDATA;
+    }
+
+    pps->col_bd = pps_setup_bd(pps->column_width, pps->num_tile_columns);
+    pps->row_bd = pps_setup_bd(pps->row_height, pps->num_tile_rows);
+    if (!pps->col_bd || !pps->row_bd)
+        return AVERROR(ENOMEM);
+
+    return 0;
+}
+
+static int pps_parse_slice_width_height_in_tiles(VVCPPS *pps, const int i,
+    const int tile_x, const int tile_y, const int pps_tile_idx_delta_present_flag,
+    GetBitContext *gb, void *log_ctx,
+    int *slice_width_in_tiles, int *slice_height_in_tiles)
+{
+    *slice_width_in_tiles = 1;
+    if (tile_x != pps->num_tile_columns - 1) {
+        uep(*slice_width_in_tiles, 1, 1, pps->num_tile_columns);
+    }
+    if (tile_y != pps->num_tile_rows - 1 &&
+        (pps_tile_idx_delta_present_flag || tile_x == 0)) {
+        uep(*slice_height_in_tiles, 1, 1, pps->num_tile_rows);
+    } else {
+        if (tile_y == pps->num_tile_rows - 1)
+            *slice_height_in_tiles = 1;
+    }
+    return 0;
+}
+
+static void pps_derive_tile_xy(const VVCPPS *pps, const int tile_idx,
+    int *tile_x, int *tile_y)
+{
+    *tile_x = tile_idx % pps->num_tile_columns;
+    *tile_y = tile_idx / pps->num_tile_columns;
+}
+
+static int pps_parse_tile_idx(const VVCPPS *pps, const int slice_idx, int tile_idx,
+    const int slice_width_in_tiles, const int slice_height_in_tiles, const int pps_tile_idx_delta_present_flag,
+    GetBitContext *gb)
+{
+    if (pps_tile_idx_delta_present_flag) {
+        tile_idx += get_se_golomb(gb);              //< pps_tile_idx_delta_val
+    } else {
+        tile_idx += slice_width_in_tiles;
+        if (tile_idx % pps->num_tile_columns == 0)
+            tile_idx += (slice_height_in_tiles - 1) * pps->num_tile_columns;
+    }
+    return tile_idx;
+}
+
+typedef struct SliceMap {
+    uint16_t start_offset;
+    const VVCSPS   *sps;
+    VVCPPS   *pps;
+    uint16_t top_left_ctu_x[VVC_MAX_SLICES];
+    uint16_t top_left_ctu_y[VVC_MAX_SLICES];
+} SliceMap;
+
+static void slice_map_init(SliceMap *slice_map, const VVCSPS *sps, VVCPPS *pps)
+{
+    slice_map->start_offset = 0;
+    slice_map->pps = pps;
+    slice_map->sps = sps;
+}
+
+static void slice_map_add_new_slice(SliceMap *slice_map, const int slice_idx, const int ctu_x, const int ctu_y, const int height, const int ctb_count)
+{
+    VVCPPS *pps = slice_map->pps;
+    pps->slice_start_offset[slice_idx] = slice_map->start_offset;
+
+    slice_map->top_left_ctu_x[slice_idx] = ctu_x;
+    slice_map->top_left_ctu_y[slice_idx] = ctu_y;
+    pps->num_ctus_in_slice[slice_idx]    = ctb_count;
+    slice_map->start_offset             += ctb_count;
+}
+
+static int slice_map_add_ctus(SliceMap *slice_map, const int slice_idx, const int ctu_x, const int ctu_y, const int width, const int height)
+{
+    VVCPPS *pps = slice_map->pps;
+    const int x_end = ctu_x + width;
+    const int y_end = ctu_y + height;
+    int ctb_count = 0;
+
+    for (int y = ctu_y; y < y_end; y++) {
+        for (int x = ctu_x; x < x_end; x++) {
+            const uint16_t ctb_addr_in_rs = y * pps->ctb_width + x;         //< CtbAddrInRs
+            const int offset = slice_map->start_offset + ctb_count;
+            pps->ctb_addr_in_slice[offset] = ctb_addr_in_rs;
+            ctb_count++;
+        }
+    }
+    slice_map_add_new_slice(slice_map, slice_idx, ctu_x, ctu_y, height, ctb_count);
+    return slice_map->sps->entropy_coding_sync_enabled_flag ? height - 1 : 0;
+}
+
+static void slice_map_add_tiles(SliceMap *slice_map, const int slice_idx,
+    const int tile_x_start, const int tile_y_start, const int tile_width, const int tile_height)
+{
+    VVCPPS *pps = slice_map->pps;
+    const int tile_x_end = tile_x_start + tile_width;
+    const int tile_y_end = tile_y_start + tile_height;
+    int height = 0, ctb_count = 0;
+
+    if (!tile_width || !tile_height)
+        return ;
+
+    for (int tile_y = tile_y_start; tile_y < tile_y_end; tile_y++) {
+        for (int tile_x = tile_x_start; tile_x < tile_x_end; tile_x++) {
+
+            const int ctu_x = pps->col_bd[tile_x];
+            const int ctu_y = pps->row_bd[tile_y];
+            const int x_end = ctu_x + pps->column_width[tile_x];
+            const int y_end = ctu_y + pps->row_height[tile_y];
+
+            for (int y = ctu_y; y < y_end; y++) {
+                for (int x = ctu_x; x < x_end; x++) {
+                    const uint16_t ctb_addr_in_rs = y * pps->ctb_width + x;         //< CtbAddrInRs
+                    const int offset = slice_map->start_offset + ctb_count;
+                    pps->ctb_addr_in_slice[offset] = ctb_addr_in_rs;
+                    ctb_count++;
+                }
+            }
+        }
+        height += pps->row_height[tile_y];
+    }
+
+    slice_map_add_new_slice(slice_map, slice_idx, pps->col_bd[tile_x_start], pps->row_bd[tile_y_start], height, ctb_count);
+}
+
+static int pps_parse_slices_in_one_tile(VVCPPS *pps, SliceMap *slice_map,
+    const int tile_x, const int tile_y, GetBitContext *gb, void *log_ctx, int *slice_idx)
+{
+    int num_slices_in_tile, pps_num_exp_slices_in_tile, uniform_slice_height, remaining_height_in_ctbs_y;
+    int ctu_x = pps->col_bd[tile_x];
+    int ctu_y = pps->row_bd[tile_y];
+    int i = *slice_idx;
+
+    remaining_height_in_ctbs_y = pps->row_height[tile_y];
+    ue(pps_num_exp_slices_in_tile, pps->row_height[tile_y] - 1);
+    if (pps_num_exp_slices_in_tile == 0) {
+        num_slices_in_tile = 1;
+        slice_map_add_ctus(slice_map, i, ctu_x, ctu_y, pps->column_width[tile_x], pps->row_height[tile_y]);
+    } else {
+        int j, slice_height_in_ctus;
+        for (j = 0; j < pps_num_exp_slices_in_tile; j++) {
+            uep(slice_height_in_ctus, 1, 1, pps->row_height[tile_y]);
+
+            slice_map_add_ctus(slice_map, i + j, ctu_x, ctu_y, pps->column_width[tile_x], slice_height_in_ctus);
+
+            ctu_y += slice_height_in_ctus;
+            remaining_height_in_ctbs_y -= slice_height_in_ctus;
+        }
+        uniform_slice_height = (j == 0 ? pps->row_height[tile_y] : slice_height_in_ctus);
+
+        while (remaining_height_in_ctbs_y > 0) {
+            slice_map_add_ctus(slice_map, i + j, ctu_x, ctu_y,
+                pps->column_width[tile_x], FFMIN(uniform_slice_height, remaining_height_in_ctbs_y));
+
+            ctu_y += uniform_slice_height;
+            remaining_height_in_ctbs_y -= uniform_slice_height;
+            j++;
+        }
+        num_slices_in_tile = j;
+    }
+    *slice_idx += num_slices_in_tile - 1;
+    return 0;
+}
+
+static int sps_pos_in_subpic(const VVCSPS *sps, const int subpic_idx, const int pos_x, const int pos_y)
+{
+    return (pos_x >= sps->subpic_ctu_top_left_x[subpic_idx]) &&
+        (pos_y >= sps->subpic_ctu_top_left_y[subpic_idx]) &&
+        (pos_x < sps->subpic_ctu_top_left_x[subpic_idx] + sps->subpic_width[subpic_idx]) &&
+        (pos_y < sps->subpic_ctu_top_left_y[subpic_idx] + sps->subpic_height[subpic_idx]);
+
+}
+
+static int pps_parse_rect_slice(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    int i;
+    SliceMap slice_map;
+    uint16_t num_slices_in_pic;             ///< pps_num_slices_in_pic_minus1 + 1;
+
+    slice_map_init(&slice_map, sps, pps);
+    if (!pps->single_slice_per_subpic_flag) {
+        int tile_idx = 0, pps_tile_idx_delta_present_flag = 0, tile_x, tile_y, ret, j;
+        uep(num_slices_in_pic, 1, 1, VVC_MAX_SLICES);
+
+        if (num_slices_in_pic > 2)
+            pps_tile_idx_delta_present_flag = get_bits1(gb);
+
+        for (i = 0; i < num_slices_in_pic; i++) {
+            pps_derive_tile_xy(pps, tile_idx, &tile_x, &tile_y);
+            if (i < num_slices_in_pic - 1) {
+                int slice_width_in_tiles, slice_height_in_tiles;
+                ret = pps_parse_slice_width_height_in_tiles(pps, i, tile_x, tile_y, pps_tile_idx_delta_present_flag,
+                    gb, log_ctx, &slice_width_in_tiles, &slice_height_in_tiles);
+                if (ret < 0)
+                    return ret;
+
+                if (slice_width_in_tiles == 1 && slice_height_in_tiles == 1 && pps->row_height[tile_y] > 1) {
+                    ret = pps_parse_slices_in_one_tile(pps, &slice_map, tile_x, tile_y, gb, log_ctx, &i);
+                    if (ret < 0)
+                        return ret;
+                } else {
+                    slice_map_add_tiles(&slice_map, i, tile_x, tile_y, slice_width_in_tiles, slice_height_in_tiles);
+                }
+                if (i < num_slices_in_pic - 1) {
+                    tile_idx = pps_parse_tile_idx(pps, i, tile_idx, slice_width_in_tiles, slice_height_in_tiles, pps_tile_idx_delta_present_flag, gb);
+                    if (tile_idx < 0 || tile_idx >= pps->num_tiles_in_pic)
+                        return AVERROR_INVALIDDATA;
+                }
+            }
+            else {
+                slice_map_add_tiles(&slice_map, num_slices_in_pic - 1,
+                    tile_x, tile_y, pps->num_tile_columns - tile_x, pps->num_tile_rows - tile_y);
+            }
+        }
+
+        //now, we got all slice information, let's resolve NumSlicesInSubpic
+        for (i = 0; i < sps->num_subpics; i++) {
+            pps->num_slices_in_subpic[i] = 0;
+            for (j = 0; j < num_slices_in_pic; j++) {
+                if (sps_pos_in_subpic(sps, i, slice_map.top_left_ctu_x[j], slice_map.top_left_ctu_y[j])) {
+                    pps->num_slices_in_subpic[i]++;
+                }
+            }
+        }
+    } else {
+        num_slices_in_pic = sps->num_subpics;
+        for (i = 0; i < num_slices_in_pic; i++) {
+            int start_x = -1, start_y = -1, width_in_tiles, height_in_tiles;
+            pps->num_slices_in_subpic[i] = 1;
+            for (int tile_y = 0; tile_y < pps->num_tile_rows; tile_y++) {
+                for (int tile_x = 0; tile_x < pps->num_tile_columns; tile_x++) {
+                    if (sps_pos_in_subpic(sps, i, pps->col_bd[tile_x], pps->row_bd[tile_y])) {
+                        if (start_x == -1) {
+                            start_x = tile_x;
+                            start_y = tile_y;
+                        }
+                        width_in_tiles = tile_x - start_x + 1;
+                        height_in_tiles = tile_y - start_y + 1;
+                    }
+                }
+            }
+            if (start_x != -1)
+                slice_map_add_tiles(&slice_map, i, start_x, start_y, width_in_tiles, height_in_tiles);
+        }
+    }
+    if (pps->single_slice_per_subpic_flag || num_slices_in_pic > 1)
+        pps->loop_filter_across_slices_enabled_flag = get_bits1(gb);
+    return 0;
+}
+
+static int pps_parse_slice(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    int  ret;
+
+    pps->loop_filter_across_slices_enabled_flag = 0;
+    if (pps->rect_slice_flag) {
+        ret = pps_parse_rect_slice(pps, sps, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    } else {
+        int ctu_idx = 0;
+        for (int tile_y = 0; tile_y < pps->num_tile_rows; tile_y++) {
+            for (int tile_x = 0; tile_x < pps->num_tile_columns; tile_x++) {
+                for (int ctu_y = pps->row_bd[tile_y]; ctu_y < pps->row_bd[tile_y] + pps->row_height[tile_y]; ctu_y++) {
+                    for (int ctu_x = pps->col_bd[tile_x]; ctu_x < pps->col_bd[tile_x] + pps->column_width[tile_x]; ctu_x++) {
+                        const int ctu_addr_rs = ctu_y * pps->ctb_width + ctu_x;
+                        pps->ctb_addr_in_slice[ctu_idx++] = ctu_addr_rs;
+
+                    }
+                }
+            }
+        }
+        pps->loop_filter_across_slices_enabled_flag = get_bits1(gb);
+    }
+
+    return 0;
+}
+
+static int pps_no_pic_partition(VVCPPS *pps, const VVCSPS *sps)
+{
+    SliceMap slice_map;
+
+    pps->num_tile_columns = 1;
+    pps->num_tile_rows    = 1;
+    pps->num_tiles_in_pic = 1;
+
+    pps->column_width = av_malloc_array(pps->num_tile_columns, sizeof(*pps->column_width));
+    pps->row_height = av_malloc_array(pps->num_tile_rows, sizeof(*pps->row_height));
+    if (!pps->column_width || !pps->row_height)
+        return AVERROR(ENOMEM);
+    pps->column_width[0] = pps->ctb_width;
+    pps->row_height[0] = pps->ctb_height;
+
+    pps->col_bd = pps_setup_bd(pps->column_width, pps->num_tile_columns);
+    pps->row_bd = pps_setup_bd(pps->row_height, pps->num_tile_rows);
+    if (!pps->col_bd || !pps->row_bd)
+        return AVERROR(ENOMEM);
+
+    slice_map_init(&slice_map, sps, pps);
+    slice_map_add_ctus(&slice_map, 0, 0, 0, pps->ctb_width, pps->ctb_height);
+
+    return 0;
+}
+
+static int pps_parse_pic_partition(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    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->no_pic_partition_flag) {
+        int ret;
+        uint8_t pps_log2_ctu_size_minus5;
+        u(2, pps_log2_ctu_size_minus5, sps->ctb_log2_size_y - 5, sps->ctb_log2_size_y - 5);
+
+        ret = pps_parse_num_tiles_in_pic(pps, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+
+        pps->rect_slice_flag = 1;
+        if (pps->num_tiles_in_pic > 1) {
+            pps->loop_filter_across_tiles_enabled_flag = get_bits1(gb);
+            pps->rect_slice_flag = get_bits1(gb);
+        }
+
+        pps->single_slice_per_subpic_flag = pps->rect_slice_flag ? get_bits1(gb) : 0;
+
+        return pps_parse_slice(pps, sps, gb, log_ctx);
+    }
+
+    return pps_no_pic_partition(pps, sps);
+}
+
+static int pps_parse_inter(VVCPPS *pps, const VVCSPS *sps, GetBitContext *gb, void *log_ctx)
+{
+    for (int i = 0; i < 2; i++)
+        uep(pps->num_ref_idx_default_active[i], 1, 1, 15);
+
+    pps->rpl1_idx_present_flag       = get_bits1(gb);
+    pps->weighted_pred_flag          = get_bits1(gb);
+    pps->weighted_bipred_flag        = get_bits1(gb);
+    pps->ref_wraparound_enabled_flag = get_bits1(gb);
+
+    if (pps->ref_wraparound_enabled_flag)
+        pps->ref_wraparound_offset = (pps->width / sps->min_cb_size_y) - get_ue_golomb_long(gb);
+    return 0;
+}
+
+static void pps_parse_ph_flags(VVCPPS *pps, GetBitContext *gb)
+{
+    if (!pps->no_pic_partition_flag) {
+        pps->rpl_info_in_ph_flag = get_bits1(gb);
+        pps->sao_info_in_ph_flag = get_bits1(gb);
+        pps->alf_info_in_ph_flag = get_bits1(gb);
+        if ((pps->weighted_pred_flag || pps->weighted_bipred_flag) &&
+            pps->rpl_info_in_ph_flag)
+            pps->wp_info_in_ph_flag   = get_bits1(gb);
+        pps->qp_delta_info_in_ph_flag = get_bits1(gb);
+    }
+}
+
+int ff_vvc_decode_pps(VVCParamSets *ps, GetBitContext *gb, void *log_ctx)
+{
+    const VVCSPS      *sps = NULL;
+    int ret = 0;
+    ptrdiff_t nal_size;
+    AVBufferRef *pps_buf;
+
+    VVCPPS *pps = av_mallocz(sizeof(*pps));
+    if (!pps)
+        return AVERROR(ENOMEM);
+
+    pps_buf = av_buffer_create((uint8_t *)pps, sizeof(*pps), vvc_pps_free, NULL, 0);
+    if (!pps_buf) {
+        av_freep(&pps);
+        return AVERROR(ENOMEM);
+    }
+
+    av_log(log_ctx, AV_LOG_DEBUG, "Decoding PPS\n");
+
+    nal_size = gb->buffer_end - gb->buffer;
+    if (nal_size > sizeof(pps->data)) {
+        av_log(log_ctx, AV_LOG_WARNING, "Truncating likely oversized PPS "
+               "(%"PTRDIFF_SPECIFIER" > %"SIZE_SPECIFIER")\n",
+               nal_size, sizeof(pps->data));
+        pps->data_size = sizeof(pps->data);
+    } else {
+        pps->data_size = nal_size;
+    }
+    memcpy(pps->data, gb->buffer, pps->data_size);
+
+    pps->pic_parameter_set_id = get_bits(gb, 6);
+    pps->seq_parameter_set_id = get_bits(gb, 4);
+    if (!ps->sps_list[pps->seq_parameter_set_id]) {
+        av_log(log_ctx, AV_LOG_ERROR, "SPS %u does not exist.\n", pps->seq_parameter_set_id);
+        ret = AVERROR_INVALIDDATA;
+        goto err;
+    }
+    sps = (VVCSPS *)ps->sps_list[pps->seq_parameter_set_id]->data;
+
+    pps->mixed_nalu_types_in_pic_flag = get_bits1(gb);
+
+    ret = pps_parse_width_height(pps, sps, gb, log_ctx);
+    if (ret < 0)
+        goto err;
+
+    pps_parse_conf_win(pps, sps, gb);
+
+    ret = pps_scaling_win_parse(pps, sps, gb, log_ctx);
+    if (ret < 0)
+        goto err;
+
+    pps->output_flag_present_flag = get_bits1(gb);
+
+    ret = pps_parse_subpic_id(pps, sps, gb, log_ctx);
+    if (ret < 0)
+        goto err;
+
+    pps->ctb_width  = vvc_ceil(pps->width,  sps->ctb_size_y);
+    pps->ctb_height = vvc_ceil(pps->height, sps->ctb_size_y);
+    pps->ctb_count  = pps->ctb_width * pps->ctb_height;
+
+    ret = pps_parse_pic_partition(pps, sps, gb, log_ctx);
+    if (ret < 0)
+        goto err;
+
+    pps->cabac_init_present_flag = get_bits1(gb);
+
+    ret = pps_parse_inter(pps, sps, gb, log_ctx);
+    if (ret < 0)
+        goto err;
+
+    sep(pps->init_qp, 26, -(sps->qp_bd_offset), 63);
+    pps->cu_qp_delta_enabled_flag = get_bits1(gb);
+
+    ret = pps_parse_chroma_tool_offsets(pps, gb, log_ctx);
+    if (ret < 0)
+        goto err;
+
+    ret = pps_parse_deblocking_control(pps, gb, log_ctx);
+    if (ret < 0)
+        goto err;
+
+    pps_parse_ph_flags(pps, gb);
+
+    pps->picture_header_extension_present_flag  = get_bits1(gb);
+    pps->slice_header_extension_present_flag    = get_bits1(gb);
+
+    skip_bits1(gb);                 ///< pps_extension_flag
+
+    pps->min_cb_width  = pps->width  / sps->min_cb_size_y;
+    pps->min_cb_height = pps->height / sps->min_cb_size_y;
+    pps->min_pu_width  = pps->width  >> MIN_PU_LOG2;
+    pps->min_pu_height = pps->height >> MIN_PU_LOG2;
+    pps->min_tb_width  = pps->width  >> MIN_TB_LOG2;
+    pps->min_tb_height = pps->height >> MIN_TB_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);
+
+    ret = pps_setup(pps, sps);
+    if (ret < 0)
+        goto err;
+
+    if (get_bits_left(gb) < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Overread PPS by %d bits\n", -get_bits_left(gb));
+        goto err;
+    }
+    remove_pps(ps, pps->pic_parameter_set_id);
+    ps->pps_list[pps->pic_parameter_set_id] = pps_buf;
+
+    return 0;
+
+err:
+    av_buffer_unref(&pps_buf);
+    return ret;
+}
+
+static int ph_parse_pic_parameter_set_id(VVCPH *ph, VVCParamSets *ps, GetBitContext* gb, void *log_ctx)
+{
+    AVBufferRef  *buf;
+
+    ue(ph->pic_parameter_set_id, VVC_MAX_PPS_COUNT - 1);
+    buf = ps->pps_list[ph->pic_parameter_set_id];
+    if (!buf) {
+        av_log(log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n",
+               ph->pic_parameter_set_id);
+        return AVERROR_INVALIDDATA;
+    }
+    ps->pps = (VVCPPS *)buf->data;
+
+    buf = ps->sps_list[ps->pps->seq_parameter_set_id];
+    if (!buf) {
+        av_log(log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n",
+            ps->pps->seq_parameter_set_id);
+        return AVERROR_INVALIDDATA;
+    }
+    ps->sps = (VVCSPS*)buf->data;
+
+    buf = ps->vps_list[ps->sps->video_parameter_set_id];
+    if (!buf) {
+        av_log(log_ctx, AV_LOG_ERROR, "VPS id %d not available.\n",
+            ps->sps->video_parameter_set_id);
+        return AVERROR_INVALIDDATA;
+    }
+    return 0;
+}
+
+static int rpls_list_parse(VVCRefPicListStruct *ref_lists, const VVCParamSets *ps,
+    const int poc, GetBitContext *gb, void *log_ctx)
+{
+    const VVCSPS *sps = ps->sps;
+    const VVCPPS *pps = ps->pps;
+    int i, j, rpl_sps_flag[2], rpl_idx[2];
+    for (i = 0; i < 2; i++) {
+        VVCRefPicListStruct *rpls = ref_lists + i;
+        int prev_delta_poc_msb = 0;
+
+        rpl_sps_flag[i] = 0;
+        if (sps->num_ref_pic_lists[i] > 0) {
+            if (i == 0 || pps->rpl1_idx_present_flag)
+                rpl_sps_flag[i] = get_bits1(gb);
+            else
+                rpl_sps_flag[i] = rpl_sps_flag[0];
+        }
+        if (rpl_sps_flag[i]) {
+            if (sps->num_ref_pic_lists[i] == 1) {
+                rpl_idx[i] = 0;
+            } else if (i == 0 || pps->rpl1_idx_present_flag) {
+                uint8_t bits = av_ceil_log2(sps->num_ref_pic_lists[i]);
+                u(bits, rpl_idx[i], 0, sps->num_ref_pic_lists[i] - 1);
+            } else {
+                rpl_idx[1] = rpl_idx[0];
+            }
+            memcpy(rpls, &sps->ref_pic_list_struct[i][rpl_idx[i]], sizeof(*rpls));
+        } else {
+            rpls_parse(rpls, gb, i, sps->num_ref_pic_lists[i], sps, log_ctx);
+        }
+
+        for (j = 0; j < rpls->num_ref_entries; j++) {
+            VVCRefPicListStructEntry *ref = &rpls->entries[j];
+
+            if (!ref->st_ref_pic_flag) {
+                if (rpls->ltrp_in_header_flag)
+                    ref->lt_poc = get_bits(gb, sps->log2_max_pic_order_cnt_lsb);
+
+                ref->lt_msb_flag = get_bits1(gb);
+                if (ref->lt_msb_flag) {
+                    uint32_t max = 1 << (32 - sps->log2_max_pic_order_cnt_lsb);
+                    uint32_t delta_poc_msb_cycle_lt;
+
+                    ue(delta_poc_msb_cycle_lt, max);
+                    delta_poc_msb_cycle_lt += prev_delta_poc_msb;
+                    ref->lt_poc =
+                        poc - delta_poc_msb_cycle_lt * sps->max_pic_order_cnt_lsb -
+                        (poc & (sps->max_pic_order_cnt_lsb - 1)) + ref->lt_poc;
+
+                    prev_delta_poc_msb = delta_poc_msb_cycle_lt;
+                }
+            }
+        }
+    }
+
+    return 0;
+
+}
+
+static int alf_parse(Alf *alf, const VVCParamSets* ps, GetBitContext *gb, void *log_ctx)
+{
+    const VVCSPS *sps = ps->sps;
+    alf->cc_enabled_flag[0] = alf->cc_enabled_flag[1] = 0;
+    alf->enabled_flag[CB]   = alf->enabled_flag[CR] = 0;
+
+    alf->enabled_flag[LUMA] = get_bits1(gb);
+    if (alf->enabled_flag[LUMA]) {
+
+        alf->num_aps_ids_luma = get_bits(gb, 3);
+        for (int i = 0; i < alf->num_aps_ids_luma; i++) {
+            alf->aps_id_luma[i] = get_bits(gb, 3);
+            if (!ps->alf_list[alf->aps_id_luma[i]]) {
+                av_log(log_ctx, AV_LOG_ERROR, "aps_id_luma %d not available.\n",
+                    alf->aps_id_luma[i]);
+                return AVERROR_INVALIDDATA;
+            }
+        }
+        if (sps->chroma_format_idc != 0) {
+            for (int i = CB; i <= CR; i++)
+                alf->enabled_flag[i] = get_bits1(gb);
+        }
+
+        if (alf->enabled_flag[CB] || alf->enabled_flag[CR]) {
+            alf->aps_id_chroma = get_bits(gb, 3);
+            if (!ps->alf_list[alf->aps_id_chroma]) {
+                av_log(log_ctx, AV_LOG_ERROR, "aps_id_chroma %d not available.\n",
+                    alf->aps_id_chroma);
+                return AVERROR_INVALIDDATA;
+            }
+        }
+
+        if (sps->ccalf_enabled_flag) {
+            for (int i = 0; i < 2; i++) {
+                alf->cc_enabled_flag[i] = get_bits1(gb);
+                if (alf->cc_enabled_flag[i]) {
+                    alf->cc_aps_id[i] = get_bits(gb, 3);
+                    if (!ps->alf_list[alf->cc_aps_id[i]]) {
+                        av_log(log_ctx, AV_LOG_ERROR, "cc_aps_id[%d] %d not available.\n",
+                            i, alf->cc_aps_id[i]);
+                        return AVERROR_INVALIDDATA;
+                    }
+                }
+            }
+        }
+    }
+    return 0;
+}
+
+// 8.3.1 Decoding process for picture order count
+static int ph_compute_poc(VVCPH *ph, const VVCSPS *sps, const int poc_tid0, const int is_clvss)
+{
+    const int max_poc_lsb       = sps->max_pic_order_cnt_lsb;
+    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->pic_order_cnt_lsb;
+    int poc_msb;
+
+    if (ph->poc_msb_cycle_present_flag) {
+        poc_msb = 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 int ph_parse_poc(VVCPH *ph, const VVCSPS *sps, const int poc_tid0, const int is_clvss, GetBitContext *gb, void *log_ctx)
+{
+    ph->pic_order_cnt_lsb = get_bits(gb, sps->log2_max_pic_order_cnt_lsb);
+    if (ph->gdr_pic_flag)
+        ue(ph->recovery_poc_cnt, 1 << sps->log2_max_pic_order_cnt_lsb);
+
+    for (int i = 0; i < sps->num_extra_ph_bytes * 8; i++) {
+        if (sps->extra_ph_bit_present_flag[i])
+            skip_bits1(gb);
+    }
+    if (sps->poc_msb_cycle_flag) {
+        ph->poc_msb_cycle_present_flag = get_bits1(gb);
+        if (ph->poc_msb_cycle_present_flag)
+            ph->poc_msb_cycle_val = get_bits(gb, sps->poc_msb_cycle_len);
+    }
+    ph->poc = ph_compute_poc(ph, sps, poc_tid0, is_clvss);
+
+    return 0;
+}
+
+static int ph_parse_alf(VVCPH *ph, const VVCParamSets *ps, GetBitContext *gb, void *log_ctx)
+{
+
+    if (ps->sps->alf_enabled_flag && ps->pps->alf_info_in_ph_flag)
+        return alf_parse(&ph->alf, ps, gb, log_ctx);
+    ph->alf.enabled_flag[LUMA] = 0;
+    return 0;
+}
+
+static av_always_inline int lmcs_derive_lut_sample(int sample,
+    int *pivot1, int *pivot2, int *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 ph_lmcs_derive_lut(VVCPH *ph, const VVCParamSets *ps)
+{
+    const VVCSPS *sps = ps->sps;
+    const AVBufferRef *lmcs_buf;
+    const VVCLMCS *lmcs;
+    const int max    = (1 << sps->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];
+    int input_pivot[LMCS_MAX_BIN_SIZE];
+    int scale_coeff[LMCS_MAX_BIN_SIZE];
+    int inv_scale_coeff[LMCS_MAX_BIN_SIZE];
+    int i;
+    if (sps->bit_depth > LMCS_MAX_BIT_DEPTH)
+        return AVERROR_PATCHWELCOME;
+
+    lmcs_buf = ps->lmcs_list[ph->lmcs_aps_id];
+    if (!lmcs_buf)
+        return AVERROR_INVALIDDATA;
+    lmcs = (VVCLMCS*)lmcs_buf->data;
+
+    memset(cw, 0, sizeof(cw));
+    for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++)
+        cw[i] = org_cw + lmcs->delta_cw[i];
+
+    ph->lmcs_pivot[0] = 0;
+    for (i = 0; i < LMCS_MAX_BIN_SIZE; i++) {
+        input_pivot[i]        = i * org_cw;
+        ph->lmcs_pivot[i + 1] = ph->lmcs_pivot[i] + cw[i];
+        scale_coeff[i]        = (cw[i] * (1 << 11) +  off) >> shift;
+        if (cw[i] == 0) {
+            inv_scale_coeff[i] = 0;
+            ph->lmcs_chroma_scale_coeff[i] = (1 << 11);
+        } else {
+            inv_scale_coeff[i] = org_cw * (1 << 11) / cw[i];
+            ph->lmcs_chroma_scale_coeff[i] = org_cw * (1 << 11) / (cw[i] + lmcs->delta_crs);
+        }
+    }
+
+    //derive lmcs_fwd_lut
+    for (int sample = 0; sample < max; sample++) {
+        const int idx_y = sample / org_cw;
+        const int fwd_sample = lmcs_derive_lut_sample(sample, ph->lmcs_pivot,
+            input_pivot, scale_coeff, idx_y, max);
+        if (sps->bit_depth > 8)
+            ((uint16_t *)ph->lmcs_fwd_lut)[sample] = fwd_sample;
+        else
+            ph->lmcs_fwd_lut[sample] = fwd_sample;
+
+    }
+
+    //derive lmcs_inv_lut
+    i = lmcs->min_bin_idx;
+    for (int sample = 0; sample < max; sample++) {
+        int inv_sample;
+        while (sample >= ph->lmcs_pivot[i + 1] && i <= lmcs->max_bin_idx)
+            i++;
+
+        inv_sample = lmcs_derive_lut_sample(sample, input_pivot, ph->lmcs_pivot,
+            inv_scale_coeff, i, max);
+
+        if (sps->bit_depth > 8)
+            ((uint16_t *)ph->lmcs_inv_lut)[sample] = inv_sample;
+        else
+            ph->lmcs_inv_lut[sample] = inv_sample;
+    }
+
+    ph->lmcs_min_bin_idx = lmcs->min_bin_idx;
+    ph->lmcs_max_bin_idx = lmcs->max_bin_idx;
+
+    return 0;
+}
+
+static int ph_parse_lmcs(VVCPH *ph, const VVCParamSets *ps, GetBitContext *gb)
+{
+    const VVCSPS *sps = ps->sps;
+    int ret;
+
+    ph->lmcs_enabled_flag = 0;
+    ph->chroma_residual_scale_flag = 0;
+    if (sps->lmcs_enabled_flag) {
+        ph->lmcs_enabled_flag = get_bits1(gb);
+        if (ph->lmcs_enabled_flag) {
+            ph->lmcs_aps_id = get_bits(gb, 2);
+            if (sps->chroma_format_idc)
+                ph->chroma_residual_scale_flag = get_bits1(gb);
+            ret = ph_lmcs_derive_lut(ph, ps);
+            if (ret < 0)
+                return ret;
+        }
+    }
+    return 0;
+}
+
+static void ph_parse_scaling_list(VVCPH *ph, const VVCSPS *sps, GetBitContext *gb)
+{
+    ph->explicit_scaling_list_enabled_flag =  0;
+    if (sps->explicit_scaling_list_enabled_flag) {
+        ph->explicit_scaling_list_enabled_flag = get_bits1(gb);
+        if (ph->explicit_scaling_list_enabled_flag) {
+            //todo: check the ph->scaling_list_aps_id range, when aps ready
+            ph->scaling_list_aps_id = get_bits(gb, 3);
+        }
+    }
+}
+
+static int ph_parse_virtual_boundaries(VVCPH *ph, const VVCSPS *sps,
+    const VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    ph->vbs.virtual_boundaries_present_flag = 0;
+    if (sps->virtual_boundaries_enabled_flag && !sps->vbs.virtual_boundaries_present_flag) {
+        const int ret = virtual_boundaries_parse(&ph->vbs, pps->width, pps->height, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    }
+    return 0;
+}
+
+static int ph_parse_temporal_mvp(VVCPH *ph, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    ph->temporal_mvp_enabled_flag = 0;
+    if (sps->temporal_mvp_enabled_flag) {
+        ph->temporal_mvp_enabled_flag = get_bits1(gb);
+        if (ph->temporal_mvp_enabled_flag && pps->rpl_info_in_ph_flag) {
+            int num_ref_entries;
+            ph->collocated_list = L0;
+            if (ph->rpls[1].num_ref_entries > 0)
+                ph->collocated_list = !get_bits1(gb);
+            num_ref_entries = ph->rpls[ph->collocated_list].num_ref_entries;
+            if (num_ref_entries > 1) {
+                ue(ph->collocated_ref_idx, num_ref_entries - 1);
+            }
+        }
+    }
+    return 0;
+}
+
+static void ph_parse_sao(VVCPH *ph, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb)
+{
+    ph->sao_luma_enabled_flag = 0;
+    ph->sao_chroma_enabled_flag = 0;
+    if (sps->sao_enabled_flag && pps->sao_info_in_ph_flag) {
+        ph->sao_luma_enabled_flag = get_bits1(gb);
+        if (sps->chroma_format_idc != 0)
+            ph->sao_chroma_enabled_flag = get_bits1(gb);
+    }
+}
+
+static int ph_parse_dbf(VVCPH *ph, const VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    ph->deblocking_filter_disabled_flag = pps->deblocking_filter_disabled_flag;
+    if (pps->dbf_info_in_ph_flag) {
+        int ph_deblocking_params_present_flag = get_bits1(gb);
+        if (ph_deblocking_params_present_flag) {
+            ph->deblocking_filter_disabled_flag = 0;
+            if (!pps->deblocking_filter_disabled_flag)
+                ph->deblocking_filter_disabled_flag = get_bits1(gb);
+            if (!ph->deblocking_filter_disabled_flag)
+                return deblock_parse(&ph->deblock, pps, gb, log_ctx);
+        }
+    }
+    memcpy(&ph->deblock, &pps->deblock, sizeof(ph->deblock));
+    return 0;
+}
+
+static int ph_parse_cu_qp(VVCPH *ph, const int inter, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    const PartitionConstraints *pc  = inter ? &ph->inter_slice : &ph->intra_slice_luma;
+    const int ctb_log2_size_y       = sps->ctb_log2_size_y;
+    const int min_qt_log2_size_y    = pc->log2_diff_min_qt_min_cb + sps->min_cb_log2_size_y;
+
+    ph->cu_qp_delta_subdiv[inter] = 0;
+    if (pps->cu_qp_delta_enabled_flag)
+        ue(ph->cu_qp_delta_subdiv[inter],
+           2 * (ctb_log2_size_y - min_qt_log2_size_y +
+           pc->max_mtt_hierarchy_depth));
+
+    ph->cu_chroma_qp_offset_subdiv[inter] = 0;
+    if (pps->cu_chroma_qp_offset_list_enabled_flag)
+        ue(ph->cu_chroma_qp_offset_subdiv[inter],
+           2 * (ctb_log2_size_y - min_qt_log2_size_y +
+           pc->max_mtt_hierarchy_depth));
+
+    return 0;
+}
+
+static int ph_parse_intra(VVCPH *ph, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    int ret;
+
+    if (ph->intra_slice_allowed_flag) {
+        ph->intra_slice_luma = sps->intra_slice_luma;
+        ph->intra_slice_chroma = sps->intra_slice_chroma;
+        if (ph->partition_constraints_override_flag) {
+            ret = partition_constraints_parse(&ph->intra_slice_luma, sps, gb, log_ctx);
+            if (ret < 0)
+                return ret;
+
+            if (sps->qtbtt_dual_tree_intra_flag) {
+                ret = partition_constraints_parse(&ph->intra_slice_chroma, sps, gb, log_ctx);
+                if (ret < 0)
+                    return ret;
+            }
+        }
+        ret = ph_parse_cu_qp(ph, 0, sps, pps, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    }
+
+    return 0;
+}
+
+static int ph_parse_inter(VVCPH *ph, const VVCSPS *sps, const VVCPPS *pps, GetBitContext *gb, void *log_ctx)
+{
+    int ret;
+
+    if (ph->inter_slice_allowed_flag) {
+        ph->inter_slice = sps->inter_slice;
+        if (ph->partition_constraints_override_flag) {
+            ret = partition_constraints_parse(&ph->inter_slice, sps, gb, log_ctx);
+            if (ret < 0)
+                return ret;
+        }
+
+        ret = ph_parse_cu_qp(ph, 1, sps, pps, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+
+        ret = ph_parse_temporal_mvp(ph, sps, pps, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+
+        if (sps->affine_enabled_flag)
+            ph->max_num_subblock_merge_cand = 5 - sps->five_minus_max_num_subblock_merge_cand;
+        else
+            ph->max_num_subblock_merge_cand = sps->sbtmvp_enabled_flag && ph->temporal_mvp_enabled_flag;
+
+        ph->mmvd_fullpel_only_flag = 0;
+        if (sps->mmvd_fullpel_only_enabled_flag)
+            ph->mmvd_fullpel_only_flag = get_bits1(gb);
+
+        ph->mvd_l1_zero_flag   = 1;
+        ph->bdof_disabled_flag = 1;
+        ph->dmvr_disabled_flag = 1;
+        if (!pps->rpl_info_in_ph_flag || ph->rpls[1].num_ref_entries > 0) {
+            ph->mvd_l1_zero_flag = get_bits1(gb);
+
+            ph->bdof_disabled_flag = 1 - sps->bdof_enabled_flag;
+            if (sps->bdof_control_present_in_ph_flag)
+                ph->bdof_disabled_flag = get_bits1(gb);
+
+            ph->dmvr_disabled_flag = 1 - sps->dmvr_enabled_flag;
+            if (sps->dmvr_control_present_in_ph_flag)
+                ph->dmvr_disabled_flag = get_bits1(gb);
+        }
+
+        ph->prof_disabled_flag = 1;
+        if (sps->prof_control_present_in_ph_flag)
+            ph->prof_disabled_flag = get_bits1(gb);
+
+        if ((pps->weighted_pred_flag ||
+            pps->weighted_bipred_flag) &&
+            pps->wp_info_in_ph_flag) {
+            av_assert0("fixme" && 0);
+    #if 0
+            // if pps->wp_info_in_ph->fla == 1
+            // pred_weight_table will not use num_ref_idx_active
+            uint8_t num_ref_idx_active[2] = {0, 0};
+            CHECK(FUNC(pred_weight_table)(ctx, rw, sps, pps, &ph->ref_pic_lists,
+                                          num_ref_idx_active,
+                                          &ph->pred_weight_table));
+    #endif
+        }
+    }
+    return 0;
+}
+
+static int ph_parse(VVCPH *ph, VVCParamSets *ps, const int poc_tid0, const int is_clvss, GetBitContext *gb, void *log_ctx)
+{
+    const VVCSPS *sps;
+    const VVCPPS *pps;
+    int ret, start_bit;
+
+    start_bit = get_bits_count(gb);
+    ph->gdr_or_irap_pic_flag = get_bits1(gb);
+    ph->non_ref_pic_flag     = get_bits1(gb);
+
+    if (ph->gdr_or_irap_pic_flag)
+        ph->gdr_pic_flag = get_bits1(gb);
+
+    ph->inter_slice_allowed_flag = get_bits1(gb);
+    ph->intra_slice_allowed_flag = !ph->inter_slice_allowed_flag || get_bits1(gb);
+
+    ret = ph_parse_pic_parameter_set_id(ph, ps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+    sps = ps->sps;
+    pps = ps->pps;
+
+    ret = ph_parse_poc(ph, sps, poc_tid0, is_clvss, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    ret = ph_parse_alf(ph, ps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    ret = ph_parse_lmcs(ph, ps, gb);
+    if (ret < 0)
+        return ret;
+    ph_parse_scaling_list(ph, sps, gb);
+    ph_parse_virtual_boundaries(ph, sps, pps, gb, log_ctx);
+
+    ph->pic_output_flag = 1;
+    if (pps->output_flag_present_flag && !ph->non_ref_pic_flag)
+        ph->pic_output_flag = get_bits1(gb);
+
+    if (pps->rpl_info_in_ph_flag)
+        rpls_list_parse(ph->rpls, ps, ph->poc, gb, log_ctx);
+
+    if (sps->partition_constraints_override_enabled_flag)
+        ph->partition_constraints_override_flag = get_bits1(gb);
+
+    ret = ph_parse_intra(ph, sps, pps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    ret = ph_parse_inter(ph, sps, pps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    if (pps->qp_delta_info_in_ph_flag)
+        se(ph->qp_delta, -sps->qp_bd_offset - pps->init_qp, 63 - pps->init_qp);
+
+    ph->joint_cbcr_sign_flag = 0;
+    if (sps->joint_cbcr_enabled_flag)
+        ph->joint_cbcr_sign_flag = get_bits1(gb);
+
+    ph_parse_sao(ph, sps, pps, gb);
+    ph_parse_dbf(ph, pps, gb, log_ctx);
+
+    av_assert0(!pps->picture_header_extension_present_flag && "fixme");
+
+    if (get_bits_left(gb) < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Overread PH by %d bits\n", -get_bits_left(gb));
+        return AVERROR_INVALIDDATA;
+    }
+    ph->size = get_bits_count(gb) - start_bit;
+    return 0;
+
+}
+
+int ff_vvc_decode_ph(VVCParamSets *ps, const int poc_tid0, const int is_clvss, GetBitContext *gb, void *log_ctx)
+{
+    const VVCSPS *sps = ps->sps;
+    const VVCPPS *pps = ps->pps;
+    VVCPH *ph;
+    AVBufferRef *ph_buf;
+    int ret;
+
+    ph_buf = av_buffer_allocz(sizeof(*ps->ph));
+    if (!ph_buf)
+        return AVERROR(ENOMEM);
+
+    ph = (VVCPH *)ph_buf->data;
+    ret = ph_parse(ph, ps, poc_tid0, is_clvss, gb, log_ctx);
+    if (ret < 0) {
+        //sps and pps may changed
+        ps->sps = sps;
+        ps->pps = pps;
+        av_buffer_unref(&ph_buf);
+        return ret;
+    }
+    av_buffer_unref(&ps->ph_buf);
+    ps->ph_buf = ph_buf;
+    ps->ph = ph;
+    return 0;
+}
+
+static int aps_alf_parse_luma(VVCALF *alf, GetBitContext *gb, void *log_ctx)
+{
+    int     alf_luma_coeff_delta_idx[ALF_NUM_FILTERS_LUMA] = { 0 };
+    int8_t  luma_coeff   [ALF_NUM_FILTERS_LUMA * ALF_NUM_COEFF_LUMA];
+    uint8_t luma_clip_idx[ALF_NUM_FILTERS_LUMA * ALF_NUM_COEFF_LUMA];
+
+    const int alf_luma_clip_flag = get_bits1(gb);
+    const int alf_luma_num_filters_signalled = get_ue_golomb(gb) + 1;
+
+    if (alf_luma_num_filters_signalled > ALF_NUM_FILTERS_LUMA) {
+        av_log(log_ctx, AV_LOG_WARNING, "alf_luma_num_filters_signalled(%d) should <= %d\n",
+            alf_luma_num_filters_signalled, ALF_NUM_FILTERS_LUMA);
+        return AVERROR_INVALIDDATA;
+    }
+
+    //alf_luma_coeff_delta_idx[]
+    if (alf_luma_num_filters_signalled > 1) {
+        const int num_bits = av_ceil_log2(alf_luma_num_filters_signalled);
+        for (int i = 0; i < ALF_NUM_FILTERS_LUMA; i++) {
+            alf_luma_coeff_delta_idx[i] = get_bits(gb, num_bits);
+            if (alf_luma_coeff_delta_idx[i] >= ALF_NUM_FILTERS_LUMA) {
+                av_log(log_ctx, AV_LOG_WARNING, "alf_luma_coeff_delta_idx[%d](%d) should < %d\n",
+                    i, alf_luma_coeff_delta_idx[i], ALF_NUM_FILTERS_LUMA);
+                return AVERROR_INVALIDDATA;
+            }
+        }
+    }
+
+    //alf_luma_coeff_abs, alf_luma_coeff_sign
+    for (int i = 0; i < alf_luma_num_filters_signalled; i++) {
+        for (int j = 0; j < ALF_NUM_COEFF_LUMA; j++) {
+
+            int alf_luma_coeff = get_ue_golomb(gb);
+
+            if (alf_luma_coeff && get_bits1(gb))
+                alf_luma_coeff = -alf_luma_coeff;
+            if (alf_luma_coeff < -128 || alf_luma_coeff > 127) {
+                av_log(log_ctx, AV_LOG_WARNING, "alf_luma_coeff[%d][%d](%d) should in range [-128, 127]\n",
+                    i, j, alf_luma_coeff);
+                return AVERROR_INVALIDDATA;
+            }
+            luma_coeff[i * ALF_NUM_COEFF_LUMA + j] = alf_luma_coeff;
+        }
+    }
+
+    if (alf_luma_clip_flag ) {
+        for (int i = 0; i < alf_luma_num_filters_signalled; i++) {
+            for (int j = 0; j < ALF_NUM_COEFF_LUMA; j++)
+                luma_clip_idx[i * ALF_NUM_COEFF_LUMA + j] = get_bits(gb, 2);
+        }
+    } else {
+        memset(luma_clip_idx, 0, sizeof(luma_clip_idx));
+    }
+
+    for (int i = 0; i < ALF_NUM_FILTERS_LUMA; i++) {
+        const int idx = alf_luma_coeff_delta_idx[i];
+        memcpy(&alf->luma_coeff[i * ALF_NUM_COEFF_LUMA], &luma_coeff[idx * ALF_NUM_COEFF_LUMA],
+            sizeof(luma_coeff[0]) * ALF_NUM_COEFF_LUMA);
+        memcpy(&alf->luma_clip_idx[i * ALF_NUM_COEFF_LUMA], &luma_clip_idx[idx * ALF_NUM_COEFF_LUMA],
+            sizeof(luma_clip_idx[0]) * ALF_NUM_COEFF_LUMA);
+    }
+    return 0;
+}
+
+static int aps_alf_parse_chroma(VVCALF *alf, GetBitContext *gb, void *log_ctx)
+{
+    const int alf_chroma_clip_flag = get_bits1(gb);
+
+    alf->num_chroma_filters        = get_ue_golomb_long(gb) + 1;
+
+    if (alf->num_chroma_filters > ALF_NUM_FILTERS_CHROMA) {
+        av_log(log_ctx, AV_LOG_WARNING, "alf_num_chroma_filters_signalled(%d) should <= %d\n",
+            alf->num_chroma_filters, ALF_NUM_FILTERS_CHROMA);
+        return AVERROR_INVALIDDATA;
+    }
+
+    //alf_chroma_coeff_abs, alf_chroma_coeff_sign
+    for (int i = 0; i < alf->num_chroma_filters; i++) {
+        for (int j = 0; j < ALF_NUM_COEFF_CHROMA; j++) {
+            int alf_chroma_coeff = get_ue_golomb(gb);
+
+            if (alf_chroma_coeff && get_bits1(gb))
+                alf_chroma_coeff = -alf_chroma_coeff;
+            if (alf_chroma_coeff < -128 || alf_chroma_coeff > 127) {
+                av_log(log_ctx, AV_LOG_WARNING, "alf_chroma_coeff[%d][%d](%d) should in range [-128, 127]\n",
+                    i, j, alf_chroma_coeff);
+                return AVERROR_INVALIDDATA;
+            }
+            alf->chroma_coeff[i * ALF_NUM_COEFF_CHROMA + j] = alf_chroma_coeff;
+        }
+        if (alf_chroma_clip_flag) {
+            for (int j = 0; j < ALF_NUM_COEFF_CHROMA; j++) {
+                const int alf_chroma_clip_idx = get_bits(gb, 2);
+                alf->chroma_clip_idx[i * ALF_NUM_COEFF_CHROMA + j] = alf_chroma_clip_idx;
+            }
+        }
+    }
+    return 0;
+}
+
+static int aps_alf_parse_cc(VVCALF *alf, const int idx, GetBitContext *gb, void *log_ctx)
+{
+    alf->cc_filters_signalled[idx] = get_ue_golomb(gb) + 1;
+    if (alf->cc_filters_signalled[idx] > ALF_NUM_FILTERS_CC) {
+        av_log(log_ctx, AV_LOG_WARNING, "cc_filters_signalled[%d](%d) should in range <= %d\n",
+                idx, alf->cc_filters_signalled[idx], ALF_NUM_FILTERS_CC);
+            return AVERROR_INVALIDDATA;
+    }
+    for (int i = 0; i < alf->cc_filters_signalled[idx]; i++) {
+        for (int j = 0; j < ALF_NUM_COEFF_CC; j++) {
+            int coeff = get_bits(gb, 3);
+            if (coeff)
+                coeff = (1 - 2 * get_bits1(gb)) * (1 << (coeff - 1));
+            alf->cc_coeff[idx][i * ALF_NUM_COEFF_CC + j] = coeff;
+        }
+    }
+    return 0;
+}
+
+static int aps_alf_parse(VVCALF *alf, GetBitContext *gb, void *log_ctx)
+{
+    const int aps_chroma_present_flag         = get_bits1(gb);
+    const int alf_luma_filter_signal_flag     = get_bits1(gb);
+    int alf_chroma_filter_signal_flag = 0;
+    int alf_cc_filter_signal_flag[2]  = { 0 };
+    int ret;
+
+    if (aps_chroma_present_flag) {
+        alf_chroma_filter_signal_flag = get_bits1(gb);
+        alf_cc_filter_signal_flag[0]  = get_bits1(gb);
+        alf_cc_filter_signal_flag[1]  = get_bits1(gb);
+    }
+    if (alf_luma_filter_signal_flag) {
+        ret = aps_alf_parse_luma(alf, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    }
+    if (alf_chroma_filter_signal_flag) {
+        ret = aps_alf_parse_chroma(alf, gb, log_ctx);
+        if (ret < 0 )
+            return ret;
+    }
+    for (int i = 0; i < 2; i++) {
+        if (alf_cc_filter_signal_flag[i]) {
+            ret = aps_alf_parse_cc(alf, i, gb, log_ctx);
+            if (ret < 0)
+                return ret;
+        }
+    }
+    return 0;
+}
+
+static int aps_lmcs_parse(VVCLMCS *lmcs, GetBitContext *gb, void *log_ctx)
+{
+    const int aps_chroma_present_flag   = get_bits1(gb);
+    int lmcs_delta_cw_prec;
+
+    lmcs->min_bin_idx   = get_ue_golomb(gb);
+    lmcs->max_bin_idx   = LMCS_MAX_BIN_SIZE - 1 - get_ue_golomb(gb);
+    lmcs_delta_cw_prec  = get_ue_golomb(gb) + 1;
+    if (lmcs->min_bin_idx >= LMCS_MAX_BIN_SIZE) {
+        av_log(log_ctx, AV_LOG_WARNING, "lmcs_min_bin_idx(%d) should < %d\n",
+            lmcs->min_bin_idx, LMCS_MAX_BIN_SIZE);
+        return AVERROR_INVALIDDATA;
+    }
+    if (lmcs->max_bin_idx >= LMCS_MAX_BIN_SIZE || lmcs->max_bin_idx < lmcs->min_bin_idx) {
+        av_log(log_ctx, AV_LOG_WARNING, "lmcs_max_bin_idx(%d) should in range [%d, %d]\n",
+            lmcs->max_bin_idx, lmcs->min_bin_idx, LMCS_MAX_BIN_SIZE - 1);
+        return AVERROR_INVALIDDATA;
+    }
+    if (lmcs_delta_cw_prec > LMCS_MAX_BIN_SIZE - 1) {
+        av_log(log_ctx, AV_LOG_WARNING, "lmcs_delta_cw_prec_minus1(%d) should in range [0, 14]\n",
+            lmcs_delta_cw_prec - 1);
+        return AVERROR_INVALIDDATA;
+    }
+
+    memset(&lmcs->delta_cw, 0, sizeof(lmcs->delta_cw));
+    for (int i = lmcs->min_bin_idx; i <= lmcs->max_bin_idx; i++) {
+        lmcs->delta_cw[i] = get_bits(gb, lmcs_delta_cw_prec);
+        if (lmcs->delta_cw[i] && get_bits1(gb))
+            lmcs->delta_cw[i] = -lmcs->delta_cw[i];
+    }
+    if (aps_chroma_present_flag) {
+        lmcs->delta_crs = get_bits(gb, 3);
+        if (lmcs->delta_crs && get_bits1(gb))
+            lmcs->delta_crs = -lmcs->delta_crs;
+    }
+    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);
+}
+
+static int derive_max_delta(const int id)
+{
+    return (id < SL_START_4x4) ? id : ((id < SL_START_8x8) ? (id - 2) : (id - 8));
+}
+
+// 7.4.3.20 Scaling list data semantics
+static int aps_scaling_parse(VVCScalingList *sl, GetBitContext *gb, AVCodecContext *log_ctx)
+{
+
+    int coeff[SL_MAX_MATRIX_SIZE * SL_MAX_MATRIX_SIZE];
+    const uint8_t *pred;
+    const int *scaling_list;
+    const int aps_chroma_present_flag = get_bits1(gb);
+
+    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 scaling_list_copy_mode_flag = 1;
+        int scaling_list_pred_mode_flag = 0;
+        int scaling_list_pred_id_delta  = 0;
+        int dc = 0;
+
+        if (aps_chroma_present_flag || is_luma_list(id)) {
+            scaling_list_copy_mode_flag = get_bits1(gb);
+            if (!scaling_list_copy_mode_flag)
+                scaling_list_pred_mode_flag = get_bits1(gb);
+            if ((scaling_list_copy_mode_flag || scaling_list_pred_mode_flag) &&
+                id != SL_START_2x2 &&
+                id != SL_START_4x4 &&
+                id != SL_START_8x8) {
+                const int max_delta = derive_max_delta(id);
+                ue(scaling_list_pred_id_delta, max_delta);
+            }
+            if (!scaling_list_copy_mode_flag) {
+                int next_coef = 0;
+                int scaling_list_delta_coef, scaling_list_dc_coef;
+
+                if (id >= SL_START_16x16) {
+                    se(scaling_list_dc_coef, -128, 127);
+                    dc = next_coef = scaling_list_dc_coef;
+                }
+
+                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)) {
+                        se(scaling_list_delta_coef, -128, 127);
+                        next_coef += scaling_list_delta_coef;
+                    }
+                    coeff[i] = next_coef;
+                }
+            }
+        }
+
+        //dc
+        if (id >= SL_START_16x16) {
+            if (!scaling_list_copy_mode_flag && !scaling_list_pred_mode_flag) {
+                sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 8;
+            } else if (!scaling_list_pred_id_delta) {
+                sl->scaling_matrix_dc_rec[id - SL_START_16x16] = 16;
+            } else {
+                const int ref_id = id - scaling_list_pred_id_delta;
+                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 = scaling_list_copy_mode_flag ? ff_vvc_scaling_list0 : coeff;
+        if (!scaling_list_copy_mode_flag && !scaling_list_pred_mode_flag)
+            pred = ff_vvc_scaling_pred_8;
+        else if (!scaling_list_pred_id_delta)
+            pred = ff_vvc_scaling_pred_16;
+        else
+            pred = sl->scaling_matrix_rec[id - scaling_list_pred_id_delta];
+        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;
+        }
+    }
+
+    return 0;
+}
+
+int ff_vvc_decode_aps(VVCParamSets *ps, GetBitContext *gb, void *log_ctx)
+{
+    int ret = 0;
+    const int aps_params_type                 = get_bits(gb, 3);
+    const int aps_adaptation_parameter_set_id = get_bits(gb, 5);
+
+    const int max_id[]  = { VVC_MAX_ALF_COUNT, VVC_MAX_LMCS_COUNT, VVC_MAX_SL_COUNT };
+    const int size[]    = { sizeof(VVCALF), sizeof(VVCLMCS), sizeof(VVCScalingList) };
+    AVBufferRef **list[] = { ps->alf_list, ps->lmcs_list, ps->scaling_list };
+    AVBufferRef *buf;
+
+    if (aps_params_type >= FF_ARRAY_ELEMS(max_id)) {
+        av_log(log_ctx, AV_LOG_INFO, "Skipping APS type %d\n", aps_params_type);
+        return 0;
+    }
+    if (aps_adaptation_parameter_set_id >= max_id[aps_params_type]) {
+        av_log(log_ctx, AV_LOG_WARNING, "aps_adaptation_parameter_set_id(%d) should <= %d\n",
+            aps_adaptation_parameter_set_id, max_id[aps_params_type]);
+        return AVERROR_INVALIDDATA;
+    }
+
+    buf = av_buffer_allocz(size[aps_params_type]);
+    if (!buf)
+        return AVERROR(ENOMEM);
+
+    switch (aps_params_type) {
+        case APS_ALF:
+            ret = aps_alf_parse((VVCALF *)buf->data, gb, log_ctx);
+            break;
+        case APS_LMCS:
+            ret = aps_lmcs_parse((VVCLMCS *)buf->data, gb, log_ctx);
+            break;
+        case APS_SCALING:
+            ret = aps_scaling_parse((VVCScalingList *)buf->data, gb, log_ctx);
+            break;
+        default:
+            av_log(log_ctx, AV_LOG_INFO, "Skipping APS type %d\n", aps_params_type);
+            goto fail;
+    }
+    if (ret < 0)
+        goto fail;
+
+    get_bits1(gb);   // aps_extension_flag
+
+    if (get_bits_left(gb) < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Overread aps type %d by %d bits\n", aps_params_type, -get_bits_left(gb));
+        ret = AVERROR_INVALIDDATA;
+        goto fail;
+    }
+
+    ret = av_buffer_replace(&list[aps_params_type][aps_adaptation_parameter_set_id], buf);
+
+fail:
+    av_buffer_unref(&buf);
+    return ret;
+}
+
+
+void ff_vvc_ps_uninit(VVCParamSets *ps)
+{
+    int i;
+
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->scaling_list); i++)
+        av_buffer_unref(&ps->scaling_list[i]);
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->lmcs_list); i++)
+        av_buffer_unref(&ps->lmcs_list[i]);
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->alf_list); i++)
+        av_buffer_unref(&ps->alf_list[i]);
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->vps_list); i++)
+        av_buffer_unref(&ps->vps_list[i]);
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->sps_list); i++)
+        av_buffer_unref(&ps->sps_list[i]);
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->pps_list); i++)
+        av_buffer_unref(&ps->pps_list[i]);
+    av_buffer_unref(&ps->ph_buf);
+}
+
+static int sh_parse_subpic_idx(VVCSH *sh, const VVCParamSets *ps, GetBitContext *gb)
+{
+    const VVCSPS *sps = ps->sps;
+    const VVCPPS *pps = ps->pps;
+    if (!sps->subpic_info_present_flag)
+        return 0;
+
+    sh->subpic_id = get_bits(gb, sps->subpic_id_len );
+    if (!sps->subpic_id_mapping_explicitly_signalled_flag)
+        return sh->subpic_id;
+
+    for (int i = 0; i < sps->num_subpics; i++) {
+        if (pps->subpic_id[i] == sh->subpic_id)
+            return i;
+    }
+    return sps->num_subpics;
+}
+
+static void sh_parse_extra_bits(const VVCSPS *sps, GetBitContext *gb)
+{
+    for (int i = 0; i < sps->num_extra_sh_bytes * 8; i++) {
+        if (sps->extra_sh_bit_present_flag[i])
+            get_bits1(gb);
+    }
+}
+
+static int sh_parse_slice_address(VVCSH *sh, VVCContext *s, const int curr_subpic_idx, GetBitContext *gb)
+{
+    void *log_ctx           = s->avctx;
+    const VVCSPS *sps       = s->ps.sps;
+    const VVCPPS *pps       = s->ps.pps;
+    int num_slices_in_subpic = pps->num_slices_in_subpic[curr_subpic_idx];
+
+    sh->slice_address = 0;
+    if ((pps->rect_slice_flag && num_slices_in_subpic > 1) ||
+        (!pps->rect_slice_flag && pps->num_tiles_in_pic > 1)) {
+        unsigned int bits, max;
+        if (!pps->rect_slice_flag) {
+            bits = av_ceil_log2(pps->num_tiles_in_pic);
+            max = pps->num_tiles_in_pic - 1;
+        } else {
+            bits = av_ceil_log2(num_slices_in_subpic);
+            max = num_slices_in_subpic - 1;
+        }
+        u(bits, sh->slice_address, 0, max);
+    }
+
+    sh_parse_extra_bits(sps, gb);
+
+    if (pps->rect_slice_flag) {
+        int pic_level_slice_idx = sh->slice_address;
+        for (int j = 0; j < curr_subpic_idx; j++)
+            pic_level_slice_idx += pps->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 = sh->slice_address % pps->num_tile_columns;
+        int tile_y = sh->slice_address / pps->num_tile_columns;
+        const int slice_start_ctb = pps->row_bd[tile_y] * pps->ctb_width + pps->col_bd[tile_x] * pps->row_height[tile_y];
+        sh->num_tiles_in_slice = 1;
+        if (pps->num_tiles_in_pic - sh->slice_address > 1)
+            uep(sh->num_tiles_in_slice, 1, 1, pps->num_tiles_in_pic - sh->slice_address);
+        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 = sh->slice_address; tile_idx < sh->slice_address + sh->num_tiles_in_slice; tile_idx++) {
+            tile_x = tile_idx % pps->num_tile_columns;
+            tile_y = tile_idx / pps->num_tile_columns;
+            sh->num_ctus_in_curr_slice += pps->row_height[tile_y] * pps->column_width[tile_x];
+        }
+    }
+
+    return 0;
+}
+
+static int sh_parse_nb_refs(const VVCSH *sh, uint8_t *nb_refs, const VVCPPS *pps, GetBitContext* gb, void *log_ctx)
+{
+    if ((!IS_I(sh)&& sh->rpls[0].num_ref_entries > 1) ||
+        (IS_B(sh) &&  sh->rpls[1].num_ref_entries > 1)) {
+        int num_ref_idx_active_override_flag = get_bits1(gb);
+        for (int i = 0; i < (IS_B(sh) ? 2 : 1); i++) {
+            if (num_ref_idx_active_override_flag) {
+                nb_refs[i] = 1;
+                if (sh->rpls[i].num_ref_entries > 1) {
+                    nb_refs[i] = get_ue_golomb_long(gb) + 1;
+                    if (nb_refs[i] > 15) {
+                        av_log(log_ctx, AV_LOG_ERROR,
+                            "num_ref_idx_active_minus1(%d) should in range [0, 14]\n",
+                            nb_refs[i] - 1);
+                        return AVERROR_INVALIDDATA;
+                    }
+                }
+            } else {
+                nb_refs[i] =
+                    FFMIN(pps->num_ref_idx_default_active[i], sh->rpls[i].num_ref_entries);
+            }
+        }
+    } else {
+        nb_refs[0] = !IS_I(sh);
+        nb_refs[1] = IS_B(sh);
+    }
+    return 0;
+}
+
+static int sh_parse_alf(VVCSH *sh, VVCContext *s, GetBitContext *gb)
+{
+    int ret;
+    const VVCParamSets *ps = &s->ps;
+
+    sh->alf.enabled_flag[LUMA] = 0;
+    if (ps->sps->alf_enabled_flag) {
+        if (!ps->pps->alf_info_in_ph_flag) {
+            ret = alf_parse(&sh->alf, ps, gb, s->avctx);
+            if (ret < 0)
+                return ret;
+        } else {
+            sh->alf = ps->ph->alf;
+        }
+    }
+    return 0;
+}
+
+static int sh_parse_inter(VVCSH *sh, VVCContext *s, GetBitContext *gb)
+{
+    int ret;
+    void *log_ctx = s->avctx;
+    const VVCParamSets *ps = &s->ps;
+    const VVCSPS *sps      = ps->sps;
+    const VVCPPS *pps      = ps->pps;
+    const VVCPH  *ph       = ps->ph;
+
+    if (!pps->rpl_info_in_ph_flag && (!IS_IDR(s) || sps->idr_rpl_present_flag)) {
+        ret = rpls_list_parse(sh->rpls, ps, ph->poc, gb, log_ctx);
+        if (ret < 0)
+            return ret;
+    } else {
+        memcpy(&sh->rpls, &ph->rpls, sizeof(ph->rpls));
+    }
+
+    ret = sh_parse_nb_refs(sh, sh->nb_refs, pps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    sh->cabac_init_flag = 0;
+    if (!IS_I(sh)) {
+        if (pps->cabac_init_present_flag)
+            sh->cabac_init_flag = get_bits1(gb);
+        if (ph->temporal_mvp_enabled_flag && !pps->rpl_info_in_ph_flag) {
+            sh->collocated_list = L0;
+            if (IS_B(sh))
+                sh->collocated_list = !get_bits1(gb);
+            if (sh->nb_refs[sh->collocated_list] > 1) {
+                ue(sh->collocated_ref_idx, sh->nb_refs[sh->collocated_list] - 1);
+            }
+            if (!pps->wp_info_in_ph_flag &&
+                ((pps->weighted_pred_flag  && IS_P(sh)) ||
+                (pps->weighted_bipred_flag && IS_B(sh)))) {
+                av_assert0(0 && "fix me");
+#if 0
+                CHECK(FUNC(pred_weight_table)(ctx, rw, sps, pps, ref_pic_lists,
+                                              nb_refs,
+                                              &sh->pred_weight_table));
+#endif
+                }
+        }
+
+    }
+    return 0;
+}
+
+static int sh_parse_slice_qp_y(VVCSH *sh, const VVCParamSets *ps, GetBitContext* gb, void *log_ctx)
+{
+    const VVCSPS *sps   = ps->sps;
+    const VVCPPS *pps   = ps->pps;
+    const VVCPH *ph     = ps->ph;
+    if (!pps->qp_delta_info_in_ph_flag) {
+        int8_t sh_qp_delta;
+        se(sh_qp_delta, -6 * (sps->bit_depth - 8) - pps->init_qp,
+           63 - pps->init_qp);
+        sh->slice_qp_y = pps->init_qp + sh_qp_delta;
+    } else {
+        sh->slice_qp_y = pps->init_qp + ph->qp_delta;
+    }
+    return 0;
+}
+
+static int sh_parse_chroma_qp_offsets(VVCSH *sh, const VVCParamSets *ps, GetBitContext* gb, void *log_ctx)
+{
+    const VVCSPS *sps   = ps->sps;
+    const VVCPPS *pps   = ps->pps;
+
+    memset(sh->chroma_qp_offset, 0, sizeof(sh->chroma_qp_offset));
+
+    if (pps->slice_chroma_qp_offsets_present_flag) {
+        int8_t off;
+        for (int i = CB - 1; i < CR + sps->joint_cbcr_enabled_flag; i++) {
+            se(sh->chroma_qp_offset[i], -12, 12);
+            off = pps->chroma_qp_offset[i] + sh->chroma_qp_offset[i];
+            if (off < -12 || off > 12) {
+                av_log(log_ctx, AV_LOG_ERROR,
+                    "chroma_qp_offset(%d) not in range [-12, 12].\n", off);
+                return AVERROR_INVALIDDATA;
+            }
+        }
+    }
+
+    sh->cu_chroma_qp_offset_enabled_flag = 0;
+    if (pps->cu_chroma_qp_offset_list_enabled_flag)
+        sh->cu_chroma_qp_offset_enabled_flag = get_bits1(gb);
+    return 0;
+}
+
+static void sh_parse_sao_used_flag(VVCSH *sh, const VVCParamSets *ps, GetBitContext *gb)
+{
+    const VVCSPS *sps   = ps->sps;
+    const VVCPPS *pps   = ps->pps;
+    const VVCPH *ph     = ps->ph;
+    if (sps->sao_enabled_flag && !pps->sao_info_in_ph_flag) {
+        const int has_chroma = sps->chroma_format_idc != 0;
+        sh->sao_used_flag[0] = get_bits1(gb);
+        sh->sao_used_flag[1] = has_chroma ? get_bits1(gb) : 0;
+    } else {
+        sh->sao_used_flag[0] = ph->sao_luma_enabled_flag;
+        sh->sao_used_flag[1] = ph->sao_chroma_enabled_flag;
+    }
+    sh->sao_used_flag[2] = sh->sao_used_flag[1];
+}
+
+static int sh_parse_deblock(VVCSH *sh, const VVCPPS *pps, const VVCPH *ph, GetBitContext *gb, void *log_ctx)
+{
+    int sh_deblocking_params_present_flag = 0;
+
+    if (pps->deblocking_filter_override_enabled_flag && !pps->dbf_info_in_ph_flag)
+        sh_deblocking_params_present_flag = get_bits1(gb);
+
+    sh->deblocking_filter_disabled_flag = ph->deblocking_filter_disabled_flag;
+    if (sh_deblocking_params_present_flag) {
+        sh->deblocking_filter_disabled_flag = 0;
+        if (!pps->deblocking_filter_disabled_flag)
+            sh->deblocking_filter_disabled_flag = get_bits1(gb);
+
+        if (!sh->deblocking_filter_disabled_flag)
+            return deblock_parse(&sh->deblock,pps, gb, log_ctx);
+    }
+    memcpy(&sh->deblock, &ph->deblock, sizeof(sh->deblock));
+
+    return 0;
+}
+
+static void sh_parse_transform(VVCSH *sh, const VVCSPS *sps, GetBitContext *gb)
+{
+    sh->dep_quant_used_flag = 0;
+    if (sps->dep_quant_enabled_flag)
+        sh->dep_quant_used_flag = get_bits1(gb);
+
+    sh->sign_data_hiding_used_flag = 0;
+    if (sps->sign_data_hiding_enabled_flag && !sh->dep_quant_used_flag)
+        sh->sign_data_hiding_used_flag = get_bits1(gb);
+
+    sh->ts_residual_coding_disabled_flag = 0;
+    if (sps->transform_skip_enabled_flag &&
+        !sh->dep_quant_used_flag &&
+        !sh->sign_data_hiding_used_flag)
+        sh->ts_residual_coding_disabled_flag = get_bits1(gb);
+}
+
+static int sh_parse_entry_points(VVCSH *sh, const int curr_subpic_idx,
+    const VVCParamSets *ps, GetBitContext *gb, void *log_ctx)
+{
+    const VVCSPS *sps   = ps->sps;
+    const VVCPPS *pps   = ps->pps;
+
+    sh->num_entry_points = 0;
+    if (sps->entry_point_offsets_present_flag) {
+        for (int i = 1; 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->entropy_coding_sync_enabled_flag)) {
+                sh->entry_point_start_ctu[sh->num_entry_points] = i;
+                sh->num_entry_points++;
+            }
+        }
+
+        if (sh->num_entry_points > VVC_MAX_ENTRY_POINTS) {
+            av_log(log_ctx, AV_LOG_ERROR, "Too many entry points: "
+                   "%"PRIu16".\n", sh->num_entry_points);
+            return AVERROR_PATCHWELCOME;
+        }
+    }
+
+    if (sh->num_entry_points > 0) {
+        int sh_entry_offset_len_minus1;
+        ue(sh_entry_offset_len_minus1, 31);
+        for (int i = 0; i < sh->num_entry_points; i++)
+            sh->entry_point_offset[i] = get_bits(gb, sh_entry_offset_len_minus1 + 1) + 1;
+    }
+
+    return 0;
+}
+
+static int sh_parse_param_set(VVCContext *s, const int sh_picture_header_in_slice_header_flag,
+    const int is_first_slice, GetBitContext *gb)
+{
+    VVCPH *ph                   = s->ps.ph;
+    int ret = 0;
+
+    if (!(sh_picture_header_in_slice_header_flag && is_first_slice) && !ph)
+        return AVERROR_INVALIDDATA;
+
+    if (sh_picture_header_in_slice_header_flag){
+        if (is_first_slice) {
+            ret = ff_vvc_decode_ph(&s->ps, s->pocTid0, IS_CLVSS(s), gb, s->avctx);
+            if (ret < 0)
+                return ret;
+        } else {
+            skip_bits(gb, ph->size);
+        }
+    }
+
+    return ret;
+}
+
+int ff_vvc_decode_sh(VVCSH *sh, VVCContext *s, const int is_first_slice, GetBitContext *gb)
+{
+    void *log_ctx = s->avctx;
+    VVCParamSets *ps   = &s->ps;
+    const int sh_picture_header_in_slice_header_flag = get_bits1(gb);
+    int ret, curr_subpic_idx;
+
+    ret = sh_parse_param_set(s, sh_picture_header_in_slice_header_flag, is_first_slice, gb);
+    if (ret < 0)
+        return ret;
+
+    curr_subpic_idx = sh_parse_subpic_idx(sh, ps, gb);
+    if (curr_subpic_idx >= ps->sps->num_subpics) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "sh->subpic_id(%d) should in range [0, %d]\n",
+               curr_subpic_idx, ps->sps->num_subpics - 1);
+        return AVERROR_INVALIDDATA;
+    }
+
+    ret = sh_parse_slice_address(sh, s, curr_subpic_idx, gb);
+    if (ret < 0)
+        return ret;
+
+    sh->slice_type = VVC_SLICE_TYPE_I;
+    if (ps->ph->inter_slice_allowed_flag)
+        ue(sh->slice_type, VVC_SLICE_TYPE_I);
+
+    if (IS_CVSS(s))
+        sh->no_output_of_prior_pics_flag = get_bits1(gb);
+
+    ret = sh_parse_alf(sh, s, gb);
+    if (ret < 0)
+        return ret;
+
+    if (ps->ph->lmcs_enabled_flag && !sh_picture_header_in_slice_header_flag)
+        sh->lmcs_used_flag = get_bits1(gb);
+    else
+        sh->lmcs_used_flag = sh_picture_header_in_slice_header_flag ? ps->ph->lmcs_enabled_flag : 0;
+
+    sh->explicit_scaling_list_used_flag = 0;
+    if (ps->ph->explicit_scaling_list_enabled_flag) {
+        sh->explicit_scaling_list_used_flag = 1;
+        if (!sh_picture_header_in_slice_header_flag)
+            sh->explicit_scaling_list_used_flag = get_bits1(gb);
+    }
+
+    ret = sh_parse_inter(sh, s, gb);
+    if (ret <  0)
+        return ret;
+
+    ret = sh_parse_slice_qp_y(sh, ps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    ret = sh_parse_chroma_qp_offsets(sh, ps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    sh_parse_sao_used_flag(sh, ps, gb);
+
+    ret = sh_parse_deblock(sh, ps->pps, ps->ph, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    sh_parse_transform(sh, ps->sps, gb);
+
+    if (ps->pps->slice_header_extension_present_flag) {
+        int sh_slice_header_extension_length;
+        ue(sh_slice_header_extension_length, 256);
+        skip_bits(gb, 8 * sh_slice_header_extension_length);
+    }
+
+    ret = sh_parse_entry_points(sh, curr_subpic_idx, ps, gb, log_ctx);
+    if (ret < 0)
+        return ret;
+
+    skip_bits(gb, 1);
+    align_get_bits(gb);
+
+    if (get_bits_left(gb) < 0) {
+        av_log(log_ctx, AV_LOG_ERROR,
+               "Overread slice header by %d bits\n", -get_bits_left(gb));
+        return AVERROR_INVALIDDATA;
+    }
+
+    //get calculated values
+    {
+        const PartitionConstraints *constraints[2] = { &ps->ph->inter_slice, &ps->ph->inter_slice };
+        if (IS_I(sh)) {
+            constraints[LUMA]   = &ps->ph->intra_slice_luma;
+            constraints[CHROMA] = &ps->ph->intra_slice_chroma;
+        }
+
+        sh->cu_qp_delta_subdiv = ps->ph->cu_qp_delta_subdiv[!IS_I(sh)];
+        sh->cu_chroma_qp_offset_subdiv = ps->ph->cu_chroma_qp_offset_subdiv[!IS_I(sh)];
+
+        for (int i = LUMA; i <= CHROMA; i++) {
+            const PartitionConstraints *pc = constraints[i];
+            const int min_qt_log2_size = ps->sps->min_cb_log2_size_y + pc->log2_diff_min_qt_min_cb;
+
+            sh->max_bt_size[i]   = 1 << (min_qt_log2_size + pc->log2_diff_max_bt_min_qt);
+            sh->max_tt_size[i]   = 1 << (min_qt_log2_size + pc->log2_diff_max_tt_min_qt);
+            sh->max_mtt_depth[i] = pc->max_mtt_hierarchy_depth;
+            sh->min_qt_size[i]   = 1 << min_qt_log2_size;
+        }
+
+    }
+    return 0;
+}
diff --git a/libavcodec/vvc_ps.h b/libavcodec/vvc_ps.h
new file mode 100644
index 0000000000..ea5d431068
--- /dev/null
+++ b/libavcodec/vvc_ps.h
@@ -0,0 +1,808 @@
+/*
+ * VVC parameter set parser
+ *
+ * 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_PS_H
+#define AVCODEC_VVC_PS_H
+
+#include <stdint.h>
+
+#include "libavutil/buffer.h"
+#include "libavutil/pixfmt.h"
+#include "libavutil/rational.h"
+
+#include "avcodec.h"
+#include "get_bits.h"
+#include "vvc.h"
+
+#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 VUI {
+    uint16_t payload_size;
+
+    uint8_t  progressive_source_flag;
+    uint8_t  interlaced_source_flag;
+    uint8_t  non_packed_constraint_flag;
+    uint8_t  non_projected_constraint_flag;
+
+    uint8_t  aspect_ratio_info_present_flag;
+    uint8_t  aspect_ratio_constant_flag;
+    uint8_t  aspect_ratio_idc;
+
+    uint16_t sar_width;
+    uint16_t sar_height;
+
+    uint8_t  overscan_info_present_flag;
+    uint8_t  overscan_appropriate_flag;
+
+    uint8_t  colour_description_present_flag;
+    uint8_t  colour_primaries;
+
+    uint8_t  transfer_characteristics;
+    uint8_t  matrix_coeffs;
+    uint8_t  full_range_flag;
+
+    uint8_t  chroma_loc_info_present_flag;
+    uint8_t  chroma_sample_loc_type_frame;
+    uint8_t  chroma_sample_loc_type_top_field;
+    uint8_t  chroma_sample_loc_type_bottom_field;
+} VUI;
+
+typedef struct GeneralConstraintsInfo {
+    uint8_t present_flag;
+    /* general */
+    uint8_t intra_only_constraint_flag;
+    uint8_t all_layers_independent_constraint_flag;
+    uint8_t one_au_only_constraint_flag;
+
+    /* picture format */
+    uint8_t sixteen_minus_max_bitdepth_constraint_idc;
+    uint8_t three_minus_max_chroma_format_constraint_idc;
+
+    /* NAL unit type related */
+    uint8_t no_mixed_nalu_types_in_pic_constraint_flag;
+    uint8_t no_trail_constraint_flag;
+    uint8_t no_stsa_constraint_flag;
+    uint8_t no_rasl_constraint_flag;
+    uint8_t no_radl_constraint_flag;
+    uint8_t no_idr_constraint_flag;
+    uint8_t no_cra_constraint_flag;
+    uint8_t no_gdr_constraint_flag;
+    uint8_t no_aps_constraint_flag;
+    uint8_t no_idr_rpl_constraint_flag;
+
+    /* tile, slice, subpicture partitioning */
+    uint8_t one_tile_per_pic_constraint_flag;
+    uint8_t pic_header_in_slice_header_constraint_flag;
+    uint8_t one_slice_per_pic_constraint_flag;
+    uint8_t no_rectangular_slice_constraint_flag;
+    uint8_t one_slice_per_subpic_constraint_flag;
+    uint8_t no_subpic_info_constraint_flag;
+
+    /* CTU and block partitioning */
+    uint8_t three_minus_max_log2_ctu_size_constraint_idc;
+    uint8_t no_partition_constraints_override_constraint_flag;
+    uint8_t no_mtt_constraint_flag;
+    uint8_t no_qtbtt_dual_tree_intra_constraint_flag;
+
+    /* intra */
+    uint8_t no_palette_constraint_flag;
+    uint8_t no_ibc_constraint_flag;
+    uint8_t no_isp_constraint_flag;
+    uint8_t no_mrl_constraint_flag;
+    uint8_t no_mip_constraint_flag;
+    uint8_t no_cclm_constraint_flag;
+
+    /* inter */
+    uint8_t no_ref_pic_resampling_constraint_flag;
+    uint8_t no_res_change_in_clvs_constraint_flag;
+    uint8_t no_weighted_prediction_constraint_flag;
+    uint8_t no_ref_wraparound_constraint_flag;
+    uint8_t no_temporal_mvp_constraint_flag;
+    uint8_t no_sbtmvp_constraint_flag;
+    uint8_t no_amvr_constraint_flag;
+    uint8_t no_bdof_constraint_flag;
+    uint8_t no_smvd_constraint_flag;
+    uint8_t no_dmvr_constraint_flag;
+    uint8_t no_mmvd_constraint_flag;
+    uint8_t no_affine_motion_constraint_flag;
+    uint8_t no_prof_constraint_flag;
+    uint8_t no_bcw_constraint_flag;
+    uint8_t no_ciip_constraint_flag;
+    uint8_t no_gpm_constraint_flag;
+
+    /* transform, quantization, residual */
+    uint8_t no_luma_transform_size_64_constraint_flag;
+    uint8_t no_transform_skip_constraint_flag;
+    uint8_t no_bdpcm_constraint_flag;
+    uint8_t no_mts_constraint_flag;
+    uint8_t no_lfnst_constraint_flag;
+    uint8_t no_joint_cbcr_constraint_flag;
+    uint8_t no_sbt_constraint_flag;
+    uint8_t no_act_constraint_flag;
+    uint8_t no_explicit_scaling_list_constraint_flag;
+    uint8_t no_dep_quant_constraint_flag;
+    uint8_t no_sign_data_hiding_constraint_flag;
+    uint8_t no_cu_qp_delta_constraint_flag;
+    uint8_t no_chroma_qp_offset_constraint_flag;
+
+    /* loop filter */
+    uint8_t no_sao_constraint_flag;
+    uint8_t no_alf_constraint_flag;
+    uint8_t no_ccalf_constraint_flag;
+    uint8_t no_lmcs_constraint_flag;
+    uint8_t no_ladf_constraint_flag;
+    uint8_t no_virtual_boundaries_constraint_flag;
+} GeneralConstraintsInfo;
+
+typedef struct PTL {
+    uint8_t  general_profile_idc;
+    uint8_t  general_tier_flag;
+    uint8_t  general_level_idc;
+    uint8_t  frame_only_constraint_flag;
+    uint8_t  multilayer_enabled_flag;
+    GeneralConstraintsInfo gci;
+    uint8_t  sublayer_level_present_flag[VVC_MAX_SUBLAYERS - 1];
+    uint8_t  sublayer_level_idc[VVC_MAX_SUBLAYERS - 1];
+    uint8_t  num_sub_profiles;
+    uint32_t general_sub_profile_idc[VVC_MAX_SUB_PROFILES];
+} PTL;
+
+typedef struct DpbParameters {
+    uint8_t max_dec_pic_buffering[VVC_MAX_SUBLAYERS];
+    uint8_t max_num_reorder_pics[VVC_MAX_SUBLAYERS];
+    uint8_t max_latency_increase[VVC_MAX_SUBLAYERS];
+} DpbParameters;
+
+typedef struct VVCVPS {
+    uint8_t  video_parameter_set_id;
+    uint8_t  max_layers;
+    uint8_t  max_sublayers_minus1;
+    uint8_t  default_ptl_dpb_hrd_max_tid_flag;
+    uint8_t  all_independent_layers_flag;
+    uint8_t  layer_id[VVC_MAX_LAYERS];
+    uint8_t  independent_layer_flag[VVC_MAX_LAYERS];
+    uint8_t  max_tid_ref_present_flag[VVC_MAX_LAYERS];
+    uint8_t  direct_ref_layer_flag[VVC_MAX_LAYERS][VVC_MAX_LAYERS - 1];
+    uint8_t  max_tid_il_ref_pics_plus1[VVC_MAX_LAYERS][VVC_MAX_LAYERS - 1];
+    uint8_t  each_layer_is_an_ols_flag;
+    uint8_t  ols_mode_idc;
+    uint8_t  num_output_layer_sets_minus2;
+    uint8_t  ols_output_layer_flag[VVC_MAX_TOTAL_NUM_OLSS][VVC_MAX_LAYERS];
+
+    uint8_t  num_ptls_minus1;
+    uint8_t  pt_present_flag[VVC_MAX_PTLS];
+    uint8_t  ptl_max_tid[VVC_MAX_PTLS];
+    //H266RawProfileTierLevel profile_tier_level[VVC_MAX_PTLS];
+    uint8_t  ols_ptl_idx[VVC_MAX_TOTAL_NUM_OLSS];
+
+    uint16_t num_dpb_params_minus1;
+    uint8_t  sublayer_dpb_params_present_flag;
+    uint8_t  dpb_max_tid[VVC_MAX_TOTAL_NUM_OLSS];
+    //H266DpbParameters dpb_params[VVC_MAX_TOTAL_NUM_OLSS];
+    uint16_t ols_dpb_pic_width[VVC_MAX_TOTAL_NUM_OLSS];
+    uint16_t ols_dpb_pic_height[VVC_MAX_TOTAL_NUM_OLSS];
+    uint8_t  ols_dpb_chroma_format[VVC_MAX_TOTAL_NUM_OLSS];
+    uint8_t  ols_dpb_bitdepth_minus8[VVC_MAX_TOTAL_NUM_OLSS];
+    uint16_t ols_dpb_params_idx[VVC_MAX_TOTAL_NUM_OLSS];
+
+    uint8_t  timing_hrd_params_present_flag;
+    //H266RawGeneralTimingHrdParameters general_timing_hrd_parameters;
+    uint8_t  sublayer_cpb_params_present_flag;
+    uint16_t num_ols_timing_hrd_params_minus1;
+    uint8_t  hrd_max_tid[VVC_MAX_TOTAL_NUM_OLSS];
+    //H266RawOlsTimingHrdParameters ols_timing_hrd_parameters;
+    uint8_t  ols_timing_hrd_idx[VVC_MAX_TOTAL_NUM_OLSS];
+
+    uint8_t data[4096];
+    int data_size;
+} VVCVPS;
+
+typedef struct VVCWindow {
+    int left_offset;
+    int right_offset;
+    int top_offset;
+    int bottom_offset;
+} VVCWindow;
+
+typedef struct VVCRefPicListStructEntry {
+    uint8_t inter_layer_ref_pic_flag;
+    uint8_t st_ref_pic_flag;
+
+    //shortterm
+    int8_t  delta_poc_val_st;   ///< DeltaPocValSt
+
+    //longterm
+    uint8_t lt_msb_flag;        ///< delta_poc_msb_cycle_present_flag
+    int16_t lt_poc;
+
+    //interlayer
+    uint8_t ilrp_idx;
+} VVCRefPicListStructEntry;
+
+typedef struct VVCRefPicListStruct {
+    uint8_t num_ref_entries;
+    uint8_t num_ltrp_entries;                               ///< NumLtrpEntries
+    uint8_t ltrp_in_header_flag;
+    VVCRefPicListStructEntry entries[VVC_MAX_REF_ENTRIES];
+} VVCRefPicListStruct;
+
+/*
+ * we use PartitionConstraints for following fields in spec
+    uint8_t  log2_diff_min_qt_min_cb_intra_slice_luma;
+    uint8_t  max_mtt_hierarchy_depth_intra_slice_luma;
+    uint8_t  log2_diff_max_bt_min_qt_intra_slice_luma;
+    uint8_t  log2_diff_max_tt_min_qt_intra_slice_luma;
+
+    uint8_t  log2_diff_min_qt_min_cb_intra_slice_chroma;
+    uint8_t  max_mtt_hierarchy_depth_intra_slice_chroma;
+    uint8_t  log2_diff_max_bt_min_qt_intra_slice_chroma;
+    uint8_t  log2_diff_max_tt_min_qt_intra_slice_chroma;
+
+    uint8_t  log2_diff_min_qt_min_cb_inter_slice;
+    uint8_t  max_mtt_hierarchy_depth_inter_slice;
+    uint8_t  log2_diff_max_bt_min_qt_inter_slice;
+    uint8_t  log2_diff_max_tt_min_qt_inter_slice;
+
+*/
+typedef struct PartitionConstraints {
+    uint8_t  log2_diff_min_qt_min_cb;
+    uint8_t  max_mtt_hierarchy_depth;
+    uint8_t  log2_diff_max_bt_min_qt;
+    uint8_t  log2_diff_max_tt_min_qt;
+} PartitionConstraints;
+
+typedef struct GeneralTimingHrdParameters {
+    uint8_t general_nal_hrd_params_present_flag;
+    uint8_t general_vcl_hrd_params_present_flag;
+    uint8_t general_du_hrd_params_present_flag;
+    int hrd_cpb_cnt;    ///< hrd_cpb_cnt_minus1 + 1
+} GeneralTimingHrdParameters;
+
+typedef struct VirtualBoundaries {
+    uint8_t  virtual_boundaries_present_flag;
+    uint8_t  num_ver_virtual_boundaries;
+    uint16_t virtual_boundary_pos_x_minus1[3];
+    uint8_t  num_hor_virtual_boundaries;
+    uint16_t virtual_boundary_pos_y_minus1[3];
+} VirtualBoundaries;
+
+typedef struct VVCSPS {
+    unsigned video_parameter_set_id;
+
+    //uint8_t separate_colour_plane_flag;
+
+    VVCWindow output_window;
+
+    VVCWindow conf_win;
+
+    int pixel_shift;
+
+    enum AVPixelFormat pix_fmt;
+
+    uint8_t  max_sublayers;
+    uint8_t  chroma_format_idc;
+
+    uint8_t  ref_pic_resampling_enabled_flag;
+    uint8_t  res_change_in_clvs_allowed_flag;
+
+    uint16_t width;
+    uint16_t height;
+
+    uint8_t  subpic_info_present_flag;
+    uint16_t num_subpics;
+    uint8_t  independent_subpics_flag;
+    uint16_t subpic_ctu_top_left_x[VVC_MAX_SLICES];
+    uint16_t subpic_ctu_top_left_y[VVC_MAX_SLICES];
+    uint16_t subpic_width[VVC_MAX_SLICES];
+    uint16_t subpic_height[VVC_MAX_SLICES];
+    uint8_t  subpic_treated_as_pic_flag[VVC_MAX_SLICES];
+    uint8_t  loop_filter_across_subpic_enabled_flag[VVC_MAX_SLICES];
+    uint8_t  subpic_id_len;
+    uint8_t  subpic_id_mapping_explicitly_signalled_flag;
+    uint8_t  subpic_id_mapping_present_flag;
+    uint32_t subpic_id[VVC_MAX_SLICES];
+
+    uint8_t  entropy_coding_sync_enabled_flag;
+    uint8_t  entry_point_offsets_present_flag;
+
+    uint8_t  log2_max_pic_order_cnt_lsb;
+    uint8_t  poc_msb_cycle_flag;
+    uint8_t  poc_msb_cycle_len;
+
+    uint8_t  num_extra_ph_bytes;
+    uint8_t  extra_ph_bit_present_flag[16];
+
+    uint8_t  num_extra_sh_bytes;
+    uint8_t  extra_sh_bit_present_flag[16];
+
+    uint8_t  sublayer_dpb_params_flag;
+    DpbParameters dpb;
+
+    uint8_t  partition_constraints_override_enabled_flag;
+    uint8_t  qtbtt_dual_tree_intra_flag;
+
+    PartitionConstraints intra_slice_luma;
+    PartitionConstraints intra_slice_chroma;
+    PartitionConstraints inter_slice;
+
+    uint8_t  transform_skip_enabled_flag;
+    uint8_t  bdpcm_enabled_flag;
+
+    uint8_t  mts_enabled_flag;
+    uint8_t  explicit_mts_intra_enabled_flag;
+    uint8_t  explicit_mts_inter_enabled_flag;
+
+    uint8_t  lfnst_enabled_flag;
+
+    uint8_t  joint_cbcr_enabled_flag;
+
+    uint8_t  sao_enabled_flag;
+    uint8_t  alf_enabled_flag;
+    uint8_t  ccalf_enabled_flag;
+    uint8_t  lmcs_enabled_flag;
+
+    // inter
+    uint8_t  weighted_pred_flag;
+    uint8_t  weighted_bipred_flag;
+    uint8_t  long_term_ref_pics_flag;
+    uint8_t  inter_layer_prediction_enabled_flag;
+    uint8_t  idr_rpl_present_flag;
+    uint8_t  num_ref_pic_lists[2];
+    VVCRefPicListStruct ref_pic_list_struct[2][VVC_MAX_REF_PIC_LISTS];
+    uint8_t  ref_wraparound_enabled_flag;
+    uint8_t  temporal_mvp_enabled_flag;
+    uint8_t  sbtmvp_enabled_flag;
+    uint8_t  amvr_enabled_flag;
+    uint8_t  bdof_enabled_flag;
+    uint8_t  bdof_control_present_in_ph_flag;
+    uint8_t  smvd_enabled_flag;
+    uint8_t  dmvr_enabled_flag;
+    uint8_t  dmvr_control_present_in_ph_flag;
+    uint8_t  mmvd_enabled_flag;
+    uint8_t  mmvd_fullpel_only_enabled_flag;
+    uint8_t  sbt_enabled_flag;
+    uint8_t  affine_enabled_flag;
+    uint8_t  five_minus_max_num_subblock_merge_cand;
+    uint8_t  six_param_affine_enabled_flag;
+    uint8_t  affine_amvr_enabled_flag;
+    uint8_t  affine_prof_enabled_flag;
+    uint8_t  prof_control_present_in_ph_flag;
+    uint8_t  bcw_enabled_flag;
+    uint8_t  ciip_enabled_flag;
+    uint8_t  gpm_enabled_flag;
+    uint8_t  log2_parallel_merge_level;
+
+    // intra
+    uint8_t  isp_enabled_flag;
+    uint8_t  mrl_enabled_flag;
+    uint8_t  mip_enabled_flag;
+    uint8_t  cclm_enabled_flag;
+    uint8_t  chroma_horizontal_collocated_flag;
+    uint8_t  chroma_vertical_collocated_flag;
+    uint8_t  palette_enabled_flag;
+    uint8_t  act_enabled_flag;
+    uint8_t  min_qp_prime_ts;
+    uint8_t  ibc_enabled_flag;
+
+    uint8_t  ladf_enabled_flag;
+    uint8_t  num_ladf_intervals;                                    ///< sps_num_ladf_intervals_minus2 + 2;
+    int8_t   ladf_lowest_interval_qp_offset;
+    int8_t   ladf_qp_offset[LADF_MAX_INTERVAL];
+
+    uint8_t  explicit_scaling_list_enabled_flag;
+    uint8_t  scaling_matrix_for_lfnst_disabled_flag;
+    uint8_t  scaling_matrix_for_alternative_colour_space_disabled_flag;
+    uint8_t  scaling_matrix_designated_colour_space_flag;
+    uint8_t  dep_quant_enabled_flag;
+    uint8_t  sign_data_hiding_enabled_flag;
+
+    uint8_t  virtual_boundaries_enabled_flag;
+    VirtualBoundaries vbs;
+
+    uint8_t  timing_hrd_params_present_flag;
+    uint8_t  sublayer_cpb_params_present_flag;
+    GeneralTimingHrdParameters general_timing_hrd_parameters;
+
+    uint8_t  field_seq_flag;
+
+    VUI vui;
+
+    PTL ptl;
+
+    int hshift[3];
+    int vshift[3];
+
+    //derived values
+    unsigned int max_pic_order_cnt_lsb;                             ///< MaxPicOrderCntLsb
+    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
+    unsigned int ladf_interval_lower_bound[LADF_MAX_INTERVAL];      ///< SpsLadfIntervalLowerBound[]
+
+    uint8_t data[4096];
+    int data_size;
+
+    int chroma_qp_table[VVC_MAX_SAMPLE_ARRAYS][VVC_MAX_POINTS_IN_QP_TABLE];
+} VVCSPS;
+
+typedef struct DBParams {
+    int beta_offset[VVC_MAX_SAMPLE_ARRAYS];
+    int tc_offset[VVC_MAX_SAMPLE_ARRAYS];
+} DBParams;
+
+typedef struct VVCPPS {
+
+    uint8_t  pic_parameter_set_id;
+    uint8_t  seq_parameter_set_id;
+    uint8_t  mixed_nalu_types_in_pic_flag;
+    uint16_t width;
+    uint16_t height;
+
+    VVCWindow conf_win;
+    VVCWindow scaling_win;
+
+    uint8_t  output_flag_present_flag;
+    uint8_t  no_pic_partition_flag;
+
+    uint8_t  loop_filter_across_tiles_enabled_flag;
+    uint8_t  rect_slice_flag;
+    uint8_t  single_slice_per_subpic_flag;
+
+    uint8_t  tile_idx_delta_present_flag;
+
+    uint8_t  loop_filter_across_slices_enabled_flag;
+    uint8_t  cabac_init_present_flag;
+
+    uint8_t  num_ref_idx_default_active[2];
+    uint8_t  rpl1_idx_present_flag;
+    uint8_t  weighted_pred_flag;
+    uint8_t  weighted_bipred_flag;
+    uint8_t  ref_wraparound_enabled_flag;
+    int8_t   init_qp;                                   ///< 26 + pps_init_qp_minus26
+    uint8_t  cu_qp_delta_enabled_flag;
+
+    uint8_t  chroma_tool_offsets_present_flag;
+    int8_t   chroma_qp_offset[3];                       ///< pps_cb_qp_offset, pps_cr_qp_offset, pps_joint_cbcr_qp_offset_value
+    uint8_t  slice_chroma_qp_offsets_present_flag;
+    uint8_t  cu_chroma_qp_offset_list_enabled_flag;
+    uint8_t  chroma_qp_offset_list_len_minus1;
+    int8_t   chroma_qp_offset_list[6][3];               ///< pps_cb_qp_offset_list, pps_cr_qp_offset_list, pps_joint_cbcr_qp_offset_list
+
+    uint8_t  deblocking_filter_control_present_flag;
+    uint8_t  deblocking_filter_override_enabled_flag;
+    uint8_t  deblocking_filter_disabled_flag;
+    uint8_t  dbf_info_in_ph_flag;
+    DBParams deblock;
+
+    uint8_t  rpl_info_in_ph_flag;
+    uint8_t  sao_info_in_ph_flag;
+    uint8_t  alf_info_in_ph_flag;
+    uint8_t  wp_info_in_ph_flag;
+    uint8_t  qp_delta_info_in_ph_flag;
+
+    uint8_t  picture_header_extension_present_flag;
+    uint8_t  slice_header_extension_present_flag;
+
+    //derived value;
+    uint16_t num_tiles_in_pic;
+    uint16_t slice_start_offset  [VVC_MAX_SLICES];
+    uint16_t num_slices_in_subpic[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;
+    int      ctb_count;
+
+    uint16_t min_pu_width;
+    uint16_t min_pu_height;
+    uint16_t min_tb_width;
+    uint16_t min_tb_height;
+
+    uint16_t num_tile_columns;              ///< NumTileColumns
+    uint16_t num_tile_rows;                 ///< NumTileRows
+    unsigned int *column_width;             ///< ColWidthVal
+    unsigned int *row_height;               ///< RowHeightVal
+    unsigned int *col_bd;                   ///< TileColBdVal
+    unsigned int *row_bd;                   ///< TileRowBdVal
+    unsigned int *ctb_to_col_bd;            ///< CtbToTileColBd
+    unsigned int *ctb_to_row_bd;            ///< CtbToTileRowBd
+    unsigned int *ctb_addr_in_slice;        ///< CtbAddrInCurrSlice for entire picture
+
+    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 subpic_id[VVC_MAX_SLICES];     ///< SubpicIdVal[]
+    uint16_t ref_wraparound_offset;         ///< PpsRefWraparoundOffset
+
+    uint8_t data[4096];
+    int data_size;
+} VVCPPS;
+
+#define VVC_MAX_ALF_COUNT        8
+#define VVC_MAX_LMCS_COUNT       4
+#define VVC_MAX_SL_COUNT         8
+
+#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
+
+enum {
+    APS_ALF,
+    APS_LMCS,
+    APS_SCALING,
+};
+
+typedef struct Alf {
+    //< ph_alf_enabled_flag, ph_alf_cb_enabled_flag, ph_alf_cr_enabled_flag
+    //< sh_alf_enabled_flag, sh_alf_cb_enabled_flag, sh_alf_cr_enabled_flag
+    uint8_t  enabled_flag[VVC_MAX_SAMPLE_ARRAYS];
+
+    //< ph_num_alf_aps_ids_luma
+    //< sh_alf_aps_id_luma
+    uint8_t  num_aps_ids_luma;
+
+    //< ph_alf_aps_id_luma
+    //< sh_alf_aps_id_luma
+    uint8_t  aps_id_luma[8];
+
+    //< ph_alf_aps_id_chroma
+    //< sh_alf_aps_id_chroma
+    uint8_t  aps_id_chroma;
+
+    //< ph_alf_cc_cb_enabled_flag, ph_alf_cc_cr_enabled_flag
+    //< sh_alf_cc_cb_enabled_flag, sh_alf_cc_cr_enabled_flag
+    uint8_t  cc_enabled_flag[2];
+
+    //< ph_alf_cc_cb_aps_id, ph_alf_cc_cr_aps_id
+    //< sh_alf_cc_cb_aps_id, sh_alf_cc_cr_aps_id
+    uint8_t  cc_aps_id[2];
+} Alf;
+
+typedef struct PredWeightTable {
+    uint8_t luma_weight_l0_flag[16];
+    uint8_t luma_weight_l0[16];
+    uint8_t luma_weight_l1_flag[16];
+    uint8_t luma_weight_l1[16];
+    uint8_t chroma_weight_l0_flag[16];
+    uint8_t chroma_weight_l0[16];
+    uint8_t chroma_weight_l1_flag[16];
+    uint8_t chroma_weight_l1[16];
+} PredWeightTable;
+
+typedef struct VVCPH {
+    uint8_t  gdr_or_irap_pic_flag;
+    uint8_t  non_ref_pic_flag;
+    uint8_t  gdr_pic_flag;
+
+    uint8_t  inter_slice_allowed_flag;
+    uint8_t  intra_slice_allowed_flag;
+
+    uint8_t  pic_parameter_set_id;
+
+    uint16_t pic_order_cnt_lsb;
+    uint8_t  recovery_poc_cnt;
+    uint8_t  poc_msb_cycle_present_flag;
+    uint8_t  poc_msb_cycle_val;
+
+    Alf alf;
+
+    uint8_t  lmcs_enabled_flag;
+    uint8_t  lmcs_aps_id;
+
+    uint8_t  chroma_residual_scale_flag;
+    uint8_t  explicit_scaling_list_enabled_flag;
+    uint8_t  scaling_list_aps_id;
+
+    VirtualBoundaries vbs;
+
+    uint8_t  pic_output_flag;
+    VVCRefPicListStruct rpls[2];
+
+    uint8_t  partition_constraints_override_flag;
+    PartitionConstraints intra_slice_luma;
+    PartitionConstraints intra_slice_chroma;
+    PartitionConstraints inter_slice;
+
+
+    uint8_t  cu_qp_delta_subdiv[2];             ///< cu_qp_delta_subdiv_intra_slice, cu_qp_delta_subdiv_inter_slice
+    uint8_t  cu_chroma_qp_offset_subdiv[2];     ///< cu_chroma_qp_offset_subdiv_intra_slice, cu_chroma_qp_offset_subdiv_inter_slice
+
+    uint8_t  temporal_mvp_enabled_flag;
+    uint8_t  collocated_ref_idx;
+    uint8_t  mmvd_fullpel_only_flag;
+    uint8_t  mvd_l1_zero_flag;
+    uint8_t  bdof_disabled_flag;
+    uint8_t  dmvr_disabled_flag;
+    uint8_t  prof_disabled_flag;
+    PredWeightTable pred_weight_table;
+
+    int8_t   qp_delta;
+    uint8_t  joint_cbcr_sign_flag;
+
+    uint8_t  sao_luma_enabled_flag;
+    uint8_t  sao_chroma_enabled_flag;
+
+    uint8_t  deblocking_filter_disabled_flag;
+    DBParams deblock;
+
+    //derived values
+    unsigned int max_num_subblock_merge_cand;   ///< MaxNumSubblockMergeCand
+
+    int     poc;                                ///< PicOrderCntVal
+    uint8_t collocated_list;                    ///< !collocated_from_l0_flag
+
+    //*2 for high depth
+    uint8_t  lmcs_fwd_lut[LMCS_MAX_LUT_SIZE * 2];
+    uint8_t  lmcs_inv_lut[LMCS_MAX_LUT_SIZE * 2];
+    int      lmcs_min_bin_idx;
+    int      lmcs_max_bin_idx;
+    int      lmcs_pivot[LMCS_MAX_BIN_SIZE + 1];
+    int      lmcs_chroma_scale_coeff[LMCS_MAX_BIN_SIZE];
+
+    int size;
+} VVCPH;
+
+typedef struct VVCALF {
+    int8_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;
+    int8_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 cc_filters_signalled[2];        //< alf_cc_cb_filters_signalled + 1, alf_cc_cr_filters_signalled + 1
+    int8_t  cc_coeff[2][ALF_NUM_FILTERS_CC * ALF_NUM_COEFF_CC];
+} VVCALF;
+
+#define SL_MAX_ID          28
+#define SL_MAX_MATRIX_SIZE 8
+
+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,
+};
+
+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 VVCParamSets {
+    AVBufferRef *vps_list[VVC_MAX_VPS_COUNT];
+    AVBufferRef *sps_list[VVC_MAX_SPS_COUNT];
+    AVBufferRef *pps_list[VVC_MAX_PPS_COUNT];
+    AVBufferRef *alf_list[VVC_MAX_ALF_COUNT];
+    AVBufferRef *lmcs_list[VVC_MAX_LMCS_COUNT];
+    AVBufferRef *scaling_list[VVC_MAX_SL_COUNT];
+    AVBufferRef *ph_buf;
+
+    const VVCSPS *sps;
+    const VVCPPS *pps;
+    VVCPH  *ph;
+} VVCParamSets;
+
+typedef struct VVCFrameParamSets {
+    AVBufferRef *sps_buf;
+    AVBufferRef *pps_buf;
+    AVBufferRef *ph_buf;
+    AVBufferRef *alf_list[VVC_MAX_ALF_COUNT];
+    AVBufferRef *lmcs_list[VVC_MAX_LMCS_COUNT];
+    AVBufferRef *scaling_list[VVC_MAX_SL_COUNT];
+
+    const VVCSPS *sps;
+    const VVCPPS *pps;
+    const VVCPH  *ph;
+} VVCFrameParamSets;
+
+typedef struct VVCSH {
+
+    uint16_t subpic_id;
+    uint16_t slice_address;
+    uint8_t  num_tiles_in_slice;
+    uint8_t  slice_type;
+    uint8_t  no_output_of_prior_pics_flag;
+
+    Alf alf;
+
+    uint8_t  lmcs_used_flag;
+    uint8_t  explicit_scaling_list_used_flag;
+
+    VVCRefPicListStruct rpls[2];
+
+    uint8_t  num_ref_idx_active_override_flag;
+    uint8_t  cabac_init_flag;
+    uint8_t  collocated_ref_idx;
+
+    //H266RawPredWeightTable pred_weight_table;
+
+    int8_t   chroma_qp_offset[3];                           ///< cb_qp_offset, cr_qp_offset, joint_cbcr_qp_offset
+    uint8_t  cu_chroma_qp_offset_enabled_flag;
+
+    uint8_t  sao_used_flag[VVC_MAX_SAMPLE_ARRAYS];          ///< sh_sao_luma_used_flag and sh_sao_chroma_used_flag
+
+    uint8_t  deblocking_filter_disabled_flag;
+    DBParams deblock;
+    uint8_t  dep_quant_used_flag;
+
+    uint8_t  sign_data_hiding_used_flag;
+    uint8_t  ts_residual_coding_disabled_flag;
+
+    //calculated value;
+    uint8_t  collocated_list;                               ///< !sh_collocated_from_l0_flag
+    uint8_t  nb_refs[2];                                    ///< NumRefIdxActive[]
+    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
+    int8_t   slice_qp_y;                                    ///< SliceQpY
+    int8_t   ref_idx_sym[2];                                ///< RefIdxSymL0, RefIdxSymL1
+    int      num_ctus_in_curr_slice;                        ///< NumCtusInCurrSlice
+    uint32_t entry_point_offset[VVC_MAX_ENTRY_POINTS];      ///< entry_point_offset_minus1 + 1
+    uint32_t entry_point_start_ctu[VVC_MAX_ENTRY_POINTS];   ///< entry point start in ctu_addr
+    unsigned int *ctb_addr_in_curr_slice;                   ///< CtbAddrInCurrSlice
+    unsigned int num_entry_points;                          ///< NumEntryPoints
+
+    uint8_t first_slice_flag;
+} VVCSH;
+
+struct VVCContext;
+
+int ff_vvc_decode_sps(VVCParamSets *ps, GetBitContext *gb,
+     int apply_defdispwin, int nuh_layer_id, AVCodecContext *avctx);
+int ff_vvc_decode_pps(VVCParamSets *ps, GetBitContext *gb, void *log_ctx);
+int ff_vvc_decode_ph(VVCParamSets *ps, const int poc_tid0, const int is_clvss, GetBitContext *gb, void *log_ctx);
+int ff_vvc_decode_aps(VVCParamSets *ps, GetBitContext *gb, void *log_ctx);
+int ff_vvc_decode_sh(VVCSH *sh, struct VVCContext *s, const int is_first_slice, GetBitContext *gb);
+void ff_vvc_ps_uninit(VVCParamSets *ps);
+
+#endif /* AVCODEC_VVC_PS_H */
diff --git a/libavcodec/vvc_refs.c b/libavcodec/vvc_refs.c
new file mode 100644
index 0000000000..1df0c77d55
--- /dev/null
+++ b/libavcodec/vvc_refs.c
@@ -0,0 +1,496 @@
+/*
+ * VVC video 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 "libavutil/avassert.h"
+#include "libavutil/pixdesc.h"
+
+#include "internal.h"
+#include "thread.h"
+#include "vvc_thread.h"
+#include "vvc.h"
+#include "vvcdec.h"
+
+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) {
+        ff_thread_release_ext_buffer(fc->avctx, &frame->tf);
+
+        av_buffer_unref(&frame->progress_buf);
+
+        av_buffer_unref(&frame->tab_mvf_buf);
+        frame->tab_mvf = NULL;
+
+        av_buffer_unref(&frame->rpl_buf);
+        av_buffer_unref(&frame->rpl_tab_buf);
+        frame->rpl_tab    = NULL;
+        frame->refPicList = NULL;
+
+        frame->collocated_ref = NULL;
+
+#if 0
+        av_buffer_unref(&frame->hwaccel_priv_buf);
+        frame->hwaccel_picture_private = NULL;
+#endif
+    }
+}
+
+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)
+{
+    int i;
+    for (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);
+}
+#if 0
+void ff_vvc_flush_dpb(VVCContext *s)
+{
+    int i;
+    for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++)
+        ff_vvc_unref_frame(s, &fc->DPB[i], ~0);
+}
+#endif
+
+static void free_progress(void *opaque, uint8_t *data)
+{
+    FrameProgress *p = (FrameProgress*)data;
+    pthread_cond_destroy(&p->cond);
+    pthread_mutex_destroy(&p->lock);
+    av_free(data);
+}
+
+static AVBufferRef *alloc_progress(void)
+{
+    int ret;
+    AVBufferRef *buf;
+    FrameProgress *p = av_mallocz(sizeof(FrameProgress));
+
+    if (!p)
+        return NULL;
+    ret = pthread_cond_init(&p->cond, NULL);
+    if (ret) {
+        av_free(p);
+        return NULL;
+    }
+
+    ret = pthread_mutex_init(&p->lock, NULL);
+    if (ret) {
+        pthread_cond_destroy(&p->cond);
+        av_free(p);
+        return NULL;
+    }
+    buf = av_buffer_create((void*)p, sizeof(*p), free_progress, NULL, 0);
+    if (!buf)
+        free_progress(NULL, (void*)p);
+    return buf;
+}
+
+static VVCFrame *alloc_frame(VVCContext *s, VVCFrameContext *fc)
+{
+    const VVCPPS *pps = s->ps.pps;
+    int i, j, ret;
+    for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+        VVCFrame *frame = &fc->DPB[i];
+        if (frame->frame->buf[0])
+            continue;
+
+        ret = ff_thread_get_ext_buffer(fc->avctx, &frame->tf,
+                                   AV_GET_BUFFER_FLAG_REF);
+        if (ret < 0)
+            return NULL;
+
+        frame->rpl_buf = av_buffer_allocz(fc->pkt.nb_nals * sizeof(RefPicListTab));
+        if (!frame->rpl_buf)
+            goto fail;
+
+        frame->tab_mvf_buf = av_buffer_pool_get(fc->tab_mvf_pool);
+        if (!frame->tab_mvf_buf)
+            goto fail;
+        frame->tab_mvf = (MvField *)frame->tab_mvf_buf->data;
+        //fixme: remove this
+        memset(frame->tab_mvf, 0, frame->tab_mvf_buf->size);
+
+        frame->rpl_tab_buf = av_buffer_pool_get(fc->rpl_tab_pool);
+        if (!frame->rpl_tab_buf)
+            goto fail;
+        frame->rpl_tab   = (RefPicListTab **)frame->rpl_tab_buf->data;
+        frame->ctb_count = pps->ctb_width * pps->ctb_height;
+        for (j = 0; j < frame->ctb_count; j++)
+            frame->rpl_tab[j] = (RefPicListTab *)frame->rpl_buf->data;
+
+
+        frame->progress_buf = alloc_progress();
+        if (!frame->progress_buf)
+            goto fail;
+
+#if 0
+        frame->frame->top_field_first  = s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD;
+        frame->frame->interlaced_frame = (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_TOP_FIELD) || (s->sei.picture_timing.picture_struct == AV_PICTURE_STRUCTURE_BOTTOM_FIELD);
+        if (s->avctx->hwaccel) {
+            const AVHWAccel *hwaccel = s->avctx->hwaccel;
+            av_assert0(!frame->hwaccel_picture_private);
+            if (hwaccel->frame_priv_data_size) {
+                frame->hwaccel_priv_buf = av_buffer_allocz(hwaccel->frame_priv_data_size);
+                if (!frame->hwaccel_priv_buf)
+                    goto fail;
+                frame->hwaccel_picture_private = frame->hwaccel_priv_buf->data;
+            }
+        }
+#endif
+        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->pic_output_flag)
+        ref->flags = VVC_FRAME_FLAG_OUTPUT;
+
+    if (!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->conf_win.left_offset;
+    ref->frame->crop_right  = fc->ps.pps->conf_win.right_offset;
+    ref->frame->crop_top    = fc->ps.pps->conf_win.top_offset;
+    ref->frame->crop_bottom = fc->ps.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 i, min_idx, ret;
+
+        if (no_output_of_prior_pics_flag) {
+            for (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 (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->dpb.max_dec_pic_buffering[sps->max_sublayers - 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;
+    int i;
+
+    for (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->dpb.max_dec_pic_buffering[sps->max_sublayers - 1]) {
+        for (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 (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;
+    int i;
+
+    for (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;
+        }
+    }
+
+#if 0
+    if (s->nal_unit_type != VVC_CRA_NUT && !IS_BLA(s))
+        av_log(s->avctx, AV_LOG_ERROR,
+               "Could not find ref with POC %d\n", poc);
+#endif
+    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;
+    int i, y;
+
+    frame = alloc_frame(s, fc);
+    if (!frame)
+        return NULL;
+
+    if (!s->avctx->hwaccel) {
+        if (!sps->pixel_shift) {
+            for (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 (i = 0; frame->frame->data[i]; i++)
+                for (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_progress(frame, INT_MAX);
+
+    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, const SliceContext *sc)
+{
+    VVCFrame *frame = fc->ref;
+    const VVCSH *sh = &sc->sh;
+
+    if (sc->slice_idx >= frame->rpl_buf->size / sizeof(RefPicListTab))
+        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] = (RefPicListTab *)frame->rpl_buf->data + sc->slice_idx;
+    }
+
+    frame->refPicList = (RefPicList *)frame->rpl_tab[sh->ctb_addr_in_curr_slice[0]];
+
+    return 0;
+}
+
+int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc)
+{
+    const VVCSH *sh = &sc->sh;
+    int i, ret = 0;
+
+    init_slice_rpl(fc, sc);
+
+    for (i = 0; i < 2; i++) {
+        const VVCRefPicListStruct *rpls = sh->rpls + i;
+        RefPicList *rpl = fc->ref->refPicList + i;
+        int poc_base = fc->ps.ph->poc;
+        rpl->nb_refs = 0;
+        for (int j = 0; j < rpls->num_ref_entries; j++) {
+            const VVCRefPicListStructEntry *ref = &rpls->entries[j];
+            int poc;
+            if (!ref->inter_layer_ref_pic_flag) {
+                int use_msb = 1;
+                int ref_flag;
+                if (ref->st_ref_pic_flag) {
+                    poc = poc_base + ref->delta_poc_val_st;
+                    poc_base = poc;
+                    ref_flag = VVC_FRAME_FLAG_SHORT_REF;
+                } else {
+                    use_msb = ref->lt_msb_flag;
+                    poc = ref->lt_poc;
+                    ref_flag = VVC_FRAME_FLAG_LONG_REF;
+                }
+                ret = add_candidate_ref(s, fc, rpl, poc, ref_flag, use_msb);
+                if (ret < 0)
+                    goto fail;
+            } else {
+                ret = AVERROR_PATCHWELCOME;
+                goto fail;
+            }
+        }
+        if (sh->collocated_list == i &&
+            sh->collocated_ref_idx < rpl->nb_refs)
+            fc->ref->collocated_ref = rpl->ref[sh->collocated_ref_idx];
+    }
+fail:
+    return ret;
+}
+
+int ff_vvc_frame_rpl(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc)
+{
+    int i, ret = 0;
+
+    /* clear the reference flags on all frames except the current one */
+    for (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 (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++)
+        ff_vvc_unref_frame(fc, &fc->DPB[i], 0);
+    return ret;
+}
diff --git a/libavcodec/vvc_thread.c b/libavcodec/vvc_thread.c
new file mode 100644
index 0000000000..54d1d21ee8
--- /dev/null
+++ b/libavcodec/vvc_thread.c
@@ -0,0 +1,749 @@
+/*
+ * VVC thread logic
+ *
+ * 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_thread.h"
+#include "vvc_ctu.h"
+#include "vvc_mvs.h"
+
+typedef struct VVCRowThread {
+    VVCTask reconstruct_task;
+    VVCTask deblock_v_task;
+    VVCTask sao_task;
+    atomic_int  alf_progress;
+} VVCRowThread;
+
+typedef struct VVCColThread {
+    VVCTask deblock_h_task;
+} VVCColThread;
+
+struct VVCFrameThread {
+    atomic_uchar abort;
+    atomic_uchar *avails;
+
+    VVCRowThread *rows;
+    VVCColThread *cols;
+    VVCTask *tasks;
+
+    int ctu_size;
+    int ctu_width;
+    int ctu_height;
+    int ctu_count;
+
+    //protected by lock
+    int nb_scheduled_tasks;
+    int nb_parse_tasks;
+    int alf_row_progress;
+
+    pthread_mutex_t lock;
+    pthread_cond_t  cond;
+};
+
+static int get_avail(const VVCFrameThread *ft, const int rx, const int ry, const VVCTaskType type)
+{
+    atomic_uchar *avail;
+    if (rx < 0 || ry < 0)
+        return 1;
+    avail = ft->avails + FFMIN(ry,  ft->ctu_height - 1)* ft->ctu_width + FFMIN(rx, ft->ctu_width - 1);
+    return atomic_load(avail) & (1 << type);
+}
+
+static void set_avail(const VVCFrameThread *ft, const int rx, const int ry, const VVCTaskType type)
+{
+    atomic_uchar *avail = ft->avails + ry * ft->ctu_width + rx;
+    if (rx < 0 || rx >= ft->ctu_width || ry < 0 || ry >= ft->ctu_height)
+        return;
+    atomic_fetch_or(avail, 1 << type);
+}
+
+void ff_vvc_task_init(VVCTask *task, VVCTaskType type, VVCFrameContext *fc)
+{
+    memset(task, 0, sizeof(*task));
+    task->type           = type;
+    task->fc             = fc;
+    task->decode_order   = fc->decode_order;
+}
+
+void ff_vvc_parse_task_init(VVCTask *t, VVCTaskType type, VVCFrameContext *fc,
+    SliceContext *sc, EntryPoint *ep, const int ctu_addr)
+{
+    const VVCFrameThread *ft = fc->frame_thread;
+    const int rs = sc->sh.ctb_addr_in_curr_slice[ctu_addr];
+
+    ff_vvc_task_init(t, type, fc);
+    t->sc = sc;
+    t->ep = ep;
+    t->ctb_addr_in_slice = ctu_addr;
+    t->rx = rs % ft->ctu_width;
+    t->ry = rs / ft->ctu_width;
+}
+
+VVCTask* ff_vvc_task_alloc(void)
+{
+    return av_malloc(sizeof(VVCTask));
+}
+
+static int is_parse_ready(const VVCFrameContext *fc, const VVCTask *t)
+{
+    av_assert0(t->type == VVC_TASK_TYPE_PARSE);
+    if (fc->ps.sps->entropy_coding_sync_enabled_flag && t->ry != fc->ps.pps->ctb_to_row_bd[t->ry])
+        return get_avail(fc->frame_thread, t->rx, t->ry - 1, VVC_TASK_TYPE_PARSE);
+    return 1;
+}
+
+static int is_recon_ready(const VVCFrameContext *fc, const VVCTask *t)
+{
+    const VVCFrameThread *ft = fc->frame_thread;
+
+    av_assert0(t->type == VVC_TASK_TYPE_RECON);
+    return get_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_PARSE) &&
+        get_avail(ft, t->rx + 1, t->ry - 1, VVC_TASK_TYPE_RECON) &&
+        get_avail(ft, t->rx - 1, t->ry, VVC_TASK_TYPE_RECON);
+}
+
+static int is_lmcs_ready(const VVCFrameContext *fc, const VVCTask *t)
+{
+    const VVCFrameThread *ft = fc->frame_thread;
+
+    av_assert0(t->type == VVC_TASK_TYPE_LMCS);
+    return get_avail(ft, t->rx + 1, t->ry + 1, VVC_TASK_TYPE_RECON) &&
+        get_avail(ft, t->rx, t->ry + 1, VVC_TASK_TYPE_RECON) &&
+        get_avail(ft, t->rx + 1, t->ry, VVC_TASK_TYPE_RECON);
+}
+
+static int is_deblock_v_ready(const VVCFrameContext *fc, const VVCTask *t)
+{
+    const VVCFrameThread *ft  = fc->frame_thread;
+
+    av_assert0(t->type == VVC_TASK_TYPE_DEBLOCK_V);
+    return get_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_LMCS) &&
+        get_avail(ft, t->rx + 1, t->ry, VVC_TASK_TYPE_LMCS);
+}
+
+static int is_deblock_h_ready(const VVCFrameContext *fc, const VVCTask *t)
+{
+    const VVCFrameThread *ft = fc->frame_thread;
+
+    av_assert0(t->type == VVC_TASK_TYPE_DEBLOCK_H);
+    return get_avail(ft, t->rx - 1, t->ry, VVC_TASK_TYPE_DEBLOCK_H) &&
+        get_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_DEBLOCK_V);
+}
+
+static int is_sao_ready(const VVCFrameContext *fc, const VVCTask *t)
+{
+    av_assert0(t->type == VVC_TASK_TYPE_SAO);
+    return get_avail(fc->frame_thread, t->rx + 1, t->ry - 1, VVC_TASK_TYPE_SAO) &&
+        get_avail(fc->frame_thread, t->rx - 1, t->ry + 1, VVC_TASK_TYPE_DEBLOCK_H) &&
+        get_avail(fc->frame_thread, t->rx, t->ry + 1, VVC_TASK_TYPE_DEBLOCK_H) &&
+        get_avail(fc->frame_thread, t->rx + 1, t->ry + 1, VVC_TASK_TYPE_DEBLOCK_H);
+}
+
+static int is_alf_ready(const VVCFrameContext *fc, const VVCTask *t)
+{
+    av_assert0(t->type == VVC_TASK_TYPE_ALF);
+    return 1;
+}
+
+typedef int (*is_ready_func)(const VVCFrameContext *fc, const VVCTask *t);
+
+int ff_vvc_task_ready(const Task *_t, void *user_data)
+{
+    const VVCTask *t            = (const VVCTask*)_t;
+    const VVCFrameThread *ft    = t->fc->frame_thread;
+    int ready;
+    is_ready_func is_ready[]    = {
+        is_parse_ready,
+        is_recon_ready,
+        is_lmcs_ready,
+        is_deblock_v_ready,
+        is_deblock_h_ready,
+        is_sao_ready,
+        is_alf_ready,
+    };
+
+    if (atomic_load(&ft->abort))
+        return 1;
+    ready = is_ready[t->type](t->fc, t);
+
+    return ready;
+}
+
+int ff_vvc_task_priority_higher(const Task *_a, const Task *_b)
+{
+    const VVCTask *a = (const VVCTask*)_a;
+    const VVCTask *b = (const VVCTask*)_b;
+    //order by frame decoder order
+    if (a->decode_order != b->decode_order)
+        return a->decode_order < b->decode_order;
+
+    if (a->type == b->type) {
+        // order by y
+        if (a->ry != b->ry)
+            return a->ry < b->ry;
+        // order by x
+        return a->rx < b->rx;
+    }
+    if (a->type != VVC_TASK_TYPE_PARSE && b->type != VVC_TASK_TYPE_PARSE) {
+        // order by y
+        if (a->ry != b->ry)
+            return a->ry < b->ry;
+        // order by x
+        if (a->rx != b->rx)
+            return a->rx < b->rx;
+    }
+    // order by type
+    return a->type < b->type;
+}
+
+static void add_filter_task(VVCContext *s, VVCTask *t, VVCTaskType type)
+{
+    t->type = type;
+    ff_vvc_frame_add_task(s, t);
+}
+
+static int run_parse(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
+{
+    VVCFrameContext *fc     = lc->fc;
+    const VVCPPS *pps       = fc->ps.pps;
+    SliceContext *sc        = t->sc;
+    const VVCSH *sh         = &sc->sh;
+    EntryPoint *ep          = t->ep;
+    VVCFrameThread *ft      = fc->frame_thread;
+    int ret, rs, prev_ry;
+
+    lc->sc = sc;
+    lc->ep = ep;
+
+    //reconstruct one line a time
+    rs = sh->ctb_addr_in_curr_slice[t->ctb_addr_in_slice];
+    do {
+
+        prev_ry = t->ry;
+
+        ret = ff_vvc_coding_tree_unit(lc, t->ctb_addr_in_slice, rs, t->rx, t->ry);
+        if (ret < 0)
+            return ret;
+
+        set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_PARSE);
+
+        if (fc->ps.sps->entropy_coding_sync_enabled_flag && t->rx == pps->ctb_to_col_bd[t->rx]) {
+            EntryPoint *next = ep + 1;
+            if (next < sc->eps + sc->nb_eps) {
+                memcpy(next->cabac_state, ep->cabac_state, sizeof(next->cabac_state));
+                av_assert0(!next->parse_task->type);
+                ff_vvc_frame_add_task(s, next->parse_task);
+            }
+        }
+
+        if (!t->rx)
+            ff_vvc_frame_add_task(s, &ft->rows[t->ry].reconstruct_task);
+
+        t->ctb_addr_in_slice++;
+        if (t->ctb_addr_in_slice >= ep->ctu_addr_last)
+            break;
+
+        rs = sh->ctb_addr_in_curr_slice[t->ctb_addr_in_slice];
+        t->rx = rs % ft->ctu_width;
+        t->ry = rs / ft->ctu_width;
+    } while (t->ry == prev_ry && is_parse_ready(fc, t));
+
+    if (t->ctb_addr_in_slice < ep->ctu_addr_last)
+        ff_vvc_frame_add_task(s, t);
+
+    return 0;
+}
+
+static int run_recon(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
+{
+    VVCFrameContext *fc = lc->fc;
+    VVCFrameThread *ft  = fc->frame_thread;
+
+    do {
+        const int rs = t->ry * ft->ctu_width + t->rx;
+        const int slice_idx = fc->tab.slice_idx[rs];
+
+        if (slice_idx != -1) {
+            lc->sc = fc->slices[slice_idx];
+            ff_vvc_reconstruct(lc, rs, t->rx, t->ry);
+        }
+
+        set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_RECON);
+        add_filter_task(s, ft->tasks + rs, VVC_TASK_TYPE_LMCS);
+
+        t->rx++;
+    } while (t->rx < ft->ctu_width && is_recon_ready(fc, t));
+
+    if (t->rx < ft->ctu_width)
+        ff_vvc_frame_add_task(s, t);
+    return 0;
+}
+
+static int run_lmcs(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
+{
+    VVCFrameContext *fc = lc->fc;
+    VVCFrameThread *ft  = fc->frame_thread;
+    const int ctu_size  = ft->ctu_size;
+    const int x0        = t->rx * ctu_size;
+    const int y0        = t->ry * ctu_size;
+    const int rs        = t->ry * ft->ctu_width + t->rx;
+    const int slice_idx = fc->tab.slice_idx[rs];
+
+    if (slice_idx != -1) {
+        lc->sc = fc->slices[slice_idx];
+        ff_vvc_lmcs_filter(lc, x0, y0);
+    }
+    set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_LMCS);
+    if (!t->rx)
+        add_filter_task(s, &ft->rows[t->ry].deblock_v_task, VVC_TASK_TYPE_DEBLOCK_V);
+
+    return 0;
+}
+
+static int run_deblock_v(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
+{
+    VVCFrameContext *fc = lc->fc;
+    VVCFrameThread *ft  = fc->frame_thread;
+    int rs              = t->ry * ft->ctu_width + t->rx;
+    const int ctb_size  = ft->ctu_size;
+
+    do {
+        const int x0        = t->rx * ctb_size;
+        const int y0        = t->ry * ctb_size;
+        const int slice_idx = fc->tab.slice_idx[rs];
+
+        if (slice_idx != -1) {
+            lc->sc = fc->slices[slice_idx];
+            if (!lc->sc->sh.deblocking_filter_disabled_flag) {
+                ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs);
+                ff_vvc_deblock_vertical(lc, x0, y0);
+            }
+        }
+
+        set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_DEBLOCK_V);
+
+        if (!t->ry)
+            add_filter_task(s, &ft->cols[t->rx].deblock_h_task , VVC_TASK_TYPE_DEBLOCK_H);
+
+        t->rx++;
+        rs++;
+    } while (t->rx < ft->ctu_width && is_deblock_v_ready(fc, t));
+
+    if (t->rx < ft->ctu_width)
+        ff_vvc_frame_add_task(s, t);
+
+    return 0;
+}
+
+static int run_deblock_h(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
+{
+    VVCFrameContext *fc = lc->fc;
+    VVCFrameThread *ft  = fc->frame_thread;
+    const int ctb_size  = ft->ctu_size;
+    int rs              = t->ry * ft->ctu_width + t->rx;
+
+    do {
+        const int x0 = t->rx * ctb_size;
+        const int y0 = t->ry * ctb_size;
+        const int slice_idx = fc->tab.slice_idx[rs];
+
+        if (slice_idx != -1) {
+            lc->sc = fc->slices[slice_idx];
+            if (!lc->sc->sh.deblocking_filter_disabled_flag) {
+                ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs);
+                ff_vvc_deblock_horizontal(lc, x0, y0);
+            }
+        }
+
+        set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_DEBLOCK_H);
+
+        if (!t->rx)
+            add_filter_task(s, &ft->rows[t->ry].sao_task, VVC_TASK_TYPE_SAO);
+
+        rs += ft->ctu_width;
+        t->ry++;
+    } while (t->ry < ft->ctu_height && is_deblock_h_ready(fc, t));
+
+    if (t->ry < ft->ctu_height)
+        ff_vvc_frame_add_task(s, t);
+
+    return 0;
+}
+
+static void add_alf_task(VVCContext *s, VVCTask *t)
+{
+   t->type = VVC_TASK_TYPE_ALF;
+   ff_vvc_frame_add_task(s, t);
+}
+
+static void add_alf_tasks(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
+{
+    VVCFrameContext *fc = lc->fc;
+    VVCFrameThread *ft  = fc->frame_thread;
+    VVCTask *at = ft->tasks + ft->ctu_width * t->ry + t->rx;
+    if (t->ry > 0) {
+        VVCTask *top = at - ft->ctu_width;
+        if (t->rx > 0)
+            add_alf_task(s, top - 1);
+        if (t->rx == ft->ctu_width - 1)
+            add_alf_task(s, top);
+    }
+    if (t->ry == ft->ctu_height - 1) {
+        if (t->rx > 0)
+            add_alf_task(s, at - 1);
+        if (t->rx == ft->ctu_width - 1)
+            add_alf_task(s, at);
+    }
+
+}
+
+static int run_sao(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
+{
+    VVCFrameContext *fc = lc->fc;
+    VVCFrameThread *ft  = fc->frame_thread;
+    int rs              = t->ry * fc->ps.pps->ctb_width + t->rx;
+    const int ctb_size  = ft->ctu_size;
+
+    do {
+        const int x0 = t->rx * ctb_size;
+        const int y0 = t->ry * ctb_size;
+
+        if (fc->ps.sps->sao_enabled_flag) {
+            ff_vvc_decode_neighbour(lc, x0, y0, t->rx, t->ry, rs);
+            ff_vvc_sao_filter(lc, x0, y0);
+        }
+
+        if (fc->ps.sps->alf_enabled_flag)
+            ff_vvc_alf_copy_ctu_to_hv(lc, x0, y0);
+
+        set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_SAO);
+
+        add_alf_tasks(s, lc, t);
+
+        rs++;
+        t->rx++;
+    } while (t->rx < ft->ctu_width && is_sao_ready(fc, t));
+
+    if (t->rx < ft->ctu_width)
+        ff_vvc_frame_add_task(s, t);
+
+    return 0;
+}
+
+static void report_frame_progress(VVCFrameContext *fc, VVCTask *t)
+{
+    VVCFrameThread *ft  = fc->frame_thread;
+    const int ctu_size  = ft->ctu_size;
+    int old;
+
+    if (atomic_fetch_add(&ft->rows[t->ry].alf_progress, 1) == ft->ctu_width - 1) {
+        pthread_mutex_lock(&ft->lock);
+        old = ft->alf_row_progress;
+        while (ft->alf_row_progress < ft->ctu_height && atomic_load(&ft->rows[ft->alf_row_progress].alf_progress) == ft->ctu_width)
+            ft->alf_row_progress++;
+        if (old != ft->alf_row_progress) {
+            const int progress = ft->alf_row_progress == ft->ctu_height ? INT_MAX : ft->alf_row_progress * ctu_size;
+            ff_vvc_report_progress(fc->ref, progress);
+        }
+        pthread_mutex_unlock(&ft->lock);
+    }
+}
+
+static int run_alf(VVCContext *s, VVCLocalContext *lc, VVCTask *t)
+{
+    VVCFrameContext *fc = lc->fc;
+    VVCFrameThread *ft  = fc->frame_thread;
+    const int ctu_size  = ft->ctu_size;
+    const int x0 = t->rx * ctu_size;
+    const int y0 = t->ry * ctu_size;
+
+    if (fc->ps.sps->alf_enabled_flag) {
+        const int slice_idx = CTB(fc->tab.slice_idx, t->rx, t->ry);
+        if (slice_idx != -1) {
+            lc->sc = fc->slices[slice_idx];
+            ff_vvc_alf_filter(lc, x0, y0);
+        }
+    }
+    ff_vvc_apply_dmvr_info_ctb(fc, x0, y0);
+    set_avail(ft, t->rx, t->ry, VVC_TASK_TYPE_ALF);
+    report_frame_progress(fc, t);
+
+    return 0;
+}
+
+static void finished_one_task(VVCFrameThread *ft, const VVCTaskType type)
+{
+    pthread_mutex_lock(&ft->lock);
+
+    av_assert0(ft->nb_scheduled_tasks);
+    ft->nb_scheduled_tasks--;
+
+    if (type == VVC_TASK_TYPE_PARSE) {
+        av_assert0(ft->nb_parse_tasks);
+        ft->nb_parse_tasks--;
+    }
+    pthread_cond_broadcast(&ft->cond);
+
+    pthread_mutex_unlock(&ft->lock);
+}
+
+
+#define VVC_THREAD_DEBUG
+#ifdef VVC_THREAD_DEBUG
+const static char* task_name[] = {
+    "P",
+    "R",
+    "L",
+    "V",
+    "H",
+    "S",
+    "A"
+};
+#endif
+
+typedef int (*run_func)(VVCContext *s, VVCLocalContext *lc, VVCTask *t);
+
+int ff_vvc_task_run(Task *_t, void *local_context, void *user_data)
+{
+    VVCTask *t              = (VVCTask*)_t;
+    VVCContext *s           = (VVCContext *)user_data;
+    VVCLocalContext *lc     = local_context;
+    VVCFrameThread *ft      = t->fc->frame_thread;
+    const VVCTaskType type  = t->type;
+    int ret = 0;
+    run_func run[] = {
+        run_parse,
+        run_recon,
+        run_lmcs,
+        run_deblock_v,
+        run_deblock_h,
+        run_sao,
+        run_alf,
+    };
+
+    lc->fc = t->fc;
+
+#ifdef VVC_THREAD_DEBUG
+    av_log(s->avctx, AV_LOG_DEBUG, "frame %5d, %s(%3d, %3d)\r\n", (int)t->fc->decode_order, task_name[t->type], t->rx, t->ry);
+#endif
+
+    if (!atomic_load(&ft->abort))
+        ret = run[t->type](s, lc, t);
+
+    // t->type may changed bun run(), we use a local copy of t->type
+    finished_one_task(ft, type);
+
+    return ret;
+}
+
+void ff_vvc_frame_thread_free(VVCFrameContext *fc)
+{
+    VVCFrameThread *ft = fc->frame_thread;
+
+    if (!ft)
+        return;
+
+    pthread_mutex_destroy(&ft->lock);
+    pthread_cond_destroy(&ft->cond);
+    av_freep(&ft->avails);
+    av_freep(&ft->cols);
+    av_freep(&ft->rows);
+    av_freep(&ft->tasks);
+    av_freep(&ft);
+}
+
+int ff_vvc_frame_thread_init(VVCFrameContext *fc)
+{
+    const VVCSPS *sps = fc->ps.sps;
+    const VVCPPS *pps = fc->ps.pps;
+    VVCFrameThread *ft = fc->frame_thread;
+    int ret;
+
+    if (!ft || ft->ctu_width != pps->ctb_width ||
+        ft->ctu_height != pps->ctb_height ||
+        ft->ctu_size != sps->ctb_size_y) {
+
+        ff_vvc_frame_thread_free(fc);
+        ft = av_calloc(1, sizeof(*fc->frame_thread));
+        if (!ft)
+            return AVERROR(ENOMEM);
+
+        ft->ctu_width  = fc->ps.pps->ctb_width;
+        ft->ctu_height = fc->ps.pps->ctb_height;
+        ft->ctu_count  = fc->ps.pps->ctb_count;
+        ft->ctu_size   = fc->ps.sps->ctb_size_y;
+
+        ft->rows = av_calloc(ft->ctu_height, sizeof(*ft->rows));
+        if (!ft->rows)
+            goto fail;
+
+        for (int y = 0; y < ft->ctu_height; y++) {
+            VVCRowThread *row = ft->rows + y;
+            ff_vvc_task_init(&row->deblock_v_task, VVC_TASK_TYPE_DEBLOCK_V, fc);
+            row->deblock_v_task.ry = y;
+            ff_vvc_task_init(&row->sao_task, VVC_TASK_TYPE_SAO, fc);
+            row->sao_task.ry = y;
+            ff_vvc_task_init(&row->reconstruct_task, VVC_TASK_TYPE_RECON, fc);
+            row->reconstruct_task.ry = y;
+        }
+
+        ft->cols = av_calloc(ft->ctu_width, sizeof(*ft->cols));
+        if (!ft->cols)
+            goto fail;
+        for (int x = 0; x < ft->ctu_width; x++) {
+            VVCColThread *col = ft->cols + x;
+            ff_vvc_task_init(&col->deblock_h_task, VVC_TASK_TYPE_DEBLOCK_H, fc);
+            col->deblock_h_task.rx = x;
+        }
+
+        ft->avails = av_calloc(ft->ctu_count, sizeof(*ft->avails));
+        if (!ft->avails)
+            goto fail;
+
+        ft->tasks = av_calloc(ft->ctu_count, sizeof(*ft->tasks));
+        if (!ft->tasks)
+            goto fail;
+        for (int rs = 0; rs < ft->ctu_count; rs++) {
+            VVCTask *t = ft->tasks + rs;
+            t->rx = rs % ft->ctu_width;
+            t->ry = rs / ft->ctu_width;
+            t->fc = fc;
+        }
+
+        if ((ret = pthread_cond_init(&ft->cond, NULL)))
+            goto fail;
+
+        if ((ret = pthread_mutex_init(&ft->lock, NULL))) {
+            pthread_cond_destroy(&ft->cond);
+            goto fail;
+        }
+    }
+
+    ft->abort = 0;
+    for (int y = 0; y < ft->ctu_height; y++) {
+        VVCRowThread *row = ft->rows + y;
+
+        row->reconstruct_task.rx = 0;
+        row->alf_progress = 0;
+        row->deblock_v_task.rx = 0;
+        row->sao_task.rx = 0;
+    }
+
+    for (int x = 0; x < ft->ctu_width; x++) {
+        VVCColThread *col = ft->cols + x;
+        col->deblock_h_task.ry = 0;
+    }
+
+    for (int rs = 0; rs < ft->ctu_count; rs++) {
+        ft->avails[rs] = 0;
+        ft->tasks[rs].decode_order = fc->decode_order;
+    }
+
+    ft->alf_row_progress = 0;
+    fc->frame_thread = ft;
+
+    return 0;
+
+fail:
+    if (ft) {
+        av_freep(&ft->avails);
+        av_freep(&ft->cols);
+        av_freep(&ft->rows);
+        av_freep(&ft->tasks);
+        av_freep(&ft);
+    }
+
+    return AVERROR(ENOMEM);
+}
+
+void ff_vvc_frame_add_task(VVCContext *s, VVCTask *t)
+{
+    VVCFrameContext *fc = t->fc;
+    VVCFrameThread *ft  = fc->frame_thread;
+
+    pthread_mutex_lock(&ft->lock);
+
+    ft->nb_scheduled_tasks++;
+    if (t->type == VVC_TASK_TYPE_PARSE)
+        ft->nb_parse_tasks++;
+
+    pthread_mutex_unlock(&ft->lock);
+
+    ff_executor_execute(s->executor, &t->task);
+}
+
+void ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc)
+{
+    VVCFrameThread *ft = fc->frame_thread;
+    int check_missed_slices = 1;
+
+    pthread_mutex_lock(&ft->lock);
+
+    while (ft->nb_scheduled_tasks) {
+        if (check_missed_slices && !ft->nb_parse_tasks) {
+            // abort for missed slices
+            for (int rs = 0; rs < ft->ctu_count; rs++){
+                atomic_uchar mask = 1 << VVC_TASK_TYPE_PARSE;
+                if (!(atomic_load(ft->avails + rs) & mask)) {
+                    atomic_store(&ft->abort, 1);
+                    // maybe all thread are waiting, let us wake up them
+                    ff_executor_wakeup(s->executor);
+                    break;
+                }
+            }
+            check_missed_slices = 0;
+        }
+        pthread_cond_wait(&ft->cond, &ft->lock);
+    }
+
+    pthread_mutex_unlock(&ft->lock);
+    ff_vvc_report_progress(fc->ref, INT_MAX);
+
+#ifdef VVC_THREAD_DEBUG
+    av_log(s->avctx, AV_LOG_DEBUG, "frame %5d done\r\n", (int)fc->decode_order);
+#endif
+
+}
+
+void ff_vvc_report_progress(VVCFrame *frame, int n)
+{
+    FrameProgress *p = (FrameProgress*)frame->progress_buf->data;
+
+    pthread_mutex_lock(&p->lock);
+
+    av_assert0(p->progress < n || p->progress == INT_MAX);
+    p->progress = n;
+
+    pthread_cond_broadcast(&p->cond);
+    pthread_mutex_unlock(&p->lock);
+}
+
+void ff_vvc_await_progress(VVCFrame *frame, int n)
+{
+    FrameProgress *p = (FrameProgress*)frame->progress_buf->data;
+
+    pthread_mutex_lock(&p->lock);
+
+    // +1 for progress default value 0
+    while (p->progress < n + 1)
+        pthread_cond_wait(&p->cond, &p->lock);
+
+    pthread_mutex_unlock(&p->lock);
+}
diff --git a/libavcodec/vvc_thread.h b/libavcodec/vvc_thread.h
new file mode 100644
index 0000000000..3f1a847250
--- /dev/null
+++ b/libavcodec/vvc_thread.h
@@ -0,0 +1,77 @@
+/*
+ * VVC thread logic
+ *
+ * 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_THREAD_H
+#define AVCODEC_VVC_THREAD_H
+
+#include "vvcdec.h"
+
+typedef enum VVCTaskType {
+    VVC_TASK_TYPE_PARSE,
+    VVC_TASK_TYPE_RECON,
+    VVC_TASK_TYPE_LMCS,
+    VVC_TASK_TYPE_DEBLOCK_V,
+    VVC_TASK_TYPE_DEBLOCK_H,
+    VVC_TASK_TYPE_SAO,
+    VVC_TASK_TYPE_ALF,
+    VVC_TASK_TYPE_LAST
+} VVCTaskType;
+
+struct VVCTask {
+    union {
+        VVCTask *next;                //for executor debug only
+        Task task;
+    };
+
+    VVCTaskType type;
+    uint64_t decode_order;
+
+    // ctu x, y in raster order
+    int rx, ry;
+    VVCFrameContext *fc;
+
+    // reconstruct task only
+    SliceContext *sc;
+    EntryPoint *ep;
+    int ctb_addr_in_slice;
+};
+
+void ff_vvc_task_init(VVCTask *task, VVCTaskType type, VVCFrameContext *fc);
+void ff_vvc_parse_task_init(VVCTask *task, VVCTaskType type, VVCFrameContext *fc,
+    SliceContext *sc,  EntryPoint *ep, int ctu_addr);
+VVCTask* ff_vvc_task_alloc(void);
+
+int ff_vvc_task_ready(const Task* t, void* user_data);
+int ff_vvc_task_priority_higher(const Task *a, const Task *b);
+int ff_vvc_task_run(Task *t, void *local_context, void *user_data);
+
+int ff_vvc_frame_thread_init(VVCFrameContext *fc);
+void ff_vvc_frame_thread_free(VVCFrameContext *fc);
+
+void ff_vvc_frame_wait(VVCContext *s, VVCFrameContext *fc);
+
+void ff_vvc_frame_add_task(VVCContext *s, VVCTask *t);
+
+void ff_vvc_report_progress(VVCFrame *frame, int n);
+void ff_vvc_await_progress(VVCFrame *frame, int n);
+
+#endif // AVCODEC_VVC_THREAD_H
\ No newline at end of file
diff --git a/libavcodec/vvcdec.c b/libavcodec/vvcdec.c
new file mode 100644
index 0000000000..f198b4d137
--- /dev/null
+++ b/libavcodec/vvcdec.c
@@ -0,0 +1,1252 @@
+/*
+ * VVC video Decoder
+ *
+ * Copyright (C) 2021 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 "config_components.h"
+
+#include "libavutil/attributes.h"
+#include "libavutil/common.h"
+#include "libavutil/cpu.h"
+#include "libavutil/display.h"
+#include "libavutil/internal.h"
+#include "libavutil/mastering_display_metadata.h"
+#include "libavutil/md5.h"
+#include "libavutil/opt.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/stereo3d.h"
+#include "libavutil/timecode.h"
+
+#include "bswapdsp.h"
+#include "bytestream.h"
+#include "codec_internal.h"
+#include "decode.h"
+#include "golomb.h"
+#include "vvc.h"
+#include "vvcdec.h"
+#include "vvc_ctu.h"
+#include "vvc_data.h"
+#include "vvc_thread.h"
+#include "profiles.h"
+#include "thread.h"
+#include "threadframe.h"
+
+static int vvc_frame_start(VVCContext *s, VVCFrameContext *fc, SliceContext *sc)
+{
+    VVCPH *ph = s->ps.ph;
+    const VVCSH *sh = &sc->sh;
+    int ret;
+    if (s->no_output_before_recovery_flag) {
+        if (IS_GDR(s))
+            s->gdr_recovery_point_poc = ph->poc + ph->recovery_poc_cnt;
+        if (!GDR_IS_RECOVERED(s) && s->gdr_recovery_point_poc <= ph->poc)
+            GDR_SET_RECOVERED(s);
+    }
+
+    // 8.3.1 Decoding process for picture order count
+    if (!s->temporal_id && !ph->non_ref_pic_flag && !(IS_RASL(s) && IS_RADL(s)))
+        s->pocTid0 = ph->poc;
+
+    if ((ret = ff_vvc_set_new_ref(s, fc, &fc->frame)) < 0)
+        goto fail;
+
+    if (!IS_IDR(s))
+        ff_vvc_bump_frame(s, fc);
+
+    av_frame_unref(fc->output_frame);
+
+    if ((ret = ff_vvc_output_frame(s, fc, fc->output_frame, sh->no_output_of_prior_pics_flag, 0)) < 0)
+        goto fail;
+
+    if ((ret = ff_vvc_frame_rpl(s, fc, sc)) < 0)
+        goto fail;
+
+    if ((ret = ff_vvc_frame_thread_init(fc)) < 0)
+        goto fail;
+    return 0;
+fail:
+    if (fc->ref)
+        ff_vvc_unref_frame(fc, fc->ref, ~0);
+    fc->ref = NULL;
+    return ret;
+}
+
+void ff_vvc_ctu_free_cus(CTU *ctu)
+{
+    while (ctu->cus) {
+        CodingUnit *cu      = ctu->cus;
+        AVBufferRef *buf    = cu->buf;
+
+        ctu->cus = ctu->cus->next;
+        av_buffer_unref(&buf);
+    }
+}
+
+static void ctb_arrays_free(VVCFrameContext *fc)
+{
+    av_freep(&fc->tab.deblock);
+    av_freep(&fc->tab.sao);
+    av_freep(&fc->tab.alf);
+    av_freep(&fc->tab.slice_idx);
+    av_freep(&fc->tab.coeffs);
+    if (fc->tab.ctus) {
+        for (int i = 0; i < fc->tab.ctu_count; i++)
+            ff_vvc_ctu_free_cus(fc->tab.ctus + i);
+        av_freep(&fc->tab.ctus);
+    }
+    av_buffer_pool_uninit(&fc->rpl_tab_pool);
+}
+
+static int ctb_arrays_init(VVCFrameContext *fc, const int ctu_count, const int ctu_size)
+{
+    if (fc->tab.ctu_count != ctu_count || fc->tab.ctu_size != ctu_size) {
+        ctb_arrays_free(fc);
+        fc->tab.deblock         = av_calloc(ctu_count, sizeof(*fc->tab.deblock));
+        fc->tab.sao             = av_calloc(ctu_count, sizeof(*fc->tab.sao));
+        fc->tab.alf             = av_calloc(ctu_count, sizeof(*fc->tab.alf));
+        fc->tab.ctus            = av_calloc(ctu_count, sizeof(fc->tab.ctus));
+        fc->tab.slice_idx   = av_malloc(ctu_count * sizeof(*fc->tab.slice_idx));
+        if (!fc->tab.deblock || !fc->tab.sao || !fc->tab.alf || !fc->tab.ctus || !fc->tab.slice_idx )
+            return AVERROR(ENOMEM);
+        fc->tab.coeffs = av_malloc(ctu_count * sizeof(*fc->tab.coeffs) * ctu_size * VVC_MAX_SAMPLE_ARRAYS);
+        if (!fc->tab.coeffs)
+            return AVERROR(ENOMEM);
+        fc->rpl_tab_pool = av_buffer_pool_init(ctu_count * sizeof(RefPicListTab), av_buffer_allocz);
+        if (!fc->rpl_tab_pool)
+            return AVERROR(ENOMEM);
+    } else {
+        memset(fc->tab.deblock, 0, ctu_count * sizeof(*fc->tab.deblock));
+        memset(fc->tab.sao, 0, ctu_count * sizeof(*fc->tab.sao));
+        memset(fc->tab.alf, 0, ctu_count * sizeof(*fc->tab.alf));
+        for (int i = 0; i < fc->tab.ctu_count; i++)
+            ff_vvc_ctu_free_cus(fc->tab.ctus + i);
+        memset(fc->tab.ctus, 0, ctu_count * sizeof(fc->tab.ctus));
+    }
+    memset(fc->tab.slice_idx, -1, ctu_count * sizeof(*fc->tab.slice_idx));
+
+    return 0;
+}
+
+static void min_cb_arrays_free(VVCFrameContext *fc)
+{
+    for (int i = LUMA; i <= CHROMA; i++) {
+        av_freep(&fc->tab.cb_pos_x[i]);
+        av_freep(&fc->tab.cb_pos_y[i]);
+        av_freep(&fc->tab.cb_width[i]);
+        av_freep(&fc->tab.cb_height[i]);
+        av_freep(&fc->tab.cqt_depth[i]);
+        av_freep(&fc->tab.cpm[i]);
+        av_freep(&fc->tab.cp_mv[i]);
+    }
+
+    av_freep(&fc->tab.ipm);
+    av_freep(&fc->tab.imf);
+    av_freep(&fc->tab.imtf);
+    av_freep(&fc->tab.imm);
+    av_freep(&fc->tab.skip);
+}
+
+static int min_cb_arrays_init(VVCFrameContext *fc, const int pic_size_in_min_cb)
+{
+    if (fc->tab.pic_size_in_min_cb != pic_size_in_min_cb) {
+        min_cb_arrays_free(fc);
+        for (int i = LUMA; i <= CHROMA; i++) {
+            fc->tab.cb_pos_x[i]  = av_mallocz(pic_size_in_min_cb * sizeof(int));
+            fc->tab.cb_pos_y[i]  = av_mallocz(pic_size_in_min_cb * sizeof(int));
+            fc->tab.cb_width[i]  = av_mallocz(pic_size_in_min_cb);
+            fc->tab.cb_height[i] = av_mallocz(pic_size_in_min_cb);
+            fc->tab.cqt_depth[i] = av_mallocz(pic_size_in_min_cb);
+            if (!fc->tab.cb_pos_x[i] || !fc->tab.cb_pos_y[i] || !fc->tab.cb_width[i] || !fc->tab.cb_height[i] || !fc->tab.cqt_depth[i])
+                return AVERROR(ENOMEM);
+
+            fc->tab.cpm[i]   = av_mallocz(pic_size_in_min_cb);
+            fc->tab.cp_mv[i] = av_mallocz(pic_size_in_min_cb * sizeof(Mv) * MAX_CONTROL_POINTS);
+            if (!fc->tab.cpm[i] || !fc->tab.cp_mv[i])
+                return AVERROR(ENOMEM);
+        }
+
+        fc->tab.ipm  = av_mallocz(pic_size_in_min_cb);
+        fc->tab.imf  = av_mallocz(pic_size_in_min_cb);
+        fc->tab.imtf = av_mallocz(pic_size_in_min_cb);
+        fc->tab.imm  = av_mallocz(pic_size_in_min_cb);
+        fc->tab.skip = av_mallocz(pic_size_in_min_cb);
+        if (!fc->tab.ipm || !fc->tab.imf || !fc->tab.imtf || !fc->tab.imm || !fc->tab.skip)
+            return AVERROR(ENOMEM);
+    } else {
+        for (int i = LUMA; i <= CHROMA; i++) {
+            memset(fc->tab.cb_pos_x[i], 0, pic_size_in_min_cb * sizeof(int));
+            memset(fc->tab.cb_pos_y[i], 0, pic_size_in_min_cb * sizeof(int));
+            memset(fc->tab.cb_width[i], 0, pic_size_in_min_cb);
+            memset(fc->tab.cb_height[i], 0, pic_size_in_min_cb);
+            memset(fc->tab.cqt_depth[i], 0, pic_size_in_min_cb);
+            memset(fc->tab.cpm[i], 0, pic_size_in_min_cb);
+            memset(fc->tab.cp_mv[i], 0, pic_size_in_min_cb * sizeof(Mv) * MAX_CONTROL_POINTS);
+        }
+
+        memset(fc->tab.ipm, 0, pic_size_in_min_cb);
+        memset(fc->tab.imf, 0, pic_size_in_min_cb);
+        memset(fc->tab.imtf, 0, pic_size_in_min_cb);
+        memset(fc->tab.imm, 0, pic_size_in_min_cb);
+        memset(fc->tab.skip, 0, pic_size_in_min_cb);
+    }
+    return 0;
+}
+
+static void min_tu_arrays_free(VVCFrameContext *fc)
+{
+    for (int i = LUMA; i <= CHROMA; i++) {
+        av_freep(&fc->tab.tb_pos_x0[i]);
+        av_freep(&fc->tab.tb_pos_y0[i]);
+        av_freep(&fc->tab.tb_width[i]);
+        av_freep(&fc->tab.tb_height[i]);
+    }
+
+    for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) {
+        av_freep(&fc->tab.qp[i]);
+        av_freep(&fc->tab.tu_coded_flag[i]);
+    }
+
+    av_freep(&fc->tab.tu_joint_cbcr_residual_flag);
+}
+
+static int min_tu_arrays_init(VVCFrameContext *fc, const int pic_size_in_min_tu)
+{
+    if (fc->tab.pic_size_in_min_tu != pic_size_in_min_tu) {
+        min_tu_arrays_free(fc);
+        for (int i = LUMA; i <= CHROMA; i++) {
+            fc->tab.tb_pos_x0[i] = av_mallocz(pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_x0[0]));
+            fc->tab.tb_pos_y0[i] = av_mallocz(pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_y0[0])) ;
+            fc->tab.tb_width[i]  = av_mallocz(pic_size_in_min_tu);
+            fc->tab.tb_height[i] = av_mallocz(pic_size_in_min_tu);
+            if (!fc->tab.tb_pos_x0[i] || !fc->tab.tb_pos_y0[i] || !fc->tab.tb_width[i] || !fc->tab.tb_height[i])
+                return AVERROR(ENOMEM);
+        }
+
+        for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) {
+            fc->tab.tu_coded_flag[i] = av_mallocz(pic_size_in_min_tu);
+            if (!fc->tab.tu_coded_flag[i])
+                return AVERROR(ENOMEM);
+
+            fc->tab.qp[i] = av_mallocz(pic_size_in_min_tu);
+            if (!fc->tab.qp[i])
+                return AVERROR(ENOMEM);
+        }
+
+        fc->tab.tu_joint_cbcr_residual_flag  = av_mallocz(pic_size_in_min_tu);
+        if (!fc->tab.tu_joint_cbcr_residual_flag)
+            return AVERROR(ENOMEM);
+    } else {
+        for (int i = LUMA; i <= CHROMA; i++) {
+            memset(fc->tab.tb_pos_x0[i], 0, pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_x0[0]));
+            memset(fc->tab.tb_pos_y0[i], 0, pic_size_in_min_tu * sizeof(*fc->tab.tb_pos_y0[0])) ;
+            memset(fc->tab.tb_width[i], 0, pic_size_in_min_tu);
+            memset(fc->tab.tb_height[i], 0, pic_size_in_min_tu);
+        }
+
+        for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) {
+            memset(fc->tab.tu_coded_flag[i], 0, pic_size_in_min_tu);
+            memset(fc->tab.qp[i], 0, pic_size_in_min_tu);
+        }
+        memset(fc->tab.tu_joint_cbcr_residual_flag, 0, pic_size_in_min_tu);
+    }
+    return 0;
+}
+
+static void min_pu_arrays_free(VVCFrameContext *fc)
+{
+    av_freep(&fc->tab.msf);
+    av_freep(&fc->tab.iaf);
+    av_freep(&fc->tab.mmi);
+    av_freep(&fc->tab.dmvr);
+    av_buffer_pool_uninit(&fc->tab_mvf_pool);
+}
+
+static int min_pu_arrays_init(VVCFrameContext *fc, const int pic_size_in_min_pu)
+{
+    if (fc->tab.pic_size_in_min_pu != pic_size_in_min_pu) {
+        min_pu_arrays_free(fc);
+        fc->tab.msf  = av_mallocz(pic_size_in_min_pu);
+        fc->tab.iaf  = av_mallocz(pic_size_in_min_pu);
+        fc->tab.mmi  = av_mallocz(pic_size_in_min_pu);
+        fc->tab.dmvr = av_calloc(pic_size_in_min_pu, sizeof(*fc->tab.dmvr));
+        if (!fc->tab.msf || !fc->tab.iaf || !fc->tab.mmi || !fc->tab.dmvr)
+            return AVERROR(ENOMEM);
+        fc->tab_mvf_pool  = av_buffer_pool_init(pic_size_in_min_pu * sizeof(MvField), av_buffer_allocz);
+        if (!fc->tab_mvf_pool)
+            return AVERROR(ENOMEM);
+    } else {
+        memset(fc->tab.msf, 0, pic_size_in_min_pu);
+        memset(fc->tab.iaf, 0, pic_size_in_min_pu);
+        memset(fc->tab.mmi, 0, pic_size_in_min_pu);
+        memset(fc->tab.dmvr, 0, pic_size_in_min_pu * sizeof(*fc->tab.dmvr));
+    }
+
+    return 0;
+}
+
+static void bs_arrays_free(VVCFrameContext *fc)
+{
+    for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) {
+        av_freep(&fc->tab.horizontal_bs[i]);
+        av_freep(&fc->tab.vertical_bs[i]);
+    }
+    av_freep(&fc->tab.horizontal_q);
+    av_freep(&fc->tab.horizontal_p);
+    av_freep(&fc->tab.vertical_p);
+    av_freep(&fc->tab.vertical_q);
+}
+
+static int bs_arrays_init(VVCFrameContext *fc, const int bs_width, const int bs_height)
+{
+    if (fc->tab.bs_width != bs_width || fc->tab.bs_height != bs_height) {
+        bs_arrays_free(fc);
+        for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) {
+            fc->tab.horizontal_bs[i] = av_calloc(bs_width, bs_height);
+            fc->tab.vertical_bs[i]   = av_calloc(bs_width, bs_height);
+            if (!fc->tab.horizontal_bs[i] || !fc->tab.vertical_bs[i])
+                return AVERROR(ENOMEM);
+        }
+        fc->tab.horizontal_q = av_calloc(bs_width, bs_height);
+        fc->tab.horizontal_p = av_calloc(bs_width, bs_height);
+        fc->tab.vertical_p   = av_calloc(bs_width, bs_height);
+        fc->tab.vertical_q   = av_calloc(bs_width, bs_height);
+        if (!fc->tab.horizontal_q || !fc->tab.horizontal_p || !fc->tab.vertical_p || !fc->tab.vertical_q)
+            return AVERROR(ENOMEM);
+    } else {
+        for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) {
+            memset(fc->tab.horizontal_bs[i], 0, bs_width * bs_height);
+            memset(fc->tab.vertical_bs[i], 0, bs_width * bs_height);
+        }
+        memset(fc->tab.horizontal_q, 0, bs_width * bs_height);
+        memset(fc->tab.horizontal_p, 0, bs_width * bs_height);
+        memset(fc->tab.vertical_p, 0, bs_width * bs_height);
+        memset(fc->tab.vertical_q, 0, bs_width * bs_height);
+    }
+    return 0;
+}
+
+static void pixel_buffer_free(VVCFrameContext *fc)
+{
+    for (int i = 0; i < VVC_MAX_SAMPLE_ARRAYS; i++) {
+        av_freep(&fc->tab.sao_pixel_buffer_h[i]);
+        av_freep(&fc->tab.sao_pixel_buffer_v[i]);
+        for (int j = 0; j < 2; j++) {
+            av_freep(&fc->tab.alf_pixel_buffer_h[i][j]);
+            av_freep(&fc->tab.alf_pixel_buffer_v[i][j]);
+        }
+    }
+}
+
+static int pixel_buffer_init(VVCFrameContext *fc, const int width, const int height,
+    const int ctu_width, const int ctu_height, const int chroma_format_idc, const int ps)
+{
+    const VVCSPS *sps = fc->ps.sps;
+    const int c_end   = chroma_format_idc ? VVC_MAX_SAMPLE_ARRAYS : 1;
+
+    if (fc->tab.chroma_format_idc != chroma_format_idc ||
+        fc->tab.width != width || fc->tab.height != height ||
+        fc->tab.ctu_width != ctu_width || fc->tab.ctu_height != ctu_height) {
+        pixel_buffer_free(fc);
+        for(int c_idx = 0; c_idx < c_end; c_idx++) {
+            const int w = width >> sps->hshift[c_idx];
+            const int h = height >> sps->vshift[c_idx];
+            fc->tab.sao_pixel_buffer_h[c_idx] = av_malloc((w * 2 * ctu_height) << ps);
+            fc->tab.sao_pixel_buffer_v[c_idx] = av_malloc((h * 2 * ctu_width)  << ps);
+            if (!fc->tab.sao_pixel_buffer_h[c_idx] || !fc->tab.sao_pixel_buffer_v[c_idx])
+                return AVERROR(ENOMEM);
+        }
+
+        for(int c_idx = 0; c_idx < c_end; c_idx++) {
+            const int w = width >> sps->hshift[c_idx];
+            const int h = height >> sps->vshift[c_idx];
+            const int border_pixels = c_idx ? ALF_BORDER_CHROMA : ALF_BORDER_LUMA;
+            for (int i = 0; i < 2; i++) {
+                fc->tab.alf_pixel_buffer_h[c_idx][i] = av_malloc((w * border_pixels * ctu_height) << ps);
+                fc->tab.alf_pixel_buffer_v[c_idx][i] = av_malloc(h * ALF_PADDING_SIZE * ctu_width);
+                if (!fc->tab.alf_pixel_buffer_h[c_idx][i] || !fc->tab.alf_pixel_buffer_v[c_idx][i])
+                    return AVERROR(ENOMEM);
+            }
+        }
+    }
+    return 0;
+}
+
+static void pic_arrays_free(VVCFrameContext *fc)
+{
+    ctb_arrays_free(fc);
+    min_cb_arrays_free(fc);
+    min_pu_arrays_free(fc);
+    min_tu_arrays_free(fc);
+    bs_arrays_free(fc);
+    av_buffer_pool_uninit(&fc->cu_pool);
+    pixel_buffer_free(fc);
+
+    for (int i = 0; i < 2; i++)
+        av_freep(&fc->tab.msm[i]);
+    av_freep(&fc->tab.ispmf);
+
+    fc->tab.ctu_count = 0;
+    fc->tab.ctu_size  = 0;
+    fc->tab.pic_size_in_min_cb = 0;
+    fc->tab.pic_size_in_min_pu = 0;
+    fc->tab.pic_size_in_min_tu = 0;
+    fc->tab.width              = 0;
+    fc->tab.height             = 0;
+    fc->tab.ctu_width          = 0;
+    fc->tab.ctu_height         = 0;
+    fc->tab.bs_width           = 0;
+    fc->tab.bs_height          = 0;
+}
+
+static int pic_arrays_init(VVCContext *s, VVCFrameContext *fc)
+{
+    const VVCSPS *sps               = fc->ps.sps;
+    const VVCPPS *pps               = fc->ps.pps;
+    const int ctu_size              = 1 << sps->ctb_log2_size_y << sps->ctb_log2_size_y;
+    const int pic_size_in_min_cb    = pps->min_cb_width * pps->min_cb_height;
+    const int pic_size_in_min_pu    = pps->min_pu_width * pps->min_pu_height;
+    const int pic_size_in_min_tu    = pps->min_tb_width * pps->min_tb_height;
+    const int w32                   = AV_CEIL_RSHIFT(pps->width,  5);
+    const int h32                   = AV_CEIL_RSHIFT(pps->height,  5);
+    const int w64                   = AV_CEIL_RSHIFT(pps->width,  6);
+    const int h64                   = AV_CEIL_RSHIFT(pps->height,  6);
+    const int bs_width              = (fc->ps.pps->width >> 2) + 1;
+    const int bs_height             = (fc->ps.pps->height >> 2) + 1;
+    int ret;
+
+    if ((ret = ctb_arrays_init(fc, pps->ctb_count, ctu_size)) < 0)
+        goto fail;
+
+    if ((ret = min_cb_arrays_init(fc, pic_size_in_min_cb)) < 0)
+        goto fail;
+
+    if ((ret = min_pu_arrays_init(fc, pic_size_in_min_pu)) < 0)
+        goto fail;
+
+    if ((ret = min_tu_arrays_init(fc, pic_size_in_min_tu)) < 0)
+        goto fail;
+
+    if ((ret = bs_arrays_init(fc, bs_width, bs_height)) < 0)
+        goto fail;
+
+    if ((ret = pixel_buffer_init(fc, pps->width, pps->height, pps->ctb_width, pps->ctb_height,
+        sps->chroma_format_idc, sps->pixel_shift)) < 0)
+        goto fail;
+
+    if (AV_CEIL_RSHIFT(fc->tab.width,  5) != w32 || AV_CEIL_RSHIFT(fc->tab.height,  5) != h32) {
+        for (int i = LUMA; i <= CHROMA; i++) {
+            av_freep(&fc->tab.msm[i]);
+            fc->tab.msm[i] = av_calloc(w32, h32);
+            if (!fc->tab.msm[i])
+                goto fail;
+        }
+    } else {
+        for (int i = LUMA; i <= CHROMA; i++)
+            memset(fc->tab.msm[i], 0, w32 * h32);
+    }
+    if (AV_CEIL_RSHIFT(fc->tab.width,  6) != w64 || AV_CEIL_RSHIFT(fc->tab.height,  6) != h64) {
+        av_freep(&fc->tab.ispmf);
+        fc->tab.ispmf = av_calloc(w64, h64);
+        if (!fc->tab.ispmf)
+            goto fail;
+    } else {
+        memset(fc->tab.ispmf, 0, w64 * h64);
+    }
+
+    if (!fc->cu_pool) {
+        fc->cu_pool = av_buffer_pool_init(sizeof(CodingUnit), NULL);
+        if (!fc->cu_pool)
+            goto fail;
+    }
+
+    fc->tab.ctu_count = pps->ctb_count;
+    fc->tab.ctu_size  = ctu_size;
+    fc->tab.pic_size_in_min_cb = pic_size_in_min_cb;
+    fc->tab.pic_size_in_min_pu = pic_size_in_min_pu;
+    fc->tab.pic_size_in_min_tu = pic_size_in_min_tu;
+    fc->tab.width              = pps->width;
+    fc->tab.height             = pps->height;
+    fc->tab.ctu_width          = pps->ctb_width;
+    fc->tab.ctu_height         = pps->ctb_height;
+    fc->tab.chroma_format_idc  = sps->chroma_format_idc;
+    fc->tab.pixel_shift        = sps->pixel_shift;
+    fc->tab.bs_width           = bs_width;
+    fc->tab.bs_height          = bs_height;
+
+    return 0;
+fail:
+    pic_arrays_free(fc);
+    return ret;
+}
+
+static int min_positive(const int idx, const int diff, const int min_diff)
+{
+    return diff > 0 && (idx < 0 || diff < min_diff);
+}
+
+static int max_negtive(const int idx, const int diff, const int max_diff)
+{
+    return diff < 0 && (idx < 0 || diff > max_diff);
+}
+
+typedef int (*smvd_find_fxn)(const int idx, const int diff, const int old_diff);
+
+static int8_t smvd_find(const VVCFrameContext *fc, const VVCSH *sh, int lx, smvd_find_fxn find)
+{
+    const RefPicList *rpl   = fc->ref->refPicList + lx;
+    const int poc           = fc->ref->poc;
+    int8_t idx              = -1;
+    int old_diff            = -1;
+    for (int i = 0; i < sh->nb_refs[lx]; i++) {
+        if (!rpl->isLongTerm[i]) {
+            int diff = poc - rpl->list[i];
+            if (find(idx, diff, old_diff)) {
+                idx = i;
+                old_diff = diff;
+            }
+        }
+    }
+    return idx;
+}
+
+static void vvc_smvd_ref_idx(const VVCFrameContext *fc, VVCSH *sh)
+{
+    if (IS_B(sh)) {
+        sh->ref_idx_sym[0] = smvd_find(fc, sh, 0, min_positive);
+        sh->ref_idx_sym[1] = smvd_find(fc, sh, 1, max_negtive);
+        if (sh->ref_idx_sym[0] == -1 || sh->ref_idx_sym[1] == -1) {
+            sh->ref_idx_sym[0] = smvd_find(fc, sh, 0, max_negtive);
+            sh->ref_idx_sym[1] = smvd_find(fc, sh, 1, min_positive);
+        }
+    }
+}
+
+static void eps_free(SliceContext *slice)
+{
+    if (slice->eps) {
+        for (int j = 0; j < slice->nb_eps; j++)
+            av_free(slice->eps[j].parse_task);
+        av_freep(&slice->eps);
+    }
+}
+
+static void slices_free(VVCFrameContext *fc)
+{
+    if (fc->slices) {
+        for (int i = 0; i < fc->nb_slices_allocated; i++) {
+            SliceContext *slice = fc->slices[i];
+            if (slice) {
+                eps_free(slice);
+                av_free(slice);
+            }
+        }
+        av_freep(&fc->slices);
+    }
+    fc->nb_slices_allocated = 0;
+    fc->nb_slices = 0;
+}
+
+static int slices_realloc(VVCFrameContext *fc)
+{
+    void *p;
+    const int size = (fc->nb_slices_allocated + 1) * 3 / 2;
+
+    if (fc->nb_slices < fc->nb_slices_allocated)
+        return 0;
+
+    p = av_realloc(fc->slices, size * sizeof(*fc->slices));
+    if (!p)
+        return AVERROR(ENOMEM);
+
+    fc->slices = p;
+    for (int i = fc->nb_slices_allocated; i < size; i++) {
+        fc->slices[i] = av_calloc(1, sizeof(*fc->slices[0]));
+        if (!fc->slices[i]) {
+            for (int j = fc->nb_slices_allocated; j < i; j++)
+                av_freep(&fc->slices[j]);
+            return AVERROR(ENOMEM);
+        }
+        fc->slices[i]->slice_idx = i;
+    }
+    fc->nb_slices_allocated = size;
+    return 0;
+}
+
+static void ep_init_cabac_decoder(SliceContext *sc, const int index, const H2645NAL *nal, GetBitContext *gb)
+{
+    const VVCSH *sh = &sc->sh;
+    EntryPoint *ep  = sc->eps + index;
+    int size;
+
+    if (index < sh->num_entry_points) {
+        int skipped = 0;
+        int64_t start =  (gb->index >> 3);
+        int64_t end = start + sh->entry_point_offset[index];
+        while (skipped < nal->skipped_bytes && nal->skipped_bytes_pos[skipped] <= start) {
+            skipped++;
+        }
+        while (skipped < nal->skipped_bytes && nal->skipped_bytes_pos[skipped] < end) {
+            end--;
+            skipped++;
+        }
+        size = end - start;
+    } else {
+        size = get_bits_left(gb) / 8;
+    }
+    ff_init_cabac_decoder (&ep->cc, gb->buffer + get_bits_count(gb) / 8, size);
+    skip_bits(gb, size * 8);
+}
+
+static int init_slice_context(SliceContext *sc, VVCFrameContext *fc, const H2645NAL *nal, GetBitContext *gb)
+{
+    const VVCSH *sh = &sc->sh;
+    int nb_eps      = sh->num_entry_points + 1;
+    int ctu_addr    = 0;
+
+    if (sc->nb_eps != nb_eps) {
+        eps_free(sc);
+        sc->eps = av_calloc(nb_eps, sizeof(*sc->eps));
+        if (!sc->eps)
+            return AVERROR(ENOMEM);
+        sc->nb_eps = nb_eps;
+        for (int i = 0; i < sc->nb_eps; i++) {
+            EntryPoint *ep = sc->eps + i;
+            ep->parse_task = ff_vvc_task_alloc();
+            if (!ep->parse_task)
+                return AVERROR(ENOMEM);
+        }
+    }
+
+    for (int i = 0; i < sc->nb_eps; i++)
+    {
+        EntryPoint *ep = sc->eps + i;
+        ff_vvc_parse_task_init(ep->parse_task, VVC_TASK_TYPE_PARSE, fc, sc, ep, ctu_addr);
+        ep->ctu_addr_last = (i + 1 == sc->nb_eps ? sh->num_ctus_in_curr_slice : sh->entry_point_start_ctu[i]);
+        ep_init_cabac_decoder(sc, i, nal, gb);
+        if (i + 1 < sc->nb_eps)
+            ctu_addr = sh->entry_point_start_ctu[i];
+    }
+
+    return 0;
+}
+
+static VVCFrameContext* get_frame_context(const VVCContext *s, const VVCFrameContext *fc, const int delta)
+{
+    const int size = s->nb_fcs;
+    const int idx = (fc - s->fcs + delta  + size) % size;
+    return s->fcs + idx;
+}
+
+static int vvc_ref_frame(VVCFrameContext *fc, VVCFrame *dst, VVCFrame *src)
+{
+    int ret;
+
+    ret = ff_thread_ref_frame(&dst->tf, &src->tf);
+    if (ret < 0)
+        return ret;
+
+#if 0
+    if (src->needs_fg) {
+        ret = ff_thread_ref_frame(&dst->tf_grain, &src->tf_grain);
+        if (ret < 0)
+            return ret;
+        dst->needs_fg = 1;
+    }
+#endif
+    dst->progress_buf = av_buffer_ref(src->progress_buf);
+
+    dst->tab_mvf_buf = av_buffer_ref(src->tab_mvf_buf);
+    if (!dst->tab_mvf_buf)
+        goto fail;
+    dst->tab_mvf = src->tab_mvf;
+
+    dst->rpl_tab_buf = av_buffer_ref(src->rpl_tab_buf);
+    if (!dst->rpl_tab_buf)
+        goto fail;
+    dst->rpl_tab = src->rpl_tab;
+
+    dst->rpl_buf = av_buffer_ref(src->rpl_buf);
+    if (!dst->rpl_buf)
+        goto fail;
+
+    dst->poc = src->poc;
+    dst->ctb_count = src->ctb_count;
+    dst->flags = src->flags;
+    dst->sequence = src->sequence;
+
+#if 0
+    if (src->hwaccel_picture_private) {
+        dst->hwaccel_priv_buf = av_buffer_ref(src->hwaccel_priv_buf);
+        if (!dst->hwaccel_priv_buf)
+            goto fail;
+        dst->hwaccel_picture_private = dst->hwaccel_priv_buf->data;
+    }
+#endif
+    return 0;
+fail:
+    ff_vvc_unref_frame(fc, dst, ~0);
+    return AVERROR(ENOMEM);
+}
+
+static int frame_context_init_params(VVCFrameContext *fc, const VVCContext *s)
+{
+    int ret;
+    for (int i = 0; i < FF_ARRAY_ELEMS(fc->ps.alf_list); i++) {
+        ret = av_buffer_replace(&fc->ps.alf_list[i], s->ps.alf_list[i]);
+        if (ret < 0)
+            return ret;
+    }
+    for (int i = 0; i < FF_ARRAY_ELEMS(fc->ps.lmcs_list); i++) {
+        ret = av_buffer_replace(&fc->ps.lmcs_list[i], s->ps.lmcs_list[i]);
+        if (ret < 0)
+            return ret;
+    }
+    for (int i = 0; i < FF_ARRAY_ELEMS(fc->ps.scaling_list); i++) {
+        ret = av_buffer_replace(&fc->ps.scaling_list[i], s->ps.scaling_list[i]);
+        if (ret < 0)
+            return ret;
+    }
+    ret = av_buffer_replace(&fc->ps.ph_buf, s->ps.ph_buf);
+    if (ret < 0)
+        return ret;
+    fc->ps.ph = (VVCPH *)fc->ps.ph_buf->data;
+
+    ret = av_buffer_replace(&fc->ps.pps_buf, s->ps.pps_list[fc->ps.ph->pic_parameter_set_id]);
+    if (ret < 0)
+        return ret;
+    fc->ps.pps = (VVCPPS*)fc->ps.pps_buf->data;
+
+    ret = av_buffer_replace(&fc->ps.sps_buf, s->ps.sps_list[fc->ps.pps->seq_parameter_set_id]);
+    if (ret < 0)
+        return ret;
+    fc->ps.sps = (VVCSPS*)fc->ps.sps_buf->data;
+
+    if (s->nb_frames && s->nb_fcs > 1) {
+        VVCFrameContext *prev = get_frame_context(s, fc, -1);
+        for (int i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+            ff_vvc_unref_frame(fc, &fc->DPB[i], ~0);
+            if (prev->DPB[i].frame->buf[0]) {
+                ret = vvc_ref_frame(fc, &fc->DPB[i], &prev->DPB[i]);
+                if (ret < 0)
+                    return ret;
+            }
+        }
+    }
+    return 0;
+}
+
+static void export_frame_params(VVCFrameContext *fc)
+{
+    AVCodecContext *c   = fc->avctx;
+    const VVCSPS *sps   = fc->ps.sps;
+    const VVCPPS *pps   = fc->ps.pps;
+    const VVCWindow *ow = &pps->conf_win;
+
+    c->pix_fmt          = sps->pix_fmt;
+    c->coded_width      = pps->width;
+    c->coded_height     = pps->height;
+    c->width            = pps->width  - ow->left_offset - ow->right_offset;
+    c->height           = pps->height - ow->top_offset  - ow->bottom_offset;
+}
+
+static int decode_slice(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal, GetBitContext *gb)
+{
+    int ret = 0;
+    SliceContext *sc;
+    VVCSH  *sh;
+    const int is_first_slice = !fc->nb_slices;
+
+    ret = slices_realloc(fc);
+    if (ret < 0)
+        return ret;
+    sc = fc->slices[fc->nb_slices];
+
+    sh = &sc->sh;
+    ret = ff_vvc_decode_sh(sh, s, is_first_slice, gb);
+    if (ret < 0)
+        goto fail;
+
+    if (is_first_slice) {
+        ret = frame_context_init_params(fc, s);
+        if (ret < 0)
+            goto fail;
+    }
+
+    if (is_first_slice) {
+        if (IS_IDR(s)) {
+            s->seq_decode = (s->seq_decode + 1) & 0xff;
+            ff_vvc_clear_refs(fc);
+        }
+
+        if (pic_arrays_init(s, fc))
+            goto fail;
+        export_frame_params(fc);
+        ff_vvc_dsp_init (&fc->vvcdsp, fc->ps.sps->bit_depth);
+        ff_vvc_pred_init (&fc->hpc, fc->ps.sps->bit_depth);
+        ff_videodsp_init (&fc->vdsp, fc->ps.sps->bit_depth);
+        ret = vvc_frame_start(s, fc, sc);
+        if (ret < 0)
+            return ret;
+    } else if (fc->ref) {
+        if (!IS_I(sh)) {
+            ret = ff_vvc_slice_rpl(s, fc, sc);
+            if (ret < 0) {
+                av_log(fc->avctx, AV_LOG_WARNING,
+                       "Error constructing the reference lists for the current slice.\n");
+                return ret;
+            }
+        }
+    } else {
+        av_log(fc->avctx, AV_LOG_ERROR, "First slice in a frame missing.\n");
+        return ret;
+    }
+
+
+    if (!IS_I(sh))
+        vvc_smvd_ref_idx(fc, sh);
+
+    ret = init_slice_context(sc, fc, nal, gb);
+    if (ret < 0)
+        goto fail;
+    fc->nb_slices++;
+
+    for (int i = 0; i < (fc->ps.sps->entropy_coding_sync_enabled_flag ? 1 : sc->nb_eps); i++)
+        ff_vvc_frame_add_task(s, sc->eps[i].parse_task);
+
+fail:
+    return ret;
+}
+
+static int decode_nal_unit(VVCContext *s, VVCFrameContext *fc, const H2645NAL *nal)
+{
+    GetBitContext gb    = nal->gb;
+    int  ret;
+
+    s->temporal_id   = nal->temporal_id;
+
+    switch (nal->type) {
+    case VVC_VPS_NUT:
+#if 0
+        if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) {
+            ret = s->avctx->hwaccel->decode_params(s->avctx,
+                                                   nal->type,
+                                                   nal->raw_data,
+                                                   nal->raw_size);
+            if (ret < 0)
+                goto fail;
+        }
+        ret = ff_vvc_decode_nal_vps(gb, s->avctx, &s->ps);
+        if (ret < 0)
+            goto fail;
+#endif
+        break;
+    case VVC_SPS_NUT:
+        if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) {
+            ret = s->avctx->hwaccel->decode_params(s->avctx,
+                                                   nal->type,
+                                                   nal->raw_data,
+                                                   nal->raw_size);
+            if (ret < 0)
+                goto fail;
+        }
+        ret = ff_vvc_decode_sps(&s->ps, &gb, s->apply_defdispwin, nal->nuh_layer_id, s->avctx);
+        if (ret < 0)
+            goto fail;
+        break;
+    case VVC_PPS_NUT:
+        if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) {
+            ret = s->avctx->hwaccel->decode_params(s->avctx,
+                                                   nal->type,
+                                                   nal->raw_data,
+                                                   nal->raw_size);
+            if (ret < 0)
+                goto fail;
+        }
+        ret = ff_vvc_decode_pps(&s->ps, &gb, s->avctx);
+        if (ret < 0)
+            goto fail;
+        break;
+    case VVC_PH_NUT:
+        if (s->avctx->hwaccel && s->avctx->hwaccel->decode_params) {
+            ret = s->avctx->hwaccel->decode_params(s->avctx,
+                                                   nal->type,
+                                                   nal->raw_data,
+                                                   nal->raw_size);
+            if (ret < 0)
+                goto fail;
+        }
+        ret = ff_vvc_decode_ph(&s->ps, s->pocTid0, IS_CLVSS(s), &gb, s->avctx);
+        if (ret < 0)
+            goto fail;
+        break;
+    case VVC_TRAIL_NUT:
+    case VVC_STSA_NUT:
+    case VVC_RADL_NUT:
+    case VVC_RASL_NUT:
+    case VVC_IDR_W_RADL:
+    case VVC_IDR_N_LP:
+    case VVC_CRA_NUT:
+    case VVC_GDR_NUT:
+        ret = decode_slice(s, fc, nal, &gb);
+        if (ret < 0)
+            goto fail;
+        break;
+
+    case VVC_SUFFIX_APS_NUT:
+    case VVC_PREFIX_APS_NUT:
+        ret = ff_vvc_decode_aps(&s->ps, &gb, s->avctx);
+        if (ret < 0)
+            goto fail;
+        break;
+
+    default:
+        av_log(s->avctx, AV_LOG_INFO,
+               "Skipping NAL unit %d\n", nal->type);
+    }
+
+    return 0;
+fail:
+    return ret;
+}
+
+
+static int decode_nal_units(VVCContext *s, VVCFrameContext *fc, const uint8_t *buf, int length)
+{
+    int i, ret = 0;
+    int eos_at_start = 1;
+    s->last_eos = s->eos;
+    s->eos = 0;
+
+    /* split the input packet into NAL units, so we know the upper bound on the
+     * number of slices in the frame */
+    ret = ff_h2645_packet_split(&fc->pkt, buf, length, s->avctx, s->is_nalff,
+                                s->nal_length_size, s->avctx->codec_id, 1, 0);
+    if (ret < 0) {
+        av_log(s->avctx, AV_LOG_ERROR,
+               "Error splitting the input into NAL units.\n");
+        return ret;
+    }
+
+    for (i = 0; i < fc->pkt.nb_nals; i++) {
+        const H2645NAL *nal = &fc->pkt.nals[i];
+        if (nal->type == VVC_EOB_NUT || nal->type == VVC_EOS_NUT) {
+            if (eos_at_start) {
+                s->last_eos = 1;
+            } else {
+                s->eos = 1;
+            }
+        } else {
+            if (IS_VCL(nal->type)) {
+                s->vcl_unit_type = nal->type;
+                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;
+            }
+            eos_at_start = 0;
+        }
+    }
+
+    /* decode the NAL units */
+    for (i = 0; i < fc->pkt.nb_nals; i++) {
+        H2645NAL *nal = &fc->pkt.nals[i];
+        ret = decode_nal_unit(s, fc, nal);
+        if (ret < 0) {
+            av_log(s->avctx, AV_LOG_WARNING,
+                    "Error parsing NAL unit #%d.\n", i);
+            goto fail;
+        }
+    }
+    return 0;
+
+fail:
+    if (fc->ref)
+        ff_vvc_report_progress(fc->ref, INT_MAX);
+    return ret;
+}
+
+static int set_output_format(const VVCContext *s, const AVFrame *output)
+{
+    AVCodecContext *c = s->avctx;
+    int ret;
+
+    if (output->width != c->width || output->height != c->height) {
+        if ((ret = ff_set_dimensions(c, output->width, output->height)) < 0)
+            return ret;
+    }
+    c->pix_fmt = output->format;
+    return 0;
+}
+
+static int wait_delayed_frame(VVCContext *s, AVFrame *output, int *got_output)
+{
+    VVCFrameContext *delayed = get_frame_context(s, s->fcs, s->nb_frames - s->nb_delayed);
+
+    ff_vvc_frame_wait(s, delayed);
+
+    if (delayed->output_frame->buf[0]) {
+        int ret;
+        av_frame_move_ref(output, delayed->output_frame);
+        if ((ret = set_output_format(s, output)) < 0)
+            return ret;
+        *got_output = 1;
+    }
+    s->nb_delayed--;
+
+    return 0;
+}
+
+static int submit_frame(VVCContext *s, VVCFrameContext *fc, AVFrame *output, int *got_output)
+{
+    int ret;
+    s->nb_frames++;
+    s->nb_delayed++;
+    if (s->nb_delayed >= s->nb_fcs) {
+        if ((ret = wait_delayed_frame(s, output, got_output)) < 0)
+            return ret;
+    }
+    return 0;
+}
+
+static int vvc_decode_frame(AVCodecContext *avctx, AVFrame *output,
+    int *got_output, AVPacket *avpkt)
+{
+    VVCContext *s = avctx->priv_data;
+    VVCFrameContext *fc;
+    int ret;
+
+    if (!avpkt->size) {
+        while (s->nb_delayed) {
+            if ((ret = wait_delayed_frame(s, output, got_output)) < 0)
+                return ret;
+            if (*got_output)
+                return 0;
+        }
+        if (s->nb_frames) {
+            //we still have frames cached in dpb.
+            VVCFrameContext *last = get_frame_context(s, s->fcs, s->nb_frames - 1);
+
+            ret = ff_vvc_output_frame(s, last, output, 0, 1);
+            if (ret < 0)
+                return ret;
+            if (ret) {
+                *got_output = ret;
+                if ((ret = set_output_format(s, output)) < 0)
+                    return ret;
+            }
+        }
+        return 0;
+    }
+
+    fc = get_frame_context(s, s->fcs, s->nb_frames);
+    /* clear ph */
+    fc->ps.ph = NULL;
+    fc->nb_slices = 0;
+    fc->decode_order = s->nb_frames;
+
+    av_packet_unref(fc->avpkt);
+    ret = av_packet_ref(fc->avpkt, avpkt);
+    if (ret < 0)
+        return ret;
+
+    ret = decode_nal_units(s, fc, avpkt->data, avpkt->size);
+    if (ret < 0)
+        return ret;
+
+    ret = submit_frame(s, fc, output, got_output);
+    if (ret < 0)
+        return ret;
+
+    return avpkt->size;
+}
+
+static av_cold void frame_context_free(VVCFrameContext *fc)
+{
+    VVCFrameParamSets *ps = &fc->ps;
+    int i;
+
+    slices_free(fc);
+
+    for (i = 0; i < FF_ARRAY_ELEMS(fc->DPB); i++) {
+        ff_vvc_unref_frame(fc, &fc->DPB[i], ~0);
+        av_frame_free(&fc->DPB[i].frame);
+    }
+
+    ff_vvc_frame_thread_free(fc);
+    pic_arrays_free(fc);
+    av_frame_free(&fc->output_frame);
+
+    av_packet_free(&fc->avpkt);
+    ff_h2645_packet_uninit(&fc->pkt);
+
+    av_buffer_unref(&ps->ph_buf);
+    av_buffer_unref(&ps->pps_buf);
+    av_buffer_unref(&ps->sps_buf);
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->scaling_list); i++)
+        av_buffer_unref(&ps->scaling_list[i]);
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->lmcs_list); i++)
+        av_buffer_unref(&ps->lmcs_list[i]);
+    for (i = 0; i < FF_ARRAY_ELEMS(ps->alf_list); i++)
+        av_buffer_unref(&ps->alf_list[i]);
+    av_freep(&fc->avctx);
+}
+
+static av_cold int frame_context_init(VVCFrameContext *fc, AVCodecContext *avctx)
+{
+
+    fc->avctx = av_memdup(avctx, sizeof(*avctx));
+    if (!fc->avctx)
+        goto fail;
+
+    fc->output_frame = av_frame_alloc();
+    if (!fc->output_frame)
+        goto fail;
+
+    for (int j = 0; j < FF_ARRAY_ELEMS(fc->DPB); j++) {
+        fc->DPB[j].frame = av_frame_alloc();
+        if (!fc->DPB[j].frame)
+            goto fail;
+        fc->DPB[j].tf.f = fc->DPB[j].frame;
+    }
+
+    fc->avpkt = av_packet_alloc();
+    if (!fc->avpkt)
+        goto fail;
+    return 0;
+fail:
+    return AVERROR(ENOMEM);
+}
+
+static void vvc_decode_flush(AVCodecContext *avctx)
+{
+    VVCContext *s = avctx->priv_data;
+    int got_output;
+    AVFrame *output = av_frame_alloc();
+
+    if (output) {
+        while (s->nb_delayed) {
+            wait_delayed_frame(s, output, &got_output);
+            if (got_output) {
+                av_frame_unref(output);
+            }
+        }
+        av_frame_free(&output);
+    }
+}
+
+static av_cold int vvc_decode_free(AVCodecContext *avctx)
+{
+    VVCContext *s = avctx->priv_data;
+    int i;
+
+    vvc_decode_flush(avctx);
+    ff_executor_free(&s->executor);
+    if (s->fcs) {
+        for (i = 0; i < s->nb_fcs; i++)
+            frame_context_free(s->fcs + i);
+        av_free(s->fcs);
+    }
+    ff_vvc_ps_uninit(&s->ps);
+
+    return 0;
+}
+
+static pthread_once_t once_control = PTHREAD_ONCE_INIT;
+
+static void vvc_one_time_init(void)
+{
+    memset(&ff_vvc_default_scale_m, 16, sizeof(ff_vvc_default_scale_m));
+}
+
+#define VVC_MAX_FRMAE_DELAY 16
+static av_cold int vvc_decode_init(AVCodecContext *avctx)
+{
+    VVCContext *s       = avctx->priv_data;
+    int ret;
+    TaskCallbacks callbacks = {
+        s,
+        sizeof(VVCLocalContext),
+        ff_vvc_task_priority_higher,
+        ff_vvc_task_ready,
+        ff_vvc_task_run,
+    };
+
+    s->avctx = avctx;
+
+    s->nb_fcs = (avctx->flags & AV_CODEC_FLAG_LOW_DELAY) ? 1 : FFMIN(av_cpu_count(), VVC_MAX_FRMAE_DELAY);
+    s->fcs = av_calloc(s->nb_fcs, sizeof(*s->fcs));
+    if (!s->fcs)
+        goto fail;
+
+    for (int i = 0; i < s->nb_fcs; i++) {
+        VVCFrameContext *fc = s->fcs + i;
+        ret = frame_context_init(fc, avctx);
+        if (ret < 0)
+            goto fail;
+    }
+
+    s->executor = ff_executor_alloc(&callbacks, s->nb_fcs * 3 / 2);
+    s->eos = 1;
+    GDR_SET_RECOVERED(s);
+    pthread_once(&once_control, vvc_one_time_init);
+
+    return 0;
+
+fail:
+    vvc_decode_free(avctx);
+    return AVERROR(ENOMEM);
+}
+
+#define OFFSET(x) offsetof(VVCContext, x)
+#define PAR (AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
+
+static const AVOption options[] = {
+#if 0
+    { "apply_defdispwin", "Apply default display window from VUI", OFFSET(apply_defdispwin),
+        AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR },
+    { "strict-displaywin", "stricly apply default display window size", OFFSET(apply_defdispwin),
+        AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, PAR },
+#endif
+    { NULL },
+};
+
+static const AVClass vvc_decoder_class = {
+    .class_name = "vvc decoder",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+const FFCodec ff_vvc_decoder = {
+    .p.name                  = "vvc",
+    .p.long_name             = NULL_IF_CONFIG_SMALL("VVC (Versatile Video Coding)"),
+    .p.type                  = AVMEDIA_TYPE_VIDEO,
+    .p.id                    = AV_CODEC_ID_VVC,
+    .priv_data_size          = sizeof(VVCContext),
+    .p.priv_class            = &vvc_decoder_class,
+    .init                    = vvc_decode_init,
+    .close                   = vvc_decode_free,
+    FF_CODEC_DECODE_CB(vvc_decode_frame),
+    .flush                   = vvc_decode_flush,
+    .p.capabilities          = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_DELAY | AV_CODEC_CAP_OTHER_THREADS,
+    .caps_internal           = FF_CODEC_CAP_EXPORTS_CROPPING | FF_CODEC_CAP_INIT_CLEANUP |
+                               FF_CODEC_CAP_AUTO_THREADS,
+    .p.profiles              = NULL_IF_CONFIG_SMALL(ff_vvc_profiles),
+};
diff --git a/libavcodec/vvcdec.h b/libavcodec/vvcdec.h
new file mode 100644
index 0000000000..df7587d6a5
--- /dev/null
+++ b/libavcodec/vvcdec.h
@@ -0,0 +1,405 @@
+/*
+ * VVC video decoder
+ *
+ * Copyright (C) 2021 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
+ */
+
+#ifndef AVCODEC_VVCDEC_H
+#define AVCODEC_VVCDEC_H
+
+#include <stdatomic.h>
+
+#include "libavutil/buffer.h"
+#include "libavutil/md5.h"
+#include "libavutil/mem_internal.h"
+#include "libavutil/thread.h"
+
+#include "avcodec.h"
+#include "bswapdsp.h"
+#include "cabac.h"
+#include "cbs_h266.h"
+#include "get_bits.h"
+#include "vvcpred.h"
+#include "h2645_parse.h"
+#include "vvc.h"
+#include "executor.h"
+#include "vvc_ps.h"
+#include "vvcdsp.h"
+#include "internal.h"
+#include "threadframe.h"
+#include "videodsp.h"
+
+#define LUMA    0
+#define CHROMA  1
+#define CB      1
+#define CR      2
+#define JCBCR   3
+
+#define MAX_CTU_SIZE        128
+#define MAX_CU_SIZE         MAX_CTU_SIZE
+#define MIN_CU_SIZE         4
+#define MIN_CU_LOG2         2
+#define MIN_PU_SIZE         4
+#define MIN_PU_LOG2         2
+#define MIN_TB_LOG2         2                       ///< MinTbLog2SizeY
+#define MIN_TB_SIZE         (1 << MIN_TB_LOG2)
+#define MIN_TB_CHROMA_LOG2  1
+#define MAX_CU_DEPTH        7
+#define MAX_PARTS_IN_CTU ((MAX_CTU_SIZE >> MIN_CU_LOG2) * (MAX_CTU_SIZE >> MIN_CU_LOG2))
+
+#define MAX_INTER_SBS   ((MAX_CU_SIZE >> MIN_PU_LOG2) * (MAX_CU_SIZE >> MIN_PU_LOG2))
+
+#define MAX_CONTROL_POINTS   3
+
+
+#define MAX_TB_SIZE 64
+#define MAX_TUS_IN_CU 64
+
+#define MAX_QP 63
+#define DEFAULT_INTRA_TC_OFFSET 2
+
+#define MRG_MAX_NUM_CANDS       6
+#define MAX_NUM_HMVP_CANDS      5
+
+#define L0 0
+#define L1 1
+
+#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 SAO_PADDING_SIZE      1
+
+#define ALF_PADDING_SIZE      8
+#define ALF_BLOCK_SIZE        4
+
+//local size to save stack memroy
+#define ALF_SUBBLOCK_SIZE    32
+
+#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 EDGE_EMU_BUFFER_STRIDE (MAX_PB_SIZE + 32)
+
+#define AFFINE_MIN_BLOCK_SIZE 4
+#define PROF_BORDER_EXT 1
+#define PROF_BLOCK_SIZE (AFFINE_MIN_BLOCK_SIZE + PROF_BORDER_EXT * 2)
+#define BDOF_BORDER_EXT 1
+
+#define BDOF_PADDED_SIZE (16 + BDOF_BORDER_EXT * 2)
+#define BDOF_BLOCK_SIZE 4
+#define BDOF_GRADIENT_SIZE (BDOF_BLOCK_SIZE + BDOF_BORDER_EXT * 2)
+
+/**
+ * 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)])
+
+#define IS_VCL(t) ((t) <= VVC_RSV_IRAP_11 && (t) >= VVC_TRAIL_NUT)
+
+#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(sh) ((sh)->slice_type == VVC_SLICE_TYPE_I)
+#define IS_P(sh) ((sh)->slice_type == VVC_SLICE_TYPE_P)
+#define IS_B(sh) ((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)
+
+typedef struct VVCLocalContext VVCLocalContext;
+typedef struct SliceContext SliceContext;
+typedef struct VVCFrameContext  VVCFrameContext;
+typedef struct VVCFrameThread VVCFrameThread;
+typedef struct EntryPoint EntryPoint;
+typedef struct VVCTask VVCTask;
+typedef struct Mv Mv;
+typedef struct MvField MvField;
+typedef struct DMVRInfo DMVRInfo;
+typedef struct CTU CTU;
+
+enum SAOType {
+    SAO_NOT_APPLIED = 0,
+    SAO_BAND,
+    SAO_EDGE,
+    SAO_APPLIED
+};
+
+enum SAOEOClass {
+    SAO_EO_HORIZ = 0,
+    SAO_EO_VERT,
+    SAO_EO_135D,
+    SAO_EO_45D,
+};
+
+typedef struct RefPicList {
+    struct VVCFrame *ref[VVC_MAX_REF_ENTRIES];
+    int list[VVC_MAX_REF_ENTRIES];
+    int isLongTerm[VVC_MAX_REF_ENTRIES];
+    int nb_refs;
+} RefPicList;
+
+typedef struct RefPicListTab {
+    RefPicList refPicList[2];
+} RefPicListTab;
+
+#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;
+    pthread_mutex_t lock;
+    pthread_cond_t cond;
+} FrameProgress;
+
+typedef struct VVCFrame {
+    AVFrame *frame;
+    ThreadFrame tf;
+
+    MvField  *tab_mvf;
+    RefPicList *refPicList;
+    RefPicListTab **rpl_tab;
+
+    int ctb_count;
+
+    int poc;
+
+    struct VVCFrame *collocated_ref;
+
+    AVBufferRef *tab_mvf_buf;
+    AVBufferRef *rpl_tab_buf;
+    AVBufferRef *rpl_buf;
+    AVBufferRef *progress_buf;
+
+#if 0
+    AVBufferRef *hwaccel_priv_buf;
+    void *hwaccel_picture_private;
+#endif
+    /**
+     * A sequence counter, so that old frames are output first
+     * after a POC reset
+     */
+    uint16_t sequence;
+    /**
+     * A combination of VVC_FRAME_FLAG_*
+     */
+    uint8_t flags;
+} VVCFrame;
+
+typedef struct VVCCabacState{
+    uint16_t state[2];
+    uint8_t  shift[2];
+} VVCCabacState;
+
+struct SliceContext {
+    int slice_idx;
+
+    VVCSH sh;
+
+    EntryPoint *eps;
+    int nb_eps;
+};
+
+struct VVCFrameContext {
+    AVCodecContext *avctx;
+
+    // +1 for the current frame
+    VVCFrame DPB[VVC_MAX_DPB_SIZE + 1];
+
+    AVFrame *frame;
+    AVFrame *output_frame;
+    VVCFrameParamSets ps;
+
+    SliceContext  **slices;
+    int nb_slices;
+    int nb_slices_allocated;
+
+    VVCFrame *ref;
+
+    VVCPredContext hpc;
+    VVCDSPContext vvcdsp;
+    VideoDSPContext vdsp;
+
+    VVCFrameThread *frame_thread;
+
+    uint64_t decode_order;
+
+    AVPacket *avpkt;
+    H2645Packet pkt;
+
+    AVBufferPool *tab_mvf_pool;
+    AVBufferPool *rpl_tab_pool;
+
+    AVBufferPool *cu_pool;
+
+    struct {
+        int16_t *slice_idx;
+
+        DBParams  *deblock;
+        SAOParams *sao;
+        ALFParams *alf;
+        DMVRInfo  *dmvr;
+
+        int     *cb_pos_x[2];                           ///< CbPosX[][][]
+        int     *cb_pos_y[2];                           ///< CbPosY[][][]
+        uint8_t *cb_width[2];                           ///< CbWidth[][][]
+        uint8_t *cb_height[2];                          ///< CbHeight[][][]
+        uint8_t *cqt_depth[2];                          ///< CqtDepth[][][]
+        int8_t  *qp[VVC_MAX_SAMPLE_ARRAYS];
+
+        uint8_t *skip;                                  ///< CuSkipFlag[][]
+        uint8_t *ispmf;                                 ///< intra_sub_partitions_mode_flag
+        uint8_t *msm[2];                                ///< MttSplitMode[][][] in 32 pixels
+        uint8_t *imf;                                   ///< IntraMipFlag[][]
+        uint8_t *imtf;                                  ///< intra_mip_transposed_flag[][]
+        uint8_t *imm;                                   ///< intra_mip_mode[][]
+        uint8_t *ipm;                                   ///< IntraPredModeY[][]
+        uint8_t *cpm[2];                                ///< CuPredMode[][][]
+        uint8_t *msf;                                   ///< MergeSubblockFlag[][]
+        uint8_t *iaf;                                   ///< InterAffineFlag[][]
+        uint8_t *mmi;                                   ///< MotionModelIdc[][]
+        Mv      *cp_mv[2];                              ///< CpMvLX[][][][MAX_CONTROL_POINTS];
+
+        uint8_t *tu_coded_flag[VVC_MAX_SAMPLE_ARRAYS];  ///< tu_y_coded_flag[][],  tu_cb_coded_flag[][],  tu_cr_coded_flag[][]
+        uint8_t *tu_joint_cbcr_residual_flag;           ///< tu_joint_cbcr_residual_flag[][]
+        int     *tb_pos_x0[2];
+        int     *tb_pos_y0[2];
+        uint8_t *tb_width[2];
+        uint8_t *tb_height[2];
+
+        uint8_t *horizontal_bs[VVC_MAX_SAMPLE_ARRAYS];
+        uint8_t *vertical_bs[VVC_MAX_SAMPLE_ARRAYS];
+        uint8_t *horizontal_p;                          ///< horizontal maxFilterLengthPs for luma
+        uint8_t *horizontal_q;                          ///< horizontal maxFilterLengthPs for luma
+        uint8_t *vertical_p;                            ///< vertical   maxFilterLengthQs for luma
+        uint8_t *vertical_q;                            ///< vertical   maxFilterLengthQs for luma
+
+        uint8_t *sao_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS];
+        uint8_t *sao_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS];
+        uint8_t *alf_pixel_buffer_h[VVC_MAX_SAMPLE_ARRAYS][2];
+        uint8_t *alf_pixel_buffer_v[VVC_MAX_SAMPLE_ARRAYS][2];
+
+        int     *coeffs;
+        CTU     *ctus;
+
+        //used in arrays_init only
+        int ctu_count;
+        int ctu_size;
+        int pic_size_in_min_cb;
+        int pic_size_in_min_pu;
+        int pic_size_in_min_tu;
+        int ctu_width;
+        int ctu_height;
+        int width;
+        int height;
+        int chroma_format_idc;
+        int pixel_shift;
+        int bs_width;
+        int bs_height;
+    } tab;
+} ;
+
+typedef struct VVCContext {
+    const AVClass *c;  // needed by private avoptions
+    AVCodecContext *avctx;
+
+    VVCParamSets ps;
+
+    int temporal_id;  ///< temporal_id_plus1 - 1
+    //int poc;
+    int pocTid0;
+
+    int eos;       ///< current packet contains an EOS/EOB NAL
+    int last_eos;  ///< last packet contains an EOS/EOB NAL
+
+
+    enum VVCNALUnitType vcl_unit_type;
+    int no_output_before_recovery_flag; ///< NoOutputBeforeRecoveryFlag
+    int gdr_recovery_point_poc;         ///< recoveryPointPocVal
+
+    /**
+     * Sequence counters for decoded and output frames, so that old
+     * frames are output first after a POC reset
+     */
+    uint16_t seq_decode;
+    uint16_t seq_output;
+
+    int is_nalff;           ///< this flag is != 0 if bitstream is encapsulated
+                            ///< as a format defined in 14496-15
+
+    int apply_defdispwin;
+    int nal_length_size;    ///< Number of bytes used for nal length (1, 2 or 4)
+
+    Executor *executor;
+
+    VVCFrameContext *fcs;
+    int nb_fcs;
+
+    // processed frames
+    uint64_t nb_frames;
+    // delayed frames
+    int nb_delayed;
+}  VVCContext ;
+
+//dpb
+int ff_vvc_output_frame(VVCContext *s, VVCFrameContext *fc, 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, 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, const SliceContext *sc);
+int ff_vvc_slice_rpl(VVCContext *s, VVCFrameContext *fc, const SliceContext *sc);
+void ff_vvc_unref_frame(VVCFrameContext *fc, VVCFrame *frame, int flags);
+void ff_vvc_clear_refs(VVCFrameContext *fc);
+
+int ff_vvc_get_qPy(const VVCFrameContext *fc, int xc, int yc);
+
+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);
+
+void ff_vvc_lmcs_filter(const VVCLocalContext *lc, const int x0, const int y0);
+void ff_vvc_deblock_vertical(const VVCLocalContext *lc, int x0, int y0);
+void ff_vvc_deblock_horizontal(const VVCLocalContext *lc, int x0, int y0);
+void ff_vvc_sao_filter(VVCLocalContext *lc, const int x0, const int y0);
+void ff_vvc_alf_copy_ctu_to_hv(VVCLocalContext* lc, int x0, int y0);
+void ff_vvc_alf_filter(VVCLocalContext *lc, const int x0, const int y0);
+
+void ff_vvc_ctu_free_cus(CTU *ctu);
+
+#endif /* AVCODEC_VVCDEC_H */
diff --git a/libavcodec/vvcdsp.c b/libavcodec/vvcdsp.c
new file mode 100644
index 0000000000..75df317b93
--- /dev/null
+++ b/libavcodec/vvcdsp.c
@@ -0,0 +1,452 @@
+/*
+ * VVC video 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 "vvcdsp.h"
+#include "vvc_itx_1d.h"
+
+#define VVC_SIGN(v) (v < 0 ? -1 : !!v)
+
+DECLARE_ALIGNED(16, const int8_t, ff_vvc_chroma_filters)[3][32][4] = {
+    {
+        //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 },
+    },
+};
+
+DECLARE_ALIGNED(16, const int8_t, ff_vvc_luma_filters)[3][16][8] = {
+    {
+        //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 },
+    },
+
+};
+
+DECLARE_ALIGNED(16, const int8_t, ff_vvc_dmvr_filters)[16][2] = {
+    { 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},
+};
+
+static void av_always_inline pad_int16(int16_t *_dst, const ptrdiff_t dst_stride, const int width, const int height)
+{
+    const int padded_width = width + 2;
+    int16_t *dst;
+    for (int y = 0; y < height; y++) {
+        dst = _dst + y * dst_stride;
+        for (int x = 0; x < width; x++) {
+            dst[-1] = dst[0];
+            dst[width] = dst[width - 1];
+        }
+    }
+
+    _dst--;
+    //top
+    memcpy(_dst - dst_stride, _dst, padded_width * sizeof(int16_t));
+    //bottom
+    _dst += dst_stride * height;
+    memcpy(_dst, _dst - dst_stride, padded_width * sizeof(int16_t));
+}
+
+static int vvc_sad(const int16_t *src0, const int16_t *src1, int dx, int dy,
+    const int block_w, const int block_h)
+{
+    int sad = 0;
+    dx -= 2;
+    dy -= 2;
+    src0 += (2 + dy) * MAX_PB_SIZE + 2 + dx;
+    src1 += (2 - dy) * MAX_PB_SIZE + 2 - dx;
+    for (int y = 0; y < block_h; y += 2) {
+        for (int x = 0; x < block_w; x++) {
+            sad += FFABS(src0[x] - src1[x]);
+        }
+        src0 += 2 * MAX_PB_SIZE;
+        src1 += 2 * MAX_PB_SIZE;
+    }
+    return sad;
+}
+
+#define BIT_DEPTH 8
+#include "vvcdsp_template.c"
+#undef BIT_DEPTH
+
+#define BIT_DEPTH 10
+#include "vvcdsp_template.c"
+#undef BIT_DEPTH
+
+#define itx_fn(type, s)                                                                         \
+static void itx_##type##_##s(int *out, ptrdiff_t out_step, const int *in, ptrdiff_t in_step)    \
+{                                                                                               \
+  ff_vvc_inv_##type##_##s(out, out_step, in, in_step);                                          \
+}
+
+#define itx_fn_common(type) \
+    itx_fn(type, 4);        \
+    itx_fn(type, 8);        \
+    itx_fn(type, 16);       \
+    itx_fn(type, 32);       \
+
+itx_fn_common(dct2);
+itx_fn_common(dst7);
+itx_fn_common(dct8);
+itx_fn(dct2, 2);
+itx_fn(dct2, 64);
+
+
+void ff_vvc_dsp_init(VVCDSPContext *vvcdsp, int bit_depth)
+{
+#undef FUNC
+#define FUNC(a, depth) a ## _ ## depth
+
+
+#undef PEL_FUNC
+#define PEL_FUNC(dst, idx1, idx2, a, depth)                                     \
+    vvcdsp->dst[idx1][idx2] = a ## _ ## depth;                                  \
+
+#undef CHROMA_FUNCS
+#define CHROMA_FUNCS(depth)                                                     \
+    PEL_FUNC(put_vvc_chroma, 0, 0, put_vvc_pel_pixels, depth);                  \
+    PEL_FUNC(put_vvc_chroma, 0, 1, put_vvc_chroma_h, depth);                    \
+    PEL_FUNC(put_vvc_chroma, 1, 0, put_vvc_chroma_v, depth);                    \
+    PEL_FUNC(put_vvc_chroma, 1, 1, put_vvc_chroma_hv, depth)
+
+#undef CHROMA_UNI_FUNCS
+#define CHROMA_UNI_FUNCS(depth)                                                 \
+    PEL_FUNC(put_vvc_chroma_uni, 0, 0, put_vvc_pel_uni_pixels, depth);          \
+    PEL_FUNC(put_vvc_chroma_uni, 0, 1, put_vvc_chroma_uni_h, depth);            \
+    PEL_FUNC(put_vvc_chroma_uni, 1, 0, put_vvc_chroma_uni_v, depth);            \
+    PEL_FUNC(put_vvc_chroma_uni, 1, 1, put_vvc_chroma_uni_hv, depth);           \
+
+#if 0
+    PEL_FUNC(put_vvc_chroma_uni_w, 0, 0, put_vvc_pel_uni_w_pixels, depth);      \
+    PEL_FUNC(put_vvc_chroma_uni_w, 0, 1, put_vvc_chroma_uni_w_h, depth);        \
+    PEL_FUNC(put_vvc_chroma_uni_w, 1, 0, put_vvc_chroma_uni_w_v, depth);        \
+    PEL_FUNC(put_vvc_chroma_uni_w, 1, 1, put_vvc_chroma_uni_w_hv, depth)
+#endif
+#undef CHROMA_BI_FUNCS
+#define CHROMA_BI_FUNCS(depth)                                                  \
+    PEL_FUNC(put_vvc_chroma_bi, 0, 0, put_vvc_pel_bi_pixels, depth);            \
+    PEL_FUNC(put_vvc_chroma_bi, 0, 1, put_vvc_chroma_bi_h, depth);              \
+    PEL_FUNC(put_vvc_chroma_bi, 1, 0, put_vvc_chroma_bi_v, depth);              \
+    PEL_FUNC(put_vvc_chroma_bi, 1, 1, put_vvc_chroma_bi_hv, depth);             \
+    PEL_FUNC(put_vvc_chroma_bi_w, 0, 0, put_vvc_pel_bi_w_pixels, depth);        \
+    PEL_FUNC(put_vvc_chroma_bi_w, 0, 1, put_vvc_chroma_bi_w_h, depth);          \
+    PEL_FUNC(put_vvc_chroma_bi_w, 1, 0, put_vvc_chroma_bi_w_v, depth);          \
+    PEL_FUNC(put_vvc_chroma_bi_w, 1, 1, put_vvc_chroma_bi_w_hv, depth)
+
+#undef LUMA_FUNCS
+#define LUMA_FUNCS(depth)                                                       \
+    PEL_FUNC(put_vvc_luma, 0, 0, put_vvc_pel_pixels, depth);                    \
+    PEL_FUNC(put_vvc_luma, 0, 1, put_vvc_luma_h, depth);                        \
+    PEL_FUNC(put_vvc_luma, 1, 0, put_vvc_luma_v, depth);                        \
+    PEL_FUNC(put_vvc_luma, 1, 1, put_vvc_luma_hv, depth)
+
+
+#undef LUMA_UNI_FUNCS
+#define LUMA_UNI_FUNCS(depth)                                                   \
+    PEL_FUNC(put_vvc_luma_uni, 0, 0, put_vvc_pel_uni_pixels, depth);            \
+    PEL_FUNC(put_vvc_luma_uni, 0, 1, put_vvc_luma_uni_h, depth);                \
+    PEL_FUNC(put_vvc_luma_uni, 1, 0, put_vvc_luma_uni_v, depth);                \
+    PEL_FUNC(put_vvc_luma_uni, 1, 1, put_vvc_luma_uni_hv, depth);               \
+
+#if 0
+    PEL_FUNC(put_vvc_luma_uni_w, 0, 0, put_vvc_pel_uni_w_pixels, depth);        \
+    PEL_FUNC(put_vvc_luma_uni_w, 0, 1, put_vvc_luma_uni_w_h, depth);            \
+    PEL_FUNC(put_vvc_luma_uni_w, 1, 0, put_vvc_luma_uni_w_v, depth);            \
+    PEL_FUNC(put_vvc_luma_uni_w, 1, 1, put_vvc_luma_uni_w_hv, depth)
+#endif
+
+#undef LUMA_BI_FUNCS
+#define LUMA_BI_FUNCS(depth)                                                    \
+    PEL_FUNC(put_vvc_luma_bi, 0, 0, put_vvc_pel_bi_pixels, depth);              \
+    PEL_FUNC(put_vvc_luma_bi, 0, 1, put_vvc_luma_bi_h, depth);                  \
+    PEL_FUNC(put_vvc_luma_bi, 1, 0, put_vvc_luma_bi_v, depth);                  \
+    PEL_FUNC(put_vvc_luma_bi, 1, 1, put_vvc_luma_bi_hv, depth);                 \
+    PEL_FUNC(put_vvc_luma_bi_w, 0, 0, put_vvc_pel_bi_w_pixels, depth);          \
+    PEL_FUNC(put_vvc_luma_bi_w, 0, 1, put_vvc_luma_bi_w_h, depth);              \
+    PEL_FUNC(put_vvc_luma_bi_w, 1, 0, put_vvc_luma_bi_w_v, depth);              \
+    PEL_FUNC(put_vvc_luma_bi_w, 1, 1, put_vvc_luma_bi_w_hv, depth)
+
+#undef DMVR_FUNCS
+#define DMVR_FUNCS(depth)                                                       \
+        PEL_FUNC(dmvr_vvc_luma, 0, 0, dmvr_vvc_luma, depth);                    \
+        PEL_FUNC(dmvr_vvc_luma, 0, 1, dmvr_vvc_luma_h, depth);                  \
+        PEL_FUNC(dmvr_vvc_luma, 1, 0, dmvr_vvc_luma_v, depth);                  \
+        PEL_FUNC(dmvr_vvc_luma, 1, 1, dmvr_vvc_luma_hv, depth)
+
+#define VVC_ITX(TYPE, type, s)                                                  \
+    vvcdsp->itx[TYPE][TX_SIZE_##s]      = itx_##type##_##s;                     \
+
+#define VVC_ITX_COMMON(TYPE, type)                                              \
+    VVC_ITX(TYPE, type, 4);                                                     \
+    VVC_ITX(TYPE, type, 8);                                                     \
+    VVC_ITX(TYPE, type, 16);                                                    \
+    VVC_ITX(TYPE, type, 32);                                                    \
+
+#define VVC_ITX_DCT2()
+
+#define VVC_DSP(depth)                                                          \
+    vvcdsp->add_residual                = FUNC(add_residual, depth);            \
+    vvcdsp->add_residual_joint          = FUNC(add_residual_joint, depth);      \
+    vvcdsp->pred_residual_joint         = FUNC(pred_residual_joint, depth);     \
+    vvcdsp->fetch_samples               = FUNC(fetch_samples, depth);           \
+    vvcdsp->bdof_fetch_samples          = FUNC(bdof_fetch_samples, depth);      \
+    vvcdsp->apply_prof                  = FUNC(apply_prof, depth);              \
+    vvcdsp->apply_prof_uni              = FUNC(apply_prof_uni, depth);          \
+    vvcdsp->apply_prof_bi               = FUNC(apply_prof_bi, depth);           \
+    vvcdsp->apply_prof_bi_w             = FUNC(apply_prof_bi_w, depth);         \
+    vvcdsp->apply_bdof                  = FUNC(apply_bdof, depth);              \
+    vvcdsp->prof_grad_filter            = FUNC(prof_grad_filter, depth);        \
+    vvcdsp->vvc_sad                     = vvc_sad;                              \
+    vvcdsp->sao_band_filter[0] =                                                \
+    vvcdsp->sao_band_filter[1] =                                                \
+    vvcdsp->sao_band_filter[2] =                                                \
+    vvcdsp->sao_band_filter[3] =                                                \
+    vvcdsp->sao_band_filter[4] =                                                \
+    vvcdsp->sao_band_filter[5] =                                                \
+    vvcdsp->sao_band_filter[6] =                                                \
+    vvcdsp->sao_band_filter[7] =                                                \
+    vvcdsp->sao_band_filter[8] = FUNC(sao_band_filter, depth);                  \
+    vvcdsp->sao_edge_filter[0] =                                                \
+    vvcdsp->sao_edge_filter[1] =                                                \
+    vvcdsp->sao_edge_filter[2] =                                                \
+    vvcdsp->sao_edge_filter[3] =                                                \
+    vvcdsp->sao_edge_filter[4] =                                                \
+    vvcdsp->sao_edge_filter[5] =                                                \
+    vvcdsp->sao_edge_filter[6] =                                                \
+    vvcdsp->sao_edge_filter[7] =                                                \
+    vvcdsp->sao_edge_filter[8] = FUNC(sao_edge_filter, depth);                  \
+    vvcdsp->sao_edge_restore[0] = FUNC(sao_edge_restore_0, depth);              \
+    vvcdsp->sao_edge_restore[1] = FUNC(sao_edge_restore_1, depth);              \
+                                                                                \
+    vvcdsp->alf_filter_luma         = FUNC(alf_filter_luma, depth);             \
+    vvcdsp->alf_filter_luma_vb      = FUNC(alf_filter_luma_vb, depth);          \
+    vvcdsp->alf_filter_chroma       = FUNC(alf_filter_chroma, depth);           \
+    vvcdsp->alf_filter_chroma_vb    = FUNC(alf_filter_chroma_vb, depth);        \
+    vvcdsp->alf_filter_cc           = FUNC(alf_filter_cc, depth);               \
+    vvcdsp->alf_get_coeff_and_clip  = FUNC(alf_get_coeff_and_clip, depth);      \
+                                                                                \
+    vvcdsp->lmcs_filter_luma    = FUNC(lmcs_filter_luma, depth);                \
+                                                                                \
+    VVC_ITX(DCT2, dct2, 2)                                                      \
+    VVC_ITX(DCT2, dct2, 64)                                                     \
+    VVC_ITX_COMMON(DCT2, dct2)                                                  \
+    VVC_ITX_COMMON(DCT8, dct8)                                                  \
+    VVC_ITX_COMMON(DST7, dst7)                                                  \
+    LUMA_FUNCS(depth)                                                           \
+    LUMA_UNI_FUNCS(depth)                                                       \
+    CHROMA_FUNCS(depth)                                                         \
+    CHROMA_UNI_FUNCS(depth)                                                     \
+    LUMA_BI_FUNCS(depth)                                                        \
+    CHROMA_BI_FUNCS(depth)                                                      \
+    DMVR_FUNCS(depth)                                                           \
+                                                                                \
+    vvcdsp->vvc_h_loop_ladf_level      = FUNC(vvc_h_loop_ladf_level, depth);    \
+    vvcdsp->vvc_v_loop_ladf_level      = FUNC(vvc_v_loop_ladf_level, depth);    \
+    vvcdsp->vvc_h_loop_filter_luma     = FUNC(vvc_h_loop_filter_luma, depth);   \
+    vvcdsp->vvc_v_loop_filter_luma     = FUNC(vvc_v_loop_filter_luma, depth);   \
+    vvcdsp->vvc_h_loop_filter_chroma   = FUNC(vvc_h_loop_filter_chroma, depth); \
+    vvcdsp->vvc_v_loop_filter_chroma   = FUNC(vvc_v_loop_filter_chroma, depth); \
+    vvcdsp->vvc_h_loop_filter_luma_c   = FUNC(vvc_h_loop_filter_luma, depth);   \
+    vvcdsp->vvc_v_loop_filter_luma_c   = FUNC(vvc_v_loop_filter_luma, depth);   \
+    vvcdsp->vvc_h_loop_filter_chroma_c = FUNC(vvc_h_loop_filter_chroma, depth); \
+    vvcdsp->vvc_v_loop_filter_chroma_c = FUNC(vvc_v_loop_filter_chroma, depth); \
+    vvcdsp->put_vvc_ciip               = FUNC(put_vvc_ciip, depth);             \
+    vvcdsp->put_vvc_gpm                = FUNC(put_vvc_gpm, depth);              \
+
+    switch (bit_depth) {
+    case 10:
+        VVC_DSP(10);
+        break;
+    default:
+        VVC_DSP(8);
+        break;
+    }
+}
diff --git a/libavcodec/vvcdsp.h b/libavcodec/vvcdsp.h
new file mode 100644
index 0000000000..b5a52b324b
--- /dev/null
+++ b/libavcodec/vvcdsp.h
@@ -0,0 +1,223 @@
+/*
+ * VVC video 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
+ */
+
+#ifndef AVCODEC_VVCDSP_H
+#define AVCODEC_VVCDSP_H
+
+#include "libavutil/mem_internal.h"
+
+#include "get_bits.h"
+
+#define MAX_PB_SIZE 128
+
+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 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;
+
+typedef struct VVCDSPContext {
+#if 0
+    void (*put_pcm)(uint8_t *_dst, ptrdiff_t _stride, int width, int height,
+                    struct GetBitContext *gb, int pcm_bit_depth);
+#endif
+    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 *out, ptrdiff_t out_step, const int *in, ptrdiff_t in_step);
+#if 0
+    void (*dequant)(int16_t *coeffs, int16_t log2_size);
+
+    void (*transform_rdpcm)(int16_t *coeffs, int16_t log2_size, int mode);
+
+    void (*transform_4x4_luma)(int16_t *coeffs);
+
+    void (*idct[4])(int16_t *coeffs, int col_limit);
+
+    void (*idct_dc[4])(int16_t *coeffs);
+#endif
+
+    void (*lmcs_filter_luma)(uint8_t *_dst, ptrdiff_t dst_stride, int width, int height, const uint8_t *lut);
+
+
+    void (*sao_band_filter[9])(uint8_t *_dst, uint8_t *_src, ptrdiff_t _dst_stride, ptrdiff_t _src_stride,
+        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 (*sao_edge_filter[9])(uint8_t *_dst /* align 16 */, uint8_t *_src /* align 32 */, ptrdiff_t dst_stride,
+        int16_t *sao_offset_val, int sao_eo_class, int width, int height);
+
+    void (*sao_edge_restore[2])(uint8_t *_dst, uint8_t *_src, ptrdiff_t _dst_stride, ptrdiff_t _src_stride,
+        struct SAOParams *sao, int *borders, int _width, int _height, int c_idx,
+        uint8_t *vert_edge, uint8_t *horiz_edge, uint8_t *diag_edge);
+
+    void (*alf_filter_luma)(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+        int width, int height, const int8_t *filter, const int16_t *clip);
+    void (*alf_filter_luma_vb)(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+        int width, int height, const int8_t *filter, const int16_t *clip, int vb_pos);
+
+    void (*alf_filter_chroma)(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+        int width, int height, const int8_t *filter, const int16_t *clip);
+    void (*alf_filter_chroma_vb)(uint8_t *dst, const uint8_t *src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+        int width, int height, const int8_t *filter, const int16_t *clip, int vb_pos);
+
+    void (*alf_filter_cc)(uint8_t *dst, const uint8_t *luma, ptrdiff_t dst_stride, ptrdiff_t luma_stride,
+        int width, int height, int hs, int vs, const int8_t *filter, int vb_pos);
+
+    void (*alf_get_coeff_and_clip)(const uint8_t *src, ptrdiff_t src_stride,
+        int x0, int y0, int width, int height, int vb_pos,
+        const int8_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt,
+        int8_t *coeff, int16_t *clip);
+
+    void (*put_vvc_luma[2][2])(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride,
+        int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx);
+    void (*put_vvc_luma_uni[2][2])(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *src, ptrdiff_t src_stride,
+        int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx);
+#if 0
+    void (*put_vvc_luma_uni_w[10][2][2])(uint8_t *_dst, ptrdiff_t _dst_stride, uint8_t *_src, ptrdiff_t _src_stride,
+                                          int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width);
+#endif
+    void (*put_vvc_luma_bi[2][2])(uint8_t *dst, ptrdiff_t dst_stride,
+        const uint8_t *_src, ptrdiff_t _src_stride, const int16_t *src2,
+        int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx);
+    void (*put_vvc_luma_bi_w[2][2])(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *_src, ptrdiff_t _src_stride,
+        const int16_t *src2,  int height, int denom, int wx0, int wx1, int ox0, int ox1,
+        intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx);
+
+    void (*put_vvc_chroma[2][2])(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride,
+        int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx);
+
+    void (*put_vvc_chroma_uni[2][2])(uint8_t *dst, ptrdiff_t dst_stride, const uint8_t *_src, ptrdiff_t _src_stride,
+        int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx);
+#if 0
+    void (*put_vvc_chroma_uni_w[10][2][2])(uint8_t *_dst, ptrdiff_t _dst_stride, uint8_t *_src, ptrdiff_t _src_stride,
+                                          int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width);
+#endif
+    void (*put_vvc_chroma_bi[2][2])(uint8_t *dst, ptrdiff_t dst_stride,
+        const uint8_t *_src, ptrdiff_t _src_stride, const int16_t *src2,
+        int height, intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx);
+
+    void (*put_vvc_chroma_bi_w[2][2])(uint8_t *dst, ptrdiff_t dst_stride,
+        const uint8_t *_src, ptrdiff_t _src_stride, const int16_t *src2,
+        int height, int denom, int wx0, int ox0, int wx1, int ox1,
+        intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx);
+
+    void (*put_vvc_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_vvc_gpm)(uint8_t *dst, ptrdiff_t dst_stride, int width, int height,
+        const int16_t *tmp, const int16_t *tmp1, const ptrdiff_t tmp_stride,
+        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, const int _width, const 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_bi)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1,
+        const int16_t *diff_mv_x, const int16_t *diff_mv_y);
+    void (*apply_prof_bi_w)(uint8_t *dst, ptrdiff_t dst_stride, const int16_t *src0, const int16_t *src1,
+        const int16_t *diff_mv_x, const int16_t *diff_mv_y, int denom, int w0, int w1, int o0, int o1);
+
+    void (*apply_bdof)(uint8_t *dst, ptrdiff_t dst_stride, int16_t *src0, int16_t *src1, int block_w, int block_h);
+
+    int (*vvc_sad)(const int16_t *src0, const int16_t *src1, int dx, int dy, const int block_w, const int block_h);
+    void (*dmvr_vvc_luma[2][2])(int16_t *dst, const uint8_t *src, ptrdiff_t src_stride, int height,
+        intptr_t mx, intptr_t my, int width);
+
+
+    int (*vvc_h_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride);
+    int (*vvc_v_loop_ladf_level)(const uint8_t *pix, ptrdiff_t stride);
+
+    void (*vvc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc,
+        uint8_t no_p, uint8_t no_q, uint8_t max_len_p, uint8_t max_len_q, int hor_ctu_edge);
+    void (*vvc_v_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc,
+        uint8_t no_p, uint8_t no_q, uint8_t max_len_p, uint8_t max_len_q);
+
+    void (*vvc_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc,
+        uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q);
+    void (*vvc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc,
+        uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q);
+
+    void (*vvc_h_loop_filter_luma_c)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc,
+        uint8_t no_p, uint8_t no_q, uint8_t max_len_p, uint8_t max_len_q, int hor_ctu_edge);
+    void (*vvc_v_loop_filter_luma_c)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc,
+        uint8_t no_p, uint8_t no_q, uint8_t max_len_p, uint8_t max_len_q);
+
+    void (*vvc_h_loop_filter_chroma_c)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc,
+        uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q);
+    void (*vvc_v_loop_filter_chroma_c)(uint8_t *pix, ptrdiff_t stride, int beta, int32_t tc,
+        uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q);
+} VVCDSPContext;
+
+void ff_vvc_dsp_init(VVCDSPContext *hpc, int bit_depth);
+
+extern const int8_t ff_vvc_chroma_filters[3][32][4];
+extern const int8_t ff_vvc_luma_filters[3][16][8];
+extern const int8_t ff_vvc_dmvr_filters[16][2];
+
+void ff_vvc_dsp_init_aarch64(VVCDSPContext *c, const int bit_depth);
+void ff_vvc_dsp_init_arm(VVCDSPContext *c, const int bit_depth);
+void ff_vvc_dsp_init_ppc(VVCDSPContext *c, const int bit_depth);
+void ff_vvc_dsp_init_x86(VVCDSPContext *c, const int bit_depth);
+void ff_vvc_dsp_init_mips(VVCDSPContext *c, const int bit_depth);
+
+#endif /* AVCODEC_VVCDSP_H */
diff --git a/libavcodec/vvcdsp_template.c b/libavcodec/vvcdsp_template.c
new file mode 100644
index 0000000000..d6075623fa
--- /dev/null
+++ b/libavcodec/vvcdsp_template.c
@@ -0,0 +1,2724 @@
+/*
+ * VVC video 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 "get_bits.h"
+#include "vvcdec.h"
+
+#include "bit_depth_template.c"
+#include "vvcdsp.h"
+
+#if 0
+static void FUNC(put_pcm)(uint8_t *_dst, ptrdiff_t stride, int width, int height,
+                          GetBitContext *gb, int pcm_bit_depth)
+{
+    int x, y;
+    pixel *dst = (pixel *)_dst;
+
+    stride /= sizeof(pixel);
+
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++)
+            dst[x] = get_bits(gb, pcm_bit_depth) << (BIT_DEPTH - pcm_bit_depth);
+        dst += stride;
+    }
+}
+#endif
+
+static void FUNC(add_residual)(uint8_t *_dst, const int *res,
+    const int w, const int h, const ptrdiff_t _stride)
+{
+    pixel *dst          = (pixel *)_dst;
+
+    const int stride    = _stride / sizeof(pixel);
+
+    for (int y = 0; y < h; y++) {
+        for (int x = 0; x < w; x++) {
+            dst[x] = av_clip_pixel(dst[x] + *res);
+            res++;
+        }
+        dst += stride;
+    }
+}
+
+static void FUNC(add_residual_joint)(uint8_t *_dst, const int *res,
+    const int w, const int h, const ptrdiff_t _stride, const int c_sign, const int shift)
+{
+    pixel *dst = (pixel *)_dst;
+
+    const int stride = _stride / sizeof(pixel);
+
+    for (int y = 0; y < h; y++) {
+        for (int x = 0; x < w; x++) {
+            const int r = ((*res) * c_sign) >> shift;
+            dst[x] = av_clip_pixel(dst[x] + r);
+            res++;
+        }
+        dst += stride;
+    }
+}
+
+static void FUNC(pred_residual_joint)(int *buf, const int w, const int h,
+    const int c_sign, const int shift)
+{
+    for (int y = 0; y < h; y++) {
+        for (int x = 0; x < w; x++) {
+            *buf = ((*buf) * c_sign) >> shift;
+            buf++;
+        }
+    }
+}
+
+
+#if 0
+static void FUNC(transform_rdpcm)(int16_t *_coeffs, int16_t log2_size, int mode)
+{
+    int16_t *coeffs = (int16_t *) _coeffs;
+    int x, y;
+    int size = 1 << log2_size;
+
+    if (mode) {
+        coeffs += size;
+        for (y = 0; y < size - 1; y++) {
+            for (x = 0; x < size; x++)
+                coeffs[x] += coeffs[x - size];
+            coeffs += size;
+        }
+    } else {
+        for (y = 0; y < size; y++) {
+            for (x = 1; x < size; x++)
+                coeffs[x] += coeffs[x - 1];
+            coeffs += size;
+        }
+    }
+}
+
+static void FUNC(dequant)(int16_t *coeffs, int16_t log2_size)
+{
+    int shift  = 15 - BIT_DEPTH - log2_size;
+    int x, y;
+    int size = 1 << log2_size;
+
+    if (shift > 0) {
+        int offset = 1 << (shift - 1);
+        for (y = 0; y < size; y++) {
+            for (x = 0; x < size; x++) {
+                *coeffs = (*coeffs + offset) >> shift;
+                coeffs++;
+            }
+        }
+    } else {
+        for (y = 0; y < size; y++) {
+            for (x = 0; x < size; x++) {
+                *coeffs = *(uint16_t*)coeffs << -shift;
+                coeffs++;
+            }
+        }
+    }
+}
+
+#endif
+
+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, uint8_t *_src,
+                                  ptrdiff_t dst_stride, ptrdiff_t src_stride,
+                                  int16_t *sao_offset_val, int sao_left_class,
+                                  int width, int height)
+{
+    pixel *dst = (pixel *)_dst;
+    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, uint8_t *_src, ptrdiff_t dst_stride, int16_t *sao_offset_val,
+                                  int eo, int width, 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;
+    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, uint8_t *_src,
+                                    ptrdiff_t dst_stride, ptrdiff_t src_stride, SAOParams *sao,
+                                    int *borders, int _width, int _height,
+                                    int c_idx, uint8_t *vert_edge,
+                                    uint8_t *horiz_edge, uint8_t *diag_edge)
+{
+    int x, y;
+    pixel *dst = (pixel *)_dst;
+    pixel *src = (pixel *)_src;
+    int16_t *sao_offset_val = sao->offset_val[c_idx];
+    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, uint8_t *_src,
+                                    ptrdiff_t dst_stride, ptrdiff_t src_stride, SAOParams *sao,
+                                    int *borders, int _width, int _height,
+                                    int c_idx, uint8_t *vert_edge,
+                                    uint8_t *horiz_edge, uint8_t *diag_edge)
+{
+    int x, y;
+    pixel *dst = (pixel *)_dst;
+    pixel *src = (pixel *)_src;
+    int16_t *sao_offset_val = sao->offset_val[c_idx];
+    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, const uint8_t *_src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+    const int width, const int height, const int8_t *filter, const int16_t *clip)
+{
+    const pixel *src    = (pixel *)_src;
+    const int shift     = 7;
+    const int offset    = 1 << ( shift - 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;
+
+                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]);
+
+                    sum = (sum + offset) >> shift;
+                    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_luma_vb)(uint8_t *_dst, const uint8_t *_src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+    const int width, const int height, const int8_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, const uint8_t* _src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+    const int width, const int height, const int8_t* filter, const int16_t* clip)
+{
+    const pixel *src = (pixel *)_src;
+    const int shift  = 7;
+    const int offset = 1 << ( shift - 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;
+
+                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]);
+
+                    sum = (sum + offset) >> shift;
+                    sum += curr;
+                    dst[j] = CLIP(sum);
+
+                    p0++;
+                    p1++;
+                    p2++;
+                    p3++;
+                    p4++;
+                    p5++;
+                    p6++;
+                }
+            }
+        }
+    }
+}
+
+static void FUNC(alf_filter_chroma_vb)(uint8_t* _dst, const uint8_t* _src, ptrdiff_t dst_stride, ptrdiff_t src_stride,
+    const int width, const int height, const int8_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, const uint8_t *_luma, ptrdiff_t dst_stride, const ptrdiff_t luma_stride,
+    const int width, const int height, const int hs, const int vs, const int8_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_GRADIENT_BORDER 2
+#define ALF_GRADIENT_SIZE   ((ALF_SUBBLOCK_SIZE + ALF_GRADIENT_BORDER * 2) / 2)
+#define ALF_GRADIENT_STEP   2
+
+#define ALF_DIR_VERT        0
+#define ALF_DIR_HORZ        1
+#define ALF_DIR_DIGA0       2
+#define ALF_DIR_DIGA1       3
+#define ALF_NUM_DIR         4
+
+static void FUNC(alf_get_idx)(const int *sum, const int ac, int *filt_idx, int *transpose_idx)
+{
+    static const int arg_var[] = {0, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4 };
+    static const int transpose_table[] = { 0, 1, 0, 2, 2, 3, 1, 3 };
+
+    int hv0, hv1, dir_hv, d0, d1, dir_d, hvd1, hvd0, dir1, dir2, dirs, sum_hv;
+    if (sum[ALF_DIR_VERT] > sum[ALF_DIR_HORZ]) {
+        hv1 = sum[ALF_DIR_VERT];
+        hv0 = sum[ALF_DIR_HORZ];
+        dir_hv = 1;
+    } else {
+        hv1 = sum[ALF_DIR_HORZ];
+        hv0 = sum[ALF_DIR_VERT];
+        dir_hv = 3;
+    }
+    if (sum[ALF_DIR_DIGA0] > sum[ALF_DIR_DIGA1]) {
+        d1 = sum[ALF_DIR_DIGA0];
+        d0 = sum[ALF_DIR_DIGA1];
+        dir_d = 0;
+    } else {
+        d1 = sum[ALF_DIR_DIGA1];
+        d0 = sum[ALF_DIR_DIGA0];
+        dir_d = 2;
+    }
+    hvd1 = (d1 * hv0 > hv1 * d0) ? d1 : hv1;
+    hvd0 = (d1 * hv0 > hv1 * d0) ? d0 : hv0;
+
+    dir1 = (d1 * hv0 > hv1 * d0) ? dir_d : dir_hv;
+    dir2 = (d1 * hv0 > hv1 * d0) ? dir_hv : dir_d;
+    dirs = (hvd1 * 2 > 9 * hvd0) ? 2 : ((hvd1 > 2 * hvd0) ? 1 : 0);
+
+    *transpose_idx = transpose_table[dir1 * 2 + (dir2 >> 1)];
+
+    sum_hv = sum[ALF_DIR_HORZ] + sum[ALF_DIR_VERT];
+    *filt_idx = arg_var[av_clip_uintp2(sum_hv * ac >> (BIT_DEPTH - 1), 4)];
+    if (dirs) {
+        *filt_idx += (((dir1 & 0x1) << 1) + dirs) * 5;
+    }
+}
+
+static void FUNC(alf_reconstruct_coeff_and_clip)(const int8_t *src_coeff, const uint8_t *clip_idx, const int transpose_idx, int8_t *coeff, int16_t *clip)
+{
+    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 < ALF_NUM_COEFF_LUMA; i++) {
+        const int idx = index[transpose_idx][i];
+        coeff[i] = src_coeff[idx];
+        clip[i]  = clip_set[clip_idx[idx]];
+    }
+}
+
+static void FUNC(alf_get_coeff_and_clip)(const uint8_t *_src, ptrdiff_t src_stride,
+    const int x0, const int y0, const int width, const int height, const int vb_pos,
+    const int8_t *coeff_set, const uint8_t *clip_idx_set, const uint8_t *class_to_filt,
+    int8_t *coeff, int16_t *clip)
+{
+    int gradient[ALF_NUM_DIR][ALF_GRADIENT_SIZE][ALF_GRADIENT_SIZE] = {0};
+
+    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 pixel *src = (const pixel *)_src;
+    src_stride /= sizeof(pixel);
+    src -= (ALF_GRADIENT_BORDER + 1) * src_stride + ALF_GRADIENT_BORDER;
+
+    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 (y0 + y == vb_pos)          //above
+            s3 = s2;
+        else if (y0 + y == vb_pos + ALF_GRADIENT_BORDER)
+            s0 = s1;
+
+        for (int x = 0; x < w; x += ALF_GRADIENT_STEP) {
+            //two points a time
+            const int xg = x / ALF_GRADIENT_STEP;
+            const int yg = y / ALF_GRADIENT_STEP;
+            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;
+
+            gradient[ALF_DIR_VERT] [yg][xg] = FFABS(val0 - *a0 - *b0) + FFABS(val1 - *a1 - *b1);
+            gradient[ALF_DIR_HORZ] [yg][xg] = FFABS(val0 - *(p0 - 1) - *(p0 + 1)) + FFABS(val1 - *(p1 - 1) - *(p1 + 1));
+            gradient[ALF_DIR_DIGA0][yg][xg] = FFABS(val0 - *(a0 - 1) - *(b0 + 1)) + FFABS(val1 - *(a1 - 1) - *(b1 + 1));
+            gradient[ALF_DIR_DIGA1][yg][xg] = FFABS(val0 - *(a0 + 1) - *(b0 - 1)) + FFABS(val1 - *(a1 + 1) - *(b1 - 1));
+        }
+    }
+
+    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 (y0 + y + ALF_BLOCK_SIZE == vb_pos) {
+            end -= ALF_GRADIENT_BORDER / ALF_GRADIENT_STEP;
+            ac = 3;
+        } else if (y0 + 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 };
+            int class_idx, transpose_idx;
+
+            //todo: optimize this
+            for (int i = start; i < end; i++) {
+                for (int j = 0; j < size; j++) {
+                    sum[ALF_DIR_VERT]  += gradient[ALF_DIR_VERT][yg + i][xg + j];
+                    sum[ALF_DIR_HORZ]  += gradient[ALF_DIR_HORZ][yg + i][xg + j];
+                    sum[ALF_DIR_DIGA0] += gradient[ALF_DIR_DIGA0][yg + i][xg + j];
+                    sum[ALF_DIR_DIGA1] += gradient[ALF_DIR_DIGA1][yg + i][xg + j];
+                }
+            }
+            FUNC(alf_get_idx)(sum, ac, &class_idx, &transpose_idx);
+            FUNC(alf_reconstruct_coeff_and_clip)(&coeff_set[class_to_filt[class_idx] * ALF_NUM_COEFF_LUMA],
+                &clip_idx_set[class_idx * ALF_NUM_COEFF_LUMA], transpose_idx, coeff, clip);
+
+            coeff += ALF_NUM_COEFF_LUMA;
+            clip  += ALF_NUM_COEFF_LUMA;
+        }
+    }
+}
+
+#undef ALF_GRADIENT_STEP
+#undef ALF_DIR_HORZ
+#undef ALF_DIR_VERT
+#undef ALF_DIR_DIGA0
+#undef ALF_DIR_DIGA1
+#undef ALF_NUM_DIR
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////////////////
+static void FUNC(put_vvc_pel_pixels)(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 int hf_idx, const int vf_idx)
+{
+    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_vvc_pel_uni_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int height,
+    const intptr_t mx, const intptr_t my, const int width, const int hf_idx, const int vf_idx)
+{
+    const pixel *src            = (const pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    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_vvc_pel_bi_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src          = (const pixel *)_src;
+    const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    pixel *dst                = (pixel *)_dst;
+    const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+
+    const int shift           = 14  + 1 - 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(((src[x] << (14 - BIT_DEPTH)) + src2[x] + offset) >> shift);
+        src  += src_stride;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+
+#if 0
+static void FUNC(put_vvc_pel_uni_w_pixels)(uint8_t *_dst, ptrdiff_t _dst_stride, uint8_t *_src, ptrdiff_t _src_stride,
+                                            int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width)
+{
+    int x, y;
+    pixel *src          = (pixel *)_src;
+    ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    pixel *dst          = (pixel *)_dst;
+    ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+    int offset = 1 << (shift - 1);
+#else
+    int offset = 0;
+#endif
+
+    ox     = ox * (1 << (BIT_DEPTH - 8));
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++)
+            dst[x] = av_clip_pixel((((src[x] << (14 - BIT_DEPTH)) * wx + offset) >> shift) + ox);
+        src += src_stride;
+        dst += dst_stride;
+    }
+}
+#endif
+static void FUNC(put_vvc_pel_bi_w_pixels)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride,  const int16_t *src2,
+    const int height, const int denom, const int wx0, const int wx1,
+    int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src          = (const pixel *)_src;
+    const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    pixel *dst                = (pixel *)_dst;
+    const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+
+    const int shift           = 14  + 1 - BIT_DEPTH;
+    const int log2Wd          = denom + shift - 1;
+
+    ox0 = ox0 * (1 << (BIT_DEPTH - 8));
+    ox1 = ox1 * (1 << (BIT_DEPTH - 8));
+    for (int y = 0; y < height; y++) {
+        for (int x = 0; x < width; x++)
+            dst[x] = av_clip_pixel(( (src[x] << (14 - BIT_DEPTH)) * wx1 + src2[x] * wx0 + (ox0 + ox1 + 1) * (1 << log2Wd)) >> (log2Wd + 1));
+        src  += src_stride;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////////////////
+#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_vvc_luma_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 int hf_idx, const int vf_idx)
+{
+    const pixel *src          = (const pixel*)_src;
+    const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    const int8_t *filter      = ff_vvc_luma_filters[hf_idx][mx];
+    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_vvc_luma_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 int hf_idx, const int vf_idx)
+{
+    const pixel *src          = (pixel*)_src;
+    const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    const int8_t *filter      = ff_vvc_luma_filters[vf_idx][my];
+    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_vvc_luma_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,
+    const int hf_idx, const int vf_idx)
+{
+    int x, y;
+    const int8_t *filter;
+    const pixel *src = (const pixel*)_src;
+    const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp = tmp_array;
+
+    src   -= LUMA_EXTRA_BEFORE * src_stride;
+    filter = ff_vvc_luma_filters[hf_idx][mx];
+    for (y = 0; y < height + LUMA_EXTRA; y++) {
+        for (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 = ff_vvc_luma_filters[vf_idx][my];
+    for (y = 0; y < height; y++) {
+        for (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_vvc_luma_uni_h)(uint8_t *_dst,  const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src          = (const pixel*)_src;
+    const ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    pixel *dst                = (pixel *)_dst;
+    const ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    const int8_t *filter      = ff_vvc_luma_filters[hf_idx][mx];
+    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_vvc_luma_bi_h)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    pixel        *src       = (pixel*)_src;
+    ptrdiff_t     src_stride = _src_stride / sizeof(pixel);
+    pixel *dst              = (pixel *)_dst;
+    ptrdiff_t dst_stride     = _dst_stride / sizeof(pixel);
+
+    const int8_t *filter    = ff_vvc_luma_filters[hf_idx][mx];
+
+    const int shift         = 14  + 1 - BIT_DEPTH;
+#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(((LUMA_FILTER(src, 1) >> (BIT_DEPTH - 8)) + src2[x] + offset) >> shift);
+        src  += src_stride;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+
+static void FUNC(put_vvc_luma_uni_v)(uint8_t *_dst,  const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+
+    const pixel *src            = (const pixel*)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    const int8_t *filter        = ff_vvc_luma_filters[vf_idx][my];
+    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_vvc_luma_bi_v)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src            = (pixel*)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+
+    const int8_t *filter        = ff_vvc_luma_filters[vf_idx][my];
+
+    const int shift             = 14 + 1 - 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)) + src2[x] + offset) >> shift);
+        src  += src_stride;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+
+static void FUNC(put_vvc_luma_uni_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    int x, y;
+    const int8_t *filter;
+    const pixel *src            = (const pixel*)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp = tmp_array;
+    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;
+    filter = ff_vvc_luma_filters[hf_idx][mx];
+    for (y = 0; y < height + LUMA_EXTRA; y++) {
+        for (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 = ff_vvc_luma_filters[vf_idx][my];
+
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++) {
+            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_vvc_luma_bi_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    int x, y;
+    const int8_t *filter;
+    const pixel *src            = (const pixel*)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp = tmp_array;
+    const int shift             = 14 + 1 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+    const int offset            = 1 << (shift - 1);
+#else
+    const int offset            = 0;
+#endif
+
+    src   -= LUMA_EXTRA_BEFORE * src_stride;
+    filter = ff_vvc_luma_filters[hf_idx][mx];
+    for (y = 0; y < height + LUMA_EXTRA; y++) {
+        for (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 = ff_vvc_luma_filters[vf_idx][my];
+
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++)
+            dst[x] = av_clip_pixel(((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift);
+        tmp  += MAX_PB_SIZE;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+#if 0
+static void FUNC(put_vvc_luma_uni_w_h)(uint8_t *_dst,  ptrdiff_t _dst_stride,
+                                        uint8_t *_src, ptrdiff_t _src_stride,
+                                        int height, int denom, int wx, int ox,
+                                        intptr_t mx, intptr_t my, int width, int hf_idx, int vf_idx)
+{
+    int x, y;
+    pixel        *src       = (pixel*)_src;
+    ptrdiff_t     src_stride = _src_stride / sizeof(pixel);
+    pixel *dst          = (pixel *)_dst;
+    ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    const int8_t *filter    = ff_vvc_luma_filters[hf_idx];
+    int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+    int offset = 1 << (shift - 1);
+#else
+    int offset = 0;
+#endif
+
+    ox = ox * (1 << (BIT_DEPTH - 8));
+    for (y = 0; y < height; y++) {
+        for (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;
+    }
+}
+#endif
+static void FUNC(put_vvc_luma_bi_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const int denom, const int wx0, const int wx1,
+    int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src            = (pixel*)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+
+    const int8_t *filter        = ff_vvc_luma_filters[hf_idx][mx];
+
+    const int shift             = 14  + 1 - BIT_DEPTH;
+    const int log2Wd            = denom + shift - 1;
+
+    ox0 = ox0 * (1 << (BIT_DEPTH - 8));
+    ox1 = ox1 * (1 << (BIT_DEPTH - 8));
+    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)) * wx1 + src2[x] * wx0 +
+                                    ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
+        src  += src_stride;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+#if 0
+static void FUNC(put_vvc_luma_uni_w_v)(uint8_t *_dst,  ptrdiff_t _dst_stride,
+                                        uint8_t *_src, ptrdiff_t _src_stride,
+                                        int height, int denom, int wx, int ox,
+                                        intptr_t mx, intptr_t my, int width)
+{
+    int x, y;
+    pixel        *src       = (pixel*)_src;
+    ptrdiff_t     src_stride = _src_stride / sizeof(pixel);
+    pixel *dst          = (pixel *)_dst;
+    ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    const int8_t *filter    = ff_vvc_luma_filters[my];
+    int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+    int offset = 1 << (shift - 1);
+#else
+    int offset = 0;
+#endif
+
+    ox = ox * (1 << (BIT_DEPTH - 8));
+    for (y = 0; y < height; y++) {
+        for (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;
+    }
+}
+#endif
+static void FUNC(put_vvc_luma_bi_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const int denom, const int wx0, const int wx1,
+    int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, int const vf_idx)
+{
+    const pixel *src            = (const pixel*)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+
+    const int8_t *filter        = ff_vvc_luma_filters[vf_idx][my];
+
+    const int shift             = 14 + 1 - BIT_DEPTH;
+    const int log2Wd            = denom + shift - 1;
+
+    ox0     = ox0 * (1 << (BIT_DEPTH - 8));
+    ox1     = ox1 * (1 << (BIT_DEPTH - 8));
+    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)) * wx1 + src2[x] * wx0 +
+                                    ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
+        src  += src_stride;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+#if 0
+static void FUNC(put_vvc_luma_uni_w_hv)(uint8_t *_dst,  ptrdiff_t _dst_stride,
+                                         uint8_t *_src, ptrdiff_t _src_stride,
+                                         int height, int denom, int wx, int ox,
+                                         intptr_t mx, intptr_t my, int width)
+{
+    int x, y;
+    const int8_t *filter;
+    pixel *src = (pixel*)_src;
+    ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    pixel *dst          = (pixel *)_dst;
+    ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp = tmp_array;
+    int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+    int offset = 1 << (shift - 1);
+#else
+    int offset = 0;
+#endif
+
+    src   -= LUMA_EXTRA_BEFORE * src_stride;
+    filter = ff_vvc_luma_filters[hf_idx][mx];
+    for (y = 0; y < height + LUMA_EXTRA; y++) {
+        for (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 = ff_vvc_luma_filters[vf_idx][my];
+
+    ox = ox * (1 << (BIT_DEPTH - 8));
+    for (y = 0; y < height; y++) {
+        for (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;
+    }
+}
+#endif
+
+static void FUNC(put_vvc_luma_bi_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const int denom, const int wx0, const int wx1,
+    int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    int x, y;
+    const int8_t *filter;
+    const pixel *src            = (pixel*)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    int16_t tmp_array[(MAX_PB_SIZE + LUMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp = tmp_array;
+    const int shift             = 14 + 1 - BIT_DEPTH;
+    const int log2Wd            = denom + shift - 1;
+
+    src   -= LUMA_EXTRA_BEFORE * src_stride;
+    filter = ff_vvc_luma_filters[hf_idx][mx];
+    for (y = 0; y < height + LUMA_EXTRA; y++) {
+        for (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 = ff_vvc_luma_filters[vf_idx][my];
+
+    ox0     = ox0 * (1 << (BIT_DEPTH - 8));
+    ox1     = ox1 * (1 << (BIT_DEPTH - 8));
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++)
+            dst[x] = av_clip_pixel(((LUMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 +
+                                    ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
+        tmp  += MAX_PB_SIZE;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+////////////////////////////////////////////////////////////////////////////////
+#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_vvc_chroma_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 int hf_idx, const int vf_idx)
+{
+    const pixel *src        = (const pixel *)_src;
+    ptrdiff_t src_stride     = _src_stride / sizeof(pixel);
+    const int8_t *filter    = ff_vvc_chroma_filters[hf_idx][mx];
+    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_vvc_chroma_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 int hf_idx, const int vf_idx)
+{
+    const pixel *src            = (const pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    const int8_t *filter        = ff_vvc_chroma_filters[vf_idx][my];
+
+    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_vvc_chroma_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,
+    const int hf_idx, const int vf_idx)
+{
+    int x, y;
+    const pixel *src            = (const pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    const int8_t *filter        = ff_vvc_chroma_filters[hf_idx][mx];
+    int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp = tmp_array;
+
+    src -= CHROMA_EXTRA_BEFORE * src_stride;
+
+    for (y = 0; y < height + CHROMA_EXTRA; y++) {
+        for (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 = ff_vvc_chroma_filters[vf_idx][my];
+
+    for (y = 0; y < height; y++) {
+        for (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_vvc_chroma_uni_h)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src            = (const pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    const int8_t *filter        = ff_vvc_chroma_filters[hf_idx][mx];
+    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_vvc_chroma_bi_h)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src            = (const pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    const int8_t *filter        = ff_vvc_chroma_filters[hf_idx][mx];
+    const int shift             = 14 + 1 - 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)) + src2[x] + offset) >> shift);
+        dst  += dst_stride;
+        src  += src_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+
+static void FUNC(put_vvc_chroma_uni_v)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src            = (const pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    const int8_t *filter        = ff_vvc_chroma_filters[vf_idx][my];
+    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_vvc_chroma_bi_v)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    pixel *src = (pixel *)_src;
+    ptrdiff_t src_stride  = _src_stride / sizeof(pixel);
+    const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my];
+    pixel *dst          = (pixel *)_dst;
+    ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    int shift = 14 + 1 - BIT_DEPTH;
+#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)) + src2[x] + offset) >> shift);
+        dst  += dst_stride;
+        src  += src_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+
+static void FUNC(put_vvc_chroma_uni_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    int x, y;
+    pixel *src = (pixel *)_src;
+    ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    pixel *dst          = (pixel *)_dst;
+    ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx];
+    int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp = tmp_array;
+    int shift = 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+    int offset = 1 << (shift - 1);
+#else
+    int offset = 0;
+#endif
+
+    src -= CHROMA_EXTRA_BEFORE * src_stride;
+
+    for (y = 0; y < height + CHROMA_EXTRA; y++) {
+        for (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 = ff_vvc_chroma_filters[vf_idx][my];
+
+    for (y = 0; y < height; y++) {
+        for (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_vvc_chroma_bi_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    int x, y;
+    const pixel *src            = (pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx];
+    int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp                = tmp_array;
+    const int shift             = 14 + 1 - 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 (y = 0; y < height + CHROMA_EXTRA; y++) {
+        for (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 = ff_vvc_chroma_filters[vf_idx][my];
+
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++)
+            dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) + src2[x] + offset) >> shift);
+        tmp  += MAX_PB_SIZE;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+#if 0
+static void FUNC(put_vvc_chroma_uni_w_h)(uint8_t *_dst, ptrdiff_t _dst_stride, uint8_t *_src, ptrdiff_t _src_stride,
+                                        int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width)
+{
+    int x, y;
+    pixel *src = (pixel *)_src;
+    ptrdiff_t src_stride  = _src_stride / sizeof(pixel);
+    pixel *dst          = (pixel *)_dst;
+    ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx];
+    int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+    int offset = 1 << (shift - 1);
+#else
+    int offset = 0;
+#endif
+
+    ox     = ox * (1 << (BIT_DEPTH - 8));
+    for (y = 0; y < height; y++) {
+        for (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;
+    }
+}
+#endif
+static void FUNC(put_vvc_chroma_bi_w_h)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const int denom, const int wx0, const int wx1,
+    int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src            = (pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    const int8_t *filter        = ff_vvc_chroma_filters[hf_idx][mx];
+    const int shift             = 14 + 1 - BIT_DEPTH;
+    const int log2Wd            = denom + shift - 1;
+
+    ox0 = ox0 * (1 << (BIT_DEPTH - 8));
+    ox1 = ox1 * (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)) * wx1 + src2[x] * wx0 +
+                                    ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
+        src  += src_stride;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+#if 0
+static void FUNC(put_vvc_chroma_uni_w_v)(uint8_t *_dst, ptrdiff_t _dst_stride, uint8_t *_src, ptrdiff_t _src_stride,
+                                        int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width)
+{
+    int x, y;
+    pixel *src = (pixel *)_src;
+    ptrdiff_t src_stride  = _src_stride / sizeof(pixel);
+    pixel *dst          = (pixel *)_dst;
+    ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    const int8_t *filter = ff_vvc_chroma_filters[vf_idx][my];
+    int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+    int offset = 1 << (shift - 1);
+#else
+    int offset = 0;
+#endif
+
+    ox     = ox * (1 << (BIT_DEPTH - 8));
+    for (y = 0; y < height; y++) {
+        for (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;
+    }
+}
+#endif
+static void FUNC(put_vvc_chroma_bi_w_v)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const   int16_t *src2,
+    const int height, const int denom, const int wx0, const int wx1,
+    int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    const pixel *src            = (pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    const int8_t *filter        = ff_vvc_chroma_filters[vf_idx][my];
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    const int shift             = 14 + 1 - BIT_DEPTH;
+    const int log2Wd            = denom + shift - 1;
+
+    ox0 = ox0 * (1 << (BIT_DEPTH - 8));
+    ox1 = ox1 * (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, src_stride) >> (BIT_DEPTH - 8)) * wx1 + src2[x] * wx0 +
+                                    ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
+        src  += src_stride;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+#if 0
+static void FUNC(put_vvc_chroma_uni_w_hv)(uint8_t *_dst, ptrdiff_t _dst_stride, uint8_t *_src, ptrdiff_t _src_stride,
+                                         int height, int denom, int wx, int ox, intptr_t mx, intptr_t my, int width)
+{
+    int x, y;
+    pixel *src = (pixel *)_src;
+    ptrdiff_t src_stride = _src_stride / sizeof(pixel);
+    pixel *dst          = (pixel *)_dst;
+    ptrdiff_t dst_stride = _dst_stride / sizeof(pixel);
+    const int8_t *filter = ff_vvc_chroma_filters[hf_idx][mx];
+    int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp = tmp_array;
+    int shift = denom + 14 - BIT_DEPTH;
+#if BIT_DEPTH < 14
+    int offset = 1 << (shift - 1);
+#else
+    int offset = 0;
+#endif
+
+    src -= CHROMA_EXTRA_BEFORE * src_stride;
+
+    for (y = 0; y < height + CHROMA_EXTRA; y++) {
+        for (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 = ff_vvc_chroma_filters[vf_idx][my];
+
+    ox     = ox * (1 << (BIT_DEPTH - 8));
+    for (y = 0; y < height; y++) {
+        for (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;
+    }
+}
+#endif
+static void FUNC(put_vvc_chroma_bi_w_hv)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const uint8_t *_src, const ptrdiff_t _src_stride, const int16_t *src2,
+    const int height, const int denom, const int wx0, const int wx1,
+    int ox0, int ox1, const intptr_t mx, const intptr_t my, const int width,
+    const int hf_idx, const int vf_idx)
+{
+    int x, y;
+    const pixel *src            = (pixel *)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    pixel *dst                  = (pixel *)_dst;
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    const int8_t *filter        = ff_vvc_chroma_filters[hf_idx][mx];
+    int16_t tmp_array[(MAX_PB_SIZE + CHROMA_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp = tmp_array;
+    const int shift             = 14 + 1 - BIT_DEPTH;
+    const int log2Wd            = denom + shift - 1;
+
+    src -= CHROMA_EXTRA_BEFORE * src_stride;
+
+    for (y = 0; y < height + CHROMA_EXTRA; y++) {
+        for (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 = ff_vvc_chroma_filters[vf_idx][my];
+
+    ox0     = ox0 * (1 << (BIT_DEPTH - 8));
+    ox1     = ox1 * (1 << (BIT_DEPTH - 8));
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++)
+            dst[x] = av_clip_pixel(((CHROMA_FILTER(tmp, MAX_PB_SIZE) >> 6) * wx1 + src2[x] * wx0 +
+                                    ((ox0 + ox1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
+        tmp  += MAX_PB_SIZE;
+        dst  += dst_stride;
+        src2 += MAX_PB_SIZE;
+    }
+}
+
+static void FUNC(put_vvc_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 int dst_stride   = _dst_stride / sizeof(pixel);
+    const int 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_vvc_gpm)(uint8_t *_dst, ptrdiff_t dst_stride, int width, int height,
+    const int16_t *tmp, const int16_t *tmp1, const ptrdiff_t tmp_stride,
+    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((tmp[x] * w + tmp1[x] * (8 - w) + offset) >> shift);
+        }
+        dst     += dst_stride;
+        tmp     += tmp_stride;
+        tmp1    += tmp_stride;
+        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
+
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    pixel *dst                  = (pixel*)_dst;
+
+    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_bi)(uint8_t *_dst, const ptrdiff_t _dst_stride,
+    const int16_t *src0, const int16_t *src1, const int16_t *diff_mv_x, const int16_t *diff_mv_y)
+{
+    const int limit             = (1 << FFMAX(13, BIT_DEPTH + 1));          ///< dILimit
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    pixel *dst                  = (pixel*)_dst;
+
+
+    const int shift             = 14 + 1 - 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, src1, 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 = src1[x] + av_clip(di, -limit, limit - 1);
+            dst[x] = av_clip_pixel((val + src0[x] + offset) >> shift);
+
+        }
+        src0 += MAX_PB_SIZE;
+        src1 += MAX_PB_SIZE;
+        dst += dst_stride;
+    }
+}
+
+static void FUNC(apply_prof_bi_w)(uint8_t *_dst, const ptrdiff_t _dst_stride, const int16_t *src0, const int16_t *src1,
+    const int16_t *diff_mv_x, const int16_t *diff_mv_y, const int denom, const int w0, const int w1, int o0, int o1)
+{
+    const int limit             = (1 << FFMAX(13, BIT_DEPTH + 1));          ///< dILimit
+    const ptrdiff_t dst_stride   = _dst_stride / sizeof(pixel);
+    pixel *dst                  = (pixel*)_dst;
+
+
+    const int shift             = 14 + 1 - BIT_DEPTH;
+    const int log2Wd            = denom + shift - 1;
+
+    int16_t gradient_h[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE];
+    int16_t gradient_v[AFFINE_MIN_BLOCK_SIZE * AFFINE_MIN_BLOCK_SIZE];
+
+    o0 = o0 * (1 << (BIT_DEPTH - 8));
+    o1 = o1 * (1 << (BIT_DEPTH - 8));
+
+    FUNC(prof_grad_filter)(gradient_h, gradient_v, AFFINE_MIN_BLOCK_SIZE, src1, 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 = src1[x] + av_clip(di, -limit, limit - 1);
+            dst[x] = av_clip_pixel((val * w1 + src0[x] * w0 +  ((o0 + o1 + 1) * (1 << log2Wd))) >> (log2Wd + 1));
+        }
+        src0 += MAX_PB_SIZE;
+        src1 += 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_vvc_luma)(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);
+#endif
+
+    for (int y = 0; y < height; y++) {
+        for (int x = 0; x < width; x++) {
+#if BIT_DEPTH > 10
+            dst[x] = (src[x] + offset4) >> shift4;
+#else
+            dst[x] = src[x] << (10 - BIT_DEPTH);
+#endif
+        }
+        src += src_stride;
+        dst += MAX_PB_SIZE;
+    }
+
+}
+
+//8.5.3.2.2 Luma sample bilinear interpolation process
+static void FUNC(dmvr_vvc_luma_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_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_vvc_luma_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_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_vvc_luma_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)
+{
+    int x, y;
+    const int8_t *filter;
+    const pixel *src            = (pixel*)_src;
+    const ptrdiff_t src_stride   = _src_stride / sizeof(pixel);
+    int16_t tmp_array[(MAX_PB_SIZE + BILINEAR_EXTRA) * MAX_PB_SIZE];
+    int16_t *tmp                = tmp_array;
+    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;
+    filter = ff_vvc_dmvr_filters[mx];
+    for (y = 0; y < height + BILINEAR_EXTRA; y++) {
+        for (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_dmvr_filters[my];
+    for (y = 0; y < height; y++) {
+        for (x = 0; x < width; x++)
+            dst[x] = (DMVR_FILTER(tmp, MAX_PB_SIZE) + offset2) >> shift2;
+        tmp += MAX_PB_SIZE;
+        dst += MAX_PB_SIZE;
+    }
+}
+
+// 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(vvc_loop_filter_luma)(uint8_t* _pix,
+    ptrdiff_t _xstride, ptrdiff_t _ystride,
+    int _beta, int _tc,
+    uint8_t _no_p, uint8_t _no_q,
+    uint8_t max_len_p, uint8_t max_len_q,
+    int hor_ctu_edge)
+{
+    int d;
+    pixel* pix = (pixel*)_pix;
+    ptrdiff_t xstride = _xstride / sizeof(pixel);
+    ptrdiff_t ystride = _ystride / sizeof(pixel);
+
+    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 + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH);
+#else
+    const int tc = _tc << (BIT_DEPTH - 10);
+#endif
+    const int no_p = _no_p;
+    const int no_q = _no_q;
+
+    const int large_p = (max_len_p > 3 && !hor_ctu_edge);
+    const int large_q = max_len_q > 3;
+    const int beta = _beta << BIT_DEPTH - 8;
+
+    const int beta_3 = beta >> 3;
+    const int beta_2 = beta >> 2;
+    const int tc25 = ((tc * 5 + 1) >> 1);
+
+
+    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) {
+                for (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;
+                }
+                return;
+
+            }
+        }
+    }
+    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) {
+            // strong filtering
+            const int tc2 = tc << 1;
+            const int tc3 = tc * 3;
+            for (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;
+            }
+        } else { // weak filtering
+            int nd_p = 1;
+            int nd_q = 1;
+            const int tc_2 = tc >> 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;
+            }
+
+            for (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_chroma)(uint8_t *_pix, const ptrdiff_t  _xstride,
+    const ptrdiff_t _ystride, const int _beta, const int _tc, const uint8_t no_p, const uint8_t no_q,
+    const int shift,  int max_len_p, int max_len_q)
+{
+    pixel *pix        = (pixel *)_pix;
+    const ptrdiff_t xstride = _xstride / sizeof(pixel);
+    const ptrdiff_t ystride = _ystride / sizeof(pixel);
+    const int end  = shift ? 2 : 4;
+    const int beta = _beta << (BIT_DEPTH - 8);
+    const int beta_3 = beta >> 3;
+    const int beta_2 = beta >> 2;
+
+#if BIT_DEPTH < 10
+    const int tc = (_tc + (1 << (9 - BIT_DEPTH))) >> (10 - BIT_DEPTH);
+#else
+    const int tc = _tc << (BIT_DEPTH - 10);
+#endif
+    const int tc25 = ((tc * 5 + 1) >> 1);
+
+    if (!max_len_p || !max_len_q)
+        return;
+
+    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) {
+        //strong
+        for (int d = 0; d < end; 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;
+        }
+    } else if (max_len_q == 3) {
+        for (int d = 0; d < end; 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;
+        }
+    } else {
+        //weak
+        for (int d = 0; d < end; 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_h_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride,
+    int beta, int32_t tc, uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q)
+{
+    FUNC(vvc_loop_filter_chroma)(pix, stride, sizeof(pixel), beta, tc, no_p, no_q, shift, max_len_p, max_len_q);
+}
+
+static void FUNC(vvc_v_loop_filter_chroma)(uint8_t *pix, ptrdiff_t stride,
+    int beta, int32_t tc, uint8_t no_p, uint8_t no_q, int shift, int max_len_p, int max_len_q)
+{
+    FUNC(vvc_loop_filter_chroma)(pix, sizeof(pixel), stride, beta, tc, no_p, no_q, shift, max_len_p, max_len_q);
+}
+
+static void FUNC(vvc_h_loop_filter_luma)(uint8_t *pix, ptrdiff_t stride,
+                                          int beta, int32_t tc, uint8_t no_p,
+                                          uint8_t no_q, uint8_t max_len_p, 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,
+                                          int beta, int32_t tc, uint8_t no_p,
+                                          uint8_t no_q, uint8_t max_len_p, uint8_t max_len_q)
+{
+    FUNC(vvc_loop_filter_luma)(pix, sizeof(pixel), stride,
+                                beta, tc, no_p, no_q, max_len_p, max_len_q, 0);
+}
+
+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
diff --git a/libavcodec/vvcpred.c b/libavcodec/vvcpred.c
new file mode 100644
index 0000000000..983abe8e6e
--- /dev/null
+++ b/libavcodec/vvcpred.c
@@ -0,0 +1,321 @@
+/*
+ * VVC video 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.
+ *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * FFmpeg is distributed in the hope that it will be useful,
+ * 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 "vvcpred.h"
+#include "vvc_ctu.h"
+
+typedef struct IntraEdgeParams {
+    uint8_t* top;
+    uint8_t* left;
+    int filter_flag;
+
+    uint16_t left_array[3 * MAX_TB_SIZE + 3];
+    uint16_t filtered_left_array[3 * MAX_TB_SIZE + 3];
+    uint16_t top_array[3 * MAX_TB_SIZE + 3];
+    uint16_t filtered_top_array[3 * MAX_TB_SIZE + 3];
+} IntraEdgeParams;
+
+#define BIT_DEPTH 8
+#include "vvcpred_template.c"
+#undef BIT_DEPTH
+
+#define BIT_DEPTH 9
+#include "vvcpred_template.c"
+#undef BIT_DEPTH
+
+#define BIT_DEPTH 10
+#include "vvcpred_template.c"
+#undef BIT_DEPTH
+
+#define BIT_DEPTH 12
+#include "vvcpred_template.c"
+#undef BIT_DEPTH
+
+void ff_vvc_pred_init(VVCPredContext *hpc, int bit_depth)
+{
+#undef FUNC
+#define FUNC(a, depth) a ## _ ## depth
+
+#define VVC_PRED(depth)                                         \
+    hpc->lmcs_scale_chroma  = FUNC(lmcs_scale_chroma, depth);   \
+    hpc->intra_cclm_pred    = FUNC(intra_cclm_pred, depth);     \
+    hpc->intra_pred         = FUNC(intra_pred, depth);          \
+    hpc->pred_planar        = FUNC(pred_planar, depth);         \
+    hpc->pred_mip           = FUNC(pred_mip, depth);            \
+    hpc->pred_dc            = FUNC(pred_dc, depth);             \
+    hpc->pred_v             = FUNC(pred_v, depth);              \
+    hpc->pred_h             = FUNC(pred_h, depth);              \
+    hpc->pred_angular_v     = FUNC(pred_angular_v, depth);      \
+    hpc->pred_angular_h     = FUNC(pred_angular_h, depth);      \
+
+
+    switch (bit_depth) {
+    case 9:
+        VVC_PRED(9);
+        break;
+    case 10:
+        VVC_PRED(10);
+        break;
+    case 12:
+        VVC_PRED(12);
+        break;
+    default:
+        VVC_PRED(8);
+        break;
+    }
+}
+
+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->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);
+}
+
+const int8_t ff_vvc_filter_c[NUM_INTRA_LUMA_FACTS][NUM_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 },
+};
+
+#define FILTER_G(fact)  { 16 - (fact >> 1), 32 - (fact >> 1), 16 + (fact >> 1), fact >> 1}
+const int8_t ff_vvc_filter_g[NUM_INTRA_LUMA_FACTS][NUM_INTRA_LUMA_TAPS] = {
+    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),
+
+};
diff --git a/libavcodec/vvcpred.h b/libavcodec/vvcpred.h
new file mode 100644
index 0000000000..b48ba5a256
--- /dev/null
+++ b/libavcodec/vvcpred.h
@@ -0,0 +1,67 @@
+/*
+ * VVC video 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
+ */
+
+#ifndef AVCODEC_VVCPRED_H
+#define AVCODEC_VVCPRED_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "vvcdsp.h"
+
+struct VVCLocalContext;
+
+typedef struct VVCPredContext {
+    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);
+} VVCPredContext;
+
+void ff_vvc_pred_init(VVCPredContext *hpc, int bit_depth);
+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);
+
+#define NUM_INTRA_LUMA_TAPS 4
+#define NUM_INTRA_LUMA_FACTS 32
+extern const int8_t ff_vvc_filter_c[NUM_INTRA_LUMA_FACTS][NUM_INTRA_LUMA_TAPS];
+extern const int8_t ff_vvc_filter_g[NUM_INTRA_LUMA_FACTS][NUM_INTRA_LUMA_TAPS];
+
+#endif /* AVCODEC_VVCPRED_H */
diff --git a/libavcodec/vvcpred_template.c b/libavcodec/vvcpred_template.c
new file mode 100644
index 0000000000..f887e9bdff
--- /dev/null
+++ b/libavcodec/vvcpred_template.c
@@ -0,0 +1,1006 @@
+/*
+ * VVC video decoder
+ *
+ * Copyright (C) 2021-2022 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 "libavutil/pixdesc.h"
+
+#include "bit_depth_template.c"
+#include "vvcpred.h"
+#include "vvc_data.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->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->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->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)
+{
+    SliceContext *sc    = lc->sc;
+    VVCFrameContext *fc = lc->fc;
+    const VVCPH *ph     = fc->ps.ph;
+    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 = ph->lmcs_min_bin_idx; i <= ph->lmcs_max_bin_idx; i++) {
+            if (luma < ph->lmcs_pivot[i + 1])
+                break;
+        }
+        i = FFMIN(i, LMCS_MAX_BIN_SIZE - 1);
+
+        lc->lmcs.chroma_scale = ph->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->hpc.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->hpc.pred_planar((uint8_t *)src, edge.top, edge.left, w, h, stride);
+    } else if (mode == INTRA_DC) {
+        fc->hpc.pred_dc((uint8_t *)src, edge.top, edge.left, w, h, stride);
+    } else if (mode == INTRA_VERT) {
+        fc->hpc.pred_v((uint8_t *)src, edge.top, w, h, stride);
+    } else if (mode == INTRA_HORZ) {
+        fc->hpc.pred_h((uint8_t *)src, edge.left, w, h, stride);
+    } else {
+        if (mode >= INTRA_DIAG) {
+            fc->hpc.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->hpc.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);
+        }
+    }
+}
+
+//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;
+                const pixel pred = p[0];
+                POS(x, y) = pred;
+            }
+        } else {
+            if (!c_idx) {
+                const int8_t* f = filter_flag ? ff_vvc_filter_g[fact] : ff_vvc_filter_c[fact];
+                for (int x = 0; x < w; x++) {
+                    const pixel *p = top + x + idx;
+                    const int pred = (p[0] * f[0] + p[1] * f[1] +
+                                        p[2] * f[2] + p[3] * f[3] + 32) >> 6;
+                    POS(x, y) = av_clip_pixel(pred);
+                }
+            } else {
+                for (int x = 0; x < w; x++) {
+                    const pixel *p = top + x + idx + 1;
+                    const pixel pred = ((32 - fact) * p[0] + fact * p[1] + 16) >> 5;
+                    POS(x, y) = pred;
+                }
+            }
+        }
+        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 = filter_flag ? ff_vvc_filter_g[fact] : ff_vvc_filter_c[fact] ;
+                    pred = (p[0] * f[0] + p[1] * f[1] + p[2] * f[2] + p[3] * f[3] + 32) >> 6;
+                    pred = CLIP(pred);
+                } else {
+                    pred = ((32 - fact) * p[1] + fact * p[2] + 16) >> 5;
+                }
+            }
+            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;
+    }
+}
-- 
2.33.1.windows.1

