Merge branch 'master' into research

Change-Id: Ifbb534b387dfd78a2eab0a3c32cefc4ee5bc82e5
diff --git a/.gitattributes b/.gitattributes
index ffc6912..0bb5faf 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -16,3 +16,8 @@
 *.pjt        -crlf
 *.vcp        -crlf
 *.inf        -crlf
+# fft2d is not available through git, and the code
+# is checked in verbatim. Do not apply diffing to
+# it, otherwise it fails the 'git diff HEAD^ --check'
+# test.
+third_party/tensorflow_dependencies/fft2d/** -diff
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..fae903e
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,27 @@
+[submodule "third_party/tensorflow"]
+	path = third_party/tensorflow
+	url = https://github.com/tensorflow/tensorflow.git
+[submodule "third_party/tensorflow_dependencies/absl"]
+	path = third_party/tensorflow_dependencies/absl
+	url = https://github.com/abseil/abseil-cpp.git
+[submodule "third_party/tensorflow_dependencies/neon_2_sse"]
+	path = third_party/tensorflow_dependencies/neon_2_sse
+	url = https://github.com/intel/ARM_NEON_2_x86_SSE.git
+[submodule "third_party/tensorflow_dependencies/farmhash"]
+	path = third_party/tensorflow_dependencies/farmhash
+	url = https://github.com/google/farmhash
+[submodule "third_party/tensorflow_dependencies/eigen"]
+	path = third_party/tensorflow_dependencies/eigen
+	url = https://gitlab.com/libeigen/eigen.git
+[submodule "third_party/tensorflow_dependencies/flatbuffers"]
+	path = third_party/tensorflow_dependencies/flatbuffers
+	url = https://github.com/google/flatbuffers
+[submodule "third_party/tensorflow_dependencies/gemmlowp"]
+	path = third_party/tensorflow_dependencies/gemmlowp
+	url = https://github.com/google/gemmlowp.git
+[submodule "third_party/tensorflow_dependencies/fp16"]
+	path = third_party/tensorflow_dependencies/fp16
+	url = https://github.com/Maratyszcza/FP16.git
+[submodule "third_party/tensorflow_dependencies/ruy"]
+	path = third_party/tensorflow_dependencies/ruy
+	url = https://github.com/google/ruy.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 80b6d27..bc8f91d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -155,12 +155,18 @@
             "${AOM_ROOT}/common/warnings.h"
             "${AOM_ROOT}/common/y4minput.c"
             "${AOM_ROOT}/common/y4minput.h"
+            "${AOM_ROOT}/common/stream_iter.c"
             "${AOM_ROOT}/examples/encoder_util.h"
             "${AOM_ROOT}/examples/encoder_util.c")
 
-list(APPEND AOM_ENCODER_STATS_SOURCES "${AOM_ROOT}/stats/aomstats.c"
-            "${AOM_ROOT}/stats/aomstats.h" "${AOM_ROOT}/stats/rate_hist.c"
-            "${AOM_ROOT}/stats/rate_hist.h")
+if(CONFIG_SINGLEPASS)
+  list(APPEND AOM_ENCODER_STATS_SOURCES "${AOM_ROOT}/stats/rate_hist.c"
+              "${AOM_ROOT}/stats/rate_hist.h")
+else()
+  list(APPEND AOM_ENCODER_STATS_SOURCES "${AOM_ROOT}/stats/aomstats.c"
+              "${AOM_ROOT}/stats/aomstats.h" "${AOM_ROOT}/stats/rate_hist.c"
+              "${AOM_ROOT}/stats/rate_hist.h")
+endif()
 
 list(APPEND AOM_VERSION_SOURCES "${AOM_CONFIG_DIR}/config/aom_version.h")
 
@@ -598,6 +604,23 @@
   endif()
 endif()
 
+if(CONFIG_TENSORFLOW_LITE)
+  include("${AOM_ROOT}/build/cmake/tensorflow_lite.cmake")
+  setup_tensorflow_lite()
+
+  if(ENABLE_EXAMPLES)
+    add_executable(tf_lite_model "${AOM_ROOT}/examples/tf_lite_model.cc")
+    list(APPEND AOM_EXAMPLE_TARGETS tf_lite_model)
+    list(APPEND AOM_APP_TARGETS tf_lite_model)
+    add_dependencies(tf_lite_model tensorflowlite_a)
+    target_link_libraries(
+      tf_lite_model
+      PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/libtensorflow-lite.a"
+      PRIVATE Threads::Threads
+      PRIVATE ${CMAKE_DL_LIBS})
+  endif()
+endif()
+
 if(ENABLE_EXAMPLES AND "${CMAKE_GENERATOR}" MATCHES "Makefiles$")
 
   # For historical purposes place the example binaries in the example directory.
diff --git a/Sample.cfg b/Sample.cfg
index d5dbe66..6092813 100644
--- a/Sample.cfg
+++ b/Sample.cfg
@@ -1,35 +1,35 @@
 #sample config file
-super_block_size = 128                  # super block size. 0, 64 or 128
+superblock_size = 128                   # super block size. 0, 64 or 128
 max_partition_size = 128                # max partition size(8, 16, 32, 64, 128)
 min_partition_size = 4                  # min partition size(4, 8, 16, 32, 64)
-disable_rect_partition_type = 0         # disable rectangle partition type
-disable_ab_partition_type = 0           # disable AB partition type
-disable_1to4_partition_type = 0         # disable 1 to 4 and 4 to 1 partition type
-disable_intra_angle_delta = 0           # disable intra angle delta
-disable_paeth_intra = 0                 # disable paeth intra
-disable_smooth_intra = 0                # disable intra smooth mode
-disable_intra_edge_filter = 0           # disable intra edge filter
-disable_filter_intra = 0                # disable filter intra
-disable_intrabc = 0                     # disable Intra Block Copy
-disable_cfl = 0                         # disable chroma from luma prediction
-disable_palette = 0                     # disable Palette
-disable_flip_idtx = 0                   # disable flip and identity transform
-disable_tx_64x64 = 0                    # disable 64x64 transform
+enable_rect_partitions = 1              # enable rectangle partition type
+enable_ab_partitions = 1                # enable AB partition type
+enable_1to4_partitions = 1              # enable 1 to 4 and 4 to 1 partition type
+enable_angle_delta = 1                  # enable intra angle delta
+enable_paeth_intra = 1                  # enable paeth intra
+enable_smooth_intra = 1                 # enable intra smooth mode
+enable_intra_edge_filter = 1            # enable intra edge filter
+enable_filter_intra = 1                 # enable filter intra
+enable_intrabc = 1                      # enable Intra Block Copy
+enable_cfl_intra = 1                    # enable chroma from luma prediction
+enable_palette = 1                      # enable Palette
+enable_flip_idtx = 1                    # enable flip and identity transform
+enable_tx64 = 1                         # enable 64x64 transform
 reduced_tx_type_set = 0                 # use reduced transform type set
-reduced_reference_set = 0               # use reduced reference frame set
-disable_obmc = 0                        # disable OBMC
-disable_warp_motion = 0                 # disable Warped Motion
-disable_global_motion = 0               # disable global motion
-disable_ref_frame_mv = 0                # disable ref mv
-disable_dual_filter = 0                 # disable dual interpolation filter
-disable_one_sided_comp = 0              # disable one sided compound mode
-disable_masked_comp = 0                 # disable masked compound prediction
-disable_diff_wtd_comp = 0               # disable difference weighted compound mode
-disable_inter_inter_wedge = 0           # disable inter/inter wedge comp
-disable_dist_wtd_comp = 0               # disable distant weighted compound mode
-disable_inter_intra_comp = 0            # disable inter/intra compound mode.
-disable_inter_intra_wedge = 0           # disable inter/intra wedge comp
-disable_smooth_inter_intra = 0          # disable smooth inter/intra
-disable_cdef = 0                        # disable CDEF filter
-disable_lr = 0                          # disable Loop Restoration Filter
-disable_trellis_quant = 0               # disable trellis quantization
\ No newline at end of file
+enable_reduced_reference_set = 0        # use reduced reference frame set
+enable_obmc = 1                         # enable OBMC
+enable_warped_motion = 1                # enable Warped Motion
+enable_global_motion = 1                # enable global motion
+enable_ref_frame_mvs = 1                # enable ref mv
+enable_dual_filter = 1                  # enable dual interpolation filter
+enable_onesided_comp = 1                # enable one sided compound mode
+enable_masked_comp = 1                  # enable masked compound prediction
+enable_diff_wtd_comp = 1                # enable difference weighted compound mode
+enable_interinter_wedge = 1             # enable inter/inter wedge comp
+enable_dist_wtd_comp = 1                # enable distant weighted compound mode
+enable_interintra_comp = 1              # enable inter/intra compound mode.
+enable_interintra_wedge = 1             # enable inter/intra wedge comp
+enable_smooth_interintra = 1            # enable smooth inter/intra
+enable_cdef = 1                         # enable CDEF filter
+enable_restoration = 1                  # enable Loop Restoration Filter
+disable_trellis_quant = 0               # enable trellis quantization
\ No newline at end of file
diff --git a/aom/aom_encoder.h b/aom/aom_encoder.h
index f36dc4e..5509c3f 100644
--- a/aom/aom_encoder.h
+++ b/aom/aom_encoder.h
@@ -30,6 +30,7 @@
 extern "C" {
 #endif
 
+#include "config/aom_config.h"
 #include "aom/aom_codec.h"
 
 /*!\brief Current ABI version number
@@ -68,6 +69,8 @@
 #define AOM_CODEC_USE_PSNR 0x10000 /**< Calculate PSNR on each frame */
 /*!\brief Make the encoder output one  partition at a time. */
 #define AOM_CODEC_USE_HIGHBITDEPTH 0x40000 /**< Use high bitdepth */
+/*!\brief Print per frame stats. */
+#define AOM_CODEC_USE_PER_FRAME_STATS 0x80000 /**< Enable printing of stats */
 
 /*!\brief Generic fixed size buffer structure
  *
@@ -210,7 +213,7 @@
   /*!\brief Superblock size
    * 0, 64 or 128
    */
-  unsigned int super_block_size;
+  unsigned int superblock_size;
   /*!\brief max partition size
    * 8, 16, 32, 64, 128
    */
@@ -219,126 +222,126 @@
    * 8, 16, 32, 64, 128
    */
   unsigned int min_partition_size;
-  /*!\brief disable AB Shape partition type
+  /*!\brief enable AB Shape partition type
    *
    */
-  unsigned int disable_ab_partition_type;
-  /*!\brief disable rectangular partition type
+  unsigned int enable_ab_partitions;
+  /*!\brief enable rectangular partition type
    *
    */
-  unsigned int disable_rect_partition_type;
-  /*!\brief disable 1:4/4:1 partition type
+  unsigned int enable_rect_partitions;
+  /*!\brief enable 1:4/4:1 partition type
    *
    */
-  unsigned int disable_1to4_partition_type;
-  /*!\brief disable flip and identity transform type
+  unsigned int enable_1to4_partitions;
+  /*!\brief enable flip and identity transform type
    *
    */
-  unsigned int disable_flip_idtx;
-  /*!\brief disable CDEF filter
+  unsigned int enable_flip_idtx;
+  /*!\brief enable CDEF filter
    *
    */
-  unsigned int disable_cdef;
-  /*!\brief disable Loop Restoration Filter
+  unsigned int enable_cdef;
+  /*!\brief enable Loop Restoration Filter
    *
    */
-  unsigned int disable_lr;
-  /*!\brief disable OBMC
+  unsigned int enable_restoration;
+  /*!\brief enable OBMC
    *
    */
-  unsigned int disable_obmc;
-  /*!\brief disable Warped Motion
+  unsigned int enable_obmc;
+  /*!\brief enable Warped Motion
    *
    */
-  unsigned int disable_warp_motion;
-  /*!\brief disable global motion
+  unsigned int enable_warped_motion;
+  /*!\brief enable global motion
    *
    */
-  unsigned int disable_global_motion;
-  /*!\brief disable dist weighted compound
+  unsigned int enable_global_motion;
+  /*!\brief enable dist weighted compound
    *
    */
-  unsigned int disable_dist_wtd_comp;
-  /*!\brief disable diff weighted compound
+  unsigned int enable_dist_wtd_comp;
+  /*!\brief enable diff weighted compound
    *
    */
-  unsigned int disable_diff_wtd_comp;
-  /*!\brief disable inter/intra compound
+  unsigned int enable_diff_wtd_comp;
+  /*!\brief enable inter/intra compound
    *
    */
-  unsigned int disable_inter_intra_comp;
-  /*!\brief disable masked compound
+  unsigned int enable_interintra_comp;
+  /*!\brief enable masked compound
    *
    */
-  unsigned int disable_masked_comp;
-  /*!\brief disable one sided compound
+  unsigned int enable_masked_comp;
+  /*!\brief enable one sided compound
    *
    */
-  unsigned int disable_one_sided_comp;
-  /*!\brief disable Palette
+  unsigned int enable_onesided_comp;
+  /*!\brief enable Palette
    *
    */
-  unsigned int disable_palette;
-  /*!\brief disable Intra Block Copy
+  unsigned int enable_palette;
+  /*!\brief enable Intra Block Copy
    *
    */
-  unsigned int disable_intrabc;
-  /*!\brief disable chroma from luma
+  unsigned int enable_intrabc;
+  /*!\brief enable chroma from luma
    *
    */
-  unsigned int disable_cfl;
-  /*!\brief disable intra smooth mode
+  unsigned int enable_cfl_intra;
+  /*!\brief enable intra smooth mode
    *
    */
-  unsigned int disable_smooth_intra;
-  /*!\brief disable filter intra
+  unsigned int enable_smooth_intra;
+  /*!\brief enable filter intra
    *
    */
-  unsigned int disable_filter_intra;
-  /*!\brief disable dual filter
+  unsigned int enable_filter_intra;
+  /*!\brief enable dual filter
    *
    */
-  unsigned int disable_dual_filter;
-  /*!\brief disable intra angle delta
+  unsigned int enable_dual_filter;
+  /*!\brief enable intra angle delta
    *
    */
-  unsigned int disable_intra_angle_delta;
-  /*!\brief disable intra edge filter
+  unsigned int enable_angle_delta;
+  /*!\brief enable intra edge filter
    *
    */
-  unsigned int disable_intra_edge_filter;
-  /*!\brief disable 64x64 transform
+  unsigned int enable_intra_edge_filter;
+  /*!\brief enable 64x64 transform
    *
    */
-  unsigned int disable_tx_64x64;
-  /*!\brief disable smooth inter/intra
+  unsigned int enable_tx64;
+  /*!\brief enable smooth inter/intra
    *
    */
-  unsigned int disable_smooth_inter_intra;
-  /*!\brief disable inter/inter wedge comp
+  unsigned int enable_smooth_interintra;
+  /*!\brief enable inter/inter wedge comp
    *
    */
-  unsigned int disable_inter_inter_wedge;
-  /*!\brief disable inter/intra wedge comp
+  unsigned int enable_interinter_wedge;
+  /*!\brief enable inter/intra wedge comp
    *
    */
-  unsigned int disable_inter_intra_wedge;
-  /*!\brief disable paeth intra
+  unsigned int enable_interintra_wedge;
+  /*!\brief enable paeth intra
    *
    */
-  unsigned int disable_paeth_intra;
-  /*!\brief disable trellis quantization
+  unsigned int enable_paeth_intra;
+  /*!\brief enable trellis quantization
    *
    */
   unsigned int disable_trellis_quant;
-  /*!\brief disable ref frame MV
+  /*!\brief enable ref frame MV
    *
    */
-  unsigned int disable_ref_frame_mv;
+  unsigned int enable_ref_frame_mvs;
   /*!\brief use reduced reference frame set
    *
    */
-  unsigned int reduced_reference_set;
+  unsigned int enable_reduced_reference_set;
   /*!\brief use reduced transform type set
    *
    */
@@ -570,7 +573,7 @@
   /*!\brief Frame super-resolution q threshold.
    *
    * The q level threshold after which superres is used.
-   * Valid values are 1 to 63.
+   * Valid values are 1 to 255.
    *
    * Used only by AOM_SUPERRES_QTHRESH
    */
@@ -579,7 +582,7 @@
   /*!\brief Keyframe super-resolution q threshold.
    *
    * The q level threshold after which superres is used for key frames.
-   * Valid values are 1 to 63.
+   * Valid values are 1 to 255.
    *
    * Used only by AOM_SUPERRES_QTHRESH
    */
@@ -595,12 +598,14 @@
    */
   enum aom_rc_mode rc_end_usage;
 
+#if !CONFIG_SINGLEPASS
   /*!\brief Two-pass stats buffer.
    *
    * A buffer containing all of the stats packets produced in the first
    * pass, concatenated.
    */
   aom_fixed_buf_t rc_twopass_stats_in;
+#endif  // !CONFIG_SINGLEPASS
 
   /*!\brief first pass mb stats buffer.
    *
@@ -698,6 +703,7 @@
    * 2 pass rate control parameters
    */
 
+#if !CONFIG_SINGLEPASS
   /*!\brief Two-pass mode CBR/VBR bias
    *
    * Bias, expressed on a scale of 0 to 100, for determining target size
@@ -707,6 +713,7 @@
    * encoder should "lean."
    */
   unsigned int rc_2pass_vbr_bias_pct;
+#endif  // !CONFIG_SINGLEPASS
 
   /*!\brief Two-pass mode per-GOP minimum bitrate
    *
@@ -859,7 +866,7 @@
    * at different levels of the pyramid.
    * - If 'fixed_qp_offsets' is also provided, encoder will use the given
    * offsets
-   * - If not, encoder will select the fixed offsets based on the cq-level
+   * - If not, encoder will select the fixed offsets based on the --qp
    *   provided.
    * If a value of 0 is provided and fixed_qp_offset are not provided, encoder
    * will NOT use fixed QP offsets.
@@ -875,7 +882,7 @@
 
   /*!\brief Array of fixed QP offsets
    *
-   * This array specifies fixed QP offsets (range: 0 to 63) for frames at
+   * This array specifies fixed QP offsets (range: 0 to 255) for frames at
    * different levels of the pyramid. It is a comma-separated list of 5 values:
    * - QP offset for keyframe
    * - QP offset for ALTREF frame
@@ -884,7 +891,7 @@
    * - QP offset for 3rd level internal ARF
    * Notes:
    * - QP offset for leaf level frames is not explicitly specified. These frames
-   *   use the worst quality allowed (--cq-level).
+   *   use the worst quality allowed (--qp).
    * - This option is only relevant for --end-usage=q.
    */
   int fixed_qp_offsets[FIXED_QP_OFFSET_COUNT];
diff --git a/aom/aomcx.h b/aom/aomcx.h
index ac69d46..6d852ba 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -227,14 +227,6 @@
    */
   AOME_GET_LAST_QUANTIZER = AOME_SET_STATIC_THRESHOLD + 2,  // 19
 
-  /*!\brief Codec control function to get last quantizer chosen by the encoder,
-   * int* parameter
-   *
-   * Return value uses the 0..63 scale as used by the rc_*_quantizer config
-   * parameters.
-   */
-  AOME_GET_LAST_QUANTIZER_64 = 20,
-
   /*!\brief Codec control function to set the max no of frames to create arf,
    * unsigned int parameter
    */
@@ -255,12 +247,12 @@
   /*!\brief Codec control function to set constrained / constant quality level,
    * unsigned int parameter
    *
-   * Valid range: 0..63
+   * Valid range: 0..255
    *
    * \attention For this value to be used aom_codec_enc_cfg_t::rc_end_usage
    *            must be set to #AOM_CQ or #AOM_Q.
    */
-  AOME_SET_CQ_LEVEL = 25,
+  AOME_SET_QP = 25,
 
   /*!\brief Codec control function to set max data rate for intra frames,
    * unsigned int parameter
@@ -1270,9 +1262,17 @@
    */
   AV1E_SET_VBR_CORPUS_COMPLEXITY_LAP = 157,
 
+  /*!\brief Control to set the subgop config string.
+   */
+  AV1E_SET_SUBGOP_CONFIG_STR = 158,
+
+  /*!\brief Control to set the subgop config path.
+   */
+  AV1E_SET_SUBGOP_CONFIG_PATH = 159,
+
   /*!\brief Control to get baseline gf interval
    */
-  AV1E_GET_BASELINE_GF_INTERVAL = 158,
+  AV1E_GET_BASELINE_GF_INTERVAL = 160,
 };
 
 /*!\brief aom 1-D scaling mode
@@ -1452,8 +1452,8 @@
 AOM_CTRL_USE_TYPE(AOME_SET_TUNING, int) /* aom_tune_metric */
 #define AOM_CTRL_AOME_SET_TUNING
 
-AOM_CTRL_USE_TYPE(AOME_SET_CQ_LEVEL, unsigned int)
-#define AOM_CTRL_AOME_SET_CQ_LEVEL
+AOM_CTRL_USE_TYPE(AOME_SET_QP, unsigned int)
+#define AOM_CTRL_AOME_SET_QP
 
 AOM_CTRL_USE_TYPE(AV1E_SET_ROW_MT, unsigned int)
 #define AOM_CTRL_AV1E_SET_ROW_MT
@@ -1473,9 +1473,6 @@
 AOM_CTRL_USE_TYPE(AOME_GET_LAST_QUANTIZER, int *)
 #define AOM_CTRL_AOME_GET_LAST_QUANTIZER
 
-AOM_CTRL_USE_TYPE(AOME_GET_LAST_QUANTIZER_64, int *)
-#define AOM_CTRL_AOME_GET_LAST_QUANTIZER_64
-
 AOM_CTRL_USE_TYPE(AOME_SET_MAX_INTRA_BITRATE_PCT, unsigned int)
 #define AOM_CTRL_AOME_SET_MAX_INTRA_BITRATE_PCT
 
@@ -1791,6 +1788,12 @@
 AOM_CTRL_USE_TYPE(AV1E_SET_VBR_CORPUS_COMPLEXITY_LAP, unsigned int)
 #define AOM_CTRL_AV1E_SET_VBR_CORPUS_COMPLEXITY_LAP
 
+AOM_CTRL_USE_TYPE(AV1E_SET_SUBGOP_CONFIG_STR, const char *)
+#define AOM_CTRL_AV1E_SET_SUBGOP_CONFIG_STR
+
+AOM_CTRL_USE_TYPE(AV1E_SET_SUBGOP_CONFIG_PATH, const char *)
+#define AOM_CTRL_AV1E_SET_SUBGOP_CONFIG_PATH
+
 /*!\endcond */
 /*! @} - end defgroup aom_encoder */
 #ifdef __cplusplus
diff --git a/aom/src/aom_encoder.c b/aom/src/aom_encoder.c
index bb51c93..a998fbd 100644
--- a/aom/src/aom_encoder.c
+++ b/aom/src/aom_encoder.c
@@ -91,7 +91,7 @@
   /* default values */
   if (cfg) {
     memset(&cfg->encoder_cfg, 0, sizeof(cfg->encoder_cfg));
-    cfg->encoder_cfg.super_block_size = 0;  // Dynamic
+    cfg->encoder_cfg.superblock_size = 0;  // Dynamic
     cfg->encoder_cfg.max_partition_size = 128;
     cfg->encoder_cfg.min_partition_size = 4;
     cfg->encoder_cfg.disable_trellis_quant = 3;
diff --git a/apps/aomenc.c b/apps/aomenc.c
index 581dff9..958913d 100644
--- a/apps/aomenc.c
+++ b/apps/aomenc.c
@@ -11,12 +11,11 @@
 
 #include "apps/aomenc.h"
 
-#include "config/aom_config.h"
-
 #include <assert.h>
 #include <limits.h>
 #include <math.h>
 #include <stdarg.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,6 +33,8 @@
 #include "aom_ports/mem_ops.h"
 #include "common/args.h"
 #include "common/ivfenc.h"
+#include "common/rawenc.h"
+#include "common/stream_iter.h"
 #include "common/tools_common.h"
 #include "common/warnings.h"
 
@@ -43,13 +44,23 @@
 
 #include "common/y4minput.h"
 #include "examples/encoder_util.h"
+#if !CONFIG_SINGLEPASS
 #include "stats/aomstats.h"
+#endif  // !CONFIG_SINGLEPASS
 #include "stats/rate_hist.h"
 
 #if CONFIG_LIBYUV
 #include "third_party/libyuv/include/libyuv/scale.h"
 #endif
 
+#if UINTPTR_MAX == 0xffffffff
+#define ENV_BITS "32 bit "
+#elif UINTPTR_MAX == 0xffffffffffffffff
+#define ENV_BITS "64 bit "
+#else
+#define ENV_BITS "unknown bit "
+#endif
+
 /* Swallow warnings about unused results of fread/fwrite */
 static size_t wrap_fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
   return fread(ptr, size, nmemb, stream);
@@ -95,20 +106,6 @@
   va_end(ap);
 }
 
-static int read_frame(struct AvxInputContext *input_ctx, aom_image_t *img) {
-  FILE *f = input_ctx->file;
-  y4m_input *y4m = &input_ctx->y4m;
-  int shortread = 0;
-
-  if (input_ctx->file_type == FILE_TYPE_Y4M) {
-    if (y4m_input_fetch_frame(y4m, f, img) < 1) return 0;
-  } else {
-    shortread = read_yuv_frame(input_ctx, img);
-  }
-
-  return !shortread;
-}
-
 static int file_is_y4m(const char detect[4]) {
   if (memcmp(detect, "YUV4", 4) == 0) {
     return 1;
@@ -129,6 +126,7 @@
     ARG_DEF("D", "debug", 0, "Debug mode (makes output deterministic)");
 static const arg_def_t outputfile =
     ARG_DEF("o", "output", 1, "Output filename");
+static const arg_def_t reconfile = ARG_DEF(NULL, "recon", 1, "Recon filename");
 static const arg_def_t use_yv12 =
     ARG_DEF(NULL, "yv12", 0, "Input file is YV12 ");
 static const arg_def_t use_i420 =
@@ -138,16 +136,26 @@
 static const arg_def_t use_i444 =
     ARG_DEF(NULL, "i444", 0, "Input file is I444");
 static const arg_def_t codecarg = ARG_DEF(NULL, "codec", 1, "Codec to use");
+#if CONFIG_SINGLEPASS
+static const arg_def_t passes =
+    ARG_DEF("p", "passes", 1, "Number of passes (must be 1)");
+static const arg_def_t pass_arg = ARG_DEF(
+    NULL, "pass", 1, "Pass to execute (0/1). 0 (default) indicates all passes");
+#else
 static const arg_def_t passes =
     ARG_DEF("p", "passes", 1, "Number of passes (1/2)");
 static const arg_def_t pass_arg =
-    ARG_DEF(NULL, "pass", 1, "Pass to execute (1/2)");
+    ARG_DEF(NULL, "pass", 1,
+            "Pass to execute (0/1/2), 0 (default) indicates all passes");
+#endif  // CONFIG_SINGLEPASS
 static const arg_def_t fpf_name =
     ARG_DEF(NULL, "fpf", 1, "First pass statistics file name");
 static const arg_def_t limit =
     ARG_DEF(NULL, "limit", 1, "Stop encoding after n input frames");
 static const arg_def_t skip =
     ARG_DEF(NULL, "skip", 1, "Skip the first n input frames");
+static const arg_def_t step = ARG_DEF(
+    NULL, "step", 1, "Encode every n-th frame (after the --skip frames)");
 static const arg_def_t good_dl =
     ARG_DEF(NULL, "good", 0, "Use Good Quality Deadline");
 static const arg_def_t rt_dl =
@@ -204,12 +212,14 @@
                                         &use_cfg,
                                         &debugmode,
                                         &outputfile,
+                                        &reconfile,
                                         &codecarg,
                                         &passes,
                                         &pass_arg,
                                         &fpf_name,
                                         &limit,
                                         &skip,
+                                        &step,
                                         &good_dl,
                                         &rt_dl,
                                         &quietarg,
@@ -320,10 +330,16 @@
     ARG_DEF_ENUM(NULL, "end-usage", 1, "Rate control mode", end_usage_enum);
 static const arg_def_t target_bitrate =
     ARG_DEF(NULL, "target-bitrate", 1, "Bitrate (kbps)");
-static const arg_def_t min_quantizer =
-    ARG_DEF(NULL, "min-q", 1, "Minimum (best) quantizer");
-static const arg_def_t max_quantizer =
-    ARG_DEF(NULL, "max-q", 1, "Maximum (worst) quantizer");
+static const arg_def_t min_qp_level =
+    ARG_DEF(NULL, "min-qp", 1, "Minimum (best) quantizer");
+static const arg_def_t max_qp_level =
+    ARG_DEF(NULL, "max-qp", 1, "Maximum (worst) quantizer");
+static const arg_def_t min_q_level = ARG_DEF(
+    NULL, "min-q", 1, "Minimum (best) quantizer in range 0 to 63 (DEPRECATED)");
+static const arg_def_t max_q_level =
+    ARG_DEF(NULL, "max-q", 1,
+            "Maximum (worst) quantizer in range 0 to 63 (DEPRECATED)");
+
 static const arg_def_t undershoot_pct =
     ARG_DEF(NULL, "undershoot-pct", 1, "Datarate undershoot (min) target (%)");
 static const arg_def_t overshoot_pct =
@@ -334,8 +350,10 @@
     ARG_DEF(NULL, "buf-initial-sz", 1, "Client initial buffer size (ms)");
 static const arg_def_t buf_optimal_sz =
     ARG_DEF(NULL, "buf-optimal-sz", 1, "Client optimal buffer size (ms)");
+#if !CONFIG_SINGLEPASS
 static const arg_def_t bias_pct =
     ARG_DEF(NULL, "bias-pct", 1, "CBR/VBR bias (0=CBR, 100=VBR)");
+#endif  // !CONFIG_SINGLEPASS
 static const arg_def_t minsection_pct =
     ARG_DEF(NULL, "minsection-pct", 1, "GOP min bitrate (% of target)");
 static const arg_def_t maxsection_pct =
@@ -351,14 +369,16 @@
                                       &superres_kf_qthresh,
                                       &end_usage,
                                       &target_bitrate,
-                                      &min_quantizer,
-                                      &max_quantizer,
+                                      &min_qp_level,
+                                      &max_qp_level,
                                       &undershoot_pct,
                                       &overshoot_pct,
                                       &buf_sz,
                                       &buf_initial_sz,
                                       &buf_optimal_sz,
+#if !CONFIG_SINGLEPASS
                                       &bias_pct,
+#endif  // !CONFIG_SINGLEPASS
                                       &minsection_pct,
                                       &maxsection_pct,
                                       NULL };
@@ -401,8 +421,11 @@
 };
 static const arg_def_t tune_metric =
     ARG_DEF_ENUM(NULL, "tune", 1, "Distortion metric tuned with", tuning_enum);
+static const arg_def_t qp_level =
+    ARG_DEF(NULL, "qp", 1, "Constant/Constrained Quality level");
 static const arg_def_t cq_level =
-    ARG_DEF(NULL, "cq-level", 1, "Constant/Constrained Quality level");
+    ARG_DEF(NULL, "cq-level", 1,
+            "Constant/Constrained Quality level in range 0 to 63 (DEPRECATED)");
 static const arg_def_t max_intra_rate_pct =
     ARG_DEF(NULL, "max-intra-rate", 1, "Max I-frame bitrate (pct)");
 
@@ -806,7 +829,7 @@
 static const arg_def_t use_fixed_qp_offsets =
     ARG_DEF(NULL, "use-fixed-qp-offsets", 1,
             "Enable fixed QP offsets for frames at different levels of the "
-            "pyramid. Selected automatically from --cq-level if "
+            "pyramid. Selected automatically from --qp if "
             "--fixed-qp-offsets is not provided. If this option is not "
             "specified (default), offsets are adaptively chosen by the "
             "encoder.");
@@ -824,6 +847,20 @@
     "Set average corpus complexity per mb for single pass VBR using lap. "
     "(0..10000), default is 0");
 
+static const arg_def_t subgop_config_str =
+    ARG_DEF(NULL, "subgop-config-str", 1,
+            "Set specified SubGOP configurations in string format provided "
+            "for various SubGOP lengths. "
+            "If this option is not specified (default), the configurations "
+            "are chosen by the encoder using a default algorithm.");
+
+static const arg_def_t subgop_config_path =
+    ARG_DEF(NULL, "subgop-config-path", 1,
+            "Set specified SubGOP configurations in config file path provided "
+            "for various SubGOP lengths. "
+            "If this option is not specified (default), the configurations "
+            "are chosen by the encoder using a default algorithm.");
+
 static const arg_def_t *av1_args[] = { &cpu_used_av1,
                                        &auto_altref,
                                        &sharpness,
@@ -836,7 +873,7 @@
                                        &arnr_maxframes,
                                        &arnr_strength,
                                        &tune_metric,
-                                       &cq_level,
+                                       &qp_level,
                                        &max_intra_rate_pct,
                                        &max_inter_rate_pct,
                                        &gf_cbr_boost_pct,
@@ -920,16 +957,11 @@
                                        &set_tier_mask,
                                        &set_min_cr,
                                        &vbr_corpus_complexity_lap,
-                                       &bitdeptharg,
-                                       &inbitdeptharg,
-                                       &input_chroma_subsampling_x,
-                                       &input_chroma_subsampling_y,
-                                       &sframe_dist,
-                                       &sframe_mode,
-                                       &save_as_annexb,
 #if CONFIG_TUNE_VMAF
                                        &vmaf_model_path,
 #endif
+                                       &subgop_config_str,
+                                       &subgop_config_path,
                                        NULL };
 static const int av1_arg_ctrl_map[] = { AOME_SET_CPUUSED,
                                         AOME_SET_ENABLEAUTOALTREF,
@@ -943,7 +975,7 @@
                                         AOME_SET_ARNR_MAXFRAMES,
                                         AOME_SET_ARNR_STRENGTH,
                                         AOME_SET_TUNING,
-                                        AOME_SET_CQ_LEVEL,
+                                        AOME_SET_QP,
                                         AOME_SET_MAX_INTRA_BITRATE_PCT,
                                         AV1E_SET_MAX_INTER_BITRATE_PCT,
                                         AV1E_SET_GF_CBR_BOOST_PCT,
@@ -1030,6 +1062,8 @@
 #if CONFIG_TUNE_VMAF
                                         AV1E_SET_VMAF_MODEL_PATH,
 #endif
+                                        AV1E_SET_SUBGOP_CONFIG_STR,
+                                        AV1E_SET_SUBGOP_CONFIG_PATH,
                                         0 };
 #endif  // CONFIG_AV1_ENCODER
 
@@ -1093,7 +1127,10 @@
 struct stream_config {
   struct aom_codec_enc_cfg cfg;
   const char *out_fn;
+  const char *recon_fn;
+#if !CONFIG_SINGLEPASS
   const char *stats_fn;
+#endif  // !CONFIG_SINGLEPASS
   stereo_format_t stereo_fmt;
   int arg_ctrls[ARG_CTRL_CNT_MAX][2];
   int arg_ctrl_cnt;
@@ -1105,6 +1142,8 @@
 #if CONFIG_TUNE_VMAF
   const char *vmaf_model_path;
 #endif
+  const char *subgop_config_str;
+  const char *subgop_config_path;
 };
 
 struct stream_state {
@@ -1118,12 +1157,14 @@
   uint64_t psnr_samples_total;
   double psnr_totals[4];
   int psnr_count;
-  int counts[64];
+  int counts[256];
   aom_codec_ctx_t encoder;
   unsigned int frames_out;
   uint64_t cx_time;
   size_t nbytes;
+#if !CONFIG_SINGLEPASS
   stats_io_t stats;
+#endif  // !CONFIG_SINGLEPASS
   struct aom_image *img;
   aom_codec_ctx_t decoder;
   int mismatch_seen;
@@ -1145,7 +1186,7 @@
 
 static void init_config(cfg_options_t *config) {
   memset(config, 0, sizeof(cfg_options_t));
-  config->super_block_size = 0;  // Dynamic
+  config->superblock_size = 0;  // Dynamic
   config->max_partition_size = 128;
   config->min_partition_size = 4;
   config->disable_trellis_quant = 3;
@@ -1164,9 +1205,15 @@
   /* Initialize default parameters */
   memset(global, 0, sizeof(*global));
   global->codec = get_aom_encoder_by_index(num_encoder - 1);
-  global->passes = 0;
+  // Set default passes
+#if CONFIG_SINGLEPASS
+  global->passes = 1;
+#else
+  global->passes = 2;
+#endif  // CONFIG_SINGLEPASS
   global->color_type = I420;
   global->csp = AOM_CSP_UNKNOWN;
+  global->step_frames = 1;
 
   int cfg_included = 0;
   init_config(&global->encoder_config);
@@ -1176,7 +1223,9 @@
 
     if (arg_match(&arg, &use_cfg, argi)) {
       if (cfg_included) continue;
-      parse_cfg(arg.val, &global->encoder_config);
+      if (parse_cfg(arg.val, &global->encoder_config)) {
+        die("Reading / parsing error for config file: %s\n", arg.val);
+      }
       cfg_included = 1;
       continue;
     }
@@ -1189,14 +1238,8 @@
         die("Error: Unrecognized argument (%s) to --codec\n", arg.val);
     } else if (arg_match(&arg, &passes, argi)) {
       global->passes = arg_parse_uint(&arg);
-
-      if (global->passes < 1 || global->passes > 2)
-        die("Error: Invalid number of passes (%d)\n", global->passes);
     } else if (arg_match(&arg, &pass_arg, argi)) {
       global->pass = arg_parse_uint(&arg);
-
-      if (global->pass < 1 || global->pass > 2)
-        die("Error: Invalid pass selected (%d)\n", global->pass);
     } else if (arg_match(&arg, &input_chroma_sample_position, argi)) {
       global->csp = arg_parse_enum(&arg);
       /* Flag is used by later code as well, preserve it. */
@@ -1223,7 +1266,12 @@
       global->limit = arg_parse_uint(&arg);
     else if (arg_match(&arg, &skip, argi))
       global->skip_frames = arg_parse_uint(&arg);
-    else if (arg_match(&arg, &psnrarg, argi))
+    else if (arg_match(&arg, &step, argi)) {
+      global->step_frames = arg_parse_uint(&arg);
+      if (global->step_frames == 0) {
+        die("--step must be positive");
+      }
+    } else if (arg_match(&arg, &psnrarg, argi))
       global->show_psnr = 1;
     else if (arg_match(&arg, &recontest, argi))
       global->test_decode = arg_parse_enum_or_int(&arg);
@@ -1245,6 +1293,22 @@
       argj++;
   }
 
+#if CONFIG_SINGLEPASS
+  if (global->passes != 1)
+    die("Error: Invalid number of passes (%d)\n", global->passes);
+#else
+  if (global->passes < 1 || global->passes > 2)
+    die("Error: Invalid number of passes (%d)\n", global->passes);
+#endif  // CONFIG_SINGLEPASS
+#if CONFIG_SINGLEPASS
+  if (global->pass < 0 || global->pass > 1)
+    die("Error: Invalid pass selected (%d)\n", global->pass);
+#else
+  if (global->pass < 0 || global->pass > 2)
+    die("Error: Invalid pass selected (%d)\n", global->pass);
+#endif  // CONFIG_SINGLEPASS
+
+#if !CONFIG_SINGLEPASS
   if (global->pass) {
     /* DWIM: Assume the user meant passes=2 if pass=2 is specified */
     if (global->pass > global->passes) {
@@ -1253,26 +1317,34 @@
       global->passes = global->pass;
     }
   }
+#endif  // !CONFIG_SINGLEPASS
+
   /* Validate global config */
   if (global->passes == 0) {
 #if CONFIG_AV1_ENCODER
     // Make default AV1 passes = 2 until there is a better quality 1-pass
     // encoder
     if (global->codec != NULL)
+#if CONFIG_SINGLEPASS
+      global->passes = 1;
+#else
       global->passes =
           (strcmp(get_short_name_by_aom_encoder(global->codec), "av1") == 0 &&
            global->usage != AOM_USAGE_REALTIME)
               ? 2
               : 1;
+#endif  // CONFIG_SINGLEPASS
 #else
     global->passes = 1;
-#endif
+#endif  // CONFIG_AV1_ENCODER
   }
 
+#if !CONFIG_SINGLEPASS
   if (global->usage == AOM_USAGE_REALTIME && global->passes > 1) {
     warn("Enforcing one-pass encoding in realtime mode\n");
     global->passes = 1;
   }
+#endif  // !CONFIG_SINGLEPASS
 }
 
 static void open_input_file(struct AvxInputContext *input,
@@ -1391,6 +1463,15 @@
     return;
   }
 
+  if (key == AV1E_SET_SUBGOP_CONFIG_STR) {
+    config->subgop_config_str = arg->val;
+    return;
+  }
+  if (key == AV1E_SET_SUBGOP_CONFIG_PATH) {
+    config->subgop_config_path = arg->val;
+    return;
+  }
+
   // For target level, the settings should accumulate rather than overwrite,
   // so we simply append it.
   if (key == AV1E_SET_TARGET_SEQ_LEVEL_IDX) {
@@ -1420,6 +1501,28 @@
   if (j == config->arg_ctrl_cnt) config->arg_ctrl_cnt++;
 }
 
+// Converts quantizer in deprecated range 0 to 63, to qindex in range 0 to 255.
+// Also prints deprecation warning.
+static unsigned int get_qindex_from_quantizer_and_warn(
+    unsigned int quantizer, const char *quantizer_str, const char *qindex_str) {
+  static const int quantizer_to_qindex[] = {
+    0,   4,   8,   12,  16,  20,  24,  28,  32,  36,  40,  44,  48,
+    52,  56,  60,  64,  68,  72,  76,  80,  84,  88,  92,  96,  100,
+    104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152,
+    156, 160, 164, 168, 172, 176, 180, 184, 188, 192, 196, 200, 204,
+    208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 249, 255,
+  };
+  warn(
+      "Option '--%s' (range 0 to 63) is DEPRECATED and will be "
+      "removed in future. Use option '--%s' (range 0 to 255) instead.",
+      quantizer_str, qindex_str);
+  if (quantizer > 63) {
+    fprintf(stderr, "'--%s=%u' out of range (0 to 63)", quantizer_str,
+            quantizer);
+  }
+  return quantizer_to_qindex[quantizer];
+}
+
 static int parse_stream_params(struct AvxEncoderConfig *global,
                                struct stream_state *stream, char **argv) {
   char **argi, **argj;
@@ -1469,8 +1572,10 @@
           config->write_ivf = 0;
         }
       }
+#if !CONFIG_SINGLEPASS
     } else if (arg_match(&arg, &fpf_name, argi)) {
       config->stats_fn = arg.val;
+#endif  // !CONFIG_SINGLEPASS
     } else if (arg_match(&arg, &use_webm, argi)) {
 #if CONFIG_WEBM_IO
       config->write_webm = 1;
@@ -1478,6 +1583,8 @@
 #else
       die("Error: --webm specified but webm is disabled.");
 #endif
+    } else if (arg_match(&arg, &reconfile, argi)) {
+      config->recon_fn = arg.val;
     } else if (arg_match(&arg, &use_ivf, argi)) {
       config->write_webm = 0;
       config->write_ivf = 1;
@@ -1551,10 +1658,18 @@
       config->cfg.rc_end_usage = arg_parse_enum_or_int(&arg);
     } else if (arg_match(&arg, &target_bitrate, argi)) {
       config->cfg.rc_target_bitrate = arg_parse_uint(&arg);
-    } else if (arg_match(&arg, &min_quantizer, argi)) {
+    } else if (arg_match(&arg, &min_qp_level, argi)) {
       config->cfg.rc_min_quantizer = arg_parse_uint(&arg);
-    } else if (arg_match(&arg, &max_quantizer, argi)) {
+    } else if (arg_match(&arg, &max_qp_level, argi)) {
       config->cfg.rc_max_quantizer = arg_parse_uint(&arg);
+    } else if (arg_match(&arg, &min_q_level, argi)) {
+      const unsigned int min_q_val = arg_parse_uint(&arg);
+      config->cfg.rc_min_quantizer =
+          get_qindex_from_quantizer_and_warn(min_q_val, "min-q", "min-qp");
+    } else if (arg_match(&arg, &max_q_level, argi)) {
+      const unsigned int max_q_val = arg_parse_uint(&arg);
+      config->cfg.rc_max_quantizer =
+          get_qindex_from_quantizer_and_warn(max_q_val, "max-q", "max-qp");
     } else if (arg_match(&arg, &undershoot_pct, argi)) {
       config->cfg.rc_undershoot_pct = arg_parse_uint(&arg);
     } else if (arg_match(&arg, &overshoot_pct, argi)) {
@@ -1565,10 +1680,12 @@
       config->cfg.rc_buf_initial_sz = arg_parse_uint(&arg);
     } else if (arg_match(&arg, &buf_optimal_sz, argi)) {
       config->cfg.rc_buf_optimal_sz = arg_parse_uint(&arg);
+#if !CONFIG_SINGLEPASS
     } else if (arg_match(&arg, &bias_pct, argi)) {
       config->cfg.rc_2pass_vbr_bias_pct = arg_parse_uint(&arg);
       if (global->passes < 2)
         warn("option %s ignored in one-pass mode.\n", arg.name);
+#endif  // !CONFIG_SINGLEPASS
     } else if (arg_match(&arg, &minsection_pct, argi)) {
       config->cfg.rc_2pass_vbr_minsection_pct = arg_parse_uint(&arg);
 
@@ -1619,6 +1736,15 @@
       if (arg_parse_uint(&arg) == 1) {
         warn("non-zero %s option ignored in realtime mode.\n", arg.name);
       }
+    } else if (arg_match(&arg, &cq_level, argi)) {
+      const unsigned int cq_level_val = arg_parse_uint(&arg);
+      const int qp_val =
+          get_qindex_from_quantizer_and_warn(cq_level_val, "cq-level", "qp");
+      const int idx = config->arg_ctrl_cnt;
+      assert(idx < (int)ARG_CTRL_CNT_MAX);
+      config->arg_ctrls[idx][0] = AOME_SET_QP;
+      config->arg_ctrls[idx][1] = qp_val;
+      ++config->arg_ctrl_cnt;
     } else {
       int i, match = 0;
       for (i = 0; ctrl_args[i]; i++) {
@@ -1677,6 +1803,7 @@
               streami->index, stream->index);
     }
 
+#if !CONFIG_SINGLEPASS
     /* Check for two streams sharing a stats file. */
     if (streami != stream) {
       const char *a = stream->config.stats_fn;
@@ -1685,6 +1812,7 @@
         fatal("Stream %d: duplicate stats file (from stream %d)",
               streami->index, stream->index);
     }
+#endif  // !CONFIG_SINGLEPASS
   }
 }
 
@@ -1723,103 +1851,155 @@
   }
 }
 
+static void print_frames_to_code(FILE *f, struct stream_state *stream,
+                                 struct AvxEncoderConfig *global) {
+  const struct aom_codec_enc_cfg *cfg = &stream->config.cfg;
+  int num_frames = cfg->g_limit / global->step_frames;
+  // E.g., if step == 3 and limit == 4, then 2 frames are encoded.
+  // Similar for all cases where limit % step != 0.
+  if (cfg->g_limit % global->step_frames != 0) {
+    ++num_frames;
+  }
+  fprintf(f, "Frames to be coded             : %d - %d (%d frames",
+          global->skip_frames, global->skip_frames + cfg->g_limit - 1,
+          num_frames);
+  if (global->step_frames == 1) {
+    fprintf(f, ")\n");
+  } else {
+    fprintf(f, ", step size:%d)\n", global->step_frames);
+  }
+}
+
 static void show_stream_config(struct stream_state *stream,
                                struct AvxEncoderConfig *global,
                                struct AvxInputContext *input) {
-#define SHOW(field) \
-  fprintf(stderr, "    %-28s = %d\n", #field, stream->config.cfg.field)
-
-  if (stream->index == 0) {
-    fprintf(stderr, "Codec: %s\n", aom_codec_iface_name(global->codec));
-    fprintf(stderr, "Source file: %s File Type: %s Format: %s\n",
-            input->filename, file_type_to_string(input->file_type),
-            image_format_to_string(input->fmt));
+  const struct aom_codec_enc_cfg *cfg = &stream->config.cfg;
+  const cfg_options_t *encoder_cfg = &stream->encoder.config.enc->encoder_cfg;
+  fprintf(stdout, "Codec                          : %s\n",
+          aom_codec_iface_name(global->codec));
+  fprintf(stdout, "Executable                     : aomenc %s\n", ENV_BITS);
+  fprintf(stdout, "Input file                     : %s\n", input->filename);
+  fprintf(stdout, "Output file                    : %s\n",
+          stream->config.out_fn);
+  fprintf(stdout,
+          "Input format                   : %s, %s, %dx%d, %3.1f FPS, %d bit\n",
+          file_type_to_string(input->file_type),
+          image_format_to_string(input->fmt), input->width, input->height,
+          (double)global->framerate.num / (double)global->framerate.den,
+          input->bit_depth);
+  fprintf(stdout, "Number of threads              : %d\n", cfg->g_threads);
+  print_frames_to_code(stdout, stream, global);
+  fprintf(stdout, "Operating bit depth            : %d\n", cfg->g_bit_depth);
+  fprintf(stdout, "Num of coding passes           : %d\n", global->passes);
+#if !CONFIG_SINGLEPASS
+  if (global->passes > 1) {
+    fprintf(stdout, "Stats file                     : %s\n",
+            stream->config.stats_fn);
   }
-  if (stream->next || stream->index)
-    fprintf(stderr, "\nStream Index: %d\n", stream->index);
-  fprintf(stderr, "Destination file: %s\n", stream->config.out_fn);
-  fprintf(stderr, "Coding path: %s\n",
-          stream->config.use_16bit_internal ? "HBD" : "LBD");
-  fprintf(stderr, "Encoder parameters:\n");
-
-  SHOW(g_usage);
-  SHOW(g_threads);
-  SHOW(g_profile);
-  SHOW(g_w);
-  SHOW(g_h);
-  SHOW(g_bit_depth);
-  SHOW(g_input_bit_depth);
-  SHOW(g_timebase.num);
-  SHOW(g_timebase.den);
-  SHOW(g_error_resilient);
-  SHOW(g_pass);
-  SHOW(g_lag_in_frames);
-  SHOW(large_scale_tile);
-  SHOW(rc_dropframe_thresh);
-  SHOW(rc_resize_mode);
-  SHOW(rc_resize_denominator);
-  SHOW(rc_resize_kf_denominator);
-  SHOW(rc_superres_mode);
-  SHOW(rc_superres_denominator);
-  SHOW(rc_superres_kf_denominator);
-  SHOW(rc_superres_qthresh);
-  SHOW(rc_superres_kf_qthresh);
-  SHOW(rc_end_usage);
-  SHOW(rc_target_bitrate);
-  SHOW(rc_min_quantizer);
-  SHOW(rc_max_quantizer);
-  SHOW(rc_undershoot_pct);
-  SHOW(rc_overshoot_pct);
-  SHOW(rc_buf_sz);
-  SHOW(rc_buf_initial_sz);
-  SHOW(rc_buf_optimal_sz);
-  SHOW(rc_2pass_vbr_bias_pct);
-  SHOW(rc_2pass_vbr_minsection_pct);
-  SHOW(rc_2pass_vbr_maxsection_pct);
-  SHOW(fwd_kf_enabled);
-  SHOW(kf_mode);
-  SHOW(kf_min_dist);
-  SHOW(kf_max_dist);
-
-#define SHOW_PARAMS(field)                    \
-  fprintf(stderr, "    %-28s = %d\n", #field, \
-          stream->config.cfg.encoder_cfg.field)
-  if (global->encoder_config.init_by_cfg_file) {
-    SHOW_PARAMS(super_block_size);
-    SHOW_PARAMS(max_partition_size);
-    SHOW_PARAMS(min_partition_size);
-    SHOW_PARAMS(disable_ab_partition_type);
-    SHOW_PARAMS(disable_rect_partition_type);
-    SHOW_PARAMS(disable_1to4_partition_type);
-    SHOW_PARAMS(disable_flip_idtx);
-    SHOW_PARAMS(disable_cdef);
-    SHOW_PARAMS(disable_lr);
-    SHOW_PARAMS(disable_obmc);
-    SHOW_PARAMS(disable_warp_motion);
-    SHOW_PARAMS(disable_global_motion);
-    SHOW_PARAMS(disable_dist_wtd_comp);
-    SHOW_PARAMS(disable_diff_wtd_comp);
-    SHOW_PARAMS(disable_inter_intra_comp);
-    SHOW_PARAMS(disable_masked_comp);
-    SHOW_PARAMS(disable_one_sided_comp);
-    SHOW_PARAMS(disable_palette);
-    SHOW_PARAMS(disable_intrabc);
-    SHOW_PARAMS(disable_cfl);
-    SHOW_PARAMS(disable_smooth_intra);
-    SHOW_PARAMS(disable_filter_intra);
-    SHOW_PARAMS(disable_dual_filter);
-    SHOW_PARAMS(disable_intra_angle_delta);
-    SHOW_PARAMS(disable_intra_edge_filter);
-    SHOW_PARAMS(disable_tx_64x64);
-    SHOW_PARAMS(disable_smooth_inter_intra);
-    SHOW_PARAMS(disable_inter_inter_wedge);
-    SHOW_PARAMS(disable_inter_intra_wedge);
-    SHOW_PARAMS(disable_paeth_intra);
-    SHOW_PARAMS(disable_trellis_quant);
-    SHOW_PARAMS(disable_ref_frame_mv);
-    SHOW_PARAMS(reduced_reference_set);
-    SHOW_PARAMS(reduced_tx_type_set);
+#endif  // !CONFIG_SINGLEPASS
+  fprintf(stdout, "Lag in frames                  : %d\n",
+          cfg->g_lag_in_frames);
+  if (cfg->kf_min_dist != cfg->kf_max_dist) {
+    fprintf(stdout, "Key frame distance             : %d - %d\n",
+            cfg->kf_min_dist, cfg->kf_max_dist);
+  } else {
+    fprintf(stdout, "Key frame distance             : %d\n", cfg->kf_min_dist);
   }
+  if (encoder_cfg->superblock_size != 0) {
+    fprintf(stdout, "Super block size               : %d\n",
+            encoder_cfg->superblock_size);
+  } else {
+    fprintf(stdout, "Super block size               : Dynamic\n");
+  }
+  fprintf(stdout, "Partition size                 : %d - %d\n",
+          encoder_cfg->min_partition_size, encoder_cfg->max_partition_size);
+
+  int qp = 0;
+  int cpu_used = 0;
+  for (int i = 0; i < stream->config.arg_ctrl_cnt; i++) {
+    int ctrl = stream->config.arg_ctrls[i][0];
+
+    if (ctrl == AOME_SET_QP) {
+      qp = stream->config.arg_ctrls[i][1];
+    }
+    if (ctrl == AOME_SET_CPUUSED) {
+      cpu_used = stream->config.arg_ctrls[i][1];
+    }
+  }
+  fprintf(stdout, "QP                             : %d", qp);
+  if (cfg->use_fixed_qp_offsets) {
+    fprintf(stdout, " [ ");
+    for (int level = 0; level < FIXED_QP_OFFSET_COUNT; level++) {
+      fprintf(stdout, "%d ", cfg->fixed_qp_offsets[level]);
+    }
+    fprintf(stdout, "]");
+  }
+  fprintf(stdout, "\nEncoder speed setting          : %d (cpu-used)\n",
+          cpu_used);
+  fprintf(stdout, "Trellis quantization           : %d\n",
+          !encoder_cfg->disable_trellis_quant);
+  fprintf(stdout, "Reduced reference frame set    : %d\n",
+          encoder_cfg->enable_reduced_reference_set);
+  fprintf(stdout, "Reduced transform set          : %d\n",
+          encoder_cfg->reduced_tx_type_set);
+
+  fprintf(
+      stdout, "Tool setting (Partition)       : T-Type (%d), 4:1/1:4 (%d)\n",
+      encoder_cfg->enable_ab_partitions, encoder_cfg->enable_1to4_partitions);
+
+  fprintf(stdout,
+          "Tool setting (Intra)           : SmoothIntra (%d), CfL (%d), "
+          "FilterIntra (%d)\n",
+          encoder_cfg->enable_smooth_intra, encoder_cfg->enable_cfl_intra,
+          encoder_cfg->enable_filter_intra);
+  fprintf(stdout,
+          "                               : DualFilter (%d), IntraDeltaAngle "
+          "(%d)\n",
+          encoder_cfg->enable_dual_filter, encoder_cfg->enable_angle_delta);
+  fprintf(stdout,
+          "                               : "
+          "EdgeFilter (%d), PaethPredictor (%d)\n",
+          encoder_cfg->enable_intra_edge_filter,
+          encoder_cfg->enable_paeth_intra);
+
+  fprintf(stdout,
+          "Tool setting (Inter)           : OBMC (%d), WarpMotion (%d), "
+          "GlobalMotion (%d)\n",
+          encoder_cfg->enable_obmc, encoder_cfg->enable_warped_motion,
+          encoder_cfg->enable_global_motion);
+
+  fprintf(stdout,
+          "                               : DistCompound (%d), DiCompound "
+          "(%d), InterIntra (%d)\n",
+          encoder_cfg->enable_dist_wtd_comp, encoder_cfg->enable_diff_wtd_comp,
+          encoder_cfg->enable_interintra_comp);
+
+  fprintf(stdout,
+          "                               : MaskCompound: (%d), "
+          "OneSideCompound (%d), SmoothInterIntra (%d)\n",
+          encoder_cfg->enable_masked_comp, encoder_cfg->enable_onesided_comp,
+          encoder_cfg->enable_smooth_interintra);
+
+  fprintf(stdout,
+          "                               : InterInterWedge (%d), "
+          "InterIntraWedge (%d), RefFrameMv (%d)\n",
+          encoder_cfg->enable_interinter_wedge,
+          encoder_cfg->enable_interintra_wedge,
+          encoder_cfg->enable_ref_frame_mvs);
+
+  fprintf(stdout,
+          "Tool setting (Transform)       : Flip & IDT (%d), TX_64 (%d)\n",
+          encoder_cfg->enable_flip_idtx, encoder_cfg->enable_tx64);
+
+  fprintf(stdout,
+          "Tool setting (Loop filter)     : CDEF (%d), LoopRestortion (%d)\n",
+          encoder_cfg->enable_cdef, encoder_cfg->enable_restoration);
+
+  fprintf(stdout,
+          "Tool setting (Others)          : Palette (%d), IntraBC (%d)\n",
+          encoder_cfg->enable_palette, encoder_cfg->enable_intrabc);
+
+  fprintf(stdout, "\n\n");
 }
 
 static void open_output_file(struct stream_state *stream,
@@ -1884,6 +2064,11 @@
 
 static void setup_pass(struct stream_state *stream,
                        struct AvxEncoderConfig *global, int pass) {
+#if CONFIG_SINGLEPASS
+  (void)global;
+  (void)pass;
+  assert(pass == 0);
+#else
   if (stream->config.stats_fn) {
     if (!stats_open_file(&stream->stats, stream->config.stats_fn, pass))
       fatal("Failed to open statistics store");
@@ -1891,13 +2076,18 @@
     if (!stats_open_mem(&stream->stats, pass))
       fatal("Failed to open statistics store");
   }
+#endif  // CONFIG_SINGLEPASS
 
+#if CONFIG_SINGLEPASS
+  stream->config.cfg.g_pass = AOM_RC_ONE_PASS;
+#else
   stream->config.cfg.g_pass = global->passes == 2
                                   ? pass ? AOM_RC_LAST_PASS : AOM_RC_FIRST_PASS
                                   : AOM_RC_ONE_PASS;
   if (pass) {
     stream->config.cfg.rc_twopass_stats_in = stats_get(&stream->stats);
   }
+#endif  // CONFIG_SINGLEPASS
 
   stream->cx_time = 0;
   stream->nbytes = 0;
@@ -1911,6 +2101,7 @@
 
   flags |= global->show_psnr ? AOM_CODEC_USE_PSNR : 0;
   flags |= stream->config.use_16bit_internal ? AOM_CODEC_USE_HIGHBITDEPTH : 0;
+  flags |= global->quiet ? 0 : AOM_CODEC_USE_PER_FRAME_STATS;
 
   /* Construct Encoder Context */
   aom_codec_enc_init(&stream->encoder, global->codec, &stream->config.cfg,
@@ -1938,6 +2129,14 @@
                                   stream->config.film_grain_filename);
   }
 
+  if (stream->config.subgop_config_str) {
+    AOM_CODEC_CONTROL_TYPECHECKED(&stream->encoder, AV1E_SET_SUBGOP_CONFIG_STR,
+                                  stream->config.subgop_config_str);
+  }
+  if (stream->config.subgop_config_path) {
+    AOM_CODEC_CONTROL_TYPECHECKED(&stream->encoder, AV1E_SET_SUBGOP_CONFIG_PATH,
+                                  stream->config.subgop_config_path);
+  }
 #if CONFIG_AV1_DECODER
   if (global->test_decode != TEST_DECODE_OFF) {
     aom_codec_iface_t *decoder = get_aom_decoder_by_short_name(
@@ -2058,7 +2257,7 @@
   if (stream->config.cfg.g_pass != AOM_RC_FIRST_PASS) {
     int q;
 
-    AOM_CODEC_CONTROL_TYPECHECKED(&stream->encoder, AOME_GET_LAST_QUANTIZER_64,
+    AOM_CODEC_CONTROL_TYPECHECKED(&stream->encoder, AOME_GET_LAST_QUANTIZER,
                                   &q);
     ctx_exit_on_error(&stream->encoder, "Failed to read quantizer");
     stream->counts[q]++;
@@ -2079,9 +2278,6 @@
     switch (pkt->kind) {
       case AOM_CODEC_CX_FRAME_PKT:
         ++stream->frames_out;
-        if (!global->quiet)
-          fprintf(stderr, " %6luF", (unsigned long)pkt->data.frame.sz);
-
         update_rate_histogram(stream->rate_hist, cfg, pkt);
 #if CONFIG_WEBM_IO
         if (stream->config.write_webm) {
@@ -2127,12 +2323,14 @@
         }
 #endif
         break;
+#if !CONFIG_SINGLEPASS
       case AOM_CODEC_STATS_PKT:
         stream->frames_out++;
         stats_write(&stream->stats, pkt->data.twopass_stats.buf,
                     pkt->data.twopass_stats.sz);
         stream->nbytes += pkt->data.raw.sz;
         break;
+#endif  // !CONFIG_SINGLEPASS
       case AOM_CODEC_PSNR_PKT:
 
         if (global->show_psnr) {
@@ -2141,8 +2339,6 @@
           stream->psnr_sse_total += pkt->data.psnr.sse[0];
           stream->psnr_samples_total += pkt->data.psnr.samples[0];
           for (i = 0; i < 4; i++) {
-            if (!global->quiet)
-              fprintf(stderr, "%.3f ", pkt->data.psnr.psnr[i]);
             stream->psnr_totals[i] += pkt->data.psnr.psnr[i];
           }
           stream->psnr_count++;
@@ -2154,31 +2350,25 @@
   }
 }
 
-static void show_psnr(struct stream_state *stream, double peak, int64_t bps) {
-  int i;
-  double ovpsnr;
-
-  if (!stream->psnr_count) return;
-
-  fprintf(stderr, "Stream %d PSNR (Overall/Avg/Y/U/V)", stream->index);
-  ovpsnr = sse_to_psnr((double)stream->psnr_samples_total, peak,
-                       (double)stream->psnr_sse_total);
-  fprintf(stderr, " %.3f", ovpsnr);
-
-  for (i = 0; i < 4; i++) {
-    fprintf(stderr, " %.3f", stream->psnr_totals[i] / stream->psnr_count);
-  }
-  if (bps > 0) {
-    fprintf(stderr, " %7" PRId64 " bps", bps);
-  }
-  fprintf(stderr, " %7" PRId64 " ms", stream->cx_time / 1000);
-  fprintf(stderr, "\n");
-}
-
 static float usec_to_fps(uint64_t usec, unsigned int frames) {
   return (float)(usec > 0 ? frames * 1000000.0 / (float)usec : 0);
 }
 
+static void write_recon_file(struct stream_state *stream, FILE *file) {
+  aom_image_t enc_img;
+
+  AOM_CODEC_CONTROL_TYPECHECKED(&stream->encoder, AV1_GET_NEW_FRAME_IMAGE,
+                                &enc_img);
+
+  ctx_exit_on_error(&stream->encoder,
+                    "Failed to get encoder reconstructed frame");
+
+  int num_planes = enc_img.monochrome ? 1 : 3;
+  const int PLANES_YUV[] = { AOM_PLANE_Y, AOM_PLANE_U, AOM_PLANE_V };
+  const int *planes = PLANES_YUV;
+  raw_write_image_file(&enc_img, planes, num_planes, file);
+}
+
 static void test_decode(struct stream_state *stream,
                         enum TestDecodeFatality fatal) {
   aom_image_t enc_img, dec_img;
@@ -2234,25 +2424,6 @@
   aom_img_free(&dec_img);
 }
 
-static void print_time(const char *label, int64_t etl) {
-  int64_t hours;
-  int64_t mins;
-  int64_t secs;
-
-  if (etl >= 0) {
-    hours = etl / 3600;
-    etl -= hours * 3600;
-    mins = etl / 60;
-    etl -= mins * 60;
-    secs = etl;
-
-    fprintf(stderr, "[%3s %2" PRId64 ":%02" PRId64 ":%02" PRId64 "] ", label,
-            hours, mins, secs);
-  } else {
-    fprintf(stderr, "[%3s  unknown] ", label);
-  }
-}
-
 int main(int argc, const char **argv_) {
   int pass;
   aom_image_t raw;
@@ -2260,7 +2431,6 @@
   int allocated_raw_shift = 0;
   int do_16bit_internal = 0;
   int input_shift = 0;
-  int frame_avail, got_data;
 
   struct AvxInputContext input;
   struct AvxEncoderConfig global;
@@ -2280,6 +2450,8 @@
   input.only_i420 = 1;
   input.bit_depth = 0;
 
+  FILE *recon_file = NULL;
+
   /* First parse the global configuration values, because we want to apply
    * other parameters on top of the default configuration provided by the
    * codec.
@@ -2327,6 +2499,13 @@
   /* Handle non-option arguments */
   input.filename = argv[0];
 
+  FOREACH_STREAM(stream, streams) {
+    if (stream->config.recon_fn != NULL) {
+      recon_file = fopen(stream->config.recon_fn, "wb");
+      if (!recon_file) fatal("Failed to open recon file");
+    }
+  }
+
   if (!input.filename) {
     fprintf(stderr, "No input file specified!\n");
     usage_exit();
@@ -2337,11 +2516,6 @@
     input.only_i420 = 0;
 
   for (pass = global.pass ? global.pass - 1 : 0; pass < global.passes; pass++) {
-    int frames_in = 0, seen_frames = 0;
-    int64_t estimated_time_left = -1;
-    int64_t average_rate = -1;
-    int64_t lagged_count = 0;
-
     open_input_file(&input, global.csp);
 
     /* If the input file doesn't specify its w/h (raw files), try to get
@@ -2491,6 +2665,7 @@
     }
     FOREACH_STREAM(stream, streams) { validate_stream_config(stream, &global); }
 
+#if !CONFIG_SINGLEPASS
     /* Ensure that --passes and --pass are consistent. If --pass is set and
      * --passes=2, ensure --fpf was set.
      */
@@ -2502,6 +2677,7 @@
               stream->index, global.pass);
       }
     }
+#endif  // !CONFIG_SINGLEPASS
 
 #if !CONFIG_WEBM_IO
     FOREACH_STREAM(stream, streams) {
@@ -2524,14 +2700,8 @@
       stream->config.cfg.g_timebase.den = global.framerate.num;
       stream->config.cfg.g_timebase.num = global.framerate.den;
     }
-    /* Show configuration */
-    if (global.verbose && pass == 0) {
-      FOREACH_STREAM(stream, streams) {
-        show_stream_config(stream, &global, &input);
-      }
-    }
 
-    if (pass == (global.pass ? global.pass - 1 : 0)) {
+    if (CONFIG_SINGLEPASS || pass == (global.pass ? global.pass - 1 : 0)) {
       if (input.file_type == FILE_TYPE_Y4M)
         /*The Y4M reader does its own allocation.
           Just initialize this here to avoid problems if we never read any
@@ -2548,6 +2718,17 @@
 
     FOREACH_STREAM(stream, streams) { setup_pass(stream, &global, pass); }
     FOREACH_STREAM(stream, streams) { initialize_encoder(stream, &global); }
+
+#if CONFIG_SINGLEPASS
+    if (global.verbose) {
+#else
+    if (global.verbose && pass == 0) {
+#endif  // CONFIG_SINGLEPASS
+      FOREACH_STREAM(stream, streams) {
+        show_stream_config(stream, &global, &input);
+      }
+    }
+
     FOREACH_STREAM(stream, streams) {
       char *encoder_settings = NULL;
 #if CONFIG_WEBM_IO
@@ -2586,155 +2767,135 @@
       };
     }
 
-    frame_avail = 1;
-    got_data = 0;
+    // Keep track of the total number of frames passed to the encoder.
+    int seen_frames = 0;
+    // Does the encoder have queued data that needs retrieval?
+    int got_data = 0;
+    // Is there a frame available for processing?
+    int frame_avail = 1;
 
+    // Wrap the original stream of frames in an "object" that returns the
+    // same set of streams.
+    StreamIter orig_stream;
+    copy_stream_iter_init(&orig_stream, &input);
+
+    // The skip iterator will skip the first N frames. Wrap the original
+    // stream in the skip iterator.
+    StreamIter skip_stream;
+    skip_stream_iter_init(&skip_stream, &orig_stream, global.skip_frames);
+
+    // The step iterator will only return every N-th frame.
+    StreamIter step_stream;
+    step_stream_iter_init(&step_stream, &skip_stream, global.step_frames);
+
+    // The limit iterator will stop returning frames after the N-th.
+    StreamIter limit_stream;
+    limit_stream_iter_init(&limit_stream, &step_stream, global.limit);
     while (frame_avail || got_data) {
-      struct aom_usec_timer timer;
-
-      if (!global.limit || frames_in < global.limit) {
-        frame_avail = read_frame(&input, &raw);
-
-        if (frame_avail) frames_in++;
-        seen_frames =
-            frames_in > global.skip_frames ? frames_in - global.skip_frames : 0;
-
-        if (!global.quiet) {
-          float fps = usec_to_fps(cx_time, seen_frames);
-          fprintf(stderr, "\rPass %d/%d ", pass + 1, global.passes);
-
-          if (stream_cnt == 1)
-            fprintf(stderr, "frame %4d/%-4d %7" PRId64 "B ", frames_in,
-                    streams->frames_out, (int64_t)streams->nbytes);
-          else
-            fprintf(stderr, "frame %4d ", frames_in);
-
-          fprintf(stderr, "%7" PRId64 " %s %.2f %s ",
-                  cx_time > 9999999 ? cx_time / 1000 : cx_time,
-                  cx_time > 9999999 ? "ms" : "us", fps >= 1.0 ? fps : fps * 60,
-                  fps >= 1.0 ? "fps" : "fpm");
-          print_time("ETA", estimated_time_left);
-        }
-
-      } else {
-        frame_avail = 0;
+      frame_avail = read_stream_iter(&limit_stream, &raw);
+      if (frame_avail) {
+        seen_frames++;
       }
-
-      if (frames_in > global.skip_frames) {
-        aom_image_t *frame_to_encode;
-        if (input_shift || (do_16bit_internal && input.bit_depth == 8)) {
-          assert(do_16bit_internal);
-          // Input bit depth and stream bit depth do not match, so up
-          // shift frame to stream bit depth
-          if (!allocated_raw_shift) {
-            aom_img_alloc(&raw_shift, raw.fmt | AOM_IMG_FMT_HIGHBITDEPTH,
-                          input.width, input.height, 32);
-            allocated_raw_shift = 1;
-          }
-          aom_img_upshift(&raw_shift, &raw, input_shift);
-          frame_to_encode = &raw_shift;
-        } else {
-          frame_to_encode = &raw;
-        }
-        aom_usec_timer_start(&timer);
-        if (do_16bit_internal) {
-          assert(frame_to_encode->fmt & AOM_IMG_FMT_HIGHBITDEPTH);
-          FOREACH_STREAM(stream, streams) {
-            if (stream->config.use_16bit_internal)
-              encode_frame(stream, &global,
-                           frame_avail ? frame_to_encode : NULL, frames_in);
-            else
-              assert(0);
-          };
-        } else {
-          assert((frame_to_encode->fmt & AOM_IMG_FMT_HIGHBITDEPTH) == 0);
-          FOREACH_STREAM(stream, streams) {
-            encode_frame(stream, &global, frame_avail ? frame_to_encode : NULL,
-                         frames_in);
-          }
-        }
-        aom_usec_timer_mark(&timer);
-        cx_time += aom_usec_timer_elapsed(&timer);
-
-        FOREACH_STREAM(stream, streams) { update_quantizer_histogram(stream); }
-
-        got_data = 0;
-        FOREACH_STREAM(stream, streams) {
-          get_cx_data(stream, &global, &got_data);
-        }
-
-        if (!got_data && input.length && streams != NULL &&
-            !streams->frames_out) {
-          lagged_count = global.limit ? seen_frames : ftello(input.file);
-        } else if (input.length) {
-          int64_t remaining;
-          int64_t rate;
-
-          if (global.limit) {
-            const int64_t frame_in_lagged = (seen_frames - lagged_count) * 1000;
-
-            rate = cx_time ? frame_in_lagged * (int64_t)1000000 / cx_time : 0;
-            remaining = 1000 * (global.limit - global.skip_frames -
-                                seen_frames + lagged_count);
-          } else {
-            const int64_t input_pos = ftello(input.file);
-            const int64_t input_pos_lagged = input_pos - lagged_count;
-            const int64_t input_limit = input.length;
-
-            rate = cx_time ? input_pos_lagged * (int64_t)1000000 / cx_time : 0;
-            remaining = input_limit - input_pos + lagged_count;
-          }
-
-          average_rate =
-              (average_rate <= 0) ? rate : (average_rate * 7 + rate) / 8;
-          estimated_time_left = average_rate ? remaining / average_rate : -1;
-        }
-
-        if (got_data && global.test_decode != TEST_DECODE_OFF) {
-          FOREACH_STREAM(stream, streams) {
-            test_decode(stream, global.test_decode);
-          }
-        }
-      }
-
       fflush(stdout);
-      if (!global.quiet) fprintf(stderr, "\033[K");
+
+      aom_image_t *frame_to_encode;
+      if (input_shift || (do_16bit_internal && input.bit_depth == 8)) {
+        assert(do_16bit_internal);
+        // Input bit depth and stream bit depth do not match, so up
+        // shift frame to stream bit depth
+        if (!allocated_raw_shift) {
+          aom_img_alloc(&raw_shift, raw.fmt | AOM_IMG_FMT_HIGHBITDEPTH,
+                        input.width, input.height, 32);
+          allocated_raw_shift = 1;
+        }
+        aom_img_upshift(&raw_shift, &raw, input_shift);
+        frame_to_encode = &raw_shift;
+      } else {
+        frame_to_encode = &raw;
+      }
+      struct aom_usec_timer timer;
+      aom_usec_timer_start(&timer);
+      if (do_16bit_internal) {
+        assert(frame_to_encode->fmt & AOM_IMG_FMT_HIGHBITDEPTH);
+        FOREACH_STREAM(stream, streams) {
+          if (stream->config.use_16bit_internal)
+            encode_frame(stream, &global, frame_avail ? frame_to_encode : NULL,
+                         seen_frames);
+          else
+            assert(0);
+        };
+      } else {
+        assert((frame_to_encode->fmt & AOM_IMG_FMT_HIGHBITDEPTH) == 0);
+        FOREACH_STREAM(stream, streams) {
+          encode_frame(stream, &global, frame_avail ? frame_to_encode : NULL,
+                       seen_frames);
+        }
+      }
+      aom_usec_timer_mark(&timer);
+      cx_time += aom_usec_timer_elapsed(&timer);
+
+      FOREACH_STREAM(stream, streams) { update_quantizer_histogram(stream); }
+
+      got_data = 0;
+      FOREACH_STREAM(stream, streams) {
+        get_cx_data(stream, &global, &got_data);
+      }
+      if (got_data && recon_file != NULL) {
+        FOREACH_STREAM(stream, streams) {
+          write_recon_file(stream, recon_file);
+        }
+      }
+      if (got_data && global.test_decode != TEST_DECODE_OFF) {
+        FOREACH_STREAM(stream, streams) {
+          test_decode(stream, global.test_decode);
+        }
+      }
+      fflush(stdout);
     }
 
     if (stream_cnt > 1) fprintf(stderr, "\n");
 
-    if (!global.quiet) {
+    if (!global.quiet && (pass + 1) == global.passes) {
       FOREACH_STREAM(stream, streams) {
-        const int64_t bpf =
-            seen_frames ? (int64_t)(stream->nbytes * 8 / seen_frames) : 0;
-        const int64_t bps = bpf * global.framerate.num / global.framerate.den;
-        fprintf(stderr,
-                "\rPass %d/%d frame %4d/%-4d %7" PRId64 "B %7" PRId64
-                "b/f %7" PRId64
-                "b/s"
-                " %7" PRId64 " %s (%.2f fps)\033[K\n",
-                pass + 1, global.passes, frames_in, stream->frames_out,
-                (int64_t)stream->nbytes, bpf, bps,
-                stream->cx_time > 9999999 ? stream->cx_time / 1000
-                                          : stream->cx_time,
-                stream->cx_time > 9999999 ? "ms" : "us",
-                usec_to_fps(stream->cx_time, seen_frames));
-      }
-    }
-
-    if (global.show_psnr) {
-      if (get_fourcc_by_aom_encoder(global.codec) == AV1_FOURCC) {
-        FOREACH_STREAM(stream, streams) {
-          int64_t bps = 0;
-          if (stream->psnr_count && seen_frames && global.framerate.den) {
-            bps = (int64_t)stream->nbytes * 8 * (int64_t)global.framerate.num /
-                  global.framerate.den / seen_frames;
+        const double bpf =
+            seen_frames ? ((double)stream->nbytes * 8.0 / (double)seen_frames)
+                        : 0.0;
+        const double kbps = (bpf * (double)global.framerate.num /
+                             (double)global.framerate.den) /
+                            1000.0;
+        const double peak = (1 << stream->config.cfg.g_input_bit_depth) - 1;
+        const double ovpsnr = sse_to_psnr((double)stream->psnr_samples_total,
+                                          peak, (double)stream->psnr_sse_total);
+        double psnr[4] = { 0.0 };
+        if (global.show_psnr) {
+          for (int i = 0; i < 4; i++) {
+            psnr[i] = stream->psnr_totals[i] / stream->psnr_count;
           }
-          show_psnr(stream, (1 << stream->config.cfg.g_input_bit_depth) - 1,
-                    bps);
+          fprintf(stdout,
+                  "\n         Bitrate(kbps)  |  PSNR(Y)  |  PSNR(U)  "
+                  "|  PSNR(V)  |  PSNR(Avg)  |  PSNR(Overall)  "
+                  "|  Encoding time (FPS)\n");
+          fprintf(stdout,
+                  "-----------------------------------------"
+                  "---------------------------------------"
+                  "----------------------------\n");
+
+          fprintf(stdout,
+                  "Summary:    %10.4f  |  %2.4f  |  %2.4f  |  %2.4f"
+                  "  |  %2.4f    |  %2.4f        |  %6.1fs (%3.1f fps)\n",
+                  kbps, psnr[1], psnr[2], psnr[3], psnr[0], ovpsnr,
+                  stream->cx_time / 1000000.0,
+                  usec_to_fps(stream->cx_time, seen_frames));
+        } else {
+          fprintf(stdout, "\n         Bitrate(kbps) |  Encoding time (FPS)\n");
+          fprintf(stdout,
+                  "-----------------------------------------"
+                  "---------------------------------------"
+                  "----------------------------\n");
+          fprintf(stdout, "Summary:    %10.4f  |  %6.1fs (%3.1f fps)\n", kbps,
+                  stream->cx_time / 1000000.0,
+                  usec_to_fps(stream->cx_time, seen_frames));
         }
-      } else {
-        FOREACH_STREAM(stream, streams) { show_psnr(stream, 255.0, 0); }
       }
     }
 
@@ -2753,9 +2914,11 @@
       close_output_file(stream, get_fourcc_by_aom_encoder(global.codec));
     }
 
+#if !CONFIG_SINGLEPASS
     FOREACH_STREAM(stream, streams) {
       stats_close(&stream->stats, global.passes - 1);
     }
+#endif  // !CONFIG_SINGLEPASS
 
     if (global.pass) break;
   }
@@ -2792,6 +2955,8 @@
   }
 #endif
 
+  if (recon_file != NULL) fclose(recon_file);
+
   if (allocated_raw_shift) aom_img_free(&raw_shift);
   aom_img_free(&raw);
   free(argv);
diff --git a/apps/aomenc.h b/apps/aomenc.h
index 8d4c25c..b0982db 100644
--- a/apps/aomenc.h
+++ b/apps/aomenc.h
@@ -11,6 +11,7 @@
 #ifndef AOM_APPS_AOMENC_H_
 #define AOM_APPS_AOMENC_H_
 
+#include "config/aom_config.h"
 #include "aom/aom_codec.h"
 #include "aom/aom_encoder.h"
 
@@ -42,6 +43,7 @@
   int verbose;
   int limit;
   int skip_frames;
+  int step_frames;
   int show_psnr;
   enum TestDecodeFatality test_decode;
   int have_framerate;
diff --git a/av1/av1.cmake b/av1/av1.cmake
index 4015801..070443a 100644
--- a/av1/av1.cmake
+++ b/av1/av1.cmake
@@ -223,6 +223,8 @@
             "${AOM_ROOT}/av1/encoder/segmentation.h"
             "${AOM_ROOT}/av1/encoder/speed_features.c"
             "${AOM_ROOT}/av1/encoder/speed_features.h"
+            "${AOM_ROOT}/av1/encoder/subgop.c"
+            "${AOM_ROOT}/av1/encoder/subgop.h"
             "${AOM_ROOT}/av1/encoder/superres_scale.c"
             "${AOM_ROOT}/av1/encoder/superres_scale.h"
             "${AOM_ROOT}/av1/encoder/svc_layercontext.c"
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index fe3178a..fb4c556 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -27,6 +27,9 @@
 #include "av1/encoder/ethread.h"
 #include "av1/encoder/firstpass.h"
 
+#include "aom_dsp/psnr.h"
+#include "aom_ports/aom_timer.h"
+
 #define MAG_SIZE (4)
 
 struct av1_extracfg {
@@ -49,7 +52,9 @@
   unsigned int gf_max_pyr_height;
   aom_tune_metric tuning;
   const char *vmaf_model_path;
-  unsigned int cq_level;  // constrained quality level
+  const char *subgop_config_str;
+  const char *subgop_config_path;
+  unsigned int qp;  // constant/constrained quality level
   unsigned int rc_max_intra_bitrate_pct;
   unsigned int rc_max_inter_bitrate_pct;
   unsigned int gf_cbr_boost_pct;
@@ -153,6 +158,56 @@
   unsigned int sb_multipass_unit_test;
 };
 
+// Example subgop configs. Currently not used by default.
+// Default config
+const char subgop_config_str_16_def[] =
+    "16:2:16F1P1/8F2P1^-1/"
+    "4U3P-2^1^-1/2U4P1^-3^-2^-1/"
+    "1V5P1^-2^-4^-3^-1/2S/3V5P4^5^1^-3^-2^-1/4S/6U4P3^5^5^1^-2^4^-1/"
+    "5V5P3^5^4^1^-4^-2^-1/6S/7V5P4^5^5^1^-2^3^-1/8R2P5^4^3^1^2^5^-1/"
+    "12U3P2^5^3^1^5^4^-1/10U4P2^5^5^1^-3^4^-1/9V5P2^5^4^1^-4^-3^-1/10S/"
+    "11V5P4^5^5^1^-3^2^-1/12S/14U4P3^5^2^1^5^4^-1/13V5P3^5^5^1^-4^4^-1/"
+    "14S/15V5P4^5^4^1^5^3^-1/16R1P5^4^5^1^3^5^1,"
+
+    "16:0:16F1P5^4^3^1^1^1^5/8F2P5^4^3^1^1^5^-1/"
+    "4U3P5^4^5^1^-2^1^-1/2U4P5^4^1^1^-3^-2^-1/"
+    "1V5P5^4^1^1^-4^-3^-1/2S/3V5P4^5^1^1^-3^-2^-1/4S/6U4P3^5^4^1^-2^1^-1/"
+    "5V5P3^5^1^1^-4^-2^-1/6S/7V5P4^5^3^1^-2^1^-1/8R2P5^4^5^1^2^1^-1/"
+    "12U3P2^5^5^1^1^4^-1/10U4P2^5^4^1^-3^1^-1/9V5P2^5^1^1^-4^-3^-1/10S/"
+    "11V5P4^5^2^1^-3^1^-1/12S/14U4P3^5^5^1^1^4^-1/13V5P3^5^4^1^-4^1^-1/"
+    "14S/15V5P4^5^5^1^1^3^-1/16R1P5^4^5^1^3^5^1,";
+
+// A config that honors temporally scalable prediction structure, i.e.
+// no frame is coded with references at higher pyramid depths.
+const char subgop_config_str_16_ts[] =
+    "16:0:16F1P1^1/8F2P1^1^2^-1/4U3P1^1^2^-2^-1/2U4P1^1^-3^-2^-1/"
+    "1V5P1^1^-4^-3^-2^-1/2S/3V5P1^5^4^-3^-2^-1/4S/6U4P1^3^4^-2^-1/"
+    "5V5P1^4^5^3^-4^-2^-1/6S/7V5P1^3^5^4^-2^-1/8R2P1^2^-1/12U3P1^3^2^-1/"
+    "10U4P1^3^4^2^-3^-1/9V5P1^3^4^2^-4^-3^-1/10S/11V5P1^2^4^5^-3^-1/12S/"
+    "14U4P1^2^4^3^-1/13V5P1^2^4^5^3^-4^-1/14S/15V5P1^2^3^4^5^-1/16R1P1^1,"
+
+    "16:1:14F1P1^1/7F2P1^1^2^-1/4U3P1^1^2^-2^-1/2U4P1^1^-3^-2^-1/"
+    "1V5P1^1^-4^-3^-2^-1/2S/3V5P1^5^4^-3^-2^-1/4S/6U4P1^3^4^-2^-1/"
+    "5V5P1^4^5^3^-4^-2^-1/6S/7R2P1^2^-1/11U3P1^3^2^-1/9U4P1^3^4^2^-3^-1/"
+    "8V5P1^3^4^2^-4^-3^-1/9S/10V5P1^2^4^5^-3^-1/11S/13U4P1^2^4^3^-1/"
+    "12V5P1^2^4^5^3^-4^-1/13S/14R1P1^1/16U4P1^1^2^3^4/15V5P1^1^2^3^4^-4^5/16S";
+
+// An asymmetrical config where the hierarchical frames are not exactly
+// dyadic, but slightly skewed.
+const char subgop_config_str_16_asym[] =
+    "16:0:16F1P1^1/11F2P1^1^2^-1/6U3P1^1^2^-2^-1/3U4P1^1^-3^-2^-1/"
+    "1V5P1^1^-4^-3^-2^-1/2V5P1^1^-4^-3^-2^-1^5/3S/4V5P1^5^5^4^-3^-2^-1/"
+    "5V5P1^5^5^4^-3^-2^-1/6S/9U3P^1^3^-1^-2/7V4P1^-1^-2^3^-3^4/"
+    "8V5P1^-1^-2^-3^3^4^5/9S/10V4P1^-1^-2^3^3^4/11R2P1^2^-1/"
+    "14U3P1^-1^2^3^3/13U4P1^-1^2^3^-3^4^4/12V5P1^-1^2^3^-3^4^-4/"
+    "13S/14S/15V5P1^-1^2^3^4^4^5/16R1P1^1,"
+
+    "16:1:14F1P1^1/8F2P1^1^2^-1/4U3P1^1^2^-2^-1/2U4P1^1^-3^-2^-1/"
+    "1V5P1^1^-4^-3^-2^-1/2S/3V5P1^5^4^-3^-2^-1/4S/6U4P1^3^4^-2^-1/"
+    "5V5P1^4^5^3^-4^-2^-1/6S/7V5P1^3^5^4^-2^-1/8R2P1^2^-1/11U3P1^3^2^-1/"
+    "9V4P1^-1^2^3^-3^4/10V5P1^-1^2^-3^4^4^5/11S/12V4P1^-1^2^3^4/"
+    "13V5P1^-1^2^3^4^4^5/14R1P1^1/16U4P1^1^2^3^4/15V5P1^1^2^3^4^-4^5/16S";
+
 static struct av1_extracfg default_extra_cfg = {
   0,              // cpu_used
   1,              // enable_auto_alt_ref
@@ -173,7 +228,9 @@
   5,              // gf_max_pyr_height
   AOM_TUNE_PSNR,  // tuning
   "/usr/local/share/model/vmaf_v0.6.1.pkl",  // VMAF model path
-  10,                                        // cq_level
+  NULL,                                      // subgop_config_str
+  NULL,                                      // subgop_config_path
+  40,                                        // qp
   0,                                         // rc_max_intra_bitrate_pct
   0,                                         // rc_max_inter_bitrate_pct
   0,                                         // gf_cbr_boost_pct
@@ -369,7 +426,7 @@
   RANGE_CHECK(cfg, g_timebase.num, 1, cfg->g_timebase.den);
   RANGE_CHECK_HI(cfg, g_profile, MAX_PROFILES - 1);
 
-  RANGE_CHECK_HI(cfg, rc_max_quantizer, 63);
+  RANGE_CHECK_HI(cfg, rc_max_quantizer, 255);
   RANGE_CHECK_HI(cfg, rc_min_quantizer, cfg->rc_max_quantizer);
   RANGE_CHECK_BOOL(extra_cfg, lossless);
   RANGE_CHECK_HI(extra_cfg, aq_mode, AQ_MODE_COUNT - 1);
@@ -381,15 +438,22 @@
   RANGE_CHECK(cfg, rc_end_usage, AOM_VBR, AOM_Q);
   RANGE_CHECK_HI(cfg, rc_undershoot_pct, 100);
   RANGE_CHECK_HI(cfg, rc_overshoot_pct, 100);
+#if !CONFIG_SINGLEPASS
   RANGE_CHECK_HI(cfg, rc_2pass_vbr_bias_pct, 100);
+#endif  // !!CONFIG_SINGLEPASS
   RANGE_CHECK(cfg, kf_mode, AOM_KF_DISABLED, AOM_KF_AUTO);
   RANGE_CHECK_HI(cfg, rc_dropframe_thresh, 100);
+#if CONFIG_SINGLEPASS
+  RANGE_CHECK(cfg, g_pass, AOM_RC_ONE_PASS, AOM_RC_ONE_PASS);
+  RANGE_CHECK_HI(cfg, g_lag_in_frames, MAX_TOTAL_BUFFERS);
+#else
   RANGE_CHECK(cfg, g_pass, AOM_RC_ONE_PASS, AOM_RC_LAST_PASS);
   if (cfg->g_pass == AOM_RC_ONE_PASS) {
     RANGE_CHECK_HI(cfg, g_lag_in_frames, MAX_TOTAL_BUFFERS);
   } else {
     RANGE_CHECK_HI(cfg, g_lag_in_frames, MAX_LAG_BUFFERS);
   }
+#endif  // CONFIG_SINGLEPASS
   RANGE_CHECK_HI(extra_cfg, min_gf_interval, MAX_LAG_BUFFERS - 1);
   RANGE_CHECK_HI(extra_cfg, max_gf_interval, MAX_LAG_BUFFERS - 1);
   if (extra_cfg->max_gf_interval > 0) {
@@ -414,8 +478,8 @@
               SCALE_NUMERATOR << 1);
   RANGE_CHECK(cfg, rc_superres_kf_denominator, SCALE_NUMERATOR,
               SCALE_NUMERATOR << 1);
-  RANGE_CHECK(cfg, rc_superres_qthresh, 1, 63);
-  RANGE_CHECK(cfg, rc_superres_kf_qthresh, 1, 63);
+  RANGE_CHECK(cfg, rc_superres_qthresh, 1, 255);
+  RANGE_CHECK(cfg, rc_superres_kf_qthresh, 1, 255);
   RANGE_CHECK_HI(extra_cfg, cdf_update_mode, 2);
 
   RANGE_CHECK_HI(extra_cfg, motion_vector_unit_test, 2);
@@ -445,11 +509,12 @@
   RANGE_CHECK_HI(extra_cfg, sharpness, 7);
   RANGE_CHECK_HI(extra_cfg, arnr_max_frames, 15);
   RANGE_CHECK_HI(extra_cfg, arnr_strength, 6);
-  RANGE_CHECK_HI(extra_cfg, cq_level, 63);
+  RANGE_CHECK_HI(extra_cfg, qp, 255);
   RANGE_CHECK(cfg, g_bit_depth, AOM_BITS_8, AOM_BITS_12);
   RANGE_CHECK(cfg, g_input_bit_depth, 8, 12);
   RANGE_CHECK(extra_cfg, content, AOM_CONTENT_DEFAULT, AOM_CONTENT_INVALID - 1);
 
+#if !CONFIG_SINGLEPASS
   if (cfg->g_pass == AOM_RC_LAST_PASS) {
     const size_t packet_sz = sizeof(FIRSTPASS_STATS);
     const int n_packets = (int)(cfg->rc_twopass_stats_in.sz / packet_sz);
@@ -470,6 +535,7 @@
     if ((int)(stats->count + 0.5) != n_packets - 1)
       ERROR("rc_twopass_stats_in missing EOS stats packet");
   }
+#endif  // !CONFIG_SINGLEPASS
 
   if (cfg->g_profile <= (unsigned int)PROFILE_1 &&
       cfg->g_bit_depth > AOM_BITS_10) {
@@ -483,7 +549,7 @@
   if (cfg->rc_end_usage == AOM_Q) {
     RANGE_CHECK_HI(cfg, use_fixed_qp_offsets, 1);
     for (int i = 0; i < FIXED_QP_OFFSET_COUNT; ++i) {
-      RANGE_CHECK_HI(cfg, fixed_qp_offsets[i], 63);
+      RANGE_CHECK_HI(cfg, fixed_qp_offsets[i], 255);
     }
   } else {
     if (cfg->use_fixed_qp_offsets > 0) {
@@ -507,7 +573,11 @@
 
   /* Average corpus complexity is supported only in the case of single pass
    * VBR*/
+#if CONFIG_SINGLEPASS
+  if (cfg->rc_end_usage == AOM_VBR)
+#else
   if (cfg->g_pass == AOM_RC_ONE_PASS && cfg->rc_end_usage == AOM_VBR)
+#endif  // CONFIG_SINGLEPASS
     RANGE_CHECK_HI(extra_cfg, vbr_corpus_complexity_lap,
                    MAX_VBR_CORPUS_COMPLEXITY);
   else if (extra_cfg->vbr_corpus_complexity_lap != 0)
@@ -624,70 +694,116 @@
   superres_cfg->superres_kf_qthresh = 255;
 }
 
+static void update_encoder_config(cfg_options_t *cfg,
+                                  struct av1_extracfg *extra_cfg) {
+  cfg->enable_cdef = extra_cfg->enable_cdef;
+  cfg->enable_restoration = extra_cfg->enable_restoration;
+  cfg->superblock_size =
+      (extra_cfg->superblock_size == AOM_SUPERBLOCK_SIZE_64X64)
+          ? 64
+          : (extra_cfg->superblock_size == AOM_SUPERBLOCK_SIZE_128X128) ? 128
+                                                                        : 0;
+  cfg->enable_warped_motion = extra_cfg->enable_warped_motion;
+  cfg->enable_dist_wtd_comp = extra_cfg->enable_dist_wtd_comp;
+  cfg->enable_diff_wtd_comp = extra_cfg->enable_diff_wtd_comp;
+  cfg->enable_dual_filter = extra_cfg->enable_dual_filter;
+  cfg->enable_angle_delta = extra_cfg->enable_angle_delta;
+  cfg->enable_rect_partitions = extra_cfg->enable_rect_partitions;
+  cfg->enable_ab_partitions = extra_cfg->enable_ab_partitions;
+  cfg->enable_1to4_partitions = extra_cfg->enable_1to4_partitions;
+  cfg->max_partition_size = extra_cfg->max_partition_size;
+  cfg->min_partition_size = extra_cfg->min_partition_size;
+  cfg->enable_intra_edge_filter = extra_cfg->enable_intra_edge_filter;
+  cfg->enable_tx64 = extra_cfg->enable_tx64;
+  cfg->enable_flip_idtx = extra_cfg->enable_flip_idtx;
+  cfg->enable_masked_comp = extra_cfg->enable_masked_comp;
+  cfg->enable_interintra_comp = extra_cfg->enable_interintra_comp;
+  cfg->enable_smooth_interintra = extra_cfg->enable_smooth_interintra;
+  cfg->enable_interinter_wedge = extra_cfg->enable_interinter_wedge;
+  cfg->enable_interintra_wedge = extra_cfg->enable_interintra_wedge;
+  cfg->enable_global_motion = extra_cfg->enable_global_motion;
+  cfg->enable_filter_intra = extra_cfg->enable_filter_intra;
+  cfg->enable_smooth_intra = extra_cfg->enable_smooth_intra;
+  cfg->enable_paeth_intra = extra_cfg->enable_paeth_intra;
+  cfg->enable_cfl_intra = extra_cfg->enable_cfl_intra;
+  cfg->enable_obmc = extra_cfg->enable_obmc;
+  cfg->enable_palette = extra_cfg->enable_palette;
+  cfg->enable_intrabc = extra_cfg->enable_intrabc;
+  cfg->disable_trellis_quant = extra_cfg->disable_trellis_quant;
+  cfg->enable_ref_frame_mvs =
+      (extra_cfg->allow_ref_frame_mvs || extra_cfg->enable_ref_frame_mvs);
+  cfg->enable_onesided_comp = extra_cfg->enable_onesided_comp;
+  cfg->enable_reduced_reference_set = extra_cfg->enable_reduced_reference_set;
+  cfg->reduced_tx_type_set = extra_cfg->reduced_tx_type_set;
+}
+
 static void update_default_encoder_config(const cfg_options_t *cfg,
                                           struct av1_extracfg *extra_cfg) {
-  extra_cfg->enable_cdef = (cfg->disable_cdef == 0);
-  extra_cfg->enable_restoration = (cfg->disable_lr == 0);
-  extra_cfg->superblock_size = (cfg->super_block_size == 64)
+  extra_cfg->enable_cdef = cfg->enable_cdef;
+  extra_cfg->enable_restoration = cfg->enable_restoration;
+  extra_cfg->superblock_size = (cfg->superblock_size == 64)
                                    ? AOM_SUPERBLOCK_SIZE_64X64
-                                   : (cfg->super_block_size == 128)
+                                   : (cfg->superblock_size == 128)
                                          ? AOM_SUPERBLOCK_SIZE_128X128
                                          : AOM_SUPERBLOCK_SIZE_DYNAMIC;
-  extra_cfg->enable_warped_motion = (cfg->disable_warp_motion == 0);
-  extra_cfg->enable_dist_wtd_comp = (cfg->disable_dist_wtd_comp == 0);
-  extra_cfg->enable_diff_wtd_comp = (cfg->disable_diff_wtd_comp == 0);
-  extra_cfg->enable_dual_filter = (cfg->disable_dual_filter == 0);
-  extra_cfg->enable_angle_delta = (cfg->disable_intra_angle_delta == 0);
-  extra_cfg->enable_rect_partitions = (cfg->disable_rect_partition_type == 0);
-  extra_cfg->enable_ab_partitions = (cfg->disable_ab_partition_type == 0);
-  extra_cfg->enable_1to4_partitions = (cfg->disable_1to4_partition_type == 0);
+  extra_cfg->enable_warped_motion = cfg->enable_warped_motion;
+  extra_cfg->enable_dist_wtd_comp = cfg->enable_dist_wtd_comp;
+  extra_cfg->enable_diff_wtd_comp = cfg->enable_diff_wtd_comp;
+  extra_cfg->enable_dual_filter = cfg->enable_dual_filter;
+  extra_cfg->enable_angle_delta = cfg->enable_angle_delta;
+  extra_cfg->enable_rect_partitions = cfg->enable_rect_partitions;
+  extra_cfg->enable_ab_partitions = cfg->enable_ab_partitions;
+  extra_cfg->enable_1to4_partitions = cfg->enable_1to4_partitions;
   extra_cfg->max_partition_size = cfg->max_partition_size;
   extra_cfg->min_partition_size = cfg->min_partition_size;
-  extra_cfg->enable_intra_edge_filter = (cfg->disable_intra_edge_filter == 0);
-  extra_cfg->enable_tx64 = (cfg->disable_tx_64x64 == 0);
-  extra_cfg->enable_flip_idtx = (cfg->disable_flip_idtx == 0);
-  extra_cfg->enable_masked_comp = (cfg->disable_masked_comp == 0);
-  extra_cfg->enable_interintra_comp = (cfg->disable_inter_intra_comp == 0);
-  extra_cfg->enable_smooth_interintra = (cfg->disable_smooth_inter_intra == 0);
-  extra_cfg->enable_interinter_wedge = (cfg->disable_inter_inter_wedge == 0);
-  extra_cfg->enable_interintra_wedge = (cfg->disable_inter_intra_wedge == 0);
-  extra_cfg->enable_global_motion = (cfg->disable_global_motion == 0);
-  extra_cfg->enable_filter_intra = (cfg->disable_filter_intra == 0);
-  extra_cfg->enable_smooth_intra = (cfg->disable_smooth_intra == 0);
-  extra_cfg->enable_paeth_intra = (cfg->disable_paeth_intra == 0);
-  extra_cfg->enable_cfl_intra = (cfg->disable_cfl == 0);
-  extra_cfg->enable_obmc = (cfg->disable_obmc == 0);
-  extra_cfg->enable_palette = (cfg->disable_palette == 0);
-  extra_cfg->enable_intrabc = (cfg->disable_intrabc == 0);
+  extra_cfg->enable_intra_edge_filter = cfg->enable_intra_edge_filter;
+  extra_cfg->enable_tx64 = cfg->enable_tx64;
+  extra_cfg->enable_flip_idtx = cfg->enable_flip_idtx;
+  extra_cfg->enable_masked_comp = cfg->enable_masked_comp;
+  extra_cfg->enable_interintra_comp = cfg->enable_interintra_comp;
+  extra_cfg->enable_smooth_interintra = cfg->enable_smooth_interintra;
+  extra_cfg->enable_interinter_wedge = cfg->enable_interinter_wedge;
+  extra_cfg->enable_interintra_wedge = cfg->enable_interintra_wedge;
+  extra_cfg->enable_global_motion = cfg->enable_global_motion;
+  extra_cfg->enable_filter_intra = cfg->enable_filter_intra;
+  extra_cfg->enable_smooth_intra = cfg->enable_smooth_intra;
+  extra_cfg->enable_paeth_intra = cfg->enable_paeth_intra;
+  extra_cfg->enable_cfl_intra = cfg->enable_cfl_intra;
+  extra_cfg->enable_obmc = cfg->enable_obmc;
+  extra_cfg->enable_palette = cfg->enable_palette;
+  extra_cfg->enable_intrabc = cfg->enable_intrabc;
   extra_cfg->disable_trellis_quant = cfg->disable_trellis_quant;
-  extra_cfg->allow_ref_frame_mvs = (cfg->disable_ref_frame_mv == 0);
-  extra_cfg->enable_ref_frame_mvs = (cfg->disable_ref_frame_mv == 0);
-  extra_cfg->enable_onesided_comp = (cfg->disable_one_sided_comp == 0);
-  extra_cfg->enable_reduced_reference_set = cfg->reduced_reference_set;
+  extra_cfg->enable_ref_frame_mvs = cfg->enable_ref_frame_mvs;
+  extra_cfg->enable_onesided_comp = cfg->enable_onesided_comp;
+  extra_cfg->enable_reduced_reference_set = cfg->enable_reduced_reference_set;
   extra_cfg->reduced_tx_type_set = cfg->reduced_tx_type_set;
 }
 
-static double convert_qp_offset(int cq_level, int q_offset, int bit_depth) {
-  const double base_q_val = av1_convert_qindex_to_q(cq_level, bit_depth);
-  const int new_q_index_offset = av1_quantizer_to_qindex(q_offset);
-  const int new_q_index = AOMMAX(cq_level - new_q_index_offset, 0);
-  const double new_q_val = av1_convert_qindex_to_q(new_q_index, bit_depth);
+static double convert_qp_offset(int qp, int qp_offset, int bit_depth) {
+  const double base_q_val = av1_convert_qindex_to_q(qp, bit_depth);
+  const int new_qp = AOMMAX(qp - qp_offset, 0);
+  const double new_q_val = av1_convert_qindex_to_q(new_qp, bit_depth);
   return (base_q_val - new_q_val);
 }
 
-static double get_modeled_qp_offset(int cq_level, int level, int bit_depth) {
+static double get_modeled_qp_offset(int qp, int level, int bit_depth) {
   // 80% for keyframe was derived empirically.
   // 40% similar to rc_pick_q_and_bounds_one_pass_vbr() for Q mode ARF.
   // Rest derived similar to rc_pick_q_and_bounds_two_pass()
   static const int percents[FIXED_QP_OFFSET_COUNT] = { 76, 60, 30, 15, 8 };
-  const double q_val = av1_convert_qindex_to_q(cq_level, bit_depth);
+  const double q_val = av1_convert_qindex_to_q(qp, bit_depth);
   return q_val * percents[level] / 100;
 }
 
+// update_config parameter is used to indicate whether extra command line
+// parameters are read. If extra command line parameters are read, then
+// parameters in the configure file will not overwrite the parameters in
+// extra_cfg.
 static aom_codec_err_t set_encoder_config(AV1EncoderConfig *oxcf,
-                                          const aom_codec_enc_cfg_t *cfg,
-                                          struct av1_extracfg *extra_cfg) {
-  if (cfg->encoder_cfg.init_by_cfg_file) {
+                                          aom_codec_enc_cfg_t *cfg,
+                                          struct av1_extracfg *extra_cfg,
+                                          int update_config) {
+  if (cfg->encoder_cfg.init_by_cfg_file && !update_config) {
     update_default_encoder_config(&cfg->encoder_cfg, extra_cfg);
   }
 
@@ -744,6 +860,9 @@
   input_cfg->input_bit_depth = cfg->g_input_bit_depth;
   // guess a frame rate if out of whack, use 30
   input_cfg->init_framerate = (double)cfg->g_timebase.den / cfg->g_timebase.num;
+#if CONFIG_SINGLEPASS
+  input_cfg->limit = cfg->g_limit;
+#else
   if (cfg->g_pass == AOM_RC_LAST_PASS) {
     const size_t packet_sz = sizeof(FIRSTPASS_STATS);
     const int n_packets = (int)(cfg->rc_twopass_stats_in.sz / packet_sz);
@@ -751,6 +870,7 @@
   } else {
     input_cfg->limit = cfg->g_limit;
   }
+#endif  // CONFIG_SINGLEPASS
   input_cfg->chroma_subsampling_x = extra_cfg->chroma_subsampling_x;
   input_cfg->chroma_subsampling_y = extra_cfg->chroma_subsampling_y;
   if (input_cfg->init_framerate > 180) {
@@ -791,8 +911,12 @@
 
   switch (cfg->g_pass) {
     case AOM_RC_ONE_PASS: oxcf->pass = 0; break;
+#if CONFIG_SINGLEPASS
+    default: oxcf->pass = 0; break;
+#else
     case AOM_RC_FIRST_PASS: oxcf->pass = 1; break;
     case AOM_RC_LAST_PASS: oxcf->pass = 2; break;
+#endif  // !CONFIG_SINGLEPASS
   }
 
   // Set Rate Control configuration.
@@ -801,11 +925,10 @@
   rc_cfg->gf_cbr_boost_pct = extra_cfg->gf_cbr_boost_pct;
   rc_cfg->mode = cfg->rc_end_usage;
   rc_cfg->min_cr = extra_cfg->min_cr;
-  rc_cfg->best_allowed_q =
-      extra_cfg->lossless ? 0 : av1_quantizer_to_qindex(cfg->rc_min_quantizer);
-  rc_cfg->worst_allowed_q =
-      extra_cfg->lossless ? 0 : av1_quantizer_to_qindex(cfg->rc_max_quantizer);
-  rc_cfg->cq_level = av1_quantizer_to_qindex(extra_cfg->cq_level);
+  rc_cfg->best_allowed_q = extra_cfg->lossless ? 0 : cfg->rc_min_quantizer;
+  rc_cfg->worst_allowed_q = extra_cfg->lossless ? 0 : cfg->rc_max_quantizer;
+  rc_cfg->qp = extra_cfg->qp;
+
   rc_cfg->under_shoot_pct = cfg->rc_undershoot_pct;
   rc_cfg->over_shoot_pct = cfg->rc_overshoot_pct;
   rc_cfg->maximum_buffer_size_ms = is_vbr ? 240000 : cfg->rc_buf_sz;
@@ -815,7 +938,9 @@
   rc_cfg->target_bandwidth = 1000 * cfg->rc_target_bitrate;
   rc_cfg->drop_frames_water_mark = cfg->rc_dropframe_thresh;
   rc_cfg->vbr_corpus_complexity_lap = extra_cfg->vbr_corpus_complexity_lap;
+#if !CONFIG_SINGLEPASS
   rc_cfg->vbrbias = cfg->rc_2pass_vbr_bias_pct;
+#endif  // !CONFIG_SINGLEPASS
   rc_cfg->vbrmin_section = cfg->rc_2pass_vbr_minsection_pct;
   rc_cfg->vbrmax_section = cfg->rc_2pass_vbr_maxsection_pct;
 
@@ -860,10 +985,10 @@
     if (q_cfg->use_fixed_qp_offsets) {
       if (cfg->fixed_qp_offsets[i] >= 0) {  // user-provided qp offset
         q_cfg->fixed_qp_offsets[i] = convert_qp_offset(
-            rc_cfg->cq_level, cfg->fixed_qp_offsets[i], tool_cfg->bit_depth);
+            rc_cfg->qp, cfg->fixed_qp_offsets[i], tool_cfg->bit_depth);
       } else {  // auto-selected qp offset
         q_cfg->fixed_qp_offsets[i] =
-            get_modeled_qp_offset(rc_cfg->cq_level, i, tool_cfg->bit_depth);
+            get_modeled_qp_offset(rc_cfg->qp, i, tool_cfg->bit_depth);
       }
     } else {
       q_cfg->fixed_qp_offsets[i] = -1.0;
@@ -899,8 +1024,10 @@
   algo_cfg->enable_tpl_model =
       resize_cfg->resize_mode ? 0 : extra_cfg->enable_tpl_model;
 
-  // Set two-pass stats configuration.
+// Set two-pass stats configuration.
+#if !CONFIG_SINGLEPASS
   oxcf->twopass_stats_in = cfg->rc_twopass_stats_in;
+#endif  // !CONFIG_SINGLEPASS
 
   // Set Key frame configuration.
   kf_cfg->fwd_kf_enabled = cfg->fwd_kf_enabled;
@@ -932,10 +1059,14 @@
   gf_cfg->gf_min_pyr_height = extra_cfg->gf_min_pyr_height;
   gf_cfg->gf_max_pyr_height = extra_cfg->gf_max_pyr_height;
 
+  oxcf->subgop_config_str = extra_cfg->subgop_config_str;
+  oxcf->subgop_config_path = extra_cfg->subgop_config_path;
+
   // Set tune related configuration.
   tune_cfg->tuning = extra_cfg->tuning;
   tune_cfg->vmaf_model_path = extra_cfg->vmaf_model_path;
   tune_cfg->content = extra_cfg->content;
+
   if (cfg->large_scale_tile) {
     tune_cfg->film_grain_test_vector = 0;
     tune_cfg->film_grain_table_filename = NULL;
@@ -1040,10 +1171,8 @@
         (uint8_t)cfg->rc_superres_denominator;
     superres_cfg->superres_kf_scale_denominator =
         (uint8_t)cfg->rc_superres_kf_denominator;
-    superres_cfg->superres_qthresh =
-        av1_quantizer_to_qindex(cfg->rc_superres_qthresh);
-    superres_cfg->superres_kf_qthresh =
-        av1_quantizer_to_qindex(cfg->rc_superres_kf_qthresh);
+    superres_cfg->superres_qthresh = cfg->rc_superres_qthresh;
+    superres_cfg->superres_kf_qthresh = cfg->rc_superres_kf_qthresh;
     if (superres_cfg->superres_mode == AOM_SUPERRES_FIXED &&
         superres_cfg->superres_scale_denominator == SCALE_NUMERATOR &&
         superres_cfg->superres_kf_scale_denominator == SCALE_NUMERATOR) {
@@ -1085,6 +1214,9 @@
          sizeof(oxcf->target_seq_level_idx));
   oxcf->tier_mask = extra_cfg->tier_mask;
 
+  if (update_config) {
+    update_encoder_config(&cfg->encoder_cfg, extra_cfg);
+  }
   return AOM_CODEC_OK;
 }
 
@@ -1095,8 +1227,13 @@
   int force_key = 0;
 
   if (cfg->g_w != ctx->cfg.g_w || cfg->g_h != ctx->cfg.g_h) {
+#if CONFIG_SINGLEPASS
+    if (cfg->g_lag_in_frames > 1)
+      ERROR("Cannot change width or height after initialization");
+#else
     if (cfg->g_lag_in_frames > 1 || cfg->g_pass != AOM_RC_ONE_PASS)
       ERROR("Cannot change width or height after initialization");
+#endif  // CONFIG_SINGLEPASS
     if (!valid_ref_frame_size(ctx->cfg.g_w, ctx->cfg.g_h, cfg->g_w, cfg->g_h) ||
         (initial_dimensions->width &&
          (int)cfg->g_w > initial_dimensions->width) ||
@@ -1120,7 +1257,7 @@
 
   if (res == AOM_CODEC_OK) {
     ctx->cfg = *cfg;
-    set_encoder_config(&ctx->oxcf, &ctx->cfg, &ctx->extra_cfg);
+    set_encoder_config(&ctx->oxcf, &ctx->cfg, &ctx->extra_cfg, 0);
     // On profile change, request a key frame
     force_key |= ctx->cpi->common.seq_params.profile != ctx->oxcf.profile;
     av1_change_config(ctx->cpi, &ctx->oxcf);
@@ -1146,14 +1283,6 @@
   return AOM_CODEC_OK;
 }
 
-static aom_codec_err_t ctrl_get_quantizer64(aom_codec_alg_priv_t *ctx,
-                                            va_list args) {
-  int *const arg = va_arg(args, int *);
-  if (arg == NULL) return AOM_CODEC_INVALID_PARAM;
-  *arg = av1_qindex_to_quantizer(av1_get_quantizer(ctx->cpi));
-  return AOM_CODEC_OK;
-}
-
 static aom_codec_err_t ctrl_get_baseline_gf_interval(aom_codec_alg_priv_t *ctx,
                                                      va_list args) {
   int *const arg = va_arg(args, int *);
@@ -1167,7 +1296,7 @@
   const aom_codec_err_t res = validate_config(ctx, &ctx->cfg, extra_cfg);
   if (res == AOM_CODEC_OK) {
     ctx->extra_cfg = *extra_cfg;
-    set_encoder_config(&ctx->oxcf, &ctx->cfg, &ctx->extra_cfg);
+    set_encoder_config(&ctx->oxcf, &ctx->cfg, &ctx->extra_cfg, 1);
     av1_change_config(ctx->cpi, &ctx->oxcf);
     if (ctx->cpi_lap != NULL) {
       av1_change_config(ctx->cpi_lap, &ctx->oxcf);
@@ -1275,10 +1404,9 @@
   return update_extra_cfg(ctx, &extra_cfg);
 }
 
-static aom_codec_err_t ctrl_set_cq_level(aom_codec_alg_priv_t *ctx,
-                                         va_list args) {
+static aom_codec_err_t ctrl_set_qp(aom_codec_alg_priv_t *ctx, va_list args) {
   struct av1_extracfg extra_cfg = ctx->extra_cfg;
-  extra_cfg.cq_level = CAST(AOME_SET_CQ_LEVEL, args);
+  extra_cfg.qp = CAST(AOME_SET_QP, args);
   return update_extra_cfg(ctx, &extra_cfg);
 }
 
@@ -1761,6 +1889,20 @@
   return update_extra_cfg(ctx, &extra_cfg);
 }
 
+static aom_codec_err_t ctrl_set_subgop_config_str(aom_codec_alg_priv_t *ctx,
+                                                  va_list args) {
+  struct av1_extracfg extra_cfg = ctx->extra_cfg;
+  extra_cfg.subgop_config_str = CAST(AV1E_SET_SUBGOP_CONFIG_STR, args);
+  return update_extra_cfg(ctx, &extra_cfg);
+}
+
+static aom_codec_err_t ctrl_set_subgop_config_path(aom_codec_alg_priv_t *ctx,
+                                                   va_list args) {
+  struct av1_extracfg extra_cfg = ctx->extra_cfg;
+  extra_cfg.subgop_config_path = CAST(AV1E_SET_SUBGOP_CONFIG_PATH, args);
+  return update_extra_cfg(ctx, &extra_cfg);
+}
+
 static aom_codec_err_t ctrl_set_film_grain_test_vector(
     aom_codec_alg_priv_t *ctx, va_list args) {
   struct av1_extracfg extra_cfg = ctx->extra_cfg;
@@ -1983,9 +2125,11 @@
       priv->timestamp_ratio.num =
           (int64_t)priv->cfg.g_timebase.num * TICKS_PER_SEC;
       reduce_ratio(&priv->timestamp_ratio);
-
-      set_encoder_config(&priv->oxcf, &priv->cfg, &priv->extra_cfg);
-      if (priv->oxcf.rc_cfg.mode != AOM_CBR && priv->oxcf.pass == 0 &&
+      set_encoder_config(&priv->oxcf, &priv->cfg, &priv->extra_cfg, 0);
+      if (priv->oxcf.rc_cfg.mode != AOM_CBR &&
+#if !CONFIG_SINGLEPASS
+          priv->oxcf.pass == 0 &&
+#endif  // !CONFIG_SINGLEPASS
           priv->oxcf.mode == GOOD) {
         // Enable look ahead - enabled for AOM_Q, AOM_CQ, AOM_VBR
         *num_lap_buffers = priv->cfg.g_lag_in_frames;
@@ -2077,6 +2221,77 @@
   return flags;
 }
 
+static void calculate_psnr(AV1_COMP *cpi, PSNR_STATS *psnr) {
+  int i;
+  PSNR_STATS stats;
+#if CONFIG_AV1_HIGHBITDEPTH
+  const uint32_t in_bit_depth = cpi->oxcf.input_cfg.input_bit_depth;
+  const uint32_t bit_depth = cpi->td.mb.e_mbd.bd;
+  aom_calc_highbd_psnr(cpi->unfiltered_source, &cpi->common.cur_frame->buf,
+                       &stats, bit_depth, in_bit_depth);
+#else
+  aom_calc_psnr(cpi->unfiltered_source, &cpi->common.cur_frame->buf, psnr);
+#endif
+
+  for (i = 0; i < 4; ++i) {
+    psnr->psnr[i] = stats.psnr[i];
+  }
+}
+
+static void report_stats(AV1_COMP *cpi, size_t frame_size, uint64_t cx_time) {
+  const AV1_COMMON *const cm = &cpi->common;
+  const int base_qindex = cm->quant_params.base_qindex;
+  const char frameType[5][20] = {
+    " KEY ", "INTER", "INTRA", "  S  ", " UNK ",
+  };
+
+  PSNR_STATS psnr;
+
+  for (int i = 0; i < 4; ++i) {
+    psnr.psnr[i] = 0;
+  }
+
+  if (cpi->b_calculate_psnr) {
+    calculate_psnr(cpi, &psnr);
+  }
+
+  if (!cm->show_existing_frame) {
+    // Get reference frame information
+    int ref_poc[INTER_REFS_PER_FRAME];
+    for (int ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
+      const int ref_idx = ref_frame - LAST_FRAME;
+      const RefCntBuffer *const buf = get_ref_frame_buf(cm, ref_frame);
+      ref_poc[ref_idx] = buf ? (int)buf->absolute_poc : -1;
+    }
+    if (cpi->b_calculate_psnr) {
+      fprintf(stdout,
+              "POC:%6d [%s][Q:%3d]: %10" PRIu64
+              " Bytes, "
+              "%6.1fms, %2.4f dB(Y), %2.4f dB(U), "
+              "%2.4f dB(V), "
+              "%2.4f dB(Avg)",
+              cm->cur_frame->absolute_poc,
+              frameType[cm->current_frame.frame_type], base_qindex,
+              (uint64_t)frame_size, cx_time / 1000.0, psnr.psnr[1],
+              psnr.psnr[2], psnr.psnr[3], psnr.psnr[0]);
+    } else {
+      fprintf(stdout,
+              "POC:%6d [%s][Q:%3d]: %10" PRIu64
+              " Bytes, "
+              "%6.1fms",
+              cm->cur_frame->absolute_poc,
+              frameType[cm->current_frame.frame_type], base_qindex,
+              (uint64_t)frame_size, cx_time / 1000.0);
+    }
+
+    fprintf(stdout, "    [");
+    for (int ref_idx = 0; ref_idx < INTER_REFS_PER_FRAME; ++ref_idx) {
+      fprintf(stdout, "%3d,", ref_poc[ref_idx]);
+    }
+    fprintf(stdout, "]\n");
+  }
+}
+
 // TODO(Mufaddal): Check feasibility of abstracting functions related to LAP
 // into a separate function.
 static aom_codec_err_t encoder_encode(aom_codec_alg_priv_t *ctx,
@@ -2094,7 +2309,11 @@
 
   if (cpi == NULL) return AOM_CODEC_INVALID_PARAM;
 
+#if CONFIG_SINGLEPASS
+  if (cpi->lap_enabled && cpi_lap == NULL)
+#else
   if (cpi->lap_enabled && cpi_lap == NULL && cpi->oxcf.pass == 0)
+#endif  // CONFIG_SINGLEPASS
     return AOM_CODEC_INVALID_PARAM;
 
   if (img != NULL) {
@@ -2166,7 +2385,12 @@
 
   if (res == AOM_CODEC_OK) {
     // Set up internal flags
-    if (ctx->base.init_flags & AOM_CODEC_USE_PSNR) cpi->b_calculate_psnr = 1;
+    if (ctx->base.init_flags & AOM_CODEC_USE_PSNR) {
+      cpi->b_calculate_psnr = 1;
+    }
+    if (ctx->base.init_flags & AOM_CODEC_USE_PER_FRAME_STATS) {
+      cpi->print_per_frame_stats = 1;
+    }
 
     if (img != NULL) {
       if (!ctx->pts_offset_initialized) {
@@ -2249,6 +2473,9 @@
     int has_no_show_keyframe = 0;
     int num_workers = 0;
 
+#if CONFIG_SINGLEPASS
+    num_workers = av1_compute_num_enc_workers(cpi, cpi->oxcf.max_threads);
+#else
     if (cpi->oxcf.pass == 1) {
 #if !CONFIG_REALTIME_ONLY
       num_workers = av1_fp_compute_num_enc_workers(cpi);
@@ -2256,6 +2483,7 @@
     } else {
       num_workers = av1_compute_num_enc_workers(cpi, cpi->oxcf.max_threads);
     }
+#endif  // CONFIG_SINGLEPASS
     if ((num_workers > 1) && (cpi->mt_info.num_workers == 0))
       av1_create_workers(cpi, num_workers);
 
@@ -2285,11 +2513,17 @@
     // visible frame.
     int64_t dst_time_stamp;
     int64_t dst_end_time_stamp;
+    struct aom_usec_timer timer;
     while (cx_data_sz - index_size >= ctx->cx_data_sz / 2 &&
            !is_frame_visible) {
+      uint64_t cx_time = 0;
+      aom_usec_timer_start(&timer);
+
       const int status = av1_get_compressed_data(
           cpi, &lib_flags, &frame_size, cx_data, &dst_time_stamp,
           &dst_end_time_stamp, !img, timestamp_ratio);
+      aom_usec_timer_mark(&timer);
+      cx_time += aom_usec_timer_elapsed(&timer);
       if (status == -1) break;
       if (status != AOM_CODEC_OK) {
         aom_internal_error(&cpi->common.error, AOM_CODEC_ERROR, NULL);
@@ -2361,6 +2595,10 @@
         has_no_show_keyframe |=
             (!is_frame_visible &&
              cpi->common.current_frame.frame_type == KEY_FRAME);
+
+        if (cpi->print_per_frame_stats) {
+          report_stats(cpi, frame_size, cx_time);
+        }
       }
     }
     if (is_frame_visible) {
@@ -2619,8 +2857,8 @@
       for (tl = 0; tl < cm->number_temporal_layers; ++tl) {
         const int layer = LAYER_IDS_TO_IDX(sl, tl, cm->number_temporal_layers);
         LAYER_CONTEXT *lc = &cpi->svc.layer_context[layer];
-        lc->max_q = params->max_quantizers[layer];
-        lc->min_q = params->min_quantizers[layer];
+        lc->max_qp = params->max_quantizers[layer];
+        lc->min_qp = params->min_quantizers[layer];
         lc->scaling_factor_num = params->scaling_factor_num[sl];
         lc->scaling_factor_den = params->scaling_factor_den[sl];
         lc->layer_target_bitrate = 1000 * params->layer_target_bitrate[layer];
@@ -2770,7 +3008,7 @@
   { AOME_SET_ARNR_MAXFRAMES, ctrl_set_arnr_max_frames },
   { AOME_SET_ARNR_STRENGTH, ctrl_set_arnr_strength },
   { AOME_SET_TUNING, ctrl_set_tuning },
-  { AOME_SET_CQ_LEVEL, ctrl_set_cq_level },
+  { AOME_SET_QP, ctrl_set_qp },
   { AOME_SET_MAX_INTRA_BITRATE_PCT, ctrl_set_rc_max_intra_bitrate_pct },
   { AOME_SET_NUMBER_SPATIAL_LAYERS, ctrl_set_number_spatial_layers },
   { AV1E_SET_MAX_INTER_BITRATE_PCT, ctrl_set_rc_max_inter_bitrate_pct },
@@ -2856,6 +3094,8 @@
   { AV1E_SET_SUPERBLOCK_SIZE, ctrl_set_superblock_size },
   { AV1E_SET_SINGLE_TILE_DECODING, ctrl_set_single_tile_decoding },
   { AV1E_SET_VMAF_MODEL_PATH, ctrl_set_vmaf_model_path },
+  { AV1E_SET_SUBGOP_CONFIG_STR, ctrl_set_subgop_config_str },
+  { AV1E_SET_SUBGOP_CONFIG_PATH, ctrl_set_subgop_config_path },
   { AV1E_SET_FILM_GRAIN_TEST_VECTOR, ctrl_set_film_grain_test_vector },
   { AV1E_SET_FILM_GRAIN_TABLE, ctrl_set_film_grain_table },
   { AV1E_SET_DENOISE_NOISE_LEVEL, ctrl_set_denoise_noise_level },
@@ -2873,7 +3113,6 @@
 
   // Getters
   { AOME_GET_LAST_QUANTIZER, ctrl_get_quantizer },
-  { AOME_GET_LAST_QUANTIZER_64, ctrl_get_quantizer64 },
   { AV1_GET_REFERENCE, ctrl_get_reference },
   { AV1E_GET_ACTIVEMAP, ctrl_get_active_map },
   { AV1_GET_NEW_FRAME_IMAGE, ctrl_get_new_frame_image },
@@ -2917,15 +3156,17 @@
       AOM_SUPERRES_NONE,  // rc_superres_mode
       SCALE_NUMERATOR,    // rc_superres_denominator
       SCALE_NUMERATOR,    // rc_superres_kf_denominator
-      63,                 // rc_superres_qthresh
-      32,                 // rc_superres_kf_qthresh
+      255,                // rc_superres_qthresh
+      128,                // rc_superres_kf_qthresh
 
-      AOM_VBR,      // rc_end_usage
+      AOM_VBR,  // rc_end_usage
+#if !CONFIG_SINGLEPASS
       { NULL, 0 },  // rc_twopass_stats_in
+#endif              // !CONFIG_SINGLEPASS
       { NULL, 0 },  // rc_firstpass_mb_stats_in
       256,          // rc_target_bandwidth
       0,            // rc_min_quantizer
-      63,           // rc_max_quantizer
+      255,          // rc_max_quantizer
       25,           // rc_undershoot_pct
       25,           // rc_overshoot_pct
 
@@ -2933,7 +3174,9 @@
       4000,  // rc_buffer_initial_size
       5000,  // rc_buffer_optimal_size
 
+#if !CONFIG_SINGLEPASS
       50,    // rc_two_pass_vbrbias
+#endif       // !CONFIG_SINGLEPASS
       0,     // rc_two_pass_vbrmin_section
       2000,  // rc_two_pass_vbrmax_section
 
@@ -2954,8 +3197,8 @@
       { 0 },                   // tile_heights
       0,                       // use_fixed_qp_offsets
       { -1, -1, -1, -1, -1 },  // fixed_qp_offsets
-      { 0, 128, 128, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  // cfg
+      { 0, 128, 128, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+        0, 1,   1,   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },  // cfg
   },
   {
       // NOLINT
@@ -2987,15 +3230,17 @@
       AOM_SUPERRES_NONE,  // rc_superres_mode
       SCALE_NUMERATOR,    // rc_superres_denominator
       SCALE_NUMERATOR,    // rc_superres_kf_denominator
-      63,                 // rc_superres_qthresh
-      32,                 // rc_superres_kf_qthresh
+      255,                // rc_superres_qthresh
+      128,                // rc_superres_kf_qthresh
 
-      AOM_CBR,      // rc_end_usage
+      AOM_CBR,  // rc_end_usage
+#if !CONFIG_SINGLEPASS
       { NULL, 0 },  // rc_twopass_stats_in
+#endif              // !CONFIG_SINGLEPASS
       { NULL, 0 },  // rc_firstpass_mb_stats_in
       256,          // rc_target_bandwidth
       0,            // rc_min_quantizer
-      63,           // rc_max_quantizer
+      255,          // rc_max_quantizer
       25,           // rc_undershoot_pct
       25,           // rc_overshoot_pct
 
@@ -3003,7 +3248,9 @@
       4000,  // rc_buffer_initial_size
       5000,  // rc_buffer_optimal_size
 
+#if !CONFIG_SINGLEPASS
       50,    // rc_two_pass_vbrbias
+#endif       // !CONFIG_SINGLEPASS
       0,     // rc_two_pass_vbrmin_section
       2000,  // rc_two_pass_vbrmax_section
 
@@ -3024,8 +3271,8 @@
       { 0 },                   // tile_heights
       0,                       // use_fixed_qp_offsets
       { -1, -1, -1, -1, -1 },  // fixed_qp_offsets
-      { 0, 128, 128, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-        0, 0,   0,   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },  // cfg
+      { 0, 128, 128, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
+        0, 1,   1,   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },  // cfg
   },
 };
 
diff --git a/av1/common/av1_common_int.h b/av1/common/av1_common_int.h
index eb5cd75..ad4b079 100644
--- a/av1/common/av1_common_int.h
+++ b/av1/common/av1_common_int.h
@@ -134,7 +134,10 @@
   // the limitation of get_relative_dist() which returns incorrect
   // distance when a very old frame is used as a reference.
   unsigned int display_order_hint;
+  unsigned int absolute_poc;
   unsigned int ref_display_order_hint[INTER_REFS_PER_FRAME];
+  // Frame's level within the hierarchical structure
+  unsigned int pyramid_level;
 
   MV_REF *mvs;
   uint8_t *seg_map;
@@ -320,6 +323,10 @@
 
   unsigned int order_hint;
   unsigned int display_order_hint;
+  // Frame's level within the hierarchical structure
+  unsigned int pyramid_level;
+  unsigned int absolute_poc;
+  unsigned int key_frame_number;
   unsigned int frame_number;
   SkipModeInfo skip_mode_info;
   int refresh_frame_flags;  // Which ref frames are overwritten by this frame
diff --git a/av1/common/mvref_common.c b/av1/common/mvref_common.c
index 56fab62..1fcf3cf 100644
--- a/av1/common/mvref_common.c
+++ b/av1/common/mvref_common.c
@@ -838,6 +838,8 @@
 void av1_setup_frame_buf_refs(AV1_COMMON *cm) {
   cm->cur_frame->order_hint = cm->current_frame.order_hint;
   cm->cur_frame->display_order_hint = cm->current_frame.display_order_hint;
+  cm->cur_frame->absolute_poc = cm->current_frame.absolute_poc;
+  cm->cur_frame->pyramid_level = cm->current_frame.pyramid_level;
 
   MV_REFERENCE_FRAME ref_frame;
   for (ref_frame = LAST_FRAME; ref_frame <= ALTREF_FRAME; ++ref_frame) {
diff --git a/av1/encoder/aq_complexity.c b/av1/encoder/aq_complexity.c
index 3ea5f63..b5f1efc 100644
--- a/av1/encoder/aq_complexity.c
+++ b/av1/encoder/aq_complexity.c
@@ -158,10 +158,14 @@
                                               cm->seq_params.bit_depth);
 
     aom_clear_system_state();
+#if CONFIG_SINGLEPASS
+    low_var_thresh = DEFAULT_LV_THRESH;
+#else
     low_var_thresh =
         (is_stat_consumption_stage_twopass(cpi))
             ? AOMMAX(exp(cpi->twopass.mb_av_energy), MIN_DEFAULT_LV_THRESH)
             : DEFAULT_LV_THRESH;
+#endif  // CONFIG_SINGLEPASS
 
     av1_setup_src_planes(mb, cpi->source, mi_row, mi_col, num_planes, bs);
     logvar = av1_log_block_var(cpi, mb, bs);
diff --git a/av1/encoder/aq_variance.c b/av1/encoder/aq_variance.c
index 92d7ad1..86a6995 100644
--- a/av1/encoder/aq_variance.c
+++ b/av1/encoder/aq_variance.c
@@ -177,9 +177,14 @@
                                    BLOCK_SIZE bs) {
   double energy, energy_midpoint;
   aom_clear_system_state();
+#if CONFIG_SINGLEPASS
+  (void)cpi;
+  energy_midpoint = DEFAULT_E_MIDPOINT;
+#else
   energy_midpoint = (is_stat_consumption_stage_twopass(cpi))
                         ? cpi->twopass.frame_avg_haar_energy
                         : DEFAULT_E_MIDPOINT;
+#endif  // CONFIG_SINGLEPASS
   energy = av1_log_block_wavelet_energy(x, bs) - energy_midpoint;
   return clamp((int)round(energy), ENERGY_MIN, ENERGY_MAX);
 }
diff --git a/av1/encoder/av1_quantize.c b/av1/encoder/av1_quantize.c
index 45eba9a..2669c9d 100644
--- a/av1/encoder/av1_quantize.c
+++ b/av1/encoder/av1_quantize.c
@@ -774,26 +774,3 @@
         aom_get_qmlevel(quant_params->base_qindex + quant_params->v_ac_delta_q,
                         min_qmlevel, max_qmlevel);
 }
-
-// Table that converts 0-63 Q-range values passed in outside to the Qindex
-// range used internally.
-static const int quantizer_to_qindex[] = {
-  0,   4,   8,   12,  16,  20,  24,  28,  32,  36,  40,  44,  48,
-  52,  56,  60,  64,  68,  72,  76,  80,  84,  88,  92,  96,  100,
-  104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 148, 152,
-  156, 160, 164, 168, 172, 176, 180, 184, 188, 192, 196, 200, 204,
-  208, 212, 216, 220, 224, 228, 232, 236, 240, 244, 249, 255,
-};
-
-int av1_quantizer_to_qindex(int quantizer) {
-  return quantizer_to_qindex[quantizer];
-}
-
-int av1_qindex_to_quantizer(int qindex) {
-  int quantizer;
-
-  for (quantizer = 0; quantizer < 64; ++quantizer)
-    if (quantizer_to_qindex[quantizer] >= qindex) return quantizer;
-
-  return 63;
-}
diff --git a/av1/encoder/av1_quantize.h b/av1/encoder/av1_quantize.h
index ad96197..f1d0c91 100644
--- a/av1/encoder/av1_quantize.h
+++ b/av1/encoder/av1_quantize.h
@@ -111,10 +111,6 @@
 void av1_set_quantizer(struct AV1Common *const cm, int min_qmlevel,
                        int max_qmlevel, int q, int enable_chroma_deltaq);
 
-int av1_quantizer_to_qindex(int quantizer);
-
-int av1_qindex_to_quantizer(int qindex);
-
 void av1_quantize_skip(intptr_t n_coeffs, tran_low_t *qcoeff_ptr,
                        tran_low_t *dqcoeff_ptr, uint16_t *eob_ptr);
 
diff --git a/av1/encoder/encode_strategy.c b/av1/encoder/encode_strategy.c
index 647ce9f..f407a3e 100644
--- a/av1/encoder/encode_strategy.c
+++ b/av1/encoder/encode_strategy.c
@@ -44,6 +44,15 @@
   refresh_frame_flags->alt_ref_frame = refresh_arf;
 }
 
+// Get the subgop config corresponding to the current frame within the
+// gf group
+const SubGOPStepCfg *get_subgop_step(const GF_GROUP *const gf_group,
+                                     int index) {
+  const SubGOPCfg *subgop_cfg = gf_group->subgop_cfg;
+  if (subgop_cfg == NULL) return NULL;
+  return index == 0 ? gf_group->last_step_prev : &subgop_cfg->step[index - 1];
+}
+
 void av1_configure_buffer_updates(
     AV1_COMP *const cpi, RefreshFrameFlagsInfo *const refresh_frame_flags,
     const FRAME_UPDATE_TYPE type, const FRAME_TYPE frame_type,
@@ -262,6 +271,109 @@
   return primary_ref_frame;
 }
 
+// Map the subgop cfg reference list to actual reference buffers. Disable
+// any reference frames that are not listed in the sub gop.
+static void get_gop_cfg_enabled_refs(AV1_COMP *const cpi, int *ref_frame_flags,
+                                     int order_offset) {
+  GF_GROUP gf_group = cpi->gf_group;
+  // The current display index stored has not yet been updated. We must add
+  // The order offset to get the correct value here.
+  const int cur_frame_disp =
+      cpi->common.current_frame.frame_number + order_offset;
+
+  const SubGOPStepCfg *step_gop_cfg =
+      get_subgop_step(&gf_group, gf_group.index);
+  assert(step_gop_cfg != NULL);
+  // No references specified
+  if (step_gop_cfg->num_references < 0) return;
+
+  // Mask to indicate whether or not each ref is allowed by the GOP config
+  int ref_frame_used[REF_FRAMES] = { 0 };
+  // Structures to hash each reference frame based on its pyramid level. This
+  // will allow us to match the pyramid levels specified in the cfg to the best
+  // reference frame index.
+  int n_references[MAX_ARF_LAYERS + 1] = { 0 };
+  int references[MAX_ARF_LAYERS + 1][REF_FRAMES] = { { 0 } };
+  int disp_orders[MAX_ARF_LAYERS + 1][REF_FRAMES] = { { 0 } };
+
+  int frame_level = -1;
+  // Loop over each reference frame and hash it based on its pyramid level
+  for (int frame = LAST_FRAME; frame <= ALTREF_FRAME; frame++) {
+    // Get reference frame buffer
+    const RefCntBuffer *const buf = get_ref_frame_buf(&cpi->common, frame);
+    if (buf == NULL) continue;
+    const int frame_order = (int)buf->display_order_hint;
+    frame_level = buf->pyramid_level;
+
+    // Handle special cases where the pyramid level computed in the gf group
+    // won't match the assumptions made in the subgop cfg.
+    if (frame_order == 0) {
+      // Keyframe case
+      frame_level = 1;
+    } else if (frame_level == MAX_ARF_LAYERS) {
+      // Leaves
+      frame_level = gf_group.max_layer_depth;
+    } else if (frame_level == (MAX_ARF_LAYERS + 1)) {
+      // Altrefs
+      frame_level = 1;
+    }
+
+    // Sometimes a frame index is in multiple reference buffers.
+    // Do not add a frame to the pyramid list multiple times.
+    int found = 0;
+    for (int r = 0; r < n_references[frame_level]; r++) {
+      if (frame_order == disp_orders[frame_level][r]) {
+        found = 1;
+        break;
+      }
+    }
+    // If this is an unseen frame, map its display order and ref buffer
+    // index to its level in the pyramid
+    if (!found) {
+      int n_refs = n_references[frame_level]++;
+      disp_orders[frame_level][n_refs] = frame_order;
+      references[frame_level][n_refs] = frame;
+    }
+  }
+
+  // For each reference specified in the step_gop_cfg, map it to a reference
+  // buffer based on pyramid level if possible.
+  for (int i = 0; i < step_gop_cfg->num_references; i++) {
+    const int level = step_gop_cfg->references[i];
+    const int abs_level = abs(level);
+    int best_frame = -1;
+    int best_frame_index = -1;
+    int best_disp_order = INT_MAX;
+    for (int ref = 0; ref < n_references[abs_level]; ref++) {
+      const int disp_order = disp_orders[abs_level][ref];
+      const int cur_order_diff = cur_frame_disp - disp_order;
+      // This frame has already been used
+      if (disp_order < 0) continue;
+      // This frame is in the wrong direction
+      if ((cur_order_diff < 0) != (level < 0)) continue;
+      // Store this frame if it is the closest in display order to the current
+      // frame so far
+      if (abs(cur_order_diff) < abs(best_disp_order - cur_frame_disp)) {
+        best_frame = references[abs_level][ref];
+        best_frame_index = ref;
+        best_disp_order = disp_order;
+      }
+    }
+    if (best_frame == -1) {
+      fprintf(stderr, "GOP CFG REF FRAME WARNING\n");
+    } else {
+      ref_frame_used[best_frame] = 1;
+      disp_orders[abs_level][best_frame_index] = -1;
+    }
+  }
+
+  // Avoid using references that were not specified by the cfg
+  for (int frame = LAST_FRAME; frame <= ALTREF_FRAME; frame++) {
+    if (!ref_frame_used[frame])
+      *ref_frame_flags &= ~(1 << (frame - LAST_FRAME));
+  }
+}
+
 static void update_fb_of_context_type(
     const AV1_COMP *const cpi, const EncodeFrameParams *const frame_params,
     int *const fb_of_context_type) {
@@ -588,13 +700,87 @@
   }
 }
 
+// Update reference frame stack info according to the subgop cfg
+static void update_ref_frame_map_gopcfg(AV1_COMP *cpi, int gf_index,
+                                        int show_existing_frame,
+                                        int ref_map_index,
+                                        RefBufferStack *ref_buffer_stack) {
+  GF_GROUP gf_group = cpi->gf_group;
+  AV1_COMMON *const cm = &cpi->common;
+
+  if (is_frame_droppable(&cpi->svc, &cpi->ext_flags.refresh_frame)) return;
+  if (cm->current_frame.frame_type == KEY_FRAME || frame_is_sframe(cm)) {
+    if (show_existing_frame)
+      ref_map_index = stack_pop(ref_buffer_stack->arf_stack,
+                                &ref_buffer_stack->arf_stack_size);
+    stack_reset(ref_buffer_stack->lst_stack, &ref_buffer_stack->lst_stack_size);
+    stack_reset(ref_buffer_stack->gld_stack, &ref_buffer_stack->gld_stack_size);
+    stack_reset(ref_buffer_stack->arf_stack, &ref_buffer_stack->arf_stack_size);
+    stack_push(ref_buffer_stack->gld_stack, &ref_buffer_stack->gld_stack_size,
+               ref_map_index);
+  } else {
+    const SubGOPStepCfg *step_gop_cfg = get_subgop_step(&gf_group, gf_index);
+
+    const int pyr_level = step_gop_cfg->pyr_level;
+    const FRAME_TYPE_CODE type_code = step_gop_cfg->type_code;
+    switch (type_code) {
+      case FRAME_TYPE_INO_REPEAT:
+        if (pyr_level == 1) {
+          ref_map_index = stack_pop(ref_buffer_stack->arf_stack,
+                                    &ref_buffer_stack->arf_stack_size);
+          stack_push(ref_buffer_stack->gld_stack,
+                     &ref_buffer_stack->gld_stack_size, ref_map_index);
+        } else {
+          ref_map_index = stack_pop(ref_buffer_stack->arf_stack,
+                                    &ref_buffer_stack->arf_stack_size);
+          stack_push(ref_buffer_stack->lst_stack,
+                     &ref_buffer_stack->lst_stack_size, ref_map_index);
+        }
+        break;
+      case FRAME_TYPE_INO_VISIBLE:
+        update_arf_stack(ref_map_index, ref_buffer_stack);
+        stack_push(ref_buffer_stack->lst_stack,
+                   &ref_buffer_stack->lst_stack_size, ref_map_index);
+        break;
+      case FRAME_TYPE_INO_SHOWEXISTING:
+        ref_map_index = stack_pop(ref_buffer_stack->arf_stack,
+                                  &ref_buffer_stack->arf_stack_size);
+        stack_push(ref_buffer_stack->lst_stack,
+                   &ref_buffer_stack->lst_stack_size, ref_map_index);
+        break;
+      case FRAME_TYPE_OOO_FILTERED:
+      case FRAME_TYPE_OOO_UNFILTERED:
+        update_arf_stack(ref_map_index, ref_buffer_stack);
+        stack_push(ref_buffer_stack->arf_stack,
+                   &ref_buffer_stack->arf_stack_size, ref_map_index);
+        break;
+      default: assert(0 && "unknown type");
+    }
+  }
+  return;
+}
+
+int use_subgop_cfg(const GF_GROUP *const gf_group, int gf_index) {
+  if (gf_index < 0) return 0;
+  if (gf_group->subgop_cfg == NULL) return 0;
+  if (gf_index == 0) return gf_group->last_step_prev != NULL;
+  return 1;
+}
+
 // Update reference frame stack info.
 void av1_update_ref_frame_map(AV1_COMP *cpi,
                               FRAME_UPDATE_TYPE frame_update_type,
-                              FRAME_TYPE frame_type, int show_existing_frame,
-                              int ref_map_index,
+                              FRAME_TYPE frame_type, int gf_index,
+                              int show_existing_frame, int ref_map_index,
                               RefBufferStack *ref_buffer_stack) {
   AV1_COMMON *const cm = &cpi->common;
+  if (use_subgop_cfg(&cpi->gf_group, gf_index)) {
+    // Use the subgop cfg to update the ref frame map
+    update_ref_frame_map_gopcfg(cpi, gf_index, show_existing_frame,
+                                ref_map_index, ref_buffer_stack);
+    return;
+  }
+
   // TODO(jingning): Consider the S-frame same as key frame for the
   // reference frame tracking purpose. The logic might be better
   // expressed than converting the frame update type.
@@ -691,9 +877,70 @@
   return INVALID_IDX;
 }
 
+static int get_refresh_frame_flags_subgop_cfg(
+    const AV1_COMP *const cpi, const RefBufferStack *const ref_buffer_stack,
+    int gf_index, int refresh_mask, int free_fb_index) {
+  const SubGOPStepCfg *step_gop_cfg = get_subgop_step(&cpi->gf_group, gf_index);
+  assert(step_gop_cfg != NULL);
+  const int pyr_level = step_gop_cfg->pyr_level;
+  const FRAME_TYPE_CODE type_code = step_gop_cfg->type_code;
+  // No refresh necessary for these frame types
+  if (type_code == FRAME_TYPE_INO_REPEAT ||
+      type_code == FRAME_TYPE_INO_SHOWEXISTING)
+    return refresh_mask;
+  // If there is an open slot, refresh that one instead of replacing a reference
+  if (free_fb_index != INVALID_IDX) {
+    refresh_mask = 1 << free_fb_index;
+    return refresh_mask;
+  }
+
+  switch (type_code) {
+    case FRAME_TYPE_INO_VISIBLE:
+      if (ref_buffer_stack->lst_stack_size >= 2)
+        refresh_mask =
+            1 << ref_buffer_stack
+                     ->lst_stack[ref_buffer_stack->lst_stack_size - 1];
+      else if (ref_buffer_stack->gld_stack_size >= 2)
+        refresh_mask =
+            1 << ref_buffer_stack
+                     ->gld_stack[ref_buffer_stack->gld_stack_size - 1];
+      else
+        assert(0 && "No ref map index found");
+      break;
+    case FRAME_TYPE_OOO_FILTERED:
+      if (pyr_level == 1) {
+        if (ref_buffer_stack->gld_stack_size >= 3)
+          refresh_mask =
+              1 << ref_buffer_stack
+                       ->gld_stack[ref_buffer_stack->gld_stack_size - 1];
+        else if (ref_buffer_stack->lst_stack_size >= 2)
+          refresh_mask =
+              1 << ref_buffer_stack
+                       ->lst_stack[ref_buffer_stack->lst_stack_size - 1];
+        else
+          assert(0 && "No ref map index found");
+
+      } else {
+        refresh_mask =
+            1 << ref_buffer_stack
+                     ->lst_stack[ref_buffer_stack->lst_stack_size - 1];
+      }
+      break;
+    case FRAME_TYPE_OOO_UNFILTERED:
+      refresh_mask = 1 << ref_buffer_stack
+                              ->lst_stack[ref_buffer_stack->lst_stack_size - 1];
+      break;
+    case FRAME_TYPE_INO_REPEAT:
+    case FRAME_TYPE_INO_SHOWEXISTING:
+    default: assert(0); break;
+  }
+  return refresh_mask;
+}
+
 int av1_get_refresh_frame_flags(const AV1_COMP *const cpi,
                                 const EncodeFrameParams *const frame_params,
                                 FRAME_UPDATE_TYPE frame_update_type,
+                                int gf_index,
                                 const RefBufferStack *const ref_buffer_stack) {
   const AV1_COMMON *const cm = &cpi->common;
   const ExtRefreshFrameFlagsInfo *const ext_refresh_frame_flags =
@@ -763,6 +1010,11 @@
 
   // Search for the open slot to store the current frame.
   int free_fb_index = get_free_ref_map_index(ref_buffer_stack);
+
+  if (use_subgop_cfg(&cpi->gf_group, gf_index)) {
+    return get_refresh_frame_flags_subgop_cfg(cpi, ref_buffer_stack, gf_index,
+                                              refresh_mask, free_fb_index);
+  }
   switch (frame_update_type) {
     case KF_UPDATE:
     case GF_UPDATE:
@@ -905,6 +1157,9 @@
              get_frame_update_type(&cpi->gf_group) == INTNL_ARF_UPDATE) {
     // ARF
     apply_filtering = oxcf->algo_cfg.arnr_max_frames > 0;
+    if (gf_group->is_user_specified) {
+      apply_filtering &= gf_group->is_filtered[gf_group->index];
+    }
     if (apply_filtering) {
       arf_src_index = gf_group->arf_src_offset[gf_group->index];
     }
@@ -1176,7 +1431,11 @@
   if (use_one_pass_rt_params) {
     av1_get_one_pass_rt_params(cpi, &frame_params, *frame_flags);
   } else if (!is_stat_generation_stage(cpi)) {
-    av1_get_second_pass_params(cpi, &frame_params, &frame_input, *frame_flags);
+    av1_get_second_pass_params(cpi, &frame_params,
+#if !CONFIG_SINGLEPASS
+                               &frame_input,
+#endif  // !CONFIG_SINGLEPASS
+                               *frame_flags);
   }
 #endif
   FRAME_UPDATE_TYPE frame_update_type = get_frame_update_type(gf_group);
@@ -1267,8 +1526,15 @@
         choose_primary_ref_frame(cpi, &frame_params);
     frame_params.order_offset = gf_group->arf_src_offset[gf_group->index];
 
+    if (!is_stat_generation_stage(cpi) &&
+        use_subgop_cfg(&cpi->gf_group, cpi->gf_group.index)) {
+      get_gop_cfg_enabled_refs(cpi, &frame_params.ref_frame_flags,
+                               frame_params.order_offset);
+    }
+
     frame_params.refresh_frame_flags = av1_get_refresh_frame_flags(
-        cpi, &frame_params, frame_update_type, &cpi->ref_buffer_stack);
+        cpi, &frame_params, frame_update_type, cpi->gf_group.index,
+        &cpi->ref_buffer_stack);
 
     frame_params.existing_fb_idx_to_show =
         frame_params.show_existing_frame
@@ -1320,8 +1586,8 @@
       int ref_map_index =
           av1_get_refresh_ref_frame_map(cm->current_frame.refresh_frame_flags);
       av1_update_ref_frame_map(cpi, frame_update_type, frame_params.frame_type,
-                               cm->show_existing_frame, ref_map_index,
-                               &cpi->ref_buffer_stack);
+                               cpi->gf_group.index, cm->show_existing_frame,
+                               ref_map_index, &cpi->ref_buffer_stack);
     }
   }
 
diff --git a/av1/encoder/encode_strategy.h b/av1/encoder/encode_strategy.h
index 4bafb0a..e17ac2f 100644
--- a/av1/encoder/encode_strategy.h
+++ b/av1/encoder/encode_strategy.h
@@ -69,14 +69,15 @@
 int av1_get_refresh_frame_flags(const AV1_COMP *const cpi,
                                 const EncodeFrameParams *const frame_params,
                                 FRAME_UPDATE_TYPE frame_update_type,
+                                int gf_index,
                                 const RefBufferStack *const ref_buffer_stack);
 
 int av1_get_refresh_ref_frame_map(int refresh_frame_flags);
 
 void av1_update_ref_frame_map(AV1_COMP *cpi,
                               FRAME_UPDATE_TYPE frame_update_type,
-                              FRAME_TYPE frame_type, int show_existing_frame,
-                              int ref_map_index,
+                              FRAME_TYPE frame_type, int gf_index,
+                              int show_existing_frame, int ref_map_index,
                               RefBufferStack *ref_buffer_stack);
 
 void av1_get_ref_frames(AV1_COMP *const cpi, RefBufferStack *ref_buffer_stack);
diff --git a/av1/encoder/encodeframe_utils.c b/av1/encoder/encodeframe_utils.c
index 695084f..cbeb58b 100644
--- a/av1/encoder/encodeframe_utils.c
+++ b/av1/encoder/encodeframe_utils.c
@@ -729,6 +729,7 @@
   int bottom_edge = cpi->common.mi_params.mi_rows;
   int is_active_h_edge = 0;
 
+#if !CONFIG_SINGLEPASS
   // For two pass account for any formatting bars detected.
   if (is_stat_consumption_stage_twopass(cpi)) {
     const AV1_COMMON *const cm = &cpi->common;
@@ -743,6 +744,7 @@
     bottom_edge -= (int)(this_frame_stats->inactive_zone_rows * 4);
     bottom_edge = AOMMAX(top_edge, bottom_edge);
   }
+#endif  // !CONFIG_SINGLEPASS
 
   if (((top_edge >= mi_row) && (top_edge < (mi_row + mi_step))) ||
       ((bottom_edge >= mi_row) && (bottom_edge < (mi_row + mi_step)))) {
@@ -759,6 +761,7 @@
   int right_edge = cpi->common.mi_params.mi_cols;
   int is_active_v_edge = 0;
 
+#if !CONFIG_SINGLEPASS
   // For two pass account for any formatting bars detected.
   if (is_stat_consumption_stage_twopass(cpi)) {
     const AV1_COMMON *const cm = &cpi->common;
@@ -773,6 +776,7 @@
     right_edge -= (int)(this_frame_stats->inactive_zone_cols * 4);
     right_edge = AOMMAX(left_edge, right_edge);
   }
+#endif  // !CONFIG_SINGLEPASS
 
   if (((left_edge >= mi_col) && (left_edge < (mi_col + mi_step))) ||
       ((right_edge >= mi_col) && (right_edge < (mi_col + mi_step)))) {
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index b829e55..f763b16 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -13,6 +13,7 @@
 #include <float.h>
 #include <math.h>
 #include <stdio.h>
+#include <string.h>
 
 #include "config/aom_config.h"
 
@@ -67,11 +68,12 @@
 #include "av1/encoder/rc_utils.h"
 #include "av1/encoder/rd.h"
 #include "av1/encoder/rdopt.h"
+#include "av1/encoder/reconinter_enc.h"
 #include "av1/encoder/segmentation.h"
 #include "av1/encoder/speed_features.h"
+#include "av1/encoder/subgop.h"
 #include "av1/encoder/superres_scale.h"
 #include "av1/encoder/tpl_model.h"
-#include "av1/encoder/reconinter_enc.h"
 #include "av1/encoder/var_based_part.h"
 
 #if CONFIG_TUNE_VMAF
@@ -578,6 +580,13 @@
   av1_noise_estimate_init(&cpi->noise_estimate, cm->width, cm->height);
 }
 
+int aom_strcmp(const char *a, const char *b) {
+  if (a == NULL && b == NULL) return 0;
+  if (a == NULL && b != NULL) return -1;
+  if (a != NULL && b == NULL) return 1;
+  return strcmp(a, b);
+}
+
 void av1_change_config(struct AV1_COMP *cpi, const AV1EncoderConfig *oxcf) {
   AV1_COMMON *const cm = &cpi->common;
   SequenceHeader *const seq_params = &cm->seq_params;
@@ -793,6 +802,42 @@
   if (lap_lag_in_frames != -1) {
     cpi->oxcf.gf_cfg.lag_in_frames = lap_lag_in_frames;
   }
+
+  bool subgop_config_changed = false;
+  if (aom_strcmp(cpi->subgop_config_path, oxcf->subgop_config_path)) {
+    aom_free(cpi->subgop_config_path);
+    if (oxcf->subgop_config_path != NULL) {
+      cpi->subgop_config_path =
+          (char *)aom_malloc((strlen(oxcf->subgop_config_path) + 1) *
+                             sizeof(*oxcf->subgop_config_path));
+      strcpy(cpi->subgop_config_path, oxcf->subgop_config_path);
+    }
+    subgop_config_changed = true;
+  }
+  if (aom_strcmp(cpi->subgop_config_str, oxcf->subgop_config_str)) {
+    aom_free(cpi->subgop_config_str);
+    if (oxcf->subgop_config_str != NULL) {
+      cpi->subgop_config_str =
+          (char *)aom_malloc((strlen(oxcf->subgop_config_str) + 1) *
+                             sizeof(*oxcf->subgop_config_str));
+      strcpy(cpi->subgop_config_str, oxcf->subgop_config_str);
+    }
+    subgop_config_changed = true;
+  }
+  if (subgop_config_changed && cpi->compressor_stage == ENCODE_STAGE) {
+    av1_init_subgop_config_set(&cpi->subgop_config_set);
+    // Parse config file first
+    av1_process_subgop_config_set_fromfile(cpi->subgop_config_path,
+                                           &cpi->subgop_config_set);
+    // Parse config string next, which may override config file configs
+    // or append to it.
+    av1_process_subgop_config_set(cpi->subgop_config_str,
+                                  &cpi->subgop_config_set);
+    printf("Successfully processed %d subgop configs.\n",
+           cpi->subgop_config_set.num_configs);
+    // Uncomment to print out the configuration
+    // av1_print_subgop_config_set(&cpi->subgop_config_set);
+  }
 }
 
 static INLINE void init_frame_info(FRAME_INFO *frame_info,
@@ -839,9 +884,15 @@
   CommonModeInfoParams *const mi_params = &cm->mi_params;
   mi_params->free_mi = enc_free_mi;
   mi_params->setup_mi = enc_setup_mi;
+#if CONFIG_SINGLEPASS
+  mi_params->set_mb_mi = (cpi->compressor_stage == LAP_STAGE)
+                             ? stat_stage_set_mb_mi
+                             : enc_set_mb_mi;
+#else
   mi_params->set_mb_mi = (oxcf->pass == 1 || cpi->compressor_stage == LAP_STAGE)
                              ? stat_stage_set_mb_mi
                              : enc_set_mb_mi;
+#endif  // CONFIG_SINGLEPASS
 
   mi_params->mi_alloc_bsize = BLOCK_4X4;
 
@@ -862,7 +913,11 @@
 
   cpi->frames_left = cpi->oxcf.input_cfg.limit;
 
+#if CONFIG_SINGLEPASS
+  av1_rc_init(&cpi->oxcf, 0, &cpi->rc);
+#else
   av1_rc_init(&cpi->oxcf, oxcf->pass, &cpi->rc);
+#endif  // CONFIG_SINGLEPASS
 
   // For two pass and lag_in_frames > 33 in LAP.
   cpi->rc.enable_scenecut_detection = ENABLE_SCENECUT_MODE_2;
@@ -883,6 +938,7 @@
   init_frame_info(&cpi->frame_info, cm);
 
   cm->current_frame.frame_number = 0;
+  cm->current_frame.key_frame_number = 0;
   cm->current_frame_id = -1;
   cpi->seq_params_locked = 0;
   cpi->partition_search_skippable_frame = 0;
@@ -950,6 +1006,9 @@
 
 #if !CONFIG_REALTIME_ONLY
   if (is_stat_consumption_stage(cpi)) {
+#if CONFIG_SINGLEPASS
+    av1_init_single_pass_lap(cpi);
+#else
     const size_t packet_sz = sizeof(FIRSTPASS_STATS);
     const int packets = (int)(oxcf->twopass_stats_in.sz / packet_sz);
 
@@ -960,13 +1019,13 @@
       cpi->twopass.stats_in = cpi->twopass.stats_buf_ctx->stats_in_start;
       cpi->twopass.stats_buf_ctx->stats_in_end =
           &cpi->twopass.stats_buf_ctx->stats_in_start[packets - 1];
-
       av1_init_second_pass(cpi);
     } else {
       av1_init_single_pass_lap(cpi);
     }
+#endif  // CONFIG_SINGLEPASS
   }
-#endif
+#endif  // !CONFIG_REALTIME_ONLY
 
   int sb_mi_size = av1_get_sb_mi_size(cm);
 
@@ -1469,6 +1528,8 @@
   av1_remove_common(cm);
   av1_free_ref_frame_buffers(cm->buffer_pool);
 
+  aom_free(cpi->subgop_config_str);
+  aom_free(cpi->subgop_config_path);
   aom_free(cpi);
 
 #ifdef OUTPUT_YUV_REC
@@ -2234,10 +2295,12 @@
   assert(IMPLIES(oxcf->rc_cfg.min_cr > 0, allow_recode));
 
   set_size_independent_vars(cpi);
+#if !CONFIG_SINGLEPASS
   if (is_stat_consumption_stage_twopass(cpi) &&
       cpi->sf.interp_sf.adaptive_interp_filter_search)
     cpi->interp_search_flags.interp_filter_search_mask =
         av1_setup_interp_filter_search_mask(cpi);
+#endif  // !CONFIG_SINGLEPASS
   cpi->source->buf_8bit_valid = 0;
 
   av1_setup_frame_size(cpi);
@@ -3104,15 +3167,34 @@
   memcpy(&cpi->refresh_frame, &frame_params->refresh_frame,
          sizeof(cpi->refresh_frame));
 
-  if (current_frame->frame_type == KEY_FRAME && !cpi->no_show_fwd_kf)
+  if (current_frame->frame_type == KEY_FRAME && !cpi->no_show_fwd_kf) {
+    current_frame->key_frame_number += current_frame->frame_number;
     current_frame->frame_number = 0;
+  }
 
   current_frame->order_hint =
       current_frame->frame_number + frame_params->order_offset;
   current_frame->display_order_hint = current_frame->order_hint;
+  current_frame->pyramid_level = cpi->gf_group.layer_depth[cpi->gf_group.index];
+
+  current_frame->absolute_poc =
+      current_frame->key_frame_number + current_frame->display_order_hint;
+
   current_frame->order_hint %=
       (1 << (cm->seq_params.order_hint_info.order_hint_bits_minus_1 + 1));
 
+#if CONFIG_SINGLEPASS
+  if (is_stat_generation_stage(cpi)) {
+#if !CONFIG_REALTIME_ONLY
+    av1_first_pass(cpi, frame_input->ts_duration);
+#endif
+  } else {
+    if (encode_frame_to_data_rate(cpi, &frame_results->size, dest) !=
+        AOM_CODEC_OK) {
+      return AOM_CODEC_ERROR;
+    }
+  }
+#else  // CONFIG_SINGLEPASS
   if (is_stat_generation_stage(cpi)) {
 #if !CONFIG_REALTIME_ONLY
     av1_first_pass(cpi, frame_input->ts_duration);
@@ -3125,6 +3207,7 @@
   } else {
     return AOM_CODEC_ERROR;
   }
+#endif  // CONFIG_SINGLEPASS
 
   return AOM_CODEC_OK;
 }
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 43279ec..4f937ba 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -489,12 +489,13 @@
   /*!
    * Indicates the Constant/Constrained Quality level.
    */
-  int cq_level;
+  int qp;
   /*!
    * Indicates if the encoding mode is vbr, cbr, constrained quality or
    * constant quality.
    */
   enum aom_rc_mode mode;
+#if !CONFIG_SINGLEPASS
   /*!
    * Indicates the bias (expressed on a scale of 0 to 100) for determining
    * target size for the current frame. The value 0 indicates the optimal CBR
@@ -502,6 +503,7 @@
    * should be used.
    */
   int vbrbias;
+#endif  // !CONFIG_SINGLEPASS
   /*!
    * Indicates the minimum bitrate to be used for a single frame as a percentage
    * of the target bitrate.
@@ -669,11 +671,10 @@
 typedef struct {
   // List of QP offsets for: keyframe, ALTREF, and 3 levels of internal ARFs.
   // If any of these values are negative, fixed offsets are disabled.
-  // Uses internal q range.
   double fixed_qp_offsets[FIXED_QP_OFFSET_COUNT];
   // If true, encoder will use fixed QP offsets, that are either:
   // - Given by the user, and stored in 'fixed_qp_offsets' array, OR
-  // - Picked automatically from cq_level.
+  // - Picked automatically from qp.
   int use_fixed_qp_offsets;
   // Indicates the minimum flatness of the quantization matrix.
   int qm_minlevel;
@@ -779,6 +780,77 @@
   bool enable_palette;
 } ToolCfg;
 
+#define MAX_SUBGOP_CONFIGS 64
+#define MAX_SUBGOP_STEPS 64
+#define MAX_SUBGOP_LENGTH 32
+
+typedef enum {
+  FRAME_TYPE_INO_VISIBLE = 'V',
+  FRAME_TYPE_INO_REPEAT = 'R',
+  FRAME_TYPE_INO_SHOWEXISTING = 'S',
+  FRAME_TYPE_OOO_FILTERED = 'F',
+  FRAME_TYPE_OOO_UNFILTERED = 'U',
+} FRAME_TYPE_CODE;
+
+typedef enum {
+  SUBGOP_IN_GOP_GENERIC = 0,
+  SUBGOP_IN_GOP_LAST,
+  SUBGOP_IN_GOP_FIRST,
+  SUBGOP_IN_GOP_CODES
+} SUBGOP_IN_GOP_CODE;
+
+typedef struct {
+  int8_t disp_frame_idx;
+  FRAME_TYPE_CODE type_code;
+  int8_t pyr_level;
+  int8_t num_references;  // value of -1 indicates unspecified references
+  int8_t references[INTER_REFS_PER_FRAME];
+} SubGOPStepCfg;
+
+typedef struct {
+  int8_t num_frames;
+  SUBGOP_IN_GOP_CODE subgop_in_gop_code;
+  int8_t num_steps;
+  SubGOPStepCfg step[MAX_SUBGOP_STEPS];
+} SubGOPCfg;
+
+/*!\endcond */
+/*!
+ * \brief  Data relating to the current GF/ARF group and the
+ * individual frames within the group
+ */
+typedef struct {
+  /*!\cond */
+  unsigned char is_user_specified;
+  unsigned char index;
+  FRAME_UPDATE_TYPE update_type[MAX_STATIC_GF_GROUP_LENGTH];
+  unsigned char arf_src_offset[MAX_STATIC_GF_GROUP_LENGTH];
+  // The number of frames displayed so far within the GOP at a given coding
+  // frame.
+  unsigned char cur_frame_idx[MAX_STATIC_GF_GROUP_LENGTH];
+  unsigned char is_filtered[MAX_STATIC_GF_GROUP_LENGTH];
+  int layer_depth[MAX_STATIC_GF_GROUP_LENGTH];
+  int arf_boost[MAX_STATIC_GF_GROUP_LENGTH];
+  int max_layer_depth;
+  int max_layer_depth_allowed;
+  // This is currently only populated for AOM_Q mode
+  unsigned char q_val[MAX_STATIC_GF_GROUP_LENGTH];
+  int bit_allocation[MAX_STATIC_GF_GROUP_LENGTH];
+  int arf_index;  // the index in the gf group of ARF, if no arf, then -1
+  int size;
+  // Current subgop cfg being used, NULL if cfg not specified
+  const SubGOPCfg *subgop_cfg;
+  // The cfg step specified for the last frame in the previous gf group.
+  const SubGOPStepCfg *last_step_prev;
+  /*!\endcond */
+} GF_GROUP;
+/*!\cond */
+
+typedef struct {
+  int8_t num_configs;
+  SubGOPCfg config[MAX_SUBGOP_CONFIGS];
+} SubGOPSetCfg;
+
 /*!\endcond */
 /*!
  * \brief Main encoder configuration data structure.
@@ -817,6 +889,7 @@
   // Frame Super-Resolution size scaling.
   SuperResCfg superres_cfg;
 
+#if !CONFIG_SINGLEPASS
   /*!\endcond */
   /*!
    * stats_in buffer contains all of the stats packets produced in the first
@@ -824,6 +897,13 @@
    */
   aom_fixed_buf_t twopass_stats_in;
   /*!\cond */
+#endif  // !CONFIG_SINGLEPASS
+
+  // SubGOP config.
+  const char *subgop_config_str;
+
+  // SubGOP config.
+  const char *subgop_config_path;
 
   // Configuration related to encoder toolsets.
   ToolCfg tool_cfg;
@@ -2206,6 +2286,21 @@
   TWO_PASS twopass;
 
   /*!
+   * SubGOP configuration string
+   */
+  char *subgop_config_str;
+
+  /*!
+   * SubGOP configuration file path
+   */
+  char *subgop_config_path;
+
+  /*!
+   * Information related to subGOP configuration if specified.
+   */
+  SubGOPSetCfg subgop_config_set;
+
+  /*!
    * Information related to a gf group.
    */
   GF_GROUP gf_group;
@@ -2265,6 +2360,11 @@
    */
   int b_calculate_psnr;
 
+  /*!
+   * Prints stats for each frame when set to 1.
+   */
+  int print_per_frame_stats;
+
 #if CONFIG_SPEED_STATS
   /*!
    * For debugging: number of transform searches we have performed.
@@ -2840,18 +2940,29 @@
 static INLINE int is_stat_generation_stage(const AV1_COMP *const cpi) {
   assert(IMPLIES(cpi->compressor_stage == LAP_STAGE,
                  cpi->oxcf.pass == 0 && cpi->lap_enabled));
+#if CONFIG_SINGLEPASS
+  return (cpi->compressor_stage == LAP_STAGE);
+#else
   return (cpi->oxcf.pass == 1 || (cpi->compressor_stage == LAP_STAGE));
+#endif  // CONFIG_SINGLEPASS
 }
+
+#if !CONFIG_SINGLEPASS
 // Check if statistics consumption stage
 static INLINE int is_stat_consumption_stage_twopass(const AV1_COMP *const cpi) {
   return (cpi->oxcf.pass == 2);
 }
+#endif  // !CONFIG_SINGLEPASS
 
 // Check if statistics consumption stage
 static INLINE int is_stat_consumption_stage(const AV1_COMP *const cpi) {
+#if CONFIG_SINGLEPASS
+  return (cpi->compressor_stage == ENCODE_STAGE && cpi->lap_enabled);
+#else
   return (is_stat_consumption_stage_twopass(cpi) ||
           (cpi->oxcf.pass == 0 && (cpi->compressor_stage == ENCODE_STAGE) &&
            cpi->lap_enabled));
+#endif  // CONFIG_SINGLEPASS
 }
 
 /*!\endcond */
@@ -2865,7 +2976,11 @@
  */
 static INLINE int has_no_stats_stage(const AV1_COMP *const cpi) {
   assert(IMPLIES(!cpi->lap_enabled, cpi->compressor_stage == ENCODE_STAGE));
+#if CONFIG_SINGLEPASS
+  return (!cpi->lap_enabled);
+#else
   return (cpi->oxcf.pass == 0 && !cpi->lap_enabled);
+#endif  // CONFIG_SINGLEPASS
 }
 /*!\cond */
 
diff --git a/av1/encoder/encoder_utils.c b/av1/encoder/encoder_utils.c
index 9baea60..35d10a8 100644
--- a/av1/encoder/encoder_utils.c
+++ b/av1/encoder/encoder_utils.c
@@ -300,6 +300,7 @@
                                              { 512, 512, 512 } }
                                          };
 
+#if !CONFIG_SINGLEPASS
 static void configure_static_seg_features(AV1_COMP *cpi) {
   AV1_COMMON *const cm = &cpi->common;
   const RATE_CONTROL *const rc = &cpi->rc;
@@ -428,6 +429,7 @@
     }
   }
 }
+#endif  // !CONFIG_SINGLEPASS
 
 void av1_apply_active_map(AV1_COMP *cpi) {
   struct segmentation *const seg = &cpi->common.seg;
@@ -567,6 +569,7 @@
   *q = av1_rc_pick_q_and_bounds(cpi, &cpi->rc, cm->width, cm->height,
                                 cpi->gf_group.index, bottom_index, top_index);
 
+#if !CONFIG_SINGLEPASS
   // Configure experimental use of segmentation for enhanced coding of
   // static regions if indicated.
   // Only allowed in the second pass of a two pass encode, as it requires
@@ -574,6 +577,7 @@
   if (is_stat_consumption_stage_twopass(cpi) &&
       cpi->sf.hl_sf.static_segmentation)
     configure_static_seg_features(cpi);
+#endif  // !CONFIG_SINGLEPASS
 }
 
 static void reset_film_grain_chroma_params(aom_film_grain_t *pars) {
diff --git a/av1/encoder/firstpass.h b/av1/encoder/firstpass.h
index e37926c..e4908f8 100644
--- a/av1/encoder/firstpass.h
+++ b/av1/encoder/firstpass.h
@@ -167,32 +167,6 @@
   FRAME_CONTENT_TYPES = 2
 } UENUM1BYTE(FRAME_CONTENT_TYPE);
 
-/*!\endcond */
-/*!
- * \brief  Data relating to the current GF/ARF group and the
- * individual frames within the group
- */
-typedef struct {
-  /*!\cond */
-  unsigned char index;
-  FRAME_UPDATE_TYPE update_type[MAX_STATIC_GF_GROUP_LENGTH];
-  unsigned char arf_src_offset[MAX_STATIC_GF_GROUP_LENGTH];
-  // The number of frames displayed so far within the GOP at a given coding
-  // frame.
-  unsigned char cur_frame_idx[MAX_STATIC_GF_GROUP_LENGTH];
-  int layer_depth[MAX_STATIC_GF_GROUP_LENGTH];
-  int arf_boost[MAX_STATIC_GF_GROUP_LENGTH];
-  int max_layer_depth;
-  int max_layer_depth_allowed;
-  // This is currently only populated for AOM_Q mode
-  unsigned char q_val[MAX_STATIC_GF_GROUP_LENGTH];
-  int bit_allocation[MAX_STATIC_GF_GROUP_LENGTH];
-  int arf_index;  // the index in the gf group of ARF, if no arf, then -1
-  int size;
-  /*!\endcond */
-} GF_GROUP;
-/*!\cond */
-
 typedef struct {
   FIRSTPASS_STATS *stats_in_start;
   FIRSTPASS_STATS *stats_in_end;
diff --git a/av1/encoder/gop_structure.c b/av1/encoder/gop_structure.c
index bd03de2..47b51ba 100644
--- a/av1/encoder/gop_structure.c
+++ b/av1/encoder/gop_structure.c
@@ -24,6 +24,7 @@
 #include "av1/encoder/encoder.h"
 #include "av1/encoder/firstpass.h"
 #include "av1/encoder/gop_structure.h"
+#include "av1/encoder/subgop.h"
 
 // Set parameters for frames between 'start' and 'end' (excluding both).
 static void set_multi_layer_params(const TWO_PASS *twopass,
@@ -85,6 +86,104 @@
   }
 }
 
+static int find_backward_alt_ref(const SubGOPCfg *subgop_cfg, int cur_idx) {
+  int cur_disp_idx = subgop_cfg->step[cur_idx].disp_frame_idx;
+  int cur_pyr_level = subgop_cfg->step[cur_idx].pyr_level;
+  int bwd_alt_ref_disp_idx = 0;
+
+  for (int i = 0; i < cur_idx; ++i) {
+    int disp_idx = subgop_cfg->step[i].disp_frame_idx;
+    int pyr_level = subgop_cfg->step[i].pyr_level;
+    if (pyr_level < cur_pyr_level && disp_idx < cur_disp_idx) {
+      if (disp_idx > bwd_alt_ref_disp_idx) bwd_alt_ref_disp_idx = disp_idx;
+    }
+  }
+
+  return bwd_alt_ref_disp_idx;
+}
+
+static int find_forward_alt_ref(const SubGOPCfg *subgop_cfg, int cur_idx) {
+  int cur_disp_idx = subgop_cfg->step[cur_idx].disp_frame_idx;
+  int cur_pyr_level = subgop_cfg->step[cur_idx].pyr_level;
+  int fwd_alt_ref_disp_idx = subgop_cfg->num_frames;
+
+  for (int i = 0; i < cur_idx; ++i) {
+    int disp_idx = subgop_cfg->step[i].disp_frame_idx;
+    int pyr_level = subgop_cfg->step[i].pyr_level;
+    if (pyr_level < cur_pyr_level && disp_idx > cur_disp_idx) {
+      if (disp_idx < fwd_alt_ref_disp_idx) fwd_alt_ref_disp_idx = disp_idx;
+    }
+  }
+
+  return fwd_alt_ref_disp_idx;
+}
+
+static void set_multi_layer_params_from_subgop_cfg(
+    const TWO_PASS *twopass, GF_GROUP *const gf_group,
+    const SubGOPCfg *subgop_cfg, RATE_CONTROL *rc, FRAME_INFO *frame_info,
+    int *cur_frame_idx, int *frame_index) {
+  int use_altref = 0;
+  int last_shown_frame = 0;
+
+  for (int idx = 0; idx < subgop_cfg->num_steps; ++idx) {
+    const SubGOPStepCfg *frame = &subgop_cfg->step[idx];
+    FRAME_TYPE_CODE type = frame->type_code;
+    int pyr_level = frame->pyr_level;
+    int disp_idx = frame->disp_frame_idx;
+
+    if (type == FRAME_TYPE_OOO_FILTERED ||
+        type == FRAME_TYPE_OOO_UNFILTERED) {  // ARF
+      use_altref = 1;
+      gf_group->update_type[*frame_index] =
+          pyr_level == 1 ? ARF_UPDATE : INTNL_ARF_UPDATE;
+      if (pyr_level == 1) {
+        gf_group->arf_index = *frame_index;
+        gf_group->arf_boost[*frame_index] = rc->gfu_boost;
+      } else {
+        int fwd_arf_disp_idx = find_forward_alt_ref(subgop_cfg, idx);
+        int bwd_arf_disp_idx = find_backward_alt_ref(subgop_cfg, idx);
+        gf_group->arf_boost[*frame_index] = av1_calc_arf_boost(
+            twopass, rc, frame_info, disp_idx, fwd_arf_disp_idx - disp_idx,
+            disp_idx - bwd_arf_disp_idx, NULL, NULL);
+      }
+      gf_group->arf_src_offset[*frame_index] = disp_idx - last_shown_frame - 1;
+    } else if (type == FRAME_TYPE_INO_VISIBLE) {  // Leaf
+      gf_group->update_type[*frame_index] = LF_UPDATE;
+
+      int fwd_arf_disp_idx = find_forward_alt_ref(subgop_cfg, idx);
+      gf_group->arf_boost[*frame_index] =
+          av1_calc_arf_boost(twopass, rc, frame_info, disp_idx,
+                             fwd_arf_disp_idx - disp_idx, 0, NULL, NULL);
+      gf_group->arf_src_offset[*frame_index] = 0;
+      last_shown_frame = disp_idx;
+
+      (*cur_frame_idx)++;
+    } else if (type == FRAME_TYPE_INO_REPEAT ||
+               type == FRAME_TYPE_INO_SHOWEXISTING) {  // Overlay
+      gf_group->update_type[*frame_index] =
+          pyr_level == 1 ? OVERLAY_UPDATE : INTNL_OVERLAY_UPDATE;
+      gf_group->arf_boost[*frame_index] = 0;
+      gf_group->arf_src_offset[*frame_index] = 0;
+      last_shown_frame = disp_idx;
+      (*cur_frame_idx)++;
+    }
+    gf_group->is_filtered[*frame_index] = (type == FRAME_TYPE_OOO_FILTERED);
+    gf_group->cur_frame_idx[*frame_index] = *cur_frame_idx;
+    gf_group->layer_depth[*frame_index] = pyr_level;
+    gf_group->max_layer_depth = AOMMAX(gf_group->max_layer_depth, pyr_level);
+
+    if (idx == (subgop_cfg->num_steps - 1)) {
+      if (!use_altref) gf_group->update_type[*frame_index] = GF_UPDATE;
+    } else {
+      (*frame_index)++;
+    }
+  }
+  for (int idx = 1; idx <= *frame_index; idx++) {
+    if (gf_group->layer_depth[idx] == gf_group->max_layer_depth)
+      gf_group->layer_depth[idx] = MAX_ARF_LAYERS;
+  }
+}
+
 static int construct_multi_layer_gf_structure(
     AV1_COMP *cpi, TWO_PASS *twopass, GF_GROUP *const gf_group,
     RATE_CONTROL *rc, FRAME_INFO *const frame_info, int gf_interval,
@@ -125,29 +224,59 @@
     ++cur_frame_index;
   }
 
-  // ALTREF.
-  const int use_altref = gf_group->max_layer_depth_allowed > 0;
-  if (use_altref) {
-    gf_group->update_type[frame_index] = ARF_UPDATE;
-    gf_group->arf_src_offset[frame_index] = gf_interval - 1;
-    gf_group->cur_frame_idx[frame_index] = cur_frame_index;
-    gf_group->layer_depth[frame_index] = 1;
-    gf_group->arf_boost[frame_index] = cpi->rc.gfu_boost;
-    gf_group->max_layer_depth = 1;
-    gf_group->arf_index = frame_index;
-    ++frame_index;
+  // Rest of the frames.
+  SubGOPSetCfg *subgop_cfg_set = &cpi->subgop_config_set;
+  const SubGOPCfg *subgop_cfg0;
+  if (first_frame_update_type == KF_UPDATE) {
+    const SubGOPCfg *subgop_cfg_first =
+        av1_find_subgop_config(subgop_cfg_set, gf_interval, 0, 1);
+    if (subgop_cfg_first == NULL)
+      subgop_cfg0 = av1_find_subgop_config(subgop_cfg_set, gf_interval, 0, 0);
+    else
+      subgop_cfg0 = subgop_cfg_first;
   } else {
-    gf_group->arf_index = -1;
+    subgop_cfg0 = av1_find_subgop_config(subgop_cfg_set, gf_interval, 0, 0);
+  }
+  gf_group->subgop_cfg = NULL;
+  if (subgop_cfg0) {
+    const SubGOPCfg *subgop_cfg = subgop_cfg0;
+    // Check if it is the last GOP
+    if ((rc->frames_to_key - 1) == gf_interval) {
+      const SubGOPCfg *subgop_cfg1 =
+          av1_find_subgop_config(subgop_cfg_set, gf_interval, 1, 0);
+      if (subgop_cfg1) subgop_cfg = subgop_cfg1;
+    }
+
+    gf_group->subgop_cfg = subgop_cfg;
+    gf_group->is_user_specified = 1;
+    set_multi_layer_params_from_subgop_cfg(twopass, gf_group, subgop_cfg, rc,
+                                           frame_info, &cur_frame_index,
+                                           &frame_index);
+  } else {
+    // ALTREF.
+    const int use_altref = gf_group->max_layer_depth_allowed > 0;
+
+    if (use_altref) {
+      gf_group->update_type[frame_index] = ARF_UPDATE;
+      gf_group->arf_src_offset[frame_index] = gf_interval - 1;
+      gf_group->cur_frame_idx[frame_index] = cur_frame_index;
+      gf_group->layer_depth[frame_index] = 1;
+      gf_group->arf_boost[frame_index] = cpi->rc.gfu_boost;
+      gf_group->max_layer_depth = 1;
+      gf_group->arf_index = frame_index;
+      ++frame_index;
+    } else {
+      gf_group->arf_index = -1;
+    }
+    set_multi_layer_params(twopass, gf_group, rc, frame_info, 0, gf_interval,
+                           &cur_frame_index, &frame_index, use_altref + 1);
+    // The end frame will be Overlay frame for an ARF GOP; otherwise set it to
+    // be GF, for consistency, which will be updated in the next GOP.
+    gf_group->update_type[frame_index] =
+        use_altref ? OVERLAY_UPDATE : GF_UPDATE;
+    gf_group->arf_src_offset[frame_index] = 0;
   }
 
-  // Rest of the frames.
-  set_multi_layer_params(twopass, gf_group, rc, frame_info, 0, gf_interval,
-                         &cur_frame_index, &frame_index, use_altref + 1);
-
-  // The end frame will be Overlay frame for an ARF GOP; otherwise set it to
-  // be GF, for consistency, which will be updated in the next GOP.
-  gf_group->update_type[frame_index] = use_altref ? OVERLAY_UPDATE : GF_UPDATE;
-  gf_group->arf_src_offset[frame_index] = 0;
   return frame_index;
 }
 
@@ -188,6 +317,7 @@
   const FRAME_UPDATE_TYPE first_frame_update_type =
       key_frame ? KF_UPDATE
                 : rc->source_alt_ref_active ? OVERLAY_UPDATE : GF_UPDATE;
+  gf_group->is_user_specified = 0;
   gf_group->size = construct_multi_layer_gf_structure(
       cpi, twopass, gf_group, rc, frame_info, rc->baseline_gf_interval,
       first_frame_update_type);
diff --git a/av1/encoder/partition_strategy.c b/av1/encoder/partition_strategy.c
index 8cc6b76..f2f8952 100644
--- a/av1/encoder/partition_strategy.c
+++ b/av1/encoder/partition_strategy.c
@@ -391,12 +391,22 @@
   const int w_mi = mi_size_wide[bsize];
   const int h_mi = mi_size_high[bsize];
   assert(mi_size_wide[bsize] == mi_size_high[bsize]);
-  assert(cpi->ref_frame_flags & av1_ref_frame_flag_list[LAST_FRAME] ||
-         cpi->ref_frame_flags & av1_ref_frame_flag_list[ALTREF_FRAME]);
-
   // Setting up motion search
-  const int ref_list[] = { cpi->rc.is_src_frame_alt_ref ? ALTREF_FRAME
-                                                        : LAST_FRAME };
+  int ref_list[1];
+  ref_list[0] = LAST_FRAME;
+  if (!(cpi->ref_frame_flags & av1_ref_frame_flag_list[LAST_FRAME]) &&
+      !(cpi->ref_frame_flags & av1_ref_frame_flag_list[ALTREF_FRAME])) {
+    for (int i = LAST_FRAME; i <= ALTREF_FRAME; i++) {
+      if (cpi->ref_frame_flags & av1_ref_frame_flag_list[i]) {
+        ref_list[0] = i;
+        break;
+      }
+    }
+  } else {
+    // Setting up motion search
+    ref_list[0] = cpi->rc.is_src_frame_alt_ref ? ALTREF_FRAME : LAST_FRAME;
+  }
+
   const int num_refs = 1;
   const int use_subpixel = 1;
 
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c
index c118a44..11fb729 100644
--- a/av1/encoder/pass2_strategy.c
+++ b/av1/encoder/pass2_strategy.c
@@ -60,14 +60,14 @@
 // Calculate a modified Error used in distributing bits between easier and
 // harder frames.
 #define ACT_AREA_CORRECTION 0.5
+#if !CONFIG_SINGLEPASS
 static double calculate_modified_err(const FRAME_INFO *frame_info,
                                      const TWO_PASS *twopass,
                                      const AV1EncoderConfig *oxcf,
                                      const FIRSTPASS_STATS *this_frame) {
   const FIRSTPASS_STATS *const stats = twopass->stats_buf_ctx->total_stats;
-  if (stats == NULL) {
-    return 0;
-  }
+  if (stats == NULL) return 0;
+  assert(stats->count);
   const double av_weight = stats->weight / stats->count;
   const double av_err = (stats->coded_error * av_weight) / stats->count;
   double modified_error =
@@ -86,6 +86,7 @@
   return fclamp(modified_error, twopass->modified_error_min,
                 twopass->modified_error_max);
 }
+#endif  // !CONFIG_SINGLEPASS
 
 // Resets the first pass file to the given position using a relative seek from
 // the current position.
@@ -274,7 +275,7 @@
         rc->worst_quality);
 
     // Restriction on active max q for constrained quality mode.
-    if (rc_cfg->mode == AOM_CQ) q = AOMMAX(q, rc_cfg->cq_level);
+    if (rc_cfg->mode == AOM_CQ) q = AOMMAX(q, rc_cfg->qp);
     return q;
   }
 }
@@ -1482,8 +1483,12 @@
 
 static INLINE void set_baseline_gf_interval(AV1_COMP *cpi, int arf_position,
                                             int active_max_gf_interval,
-                                            int use_alt_ref,
-                                            int is_final_pass) {
+                                            int use_alt_ref
+#if !CONFIG_SINGLEPASS
+                                            ,
+                                            int is_final_pass
+#endif  // !CONFIG_SINGLEPASS
+) {
   RATE_CONTROL *const rc = &cpi->rc;
   TWO_PASS *const twopass = &cpi->twopass;
   // Set the interval until the next gf.
@@ -1504,12 +1509,18 @@
       // if possible, merge the last two gf groups
       if (rc->frames_to_key <= active_max_gf_interval) {
         rc->baseline_gf_interval = rc->frames_to_key;
-        if (is_final_pass) rc->intervals_till_gf_calculate_due = 0;
+#if !CONFIG_SINGLEPASS
+        if (is_final_pass)
+#endif  // !CONFIG_SINGLEPASS
+          rc->intervals_till_gf_calculate_due = 0;
         // if merging the last two gf groups creates a group that is too long,
         // split them and force the last gf group to be the MIN_FWD_KF_INTERVAL
       } else {
         rc->baseline_gf_interval = rc->frames_to_key - MIN_FWD_KF_INTERVAL;
-        if (is_final_pass) rc->intervals_till_gf_calculate_due = 0;
+#if !CONFIG_SINGLEPASS
+        if (is_final_pass)
+#endif  // !CONFIG_SINGLEPASS
+          rc->intervals_till_gf_calculate_due = 0;
       }
     } else {
       rc->baseline_gf_interval = arf_position - rc->source_alt_ref_pending;
@@ -1565,7 +1576,12 @@
  */
 static void define_gf_group(AV1_COMP *cpi, FIRSTPASS_STATS *this_frame,
                             const EncodeFrameParams *const frame_params,
-                            int max_gop_length, int is_final_pass) {
+                            int max_gop_length
+#if !CONFIG_SINGLEPASS
+                            ,
+                            int is_final_pass
+#endif  // !CONFIG_SINGLEPASS
+) {
   AV1_COMMON *const cm = &cpi->common;
   RATE_CONTROL *const rc = &cpi->rc;
   const AV1EncoderConfig *const oxcf = &cpi->oxcf;
@@ -1589,7 +1605,12 @@
   // Reset the GF group data structures unless this is a key
   // frame in which case it will already have been done.
   if (!is_intra_only) {
+    const SubGOPCfg *subgop_cfg = cpi->gf_group.subgop_cfg;
+    const SubGOPStepCfg *last_step_prev =
+        subgop_cfg != NULL ? &subgop_cfg->step[subgop_cfg->num_steps - 1]
+                           : NULL;
     av1_zero(cpi->gf_group);
+    gf_group->last_step_prev = last_step_prev;
   }
 
   aom_clear_system_state();
@@ -1613,7 +1634,11 @@
 
   // Load stats for the current frame.
   double mod_frame_err =
+#if CONFIG_SINGLEPASS
+      0.0;
+#else
       calculate_modified_err(frame_info, twopass, oxcf, this_frame);
+#endif  // CONFIG_SINGLEPASS
 
   // Note the error of the frame at the start of the group. This will be
   // the GF frame error if we code a normal gf.
@@ -1644,7 +1669,11 @@
     ++i;
     // Accumulate error score of frames in this gf group.
     mod_frame_err =
+#if CONFIG_SINGLEPASS
+        0.0;
+#else
         calculate_modified_err(frame_info, twopass, oxcf, this_frame);
+#endif  // CONFIG_SINGLEPASS
     // accumulate stats for this frame
     accumulate_this_frame_stats(this_frame, mod_frame_err, &gf_stats);
 
@@ -1666,10 +1695,15 @@
   last_frame_stats.frame_sr_coded_error = next_frame.sr_coded_error;
   last_frame_stats.frame_tr_coded_error = next_frame.tr_coded_error;
 
+#if CONFIG_SINGLEPASS
+  rc->intervals_till_gf_calculate_due--;
+  rc->cur_gf_index++;
+#else
   if (is_final_pass) {
     rc->intervals_till_gf_calculate_due--;
     rc->cur_gf_index++;
   }
+#endif  // CONFIG_SINGLEPASS
 
   // Was the group length constrained by the requirement for a new KF?
   rc->constrained_gf_group = (i >= rc->frames_to_key) ? 1 : 0;
@@ -1703,7 +1737,7 @@
         (i >= MIN_GF_INTERVAL);
 
     // TODO(urvang): Improve and use model for VBR, CQ etc as well.
-    if (use_alt_ref && rc_cfg->mode == AOM_Q && rc_cfg->cq_level <= 200) {
+    if (use_alt_ref && rc_cfg->mode == AOM_Q && rc_cfg->qp <= 200) {
       aom_clear_system_state();
       float features[21];
       get_features_from_gf_stats(
@@ -1726,7 +1760,7 @@
   // The length reduction strategy is tweaked for certain cases, and doesn't
   // work well for certain other cases.
   const int allow_gf_length_reduction =
-      ((rc_cfg->mode == AOM_Q && rc_cfg->cq_level <= 128) ||
+      ((rc_cfg->mode == AOM_Q && rc_cfg->qp <= 128) ||
        !cpi->internal_altref_allowed) &&
       !is_lossless_requested(rc_cfg);
 
@@ -1752,7 +1786,10 @@
       if (i - roll_back >= active_min_gf_interval + 1) {
         alt_offset = -roll_back;
         i -= roll_back;
-        if (is_final_pass) rc->intervals_till_gf_calculate_due = 0;
+#if !CONFIG_SINGLEPASS
+        if (is_final_pass)
+#endif  // !CONFIG_SINGLEPASS
+          rc->intervals_till_gf_calculate_due = 0;
       }
     }
   }
@@ -1761,8 +1798,12 @@
   if (use_alt_ref) {
     rc->source_alt_ref_pending = 1;
     gf_group->max_layer_depth_allowed = gf_cfg->gf_max_pyr_height;
+#if CONFIG_SINGLEPASS
+    set_baseline_gf_interval(cpi, i, active_max_gf_interval, use_alt_ref);
+#else
     set_baseline_gf_interval(cpi, i, active_max_gf_interval, use_alt_ref,
                              is_final_pass);
+#endif  // CONFIG_SINGLEPASS
 
     const int forward_frames = (rc->frames_to_key - i >= i - 1)
                                    ? i - 1
@@ -1777,8 +1818,12 @@
     reset_fpf_position(twopass, start_pos);
     rc->source_alt_ref_pending = 0;
     gf_group->max_layer_depth_allowed = 0;
+#if CONFIG_SINGLEPASS
+    set_baseline_gf_interval(cpi, i, active_max_gf_interval, use_alt_ref);
+#else
     set_baseline_gf_interval(cpi, i, active_max_gf_interval, use_alt_ref,
                              is_final_pass);
+#endif  // CONFIG_SINGLEPASS
 
     rc->gfu_boost = AOMMIN(
         MAX_GF_BOOST,
@@ -1793,10 +1838,17 @@
   // the next gf group.
   // TODO(bohanli): should incorporate the usage of alt_ref into
   // calculate_gf_length
+#if CONFIG_SINGLEPASS
+  if (rc->source_alt_ref_pending == 0 &&
+      rc->intervals_till_gf_calculate_due > 0) {
+    rc->gf_intervals[rc->cur_gf_index]--;
+  }
+#else
   if (is_final_pass && rc->source_alt_ref_pending == 0 &&
       rc->intervals_till_gf_calculate_due > 0) {
     rc->gf_intervals[rc->cur_gf_index]--;
   }
+#endif  // CONFIG_SINGLEPASS
 
 #define LAST_ALR_BOOST_FACTOR 0.2f
   rc->arf_boost_factor = 1.0;
@@ -1865,7 +1917,9 @@
 #endif
 
   // Adjust KF group bits and error remaining.
+#if !CONFIG_SINGLEPASS
   if (is_final_pass)
+#endif  // !CONFIG_SINGLEPASS
     twopass->kf_group_error_left -= (int64_t)gf_stats.gf_group_err;
 
   // Set up the structure of this Group-Of-Pictures (same as GF_GROUP)
@@ -2112,7 +2166,7 @@
   const AV1EncoderConfig *const oxcf = &cpi->oxcf;
   const KeyFrameCfg *const kf_cfg = &oxcf->kf_cfg;
   double recent_loop_decay[FRAMES_TO_CHECK_DECAY];
-  FIRSTPASS_STATS last_frame;
+  FIRSTPASS_STATS last_frame = { 0 };
   double decay_accumulator = 1.0;
   int i = 0, j;
   int frames_to_key = 1;
@@ -2143,10 +2197,12 @@
     // Accumulate total number of stats available till next key frame
     num_stats_used_for_kf_boost++;
 
+#if !CONFIG_SINGLEPASS
     // Accumulate kf group error.
     if (kf_group_err != NULL)
       *kf_group_err +=
           calculate_modified_err(frame_info, twopass, oxcf, this_frame);
+#endif  // !CONFIG_SINGLEPASS
 
     // Load the next frame's stats.
     last_frame = *this_frame;
@@ -2381,7 +2437,9 @@
   RATE_CONTROL *const rc = &cpi->rc;
   TWO_PASS *const twopass = &cpi->twopass;
   GF_GROUP *const gf_group = &cpi->gf_group;
+#if !CONFIG_SINGLEPASS
   FRAME_INFO *const frame_info = &cpi->frame_info;
+#endif  // !CONFIG_SINGLEPASS
   AV1_COMMON *const cm = &cpi->common;
   CurrentFrame *const current_frame = &cm->current_frame;
   const AV1EncoderConfig *const oxcf = &cpi->oxcf;
@@ -2441,7 +2499,12 @@
   twopass->kf_group_error_left = 0;  // Group modified error score.
 
   kf_raw_err = this_frame->intra_error;
-  kf_mod_err = calculate_modified_err(frame_info, twopass, oxcf, this_frame);
+  kf_mod_err =
+#if CONFIG_SINGLEPASS
+      0.0;
+#else
+      calculate_modified_err(frame_info, twopass, oxcf, this_frame);
+#endif  // CONFIG_SINGLEPASS
 
   frames_to_key =
       define_kf_interval(cpi, this_frame, &kf_group_err, kf_cfg->key_freq_max);
@@ -2469,15 +2532,23 @@
 
     // Rescan to get the correct error data for the forced kf group.
     for (i = 0; i < rc->frames_to_key; ++i) {
+#if !CONFIG_SINGLEPASS
       kf_group_err +=
           calculate_modified_err(frame_info, twopass, oxcf, &tmp_frame);
+#endif  // !CONFIG_SINGLEPASS
       if (EOF == input_stats(twopass, &tmp_frame)) break;
     }
     rc->next_key_frame_forced = 1;
+
+#if CONFIG_SINGLEPASS
+  } else if (rc->frames_to_key >= kf_cfg->key_freq_max) {
+    rc->next_key_frame_forced = 1;
+#else
   } else if ((twopass->stats_in == twopass->stats_buf_ctx->stats_in_end &&
               is_stat_consumption_stage_twopass(cpi)) ||
              rc->frames_to_key >= kf_cfg->key_freq_max) {
     rc->next_key_frame_forced = 1;
+#endif  // CONFIG_SINGLEPASS
   } else {
     rc->next_key_frame_forced = 0;
   }
@@ -2487,8 +2558,10 @@
   // Special case for the last key frame of the file.
   if (twopass->stats_in >= twopass->stats_buf_ctx->stats_in_end) {
     // Accumulate kf group error.
+#if !CONFIG_SINGLEPASS
     kf_group_err +=
         calculate_modified_err(frame_info, twopass, oxcf, this_frame);
+#endif  // !CONFIG_SINGLEPASS
   }
 
   // Calculate the number of bits that should be assigned to the kf group.
@@ -2740,7 +2813,9 @@
 
 void av1_get_second_pass_params(AV1_COMP *cpi,
                                 EncodeFrameParams *const frame_params,
+#if !CONFIG_SINGLEPASS
                                 const EncodeFrameInput *const frame_input,
+#endif  // !CONFIG_SINGLEPASS
                                 unsigned int frame_flags) {
   RATE_CONTROL *const rc = &cpi->rc;
   TWO_PASS *const twopass = &cpi->twopass;
@@ -2777,8 +2852,7 @@
 
   aom_clear_system_state();
 
-  if (oxcf->rc_cfg.mode == AOM_Q)
-    rc->active_worst_quality = oxcf->rc_cfg.cq_level;
+  if (oxcf->rc_cfg.mode == AOM_Q) rc->active_worst_quality = oxcf->rc_cfg.qp;
   FIRSTPASS_STATS this_frame;
   av1_zero(this_frame);
   // call above fn
@@ -2790,7 +2864,7 @@
       process_first_pass_stats(cpi, &this_frame);
     }
   } else {
-    rc->active_worst_quality = oxcf->rc_cfg.cq_level;
+    rc->active_worst_quality = oxcf->rc_cfg.qp;
   }
 
   // Keyframe and section processing.
@@ -2856,16 +2930,25 @@
 
     reset_fpf_position(twopass, start_position);
 
+#if CONFIG_SINGLEPASS
+    int max_gop_length = MAX_GF_LENGTH_LAP;
+#else
     int max_gop_length =
         (oxcf->gf_cfg.lag_in_frames >= 32 &&
          is_stat_consumption_stage_twopass(cpi))
             ? AOMMIN(MAX_GF_INTERVAL, oxcf->gf_cfg.lag_in_frames -
                                           oxcf->algo_cfg.arnr_max_frames / 2)
             : MAX_GF_LENGTH_LAP;
+#endif  // CONFIG_SINGLEPASS
     if (rc->intervals_till_gf_calculate_due == 0) {
       calculate_gf_length(cpi, max_gop_length, MAX_NUM_GF_INTERVALS);
     }
 
+#if CONFIG_SINGLEPASS
+    // Note max_gop_length = MAX_GF_LENGTH_LAP (16) in 1-pass case
+    assert(max_gop_length == MAX_GF_LENGTH_LAP);
+    define_gf_group(cpi, &this_frame, frame_params, max_gop_length);
+#else
     if (max_gop_length > 16 && oxcf->algo_cfg.enable_tpl_model &&
         !cpi->sf.tpl_sf.disable_gop_length_decision) {
       if (rc->gf_intervals[rc->cur_gf_index] - 1 > 16) {
@@ -2886,6 +2969,7 @@
       }
     }
     define_gf_group(cpi, &this_frame, frame_params, max_gop_length, 1);
+#endif  // CONFIG_SINGLEPASS
     rc->frames_till_gf_update_due = rc->baseline_gf_interval;
     assert(gf_group->index == 0);
 
@@ -2914,6 +2998,7 @@
   setup_target_rate(cpi);
 }
 
+#if !CONFIG_SINGLEPASS
 void av1_init_second_pass(AV1_COMP *cpi) {
   const AV1EncoderConfig *const oxcf = &cpi->oxcf;
   TWO_PASS *const twopass = &cpi->twopass;
@@ -2977,6 +3062,7 @@
   twopass->rolling_arf_group_target_bits = 1;
   twopass->rolling_arf_group_actual_bits = 1;
 }
+#endif  // !CONFIG_SINGLEPASS
 
 void av1_init_single_pass_lap(AV1_COMP *cpi) {
   TWO_PASS *const twopass = &cpi->twopass;
diff --git a/av1/encoder/pass2_strategy.h b/av1/encoder/pass2_strategy.h
index a984320..c079c63 100644
--- a/av1/encoder/pass2_strategy.h
+++ b/av1/encoder/pass2_strategy.h
@@ -64,7 +64,9 @@
 } GF_FRAME_STATS;
 /*!cond */
 
+#if !CONFIG_SINGLEPASS
 void av1_init_second_pass(struct AV1_COMP *cpi);
+#endif  // !CONFIG_SINGLEPASS
 
 void av1_init_single_pass_lap(AV1_COMP *cpi);
 
@@ -91,7 +93,9 @@
  */
 void av1_get_second_pass_params(struct AV1_COMP *cpi,
                                 struct EncodeFrameParams *const frame_params,
+#if !CONFIG_SINGLEPASS
                                 const EncodeFrameInput *const frame_input,
+#endif  // !CONFIG_SINGLEPASS
                                 unsigned int frame_flags);
 
 /*!\brief Adjustments to two pass and rate control after each frame.
diff --git a/av1/encoder/picklpf.c b/av1/encoder/picklpf.c
index 2fa848e..7593d3e 100644
--- a/av1/encoder/picklpf.c
+++ b/av1/encoder/picklpf.c
@@ -38,12 +38,17 @@
 }
 
 int av1_get_max_filter_level(const AV1_COMP *cpi) {
+#if CONFIG_SINGLEPASS
+  (void)cpi;
+  return MAX_LOOP_FILTER;
+#else
   if (is_stat_consumption_stage_twopass(cpi)) {
     return cpi->twopass.section_intra_rating > 8 ? MAX_LOOP_FILTER * 3 / 4
                                                  : MAX_LOOP_FILTER;
   } else {
     return MAX_LOOP_FILTER;
   }
+#endif  // CONFIG_SINGLEPASS
 }
 
 static int64_t try_filter_frame(const YV12_BUFFER_CONFIG *sd,
@@ -145,9 +150,11 @@
     // Bias against raising loop filter in favor of lowering it.
     int64_t bias = (best_err >> (15 - (filt_mid / 8))) * filter_step;
 
+#if !CONFIG_SINGLEPASS
     if ((is_stat_consumption_stage_twopass(cpi)) &&
         (cpi->twopass.section_intra_rating < 20))
       bias = (bias * cpi->twopass.section_intra_rating) / 20;
+#endif  // !CONFIG_SINGLEPASS
 
     // yx, bias less for large block size
     if (cm->features.tx_mode != ONLY_4X4) bias >>= 1;
diff --git a/av1/encoder/ratectrl.c b/av1/encoder/ratectrl.c
index 93f5b1c..561f889 100644
--- a/av1/encoder/ratectrl.c
+++ b/av1/encoder/ratectrl.c
@@ -952,13 +952,12 @@
   return gf_group->layer_depth[gf_index];
 }
 
-static int get_active_cq_level(const RATE_CONTROL *rc,
-                               const AV1EncoderConfig *const oxcf,
-                               int intra_only, aom_superres_mode superres_mode,
-                               int superres_denom) {
+static int get_active_qp(const RATE_CONTROL *rc,
+                         const AV1EncoderConfig *const oxcf, int intra_only,
+                         aom_superres_mode superres_mode, int superres_denom) {
   const RateControlCfg *const rc_cfg = &oxcf->rc_cfg;
   static const double cq_adjust_threshold = 0.1;
-  int active_cq_level = rc_cfg->cq_level;
+  int active_qp = rc_cfg->qp;
   (void)intra_only;
   if (rc_cfg->mode == AOM_CQ || rc_cfg->mode == AOM_Q) {
     // printf("Superres %d %d %d = %d\n", superres_denom, intra_only,
@@ -974,17 +973,17 @@
       } else {
         mult = SUPERRES_QADJ_PER_DENOM_ARFFRAME;
       }
-      active_cq_level = AOMMAX(
-          active_cq_level - ((superres_denom - SCALE_NUMERATOR) * mult), 0);
+      active_qp =
+          AOMMAX(active_qp - ((superres_denom - SCALE_NUMERATOR) * mult), 0);
     }
   }
   if (rc_cfg->mode == AOM_CQ && rc->total_target_bits > 0) {
     const double x = (double)rc->total_actual_bits / rc->total_target_bits;
     if (x < cq_adjust_threshold) {
-      active_cq_level = (int)(active_cq_level * x / cq_adjust_threshold);
+      active_qp = (int)(active_qp * x / cq_adjust_threshold);
     }
   }
-  return active_cq_level;
+  return active_qp;
 }
 
 /*! \brief Pick q index for this frame using fixed q index offsets.
@@ -998,8 +997,8 @@
  * \param[in]   rc          Top level rate control structure
  * \param[in]   gf_group    Configuration of current golden frame group
  * \param[in]   gf_index    Index of this frame in the golden frame group
- * \param[in]   cq_level    Upper bound for q index (this may be same as
- *                          \c oxcf->cq_level, or slightly modified for some
+ * \param[in]   qp          Upper bound for q index (this may be same as
+ *                          \c oxcf->qp, or slightly modified for some
  *                          special cases)
  * \param[in]   bit_depth   Bit depth of the codec (same as
  *                          \c cm->seq_params.bit_depth)
@@ -1008,8 +1007,7 @@
 static int get_q_using_fixed_offsets(const AV1EncoderConfig *const oxcf,
                                      const RATE_CONTROL *const rc,
                                      const GF_GROUP *const gf_group,
-                                     int gf_index, int cq_level,
-                                     int bit_depth) {
+                                     int gf_index, int qp, int bit_depth) {
   assert(oxcf->q_cfg.use_fixed_qp_offsets);
   assert(oxcf->rc_cfg.mode == AOM_Q);
   const FRAME_UPDATE_TYPE update_type = gf_group->update_type[gf_index];
@@ -1018,7 +1016,7 @@
   if (update_type == KF_UPDATE) {
     if (rc->frames_to_key <= 1) {
       // Image / intra-only coding: ignore offsets.
-      return cq_level;
+      return qp;
     }
     offset_idx = 0;
   } else if (update_type == ARF_UPDATE || update_type == GF_UPDATE) {
@@ -1029,18 +1027,18 @@
   } else {  // Leaf level / overlay frame.
     assert(update_type == LF_UPDATE || update_type == OVERLAY_UPDATE ||
            update_type == INTNL_OVERLAY_UPDATE);
-    return cq_level;  // Directly Return worst quality allowed.
+    return qp;  // Directly Return worst quality allowed.
   }
   assert(offset_idx >= 0 && offset_idx < FIXED_QP_OFFSET_COUNT);
   assert(oxcf->q_cfg.fixed_qp_offsets[offset_idx] >= 0);
 
   // Get qindex offset, by first converting to 'q' and then back.
-  const double q_val_orig = av1_convert_qindex_to_q(cq_level, bit_depth);
+  const double q_val_orig = av1_convert_qindex_to_q(qp, bit_depth);
   const double q_val_target =
       AOMMAX(q_val_orig - oxcf->q_cfg.fixed_qp_offsets[offset_idx], 0.0);
   const int delta_qindex =
       av1_compute_qdelta(rc, q_val_orig, q_val_target, bit_depth);
-  return AOMMAX(cq_level + delta_qindex, 0);
+  return AOMMAX(qp + delta_qindex, 0);
 }
 
 /*!\brief Picks q and q bounds given non-CBR rate control params in \c cpi->rc.
@@ -1078,13 +1076,13 @@
   assert(
       IMPLIES(rc_mode == AOM_Q, gf_group->update_type[gf_index] == ARF_UPDATE));
 
-  const int cq_level =
-      get_active_cq_level(rc, oxcf, frame_is_intra_only(cm), cpi->superres_mode,
-                          cm->superres_scale_denominator);
+  const int qp =
+      get_active_qp(rc, oxcf, frame_is_intra_only(cm), cpi->superres_mode,
+                    cm->superres_scale_denominator);
   const int bit_depth = cm->seq_params.bit_depth;
 
   if (oxcf->q_cfg.use_fixed_qp_offsets) {
-    return get_q_using_fixed_offsets(oxcf, rc, gf_group, gf_index, cq_level,
+    return get_q_using_fixed_offsets(oxcf, rc, gf_group, gf_index, qp,
                                      bit_depth);
   }
 
@@ -1096,7 +1094,7 @@
 
   if (frame_is_intra_only(cm)) {
     if (rc_mode == AOM_Q) {
-      const int qindex = cq_level;
+      const int qindex = qp;
       const double q_val = av1_convert_qindex_to_q(qindex, bit_depth);
       const int delta_qindex =
           av1_compute_qdelta(rc, q_val, q_val * 0.25, bit_depth);
@@ -1138,12 +1136,12 @@
             : rc->avg_frame_qindex[KEY_FRAME];
     // For constrained quality dont allow Q less than the cq level
     if (rc_mode == AOM_CQ) {
-      if (q < cq_level) q = cq_level;
+      if (q < qp) q = qp;
       active_best_quality = get_gf_active_quality(rc, q, bit_depth);
       // Constrained quality use slightly lower active best.
       active_best_quality = active_best_quality * 15 / 16;
     } else if (rc_mode == AOM_Q) {
-      const int qindex = cq_level;
+      const int qindex = qp;
       const double q_val = av1_convert_qindex_to_q(qindex, bit_depth);
       const int delta_qindex =
           (refresh_frame_flags->alt_ref_frame)
@@ -1155,7 +1153,7 @@
     }
   } else {
     if (rc_mode == AOM_Q) {
-      const int qindex = cq_level;
+      const int qindex = qp;
       const double q_val = av1_convert_qindex_to_q(qindex, bit_depth);
       const double delta_rate[FIXED_GF_INTERVAL] = { 0.50, 1.0, 0.85, 1.0,
                                                      0.70, 1.0, 0.85, 1.0 };
@@ -1171,8 +1169,8 @@
                                 : inter_minq[rc->avg_frame_qindex[KEY_FRAME]];
       // For the constrained quality mode we don't want
       // q to fall below the cq level.
-      if ((rc_mode == AOM_CQ) && (active_best_quality < cq_level)) {
-        active_best_quality = cq_level;
+      if ((rc_mode == AOM_CQ) && (active_best_quality < qp)) {
+        active_best_quality = qp;
       }
     }
   }
@@ -1258,11 +1256,11 @@
   const AV1_COMMON *const cm = &cpi->common;
   const RATE_CONTROL *const rc = &cpi->rc;
   const AV1EncoderConfig *const oxcf = &cpi->oxcf;
-  const int cq_level =
-      get_active_cq_level(rc, oxcf, frame_is_intra_only(cm), cpi->superres_mode,
-                          cm->superres_scale_denominator);
+  const int qp =
+      get_active_qp(rc, oxcf, frame_is_intra_only(cm), cpi->superres_mode,
+                    cm->superres_scale_denominator);
   const int bit_depth = cm->seq_params.bit_depth;
-  const int q = (int)av1_convert_qindex_to_q(cq_level, bit_depth);
+  const int q = (int)av1_convert_qindex_to_q(qp, bit_depth);
   (void)width;
   (void)height;
   assert(has_no_stats_stage(cpi));
@@ -1277,8 +1275,8 @@
 
 #define STATIC_MOTION_THRESH 95
 static void get_intra_q_and_bounds(const AV1_COMP *cpi, int width, int height,
-                                   int *active_best, int *active_worst,
-                                   int cq_level, int is_fwd_kf) {
+                                   int *active_best, int *active_worst, int qp,
+                                   int is_fwd_kf) {
   const AV1_COMMON *const cm = &cpi->common;
   const RATE_CONTROL *const rc = &cpi->rc;
   const AV1EncoderConfig *const oxcf = &cpi->oxcf;
@@ -1288,10 +1286,9 @@
 
   if (rc->frames_to_key <= 1 && oxcf->rc_cfg.mode == AOM_Q) {
     // If the next frame is also a key frame or the current frame is the
-    // only frame in the sequence in AOM_Q mode, just use the cq_level
-    // as q.
-    active_best_quality = cq_level;
-    active_worst_quality = cq_level;
+    // only frame in the sequence in AOM_Q mode, just use the qp as q.
+    active_best_quality = qp;
+    active_worst_quality = qp;
   } else if (is_fwd_kf) {
     // Handle the special case for forward reference key frames.
     // Increase the boost because this keyframe is used as a forward and
@@ -1309,6 +1306,7 @@
     int delta_qindex;
     int qindex;
 
+#if !CONFIG_SINGLEPASS
     if (is_stat_consumption_stage_twopass(cpi) &&
         cpi->twopass.last_kfgroup_zeromotion_pct >= STATIC_MOTION_THRESH) {
       qindex = AOMMIN(rc->last_kf_qindex, rc->last_boosted_qindex);
@@ -1319,12 +1317,15 @@
       active_worst_quality =
           AOMMIN(qindex + delta_qindex, active_worst_quality);
     } else {
+#endif  // !CONFIG_SINGLEPASS
       qindex = rc->last_boosted_qindex;
       last_boosted_q = av1_convert_qindex_to_q(qindex, bit_depth);
       delta_qindex = av1_compute_qdelta(rc, last_boosted_q,
                                         last_boosted_q * 0.50, bit_depth);
       active_best_quality = AOMMAX(qindex + delta_qindex, rc->best_quality);
+#if !CONFIG_SINGLEPASS
     }
+#endif  // !CONFIG_SINGLEPASS
   } else {
     // Not forced keyframe.
     double q_adj_factor = 1.0;
@@ -1337,19 +1338,23 @@
       active_best_quality /= 2;
     }
 
+#if !CONFIG_SINGLEPASS
     if (is_stat_consumption_stage_twopass(cpi) &&
         cpi->twopass.kf_zeromotion_pct >= STATIC_KF_GROUP_THRESH) {
       active_best_quality /= 3;
     }
+#endif  // !CONFIG_SINGLEPASS
 
     // Allow somewhat lower kf minq with small image formats.
     if ((width * height) <= (352 * 288)) {
       q_adj_factor -= 0.25;
     }
 
+#if !CONFIG_SINGLEPASS
     // Make a further adjustment based on the kf zero motion measure.
     if (is_stat_consumption_stage_twopass(cpi))
       q_adj_factor += 0.05 - (0.001 * (double)cpi->twopass.kf_zeromotion_pct);
+#endif  // !CONFIG_SINGLEPASS
 
     // Convert the adjustment factor to a qindex delta
     // on active_best_quality.
@@ -1487,8 +1492,8 @@
 // The returning active_best_quality could further be adjusted in
 // adjust_active_best_and_worst_quality().
 static int get_active_best_quality(const AV1_COMP *const cpi,
-                                   const int active_worst_quality,
-                                   const int cq_level, const int gf_index) {
+                                   const int active_worst_quality, const int qp,
+                                   const int gf_index) {
   const AV1_COMMON *const cm = &cpi->common;
   const int bit_depth = cm->seq_params.bit_depth;
   const RATE_CONTROL *const rc = &cpi->rc;
@@ -1507,13 +1512,13 @@
   const int is_overlay_frame = rc->is_src_frame_alt_ref;
 
   if (is_leaf_frame || is_overlay_frame) {
-    if (rc_mode == AOM_Q) return cq_level;
+    if (rc_mode == AOM_Q) return qp;
 
     active_best_quality = inter_minq[active_worst_quality];
     // For the constrained quality mode we don't want
     // q to fall below the cq level.
-    if ((rc_mode == AOM_CQ) && (active_best_quality < cq_level)) {
-      active_best_quality = cq_level;
+    if ((rc_mode == AOM_CQ) && (active_best_quality < qp)) {
+      active_best_quality = qp;
     }
     return active_best_quality;
   }
@@ -1521,7 +1526,7 @@
   // TODO(chengchen): can we remove this condition?
   if (rc_mode == AOM_Q && !refresh_frame_flags->alt_ref_frame &&
       !is_intrl_arf_boost) {
-    return cq_level;
+    return qp;
   }
 
   // Determine active_best_quality for frames that are not leaf or overlay.
@@ -1533,7 +1538,7 @@
       rc->avg_frame_qindex[INTER_FRAME] < active_worst_quality) {
     q = rc->avg_frame_qindex[INTER_FRAME];
   }
-  if (rc_mode == AOM_CQ && q < cq_level) q = cq_level;
+  if (rc_mode == AOM_CQ && q < qp) q = qp;
   active_best_quality = get_gf_active_quality(rc, q, bit_depth);
   // Constrained quality use slightly lower active best.
   if (rc_mode == AOM_CQ) active_best_quality = active_best_quality * 15 / 16;
@@ -1577,14 +1582,14 @@
   assert(IMPLIES(has_no_stats_stage(cpi),
                  cpi->oxcf.rc_cfg.mode == AOM_Q &&
                      gf_group->update_type[gf_index] != ARF_UPDATE));
-  const int cq_level =
-      get_active_cq_level(rc, oxcf, frame_is_intra_only(cm), cpi->superres_mode,
-                          cm->superres_scale_denominator);
+  const int qp =
+      get_active_qp(rc, oxcf, frame_is_intra_only(cm), cpi->superres_mode,
+                    cm->superres_scale_denominator);
   const int bit_depth = cm->seq_params.bit_depth;
 
   if (oxcf->q_cfg.use_fixed_qp_offsets) {
-    return get_q_using_fixed_offsets(oxcf, rc, gf_group, gf_group->index,
-                                     cq_level, bit_depth);
+    return get_q_using_fixed_offsets(oxcf, rc, gf_group, gf_group->index, qp,
+                                     bit_depth);
   }
 
   int active_best_quality = 0;
@@ -1598,7 +1603,7 @@
     const int is_fwd_kf = cm->current_frame.frame_type == KEY_FRAME &&
                           cm->show_frame == 0 && cpi->no_show_fwd_kf;
     get_intra_q_and_bounds(cpi, width, height, &active_best_quality,
-                           &active_worst_quality, cq_level, is_fwd_kf);
+                           &active_worst_quality, qp, is_fwd_kf);
 #ifdef STRICT_RC
     active_best_quality = 0;
 #endif
@@ -1608,8 +1613,8 @@
 
     if ((pyramid_level <= 1) || (pyramid_level > MAX_ARF_LAYERS) ||
         (oxcf->rc_cfg.mode == AOM_Q)) {
-      active_best_quality = get_active_best_quality(cpi, active_worst_quality,
-                                                    cq_level, gf_index);
+      active_best_quality =
+          get_active_best_quality(cpi, active_worst_quality, qp, gf_index);
     } else {
       active_best_quality = rc->active_best_quality[pyramid_level - 1] + 1;
       active_best_quality = AOMMIN(active_best_quality, active_worst_quality);
diff --git a/av1/encoder/rc_utils.h b/av1/encoder/rc_utils.h
index b30ef28..d07f349 100644
--- a/av1/encoder/rc_utils.h
+++ b/av1/encoder/rc_utils.h
@@ -118,7 +118,7 @@
     } else if (cpi->oxcf.rc_cfg.mode == AOM_CQ) {
       // Deal with frame undershoot and whether or not we are
       // below the automatically set cq level.
-      if (q > oxcf->rc_cfg.cq_level &&
+      if (q > oxcf->rc_cfg.qp &&
           rc->projected_frame_size < ((rc->this_frame_target * 7) >> 3)) {
         force_recode = 1;
       }
diff --git a/av1/encoder/subgop.c b/av1/encoder/subgop.c
new file mode 100644
index 0000000..1f43311
--- /dev/null
+++ b/av1/encoder/subgop.c
@@ -0,0 +1,447 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include "av1/encoder/subgop.h"
+
+static char *my_strtok_r(char *str, const char *delim, char **saveptr) {
+  if (str == NULL) return NULL;
+  if (strlen(str) == 0) return NULL;
+  char *ptr = str;
+  char *x = strstr(str, delim);
+  if (x) {
+    *x = 0;
+    *saveptr = x + strlen(delim);
+  } else {
+    *saveptr = NULL;
+    return ptr;
+  }
+  return ptr;
+}
+
+static char *read_token_after(char *str, const char *delim, char **saveptr) {
+  if (str == NULL) return NULL;
+  if (strlen(str) == 0) return NULL;
+  char *ptr = str;
+  char *x = strstr(str, delim);
+  if (x) {
+    ptr = x + strlen(delim);
+    while (*x != 0 && !isspace(*x)) x++;
+    *x = 0;
+    *saveptr = x + 1;
+    return ptr;
+  } else {
+    *saveptr = NULL;
+    return NULL;
+  }
+}
+
+static bool readline(char *buf, int size, FILE *fp) {
+  buf[0] = '\0';
+  buf[size - 1] = '\0';
+  char *tmp;
+  while (1) {
+    if (fgets(buf, size, fp) == NULL) {
+      *buf = '\0';
+      return false;
+    } else {
+      if ((tmp = strrchr(buf, '\n')) != NULL) *tmp = '\0';
+      for (int i = 0; i < (int)strlen(buf); ++i) {
+        if (!isspace(buf[i])) return true;
+      }
+    }
+  }
+  return true;
+}
+
+void av1_init_subgop_config_set(SubGOPSetCfg *config_set) {
+  memset(config_set, 0, sizeof(*config_set));
+  config_set->num_configs = 0;
+  for (int i = 0; i < MAX_SUBGOP_CONFIGS; ++i) {
+    memset(&config_set->config[i], 0, sizeof(config_set->config[i]));
+  }
+}
+
+static void check_duplicate_and_add(SubGOPSetCfg *config_set) {
+  int k;
+  const int n = config_set->num_configs;
+  for (k = 0; k < n; ++k) {
+    if (config_set->config[k].num_frames == config_set->config[n].num_frames &&
+        config_set->config[k].subgop_in_gop_code ==
+            config_set->config[n].subgop_in_gop_code) {
+      memcpy(&config_set->config[k], &config_set->config[n],
+             sizeof(config_set->config[k]));
+      return;
+    }
+  }
+  if (k == n) config_set->num_configs++;
+}
+
+static int process_subgop_step(char *str, SubGOPStepCfg *step) {
+  char *ptr;
+  step->num_references = 0;
+  step->disp_frame_idx = (int8_t)strtol(str, &ptr, 10);
+  // Check if no numeric disp idx exist or is negative
+  if (ptr == str || step->disp_frame_idx < 0) return 0;
+  switch (*ptr) {
+    case 'V': step->type_code = FRAME_TYPE_INO_VISIBLE; break;
+    case 'R': step->type_code = FRAME_TYPE_INO_REPEAT; break;
+    case 'S': step->type_code = FRAME_TYPE_INO_SHOWEXISTING; return 1;
+    case 'F': step->type_code = FRAME_TYPE_OOO_FILTERED; break;
+    case 'U': step->type_code = FRAME_TYPE_OOO_UNFILTERED; break;
+    default: return 0;
+  }
+  str = ++ptr;
+  step->pyr_level = (int8_t)strtol(str, &ptr, 10);
+  // Check if no numeric disp idx exist
+  if (ptr == str) return 0;
+
+  // Check for unspecified references
+  if (*ptr == 0) {
+    step->num_references = -1;
+    return 1;
+  }
+  // Check for character P preceding the references
+  if (*ptr != 'P') return 0;
+  str = ++ptr;
+  char delim[] = "^";
+  char *token;
+  while ((token = my_strtok_r(str, delim, &str)) != NULL) {
+    if (step->num_references >= INTER_REFS_PER_FRAME) return 0;
+    step->references[step->num_references] = (int8_t)strtol(token, NULL, 10);
+    step->num_references++;
+  }
+  return 1;
+}
+
+static int process_subgop_steps(char *str, SubGOPCfg *config) {
+  char delim[] = "/";
+  config->num_steps = 0;
+  char *token;
+  while ((token = my_strtok_r(str, delim, &str)) != NULL) {
+    int res = process_subgop_step(token, &config->step[config->num_steps]);
+    if (!res) return 0;
+    // Populate pyr level for show existing frame
+    if (config->step[config->num_steps].type_code ==
+        FRAME_TYPE_INO_SHOWEXISTING) {
+      int k;
+      for (k = 0; k < config->num_steps; ++k) {
+        if (config->step[k].disp_frame_idx ==
+            config->step[config->num_steps].disp_frame_idx) {
+          config->step[config->num_steps].pyr_level = config->step[k].pyr_level;
+          break;
+        }
+      }
+      // showexisting for a frame not coded before is invalid
+      if (k == config->num_steps) return 0;
+    }
+    config->num_steps++;
+  }
+  return 1;
+}
+
+static int process_subgop_config(char *str, SubGOPCfg *config) {
+  char delim[] = ":";
+  char *token = my_strtok_r(str, delim, &str);
+  if (!token) return 0;
+  config->num_frames = atoi(token);
+  token = my_strtok_r(str, delim, &str);
+  if (!token) return 0;
+  int subgop_in_gop_code = atoi(token);
+  // check for invalid subgop_in_gop_code
+  if (subgop_in_gop_code < 0 || subgop_in_gop_code >= SUBGOP_IN_GOP_CODES)
+    return 0;
+  config->subgop_in_gop_code = (SUBGOP_IN_GOP_CODE)subgop_in_gop_code;
+  token = my_strtok_r(str, delim, &str);
+  if (!token) return 0;
+  return process_subgop_steps(token, config);
+}
+
+static int is_visible(FRAME_TYPE_CODE code) {
+  switch (code) {
+    case FRAME_TYPE_INO_VISIBLE:
+    case FRAME_TYPE_INO_REPEAT:
+    case FRAME_TYPE_INO_SHOWEXISTING: return 1;
+    case FRAME_TYPE_OOO_FILTERED:
+    case FRAME_TYPE_OOO_UNFILTERED: return 0;
+    default: assert(0 && "Invalid frame type code"); return 0;
+  }
+}
+
+static int check_subgop_config(SubGOPCfg *config) {
+  // check for invalid disp_frame_idx
+  for (int s = 0; s < config->num_steps; ++s) {
+    if (config->step[s].disp_frame_idx > config->num_frames) return 0;
+  }
+
+  // Each disp frame index must be shown exactly once
+  int visible[MAX_SUBGOP_LENGTH];
+  memset(visible, 0, config->num_frames * sizeof(*visible));
+  for (int s = 0; s < config->num_steps; ++s) {
+    if (is_visible(config->step[s].type_code))
+      visible[config->step[s].disp_frame_idx - 1]++;
+  }
+  for (int k = 0; k < config->num_frames; ++k) {
+    if (visible[k] != 1) return 0;
+  }
+
+  // Each disp frame index must have at most one invisible frame
+  int invisible[MAX_SUBGOP_LENGTH];
+  memset(invisible, 0, config->num_frames * sizeof(*invisible));
+  for (int s = 0; s < config->num_steps; ++s) {
+    if (!is_visible(config->step[s].type_code))
+      invisible[config->step[s].disp_frame_idx - 1]++;
+  }
+  for (int k = 0; k < config->num_frames; ++k) {
+    if (invisible[k] > 1) return 0;
+  }
+
+  // Check for a single level 1 frame in the subgop
+  int level[MAX_SUBGOP_LENGTH];
+  memset(level, 0, config->num_frames * sizeof(*level));
+  for (int s = 0; s < config->num_steps; ++s) {
+    if (is_visible(config->step[s].type_code))
+      level[config->step[s].disp_frame_idx - 1] = config->step[s].pyr_level;
+  }
+  int num_level1 = 0;
+  for (int k = 0; k < config->num_frames; ++k) {
+    num_level1 += (level[k] == 1);
+  }
+  if (num_level1 != 1) return 0;
+  return 1;
+}
+
+int av1_process_subgop_config_set(const char *param, SubGOPSetCfg *config_set) {
+  if (!param) return 1;
+  if (!strlen(param)) return 1;
+  const int bufsize = (int)((strlen(param) + 1) * sizeof(*param));
+  char *buf = (char *)aom_malloc(bufsize);
+  memcpy(buf, param, bufsize);
+  char delim[] = ",";
+
+  char *str = buf;
+  char *token;
+  while ((token = my_strtok_r(str, delim, &str)) != NULL) {
+    int res = process_subgop_config(
+        token, &config_set->config[config_set->num_configs]);
+    if (res) {
+      res = check_subgop_config(&config_set->config[config_set->num_configs]);
+      if (res) {
+        check_duplicate_and_add(config_set);
+      } else {
+        printf(
+            "Warning: Subgop config validation failed for config #%d, "
+            "skipping the rest.\n",
+            config_set->num_configs);
+        return 0;
+      }
+    } else {
+      printf(
+          "Warning: Subgop config parsing failed for config #%d, "
+          "skipping the rest.\n",
+          config_set->num_configs);
+      return 0;
+    }
+  }
+  aom_free(buf);
+  return 1;
+}
+
+static int process_subgop_config_fromfile(FILE *fp, SubGOPCfg *config) {
+  char line[256];
+  int linesize = 256;
+  int s;
+  if (!readline(line, linesize, fp)) return 0;
+  char *token;
+  char *str = line;
+  token = read_token_after(str, "num_frames:", &str);
+  if (!token) return 0;
+  if (strlen(token) == 0) return 0;
+  const int num_frames = atoi(token);
+  if (num_frames <= 0) return 0;
+  config->num_frames = num_frames;
+
+  token = read_token_after(str, "subgop_in_gop_code:", &str);
+  if (!token) return 0;
+  if (strlen(token) == 0) return 0;
+  const int subgop_in_gop_code = atoi(token);
+  // check for invalid subgop_in_gop_code
+  if (subgop_in_gop_code < 0 || subgop_in_gop_code >= SUBGOP_IN_GOP_CODES)
+    return 0;
+  config->subgop_in_gop_code = (SUBGOP_IN_GOP_CODE)subgop_in_gop_code;
+
+  token = read_token_after(str, "num_steps:", &str);
+  if (!token) return 0;
+  if (strlen(token) == 0) return 0;
+  config->num_steps = atoi(token);
+  for (s = 0; s < config->num_steps; ++s) {
+    SubGOPStepCfg *step = &config->step[s];
+    step->num_references = 0;
+    if (!readline(line, linesize, fp)) return 0;
+    str = line;
+
+    token = read_token_after(str, "disp_frame_idx:", &str);
+    if (!token) return 0;
+    if (strlen(token) == 0) return 0;
+    const int disp_frame_idx = atoi(token);
+    if (disp_frame_idx < 0 || disp_frame_idx > config->num_frames) return 0;
+    step->disp_frame_idx = disp_frame_idx;
+    token = read_token_after(str, "type_code:", &str);
+    if (!token) return 0;
+    if (strlen(token) != 1) return 0;
+    switch (*token) {
+      case 'V': step->type_code = FRAME_TYPE_INO_VISIBLE; break;
+      case 'R': step->type_code = FRAME_TYPE_INO_REPEAT; break;
+      case 'S': step->type_code = FRAME_TYPE_INO_SHOWEXISTING; break;
+      case 'F': step->type_code = FRAME_TYPE_OOO_FILTERED; break;
+      case 'U': step->type_code = FRAME_TYPE_OOO_UNFILTERED; break;
+      default: return 0;
+    }
+    if (step->type_code == FRAME_TYPE_INO_SHOWEXISTING) {
+      int k;
+      for (k = 0; k < s; ++k) {
+        if (config->step[k].disp_frame_idx == step->disp_frame_idx) {
+          step->pyr_level = config->step[k].pyr_level;
+          break;
+        }
+      }
+      // showexisting for a frame not coded before is invalid
+      if (k == s) return 0;
+      continue;
+    }
+    token = read_token_after(str, "pyr_level:", &str);
+    if (!token) return 0;
+    if (strlen(token) == 0) return 0;
+    const int pyr_level = atoi(token);
+    if (pyr_level <= 0) return 0;
+    step->pyr_level = pyr_level;
+
+    token = read_token_after(str, "references:", &str);
+    if (!token) {  // no references specified
+      step->num_references = -1;
+      continue;
+    }
+    if (strlen(token) == 0) continue;  // no references
+
+    char delim[] = "^";
+    str = token;
+    while ((token = my_strtok_r(str, delim, &str)) != NULL) {
+      if (step->num_references >= INTER_REFS_PER_FRAME) return 0;
+      step->references[step->num_references] = (int8_t)strtol(token, NULL, 10);
+      step->num_references++;
+    }
+  }
+  return 1;
+}
+
+int av1_process_subgop_config_set_fromfile(const char *paramfile,
+                                           SubGOPSetCfg *config_set) {
+  if (!paramfile) return 1;
+  if (!strlen(paramfile)) return 1;
+  FILE *fp = fopen(paramfile, "r");
+  if (!fp) return 0;
+  char line[256];
+  int linesize = 256;
+  char *tok, *str;
+  if (readline(line, linesize, fp)) {
+    tok = read_token_after(line, "[num_configs:", &str);
+    if (!tok) rewind(fp);
+  } else {
+    // Blank file
+    return 1;
+  }
+  while (readline(line, linesize, fp)) {
+    str = line;
+    tok = read_token_after(str, "config:", &str);
+    if (tok) {
+      int res = process_subgop_config_fromfile(
+          fp, &config_set->config[config_set->num_configs]);
+      if (res) {
+        res = check_subgop_config(&config_set->config[config_set->num_configs]);
+        if (res) {
+          check_duplicate_and_add(config_set);
+        } else {
+          printf(
+              "Warning: Subgop config validation failed for config #%d, "
+              "skipping the rest.\n",
+              config_set->num_configs);
+          fclose(fp);
+          return 0;
+        }
+      } else {
+        printf("Warning: config parsing failed, skipping the rest.\n");
+        fclose(fp);
+        return 0;
+      }
+    } else {
+      printf("Warning: config not found, skipping the rest.\n");
+      fclose(fp);
+      return 0;
+    }
+  }
+  fclose(fp);
+  return 1;
+}
+
+void av1_print_subgop_config_set(SubGOPSetCfg *config_set) {
+  if (!config_set->num_configs) return;
+  printf("SUBGOP CONFIG SET\n");
+  printf("=================\n");
+  printf("[num_configs:%d]\n", config_set->num_configs);
+  for (int i = 0; i < config_set->num_configs; ++i) {
+    printf("config:[%d]\n", i);
+    SubGOPCfg *config = &config_set->config[i];
+    printf("  num_frames:%d", config->num_frames);
+    printf(" subgop_in_gop_code:%d", config->subgop_in_gop_code);
+    printf(" num_steps:%d\n", config->num_steps);
+    for (int j = 0; j < config->num_steps; ++j) {
+      printf("  [step:%d]", j);
+      printf(" disp_frame_idx:%d", config->step[j].disp_frame_idx);
+      printf(" type_code:%c", config->step[j].type_code);
+      printf(" pyr_level:%d", config->step[j].pyr_level);
+      if (config->step[j].type_code != FRAME_TYPE_INO_SHOWEXISTING &&
+          config->step[j].num_references >= 0) {
+        printf(" references:");
+        for (int r = 0; r < config->step[j].num_references; ++r) {
+          if (r) printf("^");
+          printf("%d", config->step[j].references[r]);
+        }
+      }
+      printf("\n");
+    }
+  }
+  printf("\n");
+}
+
+const SubGOPCfg *av1_find_subgop_config(SubGOPSetCfg *config_set,
+                                        int num_frames, int is_last_subgop,
+                                        int is_first_subgop) {
+  SubGOPCfg *cfg = NULL;
+  SUBGOP_IN_GOP_CODE subgop_in_gop_code;
+  if (is_last_subgop)
+    subgop_in_gop_code = SUBGOP_IN_GOP_LAST;
+  else if (is_first_subgop)
+    subgop_in_gop_code = SUBGOP_IN_GOP_FIRST;
+  else
+    subgop_in_gop_code = SUBGOP_IN_GOP_GENERIC;
+  for (int i = 0; i < config_set->num_configs; ++i) {
+    if (config_set->config[i].num_frames == num_frames) {
+      if (config_set->config[i].subgop_in_gop_code == subgop_in_gop_code)
+        return &config_set->config[i];
+      else if (config_set->config[i].subgop_in_gop_code ==
+               SUBGOP_IN_GOP_GENERIC)
+        cfg = &config_set->config[i];
+    }
+  }
+  return cfg;
+}
diff --git a/av1/encoder/subgop.h b/av1/encoder/subgop.h
new file mode 100644
index 0000000..464af04
--- /dev/null
+++ b/av1/encoder/subgop.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_AV1_ENCODER_SUBGOP_H_
+
+#include "av1/encoder/encoder.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void av1_init_subgop_config_set(SubGOPSetCfg *config_set);
+int av1_process_subgop_config_set(const char *param, SubGOPSetCfg *config_set);
+void av1_print_subgop_config_set(SubGOPSetCfg *config_set);
+int av1_process_subgop_config_set_fromfile(const char *paramfile,
+                                           SubGOPSetCfg *config_set);
+
+// Finds the ptr to the subgop config with the queried number of
+// frames and whether it is the last or first subgop in a gop.
+// If the right number of frames is found but the right
+// subgop_in_gop_code is not found then the generic config with the
+// matching length is returned. If the right number of frames
+// is not found, Null is returned.
+const SubGOPCfg *av1_find_subgop_config(SubGOPSetCfg *config_set,
+                                        int num_frames, int is_last_gop,
+                                        int is_first_subgop);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // AOM_AV1_ENCODER_SUBGOP_H_
diff --git a/av1/encoder/svc_layercontext.c b/av1/encoder/svc_layercontext.c
index c43edf9..087d395 100644
--- a/av1/encoder/svc_layercontext.c
+++ b/av1/encoder/svc_layercontext.c
@@ -45,8 +45,8 @@
       lrc->ni_frames = 0;
       lrc->decimation_count = 0;
       lrc->decimation_factor = 0;
-      lrc->worst_quality = av1_quantizer_to_qindex(lc->max_q);
-      lrc->best_quality = av1_quantizer_to_qindex(lc->min_q);
+      lrc->worst_quality = lc->max_qp;
+      lrc->best_quality = lc->min_qp;
       for (int i = 0; i < RATE_FACTOR_LEVELS; ++i) {
         lrc->rate_correction_factors[i] = 1.0;
       }
@@ -121,8 +121,8 @@
       lc->framerate = cpi->framerate / lc->framerate_factor;
       lrc->avg_frame_bandwidth = (int)(lc->target_bandwidth / lc->framerate);
       lrc->max_frame_bandwidth = rc->max_frame_bandwidth;
-      lrc->worst_quality = av1_quantizer_to_qindex(lc->max_q);
-      lrc->best_quality = av1_quantizer_to_qindex(lc->min_q);
+      lrc->worst_quality = lc->max_qp;
+      lrc->best_quality = lc->min_qp;
     }
   }
 }
diff --git a/av1/encoder/svc_layercontext.h b/av1/encoder/svc_layercontext.h
index 3c7ae67..a34843e 100644
--- a/av1/encoder/svc_layercontext.h
+++ b/av1/encoder/svc_layercontext.h
@@ -34,8 +34,8 @@
   int64_t spatial_layer_target_bandwidth;
   double framerate;
   int avg_frame_size;
-  int max_q;
-  int min_q;
+  int max_qp;
+  int min_qp;
   int frames_from_key_frame;
   /*!\endcond */
 
diff --git a/av1/encoder/temporal_filter.c b/av1/encoder/temporal_filter.c
index 751f4e6..2da95a7 100644
--- a/av1/encoder/temporal_filter.c
+++ b/av1/encoder/temporal_filter.c
@@ -1073,7 +1073,8 @@
   // Filter one more ARF if the lookahead index is leq 7 (w.r.t. 9-th frame).
   // This frame is ALWAYS a show existing frame.
   const int is_second_arf =
-      (update_type == INTNL_ARF_UPDATE) && (filter_frame_lookahead_idx >= 7);
+      (update_type == INTNL_ARF_UPDATE) &&
+      (filter_frame_lookahead_idx >= (cpi->gf_group.is_user_specified ? 5 : 7));
   // TODO(anyone): Currently, we enforce the filtering strength on internal
   // ARFs except the second ARF to be zero. We should investigate in which case
   // it is more beneficial to use non-zero strength filtering.
diff --git a/av1/encoder/tpl_model.c b/av1/encoder/tpl_model.c
index 6a828a8..d5f1f4e 100644
--- a/av1/encoder/tpl_model.c
+++ b/av1/encoder/tpl_model.c
@@ -1007,11 +1007,11 @@
 
     av1_get_ref_frames(cpi, &ref_buffer_stack);
     int refresh_mask = av1_get_refresh_frame_flags(
-        cpi, &frame_params, frame_update_type, &ref_buffer_stack);
+        cpi, &frame_params, frame_update_type, gf_index, &ref_buffer_stack);
 
     int refresh_frame_map_index = av1_get_refresh_ref_frame_map(refresh_mask);
     av1_update_ref_frame_map(cpi, frame_update_type, frame_params.frame_type,
-                             frame_params.show_existing_frame,
+                             gf_index, frame_params.show_existing_frame,
                              refresh_frame_map_index, &ref_buffer_stack);
 
     for (int i = LAST_FRAME; i <= ALTREF_FRAME; ++i)
@@ -1067,11 +1067,15 @@
     gf_group->q_val[gf_index] = *pframe_qindex;
 
     av1_get_ref_frames(cpi, &ref_buffer_stack);
+    // TODO(sarahparker) av1_get_refresh_frame_flags() and
+    // av1_update_ref_frame_map() will execute default behavior even when
+    // subgop cfg is enabled. This should be addressed if we ever remove the
+    // frame_update_type.
     int refresh_mask = av1_get_refresh_frame_flags(
-        cpi, &frame_params, frame_update_type, &ref_buffer_stack);
+        cpi, &frame_params, frame_update_type, -1, &ref_buffer_stack);
     int refresh_frame_map_index = av1_get_refresh_ref_frame_map(refresh_mask);
     av1_update_ref_frame_map(cpi, frame_update_type, frame_params.frame_type,
-                             frame_params.show_existing_frame,
+                             -1, frame_params.show_existing_frame,
                              refresh_frame_map_index, &ref_buffer_stack);
 
     for (int i = LAST_FRAME; i <= ALTREF_FRAME; ++i)
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake
index 2497ff1..decd8bc 100644
--- a/build/cmake/aom_config_defaults.cmake
+++ b/build/cmake/aom_config_defaults.cmake
@@ -82,6 +82,7 @@
 set_aom_config_var(CONFIG_RUNTIME_CPU_DETECT 1 "Runtime CPU detection support.")
 set_aom_config_var(CONFIG_SHARED 0 "Build shared libs.")
 set_aom_config_var(CONFIG_WEBM_IO 1 "Enables WebM support.")
+set_aom_config_var(CONFIG_SINGLEPASS 1 "Only allow single pass encoding.")
 
 # Debugging flags.
 set_aom_config_var(CONFIG_DEBUG 0 "Debug build flag.")
diff --git a/build/cmake/tensorflow_lite.cmake b/build/cmake/tensorflow_lite.cmake
new file mode 100644
index 0000000..9fca969
--- /dev/null
+++ b/build/cmake/tensorflow_lite.cmake
@@ -0,0 +1,97 @@
+#
+# Copyright (c) 2020, Alliance for Open Media. All rights reserved
+#
+# This source code is subject to the terms of the BSD 2 Clause License and the
+# Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License was
+# not distributed with this source code in the LICENSE file, you can obtain it
+# at www.aomedia.org/license/software. If the Alliance for Open Media Patent
+# License 1.0 was not distributed with this source code in the PATENTS file, you
+# can obtain it at www.aomedia.org/license/patent.
+#
+if(AOM_BUILD_CMAKE_TENSORFLOW_LITE_CMAKE_)
+  return()
+endif() # AOM_BUILD_CMAKE_TENSORFLOW_LITE_CMAKE_
+set(AOM_BUILD_CMAKE_TENSORFLOW_LITE_CMAKE_ 1)
+
+include(FindGit)
+
+# Checks if the dependencies on Tensorflow Lite are already checked out -- if
+# not, uses the git submodule command to fetch them.
+function(checkout_submodules_)
+  # As a quick sanity check, see if at least 1 expected file or directory is
+  # present in each submodule. If so, assume they are all checked out (if they
+  # are not, then the base directory will be empty).
+  if(
+    (EXISTS "${AOM_ROOT}/third_party/tensorflow/tensorflow")
+    AND (EXISTS
+         "${AOM_ROOT}/third_party/tensorflow_dependencies/neon_2_sse/ReadMe.md")
+    AND (EXISTS "${AOM_ROOT}/third_party/tensorflow_aom/absl/absl")
+    AND (EXISTS "${AOM_ROOT}/third_party/tensorflow_aom/eigen/Eigen")
+    AND (EXISTS "${AOM_ROOT}/third_party/tensorflow_aom/farmhash/Makefile.am")
+    AND (EXISTS "${AOM_ROOT}/third_party/tensorflow_aom/flatbuffers/BUILD")
+    AND (EXISTS "${AOM_ROOT}/third_party/tensorflow_aom/fp16/CMakeLists.txt")
+    AND (EXISTS "${AOM_ROOT}/third_party/tensorflow_aom/gemmlowp/BUILD")
+    AND (EXISTS "${AOM_ROOT}/third_party/tensorflow_aom/ruy/BUILD"))
+    return()
+  endif()
+  if(NOT GIT_FOUND)
+    message(
+      FATAL_ERROR
+        "Tensorflow-Lite/dependencies not present; " "git could not be found; "
+        "please check out submodules with 'git submodule update --init'")
+  endif()
+  # Note that "git submodule update --init" must be run from inside the git
+  # repository; the --git-dir flag does not work.
+  message("Checking out Tensorflow-Lite submodule")
+  execute_process(COMMAND "${GIT_EXECUTABLE}" submodule update --init
+                  WORKING_DIRECTORY "${AOM_ROOT}"
+                  OUTPUT_VARIABLE submodule_out
+                  ERROR_VARIABLE submodule_err
+                  RESULT_VARIABLE submodule_result)
+  if(NOT ${submodule_result} EQUAL 0)
+    message(FATAL_ERROR "Unable to run 'git submodule update --init': "
+                        "Return code: " ${submodule_result} ", STDOUT: "
+                        ${submodule_out} ", STDERR: " ${submodule_err})
+  endif()
+endfunction()
+
+function(add_tensorflow_lite_dependency_)
+  if(NOT AOM_APP_TARGETS)
+    message(FATAL_ERROR "AOM_APP_TARGETS variable must not be empty.")
+  endif()
+  # Build the library.
+  add_custom_command(
+    OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libtensorflow-lite.a"
+    COMMAND "${AOM_ROOT}/third_party/tensorflow_dependencies/build.pl"
+            "${AOM_ROOT}" "${CMAKE_CURRENT_BINARY_DIR}/libtensorflow-lite.a"
+    WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
+  add_custom_target(tensorflowlite_a ALL
+                    DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/libtensorflow-lite.a")
+  include_directories("${AOM_ROOT}/third_party/tensorflow")
+  include_directories(
+    "${AOM_ROOT}/third_party/tensorflow_dependencies/flatbuffers/include/")
+  # Add tensorflow-lite as a dependency on all AOM applications.
+  foreach(aom_app ${AOM_APP_TARGETS})
+    add_dependencies(${aom_app} tensorflowlite_a)
+    target_link_libraries(
+      ${aom_app}
+      PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/libtensorflow-lite.a"
+              ${AOM_LIB_LINK_TYPE} Threads::Threads
+      PRIVATE ${CMAKE_DL_LIBS})
+  endforeach()
+endfunction()
+
+# If Tensorflow-Lite should be enabled, adds appropriate build rules / targets.
+function(setup_tensorflow_lite)
+  if("${AOM_ROOT}" STREQUAL "")
+    message(FATAL_ERROR "AOM_ROOT variable must not be empty.")
+  endif()
+  # Cross-compile is not currently implemented.
+  if(CMAKE_TOOLCHAIN_FILE)
+    message("TOOLCHAIN: ${CMAKE_TOOLCHAIN_FILE}")
+    message(WARNING "No cross-compile support for TensorFlow Lite; disabling")
+    set(CONFIG_TENSORFLOW_LITE 0)
+  endif()
+  checkout_submodules_()
+  add_tensorflow_lite_dependency_()
+endfunction()
diff --git a/common/args.c b/common/args.c
index ec2a863..2766a00 100644
--- a/common/args.c
+++ b/common/args.c
@@ -50,11 +50,11 @@
 }
 
 static const char kSbSizeWarningString[] =
-    "super_block_size has to be 64 or 128.";
+    "superblock_size has to be 64 or 128.";
 static const char kMinpartWarningString[] =
     "min_partition_size has to be smaller or equal to max_partition_size.";
 static const char kMaxpartWarningString[] =
-    "max_partition_size has to be smaller or equal to super_block_size.";
+    "max_partition_size has to be smaller or equal to superblock_size.";
 
 int parse_cfg(const char *file, cfg_options_t *config) {
   char line[1024 * 10];
@@ -86,46 +86,46 @@
     ignore_end_spaces(left);
     ignore_end_spaces(right);
 
-    GET_PARAMS(super_block_size);
+    GET_PARAMS(superblock_size);
     GET_PARAMS(max_partition_size);
     GET_PARAMS(min_partition_size);
-    GET_PARAMS(disable_ab_partition_type);
-    GET_PARAMS(disable_rect_partition_type);
-    GET_PARAMS(disable_1to4_partition_type);
-    GET_PARAMS(disable_flip_idtx);
-    GET_PARAMS(disable_cdef);
-    GET_PARAMS(disable_lr);
-    GET_PARAMS(disable_obmc);
-    GET_PARAMS(disable_warp_motion);
-    GET_PARAMS(disable_global_motion);
-    GET_PARAMS(disable_dist_wtd_comp);
-    GET_PARAMS(disable_diff_wtd_comp);
-    GET_PARAMS(disable_inter_intra_comp);
-    GET_PARAMS(disable_masked_comp);
-    GET_PARAMS(disable_one_sided_comp);
-    GET_PARAMS(disable_palette);
-    GET_PARAMS(disable_intrabc);
-    GET_PARAMS(disable_cfl);
-    GET_PARAMS(disable_smooth_intra);
-    GET_PARAMS(disable_filter_intra);
-    GET_PARAMS(disable_dual_filter);
-    GET_PARAMS(disable_intra_angle_delta);
-    GET_PARAMS(disable_intra_edge_filter);
-    GET_PARAMS(disable_tx_64x64);
-    GET_PARAMS(disable_smooth_inter_intra);
-    GET_PARAMS(disable_inter_inter_wedge);
-    GET_PARAMS(disable_inter_intra_wedge);
-    GET_PARAMS(disable_paeth_intra);
+    GET_PARAMS(enable_ab_partitions);
+    GET_PARAMS(enable_rect_partitions);
+    GET_PARAMS(enable_1to4_partitions);
+    GET_PARAMS(enable_flip_idtx);
+    GET_PARAMS(enable_cdef);
+    GET_PARAMS(enable_restoration);
+    GET_PARAMS(enable_obmc);
+    GET_PARAMS(enable_warped_motion);
+    GET_PARAMS(enable_global_motion);
+    GET_PARAMS(enable_dist_wtd_comp);
+    GET_PARAMS(enable_diff_wtd_comp);
+    GET_PARAMS(enable_interintra_comp);
+    GET_PARAMS(enable_masked_comp);
+    GET_PARAMS(enable_onesided_comp);
+    GET_PARAMS(enable_palette);
+    GET_PARAMS(enable_intrabc);
+    GET_PARAMS(enable_cfl_intra);
+    GET_PARAMS(enable_smooth_intra);
+    GET_PARAMS(enable_filter_intra);
+    GET_PARAMS(enable_dual_filter);
+    GET_PARAMS(enable_angle_delta);
+    GET_PARAMS(enable_intra_edge_filter);
+    GET_PARAMS(enable_tx64);
+    GET_PARAMS(enable_smooth_interintra);
+    GET_PARAMS(enable_interinter_wedge);
+    GET_PARAMS(enable_interintra_wedge);
+    GET_PARAMS(enable_paeth_intra);
     GET_PARAMS(disable_trellis_quant);
-    GET_PARAMS(disable_ref_frame_mv);
-    GET_PARAMS(reduced_reference_set);
+    GET_PARAMS(enable_ref_frame_mvs);
+    GET_PARAMS(enable_reduced_reference_set);
     GET_PARAMS(reduced_tx_type_set);
 
     fprintf(stderr, "\nInvalid parameter: %s", left);
     exit(-1);
   }
 
-  if (config->super_block_size != 128 && config->super_block_size != 64) {
+  if (config->superblock_size != 128 && config->superblock_size != 64) {
     fprintf(stderr, "\n%s", kSbSizeWarningString);
     exit(-1);
   }
@@ -133,7 +133,7 @@
     fprintf(stderr, "\n%s", kMinpartWarningString);
     exit(-1);
   }
-  if (config->max_partition_size > config->super_block_size) {
+  if (config->max_partition_size > config->superblock_size) {
     fprintf(stderr, "\n%s", kMaxpartWarningString);
     exit(-1);
   }
diff --git a/common/stream_iter.c b/common/stream_iter.c
new file mode 100644
index 0000000..bf523d8
--- /dev/null
+++ b/common/stream_iter.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "common/stream_iter.h"
+#include "common/y4minput.h"
+
+static int copy_reader(StreamIter *iter, aom_image_t *img) {
+  struct AvxInputContext *input_ctx = iter->input.avx;
+  FILE *f = input_ctx->file;
+  y4m_input *y4m = &input_ctx->y4m;
+  int shortread = 0;
+
+  if (input_ctx->file_type == FILE_TYPE_Y4M) {
+    if (y4m_input_fetch_frame(y4m, f, img) < 1) return 0;
+  } else {
+    shortread = read_yuv_frame(input_ctx, img);
+  }
+  return !shortread;
+}
+
+void copy_stream_iter_init(StreamIter *iter, struct AvxInputContext *input) {
+  iter->input.avx = input;
+  iter->reader = copy_reader;
+}
+
+static int skip_reader(StreamIter *iter, aom_image_t *raw) {
+  // While we haven't skipped enough frames, read from the underlying
+  // stream and throw the result away. If no frame is available, early
+  // exit.
+  while (iter->current < iter->n) {
+    ++iter->current;
+    int frame_avail = read_stream_iter(iter->input.stream, raw);
+    if (!frame_avail) {
+      return frame_avail;
+    }
+  }
+  // If we're past the skip region, copy the remaining output.
+  return read_stream_iter(iter->input.stream, raw);
+}
+
+void skip_stream_iter_init(StreamIter *iter, StreamIter *input, int num_skip) {
+  assert(num_skip >= 0);
+  iter->input.stream = input;
+  iter->current = 0;
+  iter->n = num_skip;
+  iter->reader = skip_reader;
+}
+
+static int step_reader(StreamIter *iter, aom_image_t *raw) {
+  while (true) {
+    int frame_avail = read_stream_iter(iter->input.stream, raw);
+    // If at end of stream, no need to read further.
+    if (!frame_avail) {
+      return frame_avail;
+    }
+
+    bool should_encode = iter->current == 0;
+    iter->current = (iter->current + 1) % (iter->n);
+    if (should_encode) {
+      return frame_avail;
+    }
+  }
+}
+
+void step_stream_iter_init(StreamIter *iter, StreamIter *input, int step_size) {
+  assert(step_size > 0);
+  iter->input.stream = input;
+  iter->current = 0;
+  iter->n = step_size;
+  iter->reader = step_reader;
+}
+
+static int limit_reader(StreamIter *iter, aom_image_t *raw) {
+  // limit of 0 is a special case meaning "no limit".
+  if (iter->n != 0 && iter->current >= iter->n) {
+    return 0;
+  }
+  ++iter->current;
+  return read_stream_iter(iter->input.stream, raw);
+}
+
+void limit_stream_iter_init(StreamIter *iter, StreamIter *input, int limit) {
+  assert(limit >= 0);
+  iter->input.stream = input;
+  iter->current = 0;
+  iter->n = limit;
+  iter->reader = limit_reader;
+}
+
+int read_stream_iter(StreamIter *iter, aom_image_t *raw) {
+  return iter->reader(iter, raw);
+}
diff --git a/common/stream_iter.h b/common/stream_iter.h
new file mode 100644
index 0000000..af98a2a
--- /dev/null
+++ b/common/stream_iter.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+#ifndef AOM_COMMON_STREAM_ITER_H_
+#define AOM_COMMON_STREAM_ITER_H_
+
+#include "aom/aom_image.h"
+#include "common/tools_common.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct StreamIter;
+
+// The input to each stream iterator can either be an AvxInputContext (when
+// reading from a file or a pipe like STDIN) or another stream iterator.
+union StreamInput {
+  struct AvxInputContext *avx;
+  struct StreamIter *stream;
+};
+
+typedef struct StreamIter {
+  union StreamInput input;
+  // For simplicity, all streams have two additional numbers associated
+  // with them, e.g., for counting the number of frames returned and the
+  // number of frames that should be skipped. Some iterators, like the copy
+  // iterator, do not use either field.
+  int current;
+  int n;
+  // Pointer to the function that performs the read. Returns whether a frame
+  // is available. If a frame is returned, it is written into *raw.
+  int (*reader)(struct StreamIter *iter, aom_image_t *raw);
+} StreamIter;
+
+// Iterator that simply copies the data from the AvxInputContext.
+void copy_stream_iter_init(StreamIter *iter, struct AvxInputContext *input);
+
+// Iterator that skips the first N frames. Takes another stream as input.
+void skip_stream_iter_init(StreamIter *iter, StreamIter *input, int num_skip);
+
+// Iterator that only returns every N-th frame. Takes another stream as input.
+void step_stream_iter_init(StreamIter *iter, StreamIter *input, int step_size);
+
+// Iterator that stops returning frames after the N-th. Takes another stream
+// as input.
+void limit_stream_iter_init(StreamIter *iter, StreamIter *input, int limit);
+
+// Invokes the iterator's specialized read function to read data from the
+// stream. Returns if a frame was read. If so, writes the data into *raw.
+int read_stream_iter(StreamIter *iter, aom_image_t *raw);
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // AOM_COMMON_STREAM_ITER_H_
diff --git a/common/tf_lite_includes.h b/common/tf_lite_includes.h
new file mode 100644
index 0000000..e28adbe
--- /dev/null
+++ b/common/tf_lite_includes.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_COMMON_TF_LITE_INCLUDES_H_
+#define AOM_COMMON_TF_LITE_INCLUDES_H_
+
+// TensorFlow Lite has several unused parameters that are
+// exposed as part of the API. In the AOM build process, this
+// will cause failures when -Wunused-parameter is set.
+// Since TF Lite is external code, instruct the compiler to
+// ignore this warning when including it.
+// Note that Clang supports this GCC pragma.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+#include "tensorflow/lite/kernels/builtin_op_kernels.h"
+#include "tensorflow/lite/interpreter.h"
+#include "tensorflow/lite/model.h"
+#include "tensorflow/lite/op_resolver.h"
+
+#pragma GCC diagnostic pop
+
+#endif  // AOM_COMMON_TF_LITE_INCLUDES_H_
diff --git a/common/warnings.c b/common/warnings.c
index 2facee2..efe7ae2 100644
--- a/common/warnings.c
+++ b/common/warnings.c
@@ -67,10 +67,10 @@
   return c == 'y';
 }
 
-static void check_quantizer(int min_q, int max_q,
+static void check_quantizer(int min_qp, int max_qp,
                             struct WarningList *warning_list) {
-  const int lossless = min_q == 0 && max_q == 0;
-  if (!lossless && (min_q == max_q || abs(max_q - min_q) < 8))
+  const int lossless = min_qp == 0 && max_qp == 0;
+  if (!lossless && (min_qp == max_qp || abs(max_qp - min_qp) < 32))
     add_warning(quantizer_warning_string, warning_list);
 }
 
diff --git a/doc/dev_guide/av1_encoder.dox b/doc/dev_guide/av1_encoder.dox
index 7ddd18d..3f85745 100644
--- a/doc/dev_guide/av1_encoder.dox
+++ b/doc/dev_guide/av1_encoder.dox
@@ -131,7 +131,7 @@
     - \ref RateControlCfg.target_bandwidth
     - \ref RateControlCfg.best_allowed_q
     - \ref RateControlCfg.worst_allowed_q
-    - \ref RateControlCfg.cq_level
+    - \ref RateControlCfg.qp
     - \ref RateControlCfg.under_shoot_pct
     - \ref RateControlCfg.over_shoot_pct
     - \ref RateControlCfg.maximum_buffer_size_ms
@@ -531,9 +531,9 @@
 control configuration data structures.
 
 - <b>--target-bitrate</b> (\ref RateControlCfg.target_bandwidth)
-- <b>--min-q</b> (\ref RateControlCfg.best_allowed_q)
-- <b>--max-q</b> (\ref RateControlCfg.worst_allowed_q)
-- <b>--cq-level</b> (\ref RateControlCfg.cq_level)
+- <b>--min-qp</b> (\ref RateControlCfg.best_allowed_q)
+- <b>--max-qp</b> (\ref RateControlCfg.worst_allowed_q)
+- <b>--qp</b> (\ref RateControlCfg.qp)
 - <b>--undershoot-pct</b> (\ref RateControlCfg.under_shoot_pct)
 - <b>--overshoot-pct</b> (\ref RateControlCfg.over_shoot_pct)
 
diff --git a/examples/lightfield_encoder.c b/examples/lightfield_encoder.c
index d24aabd..073f97e 100644
--- a/examples/lightfield_encoder.c
+++ b/examples/lightfield_encoder.c
@@ -64,6 +64,7 @@
   return image_size_bytes;
 }
 
+#if !CONFIG_SINGLEPASS
 static int get_frame_stats(aom_codec_ctx_t *ctx, const aom_image_t *img,
                            aom_codec_pts_t pts, unsigned int duration,
                            aom_enc_frame_flags_t flags,
@@ -88,6 +89,7 @@
 
   return got_pkts;
 }
+#endif  // !CONFIG_SINGLEPASS
 
 static int encode_frame(aom_codec_ctx_t *ctx, const aom_image_t *img,
                         aom_codec_pts_t pts, unsigned int duration,
@@ -127,6 +129,7 @@
   }
 }
 
+#if !CONFIG_SINGLEPASS
 static aom_fixed_buf_t pass0(aom_image_t *raw, FILE *infile,
                              aom_codec_iface_t *encoder,
                              const aom_codec_enc_cfg_t *cfg, int lf_width,
@@ -229,6 +232,7 @@
 
   return stats;
 }
+#endif  // !CONFIG_SINGLEPASS
 
 static void pass1(aom_image_t *raw, FILE *infile, const char *outfile_name,
                   aom_codec_iface_t *encoder, aom_codec_enc_cfg_t *cfg,
@@ -353,7 +357,7 @@
     die_codec(&codec, "Failed to configure encoder");
 
   // The fixed q value used in encoding.
-  if (aom_codec_control(&codec, AOME_SET_CQ_LEVEL, 36))
+  if (aom_codec_control(&codec, AOME_SET_QP, 36))
     die_codec(&codec, "Failed to set cq level");
   if (aom_codec_control(&codec, AV1E_SET_FRAME_PARALLEL_DECODING, 1))
     die_codec(&codec, "Failed to set frame parallel decoding");
@@ -435,7 +439,9 @@
   aom_image_t raw;
   aom_image_t raw_shift;
   aom_codec_err_t res;
+#if !CONFIG_SINGLEPASS
   aom_fixed_buf_t stats;
+#endif  // !CONFIG_SINGLEPASS
   int flags = 0;
 
   const int fps = 30;
@@ -500,6 +506,12 @@
   if (!(infile = fopen(infile_arg, "rb")))
     die("Failed to open %s for reading", infile_arg);
 
+#if CONFIG_SINGLEPASS
+  // Pass 1
+  cfg.g_pass = AOM_RC_ONE_PASS;
+  pass1(&raw, infile, outfile_arg, encoder, &cfg, lf_width, lf_height,
+        lf_blocksize, flags, &raw_shift);
+#else
   // Pass 0
   cfg.g_pass = AOM_RC_FIRST_PASS;
   stats = pass0(&raw, infile, encoder, &cfg, lf_width, lf_height, lf_blocksize,
@@ -512,6 +524,7 @@
   pass1(&raw, infile, outfile_arg, encoder, &cfg, lf_width, lf_height,
         lf_blocksize, flags, &raw_shift);
   free(stats.buf);
+#endif  // CONFIG_SINGLEPASS
 
   if (FORCE_HIGHBITDEPTH_DECODING) aom_img_free(&raw_shift);
   aom_img_free(&raw);
diff --git a/examples/noise_model.c b/examples/noise_model.c
index d07443f..4ad94b4 100644
--- a/examples/noise_model.c
+++ b/examples/noise_model.c
@@ -34,7 +34,7 @@
  *
  * # Encode with your favorite settings (including the grain table):
  * aomenc --limit=100  --cpu-used=4 --input-bit-depth=8                  \
- *    --i420 -w 854 -h 480 --end-usage=q --cq-level=25 --lag-in-frames=25 \
+ *    --i420 -w 854 -h 480 --end-usage=q --qp=100 --lag-in-frames=25 \
  *    --auto-alt-ref=2 --bit-depth=8 --film-grain-table=film_grain.tbl \
  *    -o denoised_with_grain_params.ivf denoised.854_480.yuv
  */
diff --git a/examples/scalable_encoder.c b/examples/scalable_encoder.c
index 5bfd184..d09cbb7 100644
--- a/examples/scalable_encoder.c
+++ b/examples/scalable_encoder.c
@@ -242,7 +242,7 @@
       die_codec(&codec, "Failed to set enc cfg for layer 0");
     if (aom_codec_control(&codec, AOME_SET_SPATIAL_LAYER_ID, 0))
       die_codec(&codec, "Failed to set layer id to 0");
-    if (aom_codec_control(&codec, AOME_SET_CQ_LEVEL, 62))
+    if (aom_codec_control(&codec, AOME_SET_QP, 62))
       die_codec(&codec, "Failed to set cq level");
     encode_frame(&codec, &raw0, frame_count++, flags, outfile);
 
@@ -261,7 +261,7 @@
       die_codec(&codec, "Failed to set enc cfg for layer 1");
     if (aom_codec_control(&codec, AOME_SET_SPATIAL_LAYER_ID, 1))
       die_codec(&codec, "Failed to set layer id to 1");
-    if (aom_codec_control(&codec, AOME_SET_CQ_LEVEL, 10))
+    if (aom_codec_control(&codec, AOME_SET_QP, 10))
       die_codec(&codec, "Failed to set cq level");
     encode_frame(&codec, &raw1, frame_count++, flags, outfile);
 
diff --git a/examples/svc_encoder_rtc.c b/examples/svc_encoder_rtc.c
index 2b883cf..c098464 100644
--- a/examples/svc_encoder_rtc.c
+++ b/examples/svc_encoder_rtc.c
@@ -723,8 +723,8 @@
 
   cfg.rc_dropframe_thresh = (unsigned int)strtoul(argv[9], NULL, 0);
   cfg.rc_end_usage = AOM_CBR;
-  cfg.rc_min_quantizer = 2;
-  cfg.rc_max_quantizer = 52;
+  cfg.rc_min_quantizer = 8;
+  cfg.rc_max_quantizer = 208;
   cfg.rc_undershoot_pct = 50;
   cfg.rc_overshoot_pct = 50;
   cfg.rc_buf_initial_sz = 600;
diff --git a/examples/tf_lite_model.cc b/examples/tf_lite_model.cc
new file mode 100644
index 0000000..8474ed6
--- /dev/null
+++ b/examples/tf_lite_model.cc
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+/*!\file
+ * \brief This binary demonstrates how to build an executable with TensorFlow
+ * Lite. A library for aomenc/aomdec is almost identical in structure,
+ * except that "main" will not be present, and a header file will be added.
+ */
+
+#include <cstdio>
+#include <memory>
+
+#include "common/tf_lite_includes.h"
+
+namespace {
+
+/**
+ * Model data. Steps to create:
+ *
+ * 1. Build your model like normal.
+ * 2. Follow the steps at https://www.tensorflow.org/lite/convert/python_api
+ *    to convert into a TF-lite model. Optionally quantize the model to make
+ *    it smaller (see the Quantization section at
+ *    https://www.tensorflow.org/lite/guide/get_started)
+ * 3. Run `xxd -i model.tflite > model.cc` to make it a CC file.
+ * 4. Change the declaration to be const.
+ * 5. Either include directly or create .h/.cc files that expose the array.
+ */
+const unsigned char tf_lite_model_data[] = {
+  0x1c, 0x00, 0x00, 0x00, 0x54, 0x46, 0x4c, 0x33, 0x00, 0x00, 0x12, 0x00, 0x1c,
+  0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x18, 0x00, 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf4, 0x04, 0x00,
+  0x00, 0x4c, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xb0,
+  0xfb, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x13, 0x00,
+  0x00, 0x00, 0x6d, 0x69, 0x6e, 0x5f, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65,
+  0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x00, 0x0a, 0x00, 0x00, 0x00,
+  0xf4, 0x00, 0x00, 0x00, 0xdc, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x5c,
+  0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x2c, 0x00,
+  0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+  0x00, 0xba, 0xfe, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+  0x31, 0x2e, 0x35, 0x2e, 0x30, 0x00, 0x00, 0x00, 0xb0, 0xfb, 0xff, 0xff, 0xb4,
+  0xfb, 0xff, 0xff, 0xb8, 0xfb, 0xff, 0xff, 0xbc, 0xfb, 0xff, 0xff, 0xde, 0xfe,
+  0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x83, 0xad, 0x05,
+  0x3e, 0x0a, 0x72, 0x13, 0x3f, 0x83, 0xfb, 0x88, 0x3f, 0x2a, 0xf1, 0x8b, 0xbe,
+  0xfa, 0xfe, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x86,
+  0xc7, 0x46, 0x3f, 0x57, 0xb7, 0x35, 0xbf, 0x78, 0x2c, 0xeb, 0x3e, 0x25, 0xab,
+  0xae, 0xbe, 0x93, 0xbe, 0x6f, 0x3f, 0x44, 0x91, 0x8d, 0x3e, 0xdc, 0x92, 0xe7,
+  0x3e, 0x25, 0x67, 0x83, 0xbf, 0x2a, 0xc0, 0x7a, 0xbf, 0xf8, 0x46, 0x9d, 0x3f,
+  0x0b, 0x18, 0x6b, 0xbf, 0x0d, 0xe7, 0xc1, 0x3e, 0xb4, 0xd9, 0x15, 0xbf, 0x8d,
+  0x62, 0xa3, 0x3e, 0xb4, 0x5e, 0xab, 0xbb, 0xb8, 0x49, 0x3a, 0x3e, 0x46, 0xff,
+  0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x5a, 0xdc, 0x3d,
+  0x3d, 0xdd, 0x63, 0x17, 0xbe, 0x4a, 0x05, 0x1a, 0xbf, 0x27, 0xde, 0x3d, 0x3e,
+  0x74, 0xcd, 0xd7, 0x3e, 0x63, 0xff, 0x0c, 0x3f, 0xf7, 0xcc, 0x0d, 0x3f, 0x10,
+  0x30, 0x00, 0xbf, 0x72, 0xff, 0xff, 0xff, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00,
+  0x00, 0x00, 0x76, 0xf6, 0x0a, 0xbf, 0x76, 0xf6, 0x0a, 0x3f, 0x68, 0xfc, 0xff,
+  0xff, 0x0f, 0x00, 0x00, 0x00, 0x54, 0x4f, 0x43, 0x4f, 0x20, 0x43, 0x6f, 0x6e,
+  0x76, 0x65, 0x72, 0x74, 0x65, 0x64, 0x2e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10,
+  0x00, 0x00, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00,
+  0x10, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0xf8, 0x00, 0x00,
+  0x00, 0xec, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0xa4, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x0e, 0x00, 0x1a, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00, 0x07, 0x00,
+  0x14, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00,
+  0x00, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x80, 0x3f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x14, 0x00, 0x00,
+  0x00, 0x08, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x08, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04,
+  0x00, 0x00, 0x00, 0x24, 0xfd, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,
+  0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
+  0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x08, 0x24, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0c, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x08, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
+  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4c, 0x02, 0x00,
+  0x00, 0xe4, 0x01, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x3c, 0x01, 0x00, 0x00,
+  0xf4, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x04,
+  0x00, 0x00, 0x00, 0xe2, 0xfd, 0xff, 0xff, 0x38, 0x00, 0x00, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xd4, 0xfd, 0xff,
+  0xff, 0x1e, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69,
+  0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x31, 0x2f, 0x4d, 0x61,
+  0x74, 0x4d, 0x75, 0x6c, 0x5f, 0x62, 0x69, 0x61, 0x73, 0x00, 0x00, 0x01, 0x00,
+  0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x26, 0xfe, 0xff, 0xff, 0x4c, 0x00, 0x00,
+  0x00, 0x02, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x18, 0xfe, 0xff, 0xff, 0x32, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65,
+  0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x31,
+  0x2f, 0x4d, 0x61, 0x74, 0x4d, 0x75, 0x6c, 0x2f, 0x52, 0x65, 0x61, 0x64, 0x56,
+  0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4f, 0x70, 0x2f, 0x74, 0x72, 0x61,
+  0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
+  0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x82, 0xfe, 0xff, 0xff, 0x34, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+  0x00, 0x74, 0xfe, 0xff, 0xff, 0x1a, 0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75,
+  0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e, 0x73, 0x65, 0x5f,
+  0x31, 0x2f, 0x42, 0x69, 0x61, 0x73, 0x41, 0x64, 0x64, 0x00, 0x00, 0x02, 0x00,
+  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc6, 0xfe, 0xff,
+  0xff, 0x38, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0xb8, 0xfe, 0xff, 0xff, 0x1c, 0x00, 0x00, 0x00, 0x73,
+  0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f, 0x64, 0x65, 0x6e,
+  0x73, 0x65, 0x2f, 0x4d, 0x61, 0x74, 0x4d, 0x75, 0x6c, 0x5f, 0x62, 0x69, 0x61,
+  0x73, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x0a, 0xff, 0xff, 0xff, 0x4c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c,
+  0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xfc, 0xfe, 0xff, 0xff, 0x30, 0x00,
+  0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2f,
+  0x64, 0x65, 0x6e, 0x73, 0x65, 0x2f, 0x4d, 0x61, 0x74, 0x4d, 0x75, 0x6c, 0x2f,
+  0x52, 0x65, 0x61, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4f,
+  0x70, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x73, 0x65, 0x00, 0x00,
+  0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+  0x00, 0x66, 0xff, 0xff, 0xff, 0x34, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x58, 0xff, 0xff, 0xff, 0x1a,
+  0x00, 0x00, 0x00, 0x73, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c,
+  0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x52,
+  0x65, 0x6c, 0x75, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0xaa, 0xff, 0xff, 0xff, 0x44, 0x00, 0x00, 0x00, 0x07,
+  0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00,
+  0x0c, 0x00, 0x04, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
+  0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x43,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x64,
+  0x65, 0x6e, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x00, 0x02, 0x00,
+  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e,
+  0x00, 0x14, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x10, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x10,
+  0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x04, 0x00,
+  0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
+  0x79, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0c,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x05, 0x00, 0x06, 0x00,
+  0x00, 0x00, 0x00, 0x19, 0x0a, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08,
+  0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x00
+};
+
+/**
+ * These lines were generated by building the binary
+ * tensorflow/lite/tools/generate_op_registrations and running it with
+ * the TF-Lite model as input. In order to build that binary, follow
+ * these instructions:
+ *
+ * 1.) Check out a clean copy of tensorflow/ in a different directory. Pin to
+ *     commit 18445b0e39b677a21c86b4cf3d2bcb823f27e3e2. Make sure you are
+ *     in the root of the tensorflow/ directory.
+ * 2.) Apply the following diff to
+ *     tensorflow/lite/tools/gen_op_registration_main.cc:
+   <pre>
+153,155c153,155
+<   std::string input_models;
+<   std::string output_registration;
+<   std::string tflite_path;
+---
+>   std::string input_model(argv[1]);
+>   std::string output_registration(argv[2]);
+>   std::string tflite_path("tensorflow/lite");
+158,159d157
+<   ParseFlagAndInit(&argc, argv, &input_models, &output_registration,
+<                    &tflite_path, &namespace_flag, &for_micro);
+163,171c161
+<   if (!input_models.empty()) {
+<     std::vector<std::string> models = absl::StrSplit(input_models, ',');
+<     for (const std::string& input_model : models) {
+<       AddOpsFromModel(input_model, &builtin_ops, &custom_ops);
+<     }
+<   }
+<   for (int i = 1; i < argc; i++) {
+<     AddOpsFromModel(argv[i], &builtin_ops, &custom_ops);
+<   }
+---
+>   AddOpsFromModel(input_model, &builtin_ops, &custom_ops);
+   </pre>
+ * This diff fixes an issue with the current binary's flag parsing
+ * library, which is failing silently.
+ *
+ * 3. Run "bazel build tensorflow/lite/tools/generate_op_registrations"
+ * and then run:
+   <pre>
+ ./bazel-bin/tensorflow/lite/tools/generate_op_registrations \
+    /path/to/model.tflite /path/to/output.cc
+   </pre>
+ * For appropriate paths to the input and output files.
+ */
+void RegisterSpecificOps(tflite::MutableOpResolver *resolver) {
+  resolver->AddBuiltin(tflite::BuiltinOperator_FULLY_CONNECTED,
+                       tflite::ops::builtin::Register_FULLY_CONNECTED(), 3, 3);
+  resolver->AddBuiltin(tflite::BuiltinOperator_SOFTMAX,
+                       tflite::ops::builtin::Register_SOFTMAX());
+}
+
+}  // namespace
+
+int main() {
+  // Toy model that takes in four floats and returns two classification
+  // probabilities.
+  auto model = tflite::GetModel(tf_lite_model_data);
+  // Build the interpreter.
+  tflite::MutableOpResolver resolver;
+  RegisterSpecificOps(&resolver);
+  tflite::InterpreterBuilder builder(model, resolver);
+  std::unique_ptr<tflite::Interpreter> interpreter;
+  builder(&interpreter);
+
+  std::unique_ptr<tflite::ErrorReporter> reporter(
+      tflite::DefaultErrorReporter());
+
+  if (interpreter->AllocateTensors() != kTfLiteOk) {
+    reporter->Report("Failed");
+    return EXIT_FAILURE;
+  }
+
+  // Always uses (0, 1, 2, 3) as inputs.
+  float *input = interpreter->typed_input_tensor<float>(0);
+  for (int i = 0; i < 4; ++i) {
+    input[i] = i;
+  }
+  auto status = interpreter->Invoke();
+  if (status != kTfLiteOk) {
+    reporter->Report("Failed");
+    return EXIT_FAILURE;
+  }
+  float y0 = interpreter->typed_output_tensor<float>(0)[0];
+  float y1 = interpreter->typed_output_tensor<float>(0)[1];
+  printf("Sample classification probability: %f, %f\n", y0, y1);
+  return EXIT_SUCCESS;
+}
diff --git a/examples/twopass_encoder.c b/examples/twopass_encoder.c
index 075eeae..9902938 100644
--- a/examples/twopass_encoder.c
+++ b/examples/twopass_encoder.c
@@ -242,7 +242,9 @@
   // Pass 1
   rewind(infile);
   cfg.g_pass = AOM_RC_LAST_PASS;
+#if !CONFIG_SINGLEPASS
   cfg.rc_twopass_stats_in = stats;
+#endif  // !CONFIG_SINGLEPASS
   pass1(&raw, infile, outfile_arg, encoder, &cfg, limit);
   free(stats.buf);
 
diff --git a/libs.doxy_template b/libs.doxy_template
index dab0ba0..bf88a1d 100644
--- a/libs.doxy_template
+++ b/libs.doxy_template
@@ -2110,10 +2110,12 @@
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 #
-# In builds where CONFIG_REALTIME_ONLY is set some functions are #ifdefed out
-# which causes reference failures. Hence for doxygen we set it to 0 here.
+# In builds where CONFIG_SINGLEPASS and/or CONFIG_REALTIME_ONLY are set some
+# functions are #ifdefed out which causes reference failures. Hence for
+# doxygen we set those to 0 here.
 
-PREDEFINED             = CONFIG_REALTIME_ONLY=0
+PREDEFINED           = "CONFIG_SINGLEPASS=0" \
+                       "CONFIG_REALTIME_ONLY=0"
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
 # tag can be used to specify a list of macro names that should be expanded. The
diff --git a/test/altref_test.cc b/test/altref_test.cc
index 742358e..d4d1fca 100644
--- a/test/altref_test.cc
+++ b/test/altref_test.cc
@@ -31,12 +31,14 @@
   { 0, 60, 12, 20, 25, ::libaom_test::kOnePassGood },
   { 60, 60, 16, 28, 30, ::libaom_test::kOnePassGood },
   { 0, 100, 16, 32, 35, ::libaom_test::kOnePassGood },
+#if !CONFIG_SINGLEPASS
   { 0, 10, 4, 8, 10, ::libaom_test::kTwoPassGood },
   { 0, 30, 8, 12, 16, ::libaom_test::kTwoPassGood },
   { 30, 30, 12, 16, 25, ::libaom_test::kTwoPassGood },
   { 0, 60, 16, 24, 25, ::libaom_test::kTwoPassGood },
   { 60, 60, 20, 28, 30, ::libaom_test::kTwoPassGood },
   { 0, 100, 24, 32, 35, ::libaom_test::kTwoPassGood },
+#endif
 };
 
 std::ostream &operator<<(std::ostream &os, const AltRefTestParams &test_arg) {
@@ -128,15 +130,17 @@
   { ::libaom_test::kOnePassGood, 8, 16 },
   { ::libaom_test::kOnePassGood, 16, 16 },
 
+#if !CONFIG_SINGLEPASS
   // two pass
   { ::libaom_test::kTwoPassGood, 0, 6 },
   { ::libaom_test::kTwoPassGood, 0, 8 },
   { ::libaom_test::kTwoPassGood, 5, 10 },
   { ::libaom_test::kTwoPassGood, 8, 16 },
   { ::libaom_test::kTwoPassGood, 16, 32 },
-  // disabled below test case because it causes failure
-  // TODO(anyone): enable below test case once issue is fixed.
-  // { ::libaom_test::kTwoPassGood, 20, 32 },
+// disabled below test case because it causes failure
+// TODO(anyone): enable below test case once issue is fixed.
+// { ::libaom_test::kTwoPassGood, 20, 32 },
+#endif
 };
 
 // This class is used to test if the gf interval bounds configured by the user
diff --git a/test/aomenc.sh b/test/aomenc.sh
index 0c0d1b1..e14b231 100755
--- a/test/aomenc.sh
+++ b/test/aomenc.sh
@@ -192,8 +192,8 @@
       $(aomenc_encode_test_fast_params) \
       --ivf \
       --output="${output}" \
-      --min-q=0 \
-      --max-q=0 || return 1
+      --min-qp=0 \
+      --max-qp=0 || return 1
 
     if [ ! -e "${output}" ]; then
       elog "Output file does not exist."
diff --git a/test/aq_segment_test.cc b/test/aq_segment_test.cc
index e2715a1..1c0c18b 100644
--- a/test/aq_segment_test.cc
+++ b/test/aq_segment_test.cc
@@ -48,8 +48,8 @@
     aq_mode_ = aq_mode;
     deltaq_mode_ = 0;
     cfg_.kf_max_dist = 12;
-    cfg_.rc_min_quantizer = 8;
-    cfg_.rc_max_quantizer = 56;
+    cfg_.rc_min_quantizer = 32;
+    cfg_.rc_max_quantizer = 224;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 6;
     cfg_.rc_buf_initial_sz = 500;
diff --git a/test/arf_freq_test.cc b/test/arf_freq_test.cc
index 0780cd7..bebcafa 100644
--- a/test/arf_freq_test.cc
+++ b/test/arf_freq_test.cc
@@ -57,8 +57,11 @@
 
 const TestEncodeParam kEncodeVectors[] = {
   { ::libaom_test::kOnePassGood, 2 }, { ::libaom_test::kOnePassGood, 5 },
+#if !CONFIG_SINGLEPASS
   { ::libaom_test::kTwoPassGood, 1 }, { ::libaom_test::kTwoPassGood, 2 },
-  { ::libaom_test::kTwoPassGood, 5 }, { ::libaom_test::kRealTime, 5 },
+  { ::libaom_test::kTwoPassGood, 5 },
+#endif  // !CONFIG_SINGLEPASS
+  { ::libaom_test::kRealTime, 5 },
 };
 
 const int kMinArfVectors[] = {
diff --git a/test/av1_encoder_parms_get_to_decoder.cc b/test/av1_encoder_parms_get_to_decoder.cc
index 717584d..6b662f9 100644
--- a/test/av1_encoder_parms_get_to_decoder.cc
+++ b/test/av1_encoder_parms_get_to_decoder.cc
@@ -89,7 +89,11 @@
 
   virtual void SetUp() {
     InitializeConfig();
+#if CONFIG_SINGLEPASS
+    SetMode(::libaom_test::kOnePassGood);
+#else
     SetMode(::libaom_test::kTwoPassGood);
+#endif  // CONFIG_SINGLEPASS
     cfg_.g_lag_in_frames = 25;
     test_video_ = kAV1ParamPassingTestVector;
     cfg_.rc_target_bitrate = test_video_.bitrate;
diff --git a/test/av1_ext_tile_test.cc b/test/av1_ext_tile_test.cc
index ad45667..d9456f8 100644
--- a/test/av1_ext_tile_test.cc
+++ b/test/av1_ext_tile_test.cc
@@ -71,7 +71,7 @@
     cfg_.rc_end_usage = AOM_VBR;
     cfg_.g_error_resilient = 1;
 
-    cfg_.rc_max_quantizer = 56;
+    cfg_.rc_max_quantizer = 224;
     cfg_.rc_min_quantizer = 0;
   }
 
@@ -199,17 +199,31 @@
 
 TEST_P(AV1ExtTileTest, DecoderResultTest) { TestRoundTrip(); }
 
+#if CONFIG_SINGLEPASS
 AV1_INSTANTIATE_TEST_SUITE(
-    // Now only test 2-pass mode.
+    // Only test 1-pass mode.
+    AV1ExtTileTest, ::testing::Values(::libaom_test::kOnePassGood),
+    ::testing::Range(1, 4));
+#else
+AV1_INSTANTIATE_TEST_SUITE(
+    // Only test 2-pass mode.
     AV1ExtTileTest, ::testing::Values(::libaom_test::kTwoPassGood),
     ::testing::Range(1, 4));
+#endif  // CONFIG_SINGLEPASS
 
 class AV1ExtTileTestLarge : public AV1ExtTileTest {};
 
 TEST_P(AV1ExtTileTestLarge, DecoderResultTest) { TestRoundTrip(); }
 
+#if CONFIG_SINGLEPASS
 AV1_INSTANTIATE_TEST_SUITE(
-    // Now only test 2-pass mode.
+    // Only test 1-pass mode.
+    AV1ExtTileTestLarge, ::testing::Values(::libaom_test::kOnePassGood),
+    ::testing::Range(0, 1));
+#else
+AV1_INSTANTIATE_TEST_SUITE(
+    // Only test 2-pass mode.
     AV1ExtTileTestLarge, ::testing::Values(::libaom_test::kTwoPassGood),
     ::testing::Range(0, 1));
+#endif  // CONFIG_SINGLEPASS
 }  // namespace
diff --git a/test/best_encode.sh b/test/best_encode.sh
index fe31a01..a9a72ae 100755
--- a/test/best_encode.sh
+++ b/test/best_encode.sh
@@ -40,8 +40,8 @@
     --kf-min-dist=0 \
     --kf-max-dist=99999 \
     --static-thresh=0 \
-    --min-q=0 \
-    --max-q=63 \
+    --min-qp=0 \
+    --max-qp=255 \
     --drop-frame=0 \
     --bias-pct=50 \
     --minsection-pct=0 \
@@ -69,8 +69,8 @@
     --kf-min-dist=0 \
     --kf-max-dist=99999 \
     --static-thresh=0 \
-    --min-q=0 \
-    --max-q=63 \
+    --min-qp=0 \
+    --max-qp=255 \
     --drop-frame=0
 
   aomenc \
@@ -90,8 +90,8 @@
     --kf-min-dist=0 \
     --kf-max-dist=99999 \
     --static-thresh=0 \
-    --min-q=0 \
-    --max-q=63 \
+    --min-qp=0 \
+    --max-qp=255 \
     --drop-frame=0 \
     --bias-pct=50 \
     --minsection-pct=0 \
diff --git a/test/block_test.cc b/test/block_test.cc
index 510a756..e6d7c98 100644
--- a/test/block_test.cc
+++ b/test/block_test.cc
@@ -192,9 +192,7 @@
       << "Failed for SB size " << superblock_size_;
 }
 
-AV1_INSTANTIATE_TEST_SUITE(SuperBlockSizeTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+AV1_INSTANTIATE_TEST_SUITE(SuperBlockSizeTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::Values(AOM_SUPERBLOCK_SIZE_64X64,
                                              AOM_SUPERBLOCK_SIZE_128X128),
                            ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ));
diff --git a/test/borders_test.cc b/test/borders_test.cc
index 841f0d9..09504cc 100644
--- a/test/borders_test.cc
+++ b/test/borders_test.cc
@@ -53,10 +53,12 @@
   // the encoder to producing lots of big partitions which will likely
   // extend into the border and test the border condition.
   cfg_.g_lag_in_frames = 25;
+#if !CONFIG_SINGLEPASS
   cfg_.rc_2pass_vbr_minsection_pct = 5;
   cfg_.rc_2pass_vbr_maxsection_pct = 2000;
+#endif  // !CONFIG_SINGLEPASS
   cfg_.rc_target_bitrate = 2000;
-  cfg_.rc_max_quantizer = 10;
+  cfg_.rc_max_quantizer = 40;
 
   ::libaom_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
                                        10);
@@ -69,10 +71,12 @@
   // lots of small partitions which might will test the other condition.
 
   cfg_.g_lag_in_frames = 25;
+#if !CONFIG_SINGLEPASS
   cfg_.rc_2pass_vbr_minsection_pct = 5;
   cfg_.rc_2pass_vbr_maxsection_pct = 2000;
+#endif  // !CONFIG_SINGLEPASS
   cfg_.rc_target_bitrate = 200;
-  cfg_.rc_min_quantizer = 40;
+  cfg_.rc_min_quantizer = 160;
 
   ::libaom_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
                                        10);
@@ -80,6 +84,11 @@
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
 }
 
+#if CONFIG_SINGLEPASS
+AV1_INSTANTIATE_TEST_SUITE(BordersTestLarge,
+                           ::testing::Values(::libaom_test::kOnePassGood));
+#else
 AV1_INSTANTIATE_TEST_SUITE(BordersTestLarge,
                            ::testing::Values(::libaom_test::kTwoPassGood));
+#endif  // !CONFIG_SINGLEPASS
 }  // namespace
diff --git a/test/codec_factory.h b/test/codec_factory.h
index 5ceb70b..2ea0ea5 100644
--- a/test/codec_factory.h
+++ b/test/codec_factory.h
@@ -41,9 +41,14 @@
   virtual Decoder *CreateDecoder(aom_codec_dec_cfg_t cfg,
                                  const aom_codec_flags_t flags) const = 0;
 
+#if CONFIG_SINGLEPASS
+  virtual Encoder *CreateEncoder(aom_codec_enc_cfg_t cfg,
+                                 const aom_codec_flags_t init_flags) const = 0;
+#else
   virtual Encoder *CreateEncoder(aom_codec_enc_cfg_t cfg,
                                  const aom_codec_flags_t init_flags,
                                  TwopassStatsStore *stats) const = 0;
+#endif  // CONFIG_SINGLEPASS
 
   virtual aom_codec_err_t DefaultEncoderConfig(aom_codec_enc_cfg_t *cfg,
                                                unsigned int usage) const = 0;
@@ -101,9 +106,14 @@
 
 class AV1Encoder : public Encoder {
  public:
+#if CONFIG_SINGLEPASS
+  AV1Encoder(aom_codec_enc_cfg_t cfg, const aom_codec_flags_t init_flags)
+      : Encoder(cfg, init_flags) {}
+#else
   AV1Encoder(aom_codec_enc_cfg_t cfg, const aom_codec_flags_t init_flags,
              TwopassStatsStore *stats)
       : Encoder(cfg, init_flags, stats) {}
+#endif  // CONFIG_SINGLEPASS
 
  protected:
   virtual aom_codec_iface_t *CodecInterface() const {
@@ -134,15 +144,27 @@
 #endif
   }
 
+#if CONFIG_SINGLEPASS
+  virtual Encoder *CreateEncoder(aom_codec_enc_cfg_t cfg,
+                                 const aom_codec_flags_t init_flags) const
+#else
   virtual Encoder *CreateEncoder(aom_codec_enc_cfg_t cfg,
                                  const aom_codec_flags_t init_flags,
-                                 TwopassStatsStore *stats) const {
+                                 TwopassStatsStore *stats) const
+#endif  // CONFIG_SINGLEPASS
+  {
 #if CONFIG_AV1_ENCODER
+#if CONFIG_SINGLEPASS
+    return new AV1Encoder(cfg, init_flags);
+#else
     return new AV1Encoder(cfg, init_flags, stats);
+#endif  // CONFIG_SINGLEPASS
 #else
     (void)cfg;
     (void)init_flags;
+#if !CONFIG_SINGLEPASS
     (void)stats;
+#endif  // !CONFIG_SINGLEPASS
     return NULL;
 #endif
   }
diff --git a/test/coding_path_sync.cc b/test/coding_path_sync.cc
index 4c613dc..4779120 100644
--- a/test/coding_path_sync.cc
+++ b/test/coding_path_sync.cc
@@ -36,9 +36,9 @@
     // force the quantizer, to reduce the sensitivity on encoding choices.
     // e.g, we don't want this test to break when the rate control is modified.
     {
-      const int max_q = cfg.rc_max_quantizer;
-      const int min_q = cfg.rc_min_quantizer;
-      const int q = rnd_.PseudoUniform(max_q - min_q + 1) + min_q;
+      const int max_qp = cfg.rc_max_quantizer;
+      const int min_qp = cfg.rc_min_quantizer;
+      const int q = rnd_.PseudoUniform(max_qp - min_qp + 1) + min_qp;
 
       cfg.rc_end_usage = AOM_Q;
       cfg.rc_max_quantizer = q;
diff --git a/test/cpu_speed_test.cc b/test/cpu_speed_test.cc
index 9ef3a5c..f31cea6 100644
--- a/test/cpu_speed_test.cc
+++ b/test/cpu_speed_test.cc
@@ -78,8 +78,10 @@
   // without a mismatch when passing in a very low max q.  This pushes
   // the encoder to producing lots of big partitions which will likely
   // extend into the border and test the border condition.
+#if !CONFIG_SINGLEPASS
   cfg_.rc_2pass_vbr_minsection_pct = 5;
   cfg_.rc_2pass_vbr_maxsection_pct = 2000;
+#endif  // !CONFIG_SINGLEPASS
   cfg_.rc_target_bitrate = 400;
   cfg_.rc_max_quantizer = 0;
   cfg_.rc_min_quantizer = 0;
@@ -96,8 +98,10 @@
 void CpuSpeedTest::TestScreencastQ0() {
   ::libaom_test::Y4mVideoSource video("screendata.y4m", 0, 3);
   cfg_.g_timebase = video.timebase();
+#if !CONFIG_SINGLEPASS
   cfg_.rc_2pass_vbr_minsection_pct = 5;
   cfg_.rc_2pass_vbr_maxsection_pct = 2000;
+#endif  // !CONFIG_SINGLEPASS
   cfg_.rc_target_bitrate = 400;
   cfg_.rc_max_quantizer = 0;
   cfg_.rc_min_quantizer = 0;
@@ -111,10 +115,12 @@
 void CpuSpeedTest::TestTuneScreen() {
   ::libaom_test::Y4mVideoSource video("screendata.y4m", 0, 3);
   cfg_.g_timebase = video.timebase();
+#if !CONFIG_SINGLEPASS
   cfg_.rc_2pass_vbr_minsection_pct = 5;
   cfg_.rc_2pass_vbr_minsection_pct = 2000;
+#endif  // !CONFIG_SINGLEPASS
   cfg_.rc_target_bitrate = 2000;
-  cfg_.rc_max_quantizer = 63;
+  cfg_.rc_max_quantizer = 255;
   cfg_.rc_min_quantizer = 0;
   tune_content_ = AOM_CONTENT_SCREEN;
 
@@ -128,10 +134,12 @@
   // without a mismatch when passing in a very low max q.  This pushes
   // the encoder to producing lots of big partitions which will likely
   // extend into the border and test the border condition.
+#if !CONFIG_SINGLEPASS
   cfg_.rc_2pass_vbr_minsection_pct = 5;
   cfg_.rc_2pass_vbr_maxsection_pct = 2000;
+#endif  // !CONFIG_SINGLEPASS
   cfg_.rc_target_bitrate = 12000;
-  cfg_.rc_max_quantizer = 10;
+  cfg_.rc_max_quantizer = 40;
   cfg_.rc_min_quantizer = 0;
 
   ::libaom_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
@@ -144,10 +152,12 @@
   // Validate that this clip encodes and decodes without a mismatch
   // when passing in a very high min q.  This pushes the encoder to producing
   // lots of small partitions which might will test the other condition.
+#if !CONFIG_SINGLEPASS
   cfg_.rc_2pass_vbr_minsection_pct = 5;
   cfg_.rc_2pass_vbr_maxsection_pct = 2000;
+#endif  // !CONFIG_SINGLEPASS
   cfg_.rc_target_bitrate = 200;
-  cfg_.rc_min_quantizer = 40;
+  cfg_.rc_min_quantizer = 160;
 
   ::libaom_test::I420VideoSource video("hantro_odd.yuv", 208, 144, 30, 1, 0,
                                        10);
@@ -169,12 +179,8 @@
 TEST_P(CpuSpeedTestLarge, TestEncodeHighBitrate) { TestEncodeHighBitrate(); }
 TEST_P(CpuSpeedTestLarge, TestLowBitrate) { TestLowBitrate(); }
 
-AV1_INSTANTIATE_TEST_SUITE(CpuSpeedTest,
-                           ::testing::Values(::libaom_test::kTwoPassGood,
-                                             ::libaom_test::kOnePassGood),
+AV1_INSTANTIATE_TEST_SUITE(CpuSpeedTest, NONREALTIME_TEST_MODES,
                            ::testing::Range(1, 3));
-AV1_INSTANTIATE_TEST_SUITE(CpuSpeedTestLarge,
-                           ::testing::Values(::libaom_test::kTwoPassGood,
-                                             ::libaom_test::kOnePassGood),
+AV1_INSTANTIATE_TEST_SUITE(CpuSpeedTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::Range(0, 1));
 }  // namespace
diff --git a/test/datarate_test.cc b/test/datarate_test.cc
index b82c31f..ff64aca 100644
--- a/test/datarate_test.cc
+++ b/test/datarate_test.cc
@@ -45,7 +45,7 @@
 
   virtual void BasicRateTargetingVBRTest() {
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.g_error_resilient = 0;
     cfg_.rc_end_usage = AOM_VBR;
     cfg_.g_lag_in_frames = 0;
@@ -68,7 +68,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 1;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
 
@@ -92,7 +92,7 @@
     cfg_.rc_dropframe_thresh = 0;
     cfg_.g_error_resilient = 1;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
 
@@ -113,7 +113,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 1;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     // Periodic keyframe
@@ -138,7 +138,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     // Periodic keyframe
@@ -162,8 +162,8 @@
     cfg_.rc_buf_optimal_sz = 500;
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
-    cfg_.rc_min_quantizer = 2;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_min_quantizer = 8;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_error_resilient = 0;
@@ -193,7 +193,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 1;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
 
     const int bitrate_array[2] = { 250, 650 };
@@ -238,7 +238,7 @@
     cfg_.rc_undershoot_pct = 20;
     cfg_.rc_dropframe_thresh = 10;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 50;
+    cfg_.rc_max_quantizer = 200;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.rc_target_bitrate = 200;
     cfg_.g_lag_in_frames = 0;
@@ -351,7 +351,7 @@
     cfg_.rc_undershoot_pct = 20;
     cfg_.rc_dropframe_thresh = 10;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 50;
+    cfg_.rc_max_quantizer = 200;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.rc_target_bitrate = 200;
     cfg_.g_lag_in_frames = 0;
diff --git a/test/decode_multithreaded_test.cc b/test/decode_multithreaded_test.cc
index 5224dcc..3b687d0 100644
--- a/test/decode_multithreaded_test.cc
+++ b/test/decode_multithreaded_test.cc
@@ -71,7 +71,11 @@
 
   virtual void SetUp() {
     InitializeConfig();
+#if CONFIG_SINGLEPASS
+    SetMode(libaom_test::kOnePassGood);
+#else
     SetMode(libaom_test::kTwoPassGood);
+#endif  // CONFIG_SINGLEPASS
   }
 
   virtual void PreEncodeFrameHook(libaom_test::VideoSource *video,
diff --git a/test/decode_perf_test.cc b/test/decode_perf_test.cc
index 9f0de03..416fd9d 100644
--- a/test/decode_perf_test.cc
+++ b/test/decode_perf_test.cc
@@ -110,8 +110,8 @@
     SetMode(encoding_mode_);
 
     cfg_.g_lag_in_frames = 25;
-    cfg_.rc_min_quantizer = 2;
-    cfg_.rc_max_quantizer = 56;
+    cfg_.rc_min_quantizer = 8;
+    cfg_.rc_max_quantizer = 224;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_undershoot_pct = 50;
     cfg_.rc_overshoot_pct = 50;
diff --git a/test/encode_perf_test.cc b/test/encode_perf_test.cc
index b626acd..bcd0184 100644
--- a/test/encode_perf_test.cc
+++ b/test/encode_perf_test.cc
@@ -70,8 +70,8 @@
     SetMode(encoding_mode_);
 
     cfg_.g_lag_in_frames = 0;
-    cfg_.rc_min_quantizer = 2;
-    cfg_.rc_max_quantizer = 56;
+    cfg_.rc_min_quantizer = 8;
+    cfg_.rc_max_quantizer = 224;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_undershoot_pct = 50;
     cfg_.rc_overshoot_pct = 50;
diff --git a/test/encode_test_driver.cc b/test/encode_test_driver.cc
index 0139d1f..89c7aec 100644
--- a/test/encode_test_driver.cc
+++ b/test/encode_test_driver.cc
@@ -32,7 +32,9 @@
     cfg_.g_w = img->d_w;
     cfg_.g_h = img->d_h;
     cfg_.g_timebase = video->timebase();
+#if !CONFIG_SINGLEPASS
     cfg_.rc_twopass_stats_in = stats_->buf();
+#endif  // !CONFIG_SINGLEPASS
 
     res = aom_codec_enc_init(&encoder_, CodecInterface(), &cfg_, init_flags_);
     ASSERT_EQ(AOM_CODEC_OK, res) << EncoderError();
@@ -45,6 +47,7 @@
   else
     Flush();
 
+#if !CONFIG_SINGLEPASS
   // Handle twopass stats
   CxDataIterator iter = GetCxData();
 
@@ -53,6 +56,7 @@
 
     stats_->Append(*pkt);
   }
+#endif  // !CONFIG_SINGLEPASS
 }
 
 void Encoder::EncodeFrameInternal(const VideoSource &video,
@@ -90,8 +94,10 @@
 
 void EncoderTest::SetMode(TestMode mode) {
   switch (mode) {
-    case kOnePassGood:
+    case kOnePassGood: break;
+#if !CONFIG_SINGLEPASS
     case kTwoPassGood: break;
+#endif  // !CONFIG_SINGLEPASS
     case kRealTime: {
       cfg_.g_lag_in_frames = 0;
       cfg_.g_usage = AOM_USAGE_REALTIME;
@@ -100,10 +106,14 @@
     default: ASSERT_TRUE(false) << "Unexpected mode " << mode;
   }
   mode_ = mode;
+#if CONFIG_SINGLEPASS
+  passes_ = 1;
+#else
   if (mode == kTwoPassGood)
     passes_ = 2;
   else
     passes_ = 1;
+#endif  // CONFIG_SINGLEPASS
 }
 
 static bool compare_plane(const uint8_t *const buf1, int stride1,
@@ -183,9 +193,15 @@
   aom_codec_dec_cfg_t dec_cfg = aom_codec_dec_cfg_t();
   dec_cfg.allow_lowbitdepth = 1;
 
+#if !CONFIG_SINGLEPASS
   stats_.Reset();
+#endif  // !CONFIG_SINGLEPASS
 
+#if CONFIG_SINGLEPASS
+  ASSERT_EQ(1, (int)passes_);
+#else
   ASSERT_TRUE(passes_ == 1 || passes_ == 2);
+#endif  // CONFIG_SINGLEPASS
   for (unsigned int pass = 0; pass < passes_; pass++) {
     last_pts_ = 0;
 
@@ -197,8 +213,12 @@
       cfg_.g_pass = AOM_RC_LAST_PASS;
 
     BeginPassHook(pass);
+#if CONFIG_SINGLEPASS
+    std::unique_ptr<Encoder> encoder(codec_->CreateEncoder(cfg_, init_flags_));
+#else
     std::unique_ptr<Encoder> encoder(
         codec_->CreateEncoder(cfg_, init_flags_, &stats_));
+#endif  // CONFIG_SINGLEPASS
     ASSERT_TRUE(encoder.get() != NULL);
 
     ASSERT_NO_FATAL_FAILURE(video->Begin());
@@ -267,7 +287,9 @@
 
             case AOM_CODEC_PSNR_PKT: PSNRPktHook(pkt); break;
 
+#if !CONFIG_SINGLEPASS
             case AOM_CODEC_STATS_PKT: StatsPktHook(pkt); break;
+#endif  // !CONFIG_SINGLEPASS
 
             default: break;
           }
diff --git a/test/encode_test_driver.h b/test/encode_test_driver.h
index 9744b11..369615f 100644
--- a/test/encode_test_driver.h
+++ b/test/encode_test_driver.h
@@ -28,6 +28,18 @@
 class CodecFactory;
 class VideoSource;
 
+#if CONFIG_SINGLEPASS
+enum TestMode { kRealTime, kOnePassGood };
+#define ALL_TEST_MODES \
+  ::testing::Values(::libaom_test::kRealTime, ::libaom_test::kOnePassGood)
+
+#define ONE_PASS_TEST_MODES \
+  ::testing::Values(::libaom_test::kRealTime, ::libaom_test::kOnePassGood)
+
+#define TWO_PASS_TEST_MODES ::testing::Values()
+
+#define NONREALTIME_TEST_MODES ::testing::Values(::libaom_test::kOnePassGood)
+#else
 enum TestMode { kRealTime, kOnePassGood, kTwoPassGood };
 #define ALL_TEST_MODES                                                     \
   ::testing::Values(::libaom_test::kRealTime, ::libaom_test::kOnePassGood, \
@@ -40,6 +52,7 @@
 
 #define NONREALTIME_TEST_MODES \
   ::testing::Values(::libaom_test::kOnePassGood, ::libaom_test::kTwoPassGood)
+#endif  // CONFIG_SINGLEPASS
 
 // Provides an object to handle the libaom get_cx_data() iteration pattern
 class CxDataIterator {
@@ -82,11 +95,18 @@
 // level of abstraction will be fleshed out as more tests are written.
 class Encoder {
  public:
+#if CONFIG_SINGLEPASS
+  Encoder(aom_codec_enc_cfg_t cfg, const aom_codec_flags_t init_flags)
+      : cfg_(cfg), init_flags_(init_flags) {
+    memset(&encoder_, 0, sizeof(encoder_));
+  }
+#else
   Encoder(aom_codec_enc_cfg_t cfg, const aom_codec_flags_t init_flags,
           TwopassStatsStore *stats)
       : cfg_(cfg), init_flags_(init_flags), stats_(stats) {
     memset(&encoder_, 0, sizeof(encoder_));
   }
+#endif  // !CONFIG_SINGLEPASS
 
   virtual ~Encoder() { aom_codec_destroy(&encoder_); }
 
@@ -165,7 +185,9 @@
   aom_codec_ctx_t encoder_;
   aom_codec_enc_cfg_t cfg_;
   aom_codec_flags_t init_flags_;
+#if !CONFIG_SINGLEPASS
   TwopassStatsStore *stats_;
+#endif  // !CONFIG_SINGLEPASS
 };
 
 // Common test functionality for all Encoder tests.
@@ -255,7 +277,9 @@
   bool abort_;
   aom_codec_enc_cfg_t cfg_;
   unsigned int passes_;
+#if !CONFIG_SINGLEPASS
   TwopassStatsStore stats_;
+#endif  // !CONFIG_SINGLEPASS
   aom_codec_flags_t init_flags_;
   unsigned long frame_flags_;
   aom_codec_pts_t last_pts_;
diff --git a/test/end_to_end_test.cc b/test/end_to_end_test.cc
index 82a4a8c..f1e5ebc 100644
--- a/test/end_to_end_test.cc
+++ b/test/end_to_end_test.cc
@@ -77,7 +77,9 @@
 
 // Encoding modes tested
 const libaom_test::TestMode kEncodingModeVectors[] = {
+#if !CONFIG_SINGLEPASS
   ::libaom_test::kTwoPassGood,
+#endif  // !CONFIG_SINGLEPASS
   ::libaom_test::kOnePassGood,
   ::libaom_test::kRealTime,
 };
@@ -136,11 +138,15 @@
       encoder->Control(AV1E_SET_FRAME_PARALLEL_DECODING, 1);
       encoder->Control(AV1E_SET_TILE_COLUMNS, 4);
       encoder->Control(AOME_SET_CPUUSED, cpu_used_);
+#if CONFIG_SINGLEPASS
+      encoder->Control(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_DEFAULT);
+#else
       // Test screen coding tools at cpu_used = 1 && encoding mode is two-pass.
       if (cpu_used_ == 1 && encoding_mode_ == ::libaom_test::kTwoPassGood)
         encoder->Control(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_SCREEN);
       else
         encoder->Control(AV1E_SET_TUNE_CONTENT, AOM_CONTENT_DEFAULT);
+#endif  // CONFIG_SINGLEPASS
       if (encoding_mode_ != ::libaom_test::kRealTime) {
         encoder->Control(AOME_SET_ENABLEAUTOALTREF, 1);
         encoder->Control(AOME_SET_ARNR_MAXFRAMES, 7);
diff --git a/test/error_resilience_test.cc b/test/error_resilience_test.cc
index f7e4287..efbb701 100644
--- a/test/error_resilience_test.cc
+++ b/test/error_resilience_test.cc
@@ -541,9 +541,7 @@
  * Hence this configuration is not added. Add this configuration after the
  * bug is fixed.
  */
-AV1_INSTANTIATE_TEST_SUITE(SFramePresenceTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+AV1_INSTANTIATE_TEST_SUITE(SFramePresenceTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ),
                            ::testing::Values(0));
 }  // namespace
diff --git a/test/ethread_test.cc b/test/ethread_test.cc
index 04ee4d7..81b036d 100644
--- a/test/ethread_test.cc
+++ b/test/ethread_test.cc
@@ -45,9 +45,11 @@
 
     cfg_.g_lag_in_frames = 35;
     cfg_.rc_end_usage = AOM_VBR;
+#if !CONFIG_SINGLEPASS
     cfg_.rc_2pass_vbr_minsection_pct = 5;
     cfg_.rc_2pass_vbr_maxsection_pct = 2000;
-    cfg_.rc_max_quantizer = 56;
+#endif  // !CONFIG_SINGLEPASS
+    cfg_.rc_max_quantizer = 224;
     cfg_.rc_min_quantizer = 0;
   }
 
@@ -232,14 +234,16 @@
     if (encoding_mode_ != ::libaom_test::kRealTime) {
       cfg_.g_lag_in_frames = 5;
       cfg_.rc_end_usage = AOM_VBR;
+#if !CONFIG_SINGLEPASS
       cfg_.rc_2pass_vbr_minsection_pct = 5;
       cfg_.rc_2pass_vbr_maxsection_pct = 2000;
+#endif  // !CONFIG_SINGLEPASS
     } else {
       cfg_.g_lag_in_frames = 0;
       cfg_.rc_end_usage = AOM_CBR;
       cfg_.g_error_resilient = 1;
     }
-    cfg_.rc_max_quantizer = 56;
+    cfg_.rc_max_quantizer = 224;
     cfg_.rc_min_quantizer = 0;
   }
 
@@ -438,6 +442,10 @@
   DoTest();
 }
 
+#if CONFIG_SINGLEPASS
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AVxFirstPassEncoderThreadTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AVxEncoderThreadTest);
+#else
 // first pass stats test
 AV1_INSTANTIATE_TEST_SUITE(AVxFirstPassEncoderThreadTest,
                            ::testing::Values(::libaom_test::kTwoPassGood),
@@ -450,14 +458,23 @@
                            ::testing::Values(::libaom_test::kTwoPassGood),
                            ::testing::Values(2), ::testing::Values(0, 2),
                            ::testing::Values(0, 2), ::testing::Values(0, 1));
+#endif  // CONFIG_SINGLEPASS
 
 // Test cpu_used 0, 1, 3 and 5.
+#if CONFIG_SINGLEPASS
 AV1_INSTANTIATE_TEST_SUITE(AVxEncoderThreadTestLarge,
-                           ::testing::Values(::libaom_test::kTwoPassGood,
-                                             ::libaom_test::kOnePassGood),
+                           ::testing::Values(::libaom_test::kOnePassGood),
                            ::testing::Values(0, 1, 3, 5),
-                           ::testing::Values(1, 6), ::testing::Values(1, 6),
+                           ::testing::Values(0, 1, 2, 6),
+                           ::testing::Values(0, 1, 2, 6),
                            ::testing::Values(0, 1));
+#else
+AV1_INSTANTIATE_TEST_SUITE(
+    AVxEncoderThreadTestLarge,
+    ::testing::Values(::libaom_test::kTwoPassGood, ::libaom_test::kOnePassGood),
+    ::testing::Values(0, 1, 3, 5), ::testing::Values(0, 1, 2, 6),
+    ::testing::Values(0, 1, 2, 6), ::testing::Values(0, 1));
+#endif  // CONFIG_SINGLEPASS
 
 class AVxEncoderThreadLSTest : public AVxEncoderThreadTest {
   virtual void SetTileSize(libaom_test::Encoder *encoder) {
@@ -493,9 +510,7 @@
   DoTest();
 }
 
-AV1_INSTANTIATE_TEST_SUITE(AVxEncoderThreadLSTestLarge,
-                           ::testing::Values(::libaom_test::kTwoPassGood,
-                                             ::libaom_test::kOnePassGood),
-                           ::testing::Values(1, 3), ::testing::Values(0, 6),
-                           ::testing::Values(0, 6), ::testing::Values(1));
+AV1_INSTANTIATE_TEST_SUITE(AVxEncoderThreadLSTestLarge, NONREALTIME_TEST_MODES,
+                           ::testing::Range(0, 4), ::testing::Values(0, 6),
+                           ::testing::Values(0, 6), ::testing::Values(0, 1));
 }  // namespace
diff --git a/test/examples.sh b/test/examples.sh
index 2cdb89d..0a52847 100755
--- a/test/examples.sh
+++ b/test/examples.sh
@@ -16,6 +16,8 @@
 
 # List of script names to exclude.
 exclude_list="best_encode examples run_encodes tools_common"
+[ "$(aom_config_option_enabled CONFIG_SINGLEPASS)" = "yes" ] && \
+  exclude_list="$exclude_list twopass_encoder lightfield_test"
 
 # Filter out the scripts in $exclude_list.
 for word in ${exclude_list}; do
diff --git a/test/fwd_kf_test.cc b/test/fwd_kf_test.cc
index fb12402..299314c 100644
--- a/test/fwd_kf_test.cc
+++ b/test/fwd_kf_test.cc
@@ -110,9 +110,7 @@
       << "kf max dist = " << kf_max_dist_;
 }
 
-AV1_INSTANTIATE_TEST_SUITE(ForwardKeyTest,
-                           ::testing::Values(::libaom_test::kTwoPassGood,
-                                             ::libaom_test::kOnePassGood),
+AV1_INSTANTIATE_TEST_SUITE(ForwardKeyTest, NONREALTIME_TEST_MODES,
                            ::testing::ValuesIn(kTestParams));
 
 typedef struct {
@@ -189,9 +187,7 @@
   ASSERT_EQ(is_fwd_kf_present_, 1);
 }
 
-AV1_INSTANTIATE_TEST_SUITE(ForwardKeyPresenceTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+AV1_INSTANTIATE_TEST_SUITE(ForwardKeyPresenceTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::ValuesIn(kfTestParams),
                            ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ));
 }  // namespace
diff --git a/test/gf_pyr_height_test.cc b/test/gf_pyr_height_test.cc
index 87a0d0d..7b301e8 100644
--- a/test/gf_pyr_height_test.cc
+++ b/test/gf_pyr_height_test.cc
@@ -111,7 +111,7 @@
     if (video->frame() == 0) {
       encoder->Control(AOME_SET_CPUUSED, cpu_used_);
       if (rc_mode_ == AOM_Q) {
-        encoder->Control(AOME_SET_CQ_LEVEL, 32);
+        encoder->Control(AOME_SET_QP, 32);
       }
       if (encoding_mode_ != ::libaom_test::kRealTime) {
         encoder->Control(AOME_SET_ENABLEAUTOALTREF, 1);
diff --git a/test/horz_superres_test.cc b/test/horz_superres_test.cc
index 4b96d44..fca8573 100644
--- a/test/horz_superres_test.cc
+++ b/test/horz_superres_test.cc
@@ -76,9 +76,9 @@
 // Superres q thresholds and superres kf q thresholds to be tested
 typedef tuple<int, int> SuperresQThresholdPair;
 const SuperresQThresholdPair kSuperresQThresholds[] = {
-  make_tuple(63, 63), make_tuple(63, 41), make_tuple(17, 63),
-  make_tuple(41, 11), make_tuple(1, 37),  make_tuple(11, 11),
-  make_tuple(1, 1),   make_tuple(17, 29), make_tuple(29, 11),
+  make_tuple(255, 255), make_tuple(255, 164), make_tuple(68, 255),
+  make_tuple(164, 44),  make_tuple(4, 148),   make_tuple(44, 44),
+  make_tuple(4, 4),     make_tuple(68, 116),  make_tuple(116, 44),
 };
 
 /* END (TESTING PARAMETERS) */
@@ -101,7 +101,11 @@
 
   virtual void SetUp() {
     InitializeConfig();
+#if CONFIG_SINGLEPASS
+    SetMode(::libaom_test::kOnePassGood);
+#else
     SetMode(::libaom_test::kTwoPassGood);
+#endif  // CONFIG_SINGLEPASS
     cfg_.g_lag_in_frames = 5;
     cfg_.rc_end_usage = AOM_Q;
     cfg_.rc_target_bitrate = kBitrate;
@@ -204,7 +208,11 @@
 
   virtual void SetUp() {
     InitializeConfig();
+#if CONFIG_SINGLEPASS
+    SetMode(::libaom_test::kOnePassGood);
+#else
     SetMode(::libaom_test::kTwoPassGood);
+#endif  // CONFIG_SINGLEPASS
     cfg_.g_lag_in_frames = 5;
     cfg_.rc_end_usage = AOM_VBR;
     cfg_.rc_target_bitrate = kBitrate;
@@ -315,7 +323,11 @@
 
   virtual void SetUp() {
     InitializeConfig();
+#if CONFIG_SINGLEPASS
+    SetMode(::libaom_test::kOnePassGood);
+#else
     SetMode(::libaom_test::kTwoPassGood);
+#endif  // CONFIG_SINGLEPASS
     cfg_.g_lag_in_frames = 5;
     cfg_.rc_end_usage = AOM_VBR;
     cfg_.rc_target_bitrate = kBitrate;
diff --git a/test/kf_test.cc b/test/kf_test.cc
index 0b36881..21eb444 100644
--- a/test/kf_test.cc
+++ b/test/kf_test.cc
@@ -248,17 +248,13 @@
   }
 }
 
-AV1_INSTANTIATE_TEST_SUITE(KeyFrameIntervalTestLarge,
-                           testing::Values(::libaom_test::kOnePassGood,
-                                           ::libaom_test::kTwoPassGood),
+AV1_INSTANTIATE_TEST_SUITE(KeyFrameIntervalTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::ValuesIn(kfTestParams),
                            ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ));
 
 // TODO(anyone): Add CBR to list of rc_modes once forced kf placement after
 // lag in frames bug is fixed.
-AV1_INSTANTIATE_TEST_SUITE(ForcedKeyTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+AV1_INSTANTIATE_TEST_SUITE(ForcedKeyTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::Values(0, 1), ::testing::Values(0, 1),
                            ::testing::Values(2, 5),
                            ::testing::Values(AOM_Q, AOM_VBR, AOM_CQ));
diff --git a/test/level_test.cc b/test/level_test.cc
index 82ada98..0739df3 100644
--- a/test/level_test.cc
+++ b/test/level_test.cc
@@ -153,8 +153,13 @@
   }
 }
 
+#if CONFIG_SINGLEPASS
 AV1_INSTANTIATE_TEST_SUITE(LevelTest,
-                           ::testing::Values(::libaom_test::kTwoPassGood,
-                                             ::libaom_test::kOnePassGood),
+                           ::testing::Values(::libaom_test::kOnePassGood),
                            ::testing::ValuesIn(kCpuUsedVectors));
+#else
+AV1_INSTANTIATE_TEST_SUITE(LevelTest,
+                           ::testing::Values(::libaom_test::kTwoPassGood),
+                           ::testing::ValuesIn(kCpuUsedVectors));
+#endif  // CONFIG_SINGLEPASS
 }  // namespace
diff --git a/test/lossless_test.cc b/test/lossless_test.cc
index b56b43a..85eced7 100644
--- a/test/lossless_test.cc
+++ b/test/lossless_test.cc
@@ -125,8 +125,8 @@
   cfg_.g_lag_in_frames = 25;
   // Intentionally set Q > 0, to make sure control can be used to activate
   // lossless
-  cfg_.rc_min_quantizer = 10;
-  cfg_.rc_max_quantizer = 20;
+  cfg_.rc_min_quantizer = 40;
+  cfg_.rc_max_quantizer = 80;
 
   init_flags_ = AOM_CODEC_USE_PSNR;
 
@@ -137,8 +137,6 @@
   EXPECT_GE(psnr_lossless, kMaxPsnr);
 }
 
-AV1_INSTANTIATE_TEST_SUITE(LosslessTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+AV1_INSTANTIATE_TEST_SUITE(LosslessTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ));
 }  // namespace
diff --git a/test/metadata_test.cc b/test/metadata_test.cc
index c102b7a..1b7e586 100644
--- a/test/metadata_test.cc
+++ b/test/metadata_test.cc
@@ -175,8 +175,8 @@
   cfg_.rc_buf_initial_sz = 500;
   cfg_.rc_buf_optimal_sz = 600;
   cfg_.rc_buf_sz = 1000;
-  cfg_.rc_min_quantizer = 2;
-  cfg_.rc_max_quantizer = 56;
+  cfg_.rc_min_quantizer = 8;
+  cfg_.rc_max_quantizer = 224;
   cfg_.rc_undershoot_pct = 50;
   cfg_.rc_overshoot_pct = 50;
   cfg_.rc_end_usage = AOM_CBR;
diff --git a/test/monochrome_test.cc b/test/monochrome_test.cc
index 4688961..f09a148 100644
--- a/test/monochrome_test.cc
+++ b/test/monochrome_test.cc
@@ -77,6 +77,7 @@
       frame0_psnr_y_ = pkt->data.psnr.psnr[1];
       EXPECT_GT(frame0_psnr_y_, 29.);
     }
+    EXPECT_GT(pkt->data.psnr.psnr[1], 29.);
     EXPECT_NEAR(pkt->data.psnr.psnr[1], frame0_psnr_y_, 2.5);
   }
 
@@ -96,8 +97,8 @@
   cfg_.rc_buf_initial_sz = 500;
   cfg_.rc_buf_optimal_sz = 600;
   cfg_.rc_buf_sz = 1000;
-  cfg_.rc_min_quantizer = 2;
-  cfg_.rc_max_quantizer = 56;
+  cfg_.rc_min_quantizer = 8;
+  cfg_.rc_max_quantizer = 224;
   cfg_.rc_undershoot_pct = 50;
   cfg_.rc_overshoot_pct = 50;
   cfg_.rc_end_usage = AOM_CBR;
@@ -124,8 +125,6 @@
   }
 }
 
-AV1_INSTANTIATE_TEST_SUITE(MonochromeTest,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood));
+AV1_INSTANTIATE_TEST_SUITE(MonochromeTest, NONREALTIME_TEST_MODES);
 
 }  // namespace
diff --git a/test/motion_vector_test.cc b/test/motion_vector_test.cc
index 7516a36..4a0a399 100644
--- a/test/motion_vector_test.cc
+++ b/test/motion_vector_test.cc
@@ -24,7 +24,9 @@
 
 // Encoding modes
 const libaom_test::TestMode kEncodingModeVectors[] = {
+#if !CONFIG_SINGLEPASS
   ::libaom_test::kTwoPassGood,
+#endif  // !CONFIG_SINGLEPASS
   ::libaom_test::kOnePassGood,
 };
 
diff --git a/test/quant_test.cc b/test/quant_test.cc
index 2ef3a76..3974284 100644
--- a/test/quant_test.cc
+++ b/test/quant_test.cc
@@ -49,8 +49,8 @@
     qm_min_ = qm_min;
     qm_max_ = qm_max;
     cfg_.kf_max_dist = 12;
-    cfg_.rc_min_quantizer = 8;
-    cfg_.rc_max_quantizer = 56;
+    cfg_.rc_min_quantizer = 32;
+    cfg_.rc_max_quantizer = 224;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 6;
     cfg_.rc_buf_initial_sz = 500;
@@ -87,7 +87,7 @@
 } QuantParam;
 
 const QuantParam QuantTestParams[] = {
-  { 0, 10 }, { 0, 60 }, { 20, 35 }, { 35, 50 }, { 50, 63 }
+  { 0, 50 }, { 0, 240 }, { 100, 150 }, { 150, 200 }, { 200, 255 }
 };
 
 std::ostream &operator<<(std::ostream &os, const QuantParam &test_arg) {
@@ -142,8 +142,8 @@
       aom_codec_ctx_t *ctx_dec = decoder->GetDecoder();
       AOM_CODEC_CONTROL_TYPECHECKED(ctx_dec, AOMD_GET_LAST_QUANTIZER,
                                     &base_qindex_);
-      min_bound_qindex_ = av1_quantizer_to_qindex(cfg_.rc_min_quantizer);
-      max_bound_qindex_ = av1_quantizer_to_qindex(cfg_.rc_max_quantizer);
+      min_bound_qindex_ = cfg_.rc_min_quantizer;
+      max_bound_qindex_ = cfg_.rc_max_quantizer;
       if ((base_qindex_ < min_bound_qindex_ ||
            base_qindex_ > max_bound_qindex_) &&
           quant_bound_violated_ == false) {
@@ -171,8 +171,7 @@
 }
 
 AV1_INSTANTIATE_TEST_SUITE(QuantizerBoundsCheckTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+                           NONREALTIME_TEST_MODES,
                            ::testing::ValuesIn(QuantTestParams),
                            ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ));
 }  // namespace
diff --git a/test/resize_test.cc b/test/resize_test.cc
index f4ad15f..41c0024 100644
--- a/test/resize_test.cc
+++ b/test/resize_test.cc
@@ -331,7 +331,7 @@
   change_config_ = false;
 
   // q picked such that initial keyframe on this clip is ~30dB PSNR
-  cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
+  cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 192;
 
   // If the number of frames being encoded is smaller than g_lag_in_frames
   // the encoded frame is unavailable using the current API. Comparing
@@ -431,8 +431,8 @@
     cfg_.rc_buf_initial_sz = 500;
     cfg_.rc_buf_optimal_sz = 600;
     cfg_.rc_buf_sz = 1000;
-    cfg_.rc_min_quantizer = 2;
-    cfg_.rc_max_quantizer = 56;
+    cfg_.rc_min_quantizer = 8;
+    cfg_.rc_max_quantizer = 224;
     cfg_.rc_undershoot_pct = 50;
     cfg_.rc_overshoot_pct = 50;
     cfg_.rc_end_usage = AOM_CBR;
@@ -775,7 +775,7 @@
   for (const aom_img_fmt_t &img_format : image_formats) {
     ResizingCspVideoSource video(img_format);
     init_flags_ = AOM_CODEC_USE_PSNR;
-    cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 48;
+    cfg_.rc_min_quantizer = cfg_.rc_max_quantizer = 192;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_profile = (img_format == AOM_IMG_FMT_I420) ? 0 : 1;
     ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
diff --git a/test/sb_multipass_test.cc b/test/sb_multipass_test.cc
index d2f56db..f5ea5b3 100644
--- a/test/sb_multipass_test.cc
+++ b/test/sb_multipass_test.cc
@@ -46,14 +46,20 @@
 
   virtual void SetUp() {
     InitializeConfig();
+#if CONFIG_SINGLEPASS
+    SetMode(::libaom_test::kOnePassGood);
+#else
     SetMode(::libaom_test::kTwoPassGood);
+#endif  // CONFIG_SINGLEPASS
 
     cfg_.g_lag_in_frames = 5;
     cfg_.rc_end_usage = AOM_VBR;
+#if !CONFIG_SINGLEPASS
     cfg_.rc_2pass_vbr_minsection_pct = 5;
     cfg_.rc_2pass_vbr_maxsection_pct = 2000;
+#endif  // !CONFIG_SINGLEPASS
 
-    cfg_.rc_max_quantizer = 56;
+    cfg_.rc_max_quantizer = 224;
     cfg_.rc_min_quantizer = 0;
   }
 
diff --git a/test/scalability_test.cc b/test/scalability_test.cc
index d38d8ab..24618b8 100644
--- a/test/scalability_test.cc
+++ b/test/scalability_test.cc
@@ -46,7 +46,7 @@
                      AOM_EFLAG_NO_UPD_LAST | AOM_EFLAG_NO_UPD_GF |
                      AOM_EFLAG_NO_UPD_ARF | AOM_EFLAG_NO_UPD_ENTROPY;
       encoder->Control(AOME_SET_SPATIAL_LAYER_ID, 1);
-      encoder->Control(AOME_SET_CQ_LEVEL, kEnhancementLayerQp);
+      encoder->Control(AOME_SET_QP, kEnhancementLayerQp);
     } else {
       frame_flags_ = AOM_EFLAG_NO_REF_LAST2 | AOM_EFLAG_NO_REF_LAST3 |
                      AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF |
@@ -54,7 +54,7 @@
                      AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF |
                      AOM_EFLAG_NO_UPD_ENTROPY;
       encoder->Control(AOME_SET_SPATIAL_LAYER_ID, 0);
-      encoder->Control(AOME_SET_CQ_LEVEL, kBaseLayerQp);
+      encoder->Control(AOME_SET_QP, kBaseLayerQp);
     }
   }
 
diff --git a/test/screen_content_test.cc b/test/screen_content_test.cc
index ecf2ef5..e050eda 100644
--- a/test/screen_content_test.cc
+++ b/test/screen_content_test.cc
@@ -107,8 +107,6 @@
   //      << "Failed detection of screen content(lowres)";
 }
 
-AV1_INSTANTIATE_TEST_SUITE(ScreenContentToolsTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+AV1_INSTANTIATE_TEST_SUITE(ScreenContentToolsTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::Values(AOM_Q));
 }  // namespace
diff --git a/test/still_picture_test.cc b/test/still_picture_test.cc
index dc88ef0..77d52c8 100644
--- a/test/still_picture_test.cc
+++ b/test/still_picture_test.cc
@@ -90,7 +90,5 @@
 }
 
 AV1_INSTANTIATE_TEST_SUITE(StillPicturePresenceTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
-                           ::testing::Values(1, 0));
+                           NONREALTIME_TEST_MODES, ::testing::Values(1, 0));
 }  // namespace
diff --git a/test/stream_iter_test.cc b/test/stream_iter_test.cc
new file mode 100644
index 0000000..f7376ac
--- /dev/null
+++ b/test/stream_iter_test.cc
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2020, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include <stdbool.h>
+#include "common/stream_iter.h"
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+
+namespace {
+
+// Dummy class that increments the width field in the image, which is used
+// as a placeholder for the frame number. Stops iterating after N frames.
+int dummy_reader(StreamIter *iter, aom_image_t *raw) {
+  if (iter->current >= iter->n) {
+    return 0;
+  }
+  ++iter->current;
+  raw->w = iter->current;
+  return 1;
+}
+
+void dummy_stream_iter_init(StreamIter *iter, int total) {
+  iter->current = 0;
+  iter->n = total;
+  iter->reader = dummy_reader;
+}
+
+class StreamIterTest : public ::testing::Test {};
+
+TEST_F(StreamIterTest, Skip0) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter skip;
+  skip_stream_iter_init(&skip, &input, 0);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&skip, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&skip, &img));
+  EXPECT_EQ(2U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&skip, &img));
+  EXPECT_EQ(3U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&skip, &img));
+}
+
+TEST_F(StreamIterTest, Skip1) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter skip;
+  skip_stream_iter_init(&skip, &input, 1);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&skip, &img));
+  EXPECT_EQ(2U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&skip, &img));
+  EXPECT_EQ(3U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&skip, &img));
+}
+
+TEST_F(StreamIterTest, Skip2) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter skip;
+  skip_stream_iter_init(&skip, &input, 2);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&skip, &img));
+  EXPECT_EQ(3U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&skip, &img));
+}
+
+TEST_F(StreamIterTest, Skip3) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter skip;
+  skip_stream_iter_init(&skip, &input, 3);
+
+  aom_image_t img;
+  EXPECT_FALSE(read_stream_iter(&skip, &img));
+}
+
+TEST_F(StreamIterTest, Skip4) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter skip;
+  skip_stream_iter_init(&skip, &input, 4);
+
+  aom_image_t img;
+  EXPECT_FALSE(read_stream_iter(&skip, &img));
+}
+
+// limit=0 is a special case meaning "no limit."
+TEST_F(StreamIterTest, Limit0) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter limit;
+  limit_stream_iter_init(&limit, &input, 0);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(2U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(3U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&limit, &img));
+}
+
+TEST_F(StreamIterTest, Limit1) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter limit;
+  limit_stream_iter_init(&limit, &input, 1);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&limit, &img));
+}
+
+TEST_F(StreamIterTest, Limit2) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter limit;
+  limit_stream_iter_init(&limit, &input, 2);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(2U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&limit, &img));
+}
+
+TEST_F(StreamIterTest, Limit3) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter limit;
+  limit_stream_iter_init(&limit, &input, 3);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(2U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(3U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&limit, &img));
+}
+
+TEST_F(StreamIterTest, Limit4) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter limit;
+  limit_stream_iter_init(&limit, &input, 4);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(2U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&limit, &img));
+  EXPECT_EQ(3U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&limit, &img));
+}
+
+TEST_F(StreamIterTest, Step1) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter step;
+  step_stream_iter_init(&step, &input, 1);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&step, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&step, &img));
+  EXPECT_EQ(2U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&step, &img));
+  EXPECT_EQ(3U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&step, &img));
+}
+
+TEST_F(StreamIterTest, Step2) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter step;
+  step_stream_iter_init(&step, &input, 2);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&step, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&step, &img));
+  EXPECT_EQ(3U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&step, &img));
+}
+
+TEST_F(StreamIterTest, Step3) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter step;
+  step_stream_iter_init(&step, &input, 3);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&step, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&step, &img));
+}
+
+TEST_F(StreamIterTest, Step4) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 3);
+
+  StreamIter step;
+  step_stream_iter_init(&step, &input, 4);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&step, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&step, &img));
+}
+
+// Test when the step size is an even divisor of the number of frames.
+TEST_F(StreamIterTest, Step5) {
+  StreamIter input;
+  dummy_stream_iter_init(&input, 10);
+
+  StreamIter step;
+  step_stream_iter_init(&step, &input, 5);
+
+  aom_image_t img;
+  EXPECT_TRUE(read_stream_iter(&step, &img));
+  EXPECT_EQ(1U, img.w);
+
+  EXPECT_TRUE(read_stream_iter(&step, &img));
+  EXPECT_EQ(6U, img.w);
+
+  EXPECT_FALSE(read_stream_iter(&step, &img));
+}
+
+}  // namespace
diff --git a/test/svc_datarate_test.cc b/test/svc_datarate_test.cc
index 774593c..f91f1b8 100644
--- a/test/svc_datarate_test.cc
+++ b/test/svc_datarate_test.cc
@@ -384,8 +384,8 @@
     svc_params->number_spatial_layers = number_spatial_layers;
     svc_params->number_temporal_layers = number_temporal_layers;
     for (int i = 0; i < number_temporal_layers * number_spatial_layers; ++i) {
-      svc_params->max_quantizers[i] = 60;
-      svc_params->min_quantizers[i] = 2;
+      svc_params->max_quantizers[i] = 240;
+      svc_params->min_quantizers[i] = 8;
       svc_params->layer_target_bitrate[i] = target_layer_bitrate_[i];
     }
     // Do at most 3 spatial or temporal layers here.
@@ -421,7 +421,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_error_resilient = 1;
@@ -450,7 +450,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_error_resilient = 1;
@@ -498,7 +498,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_error_resilient = 1;
@@ -527,7 +527,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_error_resilient = 1;
@@ -557,7 +557,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_error_resilient = 1;
@@ -599,7 +599,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_error_resilient = 1;
@@ -640,7 +640,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_error_resilient = 1;
@@ -684,7 +684,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     cfg_.g_error_resilient = 1;
@@ -773,7 +773,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     // error_resilient can set to off/0, since for SVC the context update
@@ -818,7 +818,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
     // error_resilient for sequence can be off/0, since dropped frames (TL2)
@@ -863,7 +863,7 @@
     cfg_.rc_buf_sz = 1000;
     cfg_.rc_dropframe_thresh = 0;
     cfg_.rc_min_quantizer = 0;
-    cfg_.rc_max_quantizer = 63;
+    cfg_.rc_max_quantizer = 255;
     cfg_.rc_end_usage = AOM_CBR;
     cfg_.g_lag_in_frames = 0;
 
diff --git a/test/test.cmake b/test/test.cmake
index eb245cb..43a3deb 100644
--- a/test/test.cmake
+++ b/test/test.cmake
@@ -220,6 +220,7 @@
               "${AOM_ROOT}/test/pickrst_test.cc"
               "${AOM_ROOT}/test/quantize_func_test.cc"
               "${AOM_ROOT}/test/sad_test.cc"
+              "${AOM_ROOT}/test/stream_iter_test.cc"
               "${AOM_ROOT}/test/subtract_test.cc"
               "${AOM_ROOT}/test/reconinter_test.cc"
               "${AOM_ROOT}/test/sum_squares_test.cc"
diff --git a/test/tile_config_test.cc b/test/tile_config_test.cc
index 8c92c14..0508331 100644
--- a/test/tile_config_test.cc
+++ b/test/tile_config_test.cc
@@ -255,15 +255,12 @@
   ASSERT_EQ(tile_config_violated_, false);
 }
 
-AV1_INSTANTIATE_TEST_SUITE(UniformTileConfigTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+AV1_INSTANTIATE_TEST_SUITE(UniformTileConfigTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::ValuesIn(uniformTileConfigParams),
                            ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ));
 
 AV1_INSTANTIATE_TEST_SUITE(NonUniformTileConfigTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+                           NONREALTIME_TEST_MODES,
                            ::testing::ValuesIn(nonUniformTileConfigParams),
                            ::testing::Values(AOM_Q, AOM_VBR, AOM_CBR, AOM_CQ));
 
@@ -354,8 +351,6 @@
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
 }
 
-AV1_INSTANTIATE_TEST_SUITE(TileGroupTestLarge,
-                           ::testing::Values(::libaom_test::kOnePassGood,
-                                             ::libaom_test::kTwoPassGood),
+AV1_INSTANTIATE_TEST_SUITE(TileGroupTestLarge, NONREALTIME_TEST_MODES,
                            ::testing::ValuesIn(tileGroupTestParams));
 }  // namespace
diff --git a/test/tile_independence_test.cc b/test/tile_independence_test.cc
index 5f167be..559a2ce 100644
--- a/test/tile_independence_test.cc
+++ b/test/tile_independence_test.cc
@@ -54,7 +54,11 @@
 
   virtual void SetUp() {
     InitializeConfig();
+#if CONFIG_SINGLEPASS
+    SetMode(libaom_test::kOnePassGood);
+#else
     SetMode(libaom_test::kTwoPassGood);
+#endif  // CONFIG_SINGLEPASS
   }
 
   virtual void PreEncodeFrameHook(libaom_test::VideoSource *video,
diff --git a/test/time_stamp_test.cc b/test/time_stamp_test.cc
index 3e2fa97..3e91bee 100644
--- a/test/time_stamp_test.cc
+++ b/test/time_stamp_test.cc
@@ -99,7 +99,12 @@
   ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
 }
 
+#if CONFIG_SINGLEPASS
+AV1_INSTANTIATE_TEST_SUITE(TimestampTest,
+                           ::testing::Values(::libaom_test::kOnePassGood));
+#else
 AV1_INSTANTIATE_TEST_SUITE(TimestampTest,
                            ::testing::Values(::libaom_test::kTwoPassGood));
+#endif  // CONFIG_SINGLEPASS
 
 }  // namespace
diff --git a/test/webmenc_test.cc b/test/webmenc_test.cc
index acd795f..c028f22 100644
--- a/test/webmenc_test.cc
+++ b/test/webmenc_test.cc
@@ -40,10 +40,10 @@
 }
 
 TEST(WebmencTest, ExtractEncoderSettingsOutput3) {
-  const char *argv[] = { "aomenc", "--cq-level=63", "--end-usage=q",
-                         "--output=foo", "baz" };
+  const char *argv[] = { "aomenc", "--qp=255", "--end-usage=q", "--output=foo",
+                         "baz" };
   int argc = 5;
-  const std::string expected("version:23 --cq-level=63 --end-usage=q");
+  const std::string expected("version:23 --qp=255 --end-usage=q");
   char *result = extract_encoder_settings("23", argv, argc, "baz");
   ASSERT_EQ(expected, std::string(result));
   free(result);
diff --git a/third_party/tensorflow b/third_party/tensorflow
new file mode 160000
index 0000000..18445b0
--- /dev/null
+++ b/third_party/tensorflow
@@ -0,0 +1 @@
+Subproject commit 18445b0e39b677a21c86b4cf3d2bcb823f27e3e2
diff --git a/third_party/tensorflow_dependencies/absl b/third_party/tensorflow_dependencies/absl
new file mode 160000
index 0000000..3c2bed2
--- /dev/null
+++ b/third_party/tensorflow_dependencies/absl
@@ -0,0 +1 @@
+Subproject commit 3c2bed2e77a8b77ce3092e3f75140dd21879bdba
diff --git a/third_party/tensorflow_dependencies/build.pl b/third_party/tensorflow_dependencies/build.pl
new file mode 100755
index 0000000..dcf14e5
--- /dev/null
+++ b/third_party/tensorflow_dependencies/build.pl
@@ -0,0 +1,160 @@
+#!/usr/bin/env perl
+#
+# Copyright (c) 2020, Alliance for Open Media. All rights reserved
+#
+# This source code is subject to the terms of the BSD 2 Clause License and
+# the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+# was not distributed with this source code in the LICENSE file, you can
+# obtain it at www.aomedia.org/license/software. If the Alliance for Open
+# Media Patent License 1.0 was not distributed with this source code in the
+# PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+#
+###########################################################################
+#
+# Script to build the static TensorFlow lite library.
+#
+# Tensorflow's build process generates the static libraries in the same
+# directory as the source code. AOM, however, generates binaries/objects/etc.
+# in a different directory. This script:
+#
+# 1.) Copies the TensorFlow code to a temporary directory
+# 2.) Copies the necessary dependencies into the temporary directory
+# 3.) Compiles it
+# 4.) Copies the static library to the AOM build directory
+#
+# Note that we do not use the download_dependencies.sh script directly, as
+# it downloads directly into the source directory.
+
+use strict;
+use warnings;
+use autodie;
+use Cwd;
+use File::Basename;
+use File::Copy;
+use File::Spec::Functions;
+use File::Temp;
+
+# Apply a tweak to Eigen (TF Lite's "download_dependencies.sh"
+# performs 3 tweaks, but only 1 of them is relevant).
+sub apply_eigen_tweak {
+    my $downloads_dir = $_[0];
+    my $file = catfile($downloads_dir, "eigen", "Eigen", "src", "Core",
+                       "arch", "NEON", "Complex.h");
+    open(my $fh, '<', $file);
+    my @lines = <$fh>;
+    close($fh);
+    open($fh, '>', $file);
+    foreach my $line (@lines) {
+        $line =~ s/static uint64x2_t p2ul_CONJ_XOR = vld1q_u64\( p2ul_conj_XOR_DATA \);/static uint64x2_t p2ul_CONJ_XOR;/;
+        print $fh $line;
+    }
+    close($fh);
+}
+
+# Pure Perl implementation of recursive directory-copy -
+# File::Copy::Recursive is not part of core.
+sub recursive_dircopy {
+    my $input_dir = $_[0];
+    my $output_dir = $_[1];
+    # If the output directory does not exist, create it.
+    if (!(-d $output_dir)) {
+        mkdir $output_dir;
+    }
+    opendir(my $dh, $input_dir);
+    while (my $file = readdir($dh)) {
+        # Ignore . and .. entries.
+        next if ($file eq "." or $file eq "..");
+        my $in_file = catfile($input_dir, $file);
+        my $out_file = catfile($output_dir, $file);
+        # If it is a directory, recursively call.
+        if (-d $in_file) {
+            recursive_dircopy($in_file, $out_file);
+        } else {
+          # If a file, copy over.
+          copy($in_file, $out_file);
+          # Preserve execute permission.
+          if (-x $in_file) {
+            chmod 0755, $out_file;
+          }
+        }
+    }
+    closedir($dh);
+}
+
+# Finds the first instance of the file in the directory, using a depth-first
+# search. Returns "" if unable to find.
+sub find_file {
+  my $source_dir = $_[0];
+  my $fname = $_[1];
+  opendir(my $dh, $source_dir);
+  while (my $file = readdir($dh)) {
+    # Ignore . and .. entries.
+    next if ($file eq "." or $file eq "..");
+    if (-d catfile($source_dir, $file)) {
+      my $result = find_file(catfile($source_dir, $file), $fname);
+      if ($result ne "") {
+        closedir($dh);
+        return $result;
+      }
+    } elsif ($file eq $fname) {
+      closedir($dh);
+      return catfile($source_dir, $file);
+    }
+  }
+  closedir($dh);
+  return "";
+}
+
+sub copy_tensorflow_lite_dependencies {
+    my $source_dir = $_[0];
+    my $output_dir = $_[1];
+    my $dependencies_dir = catfile($source_dir, "third_party",
+                                   "tensorflow_dependencies");
+    opendir(my $dh, $dependencies_dir);
+    while (my $file = readdir($dh)) {
+        # Ignore . and .. entries.
+        next if ($file eq "." or $file eq "..");
+        # Ignore non-directories.
+        next unless (-d catfile($dependencies_dir, $file));
+        # Copy the directory.
+        print "  * $file\n";
+        recursive_dircopy(
+          catfile($dependencies_dir, $file),
+          catfile($output_dir, $file));
+    }
+    closedir($dh);
+}
+
+# Start of program logic.
+if ($#ARGV + 1 != 2) {
+    my $prog = basename($0);
+    warn("Usage: $prog <source directory> <output directory>\n");
+    exit(1);
+}
+my ($source_dir, $output_dir) = @ARGV;
+
+my $temp_dir = File::Temp::tempdir( CLEANUP => 1 );
+
+print "Copying TensorFlow code to temporary directory for building...\n";
+my $tf_dir = catfile($source_dir, "third_party", "tensorflow");
+recursive_dircopy($tf_dir, $temp_dir);
+
+my $downloads_dir = catfile($temp_dir, "tensorflow", "lite", "tools",
+                            "make", "downloads");
+mkdir $downloads_dir;
+print "Copying TensorFlow Lite dependencies to temporary directory...\n";
+copy_tensorflow_lite_dependencies($source_dir, $downloads_dir);
+apply_eigen_tweak($downloads_dir);
+
+print "Building TensorFlow Lite...\n";
+my $build_sh = catfile($temp_dir, "tensorflow", "lite", "tools", "make",
+                       "build_lib.sh");
+`$build_sh`;
+# Find the libtensorflow-lite.a file, which is generated under
+# tensorflow/lite/tools/make/gen (but in a different sub-directory depending
+# on the architecture).
+my $gen_dir = catfile($temp_dir, "tensorflow", "lite", "tools", "make", "gen");
+my $liblite = find_file($gen_dir, "libtensorflow-lite.a");
+$liblite ne "" or die("Unable to find libtensorflow-lite.a");
+print "Copying static library into build directory...\n";
+copy($liblite, $output_dir);
diff --git a/third_party/tensorflow_dependencies/eigen b/third_party/tensorflow_dependencies/eigen
new file mode 160000
index 0000000..b11f817
--- /dev/null
+++ b/third_party/tensorflow_dependencies/eigen
@@ -0,0 +1 @@
+Subproject commit b11f817bcff04276f3024d6780f56a137968b81a
diff --git a/third_party/tensorflow_dependencies/farmhash b/third_party/tensorflow_dependencies/farmhash
new file mode 160000
index 0000000..0d859a8
--- /dev/null
+++ b/third_party/tensorflow_dependencies/farmhash
@@ -0,0 +1 @@
+Subproject commit 0d859a811870d10f53a594927d0d0b97573ad06d
diff --git a/third_party/tensorflow_dependencies/fft2d/alloc.c b/third_party/tensorflow_dependencies/fft2d/alloc.c
new file mode 100644
index 0000000..7833d88
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/alloc.c
@@ -0,0 +1,153 @@
+/* ---- memory allocation ---- */
+#include "alloc.h"
+
+
+#define alloc_error_check(p) { \
+    if ((p) == NULL) { \
+        fprintf(stderr, "Allocation Failure!\n"); \
+        exit(1); \
+    } \
+}
+
+
+int *alloc_1d_int(int n1)
+{
+    int *i;
+    
+    i = (int *) malloc(sizeof(int) * n1);
+    alloc_error_check(i);
+    return i;
+}
+
+
+void free_1d_int(int *i)
+{
+    free(i);
+}
+
+
+double *alloc_1d_double(int n1)
+{
+    double *d;
+    
+    d = (double *) malloc(sizeof(double) * n1);
+    alloc_error_check(d);
+    return d;
+}
+
+
+void free_1d_double(double *d)
+{
+    free(d);
+}
+
+
+int **alloc_2d_int(int n1, int n2)
+{
+    int **ii, *i;
+    int j;
+    
+    ii = (int **) malloc(sizeof(int *) * n1);
+    alloc_error_check(ii);
+    i = (int *) malloc(sizeof(int) * n1 * n2);
+    alloc_error_check(i);
+    ii[0] = i;
+    for (j = 1; j < n1; j++) {
+        ii[j] = ii[j - 1] + n2;
+    }
+    return ii;
+}
+
+
+void free_2d_int(int **ii)
+{
+    free(ii[0]);
+    free(ii);
+}
+
+
+double **alloc_2d_double(int n1, int n2)
+{
+    double **dd, *d;
+    int j;
+    
+    dd = (double **) malloc(sizeof(double *) * n1);
+    alloc_error_check(dd);
+    d = (double *) malloc(sizeof(double) * n1 * n2);
+    alloc_error_check(d);
+    dd[0] = d;
+    for (j = 1; j < n1; j++) {
+        dd[j] = dd[j - 1] + n2;
+    }
+    return dd;
+}
+
+
+void free_2d_double(double **dd)
+{
+    free(dd[0]);
+    free(dd);
+}
+
+
+int ***alloc_3d_int(int n1, int n2, int n3)
+{
+    int ***iii, **ii, *i;
+    int j;
+    
+    iii = (int ***) malloc(sizeof(int **) * n1);
+    alloc_error_check(iii);
+    ii = (int **) malloc(sizeof(int *) * n1 * n2);
+    alloc_error_check(ii);
+    iii[0] = ii;
+    for (j = 1; j < n1; j++) {
+        iii[j] = iii[j - 1] + n2;
+    }
+    i = (int *) malloc(sizeof(int) * n1 * n2 * n3);
+    alloc_error_check(i);
+    ii[0] = i;
+    for (j = 1; j < n1 * n2; j++) {
+        ii[j] = ii[j - 1] + n3;
+    }
+    return iii;
+}
+
+
+void free_3d_int(int ***iii)
+{
+    free(iii[0][0]);
+    free(iii[0]);
+    free(iii);
+}
+
+
+double ***alloc_3d_double(int n1, int n2, int n3)
+{
+    double ***ddd, **dd, *d;
+    int j;
+    
+    ddd = (double ***) malloc(sizeof(double **) * n1);
+    alloc_error_check(ddd);
+    dd = (double **) malloc(sizeof(double *) * n1 * n2);
+    alloc_error_check(dd);
+    ddd[0] = dd;
+    for (j = 1; j < n1; j++) {
+        ddd[j] = ddd[j - 1] + n2;
+    }
+    d = (double *) malloc(sizeof(double) * n1 * n2 * n3);
+    alloc_error_check(d);
+    dd[0] = d;
+    for (j = 1; j < n1 * n2; j++) {
+        dd[j] = dd[j - 1] + n3;
+    }
+    return ddd;
+}
+
+
+void free_3d_double(double ***ddd)
+{
+    free(ddd[0][0]);
+    free(ddd[0]);
+    free(ddd);
+}
+
diff --git a/third_party/tensorflow_dependencies/fft2d/alloc.h b/third_party/tensorflow_dependencies/fft2d/alloc.h
new file mode 100644
index 0000000..3467cc4
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/alloc.h
@@ -0,0 +1,20 @@
+/* ---- memory allocation ---- */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+int *alloc_1d_int(int n1);
+void free_1d_int(int *i);
+double *alloc_1d_double(int n1);
+void free_1d_double(double *d);
+int **alloc_2d_int(int n1, int n2);
+void free_2d_int(int **ii);
+double **alloc_2d_double(int n1, int n2);
+void free_2d_double(double **dd);
+int ***alloc_3d_int(int n1, int n2, int n3);
+void free_3d_int(int ***iii);
+double ***alloc_3d_double(int n1, int n2, int n3);
+void free_3d_double(double ***ddd);
+
diff --git a/third_party/tensorflow_dependencies/fft2d/fft4f2d.c b/third_party/tensorflow_dependencies/fft2d/fft4f2d.c
new file mode 100644
index 0000000..354c80e
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/fft4f2d.c
@@ -0,0 +1,1705 @@
+/*
+Fast Fourier/Cosine/Sine Transform
+    dimension   :two
+    data length :power of 2
+    decimation  :frequency
+    radix       :4, 2, row-column
+    data        :inplace
+    table       :use
+functions
+    cdft2d: Complex Discrete Fourier Transform
+    rdft2d: Real Discrete Fourier Transform
+    ddct2d: Discrete Cosine Transform
+    ddst2d: Discrete Sine Transform
+function prototypes
+    void cdft2d(int, int, int, double **, int *, double *);
+    void rdft2d(int, int, int, double **, int *, double *);
+    void ddct2d(int, int, int, double **, double **, int *, double *);
+    void ddst2d(int, int, int, double **, double **, int *, double *);
+
+
+-------- Complex DFT (Discrete Fourier Transform) --------
+    [definition]
+        <case1>
+            X[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 x[j1][j2] * 
+                            exp(2*pi*i*j1*k1/n1) * 
+                            exp(2*pi*i*j2*k2/n2), 0<=k1<n1, 0<=k2<n2
+        <case2>
+            X[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 x[j1][j2] * 
+                            exp(-2*pi*i*j1*k1/n1) * 
+                            exp(-2*pi*i*j2*k2/n2), 0<=k1<n1, 0<=k2<n2
+        (notes: sum_j=0^n-1 is a summation from j=0 to n-1)
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            cdft2d(n1, 2*n2, 1, a, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            cdft2d(n1, 2*n2, -1, a, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 1, n1 = power of 2
+        2*n2   :data length (int)
+                n2 >= 1, n2 = power of 2
+        a[0...n1-1][0...2*n2-1]
+               :input/output data (double **)
+                input data
+                    a[j1][2*j2] = Re(x[j1][j2]), 
+                    a[j1][2*j2+1] = Im(x[j1][j2]), 
+                    0<=j1<n1, 0<=j2<n2
+                output data
+                    a[k1][2*k2] = Re(X[k1][k2]), 
+                    a[k1][2*k2+1] = Im(X[k1][k2]), 
+                    0<=k1<n1, 0<=k2<n2
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1, n2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1/2, n2/2)
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            cdft2d(n1, 2*n2, -1, a, ip, w);
+        is 
+            cdft2d(n1, 2*n2, 1, a, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= 2 * n2 - 1; j2++) {
+                    a[j1][j2] *= 1.0 / (n1 * n2);
+                }
+            }
+        .
+
+
+-------- Real DFT / Inverse of Real DFT --------
+    [definition]
+        <case1> RDFT
+            R[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2), 
+                            0<=k1<n1, 0<=k2<n2
+            I[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2), 
+                            0<=k1<n1, 0<=k2<n2
+        <case2> IRDFT (excluding scale)
+            a[k1][k2] = (1/2) * sum_j1=0^n1-1 sum_j2=0^n2-1
+                            (R[j1][j2] * 
+                            cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2) + 
+                            I[j1][j2] * 
+                            sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2)), 
+                            0<=k1<n1, 0<=k2<n2
+        (notes: R[n1-k1][n2-k2] = R[k1][k2], 
+                I[n1-k1][n2-k2] = -I[k1][k2], 
+                R[n1-k1][0] = R[k1][0], 
+                I[n1-k1][0] = -I[k1][0], 
+                R[0][n2-k2] = R[0][k2], 
+                I[0][n2-k2] = -I[0][k2], 
+                0<k1<n1, 0<k2<n2)
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            rdft2d(n1, n2, 1, a, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            rdft2d(n1, n2, -1, a, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 2, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 2, n2 = power of 2
+        a[0...n1-1][0...n2-1]
+               :input/output data (double **)
+                <case1>
+                    output data
+                        a[k1][2*k2] = R[k1][k2] = R[n1-k1][n2-k2], 
+                        a[k1][2*k2+1] = I[k1][k2] = -I[n1-k1][n2-k2], 
+                            0<k1<n1, 0<k2<n2/2, 
+                        a[0][2*k2] = R[0][k2] = R[0][n2-k2], 
+                        a[0][2*k2+1] = I[0][k2] = -I[0][n2-k2], 
+                            0<k2<n2/2, 
+                        a[k1][0] = R[k1][0] = R[n1-k1][0], 
+                        a[k1][1] = I[k1][0] = -I[n1-k1][0], 
+                        a[n1-k1][1] = R[k1][n2/2] = R[n1-k1][n2/2], 
+                        a[n1-k1][0] = -I[k1][n2/2] = I[n1-k1][n2/2], 
+                            0<k1<n1/2, 
+                        a[0][0] = R[0][0], 
+                        a[0][1] = R[0][n2/2], 
+                        a[n1/2][0] = R[n1/2][0], 
+                        a[n1/2][1] = R[n1/2][n2/2]
+                <case2>
+                    input data
+                        a[j1][2*j2] = R[j1][j2] = R[n1-j1][n2-j2], 
+                        a[j1][2*j2+1] = I[j1][j2] = -I[n1-j1][n2-j2], 
+                            0<j1<n1, 0<j2<n2/2, 
+                        a[0][2*j2] = R[0][j2] = R[0][n2-j2], 
+                        a[0][2*j2+1] = I[0][j2] = -I[0][n2-j2], 
+                            0<j2<n2/2, 
+                        a[j1][0] = R[j1][0] = R[n1-j1][0], 
+                        a[j1][1] = I[j1][0] = -I[n1-j1][0], 
+                        a[n1-j1][1] = R[j1][n2/2] = R[n1-j1][n2/2], 
+                        a[n1-j1][0] = -I[j1][n2/2] = I[n1-j1][n2/2], 
+                            0<j1<n1/2, 
+                        a[0][0] = R[0][0], 
+                        a[0][1] = R[0][n2/2], 
+                        a[n1/2][0] = R[n1/2][0], 
+                        a[n1/2][1] = R[n1/2][n2/2]
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1, n2/2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1/2, n2/4) + n2/4
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            rdft2d(n1, n2, 1, a, ip, w);
+        is 
+            rdft2d(n1, n2, -1, a, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    a[j1][j2] *= 2.0 / (n1 * n2);
+                }
+            }
+        .
+
+
+-------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
+    [definition]
+        <case1> IDCT (excluding scale)
+            C[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            cos(pi*j1*(k1+1/2)/n1) * 
+                            cos(pi*j2*(k2+1/2)/n2), 
+                            0<=k1<n1, 0<=k2<n2
+        <case2> DCT
+            C[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            cos(pi*(j1+1/2)*k1/n1) * 
+                            cos(pi*(j2+1/2)*k2/n2), 
+                            0<=k1<n1, 0<=k2<n2
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            ddct2d(n1, n2, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            ddct2d(n1, n2, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 2, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 2, n2 = power of 2
+        a[0...n1-1][0...n2-1]
+               :input/output data (double **)
+                output data
+                    a[k1][k2] = C[k1][k2], 0<=k1<n1, 0<=k2<n2
+        t[0...n1-1][0...n2-1]
+               :work area (double **)
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1, n2/2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1/2, n2/4) + max(n1, n2)
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            ddct2d(n1, n2, -1, a, t, ip, w);
+        is 
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                a[j1][0] *= 0.5;
+            }
+            for (j2 = 0; j2 <= n2 - 1; j2++) {
+                a[0][j2] *= 0.5;
+            }
+            ddct2d(n1, n2, 1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    a[j1][j2] *= 4.0 / (n1 * n2);
+                }
+            }
+        .
+
+
+-------- DST (Discrete Sine Transform) / Inverse of DST --------
+    [definition]
+        <case1> IDST (excluding scale)
+            S[k1][k2] = sum_j1=1^n1 sum_j2=1^n2 A[j1][j2] * 
+                            sin(pi*j1*(k1+1/2)/n1) * 
+                            sin(pi*j2*(k2+1/2)/n2), 
+                            0<=k1<n1, 0<=k2<n2
+        <case2> DST
+            S[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            sin(pi*(j1+1/2)*k1/n1) * 
+                            sin(pi*(j2+1/2)*k2/n2), 
+                            0<k1<=n1, 0<k2<=n2
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            ddst2d(n1, n2, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            ddst2d(n1, n2, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 2, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 2, n2 = power of 2
+        a[0...n1-1][0...n2-1]
+               :input/output data (double **)
+                <case1>
+                    input data
+                        a[j1][j2] = A[j1][j2], 0<j1<n1, 0<j2<n2, 
+                        a[j1][0] = A[j1][n2], 0<j1<n1, 
+                        a[0][j2] = A[n1][j2], 0<j2<n2, 
+                        a[0][0] = A[n1][n2]
+                        (i.e. A[j1][j2] = a[j1 % n1][j2 % n2])
+                    output data
+                        a[k1][k2] = S[k1][k2], 0<=k1<n1, 0<=k2<n2
+                <case2>
+                    output data
+                        a[k1][k2] = S[k1][k2], 0<k1<n1, 0<k2<n2, 
+                        a[k1][0] = S[k1][n2], 0<k1<n1, 
+                        a[0][k2] = S[n1][k2], 0<k2<n2, 
+                        a[0][0] = S[n1][n2]
+                        (i.e. S[k1][k2] = a[k1 % n1][k2 % n2])
+        t[0...n1-1][0...n2-1]
+               :work area (double **)
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1, n2/2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1/2, n2/4) + max(n1, n2)
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            ddst2d(n1, n2, -1, a, t, ip, w);
+        is 
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                a[j1][0] *= 0.5;
+            }
+            for (j2 = 0; j2 <= n2 - 1; j2++) {
+                a[0][j2] *= 0.5;
+            }
+            ddst2d(n1, n2, 1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    a[j1][j2] *= 4.0 / (n1 * n2);
+                }
+            }
+        .
+*/
+
+
+void cdft2d(int n1, int n2, int isgn, double **a, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void bitrv2col(int n1, int n, int *ip, double **a);
+    void bitrv2row(int n, int n2, int *ip, double **a);
+    void cftbcol(int n1, int n, double **a, double *w);
+    void cftbrow(int n, int n2, double **a, double *w);
+    void cftfcol(int n1, int n, double **a, double *w);
+    void cftfrow(int n, int n2, double **a, double *w);
+    int n;
+    
+    n = n1 << 1;
+    if (n < n2) {
+        n = n2;
+    }
+    if (n > (ip[0] << 2)) {
+        makewt(n >> 2, ip, w);
+    }
+    if (n2 > 4) {
+        bitrv2col(n1, n2, ip + 2, a);
+    }
+    if (n1 > 2) {
+        bitrv2row(n1, n2, ip + 2, a);
+    }
+    if (isgn < 0) {
+        cftfcol(n1, n2, a, w);
+        cftfrow(n1, n2, a, w);
+    } else {
+        cftbcol(n1, n2, a, w);
+        cftbrow(n1, n2, a, w);
+    }
+}
+
+
+void rdft2d(int n1, int n2, int isgn, double **a, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void bitrv2col(int n1, int n, int *ip, double **a);
+    void bitrv2row(int n, int n2, int *ip, double **a);
+    void cftbcol(int n1, int n, double **a, double *w);
+    void cftbrow(int n, int n2, double **a, double *w);
+    void cftfcol(int n1, int n, double **a, double *w);
+    void cftfrow(int n, int n2, double **a, double *w);
+    void rftbcol(int n1, int n, double **a, int nc, double *c);
+    void rftfcol(int n1, int n, double **a, int nc, double *c);
+    int n, nw, nc, n1h, i, j;
+    double xi;
+    
+    n = n1 << 1;
+    if (n < n2) {
+        n = n2;
+    }
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n2 > (nc << 2)) {
+        nc = n2 >> 2;
+        makect(nc, ip, w + nw);
+    }
+    n1h = n1 >> 1;
+    if (isgn < 0) {
+        for (i = 1; i <= n1h - 1; i++) {
+            j = n1 - i;
+            xi = a[i][0] - a[j][0];
+            a[i][0] += a[j][0];
+            a[j][0] = xi;
+            xi = a[j][1] - a[i][1];
+            a[i][1] += a[j][1];
+            a[j][1] = xi;
+        }
+        if (n1 > 2) {
+            bitrv2row(n1, n2, ip + 2, a);
+        }
+        cftfrow(n1, n2, a, w);
+        for (i = 0; i <= n1 - 1; i++) {
+            a[i][1] = 0.5 * (a[i][0] - a[i][1]);
+            a[i][0] -= a[i][1];
+        }
+        if (n2 > 4) {
+            rftfcol(n1, n2, a, nc, w + nw);
+            bitrv2col(n1, n2, ip + 2, a);
+        }
+        cftfcol(n1, n2, a, w);
+    } else {
+        if (n2 > 4) {
+            bitrv2col(n1, n2, ip + 2, a);
+        }
+        cftbcol(n1, n2, a, w);
+        if (n2 > 4) {
+            rftbcol(n1, n2, a, nc, w + nw);
+        }
+        for (i = 0; i <= n1 - 1; i++) {
+            xi = a[i][0] - a[i][1];
+            a[i][0] += a[i][1];
+            a[i][1] = xi;
+        }
+        if (n1 > 2) {
+            bitrv2row(n1, n2, ip + 2, a);
+        }
+        cftbrow(n1, n2, a, w);
+        for (i = 1; i <= n1h - 1; i++) {
+            j = n1 - i;
+            a[j][0] = 0.5 * (a[i][0] - a[j][0]);
+            a[i][0] -= a[j][0];
+            a[j][1] = 0.5 * (a[i][1] + a[j][1]);
+            a[i][1] -= a[j][1];
+        }
+    }
+}
+
+
+void ddct2d(int n1, int n2, int isgn, double **a, double **t, 
+    int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void bitrv2col(int n1, int n, int *ip, double **a);
+    void bitrv2row(int n, int n2, int *ip, double **a);
+    void cftbcol(int n1, int n, double **a, double *w);
+    void cftbrow(int n, int n2, double **a, double *w);
+    void cftfcol(int n1, int n, double **a, double *w);
+    void cftfrow(int n, int n2, double **a, double *w);
+    void rftbcol(int n1, int n, double **a, int nc, double *c);
+    void rftfcol(int n1, int n, double **a, int nc, double *c);
+    void dctbsub(int n1, int n2, double **a, int nc, double *c);
+    void dctfsub(int n1, int n2, double **a, int nc, double *c);
+    int n, nw, nc, n1h, n2h, i, ix, ic, j, jx, jc;
+    double xi;
+    
+    n = n1 << 1;
+    if (n < n2) {
+        n = n2;
+    }
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n1 > nc || n2 > nc) {
+        if (n1 > n2) {
+            nc = n1;
+        } else {
+            nc = n2;
+        }
+        makect(nc, ip, w + nw);
+    }
+    n1h = n1 >> 1;
+    n2h = n2 >> 1;
+    if (isgn >= 0) {
+        for (i = 0; i <= n1 - 1; i++) {
+            for (j = 1; j <= n2h - 1; j++) {
+                jx = j << 1;
+                t[i][jx] = a[i][j];
+                t[i][jx + 1] = a[i][n2 - j];
+            }
+        }
+        t[0][0] = a[0][0];
+        t[0][1] = a[0][n2h];
+        t[n1h][0] = a[n1h][0];
+        t[n1h][1] = a[n1h][n2h];
+        for (i = 1; i <= n1h - 1; i++) {
+            ic = n1 - i;
+            t[i][0] = a[i][0];
+            t[ic][1] = a[i][n2h];
+            t[i][1] = a[ic][0];
+            t[ic][0] = a[ic][n2h];
+        }
+        dctfsub(n1, n2, t, nc, w + nw);
+        if (n1 > 2) {
+            bitrv2row(n1, n2, ip + 2, t);
+        }
+        cftfrow(n1, n2, t, w);
+        for (i = 0; i <= n1 - 1; i++) {
+            t[i][1] = 0.5 * (t[i][0] - t[i][1]);
+            t[i][0] -= t[i][1];
+        }
+        if (n2 > 4) {
+            rftfcol(n1, n2, t, nc, w + nw);
+            bitrv2col(n1, n2, ip + 2, t);
+        }
+        cftfcol(n1, n2, t, w);
+        for (i = 0; i <= n1h - 1; i++) {
+            ix = i << 1;
+            ic = n1 - 1 - i;
+            for (j = 0; j <= n2h - 1; j++) {
+                jx = j << 1;
+                jc = n2 - 1 - j;
+                a[ix][jx] = t[i][j];
+                a[ix][jx + 1] = t[i][jc];
+                a[ix + 1][jx] = t[ic][j];
+                a[ix + 1][jx + 1] = t[ic][jc];
+            }
+        }
+    } else {
+        for (i = 0; i <= n1h - 1; i++) {
+            ix = i << 1;
+            ic = n1 - 1 - i;
+            for (j = 0; j <= n2h - 1; j++) {
+                jx = j << 1;
+                jc = n2 - 1 - j;
+                t[i][j] = a[ix][jx];
+                t[i][jc] = a[ix][jx + 1];
+                t[ic][j] = a[ix + 1][jx];
+                t[ic][jc] = a[ix + 1][jx + 1];
+            }
+        }
+        if (n2 > 4) {
+            bitrv2col(n1, n2, ip + 2, t);
+        }
+        cftbcol(n1, n2, t, w);
+        if (n2 > 4) {
+            rftbcol(n1, n2, t, nc, w + nw);
+        }
+        for (i = 0; i <= n1 - 1; i++) {
+            xi = t[i][0] - t[i][1];
+            t[i][0] += t[i][1];
+            t[i][1] = xi;
+        }
+        if (n1 > 2) {
+            bitrv2row(n1, n2, ip + 2, t);
+        }
+        cftbrow(n1, n2, t, w);
+        dctbsub(n1, n2, t, nc, w + nw);
+        for (i = 0; i <= n1 - 1; i++) {
+            for (j = 1; j <= n2h - 1; j++) {
+                jx = j << 1;
+                a[i][j] = t[i][jx];
+                a[i][n2 - j] = t[i][jx + 1];
+            }
+        }
+        a[0][0] = t[0][0];
+        a[0][n2h] = t[0][1];
+        a[n1h][0] = t[n1h][0];
+        a[n1h][n2h] = t[n1h][1];
+        for (i = 1; i <= n1h - 1; i++) {
+            ic = n1 - i;
+            a[i][0] = t[i][0];
+            a[i][n2h] = t[ic][1];
+            a[ic][0] = t[i][1];
+            a[ic][n2h] = t[ic][0];
+        }
+    }
+}
+
+
+void ddst2d(int n1, int n2, int isgn, double **a, double **t, 
+    int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void bitrv2col(int n1, int n, int *ip, double **a);
+    void bitrv2row(int n, int n2, int *ip, double **a);
+    void cftbcol(int n1, int n, double **a, double *w);
+    void cftbrow(int n, int n2, double **a, double *w);
+    void cftfcol(int n1, int n, double **a, double *w);
+    void cftfrow(int n, int n2, double **a, double *w);
+    void rftbcol(int n1, int n, double **a, int nc, double *c);
+    void rftfcol(int n1, int n, double **a, int nc, double *c);
+    void dstbsub(int n1, int n2, double **a, int nc, double *c);
+    void dstfsub(int n1, int n2, double **a, int nc, double *c);
+    int n, nw, nc, n1h, n2h, i, ix, ic, j, jx, jc;
+    double xi;
+    
+    n = n1 << 1;
+    if (n < n2) {
+        n = n2;
+    }
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n1 > nc || n2 > nc) {
+        if (n1 > n2) {
+            nc = n1;
+        } else {
+            nc = n2;
+        }
+        makect(nc, ip, w + nw);
+    }
+    n1h = n1 >> 1;
+    n2h = n2 >> 1;
+    if (isgn >= 0) {
+        for (i = 0; i <= n1 - 1; i++) {
+            for (j = 1; j <= n2h - 1; j++) {
+                jx = j << 1;
+                t[i][jx] = a[i][j];
+                t[i][jx + 1] = a[i][n2 - j];
+            }
+        }
+        t[0][0] = a[0][0];
+        t[0][1] = a[0][n2h];
+        t[n1h][0] = a[n1h][0];
+        t[n1h][1] = a[n1h][n2h];
+        for (i = 1; i <= n1h - 1; i++) {
+            ic = n1 - i;
+            t[i][0] = a[i][0];
+            t[ic][1] = a[i][n2h];
+            t[i][1] = a[ic][0];
+            t[ic][0] = a[ic][n2h];
+        }
+        dstfsub(n1, n2, t, nc, w + nw);
+        if (n1 > 2) {
+            bitrv2row(n1, n2, ip + 2, t);
+        }
+        cftfrow(n1, n2, t, w);
+        for (i = 0; i <= n1 - 1; i++) {
+            t[i][1] = 0.5 * (t[i][0] - t[i][1]);
+            t[i][0] -= t[i][1];
+        }
+        if (n2 > 4) {
+            rftfcol(n1, n2, t, nc, w + nw);
+            bitrv2col(n1, n2, ip + 2, t);
+        }
+        cftfcol(n1, n2, t, w);
+        for (i = 0; i <= n1h - 1; i++) {
+            ix = i << 1;
+            ic = n1 - 1 - i;
+            for (j = 0; j <= n2h - 1; j++) {
+                jx = j << 1;
+                jc = n2 - 1 - j;
+                a[ix][jx] = t[i][j];
+                a[ix][jx + 1] = -t[i][jc];
+                a[ix + 1][jx] = -t[ic][j];
+                a[ix + 1][jx + 1] = t[ic][jc];
+            }
+        }
+    } else {
+        for (i = 0; i <= n1h - 1; i++) {
+            ix = i << 1;
+            ic = n1 - 1 - i;
+            for (j = 0; j <= n2h - 1; j++) {
+                jx = j << 1;
+                jc = n2 - 1 - j;
+                t[i][j] = a[ix][jx];
+                t[i][jc] = -a[ix][jx + 1];
+                t[ic][j] = -a[ix + 1][jx];
+                t[ic][jc] = a[ix + 1][jx + 1];
+            }
+        }
+        if (n2 > 4) {
+            bitrv2col(n1, n2, ip + 2, t);
+        }
+        cftbcol(n1, n2, t, w);
+        if (n2 > 4) {
+            rftbcol(n1, n2, t, nc, w + nw);
+        }
+        for (i = 0; i <= n1 - 1; i++) {
+            xi = t[i][0] - t[i][1];
+            t[i][0] += t[i][1];
+            t[i][1] = xi;
+        }
+        if (n1 > 2) {
+            bitrv2row(n1, n2, ip + 2, t);
+        }
+        cftbrow(n1, n2, t, w);
+        dstbsub(n1, n2, t, nc, w + nw);
+        for (i = 0; i <= n1 - 1; i++) {
+            for (j = 1; j <= n2h - 1; j++) {
+                jx = j << 1;
+                a[i][j] = t[i][jx];
+                a[i][n2 - j] = t[i][jx + 1];
+            }
+        }
+        a[0][0] = t[0][0];
+        a[0][n2h] = t[0][1];
+        a[n1h][0] = t[n1h][0];
+        a[n1h][n2h] = t[n1h][1];
+        for (i = 1; i <= n1h - 1; i++) {
+            ic = n1 - i;
+            a[i][0] = t[i][0];
+            a[i][n2h] = t[ic][1];
+            a[ic][0] = t[i][1];
+            a[ic][n2h] = t[ic][0];
+        }
+    }
+}
+
+
+/* -------- initializing routines -------- */
+
+
+#include <math.h>
+
+void makewt(int nw, int *ip, double *w)
+{
+    void bitrv2(int n, int *ip, double *a);
+    int nwh, j;
+    double delta, x, y;
+    
+    ip[0] = nw;
+    ip[1] = 1;
+    if (nw > 2) {
+        nwh = nw >> 1;
+        delta = atan(1.0) / nwh;
+        w[0] = 1;
+        w[1] = 0;
+        w[nwh] = cos(delta * nwh);
+        w[nwh + 1] = w[nwh];
+        for (j = 2; j <= nwh - 2; j += 2) {
+            x = cos(delta * j);
+            y = sin(delta * j);
+            w[j] = x;
+            w[j + 1] = y;
+            w[nw - j] = y;
+            w[nw - j + 1] = x;
+        }
+        bitrv2(nw, ip + 2, w);
+    }
+}
+
+
+void makect(int nc, int *ip, double *c)
+{
+    int nch, j;
+    double delta;
+    
+    ip[1] = nc;
+    if (nc > 1) {
+        nch = nc >> 1;
+        delta = atan(1.0) / nch;
+        c[0] = 0.5;
+        c[nch] = 0.5 * cos(delta * nch);
+        for (j = 1; j <= nch - 1; j++) {
+            c[j] = 0.5 * cos(delta * j);
+            c[nc - j] = 0.5 * sin(delta * j);
+        }
+    }
+}
+
+
+/* -------- child routines -------- */
+
+
+void bitrv2(int n, int *ip, double *a)
+{
+    int j, j1, k, k1, l, m, m2;
+    double xr, xi;
+    
+    ip[0] = 0;
+    l = n;
+    m = 1;
+    while ((m << 2) < l) {
+        l >>= 1;
+        for (j = 0; j <= m - 1; j++) {
+            ip[m + j] = ip[j] + l;
+        }
+        m <<= 1;
+    }
+    if ((m << 2) > l) {
+        for (k = 1; k <= m - 1; k++) {
+            for (j = 0; j <= k - 1; j++) {
+                j1 = (j << 1) + ip[k];
+                k1 = (k << 1) + ip[j];
+                xr = a[j1];
+                xi = a[j1 + 1];
+                a[j1] = a[k1];
+                a[j1 + 1] = a[k1 + 1];
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+            }
+        }
+    } else {
+        m2 = m << 1;
+        for (k = 1; k <= m - 1; k++) {
+            for (j = 0; j <= k - 1; j++) {
+                j1 = (j << 1) + ip[k];
+                k1 = (k << 1) + ip[j];
+                xr = a[j1];
+                xi = a[j1 + 1];
+                a[j1] = a[k1];
+                a[j1 + 1] = a[k1 + 1];
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += m2;
+                k1 += m2;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                a[j1] = a[k1];
+                a[j1 + 1] = a[k1 + 1];
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+            }
+        }
+    }
+}
+
+
+void bitrv2col(int n1, int n, int *ip, double **a)
+{
+    int i, j, j1, k, k1, l, m, m2;
+    double xr, xi;
+    
+    ip[0] = 0;
+    l = n;
+    m = 1;
+    while ((m << 2) < l) {
+        l >>= 1;
+        for (j = 0; j <= m - 1; j++) {
+            ip[m + j] = ip[j] + l;
+        }
+        m <<= 1;
+    }
+    if ((m << 2) > l) {
+        for (i = 0; i <= n1 - 1; i++) {
+            for (k = 1; k <= m - 1; k++) {
+                for (j = 0; j <= k - 1; j++) {
+                    j1 = (j << 1) + ip[k];
+                    k1 = (k << 1) + ip[j];
+                    xr = a[i][j1];
+                    xi = a[i][j1 + 1];
+                    a[i][j1] = a[i][k1];
+                    a[i][j1 + 1] = a[i][k1 + 1];
+                    a[i][k1] = xr;
+                    a[i][k1 + 1] = xi;
+                }
+            }
+        }
+    } else {
+        m2 = m << 1;
+        for (i = 0; i <= n1 - 1; i++) {
+            for (k = 1; k <= m - 1; k++) {
+                for (j = 0; j <= k - 1; j++) {
+                    j1 = (j << 1) + ip[k];
+                    k1 = (k << 1) + ip[j];
+                    xr = a[i][j1];
+                    xi = a[i][j1 + 1];
+                    a[i][j1] = a[i][k1];
+                    a[i][j1 + 1] = a[i][k1 + 1];
+                    a[i][k1] = xr;
+                    a[i][k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += m2;
+                    xr = a[i][j1];
+                    xi = a[i][j1 + 1];
+                    a[i][j1] = a[i][k1];
+                    a[i][j1 + 1] = a[i][k1 + 1];
+                    a[i][k1] = xr;
+                    a[i][k1 + 1] = xi;
+                }
+            }
+        }
+    }
+}
+
+
+void bitrv2row(int n, int n2, int *ip, double **a)
+{
+    int i, j, j1, k, k1, l, m;
+    double xr, xi;
+    
+    ip[0] = 0;
+    l = n;
+    m = 1;
+    while ((m << 1) < l) {
+        l >>= 1;
+        for (j = 0; j <= m - 1; j++) {
+            ip[m + j] = ip[j] + l;
+        }
+        m <<= 1;
+    }
+    if ((m << 1) > l) {
+        for (k = 1; k <= m - 1; k++) {
+            for (j = 0; j <= k - 1; j++) {
+                j1 = j + ip[k];
+                k1 = k + ip[j];
+                for (i = 0; i <= n2 - 2; i += 2) {
+                    xr = a[j1][i];
+                    xi = a[j1][i + 1];
+                    a[j1][i] = a[k1][i];
+                    a[j1][i + 1] = a[k1][i + 1];
+                    a[k1][i] = xr;
+                    a[k1][i + 1] = xi;
+                }
+            }
+        }
+    } else {
+        for (k = 1; k <= m - 1; k++) {
+            for (j = 0; j <= k - 1; j++) {
+                j1 = j + ip[k];
+                k1 = k + ip[j];
+                for (i = 0; i <= n2 - 2; i += 2) {
+                    xr = a[j1][i];
+                    xi = a[j1][i + 1];
+                    a[j1][i] = a[k1][i];
+                    a[j1][i + 1] = a[k1][i + 1];
+                    a[k1][i] = xr;
+                    a[k1][i + 1] = xi;
+                }
+                j1 += m;
+                k1 += m;
+                for (i = 0; i <= n2 - 2; i += 2) {
+                    xr = a[j1][i];
+                    xi = a[j1][i + 1];
+                    a[j1][i] = a[k1][i];
+                    a[j1][i + 1] = a[k1][i + 1];
+                    a[k1][i] = xr;
+                    a[k1][i + 1] = xi;
+                }
+            }
+        }
+    }
+}
+
+
+void cftbcol(int n1, int n, double **a, double *w)
+{
+    int i, j, j1, j2, j3, k, k1, ks, l, m;
+    double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+    
+    for (i = 0; i <= n1 - 1; i++) {
+        l = 2;
+        while ((l << 1) < n) {
+            m = l << 2;
+            for (j = 0; j <= l - 2; j += 2) {
+                j1 = j + l;
+                j2 = j1 + l;
+                j3 = j2 + l;
+                x0r = a[i][j] + a[i][j1];
+                x0i = a[i][j + 1] + a[i][j1 + 1];
+                x1r = a[i][j] - a[i][j1];
+                x1i = a[i][j + 1] - a[i][j1 + 1];
+                x2r = a[i][j2] + a[i][j3];
+                x2i = a[i][j2 + 1] + a[i][j3 + 1];
+                x3r = a[i][j2] - a[i][j3];
+                x3i = a[i][j2 + 1] - a[i][j3 + 1];
+                a[i][j] = x0r + x2r;
+                a[i][j + 1] = x0i + x2i;
+                a[i][j2] = x0r - x2r;
+                a[i][j2 + 1] = x0i - x2i;
+                a[i][j1] = x1r - x3i;
+                a[i][j1 + 1] = x1i + x3r;
+                a[i][j3] = x1r + x3i;
+                a[i][j3 + 1] = x1i - x3r;
+            }
+            if (m < n) {
+                wk1r = w[2];
+                for (j = m; j <= l + m - 2; j += 2) {
+                    j1 = j + l;
+                    j2 = j1 + l;
+                    j3 = j2 + l;
+                    x0r = a[i][j] + a[i][j1];
+                    x0i = a[i][j + 1] + a[i][j1 + 1];
+                    x1r = a[i][j] - a[i][j1];
+                    x1i = a[i][j + 1] - a[i][j1 + 1];
+                    x2r = a[i][j2] + a[i][j3];
+                    x2i = a[i][j2 + 1] + a[i][j3 + 1];
+                    x3r = a[i][j2] - a[i][j3];
+                    x3i = a[i][j2 + 1] - a[i][j3 + 1];
+                    a[i][j] = x0r + x2r;
+                    a[i][j + 1] = x0i + x2i;
+                    a[i][j2] = x2i - x0i;
+                    a[i][j2 + 1] = x0r - x2r;
+                    x0r = x1r - x3i;
+                    x0i = x1i + x3r;
+                    a[i][j1] = wk1r * (x0r - x0i);
+                    a[i][j1 + 1] = wk1r * (x0r + x0i);
+                    x0r = x3i + x1r;
+                    x0i = x3r - x1i;
+                    a[i][j3] = wk1r * (x0i - x0r);
+                    a[i][j3 + 1] = wk1r * (x0i + x0r);
+                }
+                k1 = 1;
+                ks = -1;
+                for (k = (m << 1); k <= n - m; k += m) {
+                    k1++;
+                    ks = -ks;
+                    wk1r = w[k1 << 1];
+                    wk1i = w[(k1 << 1) + 1];
+                    wk2r = ks * w[k1];
+                    wk2i = w[k1 + ks];
+                    wk3r = wk1r - 2 * wk2i * wk1i;
+                    wk3i = 2 * wk2i * wk1r - wk1i;
+                    for (j = k; j <= l + k - 2; j += 2) {
+                        j1 = j + l;
+                        j2 = j1 + l;
+                        j3 = j2 + l;
+                        x0r = a[i][j] + a[i][j1];
+                        x0i = a[i][j + 1] + a[i][j1 + 1];
+                        x1r = a[i][j] - a[i][j1];
+                        x1i = a[i][j + 1] - a[i][j1 + 1];
+                        x2r = a[i][j2] + a[i][j3];
+                        x2i = a[i][j2 + 1] + a[i][j3 + 1];
+                        x3r = a[i][j2] - a[i][j3];
+                        x3i = a[i][j2 + 1] - a[i][j3 + 1];
+                        a[i][j] = x0r + x2r;
+                        a[i][j + 1] = x0i + x2i;
+                        x0r -= x2r;
+                        x0i -= x2i;
+                        a[i][j2] = wk2r * x0r - wk2i * x0i;
+                        a[i][j2 + 1] = wk2r * x0i + wk2i * x0r;
+                        x0r = x1r - x3i;
+                        x0i = x1i + x3r;
+                        a[i][j1] = wk1r * x0r - wk1i * x0i;
+                        a[i][j1 + 1] = wk1r * x0i + wk1i * x0r;
+                        x0r = x1r + x3i;
+                        x0i = x1i - x3r;
+                        a[i][j3] = wk3r * x0r - wk3i * x0i;
+                        a[i][j3 + 1] = wk3r * x0i + wk3i * x0r;
+                    }
+                }
+            }
+            l = m;
+        }
+        if (l < n) {
+            for (j = 0; j <= l - 2; j += 2) {
+                j1 = j + l;
+                x0r = a[i][j] - a[i][j1];
+                x0i = a[i][j + 1] - a[i][j1 + 1];
+                a[i][j] += a[i][j1];
+                a[i][j + 1] += a[i][j1 + 1];
+                a[i][j1] = x0r;
+                a[i][j1 + 1] = x0i;
+            }
+        }
+    }
+}
+
+
+void cftbrow(int n, int n2, double **a, double *w)
+{
+    int i, j, j1, j2, j3, k, k1, ks, l, m;
+    double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+    
+    l = 1;
+    while ((l << 1) < n) {
+        m = l << 2;
+        for (j = 0; j <= l - 1; j++) {
+            j1 = j + l;
+            j2 = j1 + l;
+            j3 = j2 + l;
+            for (i = 0; i <= n2 - 2; i += 2) {
+                x0r = a[j][i] + a[j1][i];
+                x0i = a[j][i + 1] + a[j1][i + 1];
+                x1r = a[j][i] - a[j1][i];
+                x1i = a[j][i + 1] - a[j1][i + 1];
+                x2r = a[j2][i] + a[j3][i];
+                x2i = a[j2][i + 1] + a[j3][i + 1];
+                x3r = a[j2][i] - a[j3][i];
+                x3i = a[j2][i + 1] - a[j3][i + 1];
+                a[j][i] = x0r + x2r;
+                a[j][i + 1] = x0i + x2i;
+                a[j2][i] = x0r - x2r;
+                a[j2][i + 1] = x0i - x2i;
+                a[j1][i] = x1r - x3i;
+                a[j1][i + 1] = x1i + x3r;
+                a[j3][i] = x1r + x3i;
+                a[j3][i + 1] = x1i - x3r;
+            }
+        }
+        if (m < n) {
+            wk1r = w[2];
+            for (j = m; j <= l + m - 1; j++) {
+                j1 = j + l;
+                j2 = j1 + l;
+                j3 = j2 + l;
+                for (i = 0; i <= n2 - 2; i += 2) {
+                    x0r = a[j][i] + a[j1][i];
+                    x0i = a[j][i + 1] + a[j1][i + 1];
+                    x1r = a[j][i] - a[j1][i];
+                    x1i = a[j][i + 1] - a[j1][i + 1];
+                    x2r = a[j2][i] + a[j3][i];
+                    x2i = a[j2][i + 1] + a[j3][i + 1];
+                    x3r = a[j2][i] - a[j3][i];
+                    x3i = a[j2][i + 1] - a[j3][i + 1];
+                    a[j][i] = x0r + x2r;
+                    a[j][i + 1] = x0i + x2i;
+                    a[j2][i] = x2i - x0i;
+                    a[j2][i + 1] = x0r - x2r;
+                    x0r = x1r - x3i;
+                    x0i = x1i + x3r;
+                    a[j1][i] = wk1r * (x0r - x0i);
+                    a[j1][i + 1] = wk1r * (x0r + x0i);
+                    x0r = x3i + x1r;
+                    x0i = x3r - x1i;
+                    a[j3][i] = wk1r * (x0i - x0r);
+                    a[j3][i + 1] = wk1r * (x0i + x0r);
+                }
+            }
+            k1 = 1;
+            ks = -1;
+            for (k = (m << 1); k <= n - m; k += m) {
+                k1++;
+                ks = -ks;
+                wk1r = w[k1 << 1];
+                wk1i = w[(k1 << 1) + 1];
+                wk2r = ks * w[k1];
+                wk2i = w[k1 + ks];
+                wk3r = wk1r - 2 * wk2i * wk1i;
+                wk3i = 2 * wk2i * wk1r - wk1i;
+                for (j = k; j <= l + k - 1; j++) {
+                    j1 = j + l;
+                    j2 = j1 + l;
+                    j3 = j2 + l;
+                    for (i = 0; i <= n2 - 2; i += 2) {
+                        x0r = a[j][i] + a[j1][i];
+                        x0i = a[j][i + 1] + a[j1][i + 1];
+                        x1r = a[j][i] - a[j1][i];
+                        x1i = a[j][i + 1] - a[j1][i + 1];
+                        x2r = a[j2][i] + a[j3][i];
+                        x2i = a[j2][i + 1] + a[j3][i + 1];
+                        x3r = a[j2][i] - a[j3][i];
+                        x3i = a[j2][i + 1] - a[j3][i + 1];
+                        a[j][i] = x0r + x2r;
+                        a[j][i + 1] = x0i + x2i;
+                        x0r -= x2r;
+                        x0i -= x2i;
+                        a[j2][i] = wk2r * x0r - wk2i * x0i;
+                        a[j2][i + 1] = wk2r * x0i + wk2i * x0r;
+                        x0r = x1r - x3i;
+                        x0i = x1i + x3r;
+                        a[j1][i] = wk1r * x0r - wk1i * x0i;
+                        a[j1][i + 1] = wk1r * x0i + wk1i * x0r;
+                        x0r = x1r + x3i;
+                        x0i = x1i - x3r;
+                        a[j3][i] = wk3r * x0r - wk3i * x0i;
+                        a[j3][i + 1] = wk3r * x0i + wk3i * x0r;
+                    }
+                }
+            }
+        }
+        l = m;
+    }
+    if (l < n) {
+        for (j = 0; j <= l - 1; j++) {
+            j1 = j + l;
+            for (i = 0; i <= n2 - 2; i += 2) {
+                x0r = a[j][i] - a[j1][i];
+                x0i = a[j][i + 1] - a[j1][i + 1];
+                a[j][i] += a[j1][i];
+                a[j][i + 1] += a[j1][i + 1];
+                a[j1][i] = x0r;
+                a[j1][i + 1] = x0i;
+            }
+        }
+    }
+}
+
+
+void cftfcol(int n1, int n, double **a, double *w)
+{
+    int i, j, j1, j2, j3, k, k1, ks, l, m;
+    double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+    
+    for (i = 0; i <= n1 - 1; i++) {
+        l = 2;
+        while ((l << 1) < n) {
+            m = l << 2;
+            for (j = 0; j <= l - 2; j += 2) {
+                j1 = j + l;
+                j2 = j1 + l;
+                j3 = j2 + l;
+                x0r = a[i][j] + a[i][j1];
+                x0i = a[i][j + 1] + a[i][j1 + 1];
+                x1r = a[i][j] - a[i][j1];
+                x1i = a[i][j + 1] - a[i][j1 + 1];
+                x2r = a[i][j2] + a[i][j3];
+                x2i = a[i][j2 + 1] + a[i][j3 + 1];
+                x3r = a[i][j2] - a[i][j3];
+                x3i = a[i][j2 + 1] - a[i][j3 + 1];
+                a[i][j] = x0r + x2r;
+                a[i][j + 1] = x0i + x2i;
+                a[i][j2] = x0r - x2r;
+                a[i][j2 + 1] = x0i - x2i;
+                a[i][j1] = x1r + x3i;
+                a[i][j1 + 1] = x1i - x3r;
+                a[i][j3] = x1r - x3i;
+                a[i][j3 + 1] = x1i + x3r;
+            }
+            if (m < n) {
+                wk1r = w[2];
+                for (j = m; j <= l + m - 2; j += 2) {
+                    j1 = j + l;
+                    j2 = j1 + l;
+                    j3 = j2 + l;
+                    x0r = a[i][j] + a[i][j1];
+                    x0i = a[i][j + 1] + a[i][j1 + 1];
+                    x1r = a[i][j] - a[i][j1];
+                    x1i = a[i][j + 1] - a[i][j1 + 1];
+                    x2r = a[i][j2] + a[i][j3];
+                    x2i = a[i][j2 + 1] + a[i][j3 + 1];
+                    x3r = a[i][j2] - a[i][j3];
+                    x3i = a[i][j2 + 1] - a[i][j3 + 1];
+                    a[i][j] = x0r + x2r;
+                    a[i][j + 1] = x0i + x2i;
+                    a[i][j2] = x0i - x2i;
+                    a[i][j2 + 1] = x2r - x0r;
+                    x0r = x1r + x3i;
+                    x0i = x1i - x3r;
+                    a[i][j1] = wk1r * (x0i + x0r);
+                    a[i][j1 + 1] = wk1r * (x0i - x0r);
+                    x0r = x3i - x1r;
+                    x0i = x3r + x1i;
+                    a[i][j3] = wk1r * (x0r + x0i);
+                    a[i][j3 + 1] = wk1r * (x0r - x0i);
+                }
+                k1 = 1;
+                ks = -1;
+                for (k = (m << 1); k <= n - m; k += m) {
+                    k1++;
+                    ks = -ks;
+                    wk1r = w[k1 << 1];
+                    wk1i = w[(k1 << 1) + 1];
+                    wk2r = ks * w[k1];
+                    wk2i = w[k1 + ks];
+                    wk3r = wk1r - 2 * wk2i * wk1i;
+                    wk3i = 2 * wk2i * wk1r - wk1i;
+                    for (j = k; j <= l + k - 2; j += 2) {
+                        j1 = j + l;
+                        j2 = j1 + l;
+                        j3 = j2 + l;
+                        x0r = a[i][j] + a[i][j1];
+                        x0i = a[i][j + 1] + a[i][j1 + 1];
+                        x1r = a[i][j] - a[i][j1];
+                        x1i = a[i][j + 1] - a[i][j1 + 1];
+                        x2r = a[i][j2] + a[i][j3];
+                        x2i = a[i][j2 + 1] + a[i][j3 + 1];
+                        x3r = a[i][j2] - a[i][j3];
+                        x3i = a[i][j2 + 1] - a[i][j3 + 1];
+                        a[i][j] = x0r + x2r;
+                        a[i][j + 1] = x0i + x2i;
+                        x0r -= x2r;
+                        x0i -= x2i;
+                        a[i][j2] = wk2r * x0r + wk2i * x0i;
+                        a[i][j2 + 1] = wk2r * x0i - wk2i * x0r;
+                        x0r = x1r + x3i;
+                        x0i = x1i - x3r;
+                        a[i][j1] = wk1r * x0r + wk1i * x0i;
+                        a[i][j1 + 1] = wk1r * x0i - wk1i * x0r;
+                        x0r = x1r - x3i;
+                        x0i = x1i + x3r;
+                        a[i][j3] = wk3r * x0r + wk3i * x0i;
+                        a[i][j3 + 1] = wk3r * x0i - wk3i * x0r;
+                    }
+                }
+            }
+            l = m;
+        }
+        if (l < n) {
+            for (j = 0; j <= l - 2; j += 2) {
+                j1 = j + l;
+                x0r = a[i][j] - a[i][j1];
+                x0i = a[i][j + 1] - a[i][j1 + 1];
+                a[i][j] += a[i][j1];
+                a[i][j + 1] += a[i][j1 + 1];
+                a[i][j1] = x0r;
+                a[i][j1 + 1] = x0i;
+            }
+        }
+    }
+}
+
+
+void cftfrow(int n, int n2, double **a, double *w)
+{
+    int i, j, j1, j2, j3, k, k1, ks, l, m;
+    double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+    
+    l = 1;
+    while ((l << 1) < n) {
+        m = l << 2;
+        for (j = 0; j <= l - 1; j++) {
+            j1 = j + l;
+            j2 = j1 + l;
+            j3 = j2 + l;
+            for (i = 0; i <= n2 - 2; i += 2) {
+                x0r = a[j][i] + a[j1][i];
+                x0i = a[j][i + 1] + a[j1][i + 1];
+                x1r = a[j][i] - a[j1][i];
+                x1i = a[j][i + 1] - a[j1][i + 1];
+                x2r = a[j2][i] + a[j3][i];
+                x2i = a[j2][i + 1] + a[j3][i + 1];
+                x3r = a[j2][i] - a[j3][i];
+                x3i = a[j2][i + 1] - a[j3][i + 1];
+                a[j][i] = x0r + x2r;
+                a[j][i + 1] = x0i + x2i;
+                a[j2][i] = x0r - x2r;
+                a[j2][i + 1] = x0i - x2i;
+                a[j1][i] = x1r + x3i;
+                a[j1][i + 1] = x1i - x3r;
+                a[j3][i] = x1r - x3i;
+                a[j3][i + 1] = x1i + x3r;
+            }
+        }
+        if (m < n) {
+            wk1r = w[2];
+            for (j = m; j <= l + m - 1; j++) {
+                j1 = j + l;
+                j2 = j1 + l;
+                j3 = j2 + l;
+                for (i = 0; i <= n2 - 2; i += 2) {
+                    x0r = a[j][i] + a[j1][i];
+                    x0i = a[j][i + 1] + a[j1][i + 1];
+                    x1r = a[j][i] - a[j1][i];
+                    x1i = a[j][i + 1] - a[j1][i + 1];
+                    x2r = a[j2][i] + a[j3][i];
+                    x2i = a[j2][i + 1] + a[j3][i + 1];
+                    x3r = a[j2][i] - a[j3][i];
+                    x3i = a[j2][i + 1] - a[j3][i + 1];
+                    a[j][i] = x0r + x2r;
+                    a[j][i + 1] = x0i + x2i;
+                    a[j2][i] = x0i - x2i;
+                    a[j2][i + 1] = x2r - x0r;
+                    x0r = x1r + x3i;
+                    x0i = x1i - x3r;
+                    a[j1][i] = wk1r * (x0i + x0r);
+                    a[j1][i + 1] = wk1r * (x0i - x0r);
+                    x0r = x3i - x1r;
+                    x0i = x3r + x1i;
+                    a[j3][i] = wk1r * (x0r + x0i);
+                    a[j3][i + 1] = wk1r * (x0r - x0i);
+                }
+            }
+            k1 = 1;
+            ks = -1;
+            for (k = (m << 1); k <= n - m; k += m) {
+                k1++;
+                ks = -ks;
+                wk1r = w[k1 << 1];
+                wk1i = w[(k1 << 1) + 1];
+                wk2r = ks * w[k1];
+                wk2i = w[k1 + ks];
+                wk3r = wk1r - 2 * wk2i * wk1i;
+                wk3i = 2 * wk2i * wk1r - wk1i;
+                for (j = k; j <= l + k - 1; j++) {
+                    j1 = j + l;
+                    j2 = j1 + l;
+                    j3 = j2 + l;
+                    for (i = 0; i <= n2 - 2; i += 2) {
+                        x0r = a[j][i] + a[j1][i];
+                        x0i = a[j][i + 1] + a[j1][i + 1];
+                        x1r = a[j][i] - a[j1][i];
+                        x1i = a[j][i + 1] - a[j1][i + 1];
+                        x2r = a[j2][i] + a[j3][i];
+                        x2i = a[j2][i + 1] + a[j3][i + 1];
+                        x3r = a[j2][i] - a[j3][i];
+                        x3i = a[j2][i + 1] - a[j3][i + 1];
+                        a[j][i] = x0r + x2r;
+                        a[j][i + 1] = x0i + x2i;
+                        x0r -= x2r;
+                        x0i -= x2i;
+                        a[j2][i] = wk2r * x0r + wk2i * x0i;
+                        a[j2][i + 1] = wk2r * x0i - wk2i * x0r;
+                        x0r = x1r + x3i;
+                        x0i = x1i - x3r;
+                        a[j1][i] = wk1r * x0r + wk1i * x0i;
+                        a[j1][i + 1] = wk1r * x0i - wk1i * x0r;
+                        x0r = x1r - x3i;
+                        x0i = x1i + x3r;
+                        a[j3][i] = wk3r * x0r + wk3i * x0i;
+                        a[j3][i + 1] = wk3r * x0i - wk3i * x0r;
+                    }
+                }
+            }
+        }
+        l = m;
+    }
+    if (l < n) {
+        for (j = 0; j <= l - 1; j++) {
+            j1 = j + l;
+            for (i = 0; i <= n2 - 2; i += 2) {
+                x0r = a[j][i] - a[j1][i];
+                x0i = a[j][i + 1] - a[j1][i + 1];
+                a[j][i] += a[j1][i];
+                a[j][i + 1] += a[j1][i + 1];
+                a[j1][i] = x0r;
+                a[j1][i + 1] = x0i;
+            }
+        }
+    }
+}
+
+
+void rftbcol(int n1, int n, double **a, int nc, double *c)
+{
+    int i, j, k, kk, ks;
+    double wkr, wki, xr, xi, yr, yi;
+    
+    ks = (nc << 2) / n;
+    for (i = 0; i <= n1 - 1; i++) {
+        kk = 0;
+        for (k = (n >> 1) - 2; k >= 2; k -= 2) {
+            j = n - k;
+            kk += ks;
+            wkr = 0.5 - c[kk];
+            wki = c[nc - kk];
+            xr = a[i][k] - a[i][j];
+            xi = a[i][k + 1] + a[i][j + 1];
+            yr = wkr * xr - wki * xi;
+            yi = wkr * xi + wki * xr;
+            a[i][k] -= yr;
+            a[i][k + 1] -= yi;
+            a[i][j] += yr;
+            a[i][j + 1] -= yi;
+        }
+    }
+}
+
+
+void rftfcol(int n1, int n, double **a, int nc, double *c)
+{
+    int i, j, k, kk, ks;
+    double wkr, wki, xr, xi, yr, yi;
+    
+    ks = (nc << 2) / n;
+    for (i = 0; i <= n1 - 1; i++) {
+        kk = 0;
+        for (k = (n >> 1) - 2; k >= 2; k -= 2) {
+            j = n - k;
+            kk += ks;
+            wkr = 0.5 - c[kk];
+            wki = c[nc - kk];
+            xr = a[i][k] - a[i][j];
+            xi = a[i][k + 1] + a[i][j + 1];
+            yr = wkr * xr + wki * xi;
+            yi = wkr * xi - wki * xr;
+            a[i][k] -= yr;
+            a[i][k + 1] -= yi;
+            a[i][j] += yr;
+            a[i][j + 1] -= yi;
+        }
+    }
+}
+
+
+void dctbsub(int n1, int n2, double **a, int nc, double *c)
+{
+    int kk1, kk2, ks1, ks2, n1h, j1, k1, k2;
+    double w1r, w1i, wkr, wki, wjr, wji, x0r, x0i, x1r, x1i;
+    
+    ks1 = nc / n1;
+    ks2 = nc / n2;
+    n1h = n1 >> 1;
+    kk1 = ks1;
+    for (k1 = 1; k1 <= n1h - 1; k1++) {
+        j1 = n1 - k1;
+        w1r = 2 * c[kk1];
+        w1i = 2 * c[nc - kk1];
+        kk1 += ks1;
+        kk2 = ks2;
+        for (k2 = 2; k2 <= n2 - 2; k2 += 2) {
+            x0r = w1r * c[kk2];
+            x0i = w1i * c[kk2];
+            x1r = w1r * c[nc - kk2];
+            x1i = w1i * c[nc - kk2];
+            wkr = x0r - x1i;
+            wki = x0i + x1r;
+            wji = x0r + x1i;
+            wjr = x0i - x1r;
+            kk2 += ks2;
+            x0r = wkr * a[k1][k2] - wki * a[k1][k2 + 1];
+            x0i = wkr * a[k1][k2 + 1] + wki * a[k1][k2];
+            x1r = wjr * a[j1][k2] - wji * a[j1][k2 + 1];
+            x1i = wjr * a[j1][k2 + 1] + wji * a[j1][k2];
+            a[k1][k2] = x0r + x1i;
+            a[k1][k2 + 1] = x0i - x1r;
+            a[j1][k2] = x1r + x0i;
+            a[j1][k2 + 1] = x1i - x0r;
+        }
+        wkr = w1r * 0.5;
+        wki = w1i * 0.5;
+        wjr = w1r * c[kk2];
+        wji = w1i * c[kk2];
+        x0r = a[k1][0] + a[j1][0];
+        x0i = a[k1][1] - a[j1][1];
+        x1r = a[k1][0] - a[j1][0];
+        x1i = a[k1][1] + a[j1][1];
+        a[k1][0] = wkr * x0r - wki * x0i;
+        a[k1][1] = wkr * x0i + wki * x0r;
+        a[j1][0] = -wjr * x1r + wji * x1i;
+        a[j1][1] = wjr * x1i + wji * x1r;
+    }
+    w1r = 2 * c[kk1];
+    kk2 = ks2;
+    for (k2 = 2; k2 <= n2 - 2; k2 += 2) {
+        wkr = 2 * c[kk2];
+        wki = 2 * c[nc - kk2];
+        wjr = w1r * wkr;
+        wji = w1r * wki;
+        kk2 += ks2;
+        x0i = wkr * a[0][k2 + 1] + wki * a[0][k2];
+        a[0][k2] = wkr * a[0][k2] - wki * a[0][k2 + 1];
+        a[0][k2 + 1] = x0i;
+        x0i = wjr * a[n1h][k2 + 1] + wji * a[n1h][k2];
+        a[n1h][k2] = wjr * a[n1h][k2] - wji * a[n1h][k2 + 1];
+        a[n1h][k2 + 1] = x0i;
+    }
+    a[0][1] *= w1r;
+    a[n1h][0] *= w1r;
+    a[n1h][1] *= 0.5;
+}
+
+
+void dctfsub(int n1, int n2, double **a, int nc, double *c)
+{
+    int kk1, kk2, ks1, ks2, n1h, j1, k1, k2;
+    double w1r, w1i, wkr, wki, wjr, wji, x0r, x0i, x1r, x1i;
+    
+    ks1 = nc / n1;
+    ks2 = nc / n2;
+    n1h = n1 >> 1;
+    kk1 = ks1;
+    for (k1 = 1; k1 <= n1h - 1; k1++) {
+        j1 = n1 - k1;
+        w1r = 2 * c[kk1];
+        w1i = 2 * c[nc - kk1];
+        kk1 += ks1;
+        kk2 = ks2;
+        for (k2 = 2; k2 <= n2 - 2; k2 += 2) {
+            x0r = w1r * c[kk2];
+            x0i = w1i * c[kk2];
+            x1r = w1r * c[nc - kk2];
+            x1i = w1i * c[nc - kk2];
+            wkr = x0r - x1i;
+            wki = x0i + x1r;
+            wji = x0r + x1i;
+            wjr = x0i - x1r;
+            kk2 += ks2;
+            x0r = a[k1][k2] - a[j1][k2 + 1];
+            x0i = a[j1][k2] + a[k1][k2 + 1];
+            x1r = a[j1][k2] - a[k1][k2 + 1];
+            x1i = a[k1][k2] + a[j1][k2 + 1];
+            a[k1][k2] = wkr * x0r + wki * x0i;
+            a[k1][k2 + 1] = wkr * x0i - wki * x0r;
+            a[j1][k2] = wjr * x1r + wji * x1i;
+            a[j1][k2 + 1] = wjr * x1i - wji * x1r;
+        }
+        x0r = 2 * c[kk2];
+        wjr = x0r * w1r;
+        wji = x0r * w1i;
+        x0r = w1r * a[k1][0] + w1i * a[k1][1];
+        x0i = w1r * a[k1][1] - w1i * a[k1][0];
+        x1r = -wjr * a[j1][0] + wji * a[j1][1];
+        x1i = wjr * a[j1][1] + wji * a[j1][0];
+        a[k1][0] = x0r + x1r;
+        a[k1][1] = x1i + x0i;
+        a[j1][0] = x0r - x1r;
+        a[j1][1] = x1i - x0i;
+    }
+    w1r = 2 * c[kk1];
+    kk2 = ks2;
+    for (k2 = 2; k2 <= n2 - 2; k2 += 2) {
+        wkr = 2 * c[kk2];
+        wki = 2 * c[nc - kk2];
+        wjr = w1r * wkr;
+        wji = w1r * wki;
+        kk2 += ks2;
+        x0i = wkr * a[0][k2 + 1] - wki * a[0][k2];
+        a[0][k2] = wkr * a[0][k2] + wki * a[0][k2 + 1];
+        a[0][k2 + 1] = x0i;
+        x0i = wjr * a[n1h][k2 + 1] - wji * a[n1h][k2];
+        a[n1h][k2] = wjr * a[n1h][k2] + wji * a[n1h][k2 + 1];
+        a[n1h][k2 + 1] = x0i;
+    }
+    w1r *= 2;
+    a[0][0] *= 2;
+    a[0][1] *= w1r;
+    a[n1h][0] *= w1r;
+}
+
+
+void dstbsub(int n1, int n2, double **a, int nc, double *c)
+{
+    int kk1, kk2, ks1, ks2, n1h, j1, k1, k2;
+    double w1r, w1i, wkr, wki, wjr, wji, x0r, x0i, x1r, x1i;
+    
+    ks1 = nc / n1;
+    ks2 = nc / n2;
+    n1h = n1 >> 1;
+    kk1 = ks1;
+    for (k1 = 1; k1 <= n1h - 1; k1++) {
+        j1 = n1 - k1;
+        w1r = 2 * c[kk1];
+        w1i = 2 * c[nc - kk1];
+        kk1 += ks1;
+        kk2 = ks2;
+        for (k2 = 2; k2 <= n2 - 2; k2 += 2) {
+            x0r = w1r * c[kk2];
+            x0i = w1i * c[kk2];
+            x1r = w1r * c[nc - kk2];
+            x1i = w1i * c[nc - kk2];
+            wkr = x0r - x1i;
+            wki = x0i + x1r;
+            wji = x0r + x1i;
+            wjr = x0i - x1r;
+            kk2 += ks2;
+            x0r = wkr * a[k1][k2] - wki * a[k1][k2 + 1];
+            x0i = wkr * a[k1][k2 + 1] + wki * a[k1][k2];
+            x1r = wjr * a[j1][k2] - wji * a[j1][k2 + 1];
+            x1i = wjr * a[j1][k2 + 1] + wji * a[j1][k2];
+            a[k1][k2] = x1i - x0r;
+            a[k1][k2 + 1] = x1r + x0i;
+            a[j1][k2] = x0i - x1r;
+            a[j1][k2 + 1] = x0r + x1i;
+        }
+        wkr = w1r * 0.5;
+        wki = w1i * 0.5;
+        wjr = w1r * c[kk2];
+        wji = w1i * c[kk2];
+        x0r = a[k1][0] + a[j1][0];
+        x0i = a[k1][1] - a[j1][1];
+        x1r = a[k1][0] - a[j1][0];
+        x1i = a[k1][1] + a[j1][1];
+        a[k1][1] = wkr * x0r - wki * x0i;
+        a[k1][0] = wkr * x0i + wki * x0r;
+        a[j1][1] = -wjr * x1r + wji * x1i;
+        a[j1][0] = wjr * x1i + wji * x1r;
+    }
+    w1r = 2 * c[kk1];
+    kk2 = ks2;
+    for (k2 = 2; k2 <= n2 - 2; k2 += 2) {
+        wkr = 2 * c[kk2];
+        wki = 2 * c[nc - kk2];
+        wjr = w1r * wkr;
+        wji = w1r * wki;
+        kk2 += ks2;
+        x0i = wkr * a[0][k2 + 1] + wki * a[0][k2];
+        a[0][k2 + 1] = wkr * a[0][k2] - wki * a[0][k2 + 1];
+        a[0][k2] = x0i;
+        x0i = wjr * a[n1h][k2 + 1] + wji * a[n1h][k2];
+        a[n1h][k2 + 1] = wjr * a[n1h][k2] - wji * a[n1h][k2 + 1];
+        a[n1h][k2] = x0i;
+    }
+    a[0][1] *= w1r;
+    a[n1h][0] *= w1r;
+    a[n1h][1] *= 0.5;
+}
+
+
+void dstfsub(int n1, int n2, double **a, int nc, double *c)
+{
+    int kk1, kk2, ks1, ks2, n1h, j1, k1, k2;
+    double w1r, w1i, wkr, wki, wjr, wji, x0r, x0i, x1r, x1i;
+    
+    ks1 = nc / n1;
+    ks2 = nc / n2;
+    n1h = n1 >> 1;
+    kk1 = ks1;
+    for (k1 = 1; k1 <= n1h - 1; k1++) {
+        j1 = n1 - k1;
+        w1r = 2 * c[kk1];
+        w1i = 2 * c[nc - kk1];
+        kk1 += ks1;
+        kk2 = ks2;
+        for (k2 = 2; k2 <= n2 - 2; k2 += 2) {
+            x0r = w1r * c[kk2];
+            x0i = w1i * c[kk2];
+            x1r = w1r * c[nc - kk2];
+            x1i = w1i * c[nc - kk2];
+            wkr = x0r - x1i;
+            wki = x0i + x1r;
+            wji = x0r + x1i;
+            wjr = x0i - x1r;
+            kk2 += ks2;
+            x0r = a[j1][k2 + 1] - a[k1][k2];
+            x0i = a[k1][k2 + 1] + a[j1][k2];
+            x1r = a[k1][k2 + 1] - a[j1][k2];
+            x1i = a[j1][k2 + 1] + a[k1][k2];
+            a[k1][k2] = wkr * x0r + wki * x0i;
+            a[k1][k2 + 1] = wkr * x0i - wki * x0r;
+            a[j1][k2] = wjr * x1r + wji * x1i;
+            a[j1][k2 + 1] = wjr * x1i - wji * x1r;
+        }
+        x0r = 2 * c[kk2];
+        wjr = x0r * w1r;
+        wji = x0r * w1i;
+        x0r = w1r * a[k1][1] + w1i * a[k1][0];
+        x0i = w1r * a[k1][0] - w1i * a[k1][1];
+        x1r = -wjr * a[j1][1] + wji * a[j1][0];
+        x1i = wjr * a[j1][0] + wji * a[j1][1];
+        a[k1][0] = x0r + x1r;
+        a[k1][1] = x1i + x0i;
+        a[j1][0] = x0r - x1r;
+        a[j1][1] = x1i - x0i;
+    }
+    w1r = 2 * c[kk1];
+    kk2 = ks2;
+    for (k2 = 2; k2 <= n2 - 2; k2 += 2) {
+        wkr = 2 * c[kk2];
+        wki = 2 * c[nc - kk2];
+        wjr = w1r * wkr;
+        wji = w1r * wki;
+        kk2 += ks2;
+        x0i = wkr * a[0][k2] - wki * a[0][k2 + 1];
+        a[0][k2] = wkr * a[0][k2 + 1] + wki * a[0][k2];
+        a[0][k2 + 1] = x0i;
+        x0i = wjr * a[n1h][k2] - wji * a[n1h][k2 + 1];
+        a[n1h][k2] = wjr * a[n1h][k2 + 1] + wji * a[n1h][k2];
+        a[n1h][k2 + 1] = x0i;
+    }
+    w1r *= 2;
+    a[0][0] *= 2;
+    a[0][1] *= w1r;
+    a[n1h][0] *= w1r;
+}
+
diff --git a/third_party/tensorflow_dependencies/fft2d/fft4f2d.f b/third_party/tensorflow_dependencies/fft2d/fft4f2d.f
new file mode 100644
index 0000000..af529e3
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/fft4f2d.f
@@ -0,0 +1,1591 @@
+! Fast Fourier/Cosine/Sine Transform
+!     dimension   :two
+!     data length :power of 2
+!     decimation  :frequency
+!     radix       :4, 2, row-column
+!     data        :inplace
+!     table       :use
+! subroutines
+!     cdft2d: Complex Discrete Fourier Transform
+!     rdft2d: Real Discrete Fourier Transform
+!     ddct2d: Discrete Cosine Transform
+!     ddst2d: Discrete Sine Transform
+!
+!
+! -------- Complex DFT (Discrete Fourier Transform) --------
+!     [definition]
+!         <case1>
+!             X(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 x(j1,j2) * 
+!                            exp(2*pi*i*j1*k1/n1) * 
+!                            exp(2*pi*i*j2*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         <case2>
+!             X(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 x(j1,j2) * 
+!                            exp(-2*pi*i*j1*k1/n1) * 
+!                            exp(-2*pi*i*j2*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         (notes: sum_j=0^n-1 is a summation from j=0 to n-1)
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call cdft2d(n1max, 2*n1, n2, 1, a, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call cdft2d(n1max, 2*n1, n2, -1, a, ip, w)
+!     [parameters]
+!         n1max  :row size of the 2D array (integer)
+!         2*n1   :data length (integer)
+!                 n1 >= 1, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 1, n2 = power of 2
+!         a(0:2*n1-1,0:n2-1)
+!                :input/output data (real*8)
+!                 input data
+!                     a(2*j1,j2) = Re(x(j1,j2)), 
+!                     a(2*j1+1,j2) = Im(x(j1,j2)), 
+!                     0<=j1<n1, 0<=j2<n2
+!                 output data
+!                     a(2*k1,k2) = Re(X(k1,k2)), 
+!                     a(2*k1+1,k2) = Im(X(k1,k2)), 
+!                     0<=k1<n1, 0<=k2<n2
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1, n2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1/2, n2/2)
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call cdft2d(n1max, 2*n1, n2, -1, a, ip, w)
+!         is 
+!             call cdft2d(n1max, 2*n1, n2, 1, a, ip, w)
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, 2 * n1 - 1
+!                     a(j1, j2) = a(j1, j2) * (1.0d0 / (n1 * n2))
+!                 end do
+!             end do
+!         .
+!
+!
+! -------- Real DFT / Inverse of Real DFT --------
+!     [definition]
+!         <case1> RDFT
+!             R(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!             I(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         <case2> IRDFT (excluding scale)
+!             a(k1,k2) = (1/2) * sum_j1=0^n1-1 sum_j2=0^n2-1
+!                            (R(j1,j2) * 
+!                            cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2) + 
+!                            I(j1,j2) * 
+!                            sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2)), 
+!                            0<=k1<n1, 0<=k2<n2
+!         (notes: R(n1-k1,n2-k2) = R(k1,k2), 
+!                 I(n1-k1,n2-k2) = -I(k1,k2), 
+!                 R(n1-k1,0) = R(k1,0), 
+!                 I(n1-k1,0) = -I(k1,0), 
+!                 R(0,n2-k2) = R(0,k2), 
+!                 I(0,n2-k2) = -I(0,k2), 
+!                 0<k1<n1, 0<k2<n2)
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call rdft2d(n1max, n1, n2, 1, a, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call rdft2d(n1max, n1, n2, -1, a, ip, w)
+!     [parameters]
+!         n1max  :row size of the 2D array (integer)
+!         n1     :data length (integer)
+!                 n1 >= 2, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 2, n2 = power of 2
+!         a(0:n1-1,0:n2-1)
+!                :input/output data (real*8)
+!                 <case1>
+!                     output data
+!                         a(2*k1,k2) = R(k1,k2) = R(n1-k1,n2-k2), 
+!                         a(2*k1+1,k2) = I(k1,k2) = -I(n1-k1,n2-k2), 
+!                             0<k1<n1/2, 0<k2<n2, 
+!                         a(2*k1,0) = R(k1,0) = R(n1-k1,0), 
+!                         a(2*k1+1,0) = I(k1,0) = -I(n1-k1,0), 
+!                             0<k1<n1/2, 
+!                         a(0,k2) = R(0,k2) = R(0,n2-k2), 
+!                         a(1,k2) = I(0,k2) = -I(0,n2-k2), 
+!                         a(1,n2-k2) = R(n1/2,k2) = R(n1/2,n2-k2), 
+!                         a(0,n2-k2) = -I(n1/2,k2) = I(n1/2,n2-k2), 
+!                             0<k2<n2/2, 
+!                         a(0,0) = R(0,0), 
+!                         a(1,0) = R(n1/2,0), 
+!                         a(0,n2/2) = R(0,n2/2), 
+!                         a(1,n2/2) = R(n1/2,n2/2)
+!                 <case2>
+!                     input data
+!                         a(2*j1,j2) = R(j1,j2) = R(n1-j1,n2-j2), 
+!                         a(2*j1+1,j2) = I(j1,j2) = -I(n1-j1,n2-j2), 
+!                             0<j1<n1/2, 0<j2<n2, 
+!                         a(2*j1,0) = R(j1,0) = R(n1-j1,0), 
+!                         a(2*j1+1,0) = I(j1,0) = -I(n1-j1,0), 
+!                             0<j1<n1/2, 
+!                         a(0,j2) = R(0,j2) = R(0,n2-j2), 
+!                         a(1,j2) = I(0,j2) = -I(0,n2-j2), 
+!                         a(1,n2-j2) = R(n1/2,j2) = R(n1/2,n2-j2), 
+!                         a(0,n2-j2) = -I(n1/2,j2) = I(n1/2,n2-j2), 
+!                             0<j2<n2/2, 
+!                         a(0,0) = R(0,0), 
+!                         a(1,0) = R(n1/2,0), 
+!                         a(0,n2/2) = R(0,n2/2), 
+!                         a(1,n2/2) = R(n1/2,n2/2)
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1/2, n2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1/4, n2/2) + n1/4
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call rdft2d(n1max, n1, n2, 1, a, ip, w)
+!         is 
+!             call rdft2d(n1max, n1, n2, -1, a, ip, w)
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, n1 - 1
+!                     a(j1, j2) = a(j1, j2) * (2.0d0 / (n1 * n2))
+!                 end do
+!             end do
+!         .
+!
+!
+! -------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
+!     [definition]
+!         <case1> IDCT (excluding scale)
+!             C(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            cos(pi*j1*(k1+1/2)/n1) * 
+!                            cos(pi*j2*(k2+1/2)/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         <case2> DCT
+!             C(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            cos(pi*(j1+1/2)*k1/n1) * 
+!                            cos(pi*(j2+1/2)*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call ddct2d(n1max, n1, n2, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call ddct2d(n1max, n1, n2, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row size of the 2D array (integer)
+!         n1     :data length (integer)
+!                 n1 >= 2, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 2, n2 = power of 2
+!         a(0:n1-1,0:n2-1)
+!                :input/output data (real*8)
+!                 output data
+!                     a(k1,k2) = C(k1,k2), 0<=k1<n1, 0<=k2<n2
+!         t(0:n1-1,0:n2-1)
+!                :work area (real*8)
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1/2, n2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1/4, n2/2) + max(n1, n2)
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call ddct2d(n1max, n1, n2, -1, a, t, ip, w)
+!         is 
+!             do j1 = 0, n1 - 1
+!                 a(j1, 0) = a(j1, 0) * 0.5d0
+!             end do
+!             do j2 = 0, n2 - 1
+!                 a(0, j2) = a(0, j2) * 0.5d0
+!             end do
+!             call ddct2d(n1max, n1, n2, 1, a, t, ip, w)
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, n1 - 1
+!                     a(j1, j2) = a(j1, j2) * (4.0d0 / (n1 * n2))
+!                 end do
+!             end do
+!         .
+!
+!
+! -------- DST (Discrete Sine Transform) / Inverse of DST --------
+!     [definition]
+!         <case1> IDST (excluding scale)
+!             S(k1,k2) = sum_j1=1^n1 sum_j2=1^n2 A(j1,j2) * 
+!                            sin(pi*j1*(k1+1/2)/n1) * 
+!                            sin(pi*j2*(k2+1/2)/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         <case2> DST
+!             S(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            sin(pi*(j1+1/2)*k1/n1) * 
+!                            sin(pi*(j2+1/2)*k2/n2), 
+!                            0<k1<=n1, 0<k2<=n2
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call ddst2d(n1max, n1, n2, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call ddst2d(n1max, n1, n2, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row size of the 2D array (integer)
+!         n1     :data length (integer)
+!                 n1 >= 2, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 2, n2 = power of 2
+!         a(0:n1-1,0:n2-1)
+!                :input/output data (real*8)
+!                 <case1>
+!                     input data
+!                         a(j1,j2) = A(j1,j2), 0<j1<n1, 0<j2<n2, 
+!                         a(j1,0) = A(j1,n2), 0<j1<n1, 
+!                         a(0,j2) = A(n1,j2), 0<j2<n2, 
+!                         a(0,0) = A(n1,n2)
+!                         (i.e. A(j1,j2) = a(mod(j1,n1),mod(j2,n2)))
+!                     output data
+!                         a(k1,k2) = S(k1,k2), 0<=k1<n1, 0<=k2<n2
+!                 <case2>
+!                     output data
+!                         a(k1,k2) = S(k1,k2), 0<k1<n1, 0<k2<n2, 
+!                         a(k1,0) = S(k1,n2), 0<k1<n1, 
+!                         a(0,k2) = S(n1,k2), 0<k2<n2, 
+!                         a(0,0) = S(n1,n2)
+!                         (i.e. S(k1,k2) = a(mod(k1,n1),mod(k2,n2)))
+!         t(0:n1-1,0:n2-1)
+!                :work area (real*8)
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1/2, n2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1/4, n2/2) + max(n1, n2)
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call ddst2d(n1max, n1, n2, -1, a, t, ip, w)
+!         is 
+!             do j1 = 0, n1 - 1
+!                 a(j1, 0) = a(j1, 0) * 0.5d0
+!             end do
+!             do j2 = 0, n2 - 1
+!                 a(0, j2) = a(0, j2) * 0.5d0
+!             end do
+!             call ddst2d(n1max, n1, n2, 1, a, t, ip, w)
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, n1 - 1
+!                     a(j1, j2) = a(j1, j2) * (4.0d0 / (n1 * n2))
+!                 end do
+!             end do
+!         .
+!
+!
+      subroutine cdft2d(n1max, n1, n2, isgn, a, ip, w)
+      integer n1max, n1, n2, isgn, ip(0 : *), n
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), w(0 : *)
+      n = max(n1, 2 * n2)
+      if (n .gt. 4 * ip(0)) then
+          call makewt(n / 4, ip, w)
+      end if
+      if (n1 .gt. 4) then
+          call bitrv2row(n1max, n1, n2, ip(2), a)
+      end if
+      if (n2 .gt. 2) then
+          call bitrv2col(n1max, n1, n2, ip(2), a)
+      end if
+      if (isgn .lt. 0) then
+          call cftfrow(n1max, n1, n2, a, w)
+          call cftfcol(n1max, n1, n2, a, w)
+      else
+          call cftbrow(n1max, n1, n2, a, w)
+          call cftbcol(n1max, n1, n2, a, w)
+      end if
+      end
+!
+      subroutine rdft2d(n1max, n1, n2, isgn, a, ip, w)
+      integer n1max, n1, n2, isgn, ip(0 : *), n, nw, nc, n2h, i, j
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), w(0 : *), xi
+      n = max(n1, 2 * n2)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n1 .gt. 4 * nc) then
+          nc = n1 / 4
+          call makect(nc, ip, w(nw))
+      end if
+      n2h = n2 / 2
+      if (isgn .lt. 0) then
+          do i = 1, n2h - 1
+              j = n2 - i
+              xi = a(0, i) - a(0, j)
+              a(0, i) = a(0, i) + a(0, j)
+              a(0, j) = xi
+              xi = a(1, j) - a(1, i)
+              a(1, i) = a(1, i) + a(1, j)
+              a(1, j) = xi
+          end do
+          if (n2 .gt. 2) then
+              call bitrv2col(n1max, n1, n2, ip(2), a)
+          end if
+          call cftfcol(n1max, n1, n2, a, w)
+          do i = 0, n2 - 1
+              a(1, i) = 0.5d0 * (a(0, i) - a(1, i))
+              a(0, i) = a(0, i) - a(1, i)
+          end do
+          if (n1 .gt. 4) then
+              call rftfrow(n1max, n1, n2, a, nc, w(nw))
+              call bitrv2row(n1max, n1, n2, ip(2), a)
+          end if
+          call cftfrow(n1max, n1, n2, a, w)
+      else
+          if (n1 .gt. 4) then
+              call bitrv2row(n1max, n1, n2, ip(2), a)
+          end if
+          call cftbrow(n1max, n1, n2, a, w)
+          if (n1 .gt. 4) then
+              call rftbrow(n1max, n1, n2, a, nc, w(nw))
+          end if
+          do i = 0, n2 - 1
+              xi = a(0, i) - a(1, i)
+              a(0, i) = a(0, i) + a(1, i)
+              a(1, i) = xi
+          end do
+          if (n2 .gt. 2) then
+              call bitrv2col(n1max, n1, n2, ip(2), a)
+          end if
+          call cftbcol(n1max, n1, n2, a, w)
+          do i = 1, n2h - 1
+              j = n2 - i
+              a(0, j) = 0.5d0 * (a(0, i) - a(0, j))
+              a(0, i) = a(0, i) - a(0, j)
+              a(1, j) = 0.5d0 * (a(1, i) + a(1, j))
+              a(1, i) = a(1, i) - a(1, j)
+          end do
+      end if
+      end
+!
+      subroutine ddct2d(n1max, n1, n2, isgn, a, t, ip, w)
+      integer n1max, n1, n2, isgn, ip(0 : *), n, nw, nc, n1h, n2h, 
+     &    i, ix, ic, j, jx, jc
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), 
+     &    t(0 : n1max - 1, 0 : n2 - 1), w(0 : *), xi
+      n = max(n1, 2 * n2)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n1 .gt. nc .or. n2 .gt. nc) then
+          nc = max(n1, n2)
+          call makect(nc, ip, w(nw))
+      end if
+      n1h = n1 / 2
+      n2h = n2 / 2
+      if (isgn .ge. 0) then
+          do i = 0, n2 - 1
+              do j = 1, n1h - 1
+                  jx = 2 * j
+                  t(jx, i) = a(j, i)
+                  t(jx + 1, i) = a(n1 - j, i)
+              end do
+          end do
+          t(0, 0) = a(0, 0)
+          t(1, 0) = a(n1h, 0)
+          t(0, n2h) = a(0, n2h)
+          t(1, n2h) = a(n1h, n2h)
+          do i = 1, n2h - 1
+              ic = n2 - i
+              t(0, i) = a(0, i)
+              t(1, ic) = a(n1h, i)
+              t(1, i) = a(0, ic)
+              t(0, ic) = a(n1h, ic)
+          end do
+          call dctfsub(n1max, n1, n2, t, nc, w(nw))
+          if (n2 .gt. 2) then
+              call bitrv2col(n1max, n1, n2, ip(2), t)
+          end if
+          call cftfcol(n1max, n1, n2, t, w)
+          do i = 0, n2 - 1
+              t(1, i) = 0.5d0 * (t(0, i) - t(1, i))
+              t(0, i) = t(0, i) - t(1, i)
+          end do
+          if (n1 .gt. 4) then
+              call rftfrow(n1max, n1, n2, t, nc, w(nw))
+              call bitrv2row(n1max, n1, n2, ip(2), t)
+          end if
+          call cftfrow(n1max, n1, n2, t, w)
+          do i = 0, n2h - 1
+              ix = 2 * i
+              ic = n2 - 1 - i
+              do j = 0, n1h - 1
+                  jx = 2 * j
+                  jc = n1 - 1 - j
+                  a(jx, ix) = t(j, i)
+                  a(jx + 1, ix) = t(jc, i)
+                  a(jx, ix + 1) = t(j, ic)
+                  a(jx + 1, ix + 1) = t(jc, ic)
+              end do
+          end do
+      else
+          do i = 0, n2h - 1
+              ix = 2 * i
+              ic = n2 - 1 - i
+              do j = 0, n1h - 1
+                  jx = 2 * j
+                  jc = n1 - 1 - j
+                  t(j, i) = a(jx, ix)
+                  t(jc, i) = a(jx + 1, ix)
+                  t(j, ic) = a(jx, ix + 1)
+                  t(jc, ic) = a(jx + 1, ix + 1)
+              end do
+          end do
+          if (n1 .gt. 4) then
+              call bitrv2row(n1max, n1, n2, ip(2), t)
+          end if
+          call cftbrow(n1max, n1, n2, t, w)
+          if (n1 .gt. 4) then
+              call rftbrow(n1max, n1, n2, t, nc, w(nw))
+          end if
+          do i = 0, n2 - 1
+              xi = t(0, i) - t(1, i)
+              t(0, i) = t(0, i) + t(1, i)
+              t(1, i) = xi
+          end do
+          if (n2 .gt. 2) then
+              call bitrv2col(n1max, n1, n2, ip(2), t)
+          end if
+          call cftbcol(n1max, n1, n2, t, w)
+          call dctbsub(n1max, n1, n2, t, nc, w(nw))
+          do i = 0, n2 - 1
+              do j = 1, n1h - 1
+                  jx = 2 * j
+                  a(j, i) = t(jx, i)
+                  a(n1 - j, i) = t(jx + 1, i)
+              end do
+          end do
+          a(0, 0) = t(0, 0)
+          a(n1h, 0) = t(1, 0)
+          a(0, n2h) = t(0, n2h)
+          a(n1h, n2h) = t(1, n2h)
+          do i = 1, n2h - 1
+              ic = n2 - i
+              a(0, i) = t(0, i)
+              a(n1h, i) = t(1, ic)
+              a(0, ic) = t(1, i)
+              a(n1h, ic) = t(0, ic)
+          end do
+      end if
+      end
+!
+      subroutine ddst2d(n1max, n1, n2, isgn, a, t, ip, w)
+      integer n1max, n1, n2, isgn, ip(0 : *), n, nw, nc, n1h, n2h, 
+     &    i, ix, ic, j, jx, jc
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), 
+     &    t(0 : n1max - 1, 0 : n2 - 1), w(0 : *), xi
+      n = max(n1, 2 * n2)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n1 .gt. nc .or. n2 .gt. nc) then
+          nc = max(n1, n2)
+          call makect(nc, ip, w(nw))
+      end if
+      n1h = n1 / 2
+      n2h = n2 / 2
+      if (isgn .ge. 0) then
+          do i = 0, n2 - 1
+              do j = 1, n1h - 1
+                  jx = 2 * j
+                  t(jx, i) = a(j, i)
+                  t(jx + 1, i) = a(n1 - j, i)
+              end do
+          end do
+          t(0, 0) = a(0, 0)
+          t(1, 0) = a(n1h, 0)
+          t(0, n2h) = a(0, n2h)
+          t(1, n2h) = a(n1h, n2h)
+          do i = 1, n2h - 1
+              ic = n2 - i
+              t(0, i) = a(0, i)
+              t(1, ic) = a(n1h, i)
+              t(1, i) = a(0, ic)
+              t(0, ic) = a(n1h, ic)
+          end do
+          call dstfsub(n1max, n1, n2, t, nc, w(nw))
+          if (n2 .gt. 2) then
+              call bitrv2col(n1max, n1, n2, ip(2), t)
+          end if
+          call cftfcol(n1max, n1, n2, t, w)
+          do i = 0, n2 - 1
+              t(1, i) = 0.5d0 * (t(0, i) - t(1, i))
+              t(0, i) = t(0, i) - t(1, i)
+          end do
+          if (n1 .gt. 4) then
+              call rftfrow(n1max, n1, n2, t, nc, w(nw))
+              call bitrv2row(n1max, n1, n2, ip(2), t)
+          end if
+          call cftfrow(n1max, n1, n2, t, w)
+          do i = 0, n2h - 1
+              ix = 2 * i
+              ic = n2 - 1 - i
+              do j = 0, n1h - 1
+                  jx = 2 * j
+                  jc = n1 - 1 - j
+                  a(jx, ix) = t(j, i)
+                  a(jx + 1, ix) = -t(jc, i)
+                  a(jx, ix + 1) = -t(j, ic)
+                  a(jx + 1, ix + 1) = t(jc, ic)
+              end do
+          end do
+      else
+          do i = 0, n2h - 1
+              ix = 2 * i
+              ic = n2 - 1 - i
+              do j = 0, n1h - 1
+                  jx = 2 * j
+                  jc = n1 - 1 - j
+                  t(j, i) = a(jx, ix)
+                  t(jc, i) = -a(jx + 1, ix)
+                  t(j, ic) = -a(jx, ix + 1)
+                  t(jc, ic) = a(jx + 1, ix + 1)
+              end do
+          end do
+          if (n1 .gt. 4) then
+              call bitrv2row(n1max, n1, n2, ip(2), t)
+          end if
+          call cftbrow(n1max, n1, n2, t, w)
+          if (n1 .gt. 4) then
+              call rftbrow(n1max, n1, n2, t, nc, w(nw))
+          end if
+          do i = 0, n2 - 1
+              xi = t(0, i) - t(1, i)
+              t(0, i) = t(0, i) + t(1, i)
+              t(1, i) = xi
+          end do
+          if (n2 .gt. 2) then
+              call bitrv2col(n1max, n1, n2, ip(2), t)
+          end if
+          call cftbcol(n1max, n1, n2, t, w)
+          call dstbsub(n1max, n1, n2, t, nc, w(nw))
+          do i = 0, n2 - 1
+              do j = 1, n1h - 1
+                  jx = 2 * j
+                  a(j, i) = t(jx, i)
+                  a(n1 - j, i) = t(jx + 1, i)
+              end do
+          end do
+          a(0, 0) = t(0, 0)
+          a(n1h, 0) = t(1, 0)
+          a(0, n2h) = t(0, n2h)
+          a(n1h, n2h) = t(1, n2h)
+          do i = 1, n2h - 1
+              ic = n2 - i
+              a(0, i) = t(0, i)
+              a(n1h, i) = t(1, ic)
+              a(0, ic) = t(1, i)
+              a(n1h, ic) = t(0, ic)
+          end do
+      end if
+      end
+!
+! -------- initializing routines --------
+!
+      subroutine makewt(nw, ip, w)
+      integer nw, ip(0 : *), nwh, j
+      real*8 w(0 : nw - 1), delta, x, y
+      ip(0) = nw
+      ip(1) = 1
+      if (nw .gt. 2) then
+          nwh = nw / 2
+          delta = atan(1.0d0) / nwh
+          w(0) = 1
+          w(1) = 0
+          w(nwh) = cos(delta * nwh)
+          w(nwh + 1) = w(nwh)
+          do j = 2, nwh - 2, 2
+              x = cos(delta * j)
+              y = sin(delta * j)
+              w(j) = x
+              w(j + 1) = y
+              w(nw - j) = y
+              w(nw - j + 1) = x
+          end do
+          call bitrv2(nw, ip(2), w)
+      end if
+      end
+!
+      subroutine makect(nc, ip, c)
+      integer nc, ip(0 : *), nch, j
+      real*8 c(0 : nc - 1), delta
+      ip(1) = nc
+      if (nc .gt. 1) then
+          nch = nc / 2
+          delta = atan(1.0d0) / nch
+          c(0) = 0.5d0
+          c(nch) = 0.5d0 * cos(delta * nch)
+          do j = 1, nch - 1
+              c(j) = 0.5d0 * cos(delta * j)
+              c(nc - j) = 0.5d0 * sin(delta * j)
+          end do
+      end if
+      end
+!
+! -------- child routines --------
+!
+      subroutine bitrv2(n, ip, a)
+      integer n, ip(0 : *), j, j1, k, k1, l, m, m2
+      real*8 a(0 : n - 1), xr, xi
+      ip(0) = 0
+      l = n
+      m = 1
+      do while (4 * m .lt. l)
+          l = l / 2
+          do j = 0, m - 1
+              ip(m + j) = ip(j) + l
+          end do
+          m = m * 2
+      end do
+      if (4 * m .gt. l) then
+          do k = 1, m - 1
+              do j = 0, k - 1
+                  j1 = 2 * j + ip(k)
+                  k1 = 2 * k + ip(j)
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  a(j1) = a(k1)
+                  a(j1 + 1) = a(k1 + 1)
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+              end do
+          end do
+      else
+          m2 = 2 * m
+          do k = 1, m - 1
+              do j = 0, k - 1
+                  j1 = 2 * j + ip(k)
+                  k1 = 2 * k + ip(j)
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  a(j1) = a(k1)
+                  a(j1 + 1) = a(k1 + 1)
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + m2
+                  k1 = k1 + m2
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  a(j1) = a(k1)
+                  a(j1 + 1) = a(k1 + 1)
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+              end do
+          end do
+      end if
+      end
+!
+      subroutine bitrv2row(n1max, n, n2, ip, a)
+      integer n1max, n, n2, ip(0 : *), i, j, j1, k, k1, l, m, m2
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), xr, xi
+      ip(0) = 0
+      l = n
+      m = 1
+      do while (4 * m .lt. l)
+          l = l / 2
+          do j = 0, m - 1
+              ip(m + j) = ip(j) + l
+          end do
+          m = m * 2
+      end do
+      if (4 * m .gt. l) then
+          do i = 0, n2 - 1
+              do k = 1, m - 1
+                  do j = 0, k - 1
+                      j1 = 2 * j + ip(k)
+                      k1 = 2 * k + ip(j)
+                      xr = a(j1, i)
+                      xi = a(j1 + 1, i)
+                      a(j1, i) = a(k1, i)
+                      a(j1 + 1, i) = a(k1 + 1, i)
+                      a(k1, i) = xr
+                      a(k1 + 1, i) = xi
+                  end do
+              end do
+          end do
+      else
+          m2 = 2 * m
+          do i = 0, n2 - 1
+              do k = 1, m - 1
+                  do j = 0, k - 1
+                      j1 = 2 * j + ip(k)
+                      k1 = 2 * k + ip(j)
+                      xr = a(j1, i)
+                      xi = a(j1 + 1, i)
+                      a(j1, i) = a(k1, i)
+                      a(j1 + 1, i) = a(k1 + 1, i)
+                      a(k1, i) = xr
+                      a(k1 + 1, i) = xi
+                      j1 = j1 + m2
+                      k1 = k1 + m2
+                      xr = a(j1, i)
+                      xi = a(j1 + 1, i)
+                      a(j1, i) = a(k1, i)
+                      a(j1 + 1, i) = a(k1 + 1, i)
+                      a(k1, i) = xr
+                      a(k1 + 1, i) = xi
+                  end do
+              end do
+          end do
+      end if
+      end
+!
+      subroutine bitrv2col(n1max, n1, n, ip, a)
+      integer n1max, n1, n, ip(0 : *), i, j, j1, k, k1, l, m
+      real*8 a(0 : n1max - 1, 0 : n - 1), xr, xi
+      ip(0) = 0
+      l = n
+      m = 1
+      do while (2 * m .lt. l)
+          l = l / 2
+          do j = 0, m - 1
+              ip(m + j) = ip(j) + l
+          end do
+          m = m * 2
+      end do
+      if (2 * m .gt. l) then
+          do k = 1, m - 1
+              do j = 0, k - 1
+                  j1 = j + ip(k)
+                  k1 = k + ip(j)
+                  do i = 0, n1 - 2, 2
+                      xr = a(i, j1)
+                      xi = a(i + 1, j1)
+                      a(i, j1) = a(i, k1)
+                      a(i + 1, j1) = a(i + 1, k1)
+                      a(i, k1) = xr
+                      a(i + 1, k1) = xi
+                  end do
+              end do
+          end do
+      else
+          do k = 1, m - 1
+              do j = 0, k - 1
+                  j1 = j + ip(k)
+                  k1 = k + ip(j)
+                  do i = 0, n1 - 2, 2
+                      xr = a(i, j1)
+                      xi = a(i + 1, j1)
+                      a(i, j1) = a(i, k1)
+                      a(i + 1, j1) = a(i + 1, k1)
+                      a(i, k1) = xr
+                      a(i + 1, k1) = xi
+                  end do
+                  j1 = j1 + m
+                  k1 = k1 + m
+                  do i = 0, n1 - 2, 2
+                      xr = a(i, j1)
+                      xi = a(i + 1, j1)
+                      a(i, j1) = a(i, k1)
+                      a(i + 1, j1) = a(i + 1, k1)
+                      a(i, k1) = xr
+                      a(i + 1, k1) = xi
+                  end do
+              end do
+          end do
+      end if
+      end
+!
+      subroutine cftbrow(n1max, n, n2, a, w)
+      integer n1max, n, n2, i, j, j1, j2, j3, k, k1, ks, l, m
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), w(0 : *)
+      real*8 wk1r, wk1i, wk2r, wk2i, wk3r, wk3i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      do i = 0, n2 - 1
+          l = 2
+          do while (2 * l .lt. n)
+              m = 4 * l
+              do j = 0, l - 2, 2
+                  j1 = j + l
+                  j2 = j1 + l
+                  j3 = j2 + l
+                  x0r = a(j, i) + a(j1, i)
+                  x0i = a(j + 1, i) + a(j1 + 1, i)
+                  x1r = a(j, i) - a(j1, i)
+                  x1i = a(j + 1, i) - a(j1 + 1, i)
+                  x2r = a(j2, i) + a(j3, i)
+                  x2i = a(j2 + 1, i) + a(j3 + 1, i)
+                  x3r = a(j2, i) - a(j3, i)
+                  x3i = a(j2 + 1, i) - a(j3 + 1, i)
+                  a(j, i) = x0r + x2r
+                  a(j + 1, i) = x0i + x2i
+                  a(j2, i) = x0r - x2r
+                  a(j2 + 1, i) = x0i - x2i
+                  a(j1, i) = x1r - x3i
+                  a(j1 + 1, i) = x1i + x3r
+                  a(j3, i) = x1r + x3i
+                  a(j3 + 1, i) = x1i - x3r
+              end do
+              if (m .lt. n) then
+                  wk1r = w(2)
+                  do j = m, l + m - 2, 2
+                      j1 = j + l
+                      j2 = j1 + l
+                      j3 = j2 + l
+                      x0r = a(j, i) + a(j1, i)
+                      x0i = a(j + 1, i) + a(j1 + 1, i)
+                      x1r = a(j, i) - a(j1, i)
+                      x1i = a(j + 1, i) - a(j1 + 1, i)
+                      x2r = a(j2, i) + a(j3, i)
+                      x2i = a(j2 + 1, i) + a(j3 + 1, i)
+                      x3r = a(j2, i) - a(j3, i)
+                      x3i = a(j2 + 1, i) - a(j3 + 1, i)
+                      a(j, i) = x0r + x2r
+                      a(j + 1, i) = x0i + x2i
+                      a(j2, i) = x2i - x0i
+                      a(j2 + 1, i) = x0r - x2r
+                      x0r = x1r - x3i
+                      x0i = x1i + x3r
+                      a(j1, i) = wk1r * (x0r - x0i)
+                      a(j1 + 1, i) = wk1r * (x0r + x0i)
+                      x0r = x3i + x1r
+                      x0i = x3r - x1i
+                      a(j3, i) = wk1r * (x0i - x0r)
+                      a(j3 + 1, i) = wk1r * (x0i + x0r)
+                  end do
+                  k1 = 1
+                  ks = -1
+                  do k = 2 * m, n - m, m
+                      k1 = k1 + 1
+                      ks = -ks
+                      wk1r = w(2 * k1)
+                      wk1i = w(2 * k1 + 1)
+                      wk2r = ks * w(k1)
+                      wk2i = w(k1 + ks)
+                      wk3r = wk1r - 2 * wk2i * wk1i
+                      wk3i = 2 * wk2i * wk1r - wk1i
+                      do j = k, l + k - 2, 2
+                          j1 = j + l
+                          j2 = j1 + l
+                          j3 = j2 + l
+                          x0r = a(j, i) + a(j1, i)
+                          x0i = a(j + 1, i) + a(j1 + 1, i)
+                          x1r = a(j, i) - a(j1, i)
+                          x1i = a(j + 1, i) - a(j1 + 1, i)
+                          x2r = a(j2, i) + a(j3, i)
+                          x2i = a(j2 + 1, i) + a(j3 + 1, i)
+                          x3r = a(j2, i) - a(j3, i)
+                          x3i = a(j2 + 1, i) - a(j3 + 1, i)
+                          a(j, i) = x0r + x2r
+                          a(j + 1, i) = x0i + x2i
+                          x0r = x0r - x2r
+                          x0i = x0i - x2i
+                          a(j2, i) = wk2r * x0r - wk2i * x0i
+                          a(j2 + 1, i) = wk2r * x0i + wk2i * x0r
+                          x0r = x1r - x3i
+                          x0i = x1i + x3r
+                          a(j1, i) = wk1r * x0r - wk1i * x0i
+                          a(j1 + 1, i) = wk1r * x0i + wk1i * x0r
+                          x0r = x1r + x3i
+                          x0i = x1i - x3r
+                          a(j3, i) = wk3r * x0r - wk3i * x0i
+                          a(j3 + 1, i) = wk3r * x0i + wk3i * x0r
+                      end do
+                  end do
+              end if
+              l = m
+          end do
+          if (l .lt. n) then
+              do j = 0, l - 2, 2
+                  j1 = j + l
+                  x0r = a(j, i) - a(j1, i)
+                  x0i = a(j + 1, i) - a(j1 + 1, i)
+                  a(j, i) = a(j, i) + a(j1, i)
+                  a(j + 1, i) = a(j + 1, i) + a(j1 + 1, i)
+                  a(j1, i) = x0r
+                  a(j1 + 1, i) = x0i
+              end do
+          end if
+      end do
+      end
+!
+      subroutine cftbcol(n1max, n1, n, a, w)
+      integer n1max, n1, n, i, j, j1, j2, j3, k, k1, ks, l, m
+      real*8 a(0 : n1max - 1, 0 : n - 1), w(0 : *)
+      real*8 wk1r, wk1i, wk2r, wk2i, wk3r, wk3i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      l = 1
+      do while (2 * l .lt. n)
+          m = 4 * l
+          do j = 0, l - 1
+              j1 = j + l
+              j2 = j1 + l
+              j3 = j2 + l
+              do i = 0, n1 - 2, 2
+                  x0r = a(i, j) + a(i, j1)
+                  x0i = a(i + 1, j) + a(i + 1, j1)
+                  x1r = a(i, j) - a(i, j1)
+                  x1i = a(i + 1, j) - a(i + 1, j1)
+                  x2r = a(i, j2) + a(i, j3)
+                  x2i = a(i + 1, j2) + a(i + 1, j3)
+                  x3r = a(i, j2) - a(i, j3)
+                  x3i = a(i + 1, j2) - a(i + 1, j3)
+                  a(i, j) = x0r + x2r
+                  a(i + 1, j) = x0i + x2i
+                  a(i, j2) = x0r - x2r
+                  a(i + 1, j2) = x0i - x2i
+                  a(i, j1) = x1r - x3i
+                  a(i + 1, j1) = x1i + x3r
+                  a(i, j3) = x1r + x3i
+                  a(i + 1, j3) = x1i - x3r
+              end do
+          end do
+          if (m .lt. n) then
+              wk1r = w(2)
+              do j = m, l + m - 1
+                  j1 = j + l
+                  j2 = j1 + l
+                  j3 = j2 + l
+                  do i = 0, n1 - 2, 2
+                      x0r = a(i, j) + a(i, j1)
+                      x0i = a(i + 1, j) + a(i + 1, j1)
+                      x1r = a(i, j) - a(i, j1)
+                      x1i = a(i + 1, j) - a(i + 1, j1)
+                      x2r = a(i, j2) + a(i, j3)
+                      x2i = a(i + 1, j2) + a(i + 1, j3)
+                      x3r = a(i, j2) - a(i, j3)
+                      x3i = a(i + 1, j2) - a(i + 1, j3)
+                      a(i, j) = x0r + x2r
+                      a(i + 1, j) = x0i + x2i
+                      a(i, j2) = x2i - x0i
+                      a(i + 1, j2) = x0r - x2r
+                      x0r = x1r - x3i
+                      x0i = x1i + x3r
+                      a(i, j1) = wk1r * (x0r - x0i)
+                      a(i + 1, j1) = wk1r * (x0r + x0i)
+                      x0r = x3i + x1r
+                      x0i = x3r - x1i
+                      a(i, j3) = wk1r * (x0i - x0r)
+                      a(i + 1, j3) = wk1r * (x0i + x0r)
+                  end do
+              end do
+              k1 = 1
+              ks = -1
+              do k = 2 * m, n - m, m
+                  k1 = k1 + 1
+                  ks = -ks
+                  wk1r = w(2 * k1)
+                  wk1i = w(2 * k1 + 1)
+                  wk2r = ks * w(k1)
+                  wk2i = w(k1 + ks)
+                  wk3r = wk1r - 2 * wk2i * wk1i
+                  wk3i = 2 * wk2i * wk1r - wk1i
+                  do j = k, l + k - 1
+                      j1 = j + l
+                      j2 = j1 + l
+                      j3 = j2 + l
+                      do i = 0, n1 - 2, 2
+                          x0r = a(i, j) + a(i, j1)
+                          x0i = a(i + 1, j) + a(i + 1, j1)
+                          x1r = a(i, j) - a(i, j1)
+                          x1i = a(i + 1, j) - a(i + 1, j1)
+                          x2r = a(i, j2) + a(i, j3)
+                          x2i = a(i + 1, j2) + a(i + 1, j3)
+                          x3r = a(i, j2) - a(i, j3)
+                          x3i = a(i + 1, j2) - a(i + 1, j3)
+                          a(i, j) = x0r + x2r
+                          a(i + 1, j) = x0i + x2i
+                          x0r = x0r - x2r
+                          x0i = x0i - x2i
+                          a(i, j2) = wk2r * x0r - wk2i * x0i
+                          a(i + 1, j2) = wk2r * x0i + wk2i * x0r
+                          x0r = x1r - x3i
+                          x0i = x1i + x3r
+                          a(i, j1) = wk1r * x0r - wk1i * x0i
+                          a(i + 1, j1) = wk1r * x0i + wk1i * x0r
+                          x0r = x1r + x3i
+                          x0i = x1i - x3r
+                          a(i, j3) = wk3r * x0r - wk3i * x0i
+                          a(i + 1, j3) = wk3r * x0i + wk3i * x0r
+                      end do
+                  end do
+              end do
+          end if
+          l = m
+      end do
+      if (l .lt. n) then
+          do j = 0, l - 1
+              j1 = j + l
+              do i = 0, n1 - 2, 2
+                  x0r = a(i, j) - a(i, j1)
+                  x0i = a(i + 1, j) - a(i + 1, j1)
+                  a(i, j) = a(i, j) + a(i, j1)
+                  a(i + 1, j) = a(i + 1, j) + a(i + 1, j1)
+                  a(i, j1) = x0r
+                  a(i + 1, j1) = x0i
+              end do
+          end do
+      end if
+      end
+!
+      subroutine cftfrow(n1max, n, n2, a, w)
+      integer n1max, n, n2, i, j, j1, j2, j3, k, k1, ks, l, m
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), w(0 : *)
+      real*8 wk1r, wk1i, wk2r, wk2i, wk3r, wk3i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      do i = 0, n2 - 1
+          l = 2
+          do while (2 * l .lt. n)
+              m = 4 * l
+              do j = 0, l - 2, 2
+                  j1 = j + l
+                  j2 = j1 + l
+                  j3 = j2 + l
+                  x0r = a(j, i) + a(j1, i)
+                  x0i = a(j + 1, i) + a(j1 + 1, i)
+                  x1r = a(j, i) - a(j1, i)
+                  x1i = a(j + 1, i) - a(j1 + 1, i)
+                  x2r = a(j2, i) + a(j3, i)
+                  x2i = a(j2 + 1, i) + a(j3 + 1, i)
+                  x3r = a(j2, i) - a(j3, i)
+                  x3i = a(j2 + 1, i) - a(j3 + 1, i)
+                  a(j, i) = x0r + x2r
+                  a(j + 1, i) = x0i + x2i
+                  a(j2, i) = x0r - x2r
+                  a(j2 + 1, i) = x0i - x2i
+                  a(j1, i) = x1r + x3i
+                  a(j1 + 1, i) = x1i - x3r
+                  a(j3, i) = x1r - x3i
+                  a(j3 + 1, i) = x1i + x3r
+              end do
+              if (m .lt. n) then
+                  wk1r = w(2)
+                  do j = m, l + m - 2, 2
+                      j1 = j + l
+                      j2 = j1 + l
+                      j3 = j2 + l
+                      x0r = a(j, i) + a(j1, i)
+                      x0i = a(j + 1, i) + a(j1 + 1, i)
+                      x1r = a(j, i) - a(j1, i)
+                      x1i = a(j + 1, i) - a(j1 + 1, i)
+                      x2r = a(j2, i) + a(j3, i)
+                      x2i = a(j2 + 1, i) + a(j3 + 1, i)
+                      x3r = a(j2, i) - a(j3, i)
+                      x3i = a(j2 + 1, i) - a(j3 + 1, i)
+                      a(j, i) = x0r + x2r
+                      a(j + 1, i) = x0i + x2i
+                      a(j2, i) = x0i - x2i
+                      a(j2 + 1, i) = x2r - x0r
+                      x0r = x1r + x3i
+                      x0i = x1i - x3r
+                      a(j1, i) = wk1r * (x0i + x0r)
+                      a(j1 + 1, i) = wk1r * (x0i - x0r)
+                      x0r = x3i - x1r
+                      x0i = x3r + x1i
+                      a(j3, i) = wk1r * (x0r + x0i)
+                      a(j3 + 1, i) = wk1r * (x0r - x0i)
+                  end do
+                  k1 = 1
+                  ks = -1
+                  do k = 2 * m, n - m, m
+                      k1 = k1 + 1
+                      ks = -ks
+                      wk1r = w(2 * k1)
+                      wk1i = w(2 * k1 + 1)
+                      wk2r = ks * w(k1)
+                      wk2i = w(k1 + ks)
+                      wk3r = wk1r - 2 * wk2i * wk1i
+                      wk3i = 2 * wk2i * wk1r - wk1i
+                      do j = k, l + k - 2, 2
+                          j1 = j + l
+                          j2 = j1 + l
+                          j3 = j2 + l
+                          x0r = a(j, i) + a(j1, i)
+                          x0i = a(j + 1, i) + a(j1 + 1, i)
+                          x1r = a(j, i) - a(j1, i)
+                          x1i = a(j + 1, i) - a(j1 + 1, i)
+                          x2r = a(j2, i) + a(j3, i)
+                          x2i = a(j2 + 1, i) + a(j3 + 1, i)
+                          x3r = a(j2, i) - a(j3, i)
+                          x3i = a(j2 + 1, i) - a(j3 + 1, i)
+                          a(j, i) = x0r + x2r
+                          a(j + 1, i) = x0i + x2i
+                          x0r = x0r - x2r
+                          x0i = x0i - x2i
+                          a(j2, i) = wk2r * x0r + wk2i * x0i
+                          a(j2 + 1, i) = wk2r * x0i - wk2i * x0r
+                          x0r = x1r + x3i
+                          x0i = x1i - x3r
+                          a(j1, i) = wk1r * x0r + wk1i * x0i
+                          a(j1 + 1, i) = wk1r * x0i - wk1i * x0r
+                          x0r = x1r - x3i
+                          x0i = x1i + x3r
+                          a(j3, i) = wk3r * x0r + wk3i * x0i
+                          a(j3 + 1, i) = wk3r * x0i - wk3i * x0r
+                      end do
+                  end do
+              end if
+              l = m
+          end do
+          if (l .lt. n) then
+              do j = 0, l - 2, 2
+                  j1 = j + l
+                  x0r = a(j, i) - a(j1, i)
+                  x0i = a(j + 1, i) - a(j1 + 1, i)
+                  a(j, i) = a(j, i) + a(j1, i)
+                  a(j + 1, i) = a(j + 1, i) + a(j1 + 1, i)
+                  a(j1, i) = x0r
+                  a(j1 + 1, i) = x0i
+              end do
+          end if
+      end do
+      end
+!
+      subroutine cftfcol(n1max, n1, n, a, w)
+      integer n1max, n1, n, i, j, j1, j2, j3, k, k1, ks, l, m
+      real*8 a(0 : n1max - 1, 0 : n - 1), w(0 : *)
+      real*8 wk1r, wk1i, wk2r, wk2i, wk3r, wk3i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      l = 1
+      do while (2 * l .lt. n)
+          m = 4 * l
+          do j = 0, l - 1
+              j1 = j + l
+              j2 = j1 + l
+              j3 = j2 + l
+              do i = 0, n1 - 2, 2
+                  x0r = a(i, j) + a(i, j1)
+                  x0i = a(i + 1, j) + a(i + 1, j1)
+                  x1r = a(i, j) - a(i, j1)
+                  x1i = a(i + 1, j) - a(i + 1, j1)
+                  x2r = a(i, j2) + a(i, j3)
+                  x2i = a(i + 1, j2) + a(i + 1, j3)
+                  x3r = a(i, j2) - a(i, j3)
+                  x3i = a(i + 1, j2) - a(i + 1, j3)
+                  a(i, j) = x0r + x2r
+                  a(i + 1, j) = x0i + x2i
+                  a(i, j2) = x0r - x2r
+                  a(i + 1, j2) = x0i - x2i
+                  a(i, j1) = x1r + x3i
+                  a(i + 1, j1) = x1i - x3r
+                  a(i, j3) = x1r - x3i
+                  a(i + 1, j3) = x1i + x3r
+              end do
+          end do
+          if (m .lt. n) then
+              wk1r = w(2)
+              do j = m, l + m - 1
+                  j1 = j + l
+                  j2 = j1 + l
+                  j3 = j2 + l
+                  do i = 0, n1 - 2, 2
+                      x0r = a(i, j) + a(i, j1)
+                      x0i = a(i + 1, j) + a(i + 1, j1)
+                      x1r = a(i, j) - a(i, j1)
+                      x1i = a(i + 1, j) - a(i + 1, j1)
+                      x2r = a(i, j2) + a(i, j3)
+                      x2i = a(i + 1, j2) + a(i + 1, j3)
+                      x3r = a(i, j2) - a(i, j3)
+                      x3i = a(i + 1, j2) - a(i + 1, j3)
+                      a(i, j) = x0r + x2r
+                      a(i + 1, j) = x0i + x2i
+                      a(i, j2) = x0i - x2i
+                      a(i + 1, j2) = x2r - x0r
+                      x0r = x1r + x3i
+                      x0i = x1i - x3r
+                      a(i, j1) = wk1r * (x0i + x0r)
+                      a(i + 1, j1) = wk1r * (x0i - x0r)
+                      x0r = x3i - x1r
+                      x0i = x3r + x1i
+                      a(i, j3) = wk1r * (x0r + x0i)
+                      a(i + 1, j3) = wk1r * (x0r - x0i)
+                  end do
+              end do
+              k1 = 1
+              ks = -1
+              do k = 2 * m, n - m, m
+                  k1 = k1 + 1
+                  ks = -ks
+                  wk1r = w(2 * k1)
+                  wk1i = w(2 * k1 + 1)
+                  wk2r = ks * w(k1)
+                  wk2i = w(k1 + ks)
+                  wk3r = wk1r - 2 * wk2i * wk1i
+                  wk3i = 2 * wk2i * wk1r - wk1i
+                  do j = k, l + k - 1
+                      j1 = j + l
+                      j2 = j1 + l
+                      j3 = j2 + l
+                      do i = 0, n1 - 2, 2
+                          x0r = a(i, j) + a(i, j1)
+                          x0i = a(i + 1, j) + a(i + 1, j1)
+                          x1r = a(i, j) - a(i, j1)
+                          x1i = a(i + 1, j) - a(i + 1, j1)
+                          x2r = a(i, j2) + a(i, j3)
+                          x2i = a(i + 1, j2) + a(i + 1, j3)
+                          x3r = a(i, j2) - a(i, j3)
+                          x3i = a(i + 1, j2) - a(i + 1, j3)
+                          a(i, j) = x0r + x2r
+                          a(i + 1, j) = x0i + x2i
+                          x0r = x0r - x2r
+                          x0i = x0i - x2i
+                          a(i, j2) = wk2r * x0r + wk2i * x0i
+                          a(i + 1, j2) = wk2r * x0i - wk2i * x0r
+                          x0r = x1r + x3i
+                          x0i = x1i - x3r
+                          a(i, j1) = wk1r * x0r + wk1i * x0i
+                          a(i + 1, j1) = wk1r * x0i - wk1i * x0r
+                          x0r = x1r - x3i
+                          x0i = x1i + x3r
+                          a(i, j3) = wk3r * x0r + wk3i * x0i
+                          a(i + 1, j3) = wk3r * x0i - wk3i * x0r
+                      end do
+                  end do
+              end do
+          end if
+          l = m
+      end do
+      if (l .lt. n) then
+          do j = 0, l - 1
+              j1 = j + l
+              do i = 0, n1 - 2, 2
+                  x0r = a(i, j) - a(i, j1)
+                  x0i = a(i + 1, j) - a(i + 1, j1)
+                  a(i, j) = a(i, j) + a(i, j1)
+                  a(i + 1, j) = a(i + 1, j) + a(i + 1, j1)
+                  a(i, j1) = x0r
+                  a(i + 1, j1) = x0i
+              end do
+          end do
+      end if
+      end
+!
+      subroutine rftbrow(n1max, n, n2, a, nc, c)
+      integer n1max, n, n2, nc, i, j, k, kk, ks
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), c(0 : nc - 1), 
+     &    wkr, wki, xr, xi, yr, yi
+      ks = 4 * nc / n
+      do i = 0, n2 - 1
+          kk = 0
+          do k = n / 2 - 2, 2, -2
+              j = n - k
+              kk = kk + ks
+              wkr = 0.5d0 - c(kk)
+              wki = c(nc - kk)
+              xr = a(k, i) - a(j, i)
+              xi = a(k + 1, i) + a(j + 1, i)
+              yr = wkr * xr - wki * xi
+              yi = wkr * xi + wki * xr
+              a(k, i) = a(k, i) - yr
+              a(k + 1, i) = a(k + 1, i) - yi
+              a(j, i) = a(j, i) + yr
+              a(j + 1, i) = a(j + 1, i) - yi
+          end do
+      end do
+      end
+!
+      subroutine rftfrow(n1max, n, n2, a, nc, c)
+      integer n1max, n, n2, nc, i, j, k, kk, ks
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), c(0 : nc - 1), 
+     &    wkr, wki, xr, xi, yr, yi
+      ks = 4 * nc / n
+      do i = 0, n2 - 1
+          kk = 0
+          do k = n / 2 - 2, 2, -2
+              j = n - k
+              kk = kk + ks
+              wkr = 0.5d0 - c(kk)
+              wki = c(nc - kk)
+              xr = a(k, i) - a(j, i)
+              xi = a(k + 1, i) + a(j + 1, i)
+              yr = wkr * xr + wki * xi
+              yi = wkr * xi - wki * xr
+              a(k, i) = a(k, i) - yr
+              a(k + 1, i) = a(k + 1, i) - yi
+              a(j, i) = a(j, i) + yr
+              a(j + 1, i) = a(j + 1, i) - yi
+          end do
+      end do
+      end
+!
+      subroutine dctbsub(n1max, n1, n2, a, nc, c)
+      integer n1max, n1, n2, nc, kk1, kk2, ks1, ks2, n2h, j2, 
+     &    k1, k2
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), c(0 : nc - 1), 
+     &    w2r, w2i, wkr, wki, wjr, wji, x0r, x0i, x1r, x1i
+      ks1 = nc / n1
+      ks2 = nc / n2
+      n2h = n2 / 2
+      kk2 = ks2
+      do k2 = 1, n2h - 1
+          j2 = n2 - k2
+          w2r = 2 * c(kk2)
+          w2i = 2 * c(nc - kk2)
+          kk2 = kk2 + ks2
+          kk1 = ks1
+          do k1 = 2, n1 - 2, 2
+              x0r = w2r * c(kk1)
+              x0i = w2i * c(kk1)
+              x1r = w2r * c(nc - kk1)
+              x1i = w2i * c(nc - kk1)
+              wkr = x0r - x1i
+              wki = x0i + x1r
+              wji = x0r + x1i
+              wjr = x0i - x1r
+              kk1 = kk1 + ks1
+              x0r = wkr * a(k1, k2) - wki * a(k1 + 1, k2)
+              x0i = wkr * a(k1 + 1, k2) + wki * a(k1, k2)
+              x1r = wjr * a(k1, j2) - wji * a(k1 + 1, j2)
+              x1i = wjr * a(k1 + 1, j2) + wji * a(k1, j2)
+              a(k1, k2) = x0r + x1i
+              a(k1 + 1, k2) = x0i - x1r
+              a(k1, j2) = x1r + x0i
+              a(k1 + 1, j2) = x1i - x0r
+          end do
+          wkr = w2r * 0.5d0
+          wki = w2i * 0.5d0
+          wjr = w2r * c(kk1)
+          wji = w2i * c(kk1)
+          x0r = a(0, k2) + a(0, j2)
+          x0i = a(1, k2) - a(1, j2)
+          x1r = a(0, k2) - a(0, j2)
+          x1i = a(1, k2) + a(1, j2)
+          a(0, k2) = wkr * x0r - wki * x0i
+          a(1, k2) = wkr * x0i + wki * x0r
+          a(0, j2) = -wjr * x1r + wji * x1i
+          a(1, j2) = wjr * x1i + wji * x1r
+      end do
+      w2r = 2 * c(kk2)
+      kk1 = ks1
+      do k1 = 2, n1 - 2, 2
+          wkr = 2 * c(kk1)
+          wki = 2 * c(nc - kk1)
+          wjr = w2r * wkr
+          wji = w2r * wki
+          kk1 = kk1 + ks1
+          x0i = wkr * a(k1 + 1, 0) + wki * a(k1, 0)
+          a(k1, 0) = wkr * a(k1, 0) - wki * a(k1 + 1, 0)
+          a(k1 + 1, 0) = x0i
+          x0i = wjr * a(k1 + 1, n2h) + wji * a(k1, n2h)
+          a(k1, n2h) = wjr * a(k1, n2h) - wji * a(k1 + 1, n2h)
+          a(k1 + 1, n2h) = x0i
+      end do
+      a(1, 0) = a(1, 0) * w2r
+      a(0, n2h) = a(0, n2h) * w2r
+      a(1, n2h) = a(1, n2h) * 0.5d0
+      end
+!
+      subroutine dctfsub(n1max, n1, n2, a, nc, c)
+      integer n1max, n1, n2, nc, kk1, kk2, ks1, ks2, n2h, j2, 
+     &    k1, k2
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), c(0 : nc - 1), 
+     &    w2r, w2i, wkr, wki, wjr, wji, x0r, x0i, x1r, x1i
+      ks1 = nc / n1
+      ks2 = nc / n2
+      n2h = n2 / 2
+      kk2 = ks2
+      do k2 = 1, n2h - 1
+          j2 = n2 - k2
+          w2r = 2 * c(kk2)
+          w2i = 2 * c(nc - kk2)
+          kk2 = kk2 + ks2
+          kk1 = ks1
+          do k1 = 2, n1 - 2, 2
+              x0r = w2r * c(kk1)
+              x0i = w2i * c(kk1)
+              x1r = w2r * c(nc - kk1)
+              x1i = w2i * c(nc - kk1)
+              wkr = x0r - x1i
+              wki = x0i + x1r
+              wji = x0r + x1i
+              wjr = x0i - x1r
+              kk1 = kk1 + ks1
+              x0r = a(k1, k2) - a(k1 + 1, j2)
+              x0i = a(k1, j2) + a(k1 + 1, k2)
+              x1r = a(k1, j2) - a(k1 + 1, k2)
+              x1i = a(k1, k2) + a(k1 + 1, j2)
+              a(k1, k2) = wkr * x0r + wki * x0i
+              a(k1 + 1, k2) = wkr * x0i - wki * x0r
+              a(k1, j2) = wjr * x1r + wji * x1i
+              a(k1 + 1, j2) = wjr * x1i - wji * x1r
+          end do
+          x0r = 2 * c(kk1)
+          wjr = x0r * w2r
+          wji = x0r * w2i
+          x0r = w2r * a(0, k2) + w2i * a(1, k2)
+          x0i = w2r * a(1, k2) - w2i * a(0, k2)
+          x1r = -wjr * a(0, j2) + wji * a(1, j2)
+          x1i = wjr * a(1, j2) + wji * a(0, j2)
+          a(0, k2) = x0r + x1r
+          a(1, k2) = x1i + x0i
+          a(0, j2) = x0r - x1r
+          a(1, j2) = x1i - x0i
+      end do
+      w2r = 2 * c(kk2)
+      kk1 = ks1
+      do k1 = 2, n1 - 2, 2
+          wkr = 2 * c(kk1)
+          wki = 2 * c(nc - kk1)
+          wjr = w2r * wkr
+          wji = w2r * wki
+          kk1 = kk1 + ks1
+          x0i = wkr * a(k1 + 1, 0) - wki * a(k1, 0)
+          a(k1, 0) = wkr * a(k1, 0) + wki * a(k1 + 1, 0)
+          a(k1 + 1, 0) = x0i
+          x0i = wjr * a(k1 + 1, n2h) - wji * a(k1, n2h)
+          a(k1, n2h) = wjr * a(k1, n2h) + wji * a(k1 + 1, n2h)
+          a(k1 + 1, n2h) = x0i
+      end do
+      w2r = w2r * 2
+      a(0, 0) = a(0, 0) * 2
+      a(1, 0) = a(1, 0) * w2r
+      a(0, n2h) = a(0, n2h) * w2r
+      end
+!
+      subroutine dstbsub(n1max, n1, n2, a, nc, c)
+      integer n1max, n1, n2, nc, kk1, kk2, ks1, ks2, n2h, j2, 
+     &    k1, k2
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), c(0 : nc - 1), 
+     &    w2r, w2i, wkr, wki, wjr, wji, x0r, x0i, x1r, x1i
+      ks1 = nc / n1
+      ks2 = nc / n2
+      n2h = n2 / 2
+      kk2 = ks2
+      do k2 = 1, n2h - 1
+          j2 = n2 - k2
+          w2r = 2 * c(kk2)
+          w2i = 2 * c(nc - kk2)
+          kk2 = kk2 + ks2
+          kk1 = ks1
+          do k1 = 2, n1 - 2, 2
+              x0r = w2r * c(kk1)
+              x0i = w2i * c(kk1)
+              x1r = w2r * c(nc - kk1)
+              x1i = w2i * c(nc - kk1)
+              wkr = x0r - x1i
+              wki = x0i + x1r
+              wji = x0r + x1i
+              wjr = x0i - x1r
+              kk1 = kk1 + ks1
+              x0r = wkr * a(k1, k2) - wki * a(k1 + 1, k2)
+              x0i = wkr * a(k1 + 1, k2) + wki * a(k1, k2)
+              x1r = wjr * a(k1, j2) - wji * a(k1 + 1, j2)
+              x1i = wjr * a(k1 + 1, j2) + wji * a(k1, j2)
+              a(k1, k2) = x1i - x0r
+              a(k1 + 1, k2) = x1r + x0i
+              a(k1, j2) = x0i - x1r
+              a(k1 + 1, j2) = x0r + x1i
+          end do
+          wkr = w2r * 0.5d0
+          wki = w2i * 0.5d0
+          wjr = w2r * c(kk1)
+          wji = w2i * c(kk1)
+          x0r = a(0, k2) + a(0, j2)
+          x0i = a(1, k2) - a(1, j2)
+          x1r = a(0, k2) - a(0, j2)
+          x1i = a(1, k2) + a(1, j2)
+          a(1, k2) = wkr * x0r - wki * x0i
+          a(0, k2) = wkr * x0i + wki * x0r
+          a(1, j2) = -wjr * x1r + wji * x1i
+          a(0, j2) = wjr * x1i + wji * x1r
+      end do
+      w2r = 2 * c(kk2)
+      kk1 = ks1
+      do k1 = 2, n1 - 2, 2
+          wkr = 2 * c(kk1)
+          wki = 2 * c(nc - kk1)
+          wjr = w2r * wkr
+          wji = w2r * wki
+          kk1 = kk1 + ks1
+          x0i = wkr * a(k1 + 1, 0) + wki * a(k1, 0)
+          a(k1 + 1, 0) = wkr * a(k1, 0) - wki * a(k1 + 1, 0)
+          a(k1, 0) = x0i
+          x0i = wjr * a(k1 + 1, n2h) + wji * a(k1, n2h)
+          a(k1 + 1, n2h) = wjr * a(k1, n2h) - wji * a(k1 + 1, n2h)
+          a(k1, n2h) = x0i
+      end do
+      a(1, 0) = a(1, 0) * w2r
+      a(0, n2h) = a(0, n2h) * w2r
+      a(1, n2h) = a(1, n2h) * 0.5d0
+      end
+!
+      subroutine dstfsub(n1max, n1, n2, a, nc, c)
+      integer n1max, n1, n2, nc, kk1, kk2, ks1, ks2, n2h, j2, 
+     &    k1, k2
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), c(0 : nc - 1), 
+     &    w2r, w2i, wkr, wki, wjr, wji, x0r, x0i, x1r, x1i
+      ks1 = nc / n1
+      ks2 = nc / n2
+      n2h = n2 / 2
+      kk2 = ks2
+      do k2 = 1, n2h - 1
+          j2 = n2 - k2
+          w2r = 2 * c(kk2)
+          w2i = 2 * c(nc - kk2)
+          kk2 = kk2 + ks2
+          kk1 = ks1
+          do k1 = 2, n1 - 2, 2
+              x0r = w2r * c(kk1)
+              x0i = w2i * c(kk1)
+              x1r = w2r * c(nc - kk1)
+              x1i = w2i * c(nc - kk1)
+              wkr = x0r - x1i
+              wki = x0i + x1r
+              wji = x0r + x1i
+              wjr = x0i - x1r
+              kk1 = kk1 + ks1
+              x0r = a(k1 + 1, j2) - a(k1, k2)
+              x0i = a(k1 + 1, k2) + a(k1, j2)
+              x1r = a(k1 + 1, k2) - a(k1, j2)
+              x1i = a(k1 + 1, j2) + a(k1, k2)
+              a(k1, k2) = wkr * x0r + wki * x0i
+              a(k1 + 1, k2) = wkr * x0i - wki * x0r
+              a(k1, j2) = wjr * x1r + wji * x1i
+              a(k1 + 1, j2) = wjr * x1i - wji * x1r
+          end do
+          x0r = 2 * c(kk1)
+          wjr = x0r * w2r
+          wji = x0r * w2i
+          x0r = w2r * a(1, k2) + w2i * a(0, k2)
+          x0i = w2r * a(0, k2) - w2i * a(1, k2)
+          x1r = -wjr * a(1, j2) + wji * a(0, j2)
+          x1i = wjr * a(0, j2) + wji * a(1, j2)
+          a(0, k2) = x0r + x1r
+          a(1, k2) = x1i + x0i
+          a(0, j2) = x0r - x1r
+          a(1, j2) = x1i - x0i
+      end do
+      w2r = 2 * c(kk2)
+      kk1 = ks1
+      do k1 = 2, n1 - 2, 2
+          wkr = 2 * c(kk1)
+          wki = 2 * c(nc - kk1)
+          wjr = w2r * wkr
+          wji = w2r * wki
+          kk1 = kk1 + ks1
+          x0i = wkr * a(k1, 0) - wki * a(k1 + 1, 0)
+          a(k1, 0) = wkr * a(k1 + 1, 0) + wki * a(k1, 0)
+          a(k1 + 1, 0) = x0i
+          x0i = wjr * a(k1, n2h) - wji * a(k1 + 1, n2h)
+          a(k1, n2h) = wjr * a(k1 + 1, n2h) + wji * a(k1, n2h)
+          a(k1 + 1, n2h) = x0i
+      end do
+      w2r = w2r * 2
+      a(0, 0) = a(0, 0) * 2
+      a(1, 0) = a(1, 0) * w2r
+      a(0, n2h) = a(0, n2h) * w2r
+      end
+!
diff --git a/third_party/tensorflow_dependencies/fft2d/fftsg.c b/third_party/tensorflow_dependencies/fft2d/fftsg.c
new file mode 100644
index 0000000..43d7534
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/fftsg.c
@@ -0,0 +1,3314 @@
+/*
+Fast Fourier/Cosine/Sine Transform
+    dimension   :one
+    data length :power of 2
+    decimation  :frequency
+    radix       :split-radix
+    data        :inplace
+    table       :use
+functions
+    cdft: Complex Discrete Fourier Transform
+    rdft: Real Discrete Fourier Transform
+    ddct: Discrete Cosine Transform
+    ddst: Discrete Sine Transform
+    dfct: Cosine Transform of RDFT (Real Symmetric DFT)
+    dfst: Sine Transform of RDFT (Real Anti-symmetric DFT)
+function prototypes
+    void cdft(int, int, double *, int *, double *);
+    void rdft(int, int, double *, int *, double *);
+    void ddct(int, int, double *, int *, double *);
+    void ddst(int, int, double *, int *, double *);
+    void dfct(int, double *, double *, int *, double *);
+    void dfst(int, double *, double *, int *, double *);
+macro definitions
+    USE_CDFT_PTHREADS : default=not defined
+        CDFT_THREADS_BEGIN_N  : must be >= 512, default=8192
+        CDFT_4THREADS_BEGIN_N : must be >= 512, default=65536
+    USE_CDFT_WINTHREADS : default=not defined
+        CDFT_THREADS_BEGIN_N  : must be >= 512, default=32768
+        CDFT_4THREADS_BEGIN_N : must be >= 512, default=524288
+
+
+-------- Complex DFT (Discrete Fourier Transform) --------
+    [definition]
+        <case1>
+            X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k<n
+        <case2>
+            X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k<n
+        (notes: sum_j=0^n-1 is a summation from j=0 to n-1)
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            cdft(2*n, 1, a, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            cdft(2*n, -1, a, ip, w);
+    [parameters]
+        2*n            :data length (int)
+                        n >= 1, n = power of 2
+        a[0...2*n-1]   :input/output data (double *)
+                        input data
+                            a[2*j] = Re(x[j]), 
+                            a[2*j+1] = Im(x[j]), 0<=j<n
+                        output data
+                            a[2*k] = Re(X[k]), 
+                            a[2*k+1] = Im(X[k]), 0<=k<n
+        ip[0...*]      :work area for bit reversal (int *)
+                        length of ip >= 2+sqrt(n)
+                        strictly, 
+                        length of ip >= 
+                            2+(1<<(int)(log(n+0.5)/log(2))/2).
+                        ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...n/2-1]   :cos/sin table (double *)
+                        w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            cdft(2*n, -1, a, ip, w);
+        is 
+            cdft(2*n, 1, a, ip, w);
+            for (j = 0; j <= 2 * n - 1; j++) {
+                a[j] *= 1.0 / n;
+            }
+        .
+
+
+-------- Real DFT / Inverse of Real DFT --------
+    [definition]
+        <case1> RDFT
+            R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2
+            I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0<k<n/2
+        <case2> IRDFT (excluding scale)
+            a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + 
+                   sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + 
+                   sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k<n
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            rdft(n, 1, a, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            rdft(n, -1, a, ip, w);
+    [parameters]
+        n              :data length (int)
+                        n >= 2, n = power of 2
+        a[0...n-1]     :input/output data (double *)
+                        <case1>
+                            output data
+                                a[2*k] = R[k], 0<=k<n/2
+                                a[2*k+1] = I[k], 0<k<n/2
+                                a[1] = R[n/2]
+                        <case2>
+                            input data
+                                a[2*j] = R[j], 0<=j<n/2
+                                a[2*j+1] = I[j], 0<j<n/2
+                                a[1] = R[n/2]
+        ip[0...*]      :work area for bit reversal (int *)
+                        length of ip >= 2+sqrt(n/2)
+                        strictly, 
+                        length of ip >= 
+                            2+(1<<(int)(log(n/2+0.5)/log(2))/2).
+                        ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...n/2-1]   :cos/sin table (double *)
+                        w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            rdft(n, 1, a, ip, w);
+        is 
+            rdft(n, -1, a, ip, w);
+            for (j = 0; j <= n - 1; j++) {
+                a[j] *= 2.0 / n;
+            }
+        .
+
+
+-------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
+    [definition]
+        <case1> IDCT (excluding scale)
+            C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k<n
+        <case2> DCT
+            C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k<n
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            ddct(n, 1, a, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            ddct(n, -1, a, ip, w);
+    [parameters]
+        n              :data length (int)
+                        n >= 2, n = power of 2
+        a[0...n-1]     :input/output data (double *)
+                        output data
+                            a[k] = C[k], 0<=k<n
+        ip[0...*]      :work area for bit reversal (int *)
+                        length of ip >= 2+sqrt(n/2)
+                        strictly, 
+                        length of ip >= 
+                            2+(1<<(int)(log(n/2+0.5)/log(2))/2).
+                        ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...n*5/4-1] :cos/sin table (double *)
+                        w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            ddct(n, -1, a, ip, w);
+        is 
+            a[0] *= 0.5;
+            ddct(n, 1, a, ip, w);
+            for (j = 0; j <= n - 1; j++) {
+                a[j] *= 2.0 / n;
+            }
+        .
+
+
+-------- DST (Discrete Sine Transform) / Inverse of DST --------
+    [definition]
+        <case1> IDST (excluding scale)
+            S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k<n
+        <case2> DST
+            S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0<k<=n
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            ddst(n, 1, a, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            ddst(n, -1, a, ip, w);
+    [parameters]
+        n              :data length (int)
+                        n >= 2, n = power of 2
+        a[0...n-1]     :input/output data (double *)
+                        <case1>
+                            input data
+                                a[j] = A[j], 0<j<n
+                                a[0] = A[n]
+                            output data
+                                a[k] = S[k], 0<=k<n
+                        <case2>
+                            output data
+                                a[k] = S[k], 0<k<n
+                                a[0] = S[n]
+        ip[0...*]      :work area for bit reversal (int *)
+                        length of ip >= 2+sqrt(n/2)
+                        strictly, 
+                        length of ip >= 
+                            2+(1<<(int)(log(n/2+0.5)/log(2))/2).
+                        ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...n*5/4-1] :cos/sin table (double *)
+                        w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            ddst(n, -1, a, ip, w);
+        is 
+            a[0] *= 0.5;
+            ddst(n, 1, a, ip, w);
+            for (j = 0; j <= n - 1; j++) {
+                a[j] *= 2.0 / n;
+            }
+        .
+
+
+-------- Cosine Transform of RDFT (Real Symmetric DFT) --------
+    [definition]
+        C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n
+    [usage]
+        ip[0] = 0; // first time only
+        dfct(n, a, t, ip, w);
+    [parameters]
+        n              :data length - 1 (int)
+                        n >= 2, n = power of 2
+        a[0...n]       :input/output data (double *)
+                        output data
+                            a[k] = C[k], 0<=k<=n
+        t[0...n/2]     :work area (double *)
+        ip[0...*]      :work area for bit reversal (int *)
+                        length of ip >= 2+sqrt(n/4)
+                        strictly, 
+                        length of ip >= 
+                            2+(1<<(int)(log(n/4+0.5)/log(2))/2).
+                        ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...n*5/8-1] :cos/sin table (double *)
+                        w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            a[0] *= 0.5;
+            a[n] *= 0.5;
+            dfct(n, a, t, ip, w);
+        is 
+            a[0] *= 0.5;
+            a[n] *= 0.5;
+            dfct(n, a, t, ip, w);
+            for (j = 0; j <= n; j++) {
+                a[j] *= 2.0 / n;
+            }
+        .
+
+
+-------- Sine Transform of RDFT (Real Anti-symmetric DFT) --------
+    [definition]
+        S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0<k<n
+    [usage]
+        ip[0] = 0; // first time only
+        dfst(n, a, t, ip, w);
+    [parameters]
+        n              :data length + 1 (int)
+                        n >= 2, n = power of 2
+        a[0...n-1]     :input/output data (double *)
+                        output data
+                            a[k] = S[k], 0<k<n
+                        (a[0] is used for work area)
+        t[0...n/2-1]   :work area (double *)
+        ip[0...*]      :work area for bit reversal (int *)
+                        length of ip >= 2+sqrt(n/4)
+                        strictly, 
+                        length of ip >= 
+                            2+(1<<(int)(log(n/4+0.5)/log(2))/2).
+                        ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...n*5/8-1] :cos/sin table (double *)
+                        w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            dfst(n, a, t, ip, w);
+        is 
+            dfst(n, a, t, ip, w);
+            for (j = 1; j <= n - 1; j++) {
+                a[j] *= 2.0 / n;
+            }
+        .
+
+
+Appendix :
+    The cos/sin table is recalculated when the larger table required.
+    w[] and ip[] are compatible with all routines.
+*/
+
+
+void cdft(int n, int isgn, double *a, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void cftfsub(int n, double *a, int *ip, int nw, double *w);
+    void cftbsub(int n, double *a, int *ip, int nw, double *w);
+    int nw;
+    
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    if (isgn >= 0) {
+        cftfsub(n, a, ip, nw, w);
+    } else {
+        cftbsub(n, a, ip, nw, w);
+    }
+}
+
+
+void rdft(int n, int isgn, double *a, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void cftfsub(int n, double *a, int *ip, int nw, double *w);
+    void cftbsub(int n, double *a, int *ip, int nw, double *w);
+    void rftfsub(int n, double *a, int nc, double *c);
+    void rftbsub(int n, double *a, int nc, double *c);
+    int nw, nc;
+    double xi;
+    
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n > (nc << 2)) {
+        nc = n >> 2;
+        makect(nc, ip, w + nw);
+    }
+    if (isgn >= 0) {
+        if (n > 4) {
+            cftfsub(n, a, ip, nw, w);
+            rftfsub(n, a, nc, w + nw);
+        } else if (n == 4) {
+            cftfsub(n, a, ip, nw, w);
+        }
+        xi = a[0] - a[1];
+        a[0] += a[1];
+        a[1] = xi;
+    } else {
+        a[1] = 0.5 * (a[0] - a[1]);
+        a[0] -= a[1];
+        if (n > 4) {
+            rftbsub(n, a, nc, w + nw);
+            cftbsub(n, a, ip, nw, w);
+        } else if (n == 4) {
+            cftbsub(n, a, ip, nw, w);
+        }
+    }
+}
+
+
+void ddct(int n, int isgn, double *a, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void cftfsub(int n, double *a, int *ip, int nw, double *w);
+    void cftbsub(int n, double *a, int *ip, int nw, double *w);
+    void rftfsub(int n, double *a, int nc, double *c);
+    void rftbsub(int n, double *a, int nc, double *c);
+    void dctsub(int n, double *a, int nc, double *c);
+    int j, nw, nc;
+    double xr;
+    
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n > nc) {
+        nc = n;
+        makect(nc, ip, w + nw);
+    }
+    if (isgn < 0) {
+        xr = a[n - 1];
+        for (j = n - 2; j >= 2; j -= 2) {
+            a[j + 1] = a[j] - a[j - 1];
+            a[j] += a[j - 1];
+        }
+        a[1] = a[0] - xr;
+        a[0] += xr;
+        if (n > 4) {
+            rftbsub(n, a, nc, w + nw);
+            cftbsub(n, a, ip, nw, w);
+        } else if (n == 4) {
+            cftbsub(n, a, ip, nw, w);
+        }
+    }
+    dctsub(n, a, nc, w + nw);
+    if (isgn >= 0) {
+        if (n > 4) {
+            cftfsub(n, a, ip, nw, w);
+            rftfsub(n, a, nc, w + nw);
+        } else if (n == 4) {
+            cftfsub(n, a, ip, nw, w);
+        }
+        xr = a[0] - a[1];
+        a[0] += a[1];
+        for (j = 2; j < n; j += 2) {
+            a[j - 1] = a[j] - a[j + 1];
+            a[j] += a[j + 1];
+        }
+        a[n - 1] = xr;
+    }
+}
+
+
+void ddst(int n, int isgn, double *a, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void cftfsub(int n, double *a, int *ip, int nw, double *w);
+    void cftbsub(int n, double *a, int *ip, int nw, double *w);
+    void rftfsub(int n, double *a, int nc, double *c);
+    void rftbsub(int n, double *a, int nc, double *c);
+    void dstsub(int n, double *a, int nc, double *c);
+    int j, nw, nc;
+    double xr;
+    
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n > nc) {
+        nc = n;
+        makect(nc, ip, w + nw);
+    }
+    if (isgn < 0) {
+        xr = a[n - 1];
+        for (j = n - 2; j >= 2; j -= 2) {
+            a[j + 1] = -a[j] - a[j - 1];
+            a[j] -= a[j - 1];
+        }
+        a[1] = a[0] + xr;
+        a[0] -= xr;
+        if (n > 4) {
+            rftbsub(n, a, nc, w + nw);
+            cftbsub(n, a, ip, nw, w);
+        } else if (n == 4) {
+            cftbsub(n, a, ip, nw, w);
+        }
+    }
+    dstsub(n, a, nc, w + nw);
+    if (isgn >= 0) {
+        if (n > 4) {
+            cftfsub(n, a, ip, nw, w);
+            rftfsub(n, a, nc, w + nw);
+        } else if (n == 4) {
+            cftfsub(n, a, ip, nw, w);
+        }
+        xr = a[0] - a[1];
+        a[0] += a[1];
+        for (j = 2; j < n; j += 2) {
+            a[j - 1] = -a[j] - a[j + 1];
+            a[j] -= a[j + 1];
+        }
+        a[n - 1] = -xr;
+    }
+}
+
+
+void dfct(int n, double *a, double *t, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void cftfsub(int n, double *a, int *ip, int nw, double *w);
+    void rftfsub(int n, double *a, int nc, double *c);
+    void dctsub(int n, double *a, int nc, double *c);
+    int j, k, l, m, mh, nw, nc;
+    double xr, xi, yr, yi;
+    
+    nw = ip[0];
+    if (n > (nw << 3)) {
+        nw = n >> 3;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n > (nc << 1)) {
+        nc = n >> 1;
+        makect(nc, ip, w + nw);
+    }
+    m = n >> 1;
+    yi = a[m];
+    xi = a[0] + a[n];
+    a[0] -= a[n];
+    t[0] = xi - yi;
+    t[m] = xi + yi;
+    if (n > 2) {
+        mh = m >> 1;
+        for (j = 1; j < mh; j++) {
+            k = m - j;
+            xr = a[j] - a[n - j];
+            xi = a[j] + a[n - j];
+            yr = a[k] - a[n - k];
+            yi = a[k] + a[n - k];
+            a[j] = xr;
+            a[k] = yr;
+            t[j] = xi - yi;
+            t[k] = xi + yi;
+        }
+        t[mh] = a[mh] + a[n - mh];
+        a[mh] -= a[n - mh];
+        dctsub(m, a, nc, w + nw);
+        if (m > 4) {
+            cftfsub(m, a, ip, nw, w);
+            rftfsub(m, a, nc, w + nw);
+        } else if (m == 4) {
+            cftfsub(m, a, ip, nw, w);
+        }
+        a[n - 1] = a[0] - a[1];
+        a[1] = a[0] + a[1];
+        for (j = m - 2; j >= 2; j -= 2) {
+            a[2 * j + 1] = a[j] + a[j + 1];
+            a[2 * j - 1] = a[j] - a[j + 1];
+        }
+        l = 2;
+        m = mh;
+        while (m >= 2) {
+            dctsub(m, t, nc, w + nw);
+            if (m > 4) {
+                cftfsub(m, t, ip, nw, w);
+                rftfsub(m, t, nc, w + nw);
+            } else if (m == 4) {
+                cftfsub(m, t, ip, nw, w);
+            }
+            a[n - l] = t[0] - t[1];
+            a[l] = t[0] + t[1];
+            k = 0;
+            for (j = 2; j < m; j += 2) {
+                k += l << 2;
+                a[k - l] = t[j] - t[j + 1];
+                a[k + l] = t[j] + t[j + 1];
+            }
+            l <<= 1;
+            mh = m >> 1;
+            for (j = 0; j < mh; j++) {
+                k = m - j;
+                t[j] = t[m + k] - t[m + j];
+                t[k] = t[m + k] + t[m + j];
+            }
+            t[mh] = t[m + mh];
+            m = mh;
+        }
+        a[l] = t[0];
+        a[n] = t[2] - t[1];
+        a[0] = t[2] + t[1];
+    } else {
+        a[1] = a[0];
+        a[2] = t[0];
+        a[0] = t[1];
+    }
+}
+
+
+void dfst(int n, double *a, double *t, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void cftfsub(int n, double *a, int *ip, int nw, double *w);
+    void rftfsub(int n, double *a, int nc, double *c);
+    void dstsub(int n, double *a, int nc, double *c);
+    int j, k, l, m, mh, nw, nc;
+    double xr, xi, yr, yi;
+    
+    nw = ip[0];
+    if (n > (nw << 3)) {
+        nw = n >> 3;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n > (nc << 1)) {
+        nc = n >> 1;
+        makect(nc, ip, w + nw);
+    }
+    if (n > 2) {
+        m = n >> 1;
+        mh = m >> 1;
+        for (j = 1; j < mh; j++) {
+            k = m - j;
+            xr = a[j] + a[n - j];
+            xi = a[j] - a[n - j];
+            yr = a[k] + a[n - k];
+            yi = a[k] - a[n - k];
+            a[j] = xr;
+            a[k] = yr;
+            t[j] = xi + yi;
+            t[k] = xi - yi;
+        }
+        t[0] = a[mh] - a[n - mh];
+        a[mh] += a[n - mh];
+        a[0] = a[m];
+        dstsub(m, a, nc, w + nw);
+        if (m > 4) {
+            cftfsub(m, a, ip, nw, w);
+            rftfsub(m, a, nc, w + nw);
+        } else if (m == 4) {
+            cftfsub(m, a, ip, nw, w);
+        }
+        a[n - 1] = a[1] - a[0];
+        a[1] = a[0] + a[1];
+        for (j = m - 2; j >= 2; j -= 2) {
+            a[2 * j + 1] = a[j] - a[j + 1];
+            a[2 * j - 1] = -a[j] - a[j + 1];
+        }
+        l = 2;
+        m = mh;
+        while (m >= 2) {
+            dstsub(m, t, nc, w + nw);
+            if (m > 4) {
+                cftfsub(m, t, ip, nw, w);
+                rftfsub(m, t, nc, w + nw);
+            } else if (m == 4) {
+                cftfsub(m, t, ip, nw, w);
+            }
+            a[n - l] = t[1] - t[0];
+            a[l] = t[0] + t[1];
+            k = 0;
+            for (j = 2; j < m; j += 2) {
+                k += l << 2;
+                a[k - l] = -t[j] - t[j + 1];
+                a[k + l] = t[j] - t[j + 1];
+            }
+            l <<= 1;
+            mh = m >> 1;
+            for (j = 1; j < mh; j++) {
+                k = m - j;
+                t[j] = t[m + k] + t[m + j];
+                t[k] = t[m + k] - t[m + j];
+            }
+            t[0] = t[m + mh];
+            m = mh;
+        }
+        a[l] = t[0];
+    }
+    a[0] = 0;
+}
+
+
+/* -------- initializing routines -------- */
+
+
+#include <math.h>
+
+void makewt(int nw, int *ip, double *w)
+{
+    void makeipt(int nw, int *ip);
+    int j, nwh, nw0, nw1;
+    double delta, wn4r, wk1r, wk1i, wk3r, wk3i;
+    
+    ip[0] = nw;
+    ip[1] = 1;
+    if (nw > 2) {
+        nwh = nw >> 1;
+        delta = atan(1.0) / nwh;
+        wn4r = cos(delta * nwh);
+        w[0] = 1;
+        w[1] = wn4r;
+        if (nwh == 4) {
+            w[2] = cos(delta * 2);
+            w[3] = sin(delta * 2);
+        } else if (nwh > 4) {
+            makeipt(nw, ip);
+            w[2] = 0.5 / cos(delta * 2);
+            w[3] = 0.5 / cos(delta * 6);
+            for (j = 4; j < nwh; j += 4) {
+                w[j] = cos(delta * j);
+                w[j + 1] = sin(delta * j);
+                w[j + 2] = cos(3 * delta * j);
+                w[j + 3] = -sin(3 * delta * j);
+            }
+        }
+        nw0 = 0;
+        while (nwh > 2) {
+            nw1 = nw0 + nwh;
+            nwh >>= 1;
+            w[nw1] = 1;
+            w[nw1 + 1] = wn4r;
+            if (nwh == 4) {
+                wk1r = w[nw0 + 4];
+                wk1i = w[nw0 + 5];
+                w[nw1 + 2] = wk1r;
+                w[nw1 + 3] = wk1i;
+            } else if (nwh > 4) {
+                wk1r = w[nw0 + 4];
+                wk3r = w[nw0 + 6];
+                w[nw1 + 2] = 0.5 / wk1r;
+                w[nw1 + 3] = 0.5 / wk3r;
+                for (j = 4; j < nwh; j += 4) {
+                    wk1r = w[nw0 + 2 * j];
+                    wk1i = w[nw0 + 2 * j + 1];
+                    wk3r = w[nw0 + 2 * j + 2];
+                    wk3i = w[nw0 + 2 * j + 3];
+                    w[nw1 + j] = wk1r;
+                    w[nw1 + j + 1] = wk1i;
+                    w[nw1 + j + 2] = wk3r;
+                    w[nw1 + j + 3] = wk3i;
+                }
+            }
+            nw0 = nw1;
+        }
+    }
+}
+
+
+void makeipt(int nw, int *ip)
+{
+    int j, l, m, m2, p, q;
+    
+    ip[2] = 0;
+    ip[3] = 16;
+    m = 2;
+    for (l = nw; l > 32; l >>= 2) {
+        m2 = m << 1;
+        q = m2 << 3;
+        for (j = m; j < m2; j++) {
+            p = ip[j] << 2;
+            ip[m + j] = p;
+            ip[m2 + j] = p + q;
+        }
+        m = m2;
+    }
+}
+
+
+void makect(int nc, int *ip, double *c)
+{
+    int j, nch;
+    double delta;
+    
+    ip[1] = nc;
+    if (nc > 1) {
+        nch = nc >> 1;
+        delta = atan(1.0) / nch;
+        c[0] = cos(delta * nch);
+        c[nch] = 0.5 * c[0];
+        for (j = 1; j < nch; j++) {
+            c[j] = 0.5 * cos(delta * j);
+            c[nc - j] = 0.5 * sin(delta * j);
+        }
+    }
+}
+
+
+/* -------- child routines -------- */
+
+
+#ifdef USE_CDFT_PTHREADS
+#define USE_CDFT_THREADS
+#ifndef CDFT_THREADS_BEGIN_N
+#define CDFT_THREADS_BEGIN_N 8192
+#endif
+#ifndef CDFT_4THREADS_BEGIN_N
+#define CDFT_4THREADS_BEGIN_N 65536
+#endif
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#define cdft_thread_t pthread_t
+#define cdft_thread_create(thp,func,argp) { \
+    if (pthread_create(thp, NULL, func, (void *) argp) != 0) { \
+        fprintf(stderr, "cdft thread error\n"); \
+        exit(1); \
+    } \
+}
+#define cdft_thread_wait(th) { \
+    if (pthread_join(th, NULL) != 0) { \
+        fprintf(stderr, "cdft thread error\n"); \
+        exit(1); \
+    } \
+}
+#endif /* USE_CDFT_PTHREADS */
+
+
+#ifdef USE_CDFT_WINTHREADS
+#define USE_CDFT_THREADS
+#ifndef CDFT_THREADS_BEGIN_N
+#define CDFT_THREADS_BEGIN_N 32768
+#endif
+#ifndef CDFT_4THREADS_BEGIN_N
+#define CDFT_4THREADS_BEGIN_N 524288
+#endif
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#define cdft_thread_t HANDLE
+#define cdft_thread_create(thp,func,argp) { \
+    DWORD thid; \
+    *(thp) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, (LPVOID) argp, 0, &thid); \
+    if (*(thp) == 0) { \
+        fprintf(stderr, "cdft thread error\n"); \
+        exit(1); \
+    } \
+}
+#define cdft_thread_wait(th) { \
+    WaitForSingleObject(th, INFINITE); \
+    CloseHandle(th); \
+}
+#endif /* USE_CDFT_WINTHREADS */
+
+
+void cftfsub(int n, double *a, int *ip, int nw, double *w)
+{
+    void bitrv2(int n, int *ip, double *a);
+    void bitrv216(double *a);
+    void bitrv208(double *a);
+    void cftf1st(int n, double *a, double *w);
+    void cftrec4(int n, double *a, int nw, double *w);
+    void cftleaf(int n, int isplt, double *a, int nw, double *w);
+    void cftfx41(int n, double *a, int nw, double *w);
+    void cftf161(double *a, double *w);
+    void cftf081(double *a, double *w);
+    void cftf040(double *a);
+    void cftx020(double *a);
+#ifdef USE_CDFT_THREADS
+    void cftrec4_th(int n, double *a, int nw, double *w);
+#endif /* USE_CDFT_THREADS */
+    
+    if (n > 8) {
+        if (n > 32) {
+            cftf1st(n, a, &w[nw - (n >> 2)]);
+#ifdef USE_CDFT_THREADS
+            if (n > CDFT_THREADS_BEGIN_N) {
+                cftrec4_th(n, a, nw, w);
+            } else 
+#endif /* USE_CDFT_THREADS */
+            if (n > 512) {
+                cftrec4(n, a, nw, w);
+            } else if (n > 128) {
+                cftleaf(n, 1, a, nw, w);
+            } else {
+                cftfx41(n, a, nw, w);
+            }
+            bitrv2(n, ip, a);
+        } else if (n == 32) {
+            cftf161(a, &w[nw - 8]);
+            bitrv216(a);
+        } else {
+            cftf081(a, w);
+            bitrv208(a);
+        }
+    } else if (n == 8) {
+        cftf040(a);
+    } else if (n == 4) {
+        cftx020(a);
+    }
+}
+
+
+void cftbsub(int n, double *a, int *ip, int nw, double *w)
+{
+    void bitrv2conj(int n, int *ip, double *a);
+    void bitrv216neg(double *a);
+    void bitrv208neg(double *a);
+    void cftb1st(int n, double *a, double *w);
+    void cftrec4(int n, double *a, int nw, double *w);
+    void cftleaf(int n, int isplt, double *a, int nw, double *w);
+    void cftfx41(int n, double *a, int nw, double *w);
+    void cftf161(double *a, double *w);
+    void cftf081(double *a, double *w);
+    void cftb040(double *a);
+    void cftx020(double *a);
+#ifdef USE_CDFT_THREADS
+    void cftrec4_th(int n, double *a, int nw, double *w);
+#endif /* USE_CDFT_THREADS */
+    
+    if (n > 8) {
+        if (n > 32) {
+            cftb1st(n, a, &w[nw - (n >> 2)]);
+#ifdef USE_CDFT_THREADS
+            if (n > CDFT_THREADS_BEGIN_N) {
+                cftrec4_th(n, a, nw, w);
+            } else 
+#endif /* USE_CDFT_THREADS */
+            if (n > 512) {
+                cftrec4(n, a, nw, w);
+            } else if (n > 128) {
+                cftleaf(n, 1, a, nw, w);
+            } else {
+                cftfx41(n, a, nw, w);
+            }
+            bitrv2conj(n, ip, a);
+        } else if (n == 32) {
+            cftf161(a, &w[nw - 8]);
+            bitrv216neg(a);
+        } else {
+            cftf081(a, w);
+            bitrv208neg(a);
+        }
+    } else if (n == 8) {
+        cftb040(a);
+    } else if (n == 4) {
+        cftx020(a);
+    }
+}
+
+
+void bitrv2(int n, int *ip, double *a)
+{
+    int j, j1, k, k1, l, m, nh, nm;
+    double xr, xi, yr, yi;
+    
+    m = 1;
+    for (l = n >> 2; l > 8; l >>= 2) {
+        m <<= 1;
+    }
+    nh = n >> 1;
+    nm = 4 * m;
+    if (l == 8) {
+        for (k = 0; k < m; k++) {
+            for (j = 0; j < k; j++) {
+                j1 = 4 * j + 2 * ip[m + k];
+                k1 = 4 * k + 2 * ip[m + j];
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += 2 * nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 -= nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += 2 * nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nh;
+                k1 += 2;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= 2 * nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 += nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= 2 * nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += 2;
+                k1 += nh;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += 2 * nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 -= nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += 2 * nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nh;
+                k1 -= 2;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= 2 * nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 += nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= 2 * nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+            }
+            k1 = 4 * k + 2 * ip[m + k];
+            j1 = k1 + 2;
+            k1 += nh;
+            xr = a[j1];
+            xi = a[j1 + 1];
+            yr = a[k1];
+            yi = a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 += nm;
+            k1 += 2 * nm;
+            xr = a[j1];
+            xi = a[j1 + 1];
+            yr = a[k1];
+            yi = a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 += nm;
+            k1 -= nm;
+            xr = a[j1];
+            xi = a[j1 + 1];
+            yr = a[k1];
+            yi = a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 -= 2;
+            k1 -= nh;
+            xr = a[j1];
+            xi = a[j1 + 1];
+            yr = a[k1];
+            yi = a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 += nh + 2;
+            k1 += nh + 2;
+            xr = a[j1];
+            xi = a[j1 + 1];
+            yr = a[k1];
+            yi = a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 -= nh - nm;
+            k1 += 2 * nm - 2;
+            xr = a[j1];
+            xi = a[j1 + 1];
+            yr = a[k1];
+            yi = a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+        }
+    } else {
+        for (k = 0; k < m; k++) {
+            for (j = 0; j < k; j++) {
+                j1 = 4 * j + ip[m + k];
+                k1 = 4 * k + ip[m + j];
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nh;
+                k1 += 2;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += 2;
+                k1 += nh;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nh;
+                k1 -= 2;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= nm;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+            }
+            k1 = 4 * k + ip[m + k];
+            j1 = k1 + 2;
+            k1 += nh;
+            xr = a[j1];
+            xi = a[j1 + 1];
+            yr = a[k1];
+            yi = a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 += nm;
+            k1 += nm;
+            xr = a[j1];
+            xi = a[j1 + 1];
+            yr = a[k1];
+            yi = a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+        }
+    }
+}
+
+
+void bitrv2conj(int n, int *ip, double *a)
+{
+    int j, j1, k, k1, l, m, nh, nm;
+    double xr, xi, yr, yi;
+    
+    m = 1;
+    for (l = n >> 2; l > 8; l >>= 2) {
+        m <<= 1;
+    }
+    nh = n >> 1;
+    nm = 4 * m;
+    if (l == 8) {
+        for (k = 0; k < m; k++) {
+            for (j = 0; j < k; j++) {
+                j1 = 4 * j + 2 * ip[m + k];
+                k1 = 4 * k + 2 * ip[m + j];
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += 2 * nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 -= nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += 2 * nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nh;
+                k1 += 2;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= 2 * nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 += nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= 2 * nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += 2;
+                k1 += nh;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += 2 * nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 -= nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += 2 * nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nh;
+                k1 -= 2;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= 2 * nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 += nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= 2 * nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+            }
+            k1 = 4 * k + 2 * ip[m + k];
+            j1 = k1 + 2;
+            k1 += nh;
+            a[j1 - 1] = -a[j1 - 1];
+            xr = a[j1];
+            xi = -a[j1 + 1];
+            yr = a[k1];
+            yi = -a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            a[k1 + 3] = -a[k1 + 3];
+            j1 += nm;
+            k1 += 2 * nm;
+            xr = a[j1];
+            xi = -a[j1 + 1];
+            yr = a[k1];
+            yi = -a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 += nm;
+            k1 -= nm;
+            xr = a[j1];
+            xi = -a[j1 + 1];
+            yr = a[k1];
+            yi = -a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 -= 2;
+            k1 -= nh;
+            xr = a[j1];
+            xi = -a[j1 + 1];
+            yr = a[k1];
+            yi = -a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 += nh + 2;
+            k1 += nh + 2;
+            xr = a[j1];
+            xi = -a[j1 + 1];
+            yr = a[k1];
+            yi = -a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            j1 -= nh - nm;
+            k1 += 2 * nm - 2;
+            a[j1 - 1] = -a[j1 - 1];
+            xr = a[j1];
+            xi = -a[j1 + 1];
+            yr = a[k1];
+            yi = -a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            a[k1 + 3] = -a[k1 + 3];
+        }
+    } else {
+        for (k = 0; k < m; k++) {
+            for (j = 0; j < k; j++) {
+                j1 = 4 * j + ip[m + k];
+                k1 = 4 * k + ip[m + j];
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nh;
+                k1 += 2;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += 2;
+                k1 += nh;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 += nm;
+                k1 += nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nh;
+                k1 -= 2;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                j1 -= nm;
+                k1 -= nm;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+            }
+            k1 = 4 * k + ip[m + k];
+            j1 = k1 + 2;
+            k1 += nh;
+            a[j1 - 1] = -a[j1 - 1];
+            xr = a[j1];
+            xi = -a[j1 + 1];
+            yr = a[k1];
+            yi = -a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            a[k1 + 3] = -a[k1 + 3];
+            j1 += nm;
+            k1 += nm;
+            a[j1 - 1] = -a[j1 - 1];
+            xr = a[j1];
+            xi = -a[j1 + 1];
+            yr = a[k1];
+            yi = -a[k1 + 1];
+            a[j1] = yr;
+            a[j1 + 1] = yi;
+            a[k1] = xr;
+            a[k1 + 1] = xi;
+            a[k1 + 3] = -a[k1 + 3];
+        }
+    }
+}
+
+
+void bitrv216(double *a)
+{
+    double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, 
+        x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i, 
+        x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i;
+    
+    x1r = a[2];
+    x1i = a[3];
+    x2r = a[4];
+    x2i = a[5];
+    x3r = a[6];
+    x3i = a[7];
+    x4r = a[8];
+    x4i = a[9];
+    x5r = a[10];
+    x5i = a[11];
+    x7r = a[14];
+    x7i = a[15];
+    x8r = a[16];
+    x8i = a[17];
+    x10r = a[20];
+    x10i = a[21];
+    x11r = a[22];
+    x11i = a[23];
+    x12r = a[24];
+    x12i = a[25];
+    x13r = a[26];
+    x13i = a[27];
+    x14r = a[28];
+    x14i = a[29];
+    a[2] = x8r;
+    a[3] = x8i;
+    a[4] = x4r;
+    a[5] = x4i;
+    a[6] = x12r;
+    a[7] = x12i;
+    a[8] = x2r;
+    a[9] = x2i;
+    a[10] = x10r;
+    a[11] = x10i;
+    a[14] = x14r;
+    a[15] = x14i;
+    a[16] = x1r;
+    a[17] = x1i;
+    a[20] = x5r;
+    a[21] = x5i;
+    a[22] = x13r;
+    a[23] = x13i;
+    a[24] = x3r;
+    a[25] = x3i;
+    a[26] = x11r;
+    a[27] = x11i;
+    a[28] = x7r;
+    a[29] = x7i;
+}
+
+
+void bitrv216neg(double *a)
+{
+    double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, 
+        x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i, 
+        x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i, 
+        x13r, x13i, x14r, x14i, x15r, x15i;
+    
+    x1r = a[2];
+    x1i = a[3];
+    x2r = a[4];
+    x2i = a[5];
+    x3r = a[6];
+    x3i = a[7];
+    x4r = a[8];
+    x4i = a[9];
+    x5r = a[10];
+    x5i = a[11];
+    x6r = a[12];
+    x6i = a[13];
+    x7r = a[14];
+    x7i = a[15];
+    x8r = a[16];
+    x8i = a[17];
+    x9r = a[18];
+    x9i = a[19];
+    x10r = a[20];
+    x10i = a[21];
+    x11r = a[22];
+    x11i = a[23];
+    x12r = a[24];
+    x12i = a[25];
+    x13r = a[26];
+    x13i = a[27];
+    x14r = a[28];
+    x14i = a[29];
+    x15r = a[30];
+    x15i = a[31];
+    a[2] = x15r;
+    a[3] = x15i;
+    a[4] = x7r;
+    a[5] = x7i;
+    a[6] = x11r;
+    a[7] = x11i;
+    a[8] = x3r;
+    a[9] = x3i;
+    a[10] = x13r;
+    a[11] = x13i;
+    a[12] = x5r;
+    a[13] = x5i;
+    a[14] = x9r;
+    a[15] = x9i;
+    a[16] = x1r;
+    a[17] = x1i;
+    a[18] = x14r;
+    a[19] = x14i;
+    a[20] = x6r;
+    a[21] = x6i;
+    a[22] = x10r;
+    a[23] = x10i;
+    a[24] = x2r;
+    a[25] = x2i;
+    a[26] = x12r;
+    a[27] = x12i;
+    a[28] = x4r;
+    a[29] = x4i;
+    a[30] = x8r;
+    a[31] = x8i;
+}
+
+
+void bitrv208(double *a)
+{
+    double x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i;
+    
+    x1r = a[2];
+    x1i = a[3];
+    x3r = a[6];
+    x3i = a[7];
+    x4r = a[8];
+    x4i = a[9];
+    x6r = a[12];
+    x6i = a[13];
+    a[2] = x4r;
+    a[3] = x4i;
+    a[6] = x6r;
+    a[7] = x6i;
+    a[8] = x1r;
+    a[9] = x1i;
+    a[12] = x3r;
+    a[13] = x3i;
+}
+
+
+void bitrv208neg(double *a)
+{
+    double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, 
+        x5r, x5i, x6r, x6i, x7r, x7i;
+    
+    x1r = a[2];
+    x1i = a[3];
+    x2r = a[4];
+    x2i = a[5];
+    x3r = a[6];
+    x3i = a[7];
+    x4r = a[8];
+    x4i = a[9];
+    x5r = a[10];
+    x5i = a[11];
+    x6r = a[12];
+    x6i = a[13];
+    x7r = a[14];
+    x7i = a[15];
+    a[2] = x7r;
+    a[3] = x7i;
+    a[4] = x3r;
+    a[5] = x3i;
+    a[6] = x5r;
+    a[7] = x5i;
+    a[8] = x1r;
+    a[9] = x1i;
+    a[10] = x6r;
+    a[11] = x6i;
+    a[12] = x2r;
+    a[13] = x2i;
+    a[14] = x4r;
+    a[15] = x4i;
+}
+
+
+void cftf1st(int n, double *a, double *w)
+{
+    int j, j0, j1, j2, j3, k, m, mh;
+    double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, 
+        wd1r, wd1i, wd3r, wd3i;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, 
+        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;
+    
+    mh = n >> 3;
+    m = 2 * mh;
+    j1 = m;
+    j2 = j1 + m;
+    j3 = j2 + m;
+    x0r = a[0] + a[j2];
+    x0i = a[1] + a[j2 + 1];
+    x1r = a[0] - a[j2];
+    x1i = a[1] - a[j2 + 1];
+    x2r = a[j1] + a[j3];
+    x2i = a[j1 + 1] + a[j3 + 1];
+    x3r = a[j1] - a[j3];
+    x3i = a[j1 + 1] - a[j3 + 1];
+    a[0] = x0r + x2r;
+    a[1] = x0i + x2i;
+    a[j1] = x0r - x2r;
+    a[j1 + 1] = x0i - x2i;
+    a[j2] = x1r - x3i;
+    a[j2 + 1] = x1i + x3r;
+    a[j3] = x1r + x3i;
+    a[j3 + 1] = x1i - x3r;
+    wn4r = w[1];
+    csc1 = w[2];
+    csc3 = w[3];
+    wd1r = 1;
+    wd1i = 0;
+    wd3r = 1;
+    wd3i = 0;
+    k = 0;
+    for (j = 2; j < mh - 2; j += 4) {
+        k += 4;
+        wk1r = csc1 * (wd1r + w[k]);
+        wk1i = csc1 * (wd1i + w[k + 1]);
+        wk3r = csc3 * (wd3r + w[k + 2]);
+        wk3i = csc3 * (wd3i + w[k + 3]);
+        wd1r = w[k];
+        wd1i = w[k + 1];
+        wd3r = w[k + 2];
+        wd3i = w[k + 3];
+        j1 = j + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j] + a[j2];
+        x0i = a[j + 1] + a[j2 + 1];
+        x1r = a[j] - a[j2];
+        x1i = a[j + 1] - a[j2 + 1];
+        y0r = a[j + 2] + a[j2 + 2];
+        y0i = a[j + 3] + a[j2 + 3];
+        y1r = a[j + 2] - a[j2 + 2];
+        y1i = a[j + 3] - a[j2 + 3];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        y2r = a[j1 + 2] + a[j3 + 2];
+        y2i = a[j1 + 3] + a[j3 + 3];
+        y3r = a[j1 + 2] - a[j3 + 2];
+        y3i = a[j1 + 3] - a[j3 + 3];
+        a[j] = x0r + x2r;
+        a[j + 1] = x0i + x2i;
+        a[j + 2] = y0r + y2r;
+        a[j + 3] = y0i + y2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i - x2i;
+        a[j1 + 2] = y0r - y2r;
+        a[j1 + 3] = y0i - y2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2] = wk1r * x0r - wk1i * x0i;
+        a[j2 + 1] = wk1r * x0i + wk1i * x0r;
+        x0r = y1r - y3i;
+        x0i = y1i + y3r;
+        a[j2 + 2] = wd1r * x0r - wd1i * x0i;
+        a[j2 + 3] = wd1r * x0i + wd1i * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3] = wk3r * x0r + wk3i * x0i;
+        a[j3 + 1] = wk3r * x0i - wk3i * x0r;
+        x0r = y1r + y3i;
+        x0i = y1i - y3r;
+        a[j3 + 2] = wd3r * x0r + wd3i * x0i;
+        a[j3 + 3] = wd3r * x0i - wd3i * x0r;
+        j0 = m - j;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j0] + a[j2];
+        x0i = a[j0 + 1] + a[j2 + 1];
+        x1r = a[j0] - a[j2];
+        x1i = a[j0 + 1] - a[j2 + 1];
+        y0r = a[j0 - 2] + a[j2 - 2];
+        y0i = a[j0 - 1] + a[j2 - 1];
+        y1r = a[j0 - 2] - a[j2 - 2];
+        y1i = a[j0 - 1] - a[j2 - 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        y2r = a[j1 - 2] + a[j3 - 2];
+        y2i = a[j1 - 1] + a[j3 - 1];
+        y3r = a[j1 - 2] - a[j3 - 2];
+        y3i = a[j1 - 1] - a[j3 - 1];
+        a[j0] = x0r + x2r;
+        a[j0 + 1] = x0i + x2i;
+        a[j0 - 2] = y0r + y2r;
+        a[j0 - 1] = y0i + y2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i - x2i;
+        a[j1 - 2] = y0r - y2r;
+        a[j1 - 1] = y0i - y2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2] = wk1i * x0r - wk1r * x0i;
+        a[j2 + 1] = wk1i * x0i + wk1r * x0r;
+        x0r = y1r - y3i;
+        x0i = y1i + y3r;
+        a[j2 - 2] = wd1i * x0r - wd1r * x0i;
+        a[j2 - 1] = wd1i * x0i + wd1r * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3] = wk3i * x0r + wk3r * x0i;
+        a[j3 + 1] = wk3i * x0i - wk3r * x0r;
+        x0r = y1r + y3i;
+        x0i = y1i - y3r;
+        a[j3 - 2] = wd3i * x0r + wd3r * x0i;
+        a[j3 - 1] = wd3i * x0i - wd3r * x0r;
+    }
+    wk1r = csc1 * (wd1r + wn4r);
+    wk1i = csc1 * (wd1i + wn4r);
+    wk3r = csc3 * (wd3r - wn4r);
+    wk3i = csc3 * (wd3i - wn4r);
+    j0 = mh;
+    j1 = j0 + m;
+    j2 = j1 + m;
+    j3 = j2 + m;
+    x0r = a[j0 - 2] + a[j2 - 2];
+    x0i = a[j0 - 1] + a[j2 - 1];
+    x1r = a[j0 - 2] - a[j2 - 2];
+    x1i = a[j0 - 1] - a[j2 - 1];
+    x2r = a[j1 - 2] + a[j3 - 2];
+    x2i = a[j1 - 1] + a[j3 - 1];
+    x3r = a[j1 - 2] - a[j3 - 2];
+    x3i = a[j1 - 1] - a[j3 - 1];
+    a[j0 - 2] = x0r + x2r;
+    a[j0 - 1] = x0i + x2i;
+    a[j1 - 2] = x0r - x2r;
+    a[j1 - 1] = x0i - x2i;
+    x0r = x1r - x3i;
+    x0i = x1i + x3r;
+    a[j2 - 2] = wk1r * x0r - wk1i * x0i;
+    a[j2 - 1] = wk1r * x0i + wk1i * x0r;
+    x0r = x1r + x3i;
+    x0i = x1i - x3r;
+    a[j3 - 2] = wk3r * x0r + wk3i * x0i;
+    a[j3 - 1] = wk3r * x0i - wk3i * x0r;
+    x0r = a[j0] + a[j2];
+    x0i = a[j0 + 1] + a[j2 + 1];
+    x1r = a[j0] - a[j2];
+    x1i = a[j0 + 1] - a[j2 + 1];
+    x2r = a[j1] + a[j3];
+    x2i = a[j1 + 1] + a[j3 + 1];
+    x3r = a[j1] - a[j3];
+    x3i = a[j1 + 1] - a[j3 + 1];
+    a[j0] = x0r + x2r;
+    a[j0 + 1] = x0i + x2i;
+    a[j1] = x0r - x2r;
+    a[j1 + 1] = x0i - x2i;
+    x0r = x1r - x3i;
+    x0i = x1i + x3r;
+    a[j2] = wn4r * (x0r - x0i);
+    a[j2 + 1] = wn4r * (x0i + x0r);
+    x0r = x1r + x3i;
+    x0i = x1i - x3r;
+    a[j3] = -wn4r * (x0r + x0i);
+    a[j3 + 1] = -wn4r * (x0i - x0r);
+    x0r = a[j0 + 2] + a[j2 + 2];
+    x0i = a[j0 + 3] + a[j2 + 3];
+    x1r = a[j0 + 2] - a[j2 + 2];
+    x1i = a[j0 + 3] - a[j2 + 3];
+    x2r = a[j1 + 2] + a[j3 + 2];
+    x2i = a[j1 + 3] + a[j3 + 3];
+    x3r = a[j1 + 2] - a[j3 + 2];
+    x3i = a[j1 + 3] - a[j3 + 3];
+    a[j0 + 2] = x0r + x2r;
+    a[j0 + 3] = x0i + x2i;
+    a[j1 + 2] = x0r - x2r;
+    a[j1 + 3] = x0i - x2i;
+    x0r = x1r - x3i;
+    x0i = x1i + x3r;
+    a[j2 + 2] = wk1i * x0r - wk1r * x0i;
+    a[j2 + 3] = wk1i * x0i + wk1r * x0r;
+    x0r = x1r + x3i;
+    x0i = x1i - x3r;
+    a[j3 + 2] = wk3i * x0r + wk3r * x0i;
+    a[j3 + 3] = wk3i * x0i - wk3r * x0r;
+}
+
+
+void cftb1st(int n, double *a, double *w)
+{
+    int j, j0, j1, j2, j3, k, m, mh;
+    double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, 
+        wd1r, wd1i, wd3r, wd3i;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, 
+        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;
+    
+    mh = n >> 3;
+    m = 2 * mh;
+    j1 = m;
+    j2 = j1 + m;
+    j3 = j2 + m;
+    x0r = a[0] + a[j2];
+    x0i = -a[1] - a[j2 + 1];
+    x1r = a[0] - a[j2];
+    x1i = -a[1] + a[j2 + 1];
+    x2r = a[j1] + a[j3];
+    x2i = a[j1 + 1] + a[j3 + 1];
+    x3r = a[j1] - a[j3];
+    x3i = a[j1 + 1] - a[j3 + 1];
+    a[0] = x0r + x2r;
+    a[1] = x0i - x2i;
+    a[j1] = x0r - x2r;
+    a[j1 + 1] = x0i + x2i;
+    a[j2] = x1r + x3i;
+    a[j2 + 1] = x1i + x3r;
+    a[j3] = x1r - x3i;
+    a[j3 + 1] = x1i - x3r;
+    wn4r = w[1];
+    csc1 = w[2];
+    csc3 = w[3];
+    wd1r = 1;
+    wd1i = 0;
+    wd3r = 1;
+    wd3i = 0;
+    k = 0;
+    for (j = 2; j < mh - 2; j += 4) {
+        k += 4;
+        wk1r = csc1 * (wd1r + w[k]);
+        wk1i = csc1 * (wd1i + w[k + 1]);
+        wk3r = csc3 * (wd3r + w[k + 2]);
+        wk3i = csc3 * (wd3i + w[k + 3]);
+        wd1r = w[k];
+        wd1i = w[k + 1];
+        wd3r = w[k + 2];
+        wd3i = w[k + 3];
+        j1 = j + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j] + a[j2];
+        x0i = -a[j + 1] - a[j2 + 1];
+        x1r = a[j] - a[j2];
+        x1i = -a[j + 1] + a[j2 + 1];
+        y0r = a[j + 2] + a[j2 + 2];
+        y0i = -a[j + 3] - a[j2 + 3];
+        y1r = a[j + 2] - a[j2 + 2];
+        y1i = -a[j + 3] + a[j2 + 3];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        y2r = a[j1 + 2] + a[j3 + 2];
+        y2i = a[j1 + 3] + a[j3 + 3];
+        y3r = a[j1 + 2] - a[j3 + 2];
+        y3i = a[j1 + 3] - a[j3 + 3];
+        a[j] = x0r + x2r;
+        a[j + 1] = x0i - x2i;
+        a[j + 2] = y0r + y2r;
+        a[j + 3] = y0i - y2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i + x2i;
+        a[j1 + 2] = y0r - y2r;
+        a[j1 + 3] = y0i + y2i;
+        x0r = x1r + x3i;
+        x0i = x1i + x3r;
+        a[j2] = wk1r * x0r - wk1i * x0i;
+        a[j2 + 1] = wk1r * x0i + wk1i * x0r;
+        x0r = y1r + y3i;
+        x0i = y1i + y3r;
+        a[j2 + 2] = wd1r * x0r - wd1i * x0i;
+        a[j2 + 3] = wd1r * x0i + wd1i * x0r;
+        x0r = x1r - x3i;
+        x0i = x1i - x3r;
+        a[j3] = wk3r * x0r + wk3i * x0i;
+        a[j3 + 1] = wk3r * x0i - wk3i * x0r;
+        x0r = y1r - y3i;
+        x0i = y1i - y3r;
+        a[j3 + 2] = wd3r * x0r + wd3i * x0i;
+        a[j3 + 3] = wd3r * x0i - wd3i * x0r;
+        j0 = m - j;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j0] + a[j2];
+        x0i = -a[j0 + 1] - a[j2 + 1];
+        x1r = a[j0] - a[j2];
+        x1i = -a[j0 + 1] + a[j2 + 1];
+        y0r = a[j0 - 2] + a[j2 - 2];
+        y0i = -a[j0 - 1] - a[j2 - 1];
+        y1r = a[j0 - 2] - a[j2 - 2];
+        y1i = -a[j0 - 1] + a[j2 - 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        y2r = a[j1 - 2] + a[j3 - 2];
+        y2i = a[j1 - 1] + a[j3 - 1];
+        y3r = a[j1 - 2] - a[j3 - 2];
+        y3i = a[j1 - 1] - a[j3 - 1];
+        a[j0] = x0r + x2r;
+        a[j0 + 1] = x0i - x2i;
+        a[j0 - 2] = y0r + y2r;
+        a[j0 - 1] = y0i - y2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i + x2i;
+        a[j1 - 2] = y0r - y2r;
+        a[j1 - 1] = y0i + y2i;
+        x0r = x1r + x3i;
+        x0i = x1i + x3r;
+        a[j2] = wk1i * x0r - wk1r * x0i;
+        a[j2 + 1] = wk1i * x0i + wk1r * x0r;
+        x0r = y1r + y3i;
+        x0i = y1i + y3r;
+        a[j2 - 2] = wd1i * x0r - wd1r * x0i;
+        a[j2 - 1] = wd1i * x0i + wd1r * x0r;
+        x0r = x1r - x3i;
+        x0i = x1i - x3r;
+        a[j3] = wk3i * x0r + wk3r * x0i;
+        a[j3 + 1] = wk3i * x0i - wk3r * x0r;
+        x0r = y1r - y3i;
+        x0i = y1i - y3r;
+        a[j3 - 2] = wd3i * x0r + wd3r * x0i;
+        a[j3 - 1] = wd3i * x0i - wd3r * x0r;
+    }
+    wk1r = csc1 * (wd1r + wn4r);
+    wk1i = csc1 * (wd1i + wn4r);
+    wk3r = csc3 * (wd3r - wn4r);
+    wk3i = csc3 * (wd3i - wn4r);
+    j0 = mh;
+    j1 = j0 + m;
+    j2 = j1 + m;
+    j3 = j2 + m;
+    x0r = a[j0 - 2] + a[j2 - 2];
+    x0i = -a[j0 - 1] - a[j2 - 1];
+    x1r = a[j0 - 2] - a[j2 - 2];
+    x1i = -a[j0 - 1] + a[j2 - 1];
+    x2r = a[j1 - 2] + a[j3 - 2];
+    x2i = a[j1 - 1] + a[j3 - 1];
+    x3r = a[j1 - 2] - a[j3 - 2];
+    x3i = a[j1 - 1] - a[j3 - 1];
+    a[j0 - 2] = x0r + x2r;
+    a[j0 - 1] = x0i - x2i;
+    a[j1 - 2] = x0r - x2r;
+    a[j1 - 1] = x0i + x2i;
+    x0r = x1r + x3i;
+    x0i = x1i + x3r;
+    a[j2 - 2] = wk1r * x0r - wk1i * x0i;
+    a[j2 - 1] = wk1r * x0i + wk1i * x0r;
+    x0r = x1r - x3i;
+    x0i = x1i - x3r;
+    a[j3 - 2] = wk3r * x0r + wk3i * x0i;
+    a[j3 - 1] = wk3r * x0i - wk3i * x0r;
+    x0r = a[j0] + a[j2];
+    x0i = -a[j0 + 1] - a[j2 + 1];
+    x1r = a[j0] - a[j2];
+    x1i = -a[j0 + 1] + a[j2 + 1];
+    x2r = a[j1] + a[j3];
+    x2i = a[j1 + 1] + a[j3 + 1];
+    x3r = a[j1] - a[j3];
+    x3i = a[j1 + 1] - a[j3 + 1];
+    a[j0] = x0r + x2r;
+    a[j0 + 1] = x0i - x2i;
+    a[j1] = x0r - x2r;
+    a[j1 + 1] = x0i + x2i;
+    x0r = x1r + x3i;
+    x0i = x1i + x3r;
+    a[j2] = wn4r * (x0r - x0i);
+    a[j2 + 1] = wn4r * (x0i + x0r);
+    x0r = x1r - x3i;
+    x0i = x1i - x3r;
+    a[j3] = -wn4r * (x0r + x0i);
+    a[j3 + 1] = -wn4r * (x0i - x0r);
+    x0r = a[j0 + 2] + a[j2 + 2];
+    x0i = -a[j0 + 3] - a[j2 + 3];
+    x1r = a[j0 + 2] - a[j2 + 2];
+    x1i = -a[j0 + 3] + a[j2 + 3];
+    x2r = a[j1 + 2] + a[j3 + 2];
+    x2i = a[j1 + 3] + a[j3 + 3];
+    x3r = a[j1 + 2] - a[j3 + 2];
+    x3i = a[j1 + 3] - a[j3 + 3];
+    a[j0 + 2] = x0r + x2r;
+    a[j0 + 3] = x0i - x2i;
+    a[j1 + 2] = x0r - x2r;
+    a[j1 + 3] = x0i + x2i;
+    x0r = x1r + x3i;
+    x0i = x1i + x3r;
+    a[j2 + 2] = wk1i * x0r - wk1r * x0i;
+    a[j2 + 3] = wk1i * x0i + wk1r * x0r;
+    x0r = x1r - x3i;
+    x0i = x1i - x3r;
+    a[j3 + 2] = wk3i * x0r + wk3r * x0i;
+    a[j3 + 3] = wk3i * x0i - wk3r * x0r;
+}
+
+
+#ifdef USE_CDFT_THREADS
+struct cdft_arg_st {
+    int n0;
+    int n;
+    double *a;
+    int nw;
+    double *w;
+};
+typedef struct cdft_arg_st cdft_arg_t;
+
+
+void cftrec4_th(int n, double *a, int nw, double *w)
+{
+    void *cftrec1_th(void *p);
+    void *cftrec2_th(void *p);
+    int i, idiv4, m, nthread;
+    cdft_thread_t th[4];
+    cdft_arg_t ag[4];
+    
+    nthread = 2;
+    idiv4 = 0;
+    m = n >> 1;
+    if (n > CDFT_4THREADS_BEGIN_N) {
+        nthread = 4;
+        idiv4 = 1;
+        m >>= 1;
+    }
+    for (i = 0; i < nthread; i++) {
+        ag[i].n0 = n;
+        ag[i].n = m;
+        ag[i].a = &a[i * m];
+        ag[i].nw = nw;
+        ag[i].w = w;
+        if (i != idiv4) {
+            cdft_thread_create(&th[i], cftrec1_th, &ag[i]);
+        } else {
+            cdft_thread_create(&th[i], cftrec2_th, &ag[i]);
+        }
+    }
+    for (i = 0; i < nthread; i++) {
+        cdft_thread_wait(th[i]);
+    }
+}
+
+
+void *cftrec1_th(void *p)
+{
+    int cfttree(int n, int j, int k, double *a, int nw, double *w);
+    void cftleaf(int n, int isplt, double *a, int nw, double *w);
+    void cftmdl1(int n, double *a, double *w);
+    int isplt, j, k, m, n, n0, nw;
+    double *a, *w;
+    
+    n0 = ((cdft_arg_t *) p)->n0;
+    n = ((cdft_arg_t *) p)->n;
+    a = ((cdft_arg_t *) p)->a;
+    nw = ((cdft_arg_t *) p)->nw;
+    w = ((cdft_arg_t *) p)->w;
+    m = n0;
+    while (m > 512) {
+        m >>= 2;
+        cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]);
+    }
+    cftleaf(m, 1, &a[n - m], nw, w);
+    k = 0;
+    for (j = n - m; j > 0; j -= m) {
+        k++;
+        isplt = cfttree(m, j, k, a, nw, w);
+        cftleaf(m, isplt, &a[j - m], nw, w);
+    }
+    return (void *) 0;
+}
+
+
+void *cftrec2_th(void *p)
+{
+    int cfttree(int n, int j, int k, double *a, int nw, double *w);
+    void cftleaf(int n, int isplt, double *a, int nw, double *w);
+    void cftmdl2(int n, double *a, double *w);
+    int isplt, j, k, m, n, n0, nw;
+    double *a, *w;
+    
+    n0 = ((cdft_arg_t *) p)->n0;
+    n = ((cdft_arg_t *) p)->n;
+    a = ((cdft_arg_t *) p)->a;
+    nw = ((cdft_arg_t *) p)->nw;
+    w = ((cdft_arg_t *) p)->w;
+    k = 1;
+    m = n0;
+    while (m > 512) {
+        m >>= 2;
+        k <<= 2;
+        cftmdl2(m, &a[n - m], &w[nw - m]);
+    }
+    cftleaf(m, 0, &a[n - m], nw, w);
+    k >>= 1;
+    for (j = n - m; j > 0; j -= m) {
+        k++;
+        isplt = cfttree(m, j, k, a, nw, w);
+        cftleaf(m, isplt, &a[j - m], nw, w);
+    }
+    return (void *) 0;
+}
+#endif /* USE_CDFT_THREADS */
+
+
+void cftrec4(int n, double *a, int nw, double *w)
+{
+    int cfttree(int n, int j, int k, double *a, int nw, double *w);
+    void cftleaf(int n, int isplt, double *a, int nw, double *w);
+    void cftmdl1(int n, double *a, double *w);
+    int isplt, j, k, m;
+    
+    m = n;
+    while (m > 512) {
+        m >>= 2;
+        cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]);
+    }
+    cftleaf(m, 1, &a[n - m], nw, w);
+    k = 0;
+    for (j = n - m; j > 0; j -= m) {
+        k++;
+        isplt = cfttree(m, j, k, a, nw, w);
+        cftleaf(m, isplt, &a[j - m], nw, w);
+    }
+}
+
+
+int cfttree(int n, int j, int k, double *a, int nw, double *w)
+{
+    void cftmdl1(int n, double *a, double *w);
+    void cftmdl2(int n, double *a, double *w);
+    int i, isplt, m;
+    
+    if ((k & 3) != 0) {
+        isplt = k & 1;
+        if (isplt != 0) {
+            cftmdl1(n, &a[j - n], &w[nw - (n >> 1)]);
+        } else {
+            cftmdl2(n, &a[j - n], &w[nw - n]);
+        }
+    } else {
+        m = n;
+        for (i = k; (i & 3) == 0; i >>= 2) {
+            m <<= 2;
+        }
+        isplt = i & 1;
+        if (isplt != 0) {
+            while (m > 128) {
+                cftmdl1(m, &a[j - m], &w[nw - (m >> 1)]);
+                m >>= 2;
+            }
+        } else {
+            while (m > 128) {
+                cftmdl2(m, &a[j - m], &w[nw - m]);
+                m >>= 2;
+            }
+        }
+    }
+    return isplt;
+}
+
+
+void cftleaf(int n, int isplt, double *a, int nw, double *w)
+{
+    void cftmdl1(int n, double *a, double *w);
+    void cftmdl2(int n, double *a, double *w);
+    void cftf161(double *a, double *w);
+    void cftf162(double *a, double *w);
+    void cftf081(double *a, double *w);
+    void cftf082(double *a, double *w);
+    
+    if (n == 512) {
+        cftmdl1(128, a, &w[nw - 64]);
+        cftf161(a, &w[nw - 8]);
+        cftf162(&a[32], &w[nw - 32]);
+        cftf161(&a[64], &w[nw - 8]);
+        cftf161(&a[96], &w[nw - 8]);
+        cftmdl2(128, &a[128], &w[nw - 128]);
+        cftf161(&a[128], &w[nw - 8]);
+        cftf162(&a[160], &w[nw - 32]);
+        cftf161(&a[192], &w[nw - 8]);
+        cftf162(&a[224], &w[nw - 32]);
+        cftmdl1(128, &a[256], &w[nw - 64]);
+        cftf161(&a[256], &w[nw - 8]);
+        cftf162(&a[288], &w[nw - 32]);
+        cftf161(&a[320], &w[nw - 8]);
+        cftf161(&a[352], &w[nw - 8]);
+        if (isplt != 0) {
+            cftmdl1(128, &a[384], &w[nw - 64]);
+            cftf161(&a[480], &w[nw - 8]);
+        } else {
+            cftmdl2(128, &a[384], &w[nw - 128]);
+            cftf162(&a[480], &w[nw - 32]);
+        }
+        cftf161(&a[384], &w[nw - 8]);
+        cftf162(&a[416], &w[nw - 32]);
+        cftf161(&a[448], &w[nw - 8]);
+    } else {
+        cftmdl1(64, a, &w[nw - 32]);
+        cftf081(a, &w[nw - 8]);
+        cftf082(&a[16], &w[nw - 8]);
+        cftf081(&a[32], &w[nw - 8]);
+        cftf081(&a[48], &w[nw - 8]);
+        cftmdl2(64, &a[64], &w[nw - 64]);
+        cftf081(&a[64], &w[nw - 8]);
+        cftf082(&a[80], &w[nw - 8]);
+        cftf081(&a[96], &w[nw - 8]);
+        cftf082(&a[112], &w[nw - 8]);
+        cftmdl1(64, &a[128], &w[nw - 32]);
+        cftf081(&a[128], &w[nw - 8]);
+        cftf082(&a[144], &w[nw - 8]);
+        cftf081(&a[160], &w[nw - 8]);
+        cftf081(&a[176], &w[nw - 8]);
+        if (isplt != 0) {
+            cftmdl1(64, &a[192], &w[nw - 32]);
+            cftf081(&a[240], &w[nw - 8]);
+        } else {
+            cftmdl2(64, &a[192], &w[nw - 64]);
+            cftf082(&a[240], &w[nw - 8]);
+        }
+        cftf081(&a[192], &w[nw - 8]);
+        cftf082(&a[208], &w[nw - 8]);
+        cftf081(&a[224], &w[nw - 8]);
+    }
+}
+
+
+void cftmdl1(int n, double *a, double *w)
+{
+    int j, j0, j1, j2, j3, k, m, mh;
+    double wn4r, wk1r, wk1i, wk3r, wk3i;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+    
+    mh = n >> 3;
+    m = 2 * mh;
+    j1 = m;
+    j2 = j1 + m;
+    j3 = j2 + m;
+    x0r = a[0] + a[j2];
+    x0i = a[1] + a[j2 + 1];
+    x1r = a[0] - a[j2];
+    x1i = a[1] - a[j2 + 1];
+    x2r = a[j1] + a[j3];
+    x2i = a[j1 + 1] + a[j3 + 1];
+    x3r = a[j1] - a[j3];
+    x3i = a[j1 + 1] - a[j3 + 1];
+    a[0] = x0r + x2r;
+    a[1] = x0i + x2i;
+    a[j1] = x0r - x2r;
+    a[j1 + 1] = x0i - x2i;
+    a[j2] = x1r - x3i;
+    a[j2 + 1] = x1i + x3r;
+    a[j3] = x1r + x3i;
+    a[j3 + 1] = x1i - x3r;
+    wn4r = w[1];
+    k = 0;
+    for (j = 2; j < mh; j += 2) {
+        k += 4;
+        wk1r = w[k];
+        wk1i = w[k + 1];
+        wk3r = w[k + 2];
+        wk3i = w[k + 3];
+        j1 = j + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j] + a[j2];
+        x0i = a[j + 1] + a[j2 + 1];
+        x1r = a[j] - a[j2];
+        x1i = a[j + 1] - a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[j] = x0r + x2r;
+        a[j + 1] = x0i + x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2] = wk1r * x0r - wk1i * x0i;
+        a[j2 + 1] = wk1r * x0i + wk1i * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3] = wk3r * x0r + wk3i * x0i;
+        a[j3 + 1] = wk3r * x0i - wk3i * x0r;
+        j0 = m - j;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j0] + a[j2];
+        x0i = a[j0 + 1] + a[j2 + 1];
+        x1r = a[j0] - a[j2];
+        x1i = a[j0 + 1] - a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[j0] = x0r + x2r;
+        a[j0 + 1] = x0i + x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2] = wk1i * x0r - wk1r * x0i;
+        a[j2 + 1] = wk1i * x0i + wk1r * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3] = wk3i * x0r + wk3r * x0i;
+        a[j3 + 1] = wk3i * x0i - wk3r * x0r;
+    }
+    j0 = mh;
+    j1 = j0 + m;
+    j2 = j1 + m;
+    j3 = j2 + m;
+    x0r = a[j0] + a[j2];
+    x0i = a[j0 + 1] + a[j2 + 1];
+    x1r = a[j0] - a[j2];
+    x1i = a[j0 + 1] - a[j2 + 1];
+    x2r = a[j1] + a[j3];
+    x2i = a[j1 + 1] + a[j3 + 1];
+    x3r = a[j1] - a[j3];
+    x3i = a[j1 + 1] - a[j3 + 1];
+    a[j0] = x0r + x2r;
+    a[j0 + 1] = x0i + x2i;
+    a[j1] = x0r - x2r;
+    a[j1 + 1] = x0i - x2i;
+    x0r = x1r - x3i;
+    x0i = x1i + x3r;
+    a[j2] = wn4r * (x0r - x0i);
+    a[j2 + 1] = wn4r * (x0i + x0r);
+    x0r = x1r + x3i;
+    x0i = x1i - x3r;
+    a[j3] = -wn4r * (x0r + x0i);
+    a[j3 + 1] = -wn4r * (x0i - x0r);
+}
+
+
+void cftmdl2(int n, double *a, double *w)
+{
+    int j, j0, j1, j2, j3, k, kr, m, mh;
+    double wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i;
+    
+    mh = n >> 3;
+    m = 2 * mh;
+    wn4r = w[1];
+    j1 = m;
+    j2 = j1 + m;
+    j3 = j2 + m;
+    x0r = a[0] - a[j2 + 1];
+    x0i = a[1] + a[j2];
+    x1r = a[0] + a[j2 + 1];
+    x1i = a[1] - a[j2];
+    x2r = a[j1] - a[j3 + 1];
+    x2i = a[j1 + 1] + a[j3];
+    x3r = a[j1] + a[j3 + 1];
+    x3i = a[j1 + 1] - a[j3];
+    y0r = wn4r * (x2r - x2i);
+    y0i = wn4r * (x2i + x2r);
+    a[0] = x0r + y0r;
+    a[1] = x0i + y0i;
+    a[j1] = x0r - y0r;
+    a[j1 + 1] = x0i - y0i;
+    y0r = wn4r * (x3r - x3i);
+    y0i = wn4r * (x3i + x3r);
+    a[j2] = x1r - y0i;
+    a[j2 + 1] = x1i + y0r;
+    a[j3] = x1r + y0i;
+    a[j3 + 1] = x1i - y0r;
+    k = 0;
+    kr = 2 * m;
+    for (j = 2; j < mh; j += 2) {
+        k += 4;
+        wk1r = w[k];
+        wk1i = w[k + 1];
+        wk3r = w[k + 2];
+        wk3i = w[k + 3];
+        kr -= 4;
+        wd1i = w[kr];
+        wd1r = w[kr + 1];
+        wd3i = w[kr + 2];
+        wd3r = w[kr + 3];
+        j1 = j + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j] - a[j2 + 1];
+        x0i = a[j + 1] + a[j2];
+        x1r = a[j] + a[j2 + 1];
+        x1i = a[j + 1] - a[j2];
+        x2r = a[j1] - a[j3 + 1];
+        x2i = a[j1 + 1] + a[j3];
+        x3r = a[j1] + a[j3 + 1];
+        x3i = a[j1 + 1] - a[j3];
+        y0r = wk1r * x0r - wk1i * x0i;
+        y0i = wk1r * x0i + wk1i * x0r;
+        y2r = wd1r * x2r - wd1i * x2i;
+        y2i = wd1r * x2i + wd1i * x2r;
+        a[j] = y0r + y2r;
+        a[j + 1] = y0i + y2i;
+        a[j1] = y0r - y2r;
+        a[j1 + 1] = y0i - y2i;
+        y0r = wk3r * x1r + wk3i * x1i;
+        y0i = wk3r * x1i - wk3i * x1r;
+        y2r = wd3r * x3r + wd3i * x3i;
+        y2i = wd3r * x3i - wd3i * x3r;
+        a[j2] = y0r + y2r;
+        a[j2 + 1] = y0i + y2i;
+        a[j3] = y0r - y2r;
+        a[j3 + 1] = y0i - y2i;
+        j0 = m - j;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j0] - a[j2 + 1];
+        x0i = a[j0 + 1] + a[j2];
+        x1r = a[j0] + a[j2 + 1];
+        x1i = a[j0 + 1] - a[j2];
+        x2r = a[j1] - a[j3 + 1];
+        x2i = a[j1 + 1] + a[j3];
+        x3r = a[j1] + a[j3 + 1];
+        x3i = a[j1 + 1] - a[j3];
+        y0r = wd1i * x0r - wd1r * x0i;
+        y0i = wd1i * x0i + wd1r * x0r;
+        y2r = wk1i * x2r - wk1r * x2i;
+        y2i = wk1i * x2i + wk1r * x2r;
+        a[j0] = y0r + y2r;
+        a[j0 + 1] = y0i + y2i;
+        a[j1] = y0r - y2r;
+        a[j1 + 1] = y0i - y2i;
+        y0r = wd3i * x1r + wd3r * x1i;
+        y0i = wd3i * x1i - wd3r * x1r;
+        y2r = wk3i * x3r + wk3r * x3i;
+        y2i = wk3i * x3i - wk3r * x3r;
+        a[j2] = y0r + y2r;
+        a[j2 + 1] = y0i + y2i;
+        a[j3] = y0r - y2r;
+        a[j3 + 1] = y0i - y2i;
+    }
+    wk1r = w[m];
+    wk1i = w[m + 1];
+    j0 = mh;
+    j1 = j0 + m;
+    j2 = j1 + m;
+    j3 = j2 + m;
+    x0r = a[j0] - a[j2 + 1];
+    x0i = a[j0 + 1] + a[j2];
+    x1r = a[j0] + a[j2 + 1];
+    x1i = a[j0 + 1] - a[j2];
+    x2r = a[j1] - a[j3 + 1];
+    x2i = a[j1 + 1] + a[j3];
+    x3r = a[j1] + a[j3 + 1];
+    x3i = a[j1 + 1] - a[j3];
+    y0r = wk1r * x0r - wk1i * x0i;
+    y0i = wk1r * x0i + wk1i * x0r;
+    y2r = wk1i * x2r - wk1r * x2i;
+    y2i = wk1i * x2i + wk1r * x2r;
+    a[j0] = y0r + y2r;
+    a[j0 + 1] = y0i + y2i;
+    a[j1] = y0r - y2r;
+    a[j1 + 1] = y0i - y2i;
+    y0r = wk1i * x1r - wk1r * x1i;
+    y0i = wk1i * x1i + wk1r * x1r;
+    y2r = wk1r * x3r - wk1i * x3i;
+    y2i = wk1r * x3i + wk1i * x3r;
+    a[j2] = y0r - y2r;
+    a[j2 + 1] = y0i - y2i;
+    a[j3] = y0r + y2r;
+    a[j3 + 1] = y0i + y2i;
+}
+
+
+void cftfx41(int n, double *a, int nw, double *w)
+{
+    void cftf161(double *a, double *w);
+    void cftf162(double *a, double *w);
+    void cftf081(double *a, double *w);
+    void cftf082(double *a, double *w);
+    
+    if (n == 128) {
+        cftf161(a, &w[nw - 8]);
+        cftf162(&a[32], &w[nw - 32]);
+        cftf161(&a[64], &w[nw - 8]);
+        cftf161(&a[96], &w[nw - 8]);
+    } else {
+        cftf081(a, &w[nw - 8]);
+        cftf082(&a[16], &w[nw - 8]);
+        cftf081(&a[32], &w[nw - 8]);
+        cftf081(&a[48], &w[nw - 8]);
+    }
+}
+
+
+void cftf161(double *a, double *w)
+{
+    double wn4r, wk1r, wk1i, 
+        x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, 
+        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, 
+        y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, 
+        y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, 
+        y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+    
+    wn4r = w[1];
+    wk1r = w[2];
+    wk1i = w[3];
+    x0r = a[0] + a[16];
+    x0i = a[1] + a[17];
+    x1r = a[0] - a[16];
+    x1i = a[1] - a[17];
+    x2r = a[8] + a[24];
+    x2i = a[9] + a[25];
+    x3r = a[8] - a[24];
+    x3i = a[9] - a[25];
+    y0r = x0r + x2r;
+    y0i = x0i + x2i;
+    y4r = x0r - x2r;
+    y4i = x0i - x2i;
+    y8r = x1r - x3i;
+    y8i = x1i + x3r;
+    y12r = x1r + x3i;
+    y12i = x1i - x3r;
+    x0r = a[2] + a[18];
+    x0i = a[3] + a[19];
+    x1r = a[2] - a[18];
+    x1i = a[3] - a[19];
+    x2r = a[10] + a[26];
+    x2i = a[11] + a[27];
+    x3r = a[10] - a[26];
+    x3i = a[11] - a[27];
+    y1r = x0r + x2r;
+    y1i = x0i + x2i;
+    y5r = x0r - x2r;
+    y5i = x0i - x2i;
+    x0r = x1r - x3i;
+    x0i = x1i + x3r;
+    y9r = wk1r * x0r - wk1i * x0i;
+    y9i = wk1r * x0i + wk1i * x0r;
+    x0r = x1r + x3i;
+    x0i = x1i - x3r;
+    y13r = wk1i * x0r - wk1r * x0i;
+    y13i = wk1i * x0i + wk1r * x0r;
+    x0r = a[4] + a[20];
+    x0i = a[5] + a[21];
+    x1r = a[4] - a[20];
+    x1i = a[5] - a[21];
+    x2r = a[12] + a[28];
+    x2i = a[13] + a[29];
+    x3r = a[12] - a[28];
+    x3i = a[13] - a[29];
+    y2r = x0r + x2r;
+    y2i = x0i + x2i;
+    y6r = x0r - x2r;
+    y6i = x0i - x2i;
+    x0r = x1r - x3i;
+    x0i = x1i + x3r;
+    y10r = wn4r * (x0r - x0i);
+    y10i = wn4r * (x0i + x0r);
+    x0r = x1r + x3i;
+    x0i = x1i - x3r;
+    y14r = wn4r * (x0r + x0i);
+    y14i = wn4r * (x0i - x0r);
+    x0r = a[6] + a[22];
+    x0i = a[7] + a[23];
+    x1r = a[6] - a[22];
+    x1i = a[7] - a[23];
+    x2r = a[14] + a[30];
+    x2i = a[15] + a[31];
+    x3r = a[14] - a[30];
+    x3i = a[15] - a[31];
+    y3r = x0r + x2r;
+    y3i = x0i + x2i;
+    y7r = x0r - x2r;
+    y7i = x0i - x2i;
+    x0r = x1r - x3i;
+    x0i = x1i + x3r;
+    y11r = wk1i * x0r - wk1r * x0i;
+    y11i = wk1i * x0i + wk1r * x0r;
+    x0r = x1r + x3i;
+    x0i = x1i - x3r;
+    y15r = wk1r * x0r - wk1i * x0i;
+    y15i = wk1r * x0i + wk1i * x0r;
+    x0r = y12r - y14r;
+    x0i = y12i - y14i;
+    x1r = y12r + y14r;
+    x1i = y12i + y14i;
+    x2r = y13r - y15r;
+    x2i = y13i - y15i;
+    x3r = y13r + y15r;
+    x3i = y13i + y15i;
+    a[24] = x0r + x2r;
+    a[25] = x0i + x2i;
+    a[26] = x0r - x2r;
+    a[27] = x0i - x2i;
+    a[28] = x1r - x3i;
+    a[29] = x1i + x3r;
+    a[30] = x1r + x3i;
+    a[31] = x1i - x3r;
+    x0r = y8r + y10r;
+    x0i = y8i + y10i;
+    x1r = y8r - y10r;
+    x1i = y8i - y10i;
+    x2r = y9r + y11r;
+    x2i = y9i + y11i;
+    x3r = y9r - y11r;
+    x3i = y9i - y11i;
+    a[16] = x0r + x2r;
+    a[17] = x0i + x2i;
+    a[18] = x0r - x2r;
+    a[19] = x0i - x2i;
+    a[20] = x1r - x3i;
+    a[21] = x1i + x3r;
+    a[22] = x1r + x3i;
+    a[23] = x1i - x3r;
+    x0r = y5r - y7i;
+    x0i = y5i + y7r;
+    x2r = wn4r * (x0r - x0i);
+    x2i = wn4r * (x0i + x0r);
+    x0r = y5r + y7i;
+    x0i = y5i - y7r;
+    x3r = wn4r * (x0r - x0i);
+    x3i = wn4r * (x0i + x0r);
+    x0r = y4r - y6i;
+    x0i = y4i + y6r;
+    x1r = y4r + y6i;
+    x1i = y4i - y6r;
+    a[8] = x0r + x2r;
+    a[9] = x0i + x2i;
+    a[10] = x0r - x2r;
+    a[11] = x0i - x2i;
+    a[12] = x1r - x3i;
+    a[13] = x1i + x3r;
+    a[14] = x1r + x3i;
+    a[15] = x1i - x3r;
+    x0r = y0r + y2r;
+    x0i = y0i + y2i;
+    x1r = y0r - y2r;
+    x1i = y0i - y2i;
+    x2r = y1r + y3r;
+    x2i = y1i + y3i;
+    x3r = y1r - y3r;
+    x3i = y1i - y3i;
+    a[0] = x0r + x2r;
+    a[1] = x0i + x2i;
+    a[2] = x0r - x2r;
+    a[3] = x0i - x2i;
+    a[4] = x1r - x3i;
+    a[5] = x1i + x3r;
+    a[6] = x1r + x3i;
+    a[7] = x1i - x3r;
+}
+
+
+void cftf162(double *a, double *w)
+{
+    double wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, 
+        x0r, x0i, x1r, x1i, x2r, x2i, 
+        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, 
+        y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, 
+        y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, 
+        y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+    
+    wn4r = w[1];
+    wk1r = w[4];
+    wk1i = w[5];
+    wk3r = w[6];
+    wk3i = -w[7];
+    wk2r = w[8];
+    wk2i = w[9];
+    x1r = a[0] - a[17];
+    x1i = a[1] + a[16];
+    x0r = a[8] - a[25];
+    x0i = a[9] + a[24];
+    x2r = wn4r * (x0r - x0i);
+    x2i = wn4r * (x0i + x0r);
+    y0r = x1r + x2r;
+    y0i = x1i + x2i;
+    y4r = x1r - x2r;
+    y4i = x1i - x2i;
+    x1r = a[0] + a[17];
+    x1i = a[1] - a[16];
+    x0r = a[8] + a[25];
+    x0i = a[9] - a[24];
+    x2r = wn4r * (x0r - x0i);
+    x2i = wn4r * (x0i + x0r);
+    y8r = x1r - x2i;
+    y8i = x1i + x2r;
+    y12r = x1r + x2i;
+    y12i = x1i - x2r;
+    x0r = a[2] - a[19];
+    x0i = a[3] + a[18];
+    x1r = wk1r * x0r - wk1i * x0i;
+    x1i = wk1r * x0i + wk1i * x0r;
+    x0r = a[10] - a[27];
+    x0i = a[11] + a[26];
+    x2r = wk3i * x0r - wk3r * x0i;
+    x2i = wk3i * x0i + wk3r * x0r;
+    y1r = x1r + x2r;
+    y1i = x1i + x2i;
+    y5r = x1r - x2r;
+    y5i = x1i - x2i;
+    x0r = a[2] + a[19];
+    x0i = a[3] - a[18];
+    x1r = wk3r * x0r - wk3i * x0i;
+    x1i = wk3r * x0i + wk3i * x0r;
+    x0r = a[10] + a[27];
+    x0i = a[11] - a[26];
+    x2r = wk1r * x0r + wk1i * x0i;
+    x2i = wk1r * x0i - wk1i * x0r;
+    y9r = x1r - x2r;
+    y9i = x1i - x2i;
+    y13r = x1r + x2r;
+    y13i = x1i + x2i;
+    x0r = a[4] - a[21];
+    x0i = a[5] + a[20];
+    x1r = wk2r * x0r - wk2i * x0i;
+    x1i = wk2r * x0i + wk2i * x0r;
+    x0r = a[12] - a[29];
+    x0i = a[13] + a[28];
+    x2r = wk2i * x0r - wk2r * x0i;
+    x2i = wk2i * x0i + wk2r * x0r;
+    y2r = x1r + x2r;
+    y2i = x1i + x2i;
+    y6r = x1r - x2r;
+    y6i = x1i - x2i;
+    x0r = a[4] + a[21];
+    x0i = a[5] - a[20];
+    x1r = wk2i * x0r - wk2r * x0i;
+    x1i = wk2i * x0i + wk2r * x0r;
+    x0r = a[12] + a[29];
+    x0i = a[13] - a[28];
+    x2r = wk2r * x0r - wk2i * x0i;
+    x2i = wk2r * x0i + wk2i * x0r;
+    y10r = x1r - x2r;
+    y10i = x1i - x2i;
+    y14r = x1r + x2r;
+    y14i = x1i + x2i;
+    x0r = a[6] - a[23];
+    x0i = a[7] + a[22];
+    x1r = wk3r * x0r - wk3i * x0i;
+    x1i = wk3r * x0i + wk3i * x0r;
+    x0r = a[14] - a[31];
+    x0i = a[15] + a[30];
+    x2r = wk1i * x0r - wk1r * x0i;
+    x2i = wk1i * x0i + wk1r * x0r;
+    y3r = x1r + x2r;
+    y3i = x1i + x2i;
+    y7r = x1r - x2r;
+    y7i = x1i - x2i;
+    x0r = a[6] + a[23];
+    x0i = a[7] - a[22];
+    x1r = wk1i * x0r + wk1r * x0i;
+    x1i = wk1i * x0i - wk1r * x0r;
+    x0r = a[14] + a[31];
+    x0i = a[15] - a[30];
+    x2r = wk3i * x0r - wk3r * x0i;
+    x2i = wk3i * x0i + wk3r * x0r;
+    y11r = x1r + x2r;
+    y11i = x1i + x2i;
+    y15r = x1r - x2r;
+    y15i = x1i - x2i;
+    x1r = y0r + y2r;
+    x1i = y0i + y2i;
+    x2r = y1r + y3r;
+    x2i = y1i + y3i;
+    a[0] = x1r + x2r;
+    a[1] = x1i + x2i;
+    a[2] = x1r - x2r;
+    a[3] = x1i - x2i;
+    x1r = y0r - y2r;
+    x1i = y0i - y2i;
+    x2r = y1r - y3r;
+    x2i = y1i - y3i;
+    a[4] = x1r - x2i;
+    a[5] = x1i + x2r;
+    a[6] = x1r + x2i;
+    a[7] = x1i - x2r;
+    x1r = y4r - y6i;
+    x1i = y4i + y6r;
+    x0r = y5r - y7i;
+    x0i = y5i + y7r;
+    x2r = wn4r * (x0r - x0i);
+    x2i = wn4r * (x0i + x0r);
+    a[8] = x1r + x2r;
+    a[9] = x1i + x2i;
+    a[10] = x1r - x2r;
+    a[11] = x1i - x2i;
+    x1r = y4r + y6i;
+    x1i = y4i - y6r;
+    x0r = y5r + y7i;
+    x0i = y5i - y7r;
+    x2r = wn4r * (x0r - x0i);
+    x2i = wn4r * (x0i + x0r);
+    a[12] = x1r - x2i;
+    a[13] = x1i + x2r;
+    a[14] = x1r + x2i;
+    a[15] = x1i - x2r;
+    x1r = y8r + y10r;
+    x1i = y8i + y10i;
+    x2r = y9r - y11r;
+    x2i = y9i - y11i;
+    a[16] = x1r + x2r;
+    a[17] = x1i + x2i;
+    a[18] = x1r - x2r;
+    a[19] = x1i - x2i;
+    x1r = y8r - y10r;
+    x1i = y8i - y10i;
+    x2r = y9r + y11r;
+    x2i = y9i + y11i;
+    a[20] = x1r - x2i;
+    a[21] = x1i + x2r;
+    a[22] = x1r + x2i;
+    a[23] = x1i - x2r;
+    x1r = y12r - y14i;
+    x1i = y12i + y14r;
+    x0r = y13r + y15i;
+    x0i = y13i - y15r;
+    x2r = wn4r * (x0r - x0i);
+    x2i = wn4r * (x0i + x0r);
+    a[24] = x1r + x2r;
+    a[25] = x1i + x2i;
+    a[26] = x1r - x2r;
+    a[27] = x1i - x2i;
+    x1r = y12r + y14i;
+    x1i = y12i - y14r;
+    x0r = y13r - y15i;
+    x0i = y13i + y15r;
+    x2r = wn4r * (x0r - x0i);
+    x2i = wn4r * (x0i + x0r);
+    a[28] = x1r - x2i;
+    a[29] = x1i + x2r;
+    a[30] = x1r + x2i;
+    a[31] = x1i - x2r;
+}
+
+
+void cftf081(double *a, double *w)
+{
+    double wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, 
+        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, 
+        y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+    
+    wn4r = w[1];
+    x0r = a[0] + a[8];
+    x0i = a[1] + a[9];
+    x1r = a[0] - a[8];
+    x1i = a[1] - a[9];
+    x2r = a[4] + a[12];
+    x2i = a[5] + a[13];
+    x3r = a[4] - a[12];
+    x3i = a[5] - a[13];
+    y0r = x0r + x2r;
+    y0i = x0i + x2i;
+    y2r = x0r - x2r;
+    y2i = x0i - x2i;
+    y1r = x1r - x3i;
+    y1i = x1i + x3r;
+    y3r = x1r + x3i;
+    y3i = x1i - x3r;
+    x0r = a[2] + a[10];
+    x0i = a[3] + a[11];
+    x1r = a[2] - a[10];
+    x1i = a[3] - a[11];
+    x2r = a[6] + a[14];
+    x2i = a[7] + a[15];
+    x3r = a[6] - a[14];
+    x3i = a[7] - a[15];
+    y4r = x0r + x2r;
+    y4i = x0i + x2i;
+    y6r = x0r - x2r;
+    y6i = x0i - x2i;
+    x0r = x1r - x3i;
+    x0i = x1i + x3r;
+    x2r = x1r + x3i;
+    x2i = x1i - x3r;
+    y5r = wn4r * (x0r - x0i);
+    y5i = wn4r * (x0r + x0i);
+    y7r = wn4r * (x2r - x2i);
+    y7i = wn4r * (x2r + x2i);
+    a[8] = y1r + y5r;
+    a[9] = y1i + y5i;
+    a[10] = y1r - y5r;
+    a[11] = y1i - y5i;
+    a[12] = y3r - y7i;
+    a[13] = y3i + y7r;
+    a[14] = y3r + y7i;
+    a[15] = y3i - y7r;
+    a[0] = y0r + y4r;
+    a[1] = y0i + y4i;
+    a[2] = y0r - y4r;
+    a[3] = y0i - y4i;
+    a[4] = y2r - y6i;
+    a[5] = y2i + y6r;
+    a[6] = y2r + y6i;
+    a[7] = y2i - y6r;
+}
+
+
+void cftf082(double *a, double *w)
+{
+    double wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, 
+        y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, 
+        y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+    
+    wn4r = w[1];
+    wk1r = w[2];
+    wk1i = w[3];
+    y0r = a[0] - a[9];
+    y0i = a[1] + a[8];
+    y1r = a[0] + a[9];
+    y1i = a[1] - a[8];
+    x0r = a[4] - a[13];
+    x0i = a[5] + a[12];
+    y2r = wn4r * (x0r - x0i);
+    y2i = wn4r * (x0i + x0r);
+    x0r = a[4] + a[13];
+    x0i = a[5] - a[12];
+    y3r = wn4r * (x0r - x0i);
+    y3i = wn4r * (x0i + x0r);
+    x0r = a[2] - a[11];
+    x0i = a[3] + a[10];
+    y4r = wk1r * x0r - wk1i * x0i;
+    y4i = wk1r * x0i + wk1i * x0r;
+    x0r = a[2] + a[11];
+    x0i = a[3] - a[10];
+    y5r = wk1i * x0r - wk1r * x0i;
+    y5i = wk1i * x0i + wk1r * x0r;
+    x0r = a[6] - a[15];
+    x0i = a[7] + a[14];
+    y6r = wk1i * x0r - wk1r * x0i;
+    y6i = wk1i * x0i + wk1r * x0r;
+    x0r = a[6] + a[15];
+    x0i = a[7] - a[14];
+    y7r = wk1r * x0r - wk1i * x0i;
+    y7i = wk1r * x0i + wk1i * x0r;
+    x0r = y0r + y2r;
+    x0i = y0i + y2i;
+    x1r = y4r + y6r;
+    x1i = y4i + y6i;
+    a[0] = x0r + x1r;
+    a[1] = x0i + x1i;
+    a[2] = x0r - x1r;
+    a[3] = x0i - x1i;
+    x0r = y0r - y2r;
+    x0i = y0i - y2i;
+    x1r = y4r - y6r;
+    x1i = y4i - y6i;
+    a[4] = x0r - x1i;
+    a[5] = x0i + x1r;
+    a[6] = x0r + x1i;
+    a[7] = x0i - x1r;
+    x0r = y1r - y3i;
+    x0i = y1i + y3r;
+    x1r = y5r - y7r;
+    x1i = y5i - y7i;
+    a[8] = x0r + x1r;
+    a[9] = x0i + x1i;
+    a[10] = x0r - x1r;
+    a[11] = x0i - x1i;
+    x0r = y1r + y3i;
+    x0i = y1i - y3r;
+    x1r = y5r + y7r;
+    x1i = y5i + y7i;
+    a[12] = x0r - x1i;
+    a[13] = x0i + x1r;
+    a[14] = x0r + x1i;
+    a[15] = x0i - x1r;
+}
+
+
+void cftf040(double *a)
+{
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+    
+    x0r = a[0] + a[4];
+    x0i = a[1] + a[5];
+    x1r = a[0] - a[4];
+    x1i = a[1] - a[5];
+    x2r = a[2] + a[6];
+    x2i = a[3] + a[7];
+    x3r = a[2] - a[6];
+    x3i = a[3] - a[7];
+    a[0] = x0r + x2r;
+    a[1] = x0i + x2i;
+    a[2] = x1r - x3i;
+    a[3] = x1i + x3r;
+    a[4] = x0r - x2r;
+    a[5] = x0i - x2i;
+    a[6] = x1r + x3i;
+    a[7] = x1i - x3r;
+}
+
+
+void cftb040(double *a)
+{
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+    
+    x0r = a[0] + a[4];
+    x0i = a[1] + a[5];
+    x1r = a[0] - a[4];
+    x1i = a[1] - a[5];
+    x2r = a[2] + a[6];
+    x2i = a[3] + a[7];
+    x3r = a[2] - a[6];
+    x3i = a[3] - a[7];
+    a[0] = x0r + x2r;
+    a[1] = x0i + x2i;
+    a[2] = x1r + x3i;
+    a[3] = x1i - x3r;
+    a[4] = x0r - x2r;
+    a[5] = x0i - x2i;
+    a[6] = x1r - x3i;
+    a[7] = x1i + x3r;
+}
+
+
+void cftx020(double *a)
+{
+    double x0r, x0i;
+    
+    x0r = a[0] - a[2];
+    x0i = a[1] - a[3];
+    a[0] += a[2];
+    a[1] += a[3];
+    a[2] = x0r;
+    a[3] = x0i;
+}
+
+
+void rftfsub(int n, double *a, int nc, double *c)
+{
+    int j, k, kk, ks, m;
+    double wkr, wki, xr, xi, yr, yi;
+    
+    m = n >> 1;
+    ks = 2 * nc / m;
+    kk = 0;
+    for (j = 2; j < m; j += 2) {
+        k = n - j;
+        kk += ks;
+        wkr = 0.5 - c[nc - kk];
+        wki = c[kk];
+        xr = a[j] - a[k];
+        xi = a[j + 1] + a[k + 1];
+        yr = wkr * xr - wki * xi;
+        yi = wkr * xi + wki * xr;
+        a[j] -= yr;
+        a[j + 1] -= yi;
+        a[k] += yr;
+        a[k + 1] -= yi;
+    }
+}
+
+
+void rftbsub(int n, double *a, int nc, double *c)
+{
+    int j, k, kk, ks, m;
+    double wkr, wki, xr, xi, yr, yi;
+    
+    m = n >> 1;
+    ks = 2 * nc / m;
+    kk = 0;
+    for (j = 2; j < m; j += 2) {
+        k = n - j;
+        kk += ks;
+        wkr = 0.5 - c[nc - kk];
+        wki = c[kk];
+        xr = a[j] - a[k];
+        xi = a[j + 1] + a[k + 1];
+        yr = wkr * xr + wki * xi;
+        yi = wkr * xi - wki * xr;
+        a[j] -= yr;
+        a[j + 1] -= yi;
+        a[k] += yr;
+        a[k + 1] -= yi;
+    }
+}
+
+
+void dctsub(int n, double *a, int nc, double *c)
+{
+    int j, k, kk, ks, m;
+    double wkr, wki, xr;
+    
+    m = n >> 1;
+    ks = nc / n;
+    kk = 0;
+    for (j = 1; j < m; j++) {
+        k = n - j;
+        kk += ks;
+        wkr = c[kk] - c[nc - kk];
+        wki = c[kk] + c[nc - kk];
+        xr = wki * a[j] - wkr * a[k];
+        a[j] = wkr * a[j] + wki * a[k];
+        a[k] = xr;
+    }
+    a[m] *= c[0];
+}
+
+
+void dstsub(int n, double *a, int nc, double *c)
+{
+    int j, k, kk, ks, m;
+    double wkr, wki, xr;
+    
+    m = n >> 1;
+    ks = nc / n;
+    kk = 0;
+    for (j = 1; j < m; j++) {
+        k = n - j;
+        kk += ks;
+        wkr = c[kk] - c[nc - kk];
+        wki = c[kk] + c[nc - kk];
+        xr = wki * a[k] - wkr * a[j];
+        a[k] = wkr * a[k] + wki * a[j];
+        a[j] = xr;
+    }
+    a[m] *= c[0];
+}
+
diff --git a/third_party/tensorflow_dependencies/fft2d/fftsg.f b/third_party/tensorflow_dependencies/fft2d/fftsg.f
new file mode 100644
index 0000000..6fb26c4
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/fftsg.f
@@ -0,0 +1,2967 @@
+! Fast Fourier/Cosine/Sine Transform
+!     dimension   :one
+!     data length :power of 2
+!     decimation  :frequency
+!     radix       :split-radix
+!     data        :inplace
+!     table       :use
+! subroutines
+!     cdft: Complex Discrete Fourier Transform
+!     rdft: Real Discrete Fourier Transform
+!     ddct: Discrete Cosine Transform
+!     ddst: Discrete Sine Transform
+!     dfct: Cosine Transform of RDFT (Real Symmetric DFT)
+!     dfst: Sine Transform of RDFT (Real Anti-symmetric DFT)
+!
+!
+! -------- Complex DFT (Discrete Fourier Transform) --------
+!     [definition]
+!         <case1>
+!             X(k) = sum_j=0^n-1 x(j)*exp(2*pi*i*j*k/n), 0<=k<n
+!         <case2>
+!             X(k) = sum_j=0^n-1 x(j)*exp(-2*pi*i*j*k/n), 0<=k<n
+!         (notes: sum_j=0^n-1 is a summation from j=0 to n-1)
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call cdft(2*n, 1, a, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call cdft(2*n, -1, a, ip, w)
+!     [parameters]
+!         2*n          :data length (integer)
+!                       n >= 1, n = power of 2
+!         a(0:2*n-1)   :input/output data (real*8)
+!                       input data
+!                           a(2*j) = Re(x(j)), 
+!                           a(2*j+1) = Im(x(j)), 0<=j<n
+!                       output data
+!                           a(2*k) = Re(X(k)), 
+!                           a(2*k+1) = Im(X(k)), 0<=k<n
+!         ip(0:*)      :work area for bit reversal (integer)
+!                       length of ip >= 2+sqrt(n)
+!                       strictly, 
+!                       length of ip >= 
+!                           2+2**(int(log(n+0.5)/log(2.0))/2).
+!                       ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:n/2-1)   :cos/sin table (real*8)
+!                       w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call cdft(2*n, -1, a, ip, w)
+!         is 
+!             call cdft(2*n, 1, a, ip, w)
+!             do j = 0, 2 * n - 1
+!                 a(j) = a(j) / n
+!             end do
+!         .
+!
+!
+! -------- Real DFT / Inverse of Real DFT --------
+!     [definition]
+!         <case1> RDFT
+!             R(k) = sum_j=0^n-1 a(j)*cos(2*pi*j*k/n), 0<=k<=n/2
+!             I(k) = sum_j=0^n-1 a(j)*sin(2*pi*j*k/n), 0<k<n/2
+!         <case2> IRDFT (excluding scale)
+!             a(k) = (R(0) + R(n/2)*cos(pi*k))/2 + 
+!                    sum_j=1^n/2-1 R(j)*cos(2*pi*j*k/n) + 
+!                    sum_j=1^n/2-1 I(j)*sin(2*pi*j*k/n), 0<=k<n
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call rdft(n, 1, a, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call rdft(n, -1, a, ip, w)
+!     [parameters]
+!         n            :data length (integer)
+!                       n >= 2, n = power of 2
+!         a(0:n-1)     :input/output data (real*8)
+!                       <case1>
+!                           output data
+!                               a(2*k) = R(k), 0<=k<n/2
+!                               a(2*k+1) = I(k), 0<k<n/2
+!                               a(1) = R(n/2)
+!                       <case2>
+!                           input data
+!                               a(2*j) = R(j), 0<=j<n/2
+!                               a(2*j+1) = I(j), 0<j<n/2
+!                               a(1) = R(n/2)
+!         ip(0:*)      :work area for bit reversal (integer)
+!                       length of ip >= 2+sqrt(n/2)
+!                       strictly, 
+!                       length of ip >= 
+!                           2+2**(int(log(n/2+0.5)/log(2.0))/2).
+!                       ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:n/2-1)   :cos/sin table (real*8)
+!                       w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call rdft(n, 1, a, ip, w)
+!         is 
+!             call rdft(n, -1, a, ip, w)
+!             do j = 0, n - 1
+!                 a(j) = a(j) * 2 / n
+!             end do
+!         .
+!
+!
+! -------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
+!     [definition]
+!         <case1> IDCT (excluding scale)
+!             C(k) = sum_j=0^n-1 a(j)*cos(pi*j*(k+1/2)/n), 0<=k<n
+!         <case2> DCT
+!             C(k) = sum_j=0^n-1 a(j)*cos(pi*(j+1/2)*k/n), 0<=k<n
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call ddct(n, 1, a, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call ddct(n, -1, a, ip, w)
+!     [parameters]
+!         n            :data length (integer)
+!                       n >= 2, n = power of 2
+!         a(0:n-1)     :input/output data (real*8)
+!                       output data
+!                           a(k) = C(k), 0<=k<n
+!         ip(0:*)      :work area for bit reversal (integer)
+!                       length of ip >= 2+sqrt(n/2)
+!                       strictly, 
+!                       length of ip >= 
+!                           2+2**(int(log(n/2+0.5)/log(2.0))/2).
+!                       ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:n*5/4-1) :cos/sin table (real*8)
+!                       w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call ddct(n, -1, a, ip, w)
+!         is 
+!             a(0) = a(0) / 2
+!             call ddct(n, 1, a, ip, w)
+!             do j = 0, n - 1
+!                 a(j) = a(j) * 2 / n
+!             end do
+!         .
+!
+!
+! -------- DST (Discrete Sine Transform) / Inverse of DST --------
+!     [definition]
+!         <case1> IDST (excluding scale)
+!             S(k) = sum_j=1^n A(j)*sin(pi*j*(k+1/2)/n), 0<=k<n
+!         <case2> DST
+!             S(k) = sum_j=0^n-1 a(j)*sin(pi*(j+1/2)*k/n), 0<k<=n
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call ddst(n, 1, a, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call ddst(n, -1, a, ip, w)
+!     [parameters]
+!         n            :data length (integer)
+!                       n >= 2, n = power of 2
+!         a(0:n-1)     :input/output data (real*8)
+!                       <case1>
+!                           input data
+!                               a(j) = A(j), 0<j<n
+!                               a(0) = A(n)
+!                           output data
+!                               a(k) = S(k), 0<=k<n
+!                       <case2>
+!                           output data
+!                               a(k) = S(k), 0<k<n
+!                               a(0) = S(n)
+!         ip(0:*)      :work area for bit reversal (integer)
+!                       length of ip >= 2+sqrt(n/2)
+!                       strictly, 
+!                       length of ip >= 
+!                           2+2**(int(log(n/2+0.5)/log(2.0))/2).
+!                       ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:n*5/4-1) :cos/sin table (real*8)
+!                       w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call ddst(n, -1, a, ip, w)
+!         is 
+!             a(0) = a(0) / 2
+!             call ddst(n, 1, a, ip, w)
+!             do j = 0, n - 1
+!                 a(j) = a(j) * 2 / n
+!             end do
+!         .
+!
+!
+! -------- Cosine Transform of RDFT (Real Symmetric DFT) --------
+!     [definition]
+!         C(k) = sum_j=0^n a(j)*cos(pi*j*k/n), 0<=k<=n
+!     [usage]
+!         ip(0) = 0  ! first time only
+!         call dfct(n, a, t, ip, w)
+!     [parameters]
+!         n            :data length - 1 (integer)
+!                       n >= 2, n = power of 2
+!         a(0:n)       :input/output data (real*8)
+!                       output data
+!                           a(k) = C(k), 0<=k<=n
+!         t(0:n/2)     :work area (real*8)
+!         ip(0:*)      :work area for bit reversal (integer)
+!                       length of ip >= 2+sqrt(n/4)
+!                       strictly, 
+!                       length of ip >= 
+!                           2+2**(int(log(n/4+0.5)/log(2.0))/2).
+!                       ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:n*5/8-1) :cos/sin table (real*8)
+!                       w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             a(0) = a(0) / 2
+!             a(n) = a(n) / 2
+!             call dfct(n, a, t, ip, w)
+!         is 
+!             a(0) = a(0) / 2
+!             a(n) = a(n) / 2
+!             call dfct(n, a, t, ip, w)
+!             do j = 0, n
+!                 a(j) = a(j) * 2 / n
+!             end do
+!         .
+!
+!
+! -------- Sine Transform of RDFT (Real Anti-symmetric DFT) --------
+!     [definition]
+!         S(k) = sum_j=1^n-1 a(j)*sin(pi*j*k/n), 0<k<n
+!     [usage]
+!         ip(0) = 0  ! first time only
+!         call dfst(n, a, t, ip, w)
+!     [parameters]
+!         n            :data length + 1 (integer)
+!                       n >= 2, n = power of 2
+!         a(0:n-1)     :input/output data (real*8)
+!                       output data
+!                           a(k) = S(k), 0<k<n
+!                       (a(0) is used for work area)
+!         t(0:n/2-1)   :work area (real*8)
+!         ip(0:*)      :work area for bit reversal (integer)
+!                       length of ip >= 2+sqrt(n/4)
+!                       strictly, 
+!                       length of ip >= 
+!                           2+2**(int(log(n/4+0.5)/log(2.0))/2).
+!                       ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:n*5/8-1) :cos/sin table (real*8)
+!                       w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call dfst(n, a, t, ip, w)
+!         is 
+!             call dfst(n, a, t, ip, w)
+!             do j = 1, n - 1
+!                 a(j) = a(j) * 2 / n
+!             end do
+!         .
+!
+!
+! Appendix :
+!     The cos/sin table is recalculated when the larger table required.
+!     w() and ip() are compatible with all routines.
+!
+!
+      subroutine cdft(n, isgn, a, ip, w)
+      integer n, isgn, ip(0 : *), nw
+      real*8 a(0 : n - 1), w(0 : *)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      if (isgn .ge. 0) then
+          call cftfsub(n, a, ip, nw, w)
+      else
+          call cftbsub(n, a, ip, nw, w)
+      end if
+      end
+!
+      subroutine rdft(n, isgn, a, ip, w)
+      integer n, isgn, ip(0 : *), nw, nc
+      real*8 a(0 : n - 1), w(0 : *), xi
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n .gt. 4 * nc) then
+          nc = n / 4
+          call makect(nc, ip, w(nw))
+      end if
+      if (isgn .ge. 0) then
+          if (n .gt. 4) then
+              call cftfsub(n, a, ip, nw, w)
+              call rftfsub(n, a, nc, w(nw))
+          else if (n .eq. 4) then
+              call cftfsub(n, a, ip, nw, w)
+          end if
+          xi = a(0) - a(1)
+          a(0) = a(0) + a(1)
+          a(1) = xi
+      else
+          a(1) = 0.5d0 * (a(0) - a(1))
+          a(0) = a(0) - a(1)
+          if (n .gt. 4) then
+              call rftbsub(n, a, nc, w(nw))
+              call cftbsub(n, a, ip, nw, w)
+          else if (n .eq. 4) then
+              call cftbsub(n, a, ip, nw, w)
+          end if
+      end if
+      end
+!
+      subroutine ddct(n, isgn, a, ip, w)
+      integer n, isgn, ip(0 : *), j, nw, nc
+      real*8 a(0 : n - 1), w(0 : *), xr
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n .gt. nc) then
+          nc = n
+          call makect(nc, ip, w(nw))
+      end if
+      if (isgn .lt. 0) then
+          xr = a(n - 1)
+          do j = n - 2, 2, -2
+              a(j + 1) = a(j) - a(j - 1)
+              a(j) = a(j) + a(j - 1)
+          end do
+          a(1) = a(0) - xr
+          a(0) = a(0) + xr
+          if (n .gt. 4) then
+              call rftbsub(n, a, nc, w(nw))
+              call cftbsub(n, a, ip, nw, w)
+          else if (n .eq. 4) then
+              call cftbsub(n, a, ip, nw, w)
+          end if
+      end if
+      call dctsub(n, a, nc, w(nw))
+      if (isgn .ge. 0) then
+          if (n .gt. 4) then
+              call cftfsub(n, a, ip, nw, w)
+              call rftfsub(n, a, nc, w(nw))
+          else if (n .eq. 4) then
+              call cftfsub(n, a, ip, nw, w)
+          end if
+          xr = a(0) - a(1)
+          a(0) = a(0) + a(1)
+          do j = 2, n - 2, 2
+              a(j - 1) = a(j) - a(j + 1)
+              a(j) = a(j) + a(j + 1)
+          end do
+          a(n - 1) = xr
+      end if
+      end
+!
+      subroutine ddst(n, isgn, a, ip, w)
+      integer n, isgn, ip(0 : *), j, nw, nc
+      real*8 a(0 : n - 1), w(0 : *), xr
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n .gt. nc) then
+          nc = n
+          call makect(nc, ip, w(nw))
+      end if
+      if (isgn .lt. 0) then
+          xr = a(n - 1)
+          do j = n - 2, 2, -2
+              a(j + 1) = -a(j) - a(j - 1)
+              a(j) = a(j) - a(j - 1)
+          end do
+          a(1) = a(0) + xr
+          a(0) = a(0) - xr
+          if (n .gt. 4) then
+              call rftbsub(n, a, nc, w(nw))
+              call cftbsub(n, a, ip, nw, w)
+          else if (n .eq. 4) then
+              call cftbsub(n, a, ip, nw, w)
+          end if
+      end if
+      call dstsub(n, a, nc, w(nw))
+      if (isgn .ge. 0) then
+          if (n .gt. 4) then
+              call cftfsub(n, a, ip, nw, w)
+              call rftfsub(n, a, nc, w(nw))
+          else if (n .eq. 4) then
+              call cftfsub(n, a, ip, nw, w)
+          end if
+          xr = a(0) - a(1)
+          a(0) = a(0) + a(1)
+          do j = 2, n - 2, 2
+              a(j - 1) = -a(j) - a(j + 1)
+              a(j) = a(j) - a(j + 1)
+          end do
+          a(n - 1) = -xr
+      end if
+      end
+!
+      subroutine dfct(n, a, t, ip, w)
+      integer n, ip(0 : *), j, k, l, m, mh, nw, nc
+      real*8 a(0 : n), t(0 : n / 2), w(0 : *), xr, xi, yr, yi
+      nw = ip(0)
+      if (n .gt. 8 * nw) then
+          nw = n / 8
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n .gt. 2 * nc) then
+          nc = n / 2
+          call makect(nc, ip, w(nw))
+      end if
+      m = n / 2
+      yi = a(m)
+      xi = a(0) + a(n)
+      a(0) = a(0) - a(n)
+      t(0) = xi - yi
+      t(m) = xi + yi
+      if (n .gt. 2) then
+          mh = m / 2
+          do j = 1, mh - 1
+              k = m - j
+              xr = a(j) - a(n - j)
+              xi = a(j) + a(n - j)
+              yr = a(k) - a(n - k)
+              yi = a(k) + a(n - k)
+              a(j) = xr
+              a(k) = yr
+              t(j) = xi - yi
+              t(k) = xi + yi
+          end do
+          t(mh) = a(mh) + a(n - mh)
+          a(mh) = a(mh) - a(n - mh)
+          call dctsub(m, a, nc, w(nw))
+          if (m .gt. 4) then
+              call cftfsub(m, a, ip, nw, w)
+              call rftfsub(m, a, nc, w(nw))
+          else if (m .eq. 4) then
+              call cftfsub(m, a, ip, nw, w)
+          end if
+          a(n - 1) = a(0) - a(1)
+          a(1) = a(0) + a(1)
+          do j = m - 2, 2, -2
+              a(2 * j + 1) = a(j) + a(j + 1)
+              a(2 * j - 1) = a(j) - a(j + 1)
+          end do
+          l = 2
+          m = mh
+          do while (m .ge. 2)
+              call dctsub(m, t, nc, w(nw))
+              if (m .gt. 4) then
+                  call cftfsub(m, t, ip, nw, w)
+                  call rftfsub(m, t, nc, w(nw))
+              else if (m .eq. 4) then
+                  call cftfsub(m, t, ip, nw, w)
+              end if
+              a(n - l) = t(0) - t(1)
+              a(l) = t(0) + t(1)
+              k = 0
+              do j = 2, m - 2, 2
+                  k = k + 4 * l
+                  a(k - l) = t(j) - t(j + 1)
+                  a(k + l) = t(j) + t(j + 1)
+              end do
+              l = 2 * l
+              mh = m / 2
+              do j = 0, mh - 1
+                  k = m - j
+                  t(j) = t(m + k) - t(m + j)
+                  t(k) = t(m + k) + t(m + j)
+              end do
+              t(mh) = t(m + mh)
+              m = mh
+          end do
+          a(l) = t(0)
+          a(n) = t(2) - t(1)
+          a(0) = t(2) + t(1)
+      else
+          a(1) = a(0)
+          a(2) = t(0)
+          a(0) = t(1)
+      end if
+      end
+!
+      subroutine dfst(n, a, t, ip, w)
+      integer n, ip(0 : *), j, k, l, m, mh, nw, nc
+      real*8 a(0 : n - 1), t(0 : n / 2 - 1), w(0 : *), xr, xi, yr, yi
+      nw = ip(0)
+      if (n .gt. 8 * nw) then
+          nw = n / 8
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n .gt. 2 * nc) then
+          nc = n / 2
+          call makect(nc, ip, w(nw))
+      end if
+      if (n .gt. 2) then
+          m = n / 2
+          mh = m / 2
+          do j = 1, mh - 1
+              k = m - j
+              xr = a(j) + a(n - j)
+              xi = a(j) - a(n - j)
+              yr = a(k) + a(n - k)
+              yi = a(k) - a(n - k)
+              a(j) = xr
+              a(k) = yr
+              t(j) = xi + yi
+              t(k) = xi - yi
+          end do
+          t(0) = a(mh) - a(n - mh)
+          a(mh) = a(mh) + a(n - mh)
+          a(0) = a(m)
+          call dstsub(m, a, nc, w(nw))
+          if (m .gt. 4) then
+              call cftfsub(m, a, ip, nw, w)
+              call rftfsub(m, a, nc, w(nw))
+          else if (m .eq. 4) then
+              call cftfsub(m, a, ip, nw, w)
+          end if
+          a(n - 1) = a(1) - a(0)
+          a(1) = a(0) + a(1)
+          do j = m - 2, 2, -2
+              a(2 * j + 1) = a(j) - a(j + 1)
+              a(2 * j - 1) = -a(j) - a(j + 1)
+          end do
+          l = 2
+          m = mh
+          do while (m .ge. 2)
+              call dstsub(m, t, nc, w(nw))
+              if (m .gt. 4) then
+                  call cftfsub(m, t, ip, nw, w)
+                  call rftfsub(m, t, nc, w(nw))
+              else if (m .eq. 4) then
+                  call cftfsub(m, t, ip, nw, w)
+              end if
+              a(n - l) = t(1) - t(0)
+              a(l) = t(0) + t(1)
+              k = 0
+              do j = 2, m - 2, 2
+                  k = k + 4 * l
+                  a(k - l) = -t(j) - t(j + 1)
+                  a(k + l) = t(j) - t(j + 1)
+              end do
+              l = 2 * l
+              mh = m / 2
+              do j = 1, mh - 1
+                  k = m - j
+                  t(j) = t(m + k) + t(m + j)
+                  t(k) = t(m + k) - t(m + j)
+              end do
+              t(0) = t(m + mh)
+              m = mh
+          end do
+          a(l) = t(0)
+      end if
+      a(0) = 0
+      end
+!
+! -------- initializing routines --------
+!
+      subroutine makewt(nw, ip, w)
+      integer nw, ip(0 : *), j, nwh, nw0, nw1
+      real*8 w(0 : nw - 1), delta, wn4r, wk1r, wk1i, wk3r, wk3i
+      ip(0) = nw
+      ip(1) = 1
+      if (nw .gt. 2) then
+          nwh = nw / 2
+          delta = atan(1.0d0) / nwh
+          wn4r = cos(delta * nwh)
+          w(0) = 1
+          w(1) = wn4r
+          if (nwh .eq. 4) then
+              w(2) = cos(delta * 2)
+              w(3) = sin(delta * 2)
+          else if (nwh .gt. 4) then
+              call makeipt(nw, ip)
+              w(2) = 0.5d0 / cos(delta * 2)
+              w(3) = 0.5d0 / cos(delta * 6)
+              do j = 4, nwh - 4, 4
+                  w(j) = cos(delta * j)
+                  w(j + 1) = sin(delta * j)
+                  w(j + 2) = cos(3 * delta * j)
+                  w(j + 3) = -sin(3 * delta * j)
+              end do
+          end if
+          nw0 = 0
+          do while (nwh .gt. 2)
+              nw1 = nw0 + nwh
+              nwh = nwh / 2
+              w(nw1) = 1
+              w(nw1 + 1) = wn4r
+              if (nwh .eq. 4) then
+                  wk1r = w(nw0 + 4)
+                  wk1i = w(nw0 + 5)
+                  w(nw1 + 2) = wk1r
+                  w(nw1 + 3) = wk1i
+              else if (nwh .gt. 4) then
+                  wk1r = w(nw0 + 4)
+                  wk3r = w(nw0 + 6)
+                  w(nw1 + 2) = 0.5d0 / wk1r
+                  w(nw1 + 3) = 0.5d0 / wk3r
+                  do j = 4, nwh - 4, 4
+                      wk1r = w(nw0 + 2 * j)
+                      wk1i = w(nw0 + 2 * j + 1)
+                      wk3r = w(nw0 + 2 * j + 2)
+                      wk3i = w(nw0 + 2 * j + 3)
+                      w(nw1 + j) = wk1r
+                      w(nw1 + j + 1) = wk1i
+                      w(nw1 + j + 2) = wk3r
+                      w(nw1 + j + 3) = wk3i
+                  end do
+              end if
+              nw0 = nw1
+          end do
+      end if
+      end
+!
+      subroutine makeipt(nw, ip)
+      integer nw, ip(0 : *), j, l, m, m2, p, q
+      ip(2) = 0
+      ip(3) = 16
+      m = 2
+      l = nw
+      do while (l .gt. 32)
+          m2 = 2 * m
+          q = 8 * m2
+          do j = m, m2 - 1
+              p = 4 * ip(j)
+              ip(m + j) = p
+              ip(m2 + j) = p + q
+          end do
+          m = m2
+          l = l / 4
+      end do
+      end
+!
+      subroutine makect(nc, ip, c)
+      integer nc, ip(0 : *), j, nch
+      real*8 c(0 : nc - 1), delta
+      ip(1) = nc
+      if (nc .gt. 1) then
+          nch = nc / 2
+          delta = atan(1.0d0) / nch
+          c(0) = cos(delta * nch)
+          c(nch) = 0.5d0 * c(0)
+          do j = 1, nch - 1
+              c(j) = 0.5d0 * cos(delta * j)
+              c(nc - j) = 0.5d0 * sin(delta * j)
+          end do
+      end if
+      end
+!
+! -------- child routines --------
+!
+      subroutine cftfsub(n, a, ip, nw, w)
+      integer n, ip(0 : *), nw
+      real*8 a(0 : n - 1), w(0 : nw - 1)
+      if (n .gt. 8) then
+          if (n .gt. 32) then
+              call cftf1st(n, a, w(nw - n / 4))
+              if (n .gt. 512) then
+                  call cftrec4(n, a, nw, w)
+              else if (n .gt. 128) then
+                  call cftleaf(n, 1, a, nw, w)
+              else
+                  call cftfx41(n, a, nw, w)
+              end if
+              call bitrv2(n, ip, a)
+          else if (n .eq. 32) then
+              call cftf161(a, w(nw - 8))
+              call bitrv216(a)
+          else
+              call cftf081(a, w)
+              call bitrv208(a)
+          end if
+      else if (n .eq. 8) then
+          call cftf040(a)
+      else if (n .eq. 4) then
+          call cftx020(a)
+      end if
+      end
+!
+      subroutine cftbsub(n, a, ip, nw, w)
+      integer n, ip(0 : *), nw
+      real*8 a(0 : n - 1), w(0 : nw - 1)
+      if (n .gt. 8) then
+          if (n .gt. 32) then
+              call cftb1st(n, a, w(nw - n / 4))
+              if (n .gt. 512) then
+                  call cftrec4(n, a, nw, w)
+              else if (n .gt. 128) then
+                  call cftleaf(n, 1, a, nw, w)
+              else
+                  call cftfx41(n, a, nw, w)
+              end if
+              call bitrv2conj(n, ip, a)
+          else if (n .eq. 32) then
+              call cftf161(a, w(nw - 8))
+              call bitrv216neg(a)
+          else
+              call cftf081(a, w)
+              call bitrv208neg(a)
+          end if
+      else if (n .eq. 8) then
+          call cftb040(a)
+      else if (n .eq. 4) then
+          call cftx020(a)
+      end if
+      end
+!
+      subroutine bitrv2(n, ip, a)
+      integer n, ip(0 : *), j, j1, k, k1, l, m, nh, nm
+      real*8 a(0 : n - 1), xr, xi, yr, yi
+      m = 1
+      l = n / 4
+      do while (l .gt. 8)
+          m = m * 2
+          l = l / 4
+      end do
+      nh = n / 2
+      nm = 4 * m
+      if (l .eq. 8) then
+          do k = 0, m - 1
+              do j = 0, k - 1
+                  j1 = 4 * j + 2 * ip(m + k)
+                  k1 = 4 * k + 2 * ip(m + j)
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + 2 * nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 - nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + 2 * nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nh
+                  k1 = k1 + 2
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - 2 * nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 + nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - 2 * nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + 2
+                  k1 = k1 + nh
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + 2 * nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 - nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + 2 * nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nh
+                  k1 = k1 - 2
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - 2 * nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 + nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - 2 * nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+              end do
+              k1 = 4 * k + 2 * ip(m + k)
+              j1 = k1 + 2
+              k1 = k1 + nh
+              xr = a(j1)
+              xi = a(j1 + 1)
+              yr = a(k1)
+              yi = a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 + nm
+              k1 = k1 + 2 * nm
+              xr = a(j1)
+              xi = a(j1 + 1)
+              yr = a(k1)
+              yi = a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 + nm
+              k1 = k1 - nm
+              xr = a(j1)
+              xi = a(j1 + 1)
+              yr = a(k1)
+              yi = a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 - 2
+              k1 = k1 - nh
+              xr = a(j1)
+              xi = a(j1 + 1)
+              yr = a(k1)
+              yi = a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 + nh + 2
+              k1 = k1 + nh + 2
+              xr = a(j1)
+              xi = a(j1 + 1)
+              yr = a(k1)
+              yi = a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 - nh + nm
+              k1 = k1 + 2 * nm - 2
+              xr = a(j1)
+              xi = a(j1 + 1)
+              yr = a(k1)
+              yi = a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+          end do
+      else
+          do k = 0, m - 1
+              do j = 0, k - 1
+                  j1 = 4 * j + ip(m + k)
+                  k1 = 4 * k + ip(m + j)
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nh
+                  k1 = k1 + 2
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + 2
+                  k1 = k1 + nh
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nh
+                  k1 = k1 - 2
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - nm
+                  xr = a(j1)
+                  xi = a(j1 + 1)
+                  yr = a(k1)
+                  yi = a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+              end do
+              k1 = 4 * k + ip(m + k)
+              j1 = k1 + 2
+              k1 = k1 + nh
+              xr = a(j1)
+              xi = a(j1 + 1)
+              yr = a(k1)
+              yi = a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 + nm
+              k1 = k1 + nm
+              xr = a(j1)
+              xi = a(j1 + 1)
+              yr = a(k1)
+              yi = a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+          end do
+      end if
+      end
+!
+      subroutine bitrv2conj(n, ip, a)
+      integer n, ip(0 : *), j, j1, k, k1, l, m, nh, nm
+      real*8 a(0 : n - 1), xr, xi, yr, yi
+      m = 1
+      l = n / 4
+      do while (l .gt. 8)
+          m = m * 2
+          l = l / 4
+      end do
+      nh = n / 2
+      nm = 4 * m
+      if (l .eq. 8) then
+          do k = 0, m - 1
+              do j = 0, k - 1
+                  j1 = 4 * j + 2 * ip(m + k)
+                  k1 = 4 * k + 2 * ip(m + j)
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + 2 * nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 - nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + 2 * nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nh
+                  k1 = k1 + 2
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - 2 * nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 + nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - 2 * nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + 2
+                  k1 = k1 + nh
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + 2 * nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 - nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + 2 * nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nh
+                  k1 = k1 - 2
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - 2 * nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 + nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - 2 * nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+              end do
+              k1 = 4 * k + 2 * ip(m + k)
+              j1 = k1 + 2
+              k1 = k1 + nh
+              a(j1 - 1) = -a(j1 - 1)
+              xr = a(j1)
+              xi = -a(j1 + 1)
+              yr = a(k1)
+              yi = -a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              a(k1 + 3) = -a(k1 + 3)
+              j1 = j1 + nm
+              k1 = k1 + 2 * nm
+              xr = a(j1)
+              xi = -a(j1 + 1)
+              yr = a(k1)
+              yi = -a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 + nm
+              k1 = k1 - nm
+              xr = a(j1)
+              xi = -a(j1 + 1)
+              yr = a(k1)
+              yi = -a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 - 2
+              k1 = k1 - nh
+              xr = a(j1)
+              xi = -a(j1 + 1)
+              yr = a(k1)
+              yi = -a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 + nh + 2
+              k1 = k1 + nh + 2
+              xr = a(j1)
+              xi = -a(j1 + 1)
+              yr = a(k1)
+              yi = -a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              j1 = j1 - nh + nm
+              k1 = k1 + 2 * nm - 2
+              a(j1 - 1) = -a(j1 - 1)
+              xr = a(j1)
+              xi = -a(j1 + 1)
+              yr = a(k1)
+              yi = -a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              a(k1 + 3) = -a(k1 + 3)
+          end do
+      else
+          do k = 0, m - 1
+              do j = 0, k - 1
+                  j1 = 4 * j + ip(m + k)
+                  k1 = 4 * k + ip(m + j)
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nh
+                  k1 = k1 + 2
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + 2
+                  k1 = k1 + nh
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 + nm
+                  k1 = k1 + nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nh
+                  k1 = k1 - 2
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+                  j1 = j1 - nm
+                  k1 = k1 - nm
+                  xr = a(j1)
+                  xi = -a(j1 + 1)
+                  yr = a(k1)
+                  yi = -a(k1 + 1)
+                  a(j1) = yr
+                  a(j1 + 1) = yi
+                  a(k1) = xr
+                  a(k1 + 1) = xi
+              end do
+              k1 = 4 * k + ip(m + k)
+              j1 = k1 + 2
+              k1 = k1 + nh
+              a(j1 - 1) = -a(j1 - 1)
+              xr = a(j1)
+              xi = -a(j1 + 1)
+              yr = a(k1)
+              yi = -a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              a(k1 + 3) = -a(k1 + 3)
+              j1 = j1 + nm
+              k1 = k1 + nm
+              a(j1 - 1) = -a(j1 - 1)
+              xr = a(j1)
+              xi = -a(j1 + 1)
+              yr = a(k1)
+              yi = -a(k1 + 1)
+              a(j1) = yr
+              a(j1 + 1) = yi
+              a(k1) = xr
+              a(k1 + 1) = xi
+              a(k1 + 3) = -a(k1 + 3)
+          end do
+      end if
+      end
+!
+      subroutine bitrv216(a)
+      real*8 a(0 : 31), x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i
+      real*8 x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i
+      real*8 x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i
+      x1r = a(2)
+      x1i = a(3)
+      x2r = a(4)
+      x2i = a(5)
+      x3r = a(6)
+      x3i = a(7)
+      x4r = a(8)
+      x4i = a(9)
+      x5r = a(10)
+      x5i = a(11)
+      x7r = a(14)
+      x7i = a(15)
+      x8r = a(16)
+      x8i = a(17)
+      x10r = a(20)
+      x10i = a(21)
+      x11r = a(22)
+      x11i = a(23)
+      x12r = a(24)
+      x12i = a(25)
+      x13r = a(26)
+      x13i = a(27)
+      x14r = a(28)
+      x14i = a(29)
+      a(2) = x8r
+      a(3) = x8i
+      a(4) = x4r
+      a(5) = x4i
+      a(6) = x12r
+      a(7) = x12i
+      a(8) = x2r
+      a(9) = x2i
+      a(10) = x10r
+      a(11) = x10i
+      a(14) = x14r
+      a(15) = x14i
+      a(16) = x1r
+      a(17) = x1i
+      a(20) = x5r
+      a(21) = x5i
+      a(22) = x13r
+      a(23) = x13i
+      a(24) = x3r
+      a(25) = x3i
+      a(26) = x11r
+      a(27) = x11i
+      a(28) = x7r
+      a(29) = x7i
+      end
+!
+      subroutine bitrv216neg(a)
+      real*8 a(0 : 31), x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i
+      real*8 x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i
+      real*8 x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i
+      real*8 x13r, x13i, x14r, x14i, x15r, x15i
+      x1r = a(2)
+      x1i = a(3)
+      x2r = a(4)
+      x2i = a(5)
+      x3r = a(6)
+      x3i = a(7)
+      x4r = a(8)
+      x4i = a(9)
+      x5r = a(10)
+      x5i = a(11)
+      x6r = a(12)
+      x6i = a(13)
+      x7r = a(14)
+      x7i = a(15)
+      x8r = a(16)
+      x8i = a(17)
+      x9r = a(18)
+      x9i = a(19)
+      x10r = a(20)
+      x10i = a(21)
+      x11r = a(22)
+      x11i = a(23)
+      x12r = a(24)
+      x12i = a(25)
+      x13r = a(26)
+      x13i = a(27)
+      x14r = a(28)
+      x14i = a(29)
+      x15r = a(30)
+      x15i = a(31)
+      a(2) = x15r
+      a(3) = x15i
+      a(4) = x7r
+      a(5) = x7i
+      a(6) = x11r
+      a(7) = x11i
+      a(8) = x3r
+      a(9) = x3i
+      a(10) = x13r
+      a(11) = x13i
+      a(12) = x5r
+      a(13) = x5i
+      a(14) = x9r
+      a(15) = x9i
+      a(16) = x1r
+      a(17) = x1i
+      a(18) = x14r
+      a(19) = x14i
+      a(20) = x6r
+      a(21) = x6i
+      a(22) = x10r
+      a(23) = x10i
+      a(24) = x2r
+      a(25) = x2i
+      a(26) = x12r
+      a(27) = x12i
+      a(28) = x4r
+      a(29) = x4i
+      a(30) = x8r
+      a(31) = x8i
+      end
+!
+      subroutine bitrv208(a)
+      real*8 a(0 : 15), x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i
+      x1r = a(2)
+      x1i = a(3)
+      x3r = a(6)
+      x3i = a(7)
+      x4r = a(8)
+      x4i = a(9)
+      x6r = a(12)
+      x6i = a(13)
+      a(2) = x4r
+      a(3) = x4i
+      a(6) = x6r
+      a(7) = x6i
+      a(8) = x1r
+      a(9) = x1i
+      a(12) = x3r
+      a(13) = x3i
+      end
+!
+      subroutine bitrv208neg(a)
+      real*8 a(0 : 15), x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i
+      real*8 x5r, x5i, x6r, x6i, x7r, x7i
+      x1r = a(2)
+      x1i = a(3)
+      x2r = a(4)
+      x2i = a(5)
+      x3r = a(6)
+      x3i = a(7)
+      x4r = a(8)
+      x4i = a(9)
+      x5r = a(10)
+      x5i = a(11)
+      x6r = a(12)
+      x6i = a(13)
+      x7r = a(14)
+      x7i = a(15)
+      a(2) = x7r
+      a(3) = x7i
+      a(4) = x3r
+      a(5) = x3i
+      a(6) = x5r
+      a(7) = x5i
+      a(8) = x1r
+      a(9) = x1i
+      a(10) = x6r
+      a(11) = x6i
+      a(12) = x2r
+      a(13) = x2i
+      a(14) = x4r
+      a(15) = x4i
+      end
+!
+      subroutine cftf1st(n, a, w)
+      integer n, j, j0, j1, j2, j3, k, m, mh
+      real*8 a(0 : n - 1), w(0 : *)
+      real*8 wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i
+      real*8 wd1r, wd1i, wd3r, wd3i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      real*8 y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i
+      mh = n / 8
+      m = 2 * mh
+      j1 = m
+      j2 = j1 + m
+      j3 = j2 + m
+      x0r = a(0) + a(j2)
+      x0i = a(1) + a(j2 + 1)
+      x1r = a(0) - a(j2)
+      x1i = a(1) - a(j2 + 1)
+      x2r = a(j1) + a(j3)
+      x2i = a(j1 + 1) + a(j3 + 1)
+      x3r = a(j1) - a(j3)
+      x3i = a(j1 + 1) - a(j3 + 1)
+      a(0) = x0r + x2r
+      a(1) = x0i + x2i
+      a(j1) = x0r - x2r
+      a(j1 + 1) = x0i - x2i
+      a(j2) = x1r - x3i
+      a(j2 + 1) = x1i + x3r
+      a(j3) = x1r + x3i
+      a(j3 + 1) = x1i - x3r
+      wn4r = w(1)
+      csc1 = w(2)
+      csc3 = w(3)
+      wd1r = 1
+      wd1i = 0
+      wd3r = 1
+      wd3i = 0
+      k = 0
+      do j = 2, mh - 6, 4
+          k = k + 4
+          wk1r = csc1 * (wd1r + w(k))
+          wk1i = csc1 * (wd1i + w(k + 1))
+          wk3r = csc3 * (wd3r + w(k + 2))
+          wk3i = csc3 * (wd3i + w(k + 3))
+          wd1r = w(k)
+          wd1i = w(k + 1)
+          wd3r = w(k + 2)
+          wd3i = w(k + 3)
+          j1 = j + m
+          j2 = j1 + m
+          j3 = j2 + m
+          x0r = a(j) + a(j2)
+          x0i = a(j + 1) + a(j2 + 1)
+          x1r = a(j) - a(j2)
+          x1i = a(j + 1) - a(j2 + 1)
+          y0r = a(j + 2) + a(j2 + 2)
+          y0i = a(j + 3) + a(j2 + 3)
+          y1r = a(j + 2) - a(j2 + 2)
+          y1i = a(j + 3) - a(j2 + 3)
+          x2r = a(j1) + a(j3)
+          x2i = a(j1 + 1) + a(j3 + 1)
+          x3r = a(j1) - a(j3)
+          x3i = a(j1 + 1) - a(j3 + 1)
+          y2r = a(j1 + 2) + a(j3 + 2)
+          y2i = a(j1 + 3) + a(j3 + 3)
+          y3r = a(j1 + 2) - a(j3 + 2)
+          y3i = a(j1 + 3) - a(j3 + 3)
+          a(j) = x0r + x2r
+          a(j + 1) = x0i + x2i
+          a(j + 2) = y0r + y2r
+          a(j + 3) = y0i + y2i
+          a(j1) = x0r - x2r
+          a(j1 + 1) = x0i - x2i
+          a(j1 + 2) = y0r - y2r
+          a(j1 + 3) = y0i - y2i
+          x0r = x1r - x3i
+          x0i = x1i + x3r
+          a(j2) = wk1r * x0r - wk1i * x0i
+          a(j2 + 1) = wk1r * x0i + wk1i * x0r
+          x0r = y1r - y3i
+          x0i = y1i + y3r
+          a(j2 + 2) = wd1r * x0r - wd1i * x0i
+          a(j2 + 3) = wd1r * x0i + wd1i * x0r
+          x0r = x1r + x3i
+          x0i = x1i - x3r
+          a(j3) = wk3r * x0r + wk3i * x0i
+          a(j3 + 1) = wk3r * x0i - wk3i * x0r
+          x0r = y1r + y3i
+          x0i = y1i - y3r
+          a(j3 + 2) = wd3r * x0r + wd3i * x0i
+          a(j3 + 3) = wd3r * x0i - wd3i * x0r
+          j0 = m - j
+          j1 = j0 + m
+          j2 = j1 + m
+          j3 = j2 + m
+          x0r = a(j0) + a(j2)
+          x0i = a(j0 + 1) + a(j2 + 1)
+          x1r = a(j0) - a(j2)
+          x1i = a(j0 + 1) - a(j2 + 1)
+          y0r = a(j0 - 2) + a(j2 - 2)
+          y0i = a(j0 - 1) + a(j2 - 1)
+          y1r = a(j0 - 2) - a(j2 - 2)
+          y1i = a(j0 - 1) - a(j2 - 1)
+          x2r = a(j1) + a(j3)
+          x2i = a(j1 + 1) + a(j3 + 1)
+          x3r = a(j1) - a(j3)
+          x3i = a(j1 + 1) - a(j3 + 1)
+          y2r = a(j1 - 2) + a(j3 - 2)
+          y2i = a(j1 - 1) + a(j3 - 1)
+          y3r = a(j1 - 2) - a(j3 - 2)
+          y3i = a(j1 - 1) - a(j3 - 1)
+          a(j0) = x0r + x2r
+          a(j0 + 1) = x0i + x2i
+          a(j0 - 2) = y0r + y2r
+          a(j0 - 1) = y0i + y2i
+          a(j1) = x0r - x2r
+          a(j1 + 1) = x0i - x2i
+          a(j1 - 2) = y0r - y2r
+          a(j1 - 1) = y0i - y2i
+          x0r = x1r - x3i
+          x0i = x1i + x3r
+          a(j2) = wk1i * x0r - wk1r * x0i
+          a(j2 + 1) = wk1i * x0i + wk1r * x0r
+          x0r = y1r - y3i
+          x0i = y1i + y3r
+          a(j2 - 2) = wd1i * x0r - wd1r * x0i
+          a(j2 - 1) = wd1i * x0i + wd1r * x0r
+          x0r = x1r + x3i
+          x0i = x1i - x3r
+          a(j3) = wk3i * x0r + wk3r * x0i
+          a(j3 + 1) = wk3i * x0i - wk3r * x0r
+          x0r = y1r + y3i
+          x0i = y1i - y3r
+          a(j3 - 2) = wd3i * x0r + wd3r * x0i
+          a(j3 - 1) = wd3i * x0i - wd3r * x0r
+      end do
+      wk1r = csc1 * (wd1r + wn4r)
+      wk1i = csc1 * (wd1i + wn4r)
+      wk3r = csc3 * (wd3r - wn4r)
+      wk3i = csc3 * (wd3i - wn4r)
+      j0 = mh
+      j1 = j0 + m
+      j2 = j1 + m
+      j3 = j2 + m
+      x0r = a(j0 - 2) + a(j2 - 2)
+      x0i = a(j0 - 1) + a(j2 - 1)
+      x1r = a(j0 - 2) - a(j2 - 2)
+      x1i = a(j0 - 1) - a(j2 - 1)
+      x2r = a(j1 - 2) + a(j3 - 2)
+      x2i = a(j1 - 1) + a(j3 - 1)
+      x3r = a(j1 - 2) - a(j3 - 2)
+      x3i = a(j1 - 1) - a(j3 - 1)
+      a(j0 - 2) = x0r + x2r
+      a(j0 - 1) = x0i + x2i
+      a(j1 - 2) = x0r - x2r
+      a(j1 - 1) = x0i - x2i
+      x0r = x1r - x3i
+      x0i = x1i + x3r
+      a(j2 - 2) = wk1r * x0r - wk1i * x0i
+      a(j2 - 1) = wk1r * x0i + wk1i * x0r
+      x0r = x1r + x3i
+      x0i = x1i - x3r
+      a(j3 - 2) = wk3r * x0r + wk3i * x0i
+      a(j3 - 1) = wk3r * x0i - wk3i * x0r
+      x0r = a(j0) + a(j2)
+      x0i = a(j0 + 1) + a(j2 + 1)
+      x1r = a(j0) - a(j2)
+      x1i = a(j0 + 1) - a(j2 + 1)
+      x2r = a(j1) + a(j3)
+      x2i = a(j1 + 1) + a(j3 + 1)
+      x3r = a(j1) - a(j3)
+      x3i = a(j1 + 1) - a(j3 + 1)
+      a(j0) = x0r + x2r
+      a(j0 + 1) = x0i + x2i
+      a(j1) = x0r - x2r
+      a(j1 + 1) = x0i - x2i
+      x0r = x1r - x3i
+      x0i = x1i + x3r
+      a(j2) = wn4r * (x0r - x0i)
+      a(j2 + 1) = wn4r * (x0i + x0r)
+      x0r = x1r + x3i
+      x0i = x1i - x3r
+      a(j3) = -wn4r * (x0r + x0i)
+      a(j3 + 1) = -wn4r * (x0i - x0r)
+      x0r = a(j0 + 2) + a(j2 + 2)
+      x0i = a(j0 + 3) + a(j2 + 3)
+      x1r = a(j0 + 2) - a(j2 + 2)
+      x1i = a(j0 + 3) - a(j2 + 3)
+      x2r = a(j1 + 2) + a(j3 + 2)
+      x2i = a(j1 + 3) + a(j3 + 3)
+      x3r = a(j1 + 2) - a(j3 + 2)
+      x3i = a(j1 + 3) - a(j3 + 3)
+      a(j0 + 2) = x0r + x2r
+      a(j0 + 3) = x0i + x2i
+      a(j1 + 2) = x0r - x2r
+      a(j1 + 3) = x0i - x2i
+      x0r = x1r - x3i
+      x0i = x1i + x3r
+      a(j2 + 2) = wk1i * x0r - wk1r * x0i
+      a(j2 + 3) = wk1i * x0i + wk1r * x0r
+      x0r = x1r + x3i
+      x0i = x1i - x3r
+      a(j3 + 2) = wk3i * x0r + wk3r * x0i
+      a(j3 + 3) = wk3i * x0i - wk3r * x0r
+      end
+!
+      subroutine cftb1st(n, a, w)
+      integer n, j, j0, j1, j2, j3, k, m, mh
+      real*8 a(0 : n - 1), w(0 : *)
+      real*8 wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i
+      real*8 wd1r, wd1i, wd3r, wd3i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      real*8 y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i
+      mh = n / 8
+      m = 2 * mh
+      j1 = m
+      j2 = j1 + m
+      j3 = j2 + m
+      x0r = a(0) + a(j2)
+      x0i = -a(1) - a(j2 + 1)
+      x1r = a(0) - a(j2)
+      x1i = -a(1) + a(j2 + 1)
+      x2r = a(j1) + a(j3)
+      x2i = a(j1 + 1) + a(j3 + 1)
+      x3r = a(j1) - a(j3)
+      x3i = a(j1 + 1) - a(j3 + 1)
+      a(0) = x0r + x2r
+      a(1) = x0i - x2i
+      a(j1) = x0r - x2r
+      a(j1 + 1) = x0i + x2i
+      a(j2) = x1r + x3i
+      a(j2 + 1) = x1i + x3r
+      a(j3) = x1r - x3i
+      a(j3 + 1) = x1i - x3r
+      wn4r = w(1)
+      csc1 = w(2)
+      csc3 = w(3)
+      wd1r = 1
+      wd1i = 0
+      wd3r = 1
+      wd3i = 0
+      k = 0
+      do j = 2, mh - 6, 4
+          k = k + 4
+          wk1r = csc1 * (wd1r + w(k))
+          wk1i = csc1 * (wd1i + w(k + 1))
+          wk3r = csc3 * (wd3r + w(k + 2))
+          wk3i = csc3 * (wd3i + w(k + 3))
+          wd1r = w(k)
+          wd1i = w(k + 1)
+          wd3r = w(k + 2)
+          wd3i = w(k + 3)
+          j1 = j + m
+          j2 = j1 + m
+          j3 = j2 + m
+          x0r = a(j) + a(j2)
+          x0i = -a(j + 1) - a(j2 + 1)
+          x1r = a(j) - a(j2)
+          x1i = -a(j + 1) + a(j2 + 1)
+          y0r = a(j + 2) + a(j2 + 2)
+          y0i = -a(j + 3) - a(j2 + 3)
+          y1r = a(j + 2) - a(j2 + 2)
+          y1i = -a(j + 3) + a(j2 + 3)
+          x2r = a(j1) + a(j3)
+          x2i = a(j1 + 1) + a(j3 + 1)
+          x3r = a(j1) - a(j3)
+          x3i = a(j1 + 1) - a(j3 + 1)
+          y2r = a(j1 + 2) + a(j3 + 2)
+          y2i = a(j1 + 3) + a(j3 + 3)
+          y3r = a(j1 + 2) - a(j3 + 2)
+          y3i = a(j1 + 3) - a(j3 + 3)
+          a(j) = x0r + x2r
+          a(j + 1) = x0i - x2i
+          a(j + 2) = y0r + y2r
+          a(j + 3) = y0i - y2i
+          a(j1) = x0r - x2r
+          a(j1 + 1) = x0i + x2i
+          a(j1 + 2) = y0r - y2r
+          a(j1 + 3) = y0i + y2i
+          x0r = x1r + x3i
+          x0i = x1i + x3r
+          a(j2) = wk1r * x0r - wk1i * x0i
+          a(j2 + 1) = wk1r * x0i + wk1i * x0r
+          x0r = y1r + y3i
+          x0i = y1i + y3r
+          a(j2 + 2) = wd1r * x0r - wd1i * x0i
+          a(j2 + 3) = wd1r * x0i + wd1i * x0r
+          x0r = x1r - x3i
+          x0i = x1i - x3r
+          a(j3) = wk3r * x0r + wk3i * x0i
+          a(j3 + 1) = wk3r * x0i - wk3i * x0r
+          x0r = y1r - y3i
+          x0i = y1i - y3r
+          a(j3 + 2) = wd3r * x0r + wd3i * x0i
+          a(j3 + 3) = wd3r * x0i - wd3i * x0r
+          j0 = m - j
+          j1 = j0 + m
+          j2 = j1 + m
+          j3 = j2 + m
+          x0r = a(j0) + a(j2)
+          x0i = -a(j0 + 1) - a(j2 + 1)
+          x1r = a(j0) - a(j2)
+          x1i = -a(j0 + 1) + a(j2 + 1)
+          y0r = a(j0 - 2) + a(j2 - 2)
+          y0i = -a(j0 - 1) - a(j2 - 1)
+          y1r = a(j0 - 2) - a(j2 - 2)
+          y1i = -a(j0 - 1) + a(j2 - 1)
+          x2r = a(j1) + a(j3)
+          x2i = a(j1 + 1) + a(j3 + 1)
+          x3r = a(j1) - a(j3)
+          x3i = a(j1 + 1) - a(j3 + 1)
+          y2r = a(j1 - 2) + a(j3 - 2)
+          y2i = a(j1 - 1) + a(j3 - 1)
+          y3r = a(j1 - 2) - a(j3 - 2)
+          y3i = a(j1 - 1) - a(j3 - 1)
+          a(j0) = x0r + x2r
+          a(j0 + 1) = x0i - x2i
+          a(j0 - 2) = y0r + y2r
+          a(j0 - 1) = y0i - y2i
+          a(j1) = x0r - x2r
+          a(j1 + 1) = x0i + x2i
+          a(j1 - 2) = y0r - y2r
+          a(j1 - 1) = y0i + y2i
+          x0r = x1r + x3i
+          x0i = x1i + x3r
+          a(j2) = wk1i * x0r - wk1r * x0i
+          a(j2 + 1) = wk1i * x0i + wk1r * x0r
+          x0r = y1r + y3i
+          x0i = y1i + y3r
+          a(j2 - 2) = wd1i * x0r - wd1r * x0i
+          a(j2 - 1) = wd1i * x0i + wd1r * x0r
+          x0r = x1r - x3i
+          x0i = x1i - x3r
+          a(j3) = wk3i * x0r + wk3r * x0i
+          a(j3 + 1) = wk3i * x0i - wk3r * x0r
+          x0r = y1r - y3i
+          x0i = y1i - y3r
+          a(j3 - 2) = wd3i * x0r + wd3r * x0i
+          a(j3 - 1) = wd3i * x0i - wd3r * x0r
+      end do
+      wk1r = csc1 * (wd1r + wn4r)
+      wk1i = csc1 * (wd1i + wn4r)
+      wk3r = csc3 * (wd3r - wn4r)
+      wk3i = csc3 * (wd3i - wn4r)
+      j0 = mh
+      j1 = j0 + m
+      j2 = j1 + m
+      j3 = j2 + m
+      x0r = a(j0 - 2) + a(j2 - 2)
+      x0i = -a(j0 - 1) - a(j2 - 1)
+      x1r = a(j0 - 2) - a(j2 - 2)
+      x1i = -a(j0 - 1) + a(j2 - 1)
+      x2r = a(j1 - 2) + a(j3 - 2)
+      x2i = a(j1 - 1) + a(j3 - 1)
+      x3r = a(j1 - 2) - a(j3 - 2)
+      x3i = a(j1 - 1) - a(j3 - 1)
+      a(j0 - 2) = x0r + x2r
+      a(j0 - 1) = x0i - x2i
+      a(j1 - 2) = x0r - x2r
+      a(j1 - 1) = x0i + x2i
+      x0r = x1r + x3i
+      x0i = x1i + x3r
+      a(j2 - 2) = wk1r * x0r - wk1i * x0i
+      a(j2 - 1) = wk1r * x0i + wk1i * x0r
+      x0r = x1r - x3i
+      x0i = x1i - x3r
+      a(j3 - 2) = wk3r * x0r + wk3i * x0i
+      a(j3 - 1) = wk3r * x0i - wk3i * x0r
+      x0r = a(j0) + a(j2)
+      x0i = -a(j0 + 1) - a(j2 + 1)
+      x1r = a(j0) - a(j2)
+      x1i = -a(j0 + 1) + a(j2 + 1)
+      x2r = a(j1) + a(j3)
+      x2i = a(j1 + 1) + a(j3 + 1)
+      x3r = a(j1) - a(j3)
+      x3i = a(j1 + 1) - a(j3 + 1)
+      a(j0) = x0r + x2r
+      a(j0 + 1) = x0i - x2i
+      a(j1) = x0r - x2r
+      a(j1 + 1) = x0i + x2i
+      x0r = x1r + x3i
+      x0i = x1i + x3r
+      a(j2) = wn4r * (x0r - x0i)
+      a(j2 + 1) = wn4r * (x0i + x0r)
+      x0r = x1r - x3i
+      x0i = x1i - x3r
+      a(j3) = -wn4r * (x0r + x0i)
+      a(j3 + 1) = -wn4r * (x0i - x0r)
+      x0r = a(j0 + 2) + a(j2 + 2)
+      x0i = -a(j0 + 3) - a(j2 + 3)
+      x1r = a(j0 + 2) - a(j2 + 2)
+      x1i = -a(j0 + 3) + a(j2 + 3)
+      x2r = a(j1 + 2) + a(j3 + 2)
+      x2i = a(j1 + 3) + a(j3 + 3)
+      x3r = a(j1 + 2) - a(j3 + 2)
+      x3i = a(j1 + 3) - a(j3 + 3)
+      a(j0 + 2) = x0r + x2r
+      a(j0 + 3) = x0i - x2i
+      a(j1 + 2) = x0r - x2r
+      a(j1 + 3) = x0i + x2i
+      x0r = x1r + x3i
+      x0i = x1i + x3r
+      a(j2 + 2) = wk1i * x0r - wk1r * x0i
+      a(j2 + 3) = wk1i * x0i + wk1r * x0r
+      x0r = x1r - x3i
+      x0i = x1i - x3r
+      a(j3 + 2) = wk3i * x0r + wk3r * x0i
+      a(j3 + 3) = wk3i * x0i - wk3r * x0r
+      end
+!
+      subroutine cftrec4(n, a, nw, w)
+      integer n, nw, cfttree, isplt, j, k, m
+      real*8 a(0 : n - 1), w(0 : nw - 1)
+      m = n
+      do while (m .gt. 512)
+          m = m / 4
+          call cftmdl1(m, a(n - m), w(nw - m / 2))
+      end do
+      call cftleaf(m, 1, a(n - m), nw, w)
+      k = 0
+      do j = n - m, m, -m
+          k = k + 1
+          isplt = cfttree(m, j, k, a, nw, w)
+          call cftleaf(m, isplt, a(j - m), nw, w)
+      end do
+      end
+!
+      integer function cfttree(n, j, k, a, nw, w)
+      integer n, j, k, nw, i, isplt, m
+      real*8 a(0 : j - 1), w(0 : nw - 1)
+      if (mod(k, 4) .ne. 0) then
+          isplt = mod(k, 2)
+          if (isplt .ne. 0) then
+              call cftmdl1(n, a(j - n), w(nw - n / 2))
+          else
+              call cftmdl2(n, a(j - n), w(nw - n))
+          end if
+      else
+          m = n
+          i = k
+          do while (mod(i, 4) .eq. 0)
+              m = m * 4
+              i = i / 4
+          end do
+          isplt = mod(i, 2)
+          if (isplt .ne. 0) then
+              do while (m .gt. 128)
+                  call cftmdl1(m, a(j - m), w(nw - m / 2))
+                  m = m / 4
+              end do
+          else
+              do while (m .gt. 128)
+                  call cftmdl2(m, a(j - m), w(nw - m))
+                  m = m / 4
+              end do
+          end if
+      end if
+      cfttree = isplt
+      end
+!
+      subroutine cftleaf(n, isplt, a, nw, w)
+      integer n, isplt, nw
+      real*8 a(0 : n - 1), w(0 : nw - 1)
+      if (n .eq. 512) then
+          call cftmdl1(128, a, w(nw - 64))
+          call cftf161(a, w(nw - 8))
+          call cftf162(a(32), w(nw - 32))
+          call cftf161(a(64), w(nw - 8))
+          call cftf161(a(96), w(nw - 8))
+          call cftmdl2(128, a(128), w(nw - 128))
+          call cftf161(a(128), w(nw - 8))
+          call cftf162(a(160), w(nw - 32))
+          call cftf161(a(192), w(nw - 8))
+          call cftf162(a(224), w(nw - 32))
+          call cftmdl1(128, a(256), w(nw - 64))
+          call cftf161(a(256), w(nw - 8))
+          call cftf162(a(288), w(nw - 32))
+          call cftf161(a(320), w(nw - 8))
+          call cftf161(a(352), w(nw - 8))
+          if (isplt .ne. 0) then
+              call cftmdl1(128, a(384), w(nw - 64))
+              call cftf161(a(480), w(nw - 8))
+          else
+              call cftmdl2(128, a(384), w(nw - 128))
+              call cftf162(a(480), w(nw - 32))
+          end if
+          call cftf161(a(384), w(nw - 8))
+          call cftf162(a(416), w(nw - 32))
+          call cftf161(a(448), w(nw - 8))
+      else
+          call cftmdl1(64, a, w(nw - 32))
+          call cftf081(a, w(nw - 8))
+          call cftf082(a(16), w(nw - 8))
+          call cftf081(a(32), w(nw - 8))
+          call cftf081(a(48), w(nw - 8))
+          call cftmdl2(64, a(64), w(nw - 64))
+          call cftf081(a(64), w(nw - 8))
+          call cftf082(a(80), w(nw - 8))
+          call cftf081(a(96), w(nw - 8))
+          call cftf082(a(112), w(nw - 8))
+          call cftmdl1(64, a(128), w(nw - 32))
+          call cftf081(a(128), w(nw - 8))
+          call cftf082(a(144), w(nw - 8))
+          call cftf081(a(160), w(nw - 8))
+          call cftf081(a(176), w(nw - 8))
+          if (isplt .ne. 0) then
+              call cftmdl1(64, a(192), w(nw - 32))
+              call cftf081(a(240), w(nw - 8))
+          else
+              call cftmdl2(64, a(192), w(nw - 64))
+              call cftf082(a(240), w(nw - 8))
+          end if
+          call cftf081(a(192), w(nw - 8))
+          call cftf082(a(208), w(nw - 8))
+          call cftf081(a(224), w(nw - 8))
+      end if
+      end
+!
+      subroutine cftmdl1(n, a, w)
+      integer n, j, j0, j1, j2, j3, k, m, mh
+      real*8 a(0 : n - 1), w(0 : *)
+      real*8 wn4r, wk1r, wk1i, wk3r, wk3i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      mh = n / 8
+      m = 2 * mh
+      j1 = m
+      j2 = j1 + m
+      j3 = j2 + m
+      x0r = a(0) + a(j2)
+      x0i = a(1) + a(j2 + 1)
+      x1r = a(0) - a(j2)
+      x1i = a(1) - a(j2 + 1)
+      x2r = a(j1) + a(j3)
+      x2i = a(j1 + 1) + a(j3 + 1)
+      x3r = a(j1) - a(j3)
+      x3i = a(j1 + 1) - a(j3 + 1)
+      a(0) = x0r + x2r
+      a(1) = x0i + x2i
+      a(j1) = x0r - x2r
+      a(j1 + 1) = x0i - x2i
+      a(j2) = x1r - x3i
+      a(j2 + 1) = x1i + x3r
+      a(j3) = x1r + x3i
+      a(j3 + 1) = x1i - x3r
+      wn4r = w(1)
+      k = 0
+      do j = 2, mh - 2, 2
+          k = k + 4
+          wk1r = w(k)
+          wk1i = w(k + 1)
+          wk3r = w(k + 2)
+          wk3i = w(k + 3)
+          j1 = j + m
+          j2 = j1 + m
+          j3 = j2 + m
+          x0r = a(j) + a(j2)
+          x0i = a(j + 1) + a(j2 + 1)
+          x1r = a(j) - a(j2)
+          x1i = a(j + 1) - a(j2 + 1)
+          x2r = a(j1) + a(j3)
+          x2i = a(j1 + 1) + a(j3 + 1)
+          x3r = a(j1) - a(j3)
+          x3i = a(j1 + 1) - a(j3 + 1)
+          a(j) = x0r + x2r
+          a(j + 1) = x0i + x2i
+          a(j1) = x0r - x2r
+          a(j1 + 1) = x0i - x2i
+          x0r = x1r - x3i
+          x0i = x1i + x3r
+          a(j2) = wk1r * x0r - wk1i * x0i
+          a(j2 + 1) = wk1r * x0i + wk1i * x0r
+          x0r = x1r + x3i
+          x0i = x1i - x3r
+          a(j3) = wk3r * x0r + wk3i * x0i
+          a(j3 + 1) = wk3r * x0i - wk3i * x0r
+          j0 = m - j
+          j1 = j0 + m
+          j2 = j1 + m
+          j3 = j2 + m
+          x0r = a(j0) + a(j2)
+          x0i = a(j0 + 1) + a(j2 + 1)
+          x1r = a(j0) - a(j2)
+          x1i = a(j0 + 1) - a(j2 + 1)
+          x2r = a(j1) + a(j3)
+          x2i = a(j1 + 1) + a(j3 + 1)
+          x3r = a(j1) - a(j3)
+          x3i = a(j1 + 1) - a(j3 + 1)
+          a(j0) = x0r + x2r
+          a(j0 + 1) = x0i + x2i
+          a(j1) = x0r - x2r
+          a(j1 + 1) = x0i - x2i
+          x0r = x1r - x3i
+          x0i = x1i + x3r
+          a(j2) = wk1i * x0r - wk1r * x0i
+          a(j2 + 1) = wk1i * x0i + wk1r * x0r
+          x0r = x1r + x3i
+          x0i = x1i - x3r
+          a(j3) = wk3i * x0r + wk3r * x0i
+          a(j3 + 1) = wk3i * x0i - wk3r * x0r
+      end do
+      j0 = mh
+      j1 = j0 + m
+      j2 = j1 + m
+      j3 = j2 + m
+      x0r = a(j0) + a(j2)
+      x0i = a(j0 + 1) + a(j2 + 1)
+      x1r = a(j0) - a(j2)
+      x1i = a(j0 + 1) - a(j2 + 1)
+      x2r = a(j1) + a(j3)
+      x2i = a(j1 + 1) + a(j3 + 1)
+      x3r = a(j1) - a(j3)
+      x3i = a(j1 + 1) - a(j3 + 1)
+      a(j0) = x0r + x2r
+      a(j0 + 1) = x0i + x2i
+      a(j1) = x0r - x2r
+      a(j1 + 1) = x0i - x2i
+      x0r = x1r - x3i
+      x0i = x1i + x3r
+      a(j2) = wn4r * (x0r - x0i)
+      a(j2 + 1) = wn4r * (x0i + x0r)
+      x0r = x1r + x3i
+      x0i = x1i - x3r
+      a(j3) = -wn4r * (x0r + x0i)
+      a(j3 + 1) = -wn4r * (x0i - x0r)
+      end
+!
+      subroutine cftmdl2(n, a, w)
+      integer n, j, j0, j1, j2, j3, k, kr, m, mh
+      real*8 a(0 : n - 1), w(0 : *)
+      real*8 wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      real*8 y0r, y0i, y2r, y2i
+      mh = n / 8
+      m = 2 * mh
+      wn4r = w(1)
+      j1 = m
+      j2 = j1 + m
+      j3 = j2 + m
+      x0r = a(0) - a(j2 + 1)
+      x0i = a(1) + a(j2)
+      x1r = a(0) + a(j2 + 1)
+      x1i = a(1) - a(j2)
+      x2r = a(j1) - a(j3 + 1)
+      x2i = a(j1 + 1) + a(j3)
+      x3r = a(j1) + a(j3 + 1)
+      x3i = a(j1 + 1) - a(j3)
+      y0r = wn4r * (x2r - x2i)
+      y0i = wn4r * (x2i + x2r)
+      a(0) = x0r + y0r
+      a(1) = x0i + y0i
+      a(j1) = x0r - y0r
+      a(j1 + 1) = x0i - y0i
+      y0r = wn4r * (x3r - x3i)
+      y0i = wn4r * (x3i + x3r)
+      a(j2) = x1r - y0i
+      a(j2 + 1) = x1i + y0r
+      a(j3) = x1r + y0i
+      a(j3 + 1) = x1i - y0r
+      k = 0
+      kr = 2 * m
+      do j = 2, mh - 2, 2
+          k = k + 4
+          wk1r = w(k)
+          wk1i = w(k + 1)
+          wk3r = w(k + 2)
+          wk3i = w(k + 3)
+          kr = kr - 4
+          wd1i = w(kr)
+          wd1r = w(kr + 1)
+          wd3i = w(kr + 2)
+          wd3r = w(kr + 3)
+          j1 = j + m
+          j2 = j1 + m
+          j3 = j2 + m
+          x0r = a(j) - a(j2 + 1)
+          x0i = a(j + 1) + a(j2)
+          x1r = a(j) + a(j2 + 1)
+          x1i = a(j + 1) - a(j2)
+          x2r = a(j1) - a(j3 + 1)
+          x2i = a(j1 + 1) + a(j3)
+          x3r = a(j1) + a(j3 + 1)
+          x3i = a(j1 + 1) - a(j3)
+          y0r = wk1r * x0r - wk1i * x0i
+          y0i = wk1r * x0i + wk1i * x0r
+          y2r = wd1r * x2r - wd1i * x2i
+          y2i = wd1r * x2i + wd1i * x2r
+          a(j) = y0r + y2r
+          a(j + 1) = y0i + y2i
+          a(j1) = y0r - y2r
+          a(j1 + 1) = y0i - y2i
+          y0r = wk3r * x1r + wk3i * x1i
+          y0i = wk3r * x1i - wk3i * x1r
+          y2r = wd3r * x3r + wd3i * x3i
+          y2i = wd3r * x3i - wd3i * x3r
+          a(j2) = y0r + y2r
+          a(j2 + 1) = y0i + y2i
+          a(j3) = y0r - y2r
+          a(j3 + 1) = y0i - y2i
+          j0 = m - j
+          j1 = j0 + m
+          j2 = j1 + m
+          j3 = j2 + m
+          x0r = a(j0) - a(j2 + 1)
+          x0i = a(j0 + 1) + a(j2)
+          x1r = a(j0) + a(j2 + 1)
+          x1i = a(j0 + 1) - a(j2)
+          x2r = a(j1) - a(j3 + 1)
+          x2i = a(j1 + 1) + a(j3)
+          x3r = a(j1) + a(j3 + 1)
+          x3i = a(j1 + 1) - a(j3)
+          y0r = wd1i * x0r - wd1r * x0i
+          y0i = wd1i * x0i + wd1r * x0r
+          y2r = wk1i * x2r - wk1r * x2i
+          y2i = wk1i * x2i + wk1r * x2r
+          a(j0) = y0r + y2r
+          a(j0 + 1) = y0i + y2i
+          a(j1) = y0r - y2r
+          a(j1 + 1) = y0i - y2i
+          y0r = wd3i * x1r + wd3r * x1i
+          y0i = wd3i * x1i - wd3r * x1r
+          y2r = wk3i * x3r + wk3r * x3i
+          y2i = wk3i * x3i - wk3r * x3r
+          a(j2) = y0r + y2r
+          a(j2 + 1) = y0i + y2i
+          a(j3) = y0r - y2r
+          a(j3 + 1) = y0i - y2i
+      end do
+      wk1r = w(m)
+      wk1i = w(m + 1)
+      j0 = mh
+      j1 = j0 + m
+      j2 = j1 + m
+      j3 = j2 + m
+      x0r = a(j0) - a(j2 + 1)
+      x0i = a(j0 + 1) + a(j2)
+      x1r = a(j0) + a(j2 + 1)
+      x1i = a(j0 + 1) - a(j2)
+      x2r = a(j1) - a(j3 + 1)
+      x2i = a(j1 + 1) + a(j3)
+      x3r = a(j1) + a(j3 + 1)
+      x3i = a(j1 + 1) - a(j3)
+      y0r = wk1r * x0r - wk1i * x0i
+      y0i = wk1r * x0i + wk1i * x0r
+      y2r = wk1i * x2r - wk1r * x2i
+      y2i = wk1i * x2i + wk1r * x2r
+      a(j0) = y0r + y2r
+      a(j0 + 1) = y0i + y2i
+      a(j1) = y0r - y2r
+      a(j1 + 1) = y0i - y2i
+      y0r = wk1i * x1r - wk1r * x1i
+      y0i = wk1i * x1i + wk1r * x1r
+      y2r = wk1r * x3r - wk1i * x3i
+      y2i = wk1r * x3i + wk1i * x3r
+      a(j2) = y0r - y2r
+      a(j2 + 1) = y0i - y2i
+      a(j3) = y0r + y2r
+      a(j3 + 1) = y0i + y2i
+      end
+!
+      subroutine cftfx41(n, a, nw, w)
+      integer n, nw
+      real*8 a(0 : n - 1), w(0 : nw - 1)
+      if (n .eq. 128) then
+          call cftf161(a, w(nw - 8))
+          call cftf162(a(32), w(nw - 32))
+          call cftf161(a(64), w(nw - 8))
+          call cftf161(a(96), w(nw - 8))
+      else
+          call cftf081(a, w(nw - 8))
+          call cftf082(a(16), w(nw - 8))
+          call cftf081(a(32), w(nw - 8))
+          call cftf081(a(48), w(nw - 8))
+      end if
+      end
+!
+      subroutine cftf161(a, w)
+      real*8 a(0 : 31), w(0 : *), wn4r, wk1r, wk1i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      real*8 y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i
+      real*8 y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i
+      real*8 y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i
+      real*8 y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i
+      wn4r = w(1)
+      wk1r = w(2)
+      wk1i = w(3)
+      x0r = a(0) + a(16)
+      x0i = a(1) + a(17)
+      x1r = a(0) - a(16)
+      x1i = a(1) - a(17)
+      x2r = a(8) + a(24)
+      x2i = a(9) + a(25)
+      x3r = a(8) - a(24)
+      x3i = a(9) - a(25)
+      y0r = x0r + x2r
+      y0i = x0i + x2i
+      y4r = x0r - x2r
+      y4i = x0i - x2i
+      y8r = x1r - x3i
+      y8i = x1i + x3r
+      y12r = x1r + x3i
+      y12i = x1i - x3r
+      x0r = a(2) + a(18)
+      x0i = a(3) + a(19)
+      x1r = a(2) - a(18)
+      x1i = a(3) - a(19)
+      x2r = a(10) + a(26)
+      x2i = a(11) + a(27)
+      x3r = a(10) - a(26)
+      x3i = a(11) - a(27)
+      y1r = x0r + x2r
+      y1i = x0i + x2i
+      y5r = x0r - x2r
+      y5i = x0i - x2i
+      x0r = x1r - x3i
+      x0i = x1i + x3r
+      y9r = wk1r * x0r - wk1i * x0i
+      y9i = wk1r * x0i + wk1i * x0r
+      x0r = x1r + x3i
+      x0i = x1i - x3r
+      y13r = wk1i * x0r - wk1r * x0i
+      y13i = wk1i * x0i + wk1r * x0r
+      x0r = a(4) + a(20)
+      x0i = a(5) + a(21)
+      x1r = a(4) - a(20)
+      x1i = a(5) - a(21)
+      x2r = a(12) + a(28)
+      x2i = a(13) + a(29)
+      x3r = a(12) - a(28)
+      x3i = a(13) - a(29)
+      y2r = x0r + x2r
+      y2i = x0i + x2i
+      y6r = x0r - x2r
+      y6i = x0i - x2i
+      x0r = x1r - x3i
+      x0i = x1i + x3r
+      y10r = wn4r * (x0r - x0i)
+      y10i = wn4r * (x0i + x0r)
+      x0r = x1r + x3i
+      x0i = x1i - x3r
+      y14r = wn4r * (x0r + x0i)
+      y14i = wn4r * (x0i - x0r)
+      x0r = a(6) + a(22)
+      x0i = a(7) + a(23)
+      x1r = a(6) - a(22)
+      x1i = a(7) - a(23)
+      x2r = a(14) + a(30)
+      x2i = a(15) + a(31)
+      x3r = a(14) - a(30)
+      x3i = a(15) - a(31)
+      y3r = x0r + x2r
+      y3i = x0i + x2i
+      y7r = x0r - x2r
+      y7i = x0i - x2i
+      x0r = x1r - x3i
+      x0i = x1i + x3r
+      y11r = wk1i * x0r - wk1r * x0i
+      y11i = wk1i * x0i + wk1r * x0r
+      x0r = x1r + x3i
+      x0i = x1i - x3r
+      y15r = wk1r * x0r - wk1i * x0i
+      y15i = wk1r * x0i + wk1i * x0r
+      x0r = y12r - y14r
+      x0i = y12i - y14i
+      x1r = y12r + y14r
+      x1i = y12i + y14i
+      x2r = y13r - y15r
+      x2i = y13i - y15i
+      x3r = y13r + y15r
+      x3i = y13i + y15i
+      a(24) = x0r + x2r
+      a(25) = x0i + x2i
+      a(26) = x0r - x2r
+      a(27) = x0i - x2i
+      a(28) = x1r - x3i
+      a(29) = x1i + x3r
+      a(30) = x1r + x3i
+      a(31) = x1i - x3r
+      x0r = y8r + y10r
+      x0i = y8i + y10i
+      x1r = y8r - y10r
+      x1i = y8i - y10i
+      x2r = y9r + y11r
+      x2i = y9i + y11i
+      x3r = y9r - y11r
+      x3i = y9i - y11i
+      a(16) = x0r + x2r
+      a(17) = x0i + x2i
+      a(18) = x0r - x2r
+      a(19) = x0i - x2i
+      a(20) = x1r - x3i
+      a(21) = x1i + x3r
+      a(22) = x1r + x3i
+      a(23) = x1i - x3r
+      x0r = y5r - y7i
+      x0i = y5i + y7r
+      x2r = wn4r * (x0r - x0i)
+      x2i = wn4r * (x0i + x0r)
+      x0r = y5r + y7i
+      x0i = y5i - y7r
+      x3r = wn4r * (x0r - x0i)
+      x3i = wn4r * (x0i + x0r)
+      x0r = y4r - y6i
+      x0i = y4i + y6r
+      x1r = y4r + y6i
+      x1i = y4i - y6r
+      a(8) = x0r + x2r
+      a(9) = x0i + x2i
+      a(10) = x0r - x2r
+      a(11) = x0i - x2i
+      a(12) = x1r - x3i
+      a(13) = x1i + x3r
+      a(14) = x1r + x3i
+      a(15) = x1i - x3r
+      x0r = y0r + y2r
+      x0i = y0i + y2i
+      x1r = y0r - y2r
+      x1i = y0i - y2i
+      x2r = y1r + y3r
+      x2i = y1i + y3i
+      x3r = y1r - y3r
+      x3i = y1i - y3i
+      a(0) = x0r + x2r
+      a(1) = x0i + x2i
+      a(2) = x0r - x2r
+      a(3) = x0i - x2i
+      a(4) = x1r - x3i
+      a(5) = x1i + x3r
+      a(6) = x1r + x3i
+      a(7) = x1i - x3r
+      end
+!
+      subroutine cftf162(a, w)
+      real*8 a(0 : 31), w(0 : *)
+      real*8 wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i
+      real*8 x0r, x0i, x1r, x1i, x2r, x2i
+      real*8 y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i
+      real*8 y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i
+      real*8 y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i
+      real*8 y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i
+      wn4r = w(1)
+      wk1r = w(4)
+      wk1i = w(5)
+      wk3r = w(6)
+      wk3i = -w(7)
+      wk2r = w(8)
+      wk2i = w(9)
+      x1r = a(0) - a(17)
+      x1i = a(1) + a(16)
+      x0r = a(8) - a(25)
+      x0i = a(9) + a(24)
+      x2r = wn4r * (x0r - x0i)
+      x2i = wn4r * (x0i + x0r)
+      y0r = x1r + x2r
+      y0i = x1i + x2i
+      y4r = x1r - x2r
+      y4i = x1i - x2i
+      x1r = a(0) + a(17)
+      x1i = a(1) - a(16)
+      x0r = a(8) + a(25)
+      x0i = a(9) - a(24)
+      x2r = wn4r * (x0r - x0i)
+      x2i = wn4r * (x0i + x0r)
+      y8r = x1r - x2i
+      y8i = x1i + x2r
+      y12r = x1r + x2i
+      y12i = x1i - x2r
+      x0r = a(2) - a(19)
+      x0i = a(3) + a(18)
+      x1r = wk1r * x0r - wk1i * x0i
+      x1i = wk1r * x0i + wk1i * x0r
+      x0r = a(10) - a(27)
+      x0i = a(11) + a(26)
+      x2r = wk3i * x0r - wk3r * x0i
+      x2i = wk3i * x0i + wk3r * x0r
+      y1r = x1r + x2r
+      y1i = x1i + x2i
+      y5r = x1r - x2r
+      y5i = x1i - x2i
+      x0r = a(2) + a(19)
+      x0i = a(3) - a(18)
+      x1r = wk3r * x0r - wk3i * x0i
+      x1i = wk3r * x0i + wk3i * x0r
+      x0r = a(10) + a(27)
+      x0i = a(11) - a(26)
+      x2r = wk1r * x0r + wk1i * x0i
+      x2i = wk1r * x0i - wk1i * x0r
+      y9r = x1r - x2r
+      y9i = x1i - x2i
+      y13r = x1r + x2r
+      y13i = x1i + x2i
+      x0r = a(4) - a(21)
+      x0i = a(5) + a(20)
+      x1r = wk2r * x0r - wk2i * x0i
+      x1i = wk2r * x0i + wk2i * x0r
+      x0r = a(12) - a(29)
+      x0i = a(13) + a(28)
+      x2r = wk2i * x0r - wk2r * x0i
+      x2i = wk2i * x0i + wk2r * x0r
+      y2r = x1r + x2r
+      y2i = x1i + x2i
+      y6r = x1r - x2r
+      y6i = x1i - x2i
+      x0r = a(4) + a(21)
+      x0i = a(5) - a(20)
+      x1r = wk2i * x0r - wk2r * x0i
+      x1i = wk2i * x0i + wk2r * x0r
+      x0r = a(12) + a(29)
+      x0i = a(13) - a(28)
+      x2r = wk2r * x0r - wk2i * x0i
+      x2i = wk2r * x0i + wk2i * x0r
+      y10r = x1r - x2r
+      y10i = x1i - x2i
+      y14r = x1r + x2r
+      y14i = x1i + x2i
+      x0r = a(6) - a(23)
+      x0i = a(7) + a(22)
+      x1r = wk3r * x0r - wk3i * x0i
+      x1i = wk3r * x0i + wk3i * x0r
+      x0r = a(14) - a(31)
+      x0i = a(15) + a(30)
+      x2r = wk1i * x0r - wk1r * x0i
+      x2i = wk1i * x0i + wk1r * x0r
+      y3r = x1r + x2r
+      y3i = x1i + x2i
+      y7r = x1r - x2r
+      y7i = x1i - x2i
+      x0r = a(6) + a(23)
+      x0i = a(7) - a(22)
+      x1r = wk1i * x0r + wk1r * x0i
+      x1i = wk1i * x0i - wk1r * x0r
+      x0r = a(14) + a(31)
+      x0i = a(15) - a(30)
+      x2r = wk3i * x0r - wk3r * x0i
+      x2i = wk3i * x0i + wk3r * x0r
+      y11r = x1r + x2r
+      y11i = x1i + x2i
+      y15r = x1r - x2r
+      y15i = x1i - x2i
+      x1r = y0r + y2r
+      x1i = y0i + y2i
+      x2r = y1r + y3r
+      x2i = y1i + y3i
+      a(0) = x1r + x2r
+      a(1) = x1i + x2i
+      a(2) = x1r - x2r
+      a(3) = x1i - x2i
+      x1r = y0r - y2r
+      x1i = y0i - y2i
+      x2r = y1r - y3r
+      x2i = y1i - y3i
+      a(4) = x1r - x2i
+      a(5) = x1i + x2r
+      a(6) = x1r + x2i
+      a(7) = x1i - x2r
+      x1r = y4r - y6i
+      x1i = y4i + y6r
+      x0r = y5r - y7i
+      x0i = y5i + y7r
+      x2r = wn4r * (x0r - x0i)
+      x2i = wn4r * (x0i + x0r)
+      a(8) = x1r + x2r
+      a(9) = x1i + x2i
+      a(10) = x1r - x2r
+      a(11) = x1i - x2i
+      x1r = y4r + y6i
+      x1i = y4i - y6r
+      x0r = y5r + y7i
+      x0i = y5i - y7r
+      x2r = wn4r * (x0r - x0i)
+      x2i = wn4r * (x0i + x0r)
+      a(12) = x1r - x2i
+      a(13) = x1i + x2r
+      a(14) = x1r + x2i
+      a(15) = x1i - x2r
+      x1r = y8r + y10r
+      x1i = y8i + y10i
+      x2r = y9r - y11r
+      x2i = y9i - y11i
+      a(16) = x1r + x2r
+      a(17) = x1i + x2i
+      a(18) = x1r - x2r
+      a(19) = x1i - x2i
+      x1r = y8r - y10r
+      x1i = y8i - y10i
+      x2r = y9r + y11r
+      x2i = y9i + y11i
+      a(20) = x1r - x2i
+      a(21) = x1i + x2r
+      a(22) = x1r + x2i
+      a(23) = x1i - x2r
+      x1r = y12r - y14i
+      x1i = y12i + y14r
+      x0r = y13r + y15i
+      x0i = y13i - y15r
+      x2r = wn4r * (x0r - x0i)
+      x2i = wn4r * (x0i + x0r)
+      a(24) = x1r + x2r
+      a(25) = x1i + x2i
+      a(26) = x1r - x2r
+      a(27) = x1i - x2i
+      x1r = y12r + y14i
+      x1i = y12i - y14r
+      x0r = y13r - y15i
+      x0i = y13i + y15r
+      x2r = wn4r * (x0r - x0i)
+      x2i = wn4r * (x0i + x0r)
+      a(28) = x1r - x2i
+      a(29) = x1i + x2r
+      a(30) = x1r + x2i
+      a(31) = x1i - x2r
+      end
+!
+      subroutine cftf081(a, w)
+      real*8 a(0 : 15), w(0 : *)
+      real*8 wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      real*8 y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i
+      real*8 y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i
+      wn4r = w(1)
+      x0r = a(0) + a(8)
+      x0i = a(1) + a(9)
+      x1r = a(0) - a(8)
+      x1i = a(1) - a(9)
+      x2r = a(4) + a(12)
+      x2i = a(5) + a(13)
+      x3r = a(4) - a(12)
+      x3i = a(5) - a(13)
+      y0r = x0r + x2r
+      y0i = x0i + x2i
+      y2r = x0r - x2r
+      y2i = x0i - x2i
+      y1r = x1r - x3i
+      y1i = x1i + x3r
+      y3r = x1r + x3i
+      y3i = x1i - x3r
+      x0r = a(2) + a(10)
+      x0i = a(3) + a(11)
+      x1r = a(2) - a(10)
+      x1i = a(3) - a(11)
+      x2r = a(6) + a(14)
+      x2i = a(7) + a(15)
+      x3r = a(6) - a(14)
+      x3i = a(7) - a(15)
+      y4r = x0r + x2r
+      y4i = x0i + x2i
+      y6r = x0r - x2r
+      y6i = x0i - x2i
+      x0r = x1r - x3i
+      x0i = x1i + x3r
+      x2r = x1r + x3i
+      x2i = x1i - x3r
+      y5r = wn4r * (x0r - x0i)
+      y5i = wn4r * (x0r + x0i)
+      y7r = wn4r * (x2r - x2i)
+      y7i = wn4r * (x2r + x2i)
+      a(8) = y1r + y5r
+      a(9) = y1i + y5i
+      a(10) = y1r - y5r
+      a(11) = y1i - y5i
+      a(12) = y3r - y7i
+      a(13) = y3i + y7r
+      a(14) = y3r + y7i
+      a(15) = y3i - y7r
+      a(0) = y0r + y4r
+      a(1) = y0i + y4i
+      a(2) = y0r - y4r
+      a(3) = y0i - y4i
+      a(4) = y2r - y6i
+      a(5) = y2i + y6r
+      a(6) = y2r + y6i
+      a(7) = y2i - y6r
+      end
+!
+      subroutine cftf082(a, w)
+      real*8 a(0 : 15), w(0 : *)
+      real*8 wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i
+      real*8 y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i
+      real*8 y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i
+      wn4r = w(1)
+      wk1r = w(2)
+      wk1i = w(3)
+      y0r = a(0) - a(9)
+      y0i = a(1) + a(8)
+      y1r = a(0) + a(9)
+      y1i = a(1) - a(8)
+      x0r = a(4) - a(13)
+      x0i = a(5) + a(12)
+      y2r = wn4r * (x0r - x0i)
+      y2i = wn4r * (x0i + x0r)
+      x0r = a(4) + a(13)
+      x0i = a(5) - a(12)
+      y3r = wn4r * (x0r - x0i)
+      y3i = wn4r * (x0i + x0r)
+      x0r = a(2) - a(11)
+      x0i = a(3) + a(10)
+      y4r = wk1r * x0r - wk1i * x0i
+      y4i = wk1r * x0i + wk1i * x0r
+      x0r = a(2) + a(11)
+      x0i = a(3) - a(10)
+      y5r = wk1i * x0r - wk1r * x0i
+      y5i = wk1i * x0i + wk1r * x0r
+      x0r = a(6) - a(15)
+      x0i = a(7) + a(14)
+      y6r = wk1i * x0r - wk1r * x0i
+      y6i = wk1i * x0i + wk1r * x0r
+      x0r = a(6) + a(15)
+      x0i = a(7) - a(14)
+      y7r = wk1r * x0r - wk1i * x0i
+      y7i = wk1r * x0i + wk1i * x0r
+      x0r = y0r + y2r
+      x0i = y0i + y2i
+      x1r = y4r + y6r
+      x1i = y4i + y6i
+      a(0) = x0r + x1r
+      a(1) = x0i + x1i
+      a(2) = x0r - x1r
+      a(3) = x0i - x1i
+      x0r = y0r - y2r
+      x0i = y0i - y2i
+      x1r = y4r - y6r
+      x1i = y4i - y6i
+      a(4) = x0r - x1i
+      a(5) = x0i + x1r
+      a(6) = x0r + x1i
+      a(7) = x0i - x1r
+      x0r = y1r - y3i
+      x0i = y1i + y3r
+      x1r = y5r - y7r
+      x1i = y5i - y7i
+      a(8) = x0r + x1r
+      a(9) = x0i + x1i
+      a(10) = x0r - x1r
+      a(11) = x0i - x1i
+      x0r = y1r + y3i
+      x0i = y1i - y3r
+      x1r = y5r + y7r
+      x1i = y5i + y7i
+      a(12) = x0r - x1i
+      a(13) = x0i + x1r
+      a(14) = x0r + x1i
+      a(15) = x0i - x1r
+      end
+!
+      subroutine cftf040(a)
+      real*8 a(0 : 7), x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      x0r = a(0) + a(4)
+      x0i = a(1) + a(5)
+      x1r = a(0) - a(4)
+      x1i = a(1) - a(5)
+      x2r = a(2) + a(6)
+      x2i = a(3) + a(7)
+      x3r = a(2) - a(6)
+      x3i = a(3) - a(7)
+      a(0) = x0r + x2r
+      a(1) = x0i + x2i
+      a(2) = x1r - x3i
+      a(3) = x1i + x3r
+      a(4) = x0r - x2r
+      a(5) = x0i - x2i
+      a(6) = x1r + x3i
+      a(7) = x1i - x3r
+      end
+!
+      subroutine cftb040(a)
+      real*8 a(0 : 7), x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i
+      x0r = a(0) + a(4)
+      x0i = a(1) + a(5)
+      x1r = a(0) - a(4)
+      x1i = a(1) - a(5)
+      x2r = a(2) + a(6)
+      x2i = a(3) + a(7)
+      x3r = a(2) - a(6)
+      x3i = a(3) - a(7)
+      a(0) = x0r + x2r
+      a(1) = x0i + x2i
+      a(2) = x1r + x3i
+      a(3) = x1i - x3r
+      a(4) = x0r - x2r
+      a(5) = x0i - x2i
+      a(6) = x1r - x3i
+      a(7) = x1i + x3r
+      end
+!
+      subroutine cftx020(a)
+      real*8 a(0 : 3), x0r, x0i
+      x0r = a(0) - a(2)
+      x0i = a(1) - a(3)
+      a(0) = a(0) + a(2)
+      a(1) = a(1) + a(3)
+      a(2) = x0r
+      a(3) = x0i
+      end
+!
+      subroutine rftfsub(n, a, nc, c)
+      integer n, nc, j, k, kk, ks, m
+      real*8 a(0 : n - 1), c(0 : nc - 1), wkr, wki, xr, xi, yr, yi
+      m = n / 2
+      ks = 2 * nc / m
+      kk = 0
+      do j = 2, m - 2, 2
+          k = n - j
+          kk = kk + ks
+          wkr = 0.5d0 - c(nc - kk)
+          wki = c(kk)
+          xr = a(j) - a(k)
+          xi = a(j + 1) + a(k + 1)
+          yr = wkr * xr - wki * xi
+          yi = wkr * xi + wki * xr
+          a(j) = a(j) - yr
+          a(j + 1) = a(j + 1) - yi
+          a(k) = a(k) + yr
+          a(k + 1) = a(k + 1) - yi
+      end do
+      end
+!
+      subroutine rftbsub(n, a, nc, c)
+      integer n, nc, j, k, kk, ks, m
+      real*8 a(0 : n - 1), c(0 : nc - 1), wkr, wki, xr, xi, yr, yi
+      m = n / 2
+      ks = 2 * nc / m
+      kk = 0
+      do j = 2, m - 2, 2
+          k = n - j
+          kk = kk + ks
+          wkr = 0.5d0 - c(nc - kk)
+          wki = c(kk)
+          xr = a(j) - a(k)
+          xi = a(j + 1) + a(k + 1)
+          yr = wkr * xr + wki * xi
+          yi = wkr * xi - wki * xr
+          a(j) = a(j) - yr
+          a(j + 1) = a(j + 1) - yi
+          a(k) = a(k) + yr
+          a(k + 1) = a(k + 1) - yi
+      end do
+      end
+!
+      subroutine dctsub(n, a, nc, c)
+      integer n, nc, j, k, kk, ks, m
+      real*8 a(0 : n - 1), c(0 : nc - 1), wkr, wki, xr
+      m = n / 2
+      ks = nc / n
+      kk = 0
+      do j = 1, m - 1
+          k = n - j
+          kk = kk + ks
+          wkr = c(kk) - c(nc - kk)
+          wki = c(kk) + c(nc - kk)
+          xr = wki * a(j) - wkr * a(k)
+          a(j) = wkr * a(j) + wki * a(k)
+          a(k) = xr
+      end do
+      a(m) = c(0) * a(m)
+      end
+!
+      subroutine dstsub(n, a, nc, c)
+      integer n, nc, j, k, kk, ks, m
+      real*8 a(0 : n - 1), c(0 : nc - 1), wkr, wki, xr
+      m = n / 2
+      ks = nc / n
+      kk = 0
+      do j = 1, m - 1
+          k = n - j
+          kk = kk + ks
+          wkr = c(kk) - c(nc - kk)
+          wki = c(kk) + c(nc - kk)
+          xr = wki * a(k) - wkr * a(j)
+          a(k) = wkr * a(k) + wki * a(j)
+          a(j) = xr
+      end do
+      a(m) = c(0) * a(m)
+      end
+!
diff --git a/third_party/tensorflow_dependencies/fft2d/fftsg2d.c b/third_party/tensorflow_dependencies/fft2d/fftsg2d.c
new file mode 100644
index 0000000..2ebfa93
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/fftsg2d.c
@@ -0,0 +1,1190 @@
+/*
+Fast Fourier/Cosine/Sine Transform
+    dimension   :two
+    data length :power of 2
+    decimation  :frequency
+    radix       :split-radix, row-column
+    data        :inplace
+    table       :use
+functions
+    cdft2d: Complex Discrete Fourier Transform
+    rdft2d: Real Discrete Fourier Transform
+    ddct2d: Discrete Cosine Transform
+    ddst2d: Discrete Sine Transform
+function prototypes
+    void cdft2d(int, int, int, double **, double *, int *, double *);
+    void rdft2d(int, int, int, double **, double *, int *, double *);
+    void rdft2dsort(int, int, int, double **);
+    void ddct2d(int, int, int, double **, double *, int *, double *);
+    void ddst2d(int, int, int, double **, double *, int *, double *);
+necessary package
+    fftsg.c  : 1D-FFT package
+macro definitions
+    USE_FFT2D_PTHREADS : default=not defined
+        FFT2D_MAX_THREADS     : must be 2^N, default=4
+        FFT2D_THREADS_BEGIN_N : default=65536
+    USE_FFT2D_WINTHREADS : default=not defined
+        FFT2D_MAX_THREADS     : must be 2^N, default=4
+        FFT2D_THREADS_BEGIN_N : default=131072
+
+
+-------- Complex DFT (Discrete Fourier Transform) --------
+    [definition]
+        <case1>
+            X[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 x[j1][j2] * 
+                            exp(2*pi*i*j1*k1/n1) * 
+                            exp(2*pi*i*j2*k2/n2), 0<=k1<n1, 0<=k2<n2
+        <case2>
+            X[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 x[j1][j2] * 
+                            exp(-2*pi*i*j1*k1/n1) * 
+                            exp(-2*pi*i*j2*k2/n2), 0<=k1<n1, 0<=k2<n2
+        (notes: sum_j=0^n-1 is a summation from j=0 to n-1)
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            cdft2d(n1, 2*n2, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            cdft2d(n1, 2*n2, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 1, n1 = power of 2
+        2*n2   :data length (int)
+                n2 >= 1, n2 = power of 2
+        a[0...n1-1][0...2*n2-1]
+               :input/output data (double **)
+                input data
+                    a[j1][2*j2] = Re(x[j1][j2]), 
+                    a[j1][2*j2+1] = Im(x[j1][j2]), 
+                    0<=j1<n1, 0<=j2<n2
+                output data
+                    a[k1][2*k2] = Re(X[k1][k2]), 
+                    a[k1][2*k2+1] = Im(X[k1][k2]), 
+                    0<=k1<n1, 0<=k2<n2
+        t[0...*]
+               :work area (double *)
+                length of t >= 8*n1,                   if single thread, 
+                length of t >= 8*n1*FFT2D_MAX_THREADS, if multi threads, 
+                t is dynamically allocated, if t == NULL.
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1, n2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1/2, n2/2)
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            cdft2d(n1, 2*n2, -1, a, t, ip, w);
+        is 
+            cdft2d(n1, 2*n2, 1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= 2 * n2 - 1; j2++) {
+                    a[j1][j2] *= 1.0 / n1 / n2;
+                }
+            }
+        .
+
+
+-------- Real DFT / Inverse of Real DFT --------
+    [definition]
+        <case1> RDFT
+            R[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2), 
+                            0<=k1<n1, 0<=k2<n2
+            I[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2), 
+                            0<=k1<n1, 0<=k2<n2
+        <case2> IRDFT (excluding scale)
+            a[k1][k2] = (1/2) * sum_j1=0^n1-1 sum_j2=0^n2-1
+                            (R[j1][j2] * 
+                            cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2) + 
+                            I[j1][j2] * 
+                            sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2)), 
+                            0<=k1<n1, 0<=k2<n2
+        (notes: R[n1-k1][n2-k2] = R[k1][k2], 
+                I[n1-k1][n2-k2] = -I[k1][k2], 
+                R[n1-k1][0] = R[k1][0], 
+                I[n1-k1][0] = -I[k1][0], 
+                R[0][n2-k2] = R[0][k2], 
+                I[0][n2-k2] = -I[0][k2], 
+                0<k1<n1, 0<k2<n2)
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            rdft2d(n1, n2, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            rdft2d(n1, n2, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 2, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 2, n2 = power of 2
+        a[0...n1-1][0...n2-1]
+               :input/output data (double **)
+                <case1>
+                    output data
+                        a[k1][2*k2] = R[k1][k2] = R[n1-k1][n2-k2], 
+                        a[k1][2*k2+1] = I[k1][k2] = -I[n1-k1][n2-k2], 
+                            0<k1<n1, 0<k2<n2/2, 
+                        a[0][2*k2] = R[0][k2] = R[0][n2-k2], 
+                        a[0][2*k2+1] = I[0][k2] = -I[0][n2-k2], 
+                            0<k2<n2/2, 
+                        a[k1][0] = R[k1][0] = R[n1-k1][0], 
+                        a[k1][1] = I[k1][0] = -I[n1-k1][0], 
+                        a[n1-k1][1] = R[k1][n2/2] = R[n1-k1][n2/2], 
+                        a[n1-k1][0] = -I[k1][n2/2] = I[n1-k1][n2/2], 
+                            0<k1<n1/2, 
+                        a[0][0] = R[0][0], 
+                        a[0][1] = R[0][n2/2], 
+                        a[n1/2][0] = R[n1/2][0], 
+                        a[n1/2][1] = R[n1/2][n2/2]
+                <case2>
+                    input data
+                        a[j1][2*j2] = R[j1][j2] = R[n1-j1][n2-j2], 
+                        a[j1][2*j2+1] = I[j1][j2] = -I[n1-j1][n2-j2], 
+                            0<j1<n1, 0<j2<n2/2, 
+                        a[0][2*j2] = R[0][j2] = R[0][n2-j2], 
+                        a[0][2*j2+1] = I[0][j2] = -I[0][n2-j2], 
+                            0<j2<n2/2, 
+                        a[j1][0] = R[j1][0] = R[n1-j1][0], 
+                        a[j1][1] = I[j1][0] = -I[n1-j1][0], 
+                        a[n1-j1][1] = R[j1][n2/2] = R[n1-j1][n2/2], 
+                        a[n1-j1][0] = -I[j1][n2/2] = I[n1-j1][n2/2], 
+                            0<j1<n1/2, 
+                        a[0][0] = R[0][0], 
+                        a[0][1] = R[0][n2/2], 
+                        a[n1/2][0] = R[n1/2][0], 
+                        a[n1/2][1] = R[n1/2][n2/2]
+                ---- output ordering ----
+                    rdft2d(n1, n2, 1, a, t, ip, w);
+                    rdft2dsort(n1, n2, 1, a);
+                    // stored data is a[0...n1-1][0...n2+1]:
+                    // a[k1][2*k2] = R[k1][k2], 
+                    // a[k1][2*k2+1] = I[k1][k2], 
+                    // 0<=k1<n1, 0<=k2<=n2/2.
+                    // the stored data is larger than the input data!
+                ---- input ordering ----
+                    rdft2dsort(n1, n2, -1, a);
+                    rdft2d(n1, n2, -1, a, t, ip, w);
+        t[0...*]
+               :work area (double *)
+                length of t >= 8*n1,                   if single thread, 
+                length of t >= 8*n1*FFT2D_MAX_THREADS, if multi threads, 
+                t is dynamically allocated, if t == NULL.
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1, n2/2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1/2, n2/4) + n2/4
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            rdft2d(n1, n2, 1, a, t, ip, w);
+        is 
+            rdft2d(n1, n2, -1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    a[j1][j2] *= 2.0 / n1 / n2;
+                }
+            }
+        .
+
+
+-------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
+    [definition]
+        <case1> IDCT (excluding scale)
+            C[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            cos(pi*j1*(k1+1/2)/n1) * 
+                            cos(pi*j2*(k2+1/2)/n2), 
+                            0<=k1<n1, 0<=k2<n2
+        <case2> DCT
+            C[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            cos(pi*(j1+1/2)*k1/n1) * 
+                            cos(pi*(j2+1/2)*k2/n2), 
+                            0<=k1<n1, 0<=k2<n2
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            ddct2d(n1, n2, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            ddct2d(n1, n2, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 2, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 2, n2 = power of 2
+        a[0...n1-1][0...n2-1]
+               :input/output data (double **)
+                output data
+                    a[k1][k2] = C[k1][k2], 0<=k1<n1, 0<=k2<n2
+        t[0...*]
+               :work area (double *)
+                length of t >= 4*n1,                   if single thread, 
+                length of t >= 4*n1*FFT2D_MAX_THREADS, if multi threads, 
+                t is dynamically allocated, if t == NULL.
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1/2, n2/2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1*3/2, n2*3/2)
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            ddct2d(n1, n2, -1, a, t, ip, w);
+        is 
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                a[j1][0] *= 0.5;
+            }
+            for (j2 = 0; j2 <= n2 - 1; j2++) {
+                a[0][j2] *= 0.5;
+            }
+            ddct2d(n1, n2, 1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    a[j1][j2] *= 4.0 / n1 / n2;
+                }
+            }
+        .
+
+
+-------- DST (Discrete Sine Transform) / Inverse of DST --------
+    [definition]
+        <case1> IDST (excluding scale)
+            S[k1][k2] = sum_j1=1^n1 sum_j2=1^n2 A[j1][j2] * 
+                            sin(pi*j1*(k1+1/2)/n1) * 
+                            sin(pi*j2*(k2+1/2)/n2), 
+                            0<=k1<n1, 0<=k2<n2
+        <case2> DST
+            S[k1][k2] = sum_j1=0^n1-1 sum_j2=0^n2-1 a[j1][j2] * 
+                            sin(pi*(j1+1/2)*k1/n1) * 
+                            sin(pi*(j2+1/2)*k2/n2), 
+                            0<k1<=n1, 0<k2<=n2
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            ddst2d(n1, n2, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            ddst2d(n1, n2, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 2, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 2, n2 = power of 2
+        a[0...n1-1][0...n2-1]
+               :input/output data (double **)
+                <case1>
+                    input data
+                        a[j1][j2] = A[j1][j2], 0<j1<n1, 0<j2<n2, 
+                        a[j1][0] = A[j1][n2], 0<j1<n1, 
+                        a[0][j2] = A[n1][j2], 0<j2<n2, 
+                        a[0][0] = A[n1][n2]
+                        (i.e. A[j1][j2] = a[j1 % n1][j2 % n2])
+                    output data
+                        a[k1][k2] = S[k1][k2], 0<=k1<n1, 0<=k2<n2
+                <case2>
+                    output data
+                        a[k1][k2] = S[k1][k2], 0<k1<n1, 0<k2<n2, 
+                        a[k1][0] = S[k1][n2], 0<k1<n1, 
+                        a[0][k2] = S[n1][k2], 0<k2<n2, 
+                        a[0][0] = S[n1][n2]
+                        (i.e. S[k1][k2] = a[k1 % n1][k2 % n2])
+        t[0...*]
+               :work area (double *)
+                length of t >= 4*n1,                   if single thread, 
+                length of t >= 4*n1*FFT2D_MAX_THREADS, if multi threads, 
+                t is dynamically allocated, if t == NULL.
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1/2, n2/2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1*3/2, n2*3/2)
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            ddst2d(n1, n2, -1, a, t, ip, w);
+        is 
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                a[j1][0] *= 0.5;
+            }
+            for (j2 = 0; j2 <= n2 - 1; j2++) {
+                a[0][j2] *= 0.5;
+            }
+            ddst2d(n1, n2, 1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    a[j1][j2] *= 4.0 / n1 / n2;
+                }
+            }
+        .
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#define fft2d_alloc_error_check(p) { \
+    if ((p) == NULL) { \
+        fprintf(stderr, "fft2d memory allocation error\n"); \
+        exit(1); \
+    } \
+}
+
+
+#ifdef USE_FFT2D_PTHREADS
+#define USE_FFT2D_THREADS
+#ifndef FFT2D_MAX_THREADS
+#define FFT2D_MAX_THREADS 4
+#endif
+#ifndef FFT2D_THREADS_BEGIN_N
+#define FFT2D_THREADS_BEGIN_N 65536
+#endif
+#include <pthread.h>
+#define fft2d_thread_t pthread_t
+#define fft2d_thread_create(thp,func,argp) { \
+    if (pthread_create(thp, NULL, func, (void *) (argp)) != 0) { \
+        fprintf(stderr, "fft2d thread error\n"); \
+        exit(1); \
+    } \
+}
+#define fft2d_thread_wait(th) { \
+    if (pthread_join(th, NULL) != 0) { \
+        fprintf(stderr, "fft2d thread error\n"); \
+        exit(1); \
+    } \
+}
+#endif /* USE_FFT2D_PTHREADS */
+
+
+#ifdef USE_FFT2D_WINTHREADS
+#define USE_FFT2D_THREADS
+#ifndef FFT2D_MAX_THREADS
+#define FFT2D_MAX_THREADS 4
+#endif
+#ifndef FFT2D_THREADS_BEGIN_N
+#define FFT2D_THREADS_BEGIN_N 131072
+#endif
+#include <windows.h>
+#define fft2d_thread_t HANDLE
+#define fft2d_thread_create(thp,func,argp) { \
+    DWORD thid; \
+    *(thp) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (func), (LPVOID) (argp), 0, &thid); \
+    if (*(thp) == 0) { \
+        fprintf(stderr, "fft2d thread error\n"); \
+        exit(1); \
+    } \
+}
+#define fft2d_thread_wait(th) { \
+    WaitForSingleObject(th, INFINITE); \
+    CloseHandle(th); \
+}
+#endif /* USE_FFT2D_WINTHREADS */
+
+
+void cdft2d(int n1, int n2, int isgn, double **a, double *t, 
+    int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void cdft(int n, int isgn, double *a, int *ip, double *w);
+    void cdft2d_sub(int n1, int n2, int isgn, double **a, double *t, 
+        int *ip, double *w);
+#ifdef USE_FFT2D_THREADS
+    void xdft2d0_subth(int n1, int n2, int icr, int isgn, double **a, 
+        int *ip, double *w);
+    void cdft2d_subth(int n1, int n2, int isgn, double **a, double *t, 
+        int *ip, double *w);
+#endif /* USE_FFT2D_THREADS */
+    int n, itnull, nthread, nt, i;
+    
+    n = n1 << 1;
+    if (n < n2) {
+        n = n2;
+    }
+    if (n > (ip[0] << 2)) {
+        makewt(n >> 2, ip, w);
+    }
+    itnull = 0;
+    if (t == NULL) {
+        itnull = 1;
+        nthread = 1;
+#ifdef USE_FFT2D_THREADS
+        nthread = FFT2D_MAX_THREADS;
+#endif /* USE_FFT2D_THREADS */
+        nt = 8 * nthread * n1;
+        if (n2 == 4 * nthread) {
+            nt >>= 1;
+        } else if (n2 < 4 * nthread) {
+            nt >>= 2;
+        }
+        t = (double *) malloc(sizeof(double) * nt);
+        fft2d_alloc_error_check(t);
+    }
+#ifdef USE_FFT2D_THREADS
+    if ((double) n1 * n2 >= (double) FFT2D_THREADS_BEGIN_N) {
+        xdft2d0_subth(n1, n2, 0, isgn, a, ip, w);
+        cdft2d_subth(n1, n2, isgn, a, t, ip, w);
+    } else 
+#endif /* USE_FFT2D_THREADS */
+    {
+        for (i = 0; i < n1; i++) {
+            cdft(n2, isgn, a[i], ip, w);
+        }
+        cdft2d_sub(n1, n2, isgn, a, t, ip, w);
+    }
+    if (itnull != 0) {
+        free(t);
+    }
+}
+
+
+void rdft2d(int n1, int n2, int isgn, double **a, double *t, 
+    int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void rdft(int n, int isgn, double *a, int *ip, double *w);
+    void cdft2d_sub(int n1, int n2, int isgn, double **a, double *t, 
+        int *ip, double *w);
+    void rdft2d_sub(int n1, int n2, int isgn, double **a);
+#ifdef USE_FFT2D_THREADS
+    void xdft2d0_subth(int n1, int n2, int icr, int isgn, double **a, 
+        int *ip, double *w);
+    void cdft2d_subth(int n1, int n2, int isgn, double **a, double *t, 
+        int *ip, double *w);
+#endif /* USE_FFT2D_THREADS */
+    int n, nw, nc, itnull, nthread, nt, i;
+    
+    n = n1 << 1;
+    if (n < n2) {
+        n = n2;
+    }
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n2 > (nc << 2)) {
+        nc = n2 >> 2;
+        makect(nc, ip, w + nw);
+    }
+    itnull = 0;
+    if (t == NULL) {
+        itnull = 1;
+        nthread = 1;
+#ifdef USE_FFT2D_THREADS
+        nthread = FFT2D_MAX_THREADS;
+#endif /* USE_FFT2D_THREADS */
+        nt = 8 * nthread * n1;
+        if (n2 == 4 * nthread) {
+            nt >>= 1;
+        } else if (n2 < 4 * nthread) {
+            nt >>= 2;
+        }
+        t = (double *) malloc(sizeof(double) * nt);
+        fft2d_alloc_error_check(t);
+    }
+#ifdef USE_FFT2D_THREADS
+    if ((double) n1 * n2 >= (double) FFT2D_THREADS_BEGIN_N) {
+        if (isgn < 0) {
+            rdft2d_sub(n1, n2, isgn, a);
+            cdft2d_subth(n1, n2, isgn, a, t, ip, w);
+        }
+        xdft2d0_subth(n1, n2, 1, isgn, a, ip, w);
+        if (isgn >= 0) {
+            cdft2d_subth(n1, n2, isgn, a, t, ip, w);
+            rdft2d_sub(n1, n2, isgn, a);
+        }
+    } else 
+#endif /* USE_FFT2D_THREADS */
+    {
+        if (isgn < 0) {
+            rdft2d_sub(n1, n2, isgn, a);
+            cdft2d_sub(n1, n2, isgn, a, t, ip, w);
+        }
+        for (i = 0; i < n1; i++) {
+            rdft(n2, isgn, a[i], ip, w);
+        }
+        if (isgn >= 0) {
+            cdft2d_sub(n1, n2, isgn, a, t, ip, w);
+            rdft2d_sub(n1, n2, isgn, a);
+        }
+    }
+    if (itnull != 0) {
+        free(t);
+    }
+}
+
+
+void rdft2dsort(int n1, int n2, int isgn, double **a)
+{
+    int n1h, i;
+    double x, y;
+    
+    n1h = n1 >> 1;
+    if (isgn < 0) {
+        for (i = n1h + 1; i < n1; i++) {
+            a[i][0] = a[i][n2 + 1];
+            a[i][1] = a[i][n2];
+        }
+        a[0][1] = a[0][n2];
+        a[n1h][1] = a[n1h][n2];
+    } else {
+        for (i = n1h + 1; i < n1; i++) {
+            y = a[i][0];
+            x = a[i][1];
+            a[i][n2] = x;
+            a[i][n2 + 1] = y;
+            a[n1 - i][n2] = x;
+            a[n1 - i][n2 + 1] = -y;
+            a[i][0] = a[n1 - i][0];
+            a[i][1] = -a[n1 - i][1];
+        }
+        a[0][n2] = a[0][1];
+        a[0][n2 + 1] = 0;
+        a[0][1] = 0;
+        a[n1h][n2] = a[n1h][1];
+        a[n1h][n2 + 1] = 0;
+        a[n1h][1] = 0;
+    }
+}
+
+
+void ddct2d(int n1, int n2, int isgn, double **a, double *t, 
+    int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void ddct(int n, int isgn, double *a, int *ip, double *w);
+    void ddxt2d_sub(int n1, int n2, int ics, int isgn, double **a, 
+        double *t, int *ip, double *w);
+#ifdef USE_FFT2D_THREADS
+    void ddxt2d0_subth(int n1, int n2, int ics, int isgn, double **a, 
+        int *ip, double *w);
+    void ddxt2d_subth(int n1, int n2, int ics, int isgn, double **a, 
+        double *t, int *ip, double *w);
+#endif /* USE_FFT2D_THREADS */
+    int n, nw, nc, itnull, nthread, nt, i;
+    
+    n = n1;
+    if (n < n2) {
+        n = n2;
+    }
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n > nc) {
+        nc = n;
+        makect(nc, ip, w + nw);
+    }
+    itnull = 0;
+    if (t == NULL) {
+        itnull = 1;
+        nthread = 1;
+#ifdef USE_FFT2D_THREADS
+        nthread = FFT2D_MAX_THREADS;
+#endif /* USE_FFT2D_THREADS */
+        nt = 4 * nthread * n1;
+        if (n2 == 2 * nthread) {
+            nt >>= 1;
+        } else if (n2 < 2 * nthread) {
+            nt >>= 2;
+        }
+        t = (double *) malloc(sizeof(double) * nt);
+        fft2d_alloc_error_check(t);
+    }
+#ifdef USE_FFT2D_THREADS
+    if ((double) n1 * n2 >= (double) FFT2D_THREADS_BEGIN_N) {
+        ddxt2d0_subth(n1, n2, 0, isgn, a, ip, w);
+        ddxt2d_subth(n1, n2, 0, isgn, a, t, ip, w);
+    } else 
+#endif /* USE_FFT2D_THREADS */
+    {
+        for (i = 0; i < n1; i++) {
+            ddct(n2, isgn, a[i], ip, w);
+        }
+        ddxt2d_sub(n1, n2, 0, isgn, a, t, ip, w);
+    }
+    if (itnull != 0) {
+        free(t);
+    }
+}
+
+
+void ddst2d(int n1, int n2, int isgn, double **a, double *t, 
+    int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void ddst(int n, int isgn, double *a, int *ip, double *w);
+    void ddxt2d_sub(int n1, int n2, int ics, int isgn, double **a, 
+        double *t, int *ip, double *w);
+#ifdef USE_FFT2D_THREADS
+    void ddxt2d0_subth(int n1, int n2, int ics, int isgn, double **a, 
+        int *ip, double *w);
+    void ddxt2d_subth(int n1, int n2, int ics, int isgn, double **a, 
+        double *t, int *ip, double *w);
+#endif /* USE_FFT2D_THREADS */
+    int n, nw, nc, itnull, nthread, nt, i;
+    
+    n = n1;
+    if (n < n2) {
+        n = n2;
+    }
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n > nc) {
+        nc = n;
+        makect(nc, ip, w + nw);
+    }
+    itnull = 0;
+    if (t == NULL) {
+        itnull = 1;
+        nthread = 1;
+#ifdef USE_FFT2D_THREADS
+        nthread = FFT2D_MAX_THREADS;
+#endif /* USE_FFT2D_THREADS */
+        nt = 4 * nthread * n1;
+        if (n2 == 2 * nthread) {
+            nt >>= 1;
+        } else if (n2 < 2 * nthread) {
+            nt >>= 2;
+        }
+        t = (double *) malloc(sizeof(double) * nt);
+        fft2d_alloc_error_check(t);
+    }
+#ifdef USE_FFT2D_THREADS
+    if ((double) n1 * n2 >= (double) FFT2D_THREADS_BEGIN_N) {
+        ddxt2d0_subth(n1, n2, 1, isgn, a, ip, w);
+        ddxt2d_subth(n1, n2, 1, isgn, a, t, ip, w);
+    } else 
+#endif /* USE_FFT2D_THREADS */
+    {
+        for (i = 0; i < n1; i++) {
+            ddst(n2, isgn, a[i], ip, w);
+        }
+        ddxt2d_sub(n1, n2, 1, isgn, a, t, ip, w);
+    }
+    if (itnull != 0) {
+        free(t);
+    }
+}
+
+
+/* -------- child routines -------- */
+
+
+void cdft2d_sub(int n1, int n2, int isgn, double **a, double *t, 
+    int *ip, double *w)
+{
+    void cdft(int n, int isgn, double *a, int *ip, double *w);
+    int i, j;
+    
+    if (n2 > 4) {
+        for (j = 0; j < n2; j += 8) {
+            for (i = 0; i < n1; i++) {
+                t[2 * i] = a[i][j];
+                t[2 * i + 1] = a[i][j + 1];
+                t[2 * n1 + 2 * i] = a[i][j + 2];
+                t[2 * n1 + 2 * i + 1] = a[i][j + 3];
+                t[4 * n1 + 2 * i] = a[i][j + 4];
+                t[4 * n1 + 2 * i + 1] = a[i][j + 5];
+                t[6 * n1 + 2 * i] = a[i][j + 6];
+                t[6 * n1 + 2 * i + 1] = a[i][j + 7];
+            }
+            cdft(2 * n1, isgn, t, ip, w);
+            cdft(2 * n1, isgn, &t[2 * n1], ip, w);
+            cdft(2 * n1, isgn, &t[4 * n1], ip, w);
+            cdft(2 * n1, isgn, &t[6 * n1], ip, w);
+            for (i = 0; i < n1; i++) {
+                a[i][j] = t[2 * i];
+                a[i][j + 1] = t[2 * i + 1];
+                a[i][j + 2] = t[2 * n1 + 2 * i];
+                a[i][j + 3] = t[2 * n1 + 2 * i + 1];
+                a[i][j + 4] = t[4 * n1 + 2 * i];
+                a[i][j + 5] = t[4 * n1 + 2 * i + 1];
+                a[i][j + 6] = t[6 * n1 + 2 * i];
+                a[i][j + 7] = t[6 * n1 + 2 * i + 1];
+            }
+        }
+    } else if (n2 == 4) {
+        for (i = 0; i < n1; i++) {
+            t[2 * i] = a[i][0];
+            t[2 * i + 1] = a[i][1];
+            t[2 * n1 + 2 * i] = a[i][2];
+            t[2 * n1 + 2 * i + 1] = a[i][3];
+        }
+        cdft(2 * n1, isgn, t, ip, w);
+        cdft(2 * n1, isgn, &t[2 * n1], ip, w);
+        for (i = 0; i < n1; i++) {
+            a[i][0] = t[2 * i];
+            a[i][1] = t[2 * i + 1];
+            a[i][2] = t[2 * n1 + 2 * i];
+            a[i][3] = t[2 * n1 + 2 * i + 1];
+        }
+    } else if (n2 == 2) {
+        for (i = 0; i < n1; i++) {
+            t[2 * i] = a[i][0];
+            t[2 * i + 1] = a[i][1];
+        }
+        cdft(2 * n1, isgn, t, ip, w);
+        for (i = 0; i < n1; i++) {
+            a[i][0] = t[2 * i];
+            a[i][1] = t[2 * i + 1];
+        }
+    }
+}
+
+
+void rdft2d_sub(int n1, int n2, int isgn, double **a)
+{
+    int n1h, i, j;
+    double xi;
+    
+    n1h = n1 >> 1;
+    if (isgn < 0) {
+        for (i = 1; i < n1h; i++) {
+            j = n1 - i;
+            xi = a[i][0] - a[j][0];
+            a[i][0] += a[j][0];
+            a[j][0] = xi;
+            xi = a[j][1] - a[i][1];
+            a[i][1] += a[j][1];
+            a[j][1] = xi;
+        }
+    } else {
+        for (i = 1; i < n1h; i++) {
+            j = n1 - i;
+            a[j][0] = 0.5 * (a[i][0] - a[j][0]);
+            a[i][0] -= a[j][0];
+            a[j][1] = 0.5 * (a[i][1] + a[j][1]);
+            a[i][1] -= a[j][1];
+        }
+    }
+}
+
+
+void ddxt2d_sub(int n1, int n2, int ics, int isgn, double **a, 
+    double *t, int *ip, double *w)
+{
+    void ddct(int n, int isgn, double *a, int *ip, double *w);
+    void ddst(int n, int isgn, double *a, int *ip, double *w);
+    int i, j;
+    
+    if (n2 > 2) {
+        for (j = 0; j < n2; j += 4) {
+            for (i = 0; i < n1; i++) {
+                t[i] = a[i][j];
+                t[n1 + i] = a[i][j + 1];
+                t[2 * n1 + i] = a[i][j + 2];
+                t[3 * n1 + i] = a[i][j + 3];
+            }
+            if (ics == 0) {
+                ddct(n1, isgn, t, ip, w);
+                ddct(n1, isgn, &t[n1], ip, w);
+                ddct(n1, isgn, &t[2 * n1], ip, w);
+                ddct(n1, isgn, &t[3 * n1], ip, w);
+            } else {
+                ddst(n1, isgn, t, ip, w);
+                ddst(n1, isgn, &t[n1], ip, w);
+                ddst(n1, isgn, &t[2 * n1], ip, w);
+                ddst(n1, isgn, &t[3 * n1], ip, w);
+            }
+            for (i = 0; i < n1; i++) {
+                a[i][j] = t[i];
+                a[i][j + 1] = t[n1 + i];
+                a[i][j + 2] = t[2 * n1 + i];
+                a[i][j + 3] = t[3 * n1 + i];
+            }
+        }
+    } else if (n2 == 2) {
+        for (i = 0; i < n1; i++) {
+            t[i] = a[i][0];
+            t[n1 + i] = a[i][1];
+        }
+        if (ics == 0) {
+            ddct(n1, isgn, t, ip, w);
+            ddct(n1, isgn, &t[n1], ip, w);
+        } else {
+            ddst(n1, isgn, t, ip, w);
+            ddst(n1, isgn, &t[n1], ip, w);
+        }
+        for (i = 0; i < n1; i++) {
+            a[i][0] = t[i];
+            a[i][1] = t[n1 + i];
+        }
+    }
+}
+
+
+#ifdef USE_FFT2D_THREADS
+struct fft2d_arg_st {
+    int nthread;
+    int n0;
+    int n1;
+    int n2;
+    int ic;
+    int isgn;
+    double **a;
+    double *t;
+    int *ip;
+    double *w;
+};
+typedef struct fft2d_arg_st fft2d_arg_t;
+
+
+void xdft2d0_subth(int n1, int n2, int icr, int isgn, double **a, 
+    int *ip, double *w)
+{
+    void *xdft2d0_th(void *p);
+    fft2d_thread_t th[FFT2D_MAX_THREADS];
+    fft2d_arg_t ag[FFT2D_MAX_THREADS];
+    int nthread, i;
+    
+    nthread = FFT2D_MAX_THREADS;
+    if (nthread > n1) {
+        nthread = n1;
+    }
+    for (i = 0; i < nthread; i++) {
+        ag[i].nthread = nthread;
+        ag[i].n0 = i;
+        ag[i].n1 = n1;
+        ag[i].n2 = n2;
+        ag[i].ic = icr;
+        ag[i].isgn = isgn;
+        ag[i].a = a;
+        ag[i].ip = ip;
+        ag[i].w = w;
+        fft2d_thread_create(&th[i], xdft2d0_th, &ag[i]);
+    }
+    for (i = 0; i < nthread; i++) {
+        fft2d_thread_wait(th[i]);
+    }
+}
+
+
+void cdft2d_subth(int n1, int n2, int isgn, double **a, double *t, 
+    int *ip, double *w)
+{
+    void *cdft2d_th(void *p);
+    fft2d_thread_t th[FFT2D_MAX_THREADS];
+    fft2d_arg_t ag[FFT2D_MAX_THREADS];
+    int nthread, nt, i;
+    
+    nthread = FFT2D_MAX_THREADS;
+    nt = 8 * n1;
+    if (n2 == 4 * FFT2D_MAX_THREADS) {
+        nt >>= 1;
+    } else if (n2 < 4 * FFT2D_MAX_THREADS) {
+        nthread = n2 >> 1;
+        nt >>= 2;
+    }
+    for (i = 0; i < nthread; i++) {
+        ag[i].nthread = nthread;
+        ag[i].n0 = i;
+        ag[i].n1 = n1;
+        ag[i].n2 = n2;
+        ag[i].isgn = isgn;
+        ag[i].a = a;
+        ag[i].t = &t[nt * i];
+        ag[i].ip = ip;
+        ag[i].w = w;
+        fft2d_thread_create(&th[i], cdft2d_th, &ag[i]);
+    }
+    for (i = 0; i < nthread; i++) {
+        fft2d_thread_wait(th[i]);
+    }
+}
+
+
+void ddxt2d0_subth(int n1, int n2, int ics, int isgn, double **a, 
+    int *ip, double *w)
+{
+    void *ddxt2d0_th(void *p);
+    fft2d_thread_t th[FFT2D_MAX_THREADS];
+    fft2d_arg_t ag[FFT2D_MAX_THREADS];
+    int nthread, i;
+    
+    nthread = FFT2D_MAX_THREADS;
+    if (nthread > n1) {
+        nthread = n1;
+    }
+    for (i = 0; i < nthread; i++) {
+        ag[i].nthread = nthread;
+        ag[i].n0 = i;
+        ag[i].n1 = n1;
+        ag[i].n2 = n2;
+        ag[i].ic = ics;
+        ag[i].isgn = isgn;
+        ag[i].a = a;
+        ag[i].ip = ip;
+        ag[i].w = w;
+        fft2d_thread_create(&th[i], ddxt2d0_th, &ag[i]);
+    }
+    for (i = 0; i < nthread; i++) {
+        fft2d_thread_wait(th[i]);
+    }
+}
+
+
+void ddxt2d_subth(int n1, int n2, int ics, int isgn, double **a, 
+    double *t, int *ip, double *w)
+{
+    void *ddxt2d_th(void *p);
+    fft2d_thread_t th[FFT2D_MAX_THREADS];
+    fft2d_arg_t ag[FFT2D_MAX_THREADS];
+    int nthread, nt, i;
+    
+    nthread = FFT2D_MAX_THREADS;
+    nt = 4 * n1;
+    if (n2 == 2 * FFT2D_MAX_THREADS) {
+        nt >>= 1;
+    } else if (n2 < 2 * FFT2D_MAX_THREADS) {
+        nthread = n2;
+        nt >>= 2;
+    }
+    for (i = 0; i < nthread; i++) {
+        ag[i].nthread = nthread;
+        ag[i].n0 = i;
+        ag[i].n1 = n1;
+        ag[i].n2 = n2;
+        ag[i].ic = ics;
+        ag[i].isgn = isgn;
+        ag[i].a = a;
+        ag[i].t = &t[nt * i];
+        ag[i].ip = ip;
+        ag[i].w = w;
+        fft2d_thread_create(&th[i], ddxt2d_th, &ag[i]);
+    }
+    for (i = 0; i < nthread; i++) {
+        fft2d_thread_wait(th[i]);
+    }
+}
+
+
+void *xdft2d0_th(void *p)
+{
+    void cdft(int n, int isgn, double *a, int *ip, double *w);
+    void rdft(int n, int isgn, double *a, int *ip, double *w);
+    int nthread, n0, n1, n2, icr, isgn, *ip, i;
+    double **a, *w;
+    
+    nthread = ((fft2d_arg_t *) p)->nthread;
+    n0 = ((fft2d_arg_t *) p)->n0;
+    n1 = ((fft2d_arg_t *) p)->n1;
+    n2 = ((fft2d_arg_t *) p)->n2;
+    icr = ((fft2d_arg_t *) p)->ic;
+    isgn = ((fft2d_arg_t *) p)->isgn;
+    a = ((fft2d_arg_t *) p)->a;
+    ip = ((fft2d_arg_t *) p)->ip;
+    w = ((fft2d_arg_t *) p)->w;
+    if (icr == 0) {
+        for (i = n0; i < n1; i += nthread) {
+            cdft(n2, isgn, a[i], ip, w);
+        }
+    } else {
+        for (i = n0; i < n1; i += nthread) {
+            rdft(n2, isgn, a[i], ip, w);
+        }
+    }
+    return (void *) 0;
+}
+
+
+void *cdft2d_th(void *p)
+{
+    void cdft(int n, int isgn, double *a, int *ip, double *w);
+    int nthread, n0, n1, n2, isgn, *ip, i, j;
+    double **a, *t, *w;
+    
+    nthread = ((fft2d_arg_t *) p)->nthread;
+    n0 = ((fft2d_arg_t *) p)->n0;
+    n1 = ((fft2d_arg_t *) p)->n1;
+    n2 = ((fft2d_arg_t *) p)->n2;
+    isgn = ((fft2d_arg_t *) p)->isgn;
+    a = ((fft2d_arg_t *) p)->a;
+    t = ((fft2d_arg_t *) p)->t;
+    ip = ((fft2d_arg_t *) p)->ip;
+    w = ((fft2d_arg_t *) p)->w;
+    if (n2 > 4 * nthread) {
+        for (j = 8 * n0; j < n2; j += 8 * nthread) {
+            for (i = 0; i < n1; i++) {
+                t[2 * i] = a[i][j];
+                t[2 * i + 1] = a[i][j + 1];
+                t[2 * n1 + 2 * i] = a[i][j + 2];
+                t[2 * n1 + 2 * i + 1] = a[i][j + 3];
+                t[4 * n1 + 2 * i] = a[i][j + 4];
+                t[4 * n1 + 2 * i + 1] = a[i][j + 5];
+                t[6 * n1 + 2 * i] = a[i][j + 6];
+                t[6 * n1 + 2 * i + 1] = a[i][j + 7];
+            }
+            cdft(2 * n1, isgn, t, ip, w);
+            cdft(2 * n1, isgn, &t[2 * n1], ip, w);
+            cdft(2 * n1, isgn, &t[4 * n1], ip, w);
+            cdft(2 * n1, isgn, &t[6 * n1], ip, w);
+            for (i = 0; i < n1; i++) {
+                a[i][j] = t[2 * i];
+                a[i][j + 1] = t[2 * i + 1];
+                a[i][j + 2] = t[2 * n1 + 2 * i];
+                a[i][j + 3] = t[2 * n1 + 2 * i + 1];
+                a[i][j + 4] = t[4 * n1 + 2 * i];
+                a[i][j + 5] = t[4 * n1 + 2 * i + 1];
+                a[i][j + 6] = t[6 * n1 + 2 * i];
+                a[i][j + 7] = t[6 * n1 + 2 * i + 1];
+            }
+        }
+    } else if (n2 == 4 * nthread) {
+        for (i = 0; i < n1; i++) {
+            t[2 * i] = a[i][4 * n0];
+            t[2 * i + 1] = a[i][4 * n0 + 1];
+            t[2 * n1 + 2 * i] = a[i][4 * n0 + 2];
+            t[2 * n1 + 2 * i + 1] = a[i][4 * n0 + 3];
+        }
+        cdft(2 * n1, isgn, t, ip, w);
+        cdft(2 * n1, isgn, &t[2 * n1], ip, w);
+        for (i = 0; i < n1; i++) {
+            a[i][4 * n0] = t[2 * i];
+            a[i][4 * n0 + 1] = t[2 * i + 1];
+            a[i][4 * n0 + 2] = t[2 * n1 + 2 * i];
+            a[i][4 * n0 + 3] = t[2 * n1 + 2 * i + 1];
+        }
+    } else if (n2 == 2 * nthread) {
+        for (i = 0; i < n1; i++) {
+            t[2 * i] = a[i][2 * n0];
+            t[2 * i + 1] = a[i][2 * n0 + 1];
+        }
+        cdft(2 * n1, isgn, t, ip, w);
+        for (i = 0; i < n1; i++) {
+            a[i][2 * n0] = t[2 * i];
+            a[i][2 * n0 + 1] = t[2 * i + 1];
+        }
+    }
+    return (void *) 0;
+}
+
+
+void *ddxt2d0_th(void *p)
+{
+    void ddct(int n, int isgn, double *a, int *ip, double *w);
+    void ddst(int n, int isgn, double *a, int *ip, double *w);
+    int nthread, n0, n1, n2, ics, isgn, *ip, i;
+    double **a, *w;
+    
+    nthread = ((fft2d_arg_t *) p)->nthread;
+    n0 = ((fft2d_arg_t *) p)->n0;
+    n1 = ((fft2d_arg_t *) p)->n1;
+    n2 = ((fft2d_arg_t *) p)->n2;
+    ics = ((fft2d_arg_t *) p)->ic;
+    isgn = ((fft2d_arg_t *) p)->isgn;
+    a = ((fft2d_arg_t *) p)->a;
+    ip = ((fft2d_arg_t *) p)->ip;
+    w = ((fft2d_arg_t *) p)->w;
+    if (ics == 0) {
+        for (i = n0; i < n1; i += nthread) {
+            ddct(n2, isgn, a[i], ip, w);
+        }
+    } else {
+        for (i = n0; i < n1; i += nthread) {
+            ddst(n2, isgn, a[i], ip, w);
+        }
+    }
+    return (void *) 0;
+}
+
+
+void *ddxt2d_th(void *p)
+{
+    void ddct(int n, int isgn, double *a, int *ip, double *w);
+    void ddst(int n, int isgn, double *a, int *ip, double *w);
+    int nthread, n0, n1, n2, ics, isgn, *ip, i, j;
+    double **a, *t, *w;
+    
+    nthread = ((fft2d_arg_t *) p)->nthread;
+    n0 = ((fft2d_arg_t *) p)->n0;
+    n1 = ((fft2d_arg_t *) p)->n1;
+    n2 = ((fft2d_arg_t *) p)->n2;
+    ics = ((fft2d_arg_t *) p)->ic;
+    isgn = ((fft2d_arg_t *) p)->isgn;
+    a = ((fft2d_arg_t *) p)->a;
+    t = ((fft2d_arg_t *) p)->t;
+    ip = ((fft2d_arg_t *) p)->ip;
+    w = ((fft2d_arg_t *) p)->w;
+    if (n2 > 2 * nthread) {
+        for (j = 4 * n0; j < n2; j += 4 * nthread) {
+            for (i = 0; i < n1; i++) {
+                t[i] = a[i][j];
+                t[n1 + i] = a[i][j + 1];
+                t[2 * n1 + i] = a[i][j + 2];
+                t[3 * n1 + i] = a[i][j + 3];
+            }
+            if (ics == 0) {
+                ddct(n1, isgn, t, ip, w);
+                ddct(n1, isgn, &t[n1], ip, w);
+                ddct(n1, isgn, &t[2 * n1], ip, w);
+                ddct(n1, isgn, &t[3 * n1], ip, w);
+            } else {
+                ddst(n1, isgn, t, ip, w);
+                ddst(n1, isgn, &t[n1], ip, w);
+                ddst(n1, isgn, &t[2 * n1], ip, w);
+                ddst(n1, isgn, &t[3 * n1], ip, w);
+            }
+            for (i = 0; i < n1; i++) {
+                a[i][j] = t[i];
+                a[i][j + 1] = t[n1 + i];
+                a[i][j + 2] = t[2 * n1 + i];
+                a[i][j + 3] = t[3 * n1 + i];
+            }
+        }
+    } else if (n2 == 2 * nthread) {
+        for (i = 0; i < n1; i++) {
+            t[i] = a[i][2 * n0];
+            t[n1 + i] = a[i][2 * n0 + 1];
+        }
+        if (ics == 0) {
+            ddct(n1, isgn, t, ip, w);
+            ddct(n1, isgn, &t[n1], ip, w);
+        } else {
+            ddst(n1, isgn, t, ip, w);
+            ddst(n1, isgn, &t[n1], ip, w);
+        }
+        for (i = 0; i < n1; i++) {
+            a[i][2 * n0] = t[i];
+            a[i][2 * n0 + 1] = t[n1 + i];
+        }
+    } else if (n2 == nthread) {
+        for (i = 0; i < n1; i++) {
+            t[i] = a[i][n0];
+        }
+        if (ics == 0) {
+            ddct(n1, isgn, t, ip, w);
+        } else {
+            ddst(n1, isgn, t, ip, w);
+        }
+        for (i = 0; i < n1; i++) {
+            a[i][n0] = t[i];
+        }
+    }
+    return (void *) 0;
+}
+#endif /* USE_FFT2D_THREADS */
+
diff --git a/third_party/tensorflow_dependencies/fft2d/fftsg2d.f b/third_party/tensorflow_dependencies/fft2d/fftsg2d.f
new file mode 100644
index 0000000..49a9d6c
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/fftsg2d.f
@@ -0,0 +1,562 @@
+! Fast Fourier/Cosine/Sine Transform
+!     dimension   :two
+!     data length :power of 2
+!     decimation  :frequency
+!     radix       :split-radix, row-column
+!     data        :inplace
+!     table       :use
+! subroutines
+!     cdft2d: Complex Discrete Fourier Transform
+!     rdft2d: Real Discrete Fourier Transform
+!     ddct2d: Discrete Cosine Transform
+!     ddst2d: Discrete Sine Transform
+! necessary package
+!     fftsg.f  : 1D-FFT package
+!
+!
+! -------- Complex DFT (Discrete Fourier Transform) --------
+!     [definition]
+!         <case1>
+!             X(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 x(j1,j2) * 
+!                            exp(2*pi*i*j1*k1/n1) * 
+!                            exp(2*pi*i*j2*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         <case2>
+!             X(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 x(j1,j2) * 
+!                            exp(-2*pi*i*j1*k1/n1) * 
+!                            exp(-2*pi*i*j2*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         (notes: sum_j=0^n-1 is a summation from j=0 to n-1)
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call cdft2d(n1max, 2*n1, n2, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call cdft2d(n1max, 2*n1, n2, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row size of the 2D array (integer)
+!         2*n1   :data length (integer)
+!                 n1 >= 1, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 1, n2 = power of 2
+!         a(0:2*n1-1,0:n2-1)
+!                :input/output data (real*8)
+!                 input data
+!                     a(2*j1,j2) = Re(x(j1,j2)), 
+!                     a(2*j1+1,j2) = Im(x(j1,j2)), 
+!                     0<=j1<n1, 0<=j2<n2
+!                 output data
+!                     a(2*k1,k2) = Re(X(k1,k2)), 
+!                     a(2*k1+1,k2) = Im(X(k1,k2)), 
+!                     0<=k1<n1, 0<=k2<n2
+!         t(0:8*n2-1)
+!                :work area (real*8)
+!                 length of t >= 8*n2
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1, n2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1/2, n2/2)
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call cdft2d(n1max, 2*n1, n2, -1, a, t, ip, w)
+!         is 
+!             call cdft2d(n1max, 2*n1, n2, 1, a, t, ip, w)
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, 2 * n1 - 1
+!                     a(j1, j2) = a(j1, j2) * (1.0d0 / n1 / n2)
+!                 end do
+!             end do
+!         .
+!
+!
+! -------- Real DFT / Inverse of Real DFT --------
+!     [definition]
+!         <case1> RDFT
+!             R(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!             I(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         <case2> IRDFT (excluding scale)
+!             a(k1,k2) = (1/2) * sum_j1=0^n1-1 sum_j2=0^n2-1
+!                            (R(j1,j2) * 
+!                            cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2) + 
+!                            I(j1,j2) * 
+!                            sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2)), 
+!                            0<=k1<n1, 0<=k2<n2
+!         (notes: R(n1-k1,n2-k2) = R(k1,k2), 
+!                 I(n1-k1,n2-k2) = -I(k1,k2), 
+!                 R(n1-k1,0) = R(k1,0), 
+!                 I(n1-k1,0) = -I(k1,0), 
+!                 R(0,n2-k2) = R(0,k2), 
+!                 I(0,n2-k2) = -I(0,k2), 
+!                 0<k1<n1, 0<k2<n2)
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call rdft2d(n1max, n1, n2, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call rdft2d(n1max, n1, n2, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row size of the 2D array (integer)
+!         n1     :data length (integer)
+!                 n1 >= 2, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 2, n2 = power of 2
+!         a(0:n1-1,0:n2-1)
+!                :input/output data (real*8)
+!                 <case1>
+!                     output data
+!                         a(2*k1,k2) = R(k1,k2) = R(n1-k1,n2-k2), 
+!                         a(2*k1+1,k2) = I(k1,k2) = -I(n1-k1,n2-k2), 
+!                             0<k1<n1/2, 0<k2<n2, 
+!                         a(2*k1,0) = R(k1,0) = R(n1-k1,0), 
+!                         a(2*k1+1,0) = I(k1,0) = -I(n1-k1,0), 
+!                             0<k1<n1/2, 
+!                         a(0,k2) = R(0,k2) = R(0,n2-k2), 
+!                         a(1,k2) = I(0,k2) = -I(0,n2-k2), 
+!                         a(1,n2-k2) = R(n1/2,k2) = R(n1/2,n2-k2), 
+!                         a(0,n2-k2) = -I(n1/2,k2) = I(n1/2,n2-k2), 
+!                             0<k2<n2/2, 
+!                         a(0,0) = R(0,0), 
+!                         a(1,0) = R(n1/2,0), 
+!                         a(0,n2/2) = R(0,n2/2), 
+!                         a(1,n2/2) = R(n1/2,n2/2)
+!                 <case2>
+!                     input data
+!                         a(2*j1,j2) = R(j1,j2) = R(n1-j1,n2-j2), 
+!                         a(2*j1+1,j2) = I(j1,j2) = -I(n1-j1,n2-j2), 
+!                             0<j1<n1/2, 0<j2<n2, 
+!                         a(2*j1,0) = R(j1,0) = R(n1-j1,0), 
+!                         a(2*j1+1,0) = I(j1,0) = -I(n1-j1,0), 
+!                             0<j1<n1/2, 
+!                         a(0,j2) = R(0,j2) = R(0,n2-j2), 
+!                         a(1,j2) = I(0,j2) = -I(0,n2-j2), 
+!                         a(1,n2-j2) = R(n1/2,j2) = R(n1/2,n2-j2), 
+!                         a(0,n2-j2) = -I(n1/2,j2) = I(n1/2,n2-j2), 
+!                             0<j2<n2/2, 
+!                         a(0,0) = R(0,0), 
+!                         a(1,0) = R(n1/2,0), 
+!                         a(0,n2/2) = R(0,n2/2), 
+!                         a(1,n2/2) = R(n1/2,n2/2)
+!                 ---- output ordering ----
+!                     call rdft2d(n1max, n1, n2, 1, a, t, ip, w)
+!                     call rdft2dsort(n1max, n1, n2, 1, a)
+!                     ! stored data is a(0:n1-1,0:n2+1):
+!                     ! a(2*k1,k2) = R(k1,k2), 
+!                     ! a(2*k1+1,k2) = I(k1,k2), 
+!                     ! 0<=k1<=n1/2, 0<=k2<n2.
+!                     ! the stored data is larger than the input data!
+!                 ---- input ordering ----
+!                     call rdft2dsort(n1max, n1, n2, -1, a)
+!                     call rdft2d(n1max, n1, n2, -1, a, t, ip, w)
+!         t(0:8*n2-1)
+!                :work area (real*8)
+!                 length of t >= 8*n2
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1/2, n2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1/4, n2/2) + n1/4
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call rdft2d(n1max, n1, n2, 1, a, t, ip, w)
+!         is 
+!             call rdft2d(n1max, n1, n2, -1, a, t, ip, w)
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, n1 - 1
+!                     a(j1, j2) = a(j1, j2) * (2.0d0 / n1 / n2)
+!                 end do
+!             end do
+!         .
+!
+!
+! -------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
+!     [definition]
+!         <case1> IDCT (excluding scale)
+!             C(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            cos(pi*j1*(k1+1/2)/n1) * 
+!                            cos(pi*j2*(k2+1/2)/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         <case2> DCT
+!             C(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            cos(pi*(j1+1/2)*k1/n1) * 
+!                            cos(pi*(j2+1/2)*k2/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call ddct2d(n1max, n1, n2, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call ddct2d(n1max, n1, n2, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row size of the 2D array (integer)
+!         n1     :data length (integer)
+!                 n1 >= 2, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 2, n2 = power of 2
+!         a(0:n1-1,0:n2-1)
+!                :input/output data (real*8)
+!                 output data
+!                     a(k1,k2) = C(k1,k2), 0<=k1<n1, 0<=k2<n2
+!         t(0:4*n2-1)
+!                :work area (real*8)
+!                 length of t >= 4*n2
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1/2, n2/2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1*3/2, n2*3/2)
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call ddct2d(n1max, n1, n2, -1, a, t, ip, w)
+!         is 
+!             do j1 = 0, n1 - 1
+!                 a(j1, 0) = a(j1, 0) * 0.5d0
+!             end do
+!             do j2 = 0, n2 - 1
+!                 a(0, j2) = a(0, j2) * 0.5d0
+!             end do
+!             call ddct2d(n1max, n1, n2, 1, a, t, ip, w)
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, n1 - 1
+!                     a(j1, j2) = a(j1, j2) * (4.0d0 / n1 / n2)
+!                 end do
+!             end do
+!         .
+!
+!
+! -------- DST (Discrete Sine Transform) / Inverse of DST --------
+!     [definition]
+!         <case1> IDST (excluding scale)
+!             S(k1,k2) = sum_j1=1^n1 sum_j2=1^n2 A(j1,j2) * 
+!                            sin(pi*j1*(k1+1/2)/n1) * 
+!                            sin(pi*j2*(k2+1/2)/n2), 
+!                            0<=k1<n1, 0<=k2<n2
+!         <case2> DST
+!             S(k1,k2) = sum_j1=0^n1-1 sum_j2=0^n2-1 a(j1,j2) * 
+!                            sin(pi*(j1+1/2)*k1/n1) * 
+!                            sin(pi*(j2+1/2)*k2/n2), 
+!                            0<k1<=n1, 0<k2<=n2
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call ddst2d(n1max, n1, n2, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call ddst2d(n1max, n1, n2, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row size of the 2D array (integer)
+!         n1     :data length (integer)
+!                 n1 >= 2, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 2, n2 = power of 2
+!         a(0:n1-1,0:n2-1)
+!                :input/output data (real*8)
+!                 <case1>
+!                     input data
+!                         a(j1,j2) = A(j1,j2), 0<j1<n1, 0<j2<n2, 
+!                         a(j1,0) = A(j1,n2), 0<j1<n1, 
+!                         a(0,j2) = A(n1,j2), 0<j2<n2, 
+!                         a(0,0) = A(n1,n2)
+!                         (i.e. A(j1,j2) = a(mod(j1,n1),mod(j2,n2)))
+!                     output data
+!                         a(k1,k2) = S(k1,k2), 0<=k1<n1, 0<=k2<n2
+!                 <case2>
+!                     output data
+!                         a(k1,k2) = S(k1,k2), 0<k1<n1, 0<k2<n2, 
+!                         a(k1,0) = S(k1,n2), 0<k1<n1, 
+!                         a(0,k2) = S(n1,k2), 0<k2<n2, 
+!                         a(0,0) = S(n1,n2)
+!                         (i.e. S(k1,k2) = a(mod(k1,n1),mod(k2,n2)))
+!         t(0:4*n2-1)
+!                :work area (real*8)
+!                 length of t >= 4*n2
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1/2, n2/2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1*3/2, n2*3/2)
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call ddst2d(n1max, n1, n2, -1, a, t, ip, w)
+!         is 
+!             do j1 = 0, n1 - 1
+!                 a(j1, 0) = a(j1, 0) * 0.5d0
+!             end do
+!             do j2 = 0, n2 - 1
+!                 a(0, j2) = a(0, j2) * 0.5d0
+!             end do
+!             call ddst2d(n1max, n1, n2, 1, a, t, ip, w)
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, n1 - 1
+!                     a(j1, j2) = a(j1, j2) * (4.0d0 / n1 / n2)
+!                 end do
+!             end do
+!         .
+!
+!
+      subroutine cdft2d(n1max, n1, n2, isgn, a, t, ip, w)
+      integer n1max, n1, n2, isgn, ip(0 : *), n, j
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), t(0 : 8 * n2 - 1), 
+     &    w(0 : *)
+      n = max(n1, 2 * n2)
+      if (n .gt. 4 * ip(0)) then
+          call makewt(n / 4, ip, w)
+      end if
+      do j = 0, n2 - 1
+          call cdft(n1, isgn, a(0, j), ip, w)
+      end do
+      call cdft2d_sub(n1max, n1, n2, isgn, a, t, ip, w)
+      end
+!
+      subroutine rdft2d(n1max, n1, n2, isgn, a, t, ip, w)
+      integer n1max, n1, n2, isgn, ip(0 : *), n, nw, nc, j
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), t(0 : 8 * n2 - 1), 
+     &    w(0 : *)
+      n = max(n1, 2 * n2)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n1 .gt. 4 * nc) then
+          nc = n1 / 4
+          call makect(nc, ip, w(nw))
+      end if
+      if (isgn .lt. 0) then
+          call rdft2d_sub(n1max, n1, n2, isgn, a)
+          call cdft2d_sub(n1max, n1, n2, isgn, a, t, ip, w)
+      end if
+      do j = 0, n2 - 1
+          call rdft(n1, isgn, a(0, j), ip, w)
+      end do
+      if (isgn .ge. 0) then
+          call cdft2d_sub(n1max, n1, n2, isgn, a, t, ip, w)
+          call rdft2d_sub(n1max, n1, n2, isgn, a)
+      end if
+      end
+!
+      subroutine rdft2dsort(n1max, n1, n2, isgn, a)
+      integer n1max, n1, n2, isgn, n2h, j
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), x, y
+      n2h = n2 / 2
+      if (isgn .lt. 0) then
+          do j = n2h + 1, n2 - 1
+              a(0, j) = a(n1 + 1, j)
+              a(1, j) = a(n1, j)
+          end do
+          a(1, 0) = a(n1, 0)
+          a(1, n2h) = a(n1, n2h)
+      else
+          do j = n2h + 1, n2 - 1
+              y = a(0, j)
+              x = a(1, j)
+              a(n1, j) = x
+              a(n1 + 1, j) = y
+              a(n1, n2 - j) = x
+              a(n1 + 1, n2 - j) = -y
+              a(0, j) = a(0, n2 - j)
+              a(1, j) = -a(1, n2 - j)
+          end do
+          a(n1, 0) = a(1, 0)
+          a(n1 + 1, 0) = 0
+          a(1, 0) = 0
+          a(n1, n2h) = a(1, n2h)
+          a(n1 + 1, n2h) = 0
+          a(1, n2h) = 0
+      end if
+      end
+!
+      subroutine ddct2d(n1max, n1, n2, isgn, a, t, ip, w)
+      integer n1max, n1, n2, isgn, ip(0 : *), n, nw, nc, j
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), t(0 : 4 * n2 - 1), 
+     &    w(0 : *)
+      n = max(n1, n2)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n .gt. nc) then
+          nc = n
+          call makect(nc, ip, w(nw))
+      end if
+      do j = 0, n2 - 1
+          call ddct(n1, isgn, a(0, j), ip, w)
+      end do
+      call ddxt2d_sub(n1max, n1, n2, 0, isgn, a, t, ip, w)
+      end
+!
+      subroutine ddst2d(n1max, n1, n2, isgn, a, t, ip, w)
+      integer n1max, n1, n2, isgn, ip(0 : *), n, nw, nc, j
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), t(0 : 4 * n2 - 1), 
+     &    w(0 : *)
+      n = max(n1, n2)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n .gt. nc) then
+          nc = n
+          call makect(nc, ip, w(nw))
+      end if
+      do j = 0, n2 - 1
+          call ddst(n1, isgn, a(0, j), ip, w)
+      end do
+      call ddxt2d_sub(n1max, n1, n2, 1, isgn, a, t, ip, w)
+      end
+!
+! -------- child routines --------
+!
+      subroutine cdft2d_sub(n1max, n1, n2, isgn, a, t, ip, w)
+      integer n1max, n1, n2, isgn, ip(0 : *), i, j
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), t(0 : 8 * n2 - 1), 
+     &    w(0 : *)
+      if (n1 .gt. 4) then
+          do i = 0, n1 - 8, 8
+              do j = 0, n2 - 1
+                  t(2 * j) = a(i, j)
+                  t(2 * j + 1) = a(i + 1, j)
+                  t(2 * n2 + 2 * j) = a(i + 2, j)
+                  t(2 * n2 + 2 * j + 1) = a(i + 3, j)
+                  t(4 * n2 + 2 * j) = a(i + 4, j)
+                  t(4 * n2 + 2 * j + 1) = a(i + 5, j)
+                  t(6 * n2 + 2 * j) = a(i + 6, j)
+                  t(6 * n2 + 2 * j + 1) = a(i + 7, j)
+              end do
+              call cdft(2 * n2, isgn, t, ip, w)
+              call cdft(2 * n2, isgn, t(2 * n2), ip, w)
+              call cdft(2 * n2, isgn, t(4 * n2), ip, w)
+              call cdft(2 * n2, isgn, t(6 * n2), ip, w)
+              do j = 0, n2 - 1
+                  a(i, j) = t(2 * j)
+                  a(i + 1, j) = t(2 * j + 1)
+                  a(i + 2, j) = t(2 * n2 + 2 * j)
+                  a(i + 3, j) = t(2 * n2 + 2 * j + 1)
+                  a(i + 4, j) = t(4 * n2 + 2 * j)
+                  a(i + 5, j) = t(4 * n2 + 2 * j + 1)
+                  a(i + 6, j) = t(6 * n2 + 2 * j)
+                  a(i + 7, j) = t(6 * n2 + 2 * j + 1)
+              end do
+          end do
+      else if (n1 .eq. 4) then
+          do j = 0, n2 - 1
+              t(2 * j) = a(0, j)
+              t(2 * j + 1) = a(1, j)
+              t(2 * n2 + 2 * j) = a(2, j)
+              t(2 * n2 + 2 * j + 1) = a(3, j)
+          end do
+          call cdft(2 * n2, isgn, t, ip, w)
+          call cdft(2 * n2, isgn, t(2 * n2), ip, w)
+          do j = 0, n2 - 1
+              a(0, j) = t(2 * j)
+              a(1, j) = t(2 * j + 1)
+              a(2, j) = t(2 * n2 + 2 * j)
+              a(3, j) = t(2 * n2 + 2 * j + 1)
+          end do
+      else if (n1 .eq. 2) then
+          do j = 0, n2 - 1
+              t(2 * j) = a(0, j)
+              t(2 * j + 1) = a(1, j)
+          end do
+          call cdft(2 * n2, isgn, t, ip, w)
+          do j = 0, n2 - 1
+              a(0, j) = t(2 * j)
+              a(1, j) = t(2 * j + 1)
+          end do
+      end if
+      end
+!
+      subroutine rdft2d_sub(n1max, n1, n2, isgn, a)
+      integer n1max, n1, n2, isgn, n2h, i, j
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), xi
+      n2h = n2 / 2
+      if (isgn .lt. 0) then
+          do i = 1, n2h - 1
+              j = n2 - i
+              xi = a(0, i) - a(0, j)
+              a(0, i) = a(0, i) + a(0, j)
+              a(0, j) = xi
+              xi = a(1, j) - a(1, i)
+              a(1, i) = a(1, i) + a(1, j)
+              a(1, j) = xi
+          end do
+      else
+          do i = 1, n2h - 1
+              j = n2 - i
+              a(0, j) = 0.5d0 * (a(0, i) - a(0, j))
+              a(0, i) = a(0, i) - a(0, j)
+              a(1, j) = 0.5d0 * (a(1, i) + a(1, j))
+              a(1, i) = a(1, i) - a(1, j)
+          end do
+      end if
+      end
+!
+      subroutine ddxt2d_sub(n1max, n1, n2, ics, isgn, a, t, 
+     &    ip, w)
+      integer n1max, n1, n2, ics, isgn, ip(0 : *), i, j
+      real*8 a(0 : n1max - 1, 0 : n2 - 1), t(0 : 4 * n2 - 1), 
+     &    w(0 : *)
+      if (n1 .gt. 2) then
+          do i = 0, n1 - 4, 4
+              do j = 0, n2 - 1
+                  t(j) = a(i, j)
+                  t(n2 + j) = a(i + 1, j)
+                  t(2 * n2 + j) = a(i + 2, j)
+                  t(3 * n2 + j) = a(i + 3, j)
+              end do
+              if (ics .eq. 0) then
+                  call ddct(n2, isgn, t, ip, w)
+                  call ddct(n2, isgn, t(n2), ip, w)
+                  call ddct(n2, isgn, t(2 * n2), ip, w)
+                  call ddct(n2, isgn, t(3 * n2), ip, w)
+              else
+                  call ddst(n2, isgn, t, ip, w)
+                  call ddst(n2, isgn, t(n2), ip, w)
+                  call ddst(n2, isgn, t(2 * n2), ip, w)
+                  call ddst(n2, isgn, t(3 * n2), ip, w)
+              end if
+              do j = 0, n2 - 1
+                  a(i, j) = t(j)
+                  a(i + 1, j) = t(n2 + j)
+                  a(i + 2, j) = t(2 * n2 + j)
+                  a(i + 3, j) = t(3 * n2 + j)
+              end do
+          end do
+      else if (n1 .eq. 2) then
+          do j = 0, n2 - 1
+              t(j) = a(0, j)
+              t(n2 + j) = a(1, j)
+          end do
+          if (ics .eq. 0) then
+              call ddct(n2, isgn, t, ip, w)
+              call ddct(n2, isgn, t(n2), ip, w)
+          else
+              call ddst(n2, isgn, t, ip, w)
+              call ddst(n2, isgn, t(n2), ip, w)
+          end if
+          do j = 0, n2 - 1
+              a(0, j) = t(j)
+              a(1, j) = t(n2 + j)
+          end do
+      end if
+      end
+!
diff --git a/third_party/tensorflow_dependencies/fft2d/fftsg3d.c b/third_party/tensorflow_dependencies/fft2d/fftsg3d.c
new file mode 100644
index 0000000..62f7daa
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/fftsg3d.c
@@ -0,0 +1,1695 @@
+/*
+Fast Fourier/Cosine/Sine Transform
+    dimension   :three
+    data length :power of 2
+    decimation  :frequency
+    radix       :split-radix, row-column
+    data        :inplace
+    table       :use
+functions
+    cdft3d: Complex Discrete Fourier Transform
+    rdft3d: Real Discrete Fourier Transform
+    ddct3d: Discrete Cosine Transform
+    ddst3d: Discrete Sine Transform
+function prototypes
+    void cdft3d(int, int, int, int, double ***, double *, int *, double *);
+    void rdft3d(int, int, int, int, double ***, double *, int *, double *);
+    void rdft3dsort(int, int, int, int, double ***);
+    void ddct3d(int, int, int, int, double ***, double *, int *, double *);
+    void ddst3d(int, int, int, int, double ***, double *, int *, double *);
+necessary package
+    fftsg.c  : 1D-FFT package
+macro definitions
+    USE_FFT3D_PTHREADS : default=not defined
+        FFT3D_MAX_THREADS     : must be 2^N, default=4
+        FFT3D_THREADS_BEGIN_N : default=65536
+    USE_FFT3D_WINTHREADS : default=not defined
+        FFT3D_MAX_THREADS     : must be 2^N, default=4
+        FFT3D_THREADS_BEGIN_N : default=131072
+
+
+-------- Complex DFT (Discrete Fourier Transform) --------
+    [definition]
+        <case1>
+            X[k1][k2][k3] = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+                                x[j1][j2][j3] * 
+                                exp(2*pi*i*j1*k1/n1) * 
+                                exp(2*pi*i*j2*k2/n2) * 
+                                exp(2*pi*i*j3*k3/n3), 
+                                0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+        <case2>
+            X[k1][k2][k3] = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+                                x[j1][j2][j3] * 
+                                exp(-2*pi*i*j1*k1/n1) * 
+                                exp(-2*pi*i*j2*k2/n2) * 
+                                exp(-2*pi*i*j3*k3/n3), 
+                                0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+        (notes: sum_j=0^n-1 is a summation from j=0 to n-1)
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            cdft3d(n1, n2, 2*n3, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            cdft3d(n1, n2, 2*n3, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 1, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 1, n2 = power of 2
+        2*n3   :data length (int)
+                n3 >= 1, n3 = power of 2
+        a[0...n1-1][0...n2-1][0...2*n3-1]
+               :input/output data (double ***)
+                input data
+                    a[j1][j2][2*j3] = Re(x[j1][j2][j3]), 
+                    a[j1][j2][2*j3+1] = Im(x[j1][j2][j3]), 
+                    0<=j1<n1, 0<=j2<n2, 0<=j3<n3
+                output data
+                    a[k1][k2][2*k3] = Re(X[k1][k2][k3]), 
+                    a[k1][k2][2*k3+1] = Im(X[k1][k2][k3]), 
+                    0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+        t[0...*]
+               :work area (double *)
+                length of t >= max(8*n1, 8*n2),        if single thread, 
+                length of t >= max(8*n1, 8*n2)*FFT3D_MAX_THREADS, 
+                                                       if multi threads, 
+                t is dynamically allocated, if t == NULL.
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1, n2, n3))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1/2, n2/2, n3/2)
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            cdft3d(n1, n2, 2*n3, -1, a, t, ip, w);
+        is 
+            cdft3d(n1, n2, 2*n3, 1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    for (j3 = 0; j3 <= 2 * n3 - 1; j3++) {
+                        a[j1][j2][j3] *= 1.0 / n1 / n2 / n3;
+                    }
+                }
+            }
+        .
+
+
+-------- Real DFT / Inverse of Real DFT --------
+    [definition]
+        <case1> RDFT
+            R[k1][k2][k3] = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+                                a[j1][j2][j3] * 
+                                cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2 + 
+                                    2*pi*j3*k3/n3), 
+                                0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+            I[k1][k2][k3] = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+                                a[j1][j2][j3] * 
+                                sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2 + 
+                                    2*pi*j3*k3/n3), 
+                                0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+        <case2> IRDFT (excluding scale)
+            a[k1][k2][k3] = (1/2) * sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+                                (R[j1][j2][j3] * 
+                                cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2 + 
+                                    2*pi*j3*k3/n3) + 
+                                I[j1][j2][j3] * 
+                                sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2 + 
+                                    2*pi*j3*k3/n3)), 
+                                0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+        (notes: R[(n1-k1)%n1][(n2-k2)%n2][(n3-k3)%n3] = R[k1][k2][k3], 
+                I[(n1-k1)%n1][(n2-k2)%n2][(n3-k3)%n3] = -I[k1][k2][k3], 
+                0<=k1<n1, 0<=k2<n2, 0<=k3<n3)
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            rdft3d(n1, n2, n3, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            rdft3d(n1, n2, n3, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 2, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 2, n2 = power of 2
+        n3     :data length (int)
+                n3 >= 2, n3 = power of 2
+        a[0...n1-1][0...n2-1][0...n3-1]
+               :input/output data (double ***)
+                <case1>
+                    output data
+                        a[k1][k2][2*k3] = R[k1][k2][k3]
+                                        = R[(n1-k1)%n1][(n2-k2)%n2][n3-k3], 
+                        a[k1][k2][2*k3+1] = I[k1][k2][k3]
+                                          = -I[(n1-k1)%n1][(n2-k2)%n2][n3-k3], 
+                            0<=k1<n1, 0<=k2<n2, 0<k3<n3/2, 
+                            (n%m : n mod m), 
+                        a[k1][k2][0] = R[k1][k2][0]
+                                     = R[(n1-k1)%n1][n2-k2][0], 
+                        a[k1][k2][1] = I[k1][k2][0]
+                                     = -I[(n1-k1)%n1][n2-k2][0], 
+                        a[k1][n2-k2][1] = R[(n1-k1)%n1][k2][n3/2]
+                                        = R[k1][n2-k2][n3/2], 
+                        a[k1][n2-k2][0] = -I[(n1-k1)%n1][k2][n3/2]
+                                        = I[k1][n2-k2][n3/2], 
+                            0<=k1<n1, 0<k2<n2/2, 
+                        a[k1][0][0] = R[k1][0][0]
+                                    = R[n1-k1][0][0], 
+                        a[k1][0][1] = I[k1][0][0]
+                                    = -I[n1-k1][0][0], 
+                        a[k1][n2/2][0] = R[k1][n2/2][0]
+                                       = R[n1-k1][n2/2][0], 
+                        a[k1][n2/2][1] = I[k1][n2/2][0]
+                                       = -I[n1-k1][n2/2][0], 
+                        a[n1-k1][0][1] = R[k1][0][n3/2]
+                                       = R[n1-k1][0][n3/2], 
+                        a[n1-k1][0][0] = -I[k1][0][n3/2]
+                                       = I[n1-k1][0][n3/2], 
+                        a[n1-k1][n2/2][1] = R[k1][n2/2][n3/2]
+                                          = R[n1-k1][n2/2][n3/2], 
+                        a[n1-k1][n2/2][0] = -I[k1][n2/2][n3/2]
+                                          = I[n1-k1][n2/2][n3/2], 
+                            0<k1<n1/2, 
+                        a[0][0][0] = R[0][0][0], 
+                        a[0][0][1] = R[0][0][n3/2], 
+                        a[0][n2/2][0] = R[0][n2/2][0], 
+                        a[0][n2/2][1] = R[0][n2/2][n3/2], 
+                        a[n1/2][0][0] = R[n1/2][0][0], 
+                        a[n1/2][0][1] = R[n1/2][0][n3/2], 
+                        a[n1/2][n2/2][0] = R[n1/2][n2/2][0], 
+                        a[n1/2][n2/2][1] = R[n1/2][n2/2][n3/2]
+                <case2>
+                    input data
+                        a[j1][j2][2*j3] = R[j1][j2][j3]
+                                        = R[(n1-j1)%n1][(n2-j2)%n2][n3-j3], 
+                        a[j1][j2][2*j3+1] = I[j1][j2][j3]
+                                          = -I[(n1-j1)%n1][(n2-j2)%n2][n3-j3], 
+                            0<=j1<n1, 0<=j2<n2, 0<j3<n3/2, 
+                        a[j1][j2][0] = R[j1][j2][0]
+                                     = R[(n1-j1)%n1][n2-j2][0], 
+                        a[j1][j2][1] = I[j1][j2][0]
+                                     = -I[(n1-j1)%n1][n2-j2][0], 
+                        a[j1][n2-j2][1] = R[(n1-j1)%n1][j2][n3/2]
+                                        = R[j1][n2-j2][n3/2], 
+                        a[j1][n2-j2][0] = -I[(n1-j1)%n1][j2][n3/2]
+                                        = I[j1][n2-j2][n3/2], 
+                            0<=j1<n1, 0<j2<n2/2, 
+                        a[j1][0][0] = R[j1][0][0]
+                                    = R[n1-j1][0][0], 
+                        a[j1][0][1] = I[j1][0][0]
+                                    = -I[n1-j1][0][0], 
+                        a[j1][n2/2][0] = R[j1][n2/2][0]
+                                       = R[n1-j1][n2/2][0], 
+                        a[j1][n2/2][1] = I[j1][n2/2][0]
+                                       = -I[n1-j1][n2/2][0], 
+                        a[n1-j1][0][1] = R[j1][0][n3/2]
+                                       = R[n1-j1][0][n3/2], 
+                        a[n1-j1][0][0] = -I[j1][0][n3/2]
+                                       = I[n1-j1][0][n3/2], 
+                        a[n1-j1][n2/2][1] = R[j1][n2/2][n3/2]
+                                          = R[n1-j1][n2/2][n3/2], 
+                        a[n1-j1][n2/2][0] = -I[j1][n2/2][n3/2]
+                                          = I[n1-j1][n2/2][n3/2], 
+                            0<j1<n1/2, 
+                        a[0][0][0] = R[0][0][0], 
+                        a[0][0][1] = R[0][0][n3/2], 
+                        a[0][n2/2][0] = R[0][n2/2][0], 
+                        a[0][n2/2][1] = R[0][n2/2][n3/2], 
+                        a[n1/2][0][0] = R[n1/2][0][0], 
+                        a[n1/2][0][1] = R[n1/2][0][n3/2], 
+                        a[n1/2][n2/2][0] = R[n1/2][n2/2][0], 
+                        a[n1/2][n2/2][1] = R[n1/2][n2/2][n3/2]
+                ---- output ordering ----
+                    rdft3d(n1, n2, n3, 1, a, t, ip, w);
+                    rdft3dsort(n1, n2, n3, 1, a);
+                    // stored data is a[0...n1-1][0...n2-1][0...n3+1]:
+                    // a[k1][k2][2*k3] = R[k1][k2][k3], 
+                    // a[k1][k2][2*k3+1] = I[k1][k2][k3], 
+                    // 0<=k1<n1, 0<=k2<n2, 0<=k3<=n3/2.
+                    // the stored data is larger than the input data!
+                ---- input ordering ----
+                    rdft3dsort(n1, n2, n3, -1, a);
+                    rdft3d(n1, n2, n3, -1, a, t, ip, w);
+        t[0...*]
+               :work area (double *)
+                length of t >= max(8*n1, 8*n2),        if single thread, 
+                length of t >= max(8*n1, 8*n2)*FFT3D_MAX_THREADS, 
+                                                       if multi threads, 
+                t is dynamically allocated, if t == NULL.
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1, n2, n3/2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1/2, n2/2, n3/4) + n3/4
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            rdft3d(n1, n2, n3, 1, a, t, ip, w);
+        is 
+            rdft3d(n1, n2, n3, -1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    for (j3 = 0; j3 <= n3 - 1; j3++) {
+                        a[j1][j2][j3] *= 2.0 / n1 / n2 / n3;
+                    }
+                }
+            }
+        .
+
+
+-------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
+    [definition]
+        <case1> IDCT (excluding scale)
+            C[k1][k2][k3] = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+                                a[j1][j2][j3] * 
+                                cos(pi*j1*(k1+1/2)/n1) * 
+                                cos(pi*j2*(k2+1/2)/n2) * 
+                                cos(pi*j3*(k3+1/2)/n3), 
+                                0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+        <case2> DCT
+            C[k1][k2][k3] = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+                                a[j1][j2][j3] * 
+                                cos(pi*(j1+1/2)*k1/n1) * 
+                                cos(pi*(j2+1/2)*k2/n2) * 
+                                cos(pi*(j3+1/2)*k3/n3), 
+                                0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            ddct3d(n1, n2, n3, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            ddct3d(n1, n2, n3, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 2, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 2, n2 = power of 2
+        n3     :data length (int)
+                n3 >= 2, n3 = power of 2
+        a[0...n1-1][0...n2-1][0...n3-1]
+               :input/output data (double ***)
+                output data
+                    a[k1][k2][k3] = C[k1][k2][k3], 
+                        0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+        t[0...*]
+               :work area (double *)
+                length of t >= max(4*n1, 4*n2),        if single thread, 
+                length of t >= max(4*n1, 4*n2)*FFT3D_MAX_THREADS, 
+                                                       if multi threads, 
+                t is dynamically allocated, if t == NULL.
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1/2, n2/2, n3/2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1*3/2, n2*3/2, n3*3/2)
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            ddct3d(n1, n2, n3, -1, a, t, ip, w);
+        is 
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    a[j1][j2][0] *= 0.5;
+                }
+                for (j3 = 0; j3 <= n3 - 1; j3++) {
+                    a[j1][0][j3] *= 0.5;
+                }
+            }
+            for (j2 = 0; j2 <= n2 - 1; j2++) {
+                for (j3 = 0; j3 <= n3 - 1; j3++) {
+                    a[0][j2][j3] *= 0.5;
+                }
+            }
+            ddct3d(n1, n2, n3, 1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    for (j3 = 0; j3 <= n3 - 1; j3++) {
+                        a[j1][j2][j3] *= 8.0 / n1 / n2 / n3;
+                    }
+                }
+            }
+        .
+
+
+-------- DST (Discrete Sine Transform) / Inverse of DST --------
+    [definition]
+        <case1> IDST (excluding scale)
+            S[k1][k2][k3] = sum_j1=1^n1 sum_j2=1^n2 sum_j3=1^n3
+                                A[j1][j2][j3] * 
+                                sin(pi*j1*(k1+1/2)/n1) * 
+                                sin(pi*j2*(k2+1/2)/n2) * 
+                                sin(pi*j3*(k3+1/2)/n3), 
+                                0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+        <case2> DST
+            S[k1][k2][k3] = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+                                a[j1][j2][j3] * 
+                                sin(pi*(j1+1/2)*k1/n1) * 
+                                sin(pi*(j2+1/2)*k2/n2) * 
+                                sin(pi*(j3+1/2)*k3/n3), 
+                                0<k1<=n1, 0<k2<=n2, 0<k3<=n3
+    [usage]
+        <case1>
+            ip[0] = 0; // first time only
+            ddst3d(n1, n2, n3, 1, a, t, ip, w);
+        <case2>
+            ip[0] = 0; // first time only
+            ddst3d(n1, n2, n3, -1, a, t, ip, w);
+    [parameters]
+        n1     :data length (int)
+                n1 >= 2, n1 = power of 2
+        n2     :data length (int)
+                n2 >= 2, n2 = power of 2
+        n3     :data length (int)
+                n3 >= 2, n3 = power of 2
+        a[0...n1-1][0...n2-1][0...n3-1]
+               :input/output data (double ***)
+                <case1>
+                    input data
+                        a[j1%n1][j2%n2][j3%n3] = A[j1][j2][j3], 
+                            0<j1<=n1, 0<j2<=n2, 0<j3<=n3, 
+                            (n%m : n mod m)
+                    output data
+                        a[k1][k2][k3] = S[k1][k2][k3], 
+                            0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+                <case2>
+                    output data
+                        a[k1%n1][k2%n2][k3%n3] = S[k1][k2][k3], 
+                            0<k1<=n1, 0<k2<=n2, 0<k3<=n3
+        t[0...*]
+               :work area (double *)
+                length of t >= max(4*n1, 4*n2),        if single thread, 
+                length of t >= max(4*n1, 4*n2)*FFT3D_MAX_THREADS, 
+                                                       if multi threads, 
+                t is dynamically allocated, if t == NULL.
+        ip[0...*]
+               :work area for bit reversal (int *)
+                length of ip >= 2+sqrt(n)
+                (n = max(n1/2, n2/2, n3/2))
+                ip[0],ip[1] are pointers of the cos/sin table.
+        w[0...*]
+               :cos/sin table (double *)
+                length of w >= max(n1*3/2, n2*3/2, n3*3/2)
+                w[],ip[] are initialized if ip[0] == 0.
+    [remark]
+        Inverse of 
+            ddst3d(n1, n2, n3, -1, a, t, ip, w);
+        is 
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    a[j1][j2][0] *= 0.5;
+                }
+                for (j3 = 0; j3 <= n3 - 1; j3++) {
+                    a[j1][0][j3] *= 0.5;
+                }
+            }
+            for (j2 = 0; j2 <= n2 - 1; j2++) {
+                for (j3 = 0; j3 <= n3 - 1; j3++) {
+                    a[0][j2][j3] *= 0.5;
+                }
+            }
+            ddst3d(n1, n2, n3, 1, a, t, ip, w);
+            for (j1 = 0; j1 <= n1 - 1; j1++) {
+                for (j2 = 0; j2 <= n2 - 1; j2++) {
+                    for (j3 = 0; j3 <= n3 - 1; j3++) {
+                        a[j1][j2][j3] *= 8.0 / n1 / n2 / n3;
+                    }
+                }
+            }
+        .
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#define fft3d_alloc_error_check(p) { \
+    if ((p) == NULL) { \
+        fprintf(stderr, "fft3d memory allocation error\n"); \
+        exit(1); \
+    } \
+}
+
+
+#ifdef USE_FFT3D_PTHREADS
+#define USE_FFT3D_THREADS
+#ifndef FFT3D_MAX_THREADS
+#define FFT3D_MAX_THREADS 4
+#endif
+#ifndef FFT3D_THREADS_BEGIN_N
+#define FFT3D_THREADS_BEGIN_N 65536
+#endif
+#include <pthread.h>
+#define fft3d_thread_t pthread_t
+#define fft3d_thread_create(thp,func,argp) { \
+    if (pthread_create(thp, NULL, func, (void *) (argp)) != 0) { \
+        fprintf(stderr, "fft3d thread error\n"); \
+        exit(1); \
+    } \
+}
+#define fft3d_thread_wait(th) { \
+    if (pthread_join(th, NULL) != 0) { \
+        fprintf(stderr, "fft3d thread error\n"); \
+        exit(1); \
+    } \
+}
+#endif /* USE_FFT3D_PTHREADS */
+
+
+#ifdef USE_FFT3D_WINTHREADS
+#define USE_FFT3D_THREADS
+#ifndef FFT3D_MAX_THREADS
+#define FFT3D_MAX_THREADS 4
+#endif
+#ifndef FFT3D_THREADS_BEGIN_N
+#define FFT3D_THREADS_BEGIN_N 131072
+#endif
+#include <windows.h>
+#define fft3d_thread_t HANDLE
+#define fft3d_thread_create(thp,func,argp) { \
+    DWORD thid; \
+    *(thp) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) (func), (LPVOID) (argp), 0, &thid); \
+    if (*(thp) == 0) { \
+        fprintf(stderr, "fft3d thread error\n"); \
+        exit(1); \
+    } \
+}
+#define fft3d_thread_wait(th) { \
+    WaitForSingleObject(th, INFINITE); \
+    CloseHandle(th); \
+}
+#endif /* USE_FFT3D_WINTHREADS */
+
+
+void cdft3d(int n1, int n2, int n3, int isgn, double ***a, 
+    double *t, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void xdft3da_sub(int n1, int n2, int n3, int icr, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+    void cdft3db_sub(int n1, int n2, int n3, int isgn, double ***a, 
+        double *t, int *ip, double *w);
+#ifdef USE_FFT3D_THREADS
+    void xdft3da_subth(int n1, int n2, int n3, int icr, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+    void cdft3db_subth(int n1, int n2, int n3, int isgn, double ***a, 
+        double *t, int *ip, double *w);
+#endif /* USE_FFT3D_THREADS */
+    int n, itnull, nt;
+    
+    n = n1;
+    if (n < n2) {
+        n = n2;
+    }
+    n <<= 1;
+    if (n < n3) {
+        n = n3;
+    }
+    if (n > (ip[0] << 2)) {
+        makewt(n >> 2, ip, w);
+    }
+    itnull = 0;
+    if (t == NULL) {
+        itnull = 1;
+        nt = n1;
+        if (nt < n2) {
+            nt = n2;
+        }
+        nt *= 8;
+#ifdef USE_FFT3D_THREADS
+        nt *= FFT3D_MAX_THREADS;
+#endif /* USE_FFT3D_THREADS */
+        if (n3 == 4) {
+            nt >>= 1;
+        } else if (n3 < 4) {
+            nt >>= 2;
+        }
+        t = (double *) malloc(sizeof(double) * nt);
+        fft3d_alloc_error_check(t);
+    }
+#ifdef USE_FFT3D_THREADS
+    if ((double) n1 * n2 * n3 >= (double) FFT3D_THREADS_BEGIN_N) {
+        xdft3da_subth(n1, n2, n3, 0, isgn, a, t, ip, w);
+        cdft3db_subth(n1, n2, n3, isgn, a, t, ip, w);
+    } else 
+#endif /* USE_FFT3D_THREADS */
+    {
+        xdft3da_sub(n1, n2, n3, 0, isgn, a, t, ip, w);
+        cdft3db_sub(n1, n2, n3, isgn, a, t, ip, w);
+    }
+    if (itnull != 0) {
+        free(t);
+    }
+}
+
+
+void rdft3d(int n1, int n2, int n3, int isgn, double ***a, 
+    double *t, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void xdft3da_sub(int n1, int n2, int n3, int icr, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+    void cdft3db_sub(int n1, int n2, int n3, int isgn, double ***a, 
+        double *t, int *ip, double *w);
+    void rdft3d_sub(int n1, int n2, int n3, int isgn, double ***a);
+#ifdef USE_FFT3D_THREADS
+    void xdft3da_subth(int n1, int n2, int n3, int icr, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+    void cdft3db_subth(int n1, int n2, int n3, int isgn, double ***a, 
+        double *t, int *ip, double *w);
+#endif /* USE_FFT3D_THREADS */
+    int n, nw, nc, itnull, nt;
+    
+    n = n1;
+    if (n < n2) {
+        n = n2;
+    }
+    n <<= 1;
+    if (n < n3) {
+        n = n3;
+    }
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n3 > (nc << 2)) {
+        nc = n3 >> 2;
+        makect(nc, ip, w + nw);
+    }
+    itnull = 0;
+    if (t == NULL) {
+        itnull = 1;
+        nt = n1;
+        if (nt < n2) {
+            nt = n2;
+        }
+        nt *= 8;
+#ifdef USE_FFT3D_THREADS
+        nt *= FFT3D_MAX_THREADS;
+#endif /* USE_FFT3D_THREADS */
+        if (n3 == 4) {
+            nt >>= 1;
+        } else if (n3 < 4) {
+            nt >>= 2;
+        }
+        t = (double *) malloc(sizeof(double) * nt);
+        fft3d_alloc_error_check(t);
+    }
+#ifdef USE_FFT3D_THREADS
+    if ((double) n1 * n2 * n3 >= (double) FFT3D_THREADS_BEGIN_N) {
+        if (isgn < 0) {
+            rdft3d_sub(n1, n2, n3, isgn, a);
+            cdft3db_subth(n1, n2, n3, isgn, a, t, ip, w);
+        }
+        xdft3da_subth(n1, n2, n3, 1, isgn, a, t, ip, w);
+        if (isgn >= 0) {
+            cdft3db_subth(n1, n2, n3, isgn, a, t, ip, w);
+            rdft3d_sub(n1, n2, n3, isgn, a);
+        }
+    } else 
+#endif /* USE_FFT3D_THREADS */
+    {
+        if (isgn < 0) {
+            rdft3d_sub(n1, n2, n3, isgn, a);
+            cdft3db_sub(n1, n2, n3, isgn, a, t, ip, w);
+        }
+        xdft3da_sub(n1, n2, n3, 1, isgn, a, t, ip, w);
+        if (isgn >= 0) {
+            cdft3db_sub(n1, n2, n3, isgn, a, t, ip, w);
+            rdft3d_sub(n1, n2, n3, isgn, a);
+        }
+    }
+    if (itnull != 0) {
+        free(t);
+    }
+}
+
+
+void rdft3dsort(int n1, int n2, int n3, int isgn, double ***a)
+{
+    int n1h, n2h, i, j;
+    double x, y;
+    
+    n1h = n1 >> 1;
+    n2h = n2 >> 1;
+    if (isgn < 0) {
+        for (i = 0; i < n1; i++) {
+            for (j = n2h + 1; j < n2; j++) {
+                a[i][j][0] = a[i][j][n3 + 1];
+                a[i][j][1] = a[i][j][n3];
+            }
+        }
+        for (i = n1h + 1; i < n1; i++) {
+            a[i][0][0] = a[i][0][n3 + 1];
+            a[i][0][1] = a[i][0][n3];
+            a[i][n2h][0] = a[i][n2h][n3 + 1];
+            a[i][n2h][1] = a[i][n2h][n3];
+        }
+        a[0][0][1] = a[0][0][n3];
+        a[0][n2h][1] = a[0][n2h][n3];
+        a[n1h][0][1] = a[n1h][0][n3];
+        a[n1h][n2h][1] = a[n1h][n2h][n3];
+    } else {
+        for (j = n2h + 1; j < n2; j++) {
+            y = a[0][j][0];
+            x = a[0][j][1];
+            a[0][j][n3] = x;
+            a[0][j][n3 + 1] = y;
+            a[0][n2 - j][n3] = x;
+            a[0][n2 - j][n3 + 1] = -y;
+            a[0][j][0] = a[0][n2 - j][0];
+            a[0][j][1] = -a[0][n2 - j][1];
+        }
+        for (i = 1; i < n1; i++) {
+            for (j = n2h + 1; j < n2; j++) {
+                y = a[i][j][0];
+                x = a[i][j][1];
+                a[i][j][n3] = x;
+                a[i][j][n3 + 1] = y;
+                a[n1 - i][n2 - j][n3] = x;
+                a[n1 - i][n2 - j][n3 + 1] = -y;
+                a[i][j][0] = a[n1 - i][n2 - j][0];
+                a[i][j][1] = -a[n1 - i][n2 - j][1];
+            }
+        }
+        for (i = n1h + 1; i < n1; i++) {
+            y = a[i][0][0];
+            x = a[i][0][1];
+            a[i][0][n3] = x;
+            a[i][0][n3 + 1] = y;
+            a[n1 - i][0][n3] = x;
+            a[n1 - i][0][n3 + 1] = -y;
+            a[i][0][0] = a[n1 - i][0][0];
+            a[i][0][1] = -a[n1 - i][0][1];
+            y = a[i][n2h][0];
+            x = a[i][n2h][1];
+            a[i][n2h][n3] = x;
+            a[i][n2h][n3 + 1] = y;
+            a[n1 - i][n2h][n3] = x;
+            a[n1 - i][n2h][n3 + 1] = -y;
+            a[i][n2h][0] = a[n1 - i][n2h][0];
+            a[i][n2h][1] = -a[n1 - i][n2h][1];
+        }
+        a[0][0][n3] = a[0][0][1];
+        a[0][0][n3 + 1] = 0;
+        a[0][0][1] = 0;
+        a[0][n2h][n3] = a[0][n2h][1];
+        a[0][n2h][n3 + 1] = 0;
+        a[0][n2h][1] = 0;
+        a[n1h][0][n3] = a[n1h][0][1];
+        a[n1h][0][n3 + 1] = 0;
+        a[n1h][0][1] = 0;
+        a[n1h][n2h][n3] = a[n1h][n2h][1];
+        a[n1h][n2h][n3 + 1] = 0;
+        a[n1h][n2h][1] = 0;
+    }
+}
+
+
+void ddct3d(int n1, int n2, int n3, int isgn, double ***a, 
+    double *t, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void ddxt3da_sub(int n1, int n2, int n3, int ics, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+    void ddxt3db_sub(int n1, int n2, int n3, int ics, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+#ifdef USE_FFT3D_THREADS
+    void ddxt3da_subth(int n1, int n2, int n3, int ics, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+    void ddxt3db_subth(int n1, int n2, int n3, int ics, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+#endif /* USE_FFT3D_THREADS */
+    int n, nw, nc, itnull, nt;
+    
+    n = n1;
+    if (n < n2) {
+        n = n2;
+    }
+    if (n < n3) {
+        n = n3;
+    }
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n > nc) {
+        nc = n;
+        makect(nc, ip, w + nw);
+    }
+    itnull = 0;
+    if (t == NULL) {
+        itnull = 1;
+        nt = n1;
+        if (nt < n2) {
+            nt = n2;
+        }
+        nt *= 4;
+#ifdef USE_FFT3D_THREADS
+        nt *= FFT3D_MAX_THREADS;
+#endif /* USE_FFT3D_THREADS */
+        if (n3 == 2) {
+            nt >>= 1;
+        }
+        t = (double *) malloc(sizeof(double) * nt);
+        fft3d_alloc_error_check(t);
+    }
+#ifdef USE_FFT3D_THREADS
+    if ((double) n1 * n2 * n3 >= (double) FFT3D_THREADS_BEGIN_N) {
+        ddxt3da_subth(n1, n2, n3, 0, isgn, a, t, ip, w);
+        ddxt3db_subth(n1, n2, n3, 0, isgn, a, t, ip, w);
+    } else 
+#endif /* USE_FFT3D_THREADS */
+    {
+        ddxt3da_sub(n1, n2, n3, 0, isgn, a, t, ip, w);
+        ddxt3db_sub(n1, n2, n3, 0, isgn, a, t, ip, w);
+    }
+    if (itnull != 0) {
+        free(t);
+    }
+}
+
+
+void ddst3d(int n1, int n2, int n3, int isgn, double ***a, 
+    double *t, int *ip, double *w)
+{
+    void makewt(int nw, int *ip, double *w);
+    void makect(int nc, int *ip, double *c);
+    void ddxt3da_sub(int n1, int n2, int n3, int ics, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+    void ddxt3db_sub(int n1, int n2, int n3, int ics, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+#ifdef USE_FFT3D_THREADS
+    void ddxt3da_subth(int n1, int n2, int n3, int ics, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+    void ddxt3db_subth(int n1, int n2, int n3, int ics, int isgn, 
+        double ***a, double *t, int *ip, double *w);
+#endif /* USE_FFT3D_THREADS */
+    int n, nw, nc, itnull, nt;
+    
+    n = n1;
+    if (n < n2) {
+        n = n2;
+    }
+    if (n < n3) {
+        n = n3;
+    }
+    nw = ip[0];
+    if (n > (nw << 2)) {
+        nw = n >> 2;
+        makewt(nw, ip, w);
+    }
+    nc = ip[1];
+    if (n > nc) {
+        nc = n;
+        makect(nc, ip, w + nw);
+    }
+    itnull = 0;
+    if (t == NULL) {
+        itnull = 1;
+        nt = n1;
+        if (nt < n2) {
+            nt = n2;
+        }
+        nt *= 4;
+#ifdef USE_FFT3D_THREADS
+        nt *= FFT3D_MAX_THREADS;
+#endif /* USE_FFT3D_THREADS */
+        if (n3 == 2) {
+            nt >>= 1;
+        }
+        t = (double *) malloc(sizeof(double) * nt);
+        fft3d_alloc_error_check(t);
+    }
+#ifdef USE_FFT3D_THREADS
+    if ((double) n1 * n2 * n3 >= (double) FFT3D_THREADS_BEGIN_N) {
+        ddxt3da_subth(n1, n2, n3, 1, isgn, a, t, ip, w);
+        ddxt3db_subth(n1, n2, n3, 1, isgn, a, t, ip, w);
+    } else 
+#endif /* USE_FFT3D_THREADS */
+    {
+        ddxt3da_sub(n1, n2, n3, 1, isgn, a, t, ip, w);
+        ddxt3db_sub(n1, n2, n3, 1, isgn, a, t, ip, w);
+    }
+    if (itnull != 0) {
+        free(t);
+    }
+}
+
+
+/* -------- child routines -------- */
+
+
+void xdft3da_sub(int n1, int n2, int n3, int icr, int isgn, 
+    double ***a, double *t, int *ip, double *w)
+{
+    void cdft(int n, int isgn, double *a, int *ip, double *w);
+    void rdft(int n, int isgn, double *a, int *ip, double *w);
+    int i, j, k;
+    
+    for (i = 0; i < n1; i++) {
+        if (icr == 0) {
+            for (j = 0; j < n2; j++) {
+                cdft(n3, isgn, a[i][j], ip, w);
+            }
+        } else if (isgn >= 0) {
+            for (j = 0; j < n2; j++) {
+                rdft(n3, isgn, a[i][j], ip, w);
+            }
+        }
+        if (n3 > 4) {
+            for (k = 0; k < n3; k += 8) {
+                for (j = 0; j < n2; j++) {
+                    t[2 * j] = a[i][j][k];
+                    t[2 * j + 1] = a[i][j][k + 1];
+                    t[2 * n2 + 2 * j] = a[i][j][k + 2];
+                    t[2 * n2 + 2 * j + 1] = a[i][j][k + 3];
+                    t[4 * n2 + 2 * j] = a[i][j][k + 4];
+                    t[4 * n2 + 2 * j + 1] = a[i][j][k + 5];
+                    t[6 * n2 + 2 * j] = a[i][j][k + 6];
+                    t[6 * n2 + 2 * j + 1] = a[i][j][k + 7];
+                }
+                cdft(2 * n2, isgn, t, ip, w);
+                cdft(2 * n2, isgn, &t[2 * n2], ip, w);
+                cdft(2 * n2, isgn, &t[4 * n2], ip, w);
+                cdft(2 * n2, isgn, &t[6 * n2], ip, w);
+                for (j = 0; j < n2; j++) {
+                    a[i][j][k] = t[2 * j];
+                    a[i][j][k + 1] = t[2 * j + 1];
+                    a[i][j][k + 2] = t[2 * n2 + 2 * j];
+                    a[i][j][k + 3] = t[2 * n2 + 2 * j + 1];
+                    a[i][j][k + 4] = t[4 * n2 + 2 * j];
+                    a[i][j][k + 5] = t[4 * n2 + 2 * j + 1];
+                    a[i][j][k + 6] = t[6 * n2 + 2 * j];
+                    a[i][j][k + 7] = t[6 * n2 + 2 * j + 1];
+                }
+            }
+        } else if (n3 == 4) {
+            for (j = 0; j < n2; j++) {
+                t[2 * j] = a[i][j][0];
+                t[2 * j + 1] = a[i][j][1];
+                t[2 * n2 + 2 * j] = a[i][j][2];
+                t[2 * n2 + 2 * j + 1] = a[i][j][3];
+            }
+            cdft(2 * n2, isgn, t, ip, w);
+            cdft(2 * n2, isgn, &t[2 * n2], ip, w);
+            for (j = 0; j < n2; j++) {
+                a[i][j][0] = t[2 * j];
+                a[i][j][1] = t[2 * j + 1];
+                a[i][j][2] = t[2 * n2 + 2 * j];
+                a[i][j][3] = t[2 * n2 + 2 * j + 1];
+            }
+        } else if (n3 == 2) {
+            for (j = 0; j < n2; j++) {
+                t[2 * j] = a[i][j][0];
+                t[2 * j + 1] = a[i][j][1];
+            }
+            cdft(2 * n2, isgn, t, ip, w);
+            for (j = 0; j < n2; j++) {
+                a[i][j][0] = t[2 * j];
+                a[i][j][1] = t[2 * j + 1];
+            }
+        }
+        if (icr != 0 && isgn < 0) {
+            for (j = 0; j < n2; j++) {
+                rdft(n3, isgn, a[i][j], ip, w);
+            }
+        }
+    }
+}
+
+
+void cdft3db_sub(int n1, int n2, int n3, int isgn, double ***a, 
+    double *t, int *ip, double *w)
+{
+    void cdft(int n, int isgn, double *a, int *ip, double *w);
+    int i, j, k;
+    
+    if (n3 > 4) {
+        for (j = 0; j < n2; j++) {
+            for (k = 0; k < n3; k += 8) {
+                for (i = 0; i < n1; i++) {
+                    t[2 * i] = a[i][j][k];
+                    t[2 * i + 1] = a[i][j][k + 1];
+                    t[2 * n1 + 2 * i] = a[i][j][k + 2];
+                    t[2 * n1 + 2 * i + 1] = a[i][j][k + 3];
+                    t[4 * n1 + 2 * i] = a[i][j][k + 4];
+                    t[4 * n1 + 2 * i + 1] = a[i][j][k + 5];
+                    t[6 * n1 + 2 * i] = a[i][j][k + 6];
+                    t[6 * n1 + 2 * i + 1] = a[i][j][k + 7];
+                }
+                cdft(2 * n1, isgn, t, ip, w);
+                cdft(2 * n1, isgn, &t[2 * n1], ip, w);
+                cdft(2 * n1, isgn, &t[4 * n1], ip, w);
+                cdft(2 * n1, isgn, &t[6 * n1], ip, w);
+                for (i = 0; i < n1; i++) {
+                    a[i][j][k] = t[2 * i];
+                    a[i][j][k + 1] = t[2 * i + 1];
+                    a[i][j][k + 2] = t[2 * n1 + 2 * i];
+                    a[i][j][k + 3] = t[2 * n1 + 2 * i + 1];
+                    a[i][j][k + 4] = t[4 * n1 + 2 * i];
+                    a[i][j][k + 5] = t[4 * n1 + 2 * i + 1];
+                    a[i][j][k + 6] = t[6 * n1 + 2 * i];
+                    a[i][j][k + 7] = t[6 * n1 + 2 * i + 1];
+                }
+            }
+        }
+    } else if (n3 == 4) {
+        for (j = 0; j < n2; j++) {
+            for (i = 0; i < n1; i++) {
+                t[2 * i] = a[i][j][0];
+                t[2 * i + 1] = a[i][j][1];
+                t[2 * n1 + 2 * i] = a[i][j][2];
+                t[2 * n1 + 2 * i + 1] = a[i][j][3];
+            }
+            cdft(2 * n1, isgn, t, ip, w);
+            cdft(2 * n1, isgn, &t[2 * n1], ip, w);
+            for (i = 0; i < n1; i++) {
+                a[i][j][0] = t[2 * i];
+                a[i][j][1] = t[2 * i + 1];
+                a[i][j][2] = t[2 * n1 + 2 * i];
+                a[i][j][3] = t[2 * n1 + 2 * i + 1];
+            }
+        }
+    } else if (n3 == 2) {
+        for (j = 0; j < n2; j++) {
+            for (i = 0; i < n1; i++) {
+                t[2 * i] = a[i][j][0];
+                t[2 * i + 1] = a[i][j][1];
+            }
+            cdft(2 * n1, isgn, t, ip, w);
+            for (i = 0; i < n1; i++) {
+                a[i][j][0] = t[2 * i];
+                a[i][j][1] = t[2 * i + 1];
+            }
+        }
+    }
+}
+
+
+void rdft3d_sub(int n1, int n2, int n3, int isgn, double ***a)
+{
+    int n1h, n2h, i, j, k, l;
+    double xi;
+    
+    n1h = n1 >> 1;
+    n2h = n2 >> 1;
+    if (isgn < 0) {
+        for (i = 1; i < n1h; i++) {
+            j = n1 - i;
+            xi = a[i][0][0] - a[j][0][0];
+            a[i][0][0] += a[j][0][0];
+            a[j][0][0] = xi;
+            xi = a[j][0][1] - a[i][0][1];
+            a[i][0][1] += a[j][0][1];
+            a[j][0][1] = xi;
+            xi = a[i][n2h][0] - a[j][n2h][0];
+            a[i][n2h][0] += a[j][n2h][0];
+            a[j][n2h][0] = xi;
+            xi = a[j][n2h][1] - a[i][n2h][1];
+            a[i][n2h][1] += a[j][n2h][1];
+            a[j][n2h][1] = xi;
+            for (k = 1; k < n2h; k++) {
+                l = n2 - k;
+                xi = a[i][k][0] - a[j][l][0];
+                a[i][k][0] += a[j][l][0];
+                a[j][l][0] = xi;
+                xi = a[j][l][1] - a[i][k][1];
+                a[i][k][1] += a[j][l][1];
+                a[j][l][1] = xi;
+                xi = a[j][k][0] - a[i][l][0];
+                a[j][k][0] += a[i][l][0];
+                a[i][l][0] = xi;
+                xi = a[i][l][1] - a[j][k][1];
+                a[j][k][1] += a[i][l][1];
+                a[i][l][1] = xi;
+            }
+        }
+        for (k = 1; k < n2h; k++) {
+            l = n2 - k;
+            xi = a[0][k][0] - a[0][l][0];
+            a[0][k][0] += a[0][l][0];
+            a[0][l][0] = xi;
+            xi = a[0][l][1] - a[0][k][1];
+            a[0][k][1] += a[0][l][1];
+            a[0][l][1] = xi;
+            xi = a[n1h][k][0] - a[n1h][l][0];
+            a[n1h][k][0] += a[n1h][l][0];
+            a[n1h][l][0] = xi;
+            xi = a[n1h][l][1] - a[n1h][k][1];
+            a[n1h][k][1] += a[n1h][l][1];
+            a[n1h][l][1] = xi;
+        }
+    } else {
+        for (i = 1; i < n1h; i++) {
+            j = n1 - i;
+            a[j][0][0] = 0.5 * (a[i][0][0] - a[j][0][0]);
+            a[i][0][0] -= a[j][0][0];
+            a[j][0][1] = 0.5 * (a[i][0][1] + a[j][0][1]);
+            a[i][0][1] -= a[j][0][1];
+            a[j][n2h][0] = 0.5 * (a[i][n2h][0] - a[j][n2h][0]);
+            a[i][n2h][0] -= a[j][n2h][0];
+            a[j][n2h][1] = 0.5 * (a[i][n2h][1] + a[j][n2h][1]);
+            a[i][n2h][1] -= a[j][n2h][1];
+            for (k = 1; k < n2h; k++) {
+                l = n2 - k;
+                a[j][l][0] = 0.5 * (a[i][k][0] - a[j][l][0]);
+                a[i][k][0] -= a[j][l][0];
+                a[j][l][1] = 0.5 * (a[i][k][1] + a[j][l][1]);
+                a[i][k][1] -= a[j][l][1];
+                a[i][l][0] = 0.5 * (a[j][k][0] - a[i][l][0]);
+                a[j][k][0] -= a[i][l][0];
+                a[i][l][1] = 0.5 * (a[j][k][1] + a[i][l][1]);
+                a[j][k][1] -= a[i][l][1];
+            }
+        }
+        for (k = 1; k < n2h; k++) {
+            l = n2 - k;
+            a[0][l][0] = 0.5 * (a[0][k][0] - a[0][l][0]);
+            a[0][k][0] -= a[0][l][0];
+            a[0][l][1] = 0.5 * (a[0][k][1] + a[0][l][1]);
+            a[0][k][1] -= a[0][l][1];
+            a[n1h][l][0] = 0.5 * (a[n1h][k][0] - a[n1h][l][0]);
+            a[n1h][k][0] -= a[n1h][l][0];
+            a[n1h][l][1] = 0.5 * (a[n1h][k][1] + a[n1h][l][1]);
+            a[n1h][k][1] -= a[n1h][l][1];
+        }
+    }
+}
+
+
+void ddxt3da_sub(int n1, int n2, int n3, int ics, int isgn, 
+    double ***a, double *t, int *ip, double *w)
+{
+    void ddct(int n, int isgn, double *a, int *ip, double *w);
+    void ddst(int n, int isgn, double *a, int *ip, double *w);
+    int i, j, k;
+    
+    for (i = 0; i < n1; i++) {
+        if (ics == 0) {
+            for (j = 0; j < n2; j++) {
+                ddct(n3, isgn, a[i][j], ip, w);
+            }
+        } else {
+            for (j = 0; j < n2; j++) {
+                ddst(n3, isgn, a[i][j], ip, w);
+            }
+        }
+        if (n3 > 2) {
+            for (k = 0; k < n3; k += 4) {
+                for (j = 0; j < n2; j++) {
+                    t[j] = a[i][j][k];
+                    t[n2 + j] = a[i][j][k + 1];
+                    t[2 * n2 + j] = a[i][j][k + 2];
+                    t[3 * n2 + j] = a[i][j][k + 3];
+                }
+                if (ics == 0) {
+                    ddct(n2, isgn, t, ip, w);
+                    ddct(n2, isgn, &t[n2], ip, w);
+                    ddct(n2, isgn, &t[2 * n2], ip, w);
+                    ddct(n2, isgn, &t[3 * n2], ip, w);
+                } else {
+                    ddst(n2, isgn, t, ip, w);
+                    ddst(n2, isgn, &t[n2], ip, w);
+                    ddst(n2, isgn, &t[2 * n2], ip, w);
+                    ddst(n2, isgn, &t[3 * n2], ip, w);
+                }
+                for (j = 0; j < n2; j++) {
+                    a[i][j][k] = t[j];
+                    a[i][j][k + 1] = t[n2 + j];
+                    a[i][j][k + 2] = t[2 * n2 + j];
+                    a[i][j][k + 3] = t[3 * n2 + j];
+                }
+            }
+        } else if (n3 == 2) {
+            for (j = 0; j < n2; j++) {
+                t[j] = a[i][j][0];
+                t[n2 + j] = a[i][j][1];
+            }
+            if (ics == 0) {
+                ddct(n2, isgn, t, ip, w);
+                ddct(n2, isgn, &t[n2], ip, w);
+            } else {
+                ddst(n2, isgn, t, ip, w);
+                ddst(n2, isgn, &t[n2], ip, w);
+            }
+            for (j = 0; j < n2; j++) {
+                a[i][j][0] = t[j];
+                a[i][j][1] = t[n2 + j];
+            }
+        }
+    }
+}
+
+
+void ddxt3db_sub(int n1, int n2, int n3, int ics, int isgn, 
+    double ***a, double *t, int *ip, double *w)
+{
+    void ddct(int n, int isgn, double *a, int *ip, double *w);
+    void ddst(int n, int isgn, double *a, int *ip, double *w);
+    int i, j, k;
+    
+    if (n3 > 2) {
+        for (j = 0; j < n2; j++) {
+            for (k = 0; k < n3; k += 4) {
+                for (i = 0; i < n1; i++) {
+                    t[i] = a[i][j][k];
+                    t[n1 + i] = a[i][j][k + 1];
+                    t[2 * n1 + i] = a[i][j][k + 2];
+                    t[3 * n1 + i] = a[i][j][k + 3];
+                }
+                if (ics == 0) {
+                    ddct(n1, isgn, t, ip, w);
+                    ddct(n1, isgn, &t[n1], ip, w);
+                    ddct(n1, isgn, &t[2 * n1], ip, w);
+                    ddct(n1, isgn, &t[3 * n1], ip, w);
+                } else {
+                    ddst(n1, isgn, t, ip, w);
+                    ddst(n1, isgn, &t[n1], ip, w);
+                    ddst(n1, isgn, &t[2 * n1], ip, w);
+                    ddst(n1, isgn, &t[3 * n1], ip, w);
+                }
+                for (i = 0; i < n1; i++) {
+                    a[i][j][k] = t[i];
+                    a[i][j][k + 1] = t[n1 + i];
+                    a[i][j][k + 2] = t[2 * n1 + i];
+                    a[i][j][k + 3] = t[3 * n1 + i];
+                }
+            }
+        }
+    } else if (n3 == 2) {
+        for (j = 0; j < n2; j++) {
+            for (i = 0; i < n1; i++) {
+                t[i] = a[i][j][0];
+                t[n1 + i] = a[i][j][1];
+            }
+            if (ics == 0) {
+                ddct(n1, isgn, t, ip, w);
+                ddct(n1, isgn, &t[n1], ip, w);
+            } else {
+                ddst(n1, isgn, t, ip, w);
+                ddst(n1, isgn, &t[n1], ip, w);
+            }
+            for (i = 0; i < n1; i++) {
+                a[i][j][0] = t[i];
+                a[i][j][1] = t[n1 + i];
+            }
+        }
+    }
+}
+
+
+#ifdef USE_FFT3D_THREADS
+struct fft3d_arg_st {
+    int nthread;
+    int n0;
+    int n1;
+    int n2;
+    int n3;
+    int ic;
+    int isgn;
+    double ***a;
+    double *t;
+    int *ip;
+    double *w;
+};
+typedef struct fft3d_arg_st fft3d_arg_t;
+
+
+void xdft3da_subth(int n1, int n2, int n3, int icr, int isgn, 
+    double ***a, double *t, int *ip, double *w)
+{
+    void *xdft3da_th(void *p);
+    fft3d_thread_t th[FFT3D_MAX_THREADS];
+    fft3d_arg_t ag[FFT3D_MAX_THREADS];
+    int nthread, nt, i;
+    
+    nthread = FFT3D_MAX_THREADS;
+    if (nthread > n1) {
+        nthread = n1;
+    }
+    nt = 8 * n2;
+    if (n3 == 4) {
+        nt >>= 1;
+    } else if (n3 < 4) {
+        nt >>= 2;
+    }
+    for (i = 0; i < nthread; i++) {
+        ag[i].nthread = nthread;
+        ag[i].n0 = i;
+        ag[i].n1 = n1;
+        ag[i].n2 = n2;
+        ag[i].n3 = n3;
+        ag[i].ic = icr;
+        ag[i].isgn = isgn;
+        ag[i].a = a;
+        ag[i].t = &t[nt * i];
+        ag[i].ip = ip;
+        ag[i].w = w;
+        fft3d_thread_create(&th[i], xdft3da_th, &ag[i]);
+    }
+    for (i = 0; i < nthread; i++) {
+        fft3d_thread_wait(th[i]);
+    }
+}
+
+
+void cdft3db_subth(int n1, int n2, int n3, int isgn, double ***a, 
+    double *t, int *ip, double *w)
+{
+    void *cdft3db_th(void *p);
+    fft3d_thread_t th[FFT3D_MAX_THREADS];
+    fft3d_arg_t ag[FFT3D_MAX_THREADS];
+    int nthread, nt, i;
+    
+    nthread = FFT3D_MAX_THREADS;
+    if (nthread > n2) {
+        nthread = n2;
+    }
+    nt = 8 * n1;
+    if (n3 == 4) {
+        nt >>= 1;
+    } else if (n3 < 4) {
+        nt >>= 2;
+    }
+    for (i = 0; i < nthread; i++) {
+        ag[i].nthread = nthread;
+        ag[i].n0 = i;
+        ag[i].n1 = n1;
+        ag[i].n2 = n2;
+        ag[i].n3 = n3;
+        ag[i].isgn = isgn;
+        ag[i].a = a;
+        ag[i].t = &t[nt * i];
+        ag[i].ip = ip;
+        ag[i].w = w;
+        fft3d_thread_create(&th[i], cdft3db_th, &ag[i]);
+    }
+    for (i = 0; i < nthread; i++) {
+        fft3d_thread_wait(th[i]);
+    }
+}
+
+
+void ddxt3da_subth(int n1, int n2, int n3, int ics, int isgn, 
+    double ***a, double *t, int *ip, double *w)
+{
+    void *ddxt3da_th(void *p);
+    fft3d_thread_t th[FFT3D_MAX_THREADS];
+    fft3d_arg_t ag[FFT3D_MAX_THREADS];
+    int nthread, nt, i;
+    
+    nthread = FFT3D_MAX_THREADS;
+    if (nthread > n1) {
+        nthread = n1;
+    }
+    nt = 4 * n2;
+    if (n3 == 2) {
+        nt >>= 1;
+    }
+    for (i = 0; i < nthread; i++) {
+        ag[i].nthread = nthread;
+        ag[i].n0 = i;
+        ag[i].n1 = n1;
+        ag[i].n2 = n2;
+        ag[i].n3 = n3;
+        ag[i].ic = ics;
+        ag[i].isgn = isgn;
+        ag[i].a = a;
+        ag[i].t = &t[nt * i];
+        ag[i].ip = ip;
+        ag[i].w = w;
+        fft3d_thread_create(&th[i], ddxt3da_th, &ag[i]);
+    }
+    for (i = 0; i < nthread; i++) {
+        fft3d_thread_wait(th[i]);
+    }
+}
+
+
+void ddxt3db_subth(int n1, int n2, int n3, int ics, int isgn, 
+    double ***a, double *t, int *ip, double *w)
+{
+    void *ddxt3db_th(void *p);
+    fft3d_thread_t th[FFT3D_MAX_THREADS];
+    fft3d_arg_t ag[FFT3D_MAX_THREADS];
+    int nthread, nt, i;
+    
+    nthread = FFT3D_MAX_THREADS;
+    if (nthread > n2) {
+        nthread = n2;
+    }
+    nt = 4 * n1;
+    if (n3 == 2) {
+        nt >>= 1;
+    }
+    for (i = 0; i < nthread; i++) {
+        ag[i].nthread = nthread;
+        ag[i].n0 = i;
+        ag[i].n1 = n1;
+        ag[i].n2 = n2;
+        ag[i].n3 = n3;
+        ag[i].ic = ics;
+        ag[i].isgn = isgn;
+        ag[i].a = a;
+        ag[i].t = &t[nt * i];
+        ag[i].ip = ip;
+        ag[i].w = w;
+        fft3d_thread_create(&th[i], ddxt3db_th, &ag[i]);
+    }
+    for (i = 0; i < nthread; i++) {
+        fft3d_thread_wait(th[i]);
+    }
+}
+
+
+void *xdft3da_th(void *p)
+{
+    void cdft(int n, int isgn, double *a, int *ip, double *w);
+    void rdft(int n, int isgn, double *a, int *ip, double *w);
+    int nthread, n0, n1, n2, n3, icr, isgn, *ip, i, j, k;
+    double ***a, *t, *w;
+    
+    nthread = ((fft3d_arg_t *) p)->nthread;
+    n0 = ((fft3d_arg_t *) p)->n0;
+    n1 = ((fft3d_arg_t *) p)->n1;
+    n2 = ((fft3d_arg_t *) p)->n2;
+    n3 = ((fft3d_arg_t *) p)->n3;
+    icr = ((fft3d_arg_t *) p)->ic;
+    isgn = ((fft3d_arg_t *) p)->isgn;
+    a = ((fft3d_arg_t *) p)->a;
+    t = ((fft3d_arg_t *) p)->t;
+    ip = ((fft3d_arg_t *) p)->ip;
+    w = ((fft3d_arg_t *) p)->w;
+    for (i = n0; i < n1; i += nthread) {
+        if (icr == 0) {
+            for (j = 0; j < n2; j++) {
+                cdft(n3, isgn, a[i][j], ip, w);
+            }
+        } else if (isgn >= 0) {
+            for (j = 0; j < n2; j++) {
+                rdft(n3, isgn, a[i][j], ip, w);
+            }
+        }
+        if (n3 > 4) {
+            for (k = 0; k < n3; k += 8) {
+                for (j = 0; j < n2; j++) {
+                    t[2 * j] = a[i][j][k];
+                    t[2 * j + 1] = a[i][j][k + 1];
+                    t[2 * n2 + 2 * j] = a[i][j][k + 2];
+                    t[2 * n2 + 2 * j + 1] = a[i][j][k + 3];
+                    t[4 * n2 + 2 * j] = a[i][j][k + 4];
+                    t[4 * n2 + 2 * j + 1] = a[i][j][k + 5];
+                    t[6 * n2 + 2 * j] = a[i][j][k + 6];
+                    t[6 * n2 + 2 * j + 1] = a[i][j][k + 7];
+                }
+                cdft(2 * n2, isgn, t, ip, w);
+                cdft(2 * n2, isgn, &t[2 * n2], ip, w);
+                cdft(2 * n2, isgn, &t[4 * n2], ip, w);
+                cdft(2 * n2, isgn, &t[6 * n2], ip, w);
+                for (j = 0; j < n2; j++) {
+                    a[i][j][k] = t[2 * j];
+                    a[i][j][k + 1] = t[2 * j + 1];
+                    a[i][j][k + 2] = t[2 * n2 + 2 * j];
+                    a[i][j][k + 3] = t[2 * n2 + 2 * j + 1];
+                    a[i][j][k + 4] = t[4 * n2 + 2 * j];
+                    a[i][j][k + 5] = t[4 * n2 + 2 * j + 1];
+                    a[i][j][k + 6] = t[6 * n2 + 2 * j];
+                    a[i][j][k + 7] = t[6 * n2 + 2 * j + 1];
+                }
+            }
+        } else if (n3 == 4) {
+            for (j = 0; j < n2; j++) {
+                t[2 * j] = a[i][j][0];
+                t[2 * j + 1] = a[i][j][1];
+                t[2 * n2 + 2 * j] = a[i][j][2];
+                t[2 * n2 + 2 * j + 1] = a[i][j][3];
+            }
+            cdft(2 * n2, isgn, t, ip, w);
+            cdft(2 * n2, isgn, &t[2 * n2], ip, w);
+            for (j = 0; j < n2; j++) {
+                a[i][j][0] = t[2 * j];
+                a[i][j][1] = t[2 * j + 1];
+                a[i][j][2] = t[2 * n2 + 2 * j];
+                a[i][j][3] = t[2 * n2 + 2 * j + 1];
+            }
+        } else if (n3 == 2) {
+            for (j = 0; j < n2; j++) {
+                t[2 * j] = a[i][j][0];
+                t[2 * j + 1] = a[i][j][1];
+            }
+            cdft(2 * n2, isgn, t, ip, w);
+            for (j = 0; j < n2; j++) {
+                a[i][j][0] = t[2 * j];
+                a[i][j][1] = t[2 * j + 1];
+            }
+        }
+        if (icr != 0 && isgn < 0) {
+            for (j = 0; j < n2; j++) {
+                rdft(n3, isgn, a[i][j], ip, w);
+            }
+        }
+    }
+    return (void *) 0;
+}
+
+
+void *cdft3db_th(void *p)
+{
+    void cdft(int n, int isgn, double *a, int *ip, double *w);
+    int nthread, n0, n1, n2, n3, isgn, *ip, i, j, k;
+    double ***a, *t, *w;
+    
+    nthread = ((fft3d_arg_t *) p)->nthread;
+    n0 = ((fft3d_arg_t *) p)->n0;
+    n1 = ((fft3d_arg_t *) p)->n1;
+    n2 = ((fft3d_arg_t *) p)->n2;
+    n3 = ((fft3d_arg_t *) p)->n3;
+    isgn = ((fft3d_arg_t *) p)->isgn;
+    a = ((fft3d_arg_t *) p)->a;
+    t = ((fft3d_arg_t *) p)->t;
+    ip = ((fft3d_arg_t *) p)->ip;
+    w = ((fft3d_arg_t *) p)->w;
+    if (n3 > 4) {
+        for (j = n0; j < n2; j += nthread) {
+            for (k = 0; k < n3; k += 8) {
+                for (i = 0; i < n1; i++) {
+                    t[2 * i] = a[i][j][k];
+                    t[2 * i + 1] = a[i][j][k + 1];
+                    t[2 * n1 + 2 * i] = a[i][j][k + 2];
+                    t[2 * n1 + 2 * i + 1] = a[i][j][k + 3];
+                    t[4 * n1 + 2 * i] = a[i][j][k + 4];
+                    t[4 * n1 + 2 * i + 1] = a[i][j][k + 5];
+                    t[6 * n1 + 2 * i] = a[i][j][k + 6];
+                    t[6 * n1 + 2 * i + 1] = a[i][j][k + 7];
+                }
+                cdft(2 * n1, isgn, t, ip, w);
+                cdft(2 * n1, isgn, &t[2 * n1], ip, w);
+                cdft(2 * n1, isgn, &t[4 * n1], ip, w);
+                cdft(2 * n1, isgn, &t[6 * n1], ip, w);
+                for (i = 0; i < n1; i++) {
+                    a[i][j][k] = t[2 * i];
+                    a[i][j][k + 1] = t[2 * i + 1];
+                    a[i][j][k + 2] = t[2 * n1 + 2 * i];
+                    a[i][j][k + 3] = t[2 * n1 + 2 * i + 1];
+                    a[i][j][k + 4] = t[4 * n1 + 2 * i];
+                    a[i][j][k + 5] = t[4 * n1 + 2 * i + 1];
+                    a[i][j][k + 6] = t[6 * n1 + 2 * i];
+                    a[i][j][k + 7] = t[6 * n1 + 2 * i + 1];
+                }
+            }
+        }
+    } else if (n3 == 4) {
+        for (j = n0; j < n2; j += nthread) {
+            for (i = 0; i < n1; i++) {
+                t[2 * i] = a[i][j][0];
+                t[2 * i + 1] = a[i][j][1];
+                t[2 * n1 + 2 * i] = a[i][j][2];
+                t[2 * n1 + 2 * i + 1] = a[i][j][3];
+            }
+            cdft(2 * n1, isgn, t, ip, w);
+            cdft(2 * n1, isgn, &t[2 * n1], ip, w);
+            for (i = 0; i < n1; i++) {
+                a[i][j][0] = t[2 * i];
+                a[i][j][1] = t[2 * i + 1];
+                a[i][j][2] = t[2 * n1 + 2 * i];
+                a[i][j][3] = t[2 * n1 + 2 * i + 1];
+            }
+        }
+    } else if (n3 == 2) {
+        for (j = n0; j < n2; j += nthread) {
+            for (i = 0; i < n1; i++) {
+                t[2 * i] = a[i][j][0];
+                t[2 * i + 1] = a[i][j][1];
+            }
+            cdft(2 * n1, isgn, t, ip, w);
+            for (i = 0; i < n1; i++) {
+                a[i][j][0] = t[2 * i];
+                a[i][j][1] = t[2 * i + 1];
+            }
+        }
+    }
+    return (void *) 0;
+}
+
+
+void *ddxt3da_th(void *p)
+{
+    void ddct(int n, int isgn, double *a, int *ip, double *w);
+    void ddst(int n, int isgn, double *a, int *ip, double *w);
+    int nthread, n0, n1, n2, n3, ics, isgn, *ip, i, j, k;
+    double ***a, *t, *w;
+    
+    nthread = ((fft3d_arg_t *) p)->nthread;
+    n0 = ((fft3d_arg_t *) p)->n0;
+    n1 = ((fft3d_arg_t *) p)->n1;
+    n2 = ((fft3d_arg_t *) p)->n2;
+    n3 = ((fft3d_arg_t *) p)->n3;
+    ics = ((fft3d_arg_t *) p)->ic;
+    isgn = ((fft3d_arg_t *) p)->isgn;
+    a = ((fft3d_arg_t *) p)->a;
+    t = ((fft3d_arg_t *) p)->t;
+    ip = ((fft3d_arg_t *) p)->ip;
+    w = ((fft3d_arg_t *) p)->w;
+    for (i = n0; i < n1; i += nthread) {
+        if (ics == 0) {
+            for (j = 0; j < n2; j++) {
+                ddct(n3, isgn, a[i][j], ip, w);
+            }
+        } else {
+            for (j = 0; j < n2; j++) {
+                ddst(n3, isgn, a[i][j], ip, w);
+            }
+        }
+        if (n3 > 2) {
+            for (k = 0; k < n3; k += 4) {
+                for (j = 0; j < n2; j++) {
+                    t[j] = a[i][j][k];
+                    t[n2 + j] = a[i][j][k + 1];
+                    t[2 * n2 + j] = a[i][j][k + 2];
+                    t[3 * n2 + j] = a[i][j][k + 3];
+                }
+                if (ics == 0) {
+                    ddct(n2, isgn, t, ip, w);
+                    ddct(n2, isgn, &t[n2], ip, w);
+                    ddct(n2, isgn, &t[2 * n2], ip, w);
+                    ddct(n2, isgn, &t[3 * n2], ip, w);
+                } else {
+                    ddst(n2, isgn, t, ip, w);
+                    ddst(n2, isgn, &t[n2], ip, w);
+                    ddst(n2, isgn, &t[2 * n2], ip, w);
+                    ddst(n2, isgn, &t[3 * n2], ip, w);
+                }
+                for (j = 0; j < n2; j++) {
+                    a[i][j][k] = t[j];
+                    a[i][j][k + 1] = t[n2 + j];
+                    a[i][j][k + 2] = t[2 * n2 + j];
+                    a[i][j][k + 3] = t[3 * n2 + j];
+                }
+            }
+        } else if (n3 == 2) {
+            for (j = 0; j < n2; j++) {
+                t[j] = a[i][j][0];
+                t[n2 + j] = a[i][j][1];
+            }
+            if (ics == 0) {
+                ddct(n2, isgn, t, ip, w);
+                ddct(n2, isgn, &t[n2], ip, w);
+            } else {
+                ddst(n2, isgn, t, ip, w);
+                ddst(n2, isgn, &t[n2], ip, w);
+            }
+            for (j = 0; j < n2; j++) {
+                a[i][j][0] = t[j];
+                a[i][j][1] = t[n2 + j];
+            }
+        }
+    }
+    return (void *) 0;
+}
+
+
+void *ddxt3db_th(void *p)
+{
+    void ddct(int n, int isgn, double *a, int *ip, double *w);
+    void ddst(int n, int isgn, double *a, int *ip, double *w);
+    int nthread, n0, n1, n2, n3, ics, isgn, *ip, i, j, k;
+    double ***a, *t, *w;
+    
+    nthread = ((fft3d_arg_t *) p)->nthread;
+    n0 = ((fft3d_arg_t *) p)->n0;
+    n1 = ((fft3d_arg_t *) p)->n1;
+    n2 = ((fft3d_arg_t *) p)->n2;
+    n3 = ((fft3d_arg_t *) p)->n3;
+    ics = ((fft3d_arg_t *) p)->ic;
+    isgn = ((fft3d_arg_t *) p)->isgn;
+    a = ((fft3d_arg_t *) p)->a;
+    t = ((fft3d_arg_t *) p)->t;
+    ip = ((fft3d_arg_t *) p)->ip;
+    w = ((fft3d_arg_t *) p)->w;
+    if (n3 > 2) {
+        for (j = n0; j < n2; j += nthread) {
+            for (k = 0; k < n3; k += 4) {
+                for (i = 0; i < n1; i++) {
+                    t[i] = a[i][j][k];
+                    t[n1 + i] = a[i][j][k + 1];
+                    t[2 * n1 + i] = a[i][j][k + 2];
+                    t[3 * n1 + i] = a[i][j][k + 3];
+                }
+                if (ics == 0) {
+                    ddct(n1, isgn, t, ip, w);
+                    ddct(n1, isgn, &t[n1], ip, w);
+                    ddct(n1, isgn, &t[2 * n1], ip, w);
+                    ddct(n1, isgn, &t[3 * n1], ip, w);
+                } else {
+                    ddst(n1, isgn, t, ip, w);
+                    ddst(n1, isgn, &t[n1], ip, w);
+                    ddst(n1, isgn, &t[2 * n1], ip, w);
+                    ddst(n1, isgn, &t[3 * n1], ip, w);
+                }
+                for (i = 0; i < n1; i++) {
+                    a[i][j][k] = t[i];
+                    a[i][j][k + 1] = t[n1 + i];
+                    a[i][j][k + 2] = t[2 * n1 + i];
+                    a[i][j][k + 3] = t[3 * n1 + i];
+                }
+            }
+        }
+    } else if (n3 == 2) {
+        for (j = n0; j < n2; j += nthread) {
+            for (i = 0; i < n1; i++) {
+                t[i] = a[i][j][0];
+                t[n1 + i] = a[i][j][1];
+            }
+            if (ics == 0) {
+                ddct(n1, isgn, t, ip, w);
+                ddct(n1, isgn, &t[n1], ip, w);
+            } else {
+                ddst(n1, isgn, t, ip, w);
+                ddst(n1, isgn, &t[n1], ip, w);
+            }
+            for (i = 0; i < n1; i++) {
+                a[i][j][0] = t[i];
+                a[i][j][1] = t[n1 + i];
+            }
+        }
+    }
+    return (void *) 0;
+}
+#endif /* USE_FFT3D_THREADS */
+
diff --git a/third_party/tensorflow_dependencies/fft2d/fftsg3d.f b/third_party/tensorflow_dependencies/fft2d/fftsg3d.f
new file mode 100644
index 0000000..f08b1dc
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/fftsg3d.f
@@ -0,0 +1,926 @@
+! Fast Fourier/Cosine/Sine Transform
+!     dimension   :three
+!     data length :power of 2
+!     decimation  :frequency
+!     radix       :split-radix, row-column
+!     data        :inplace
+!     table       :use
+! subroutines
+!     cdft3d: Complex Discrete Fourier Transform
+!     rdft3d: Real Discrete Fourier Transform
+!     ddct3d: Discrete Cosine Transform
+!     ddst3d: Discrete Sine Transform
+! necessary package
+!     fftsg.f  : 1D-FFT package
+!
+!
+! -------- Complex DFT (Discrete Fourier Transform) --------
+!     [definition]
+!         <case1>
+!             X(k1,k2,k3) = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+!                               x(j1,j2,j3) * 
+!                               exp(2*pi*i*j1*k1/n1) * 
+!                               exp(2*pi*i*j2*k2/n2) * 
+!                               exp(2*pi*i*j3*k3/n3), 
+!                               0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!         <case2>
+!             X(k1,k2,k3) = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+!                               x(j1,j2,j3) * 
+!                               exp(-2*pi*i*j1*k1/n1) * 
+!                               exp(-2*pi*i*j2*k2/n2) * 
+!                               exp(-2*pi*i*j3*k3/n3), 
+!                               0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!         (notes: sum_j=0^n-1 is a summation from j=0 to n-1)
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call cdft3d(n1max, n2max, 2*n1, n2, n3, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call cdft3d(n1max, n2max, 2*n1, n2, n3, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row1 size of the 3D array (integer)
+!         n2max  :row2 size of the 3D array (integer)
+!         2*n1   :data length (integer)
+!                 n1 >= 1, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 1, n2 = power of 2
+!         n3     :data length (integer)
+!                 n3 >= 1, n3 = power of 2
+!         a(0:2*n1-1,0:n2-1,0:n3-1)
+!                :input/output data (real*8)
+!                 input data
+!                     a(2*j1,j2,j3) = Re(x(j1,j2,j3)), 
+!                     a(2*j1+1,j2,j3) = Im(x(j1,j2,j3)), 
+!                     0<=j1<n1, 0<=j2<n2, 0<=j3<n3
+!                 output data
+!                     a(2*k1,k2,k3) = Re(X(k1,k2,k3)), 
+!                     a(2*k1+1,k2,k3) = Im(X(k1,k2,k3)), 
+!                     0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!         t(0:*) :work area (real*8)
+!                 length of t >= max(8*n2, 8*n3)
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1, n2, n3))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1/2, n2/2, n3/2)
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call cdft3d(n1max, n2max, 2*n1, n2, n3, -1, a, t, ip, w)
+!         is 
+!             call cdft3d(n1max, n2max, 2*n1, n2, n3, 1, a, t, ip, w)
+!             do j3 = 0, n3 - 1
+!                 do j2 = 0, n2 - 1
+!                     do j1 = 0, 2 * n1 - 1
+!                         a(j1,j2,j3) = a(j1,j2,j3) * (1.0d0/n1/n2/n3)
+!                     end do
+!                 end do
+!             end do
+!         .
+!
+!
+! -------- Real DFT / Inverse of Real DFT --------
+!     [definition]
+!         <case1> RDFT
+!             R(k1,k2,k3) = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+!                               a(j1,j2,j3) * 
+!                               cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2 + 
+!                                   2*pi*j3*k3/n3), 
+!                               0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!             I(k1,k2,k3) = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+!                               a(j1,j2,j3) * 
+!                               sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2 + 
+!                                   2*pi*j3*k3/n3), 
+!                               0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!         <case2> IRDFT (excluding scale)
+!             a(k1,k2,k3) = (1/2) * sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+!                               (R(j1,j2,j3) * 
+!                               cos(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2 + 
+!                                   2*pi*j3*k3/n3) + 
+!                               I(j1,j2,j3) * 
+!                               sin(2*pi*j1*k1/n1 + 2*pi*j2*k2/n2 + 
+!                                   2*pi*j3*k3/n3)), 
+!                               0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!         (notes: R(mod(n1-k1,n1),mod(n2-k2,n2),mod(n3-k3,n3)) = R(k1,k2,k3), 
+!                 I(mod(n1-k1,n1),mod(n2-k2,n2),mod(n3-k3,n3)) = -I(k1,k2,k3), 
+!                 0<=k1<n1, 0<=k2<n2, 0<=k3<n3)
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call rdft3d(n1max, n2max, n1, n2, n3, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call rdft3d(n1max, n2max, n1, n2, n3, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row1 size of the 3D array (integer)
+!         n2max  :row2 size of the 3D array (integer)
+!         n1     :data length (integer)
+!                 n1 >= 2, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 2, n2 = power of 2
+!         n3     :data length (integer)
+!                 n3 >= 2, n3 = power of 2
+!         a(0:n1-1,0:n2-1,0:n3-1)
+!                :input/output data (real*8)
+!                 <case1>
+!                     output data
+!                         a(2*k1,k2,k3) = R(k1,k2,k3)
+!                                 = R(n1-k1,mod(n2-k2,n2),mod(n3-k3,n3)), 
+!                         a(2*k1+1,k2,k3) = I(k1,k2,k3)
+!                                 = -I(n1-k1,mod(n2-k2,n2),mod(n3-k3,n3)), 
+!                             0<k1<n1/2, 0<=k2<n2, 0<=k3<n3, 
+!                         a(0,k2,k3) = R(0,k2,k3)
+!                                    = R(0,n2-k2,mod(n3-k3,n3)), 
+!                         a(1,k2,k3) = I(0,k2,k3)
+!                                    = -I(0,n2-k2,mod(n3-k3,n3)), 
+!                         a(1,n2-k2,k3) = R(n1/2,k2,mod(n3-k3,n3))
+!                                       = R(n1/2,n2-k2,k3), 
+!                         a(0,n2-k2,k3) = -I(n1/2,k2,mod(n3-k3,n3))
+!                                       = I(n1/2,n2-k2,k3), 
+!                             0<k2<n2/2, 0<=k3<n3, 
+!                         a(0,0,k3) = R(0,0,k3)
+!                                   = R(0,0,n3-k3), 
+!                         a(1,0,k3) = I(0,0,k3)
+!                                   = -I(0,0,n3-k3), 
+!                         a(0,n2/2,k3) = R(0,n2/2,k3)
+!                                      = R(0,n2/2,n3-k3), 
+!                         a(1,n2/2,k3) = I(0,n2/2,k3)
+!                                      = -I(0,n2/2,n3-k3), 
+!                         a(1,0,n3-k3) = R(n1/2,0,k3)
+!                                      = R(n1/2,0,n3-k3), 
+!                         a(0,0,n3-k3) = -I(n1/2,0,k3)
+!                                      = I(n1/2,0,n3-k3), 
+!                         a(1,n2/2,n3-k3) = R(n1/2,n2/2,k3)
+!                                         = R(n1/2,n2/2,n3-k3), 
+!                         a(0,n2/2,n3-k3) = -I(n1/2,n2/2,k3)
+!                                         = I(n1/2,n2/2,n3-k3), 
+!                             0<k3<n3/2, 
+!                         a(0,0,0) = R(0,0,0), 
+!                         a(1,0,0) = R(n1/2,0,0), 
+!                         a(0,0,n3/2) = R(0,0,n3/2), 
+!                         a(1,0,n3/2) = R(n1/2,0,n3/2), 
+!                         a(0,n2/2,0) = R(0,n2/2,0), 
+!                         a(1,n2/2,0) = R(n1/2,n2/2,0), 
+!                         a(0,n2/2,n3/2) = R(0,n2/2,n3/2), 
+!                         a(1,n2/2,n3/2) = R(n1/2,n2/2,n3/2)
+!                 <case2>
+!                     input data
+!                         a(2*j1,j2,j3) = R(j1,j2,j3)
+!                                 = R(n1-j1,mod(n2-j2,n2),mod(n3-j3,n3)), 
+!                         a(2*j1+1,j2,j3) = I(j1,j2,j3)
+!                                 = -I(n1-j1,mod(n2-j2,n2),mod(n3-j3,n3)), 
+!                             0<j1<n1/2, 0<=j2<n2, 0<=j3<n3, 
+!                         a(0,j2,j3) = R(0,j2,j3)
+!                                    = R(0,n2-j2,mod(n3-j3,n3)), 
+!                         a(1,j2,j3) = I(0,j2,j3)
+!                                    = -I(0,n2-j2,mod(n3-j3,n3)), 
+!                         a(1,n2-j2,j3) = R(n1/2,j2,mod(n3-j3,n3))
+!                                       = R(n1/2,n2-j2,j3), 
+!                         a(0,n2-j2,j3) = -I(n1/2,j2,mod(n3-j3,n3))
+!                                       = I(n1/2,n2-j2,j3), 
+!                             0<j2<n2/2, 0<=j3<n3, 
+!                         a(0,0,j3) = R(0,0,j3)
+!                                   = R(0,0,n3-j3), 
+!                         a(1,0,j3) = I(0,0,j3)
+!                                   = -I(0,0,n3-j3), 
+!                         a(0,n2/2,j3) = R(0,n2/2,j3)
+!                                      = R(0,n2/2,n3-j3), 
+!                         a(1,n2/2,j3) = I(0,n2/2,j3)
+!                                      = -I(0,n2/2,n3-j3), 
+!                         a(1,0,n3-j3) = R(n1/2,0,j3)
+!                                      = R(n1/2,0,n3-j3), 
+!                         a(0,0,n3-j3) = -I(n1/2,0,j3)
+!                                      = I(n1/2,0,n3-j3), 
+!                         a(1,n2/2,n3-j3) = R(n1/2,n2/2,j3)
+!                                         = R(n1/2,n2/2,n3-j3), 
+!                         a(0,n2/2,n3-j3) = -I(n1/2,n2/2,j3)
+!                                         = I(n1/2,n2/2,n3-j3), 
+!                             0<j3<n3/2, 
+!                         a(0,0,0) = R(0,0,0), 
+!                         a(1,0,0) = R(n1/2,0,0), 
+!                         a(0,0,n3/2) = R(0,0,n3/2), 
+!                         a(1,0,n3/2) = R(n1/2,0,n3/2), 
+!                         a(0,n2/2,0) = R(0,n2/2,0), 
+!                         a(1,n2/2,0) = R(n1/2,n2/2,0), 
+!                         a(0,n2/2,n3/2) = R(0,n2/2,n3/2), 
+!                         a(1,n2/2,n3/2) = R(n1/2,n2/2,n3/2)
+!                 ---- output ordering ----
+!                     call rdft3d(n1max, n2max, n1, n2, n3, 1, a, t, ip, w)
+!                     call rdft3dsort(n1max, n2max, n1, n2, n3, 1, a)
+!                     ! stored data is a(0:n1-1,0:n2-1,0:n3+1):
+!                     ! a(2*k1,k2,k3) = R(k1,k2,k3), 
+!                     ! a(2*k1+1,k2,k3) = I(k1,k2,k3), 
+!                     ! 0<=k1<=n1/2, 0<=k2<n2, 0<=k3<n3.
+!                     ! the stored data is larger than the input data!
+!                 ---- input ordering ----
+!                     call rdft3dsort(n1max, n2max, n1, n2, n3, -1, a)
+!                     call rdft3d(n1max, n2max, n1, n2, n3, -1, a, t, ip, w)
+!         t(0:*) :work area (real*8)
+!                 length of t >= max(8*n2, 8*n3)
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1/2, n2, n3))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1/4, n2/2, n3/2) + n1/4
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call rdft3d(n1max, n2max, n1, n2, n3, 1, a, t, ip, w)
+!         is 
+!             call rdft3d(n1max, n2max, n1, n2, n3, -1, a, t, ip, w)
+!             do j3 = 0, n3 - 1
+!                 do j2 = 0, n2 - 1
+!                     do j1 = 0, n1 - 1
+!                         a(j1,j2,j3) = a(j1,j2,j3) * (2.0d0/n1/n2/n3)
+!                     end do
+!                 end do
+!             end do
+!         .
+!
+!
+! -------- DCT (Discrete Cosine Transform) / Inverse of DCT --------
+!     [definition]
+!         <case1> IDCT (excluding scale)
+!             C(k1,k2,k3) = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+!                               a(j1,j2,j3) * 
+!                               cos(pi*j1*(k1+1/2)/n1) * 
+!                               cos(pi*j2*(k2+1/2)/n2) * 
+!                               cos(pi*j3*(k3+1/2)/n3), 
+!                               0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!         <case2> DCT
+!             C(k1,k2,k3) = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+!                               a(j1,j2,j3) * 
+!                               cos(pi*(j1+1/2)*k1/n1) * 
+!                               cos(pi*(j2+1/2)*k2/n2) * 
+!                               cos(pi*(j3+1/2)*k3/n3), 
+!                               0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call ddct3d(n1max, n2max, n1, n2, n3, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call ddct3d(n1max, n2max, n1, n2, n3, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row1 size of the 3D array (integer)
+!         n2max  :row2 size of the 3D array (integer)
+!         n1     :data length (integer)
+!                 n1 >= 2, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 2, n2 = power of 2
+!         n3     :data length (integer)
+!                 n3 >= 2, n3 = power of 2
+!         a(0:n1-1,0:n2-1,0:n3-1)
+!                :input/output data (real*8)
+!                 output data
+!                     a(k1,k2,k3) = C(k1,k2,k3), 
+!                         0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!         t(0:*) :work area (real*8)
+!                 length of t >= max(4*n2, 4*n3)
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1/2, n2/2, n3/2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1*3/2, n2*3/2, n3*3/2)
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call ddct3d(n1max, n2max, n1, n2, n3, -1, a, t, ip, w)
+!         is 
+!             do j3 = 0, n3 - 1
+!                 do j2 = 0, n2 - 1
+!                     a(0, j2, j3) = a(0, j2, j3) * 0.5d0
+!                 end do
+!                 do j1 = 0, n1 - 1
+!                     a(j1, 0, j3) = a(j1, 0, j3) * 0.5d0
+!                 end do
+!             end do
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, n1 - 1
+!                     a(j1, j2, 0) = a(j1, j2, 0) * 0.5d0
+!                 end do
+!             end do
+!             call ddct3d(n1max, n2max, n1, n2, n3, 1, a, t, ip, w)
+!             do j3 = 0, n3 - 1
+!                 do j2 = 0, n2 - 1
+!                     do j1 = 0, n1 - 1
+!                         a(j1,j2,j3) = a(j1,j2,j3) * (8.0d0/n1/n2/n3)
+!                     end do
+!                 end do
+!             end do
+!         .
+!
+!
+! -------- DST (Discrete Sine Transform) / Inverse of DST --------
+!     [definition]
+!         <case1> IDST (excluding scale)
+!             S(k1,k2,k3) = sum_j1=1^n1 sum_j2=1^n2 sum_j3=1^n3
+!                               A(j1,j2,j3) * 
+!                               sin(pi*j1*(k1+1/2)/n1) * 
+!                               sin(pi*j2*(k2+1/2)/n2) * 
+!                               sin(pi*j3*(k3+1/2)/n3), 
+!                               0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!         <case2> DST
+!             S(k1,k2,k3) = sum_j1=0^n1-1 sum_j2=0^n2-1 sum_j3=0^n3-1
+!                               a(j1,j2,j3) * 
+!                               sin(pi*(j1+1/2)*k1/n1) * 
+!                               sin(pi*(j2+1/2)*k2/n2) * 
+!                               sin(pi*(j3+1/2)*k3/n3), 
+!                               0<k1<=n1, 0<k2<=n2, 0<k3<=n3
+!     [usage]
+!         <case1>
+!             ip(0) = 0  ! first time only
+!             call ddst3d(n1max, n2max, n1, n2, n3, 1, a, t, ip, w)
+!         <case2>
+!             ip(0) = 0  ! first time only
+!             call ddst3d(n1max, n2max, n1, n2, n3, -1, a, t, ip, w)
+!     [parameters]
+!         n1max  :row1 size of the 3D array (integer)
+!         n2max  :row2 size of the 3D array (integer)
+!         n1     :data length (integer)
+!                 n1 >= 2, n1 = power of 2
+!         n2     :data length (integer)
+!                 n2 >= 2, n2 = power of 2
+!         n3     :data length (integer)
+!                 n3 >= 2, n3 = power of 2
+!         a(0:n1-1,0:n2-1,0:n3-1)
+!                :input/output data (real*8)
+!                 <case1>
+!                     input data
+!                         a(mod(j1,n1),mod(j2,n2),mod(j3,n3)) = A(j1,j2,j3), 
+!                             0<j1<=n1, 0<j2<=n2, 0<j3<=n3
+!                     output data
+!                         a(k1,k2,k3) = S(k1,k2,k3), 
+!                             0<=k1<n1, 0<=k2<n2, 0<=k3<n3
+!                 <case2>
+!                     output data
+!                         a(mod(k1,n1),mod(k2,n2),mod(k3,n3)) = S(k1,k2,k3), 
+!                             0<k1<=n1, 0<k2<=n2, 0<k3<=n3
+!         t(0:*) :work area (real*8)
+!                 length of t >= max(4*n2, 4*n3)
+!         ip(0:*):work area for bit reversal (integer)
+!                 length of ip >= 2+sqrt(n)
+!                 (n = max(n1/2, n2/2, n3/2))
+!                 ip(0),ip(1) are pointers of the cos/sin table.
+!         w(0:*) :cos/sin table (real*8)
+!                 length of w >= max(n1*3/2, n2*3/2, n3*3/2)
+!                 w(),ip() are initialized if ip(0) = 0.
+!     [remark]
+!         Inverse of 
+!             call ddst3d(n1max, n2max, n1, n2, n3, -1, a, t, ip, w)
+!         is 
+!             do j3 = 0, n3 - 1
+!                 do j2 = 0, n2 - 1
+!                     a(0, j2, j3) = a(0, j2, j3) * 0.5d0
+!                 end do
+!                 do j1 = 0, n1 - 1
+!                     a(j1, 0, j3) = a(j1, 0, j3) * 0.5d0
+!                 end do
+!             end do
+!             do j2 = 0, n2 - 1
+!                 do j1 = 0, n1 - 1
+!                     a(j1, j2, 0) = a(j1, j2, 0) * 0.5d0
+!                 end do
+!             end do
+!             call ddst3d(n1max, n2max, n1, n2, n3, 1, a, t, ip, w)
+!             do j3 = 0, n3 - 1
+!                 do j2 = 0, n2 - 1
+!                     do j1 = 0, n1 - 1
+!                         a(j1,j2,j3) = a(j1,j2,j3) * (8.0d0/n1/n2/n3)
+!                     end do
+!                 end do
+!             end do
+!         .
+!
+!
+      subroutine cdft3d(n1max, n2max, n1, n2, n3, isgn, a, 
+     &    t, ip, w)
+      integer n1max, n2max, n1, n2, n3, isgn, ip(0 : *), n
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), 
+     &    t(0 : *), w(0 : *)
+      n = 2 * max(n2, n3)
+      n = max(n, n1)
+      if (n .gt. 4 * ip(0)) then
+          call makewt(n / 4, ip, w)
+      end if
+      call xdft3da_sub(n1max, n2max, n1, n2, n3, 0, 
+     &    isgn, a, t, ip, w)
+      call cdft3db_sub(n1max, n2max, n1, n2, n3, 
+     &    isgn, a, t, ip, w)
+      end
+!
+      subroutine rdft3d(n1max, n2max, n1, n2, n3, isgn, a, 
+     &    t, ip, w)
+      integer n1max, n2max, n1, n2, n3, isgn, ip(0 : *), 
+     &    n, nw, nc
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), 
+     &    t(0 : *), w(0 : *)
+      n = 2 * max(n2, n3)
+      n = max(n, n1)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n1 .gt. 4 * nc) then
+          nc = n1 / 4
+          call makect(nc, ip, w(nw))
+      end if
+      if (isgn .lt. 0) then
+          call rdft3d_sub(n1max, n2max, n1, n2, n3, isgn, a)
+          call cdft3db_sub(n1max, n2max, n1, n2, n3, 
+     &        isgn, a, t, ip, w)
+          call xdft3da_sub(n1max, n2max, n1, n2, n3, 1, 
+     &        isgn, a, t, ip, w)
+      else
+          call xdft3da_sub(n1max, n2max, n1, n2, n3, 1, 
+     &        isgn, a, t, ip, w)
+          call cdft3db_sub(n1max, n2max, n1, n2, n3, 
+     &        isgn, a, t, ip, w)
+          call rdft3d_sub(n1max, n2max, n1, n2, n3, isgn, a)
+      end if
+      end
+!
+      subroutine rdft3dsort(n1max, n2max, n1, n2, n3, isgn, a)
+      integer n1max, n2max, n1, n2, n3, isgn, n2h, n3h, j, k
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), x, y
+      n2h = n2 / 2
+      n3h = n3 / 2
+      if (isgn .lt. 0) then
+          do k = 0, n3 - 1
+              do j = n2h + 1, n2 - 1
+                  a(0, j, k) = a(n1 + 1, j, k)
+                  a(1, j, k) = a(n1, j, k)
+              end do
+          end do
+          do k = n3h + 1, n3 - 1
+              a(0, 0, k) = a(n1 + 1, 0, k)
+              a(1, 0, k) = a(n1, 0, k)
+              a(0, n2h, k) = a(n1 + 1, n2h, k)
+              a(1, n2h, k) = a(n1, n2h, k)
+          end do
+          a(1, 0, 0) = a(n1, 0, 0)
+          a(1, n2h, 0) = a(n1, n2h, 0)
+          a(1, 0, n3h) = a(n1, 0, n3h)
+          a(1, n2h, n3h) = a(n1, n2h, n3h)
+      else
+          do j = n2h + 1, n2 - 1
+              y = a(0, j, 0)
+              x = a(1, j, 0)
+              a(n1, j, 0) = x
+              a(n1 + 1, j, 0) = y
+              a(n1, n2 - j, 0) = x
+              a(n1 + 1, n2 - j, 0) = -y
+              a(0, j, 0) = a(0, n2 - j, 0)
+              a(1, j, 0) = -a(1, n2 - j, 0)
+          end do
+          do k = 1, n3 - 1
+              do j = n2h + 1, n2 - 1
+                  y = a(0, j, k)
+                  x = a(1, j, k)
+                  a(n1, j, k) = x
+                  a(n1 + 1, j, k) = y
+                  a(n1, n2 - j, n3 - k) = x
+                  a(n1 + 1, n2 - j, n3 - k) = -y
+                  a(0, j, k) = a(0, n2 - j, n3 - k)
+                  a(1, j, k) = -a(1, n2 - j, n3 - k)
+              end do
+          end do
+          do k = n3h + 1, n3 - 1
+              y = a(0, 0, k)
+              x = a(1, 0, k)
+              a(n1, 0, k) = x
+              a(n1 + 1, 0, k) = y
+              a(n1, 0, n3 - k) = x
+              a(n1 + 1, 0, n3 - k) = -y
+              a(0, 0, k) = a(0, 0, n3 - k)
+              a(1, 0, k) = -a(1, 0, n3 - k)
+              y = a(0, n2h, k)
+              x = a(1, n2h, k)
+              a(n1, n2h, k) = x
+              a(n1 + 1, n2h, k) = y
+              a(n1, n2h, n3 - k) = x
+              a(n1 + 1, n2h, n3 - k) = -y
+              a(0, n2h, k) = a(0, n2h, n3 - k)
+              a(1, n2h, k) = -a(1, n2h, n3 - k)
+          end do
+          a(n1, 0, 0) = a(1, 0, 0)
+          a(n1 + 1, 0, 0) = 0
+          a(1, 0, 0) = 0
+          a(n1, n2h, 0) = a(1, n2h, 0)
+          a(n1 + 1, n2h, 0) = 0
+          a(1, n2h, 0) = 0
+          a(n1, 0, n3h) = a(1, 0, n3h)
+          a(n1 + 1, 0, n3h) = 0
+          a(1, 0, n3h) = 0
+          a(n1, n2h, n3h) = a(1, n2h, n3h)
+          a(n1 + 1, n2h, n3h) = 0
+          a(1, n2h, n3h) = 0
+      end if
+      end
+!
+      subroutine ddct3d(n1max, n2max, n1, n2, n3, isgn, a, 
+     &    t, ip, w)
+      integer n1max, n2max, n1, n2, n3, isgn, ip(0 : *), 
+     &    n, nw, nc
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), 
+     &    t(0 : *), w(0 : *)
+      n = max(n2, n3)
+      n = max(n, n1)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n .gt. nc) then
+          nc = n
+          call makect(nc, ip, w(nw))
+      end if
+      call ddxt3da_sub(n1max, n2max, n1, n2, n3, 0, 
+     &    isgn, a, t, ip, w)
+      call ddxt3db_sub(n1max, n2max, n1, n2, n3, 0, 
+     &    isgn, a, t, ip, w)
+      end
+!
+      subroutine ddst3d(n1max, n2max, n1, n2, n3, isgn, a, 
+     &    t, ip, w)
+      integer n1max, n2max, n1, n2, n3, isgn, ip(0 : *), 
+     &    n, nw, nc
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), 
+     &    t(0 : *), w(0 : *)
+      n = max(n2, n3)
+      n = max(n, n1)
+      nw = ip(0)
+      if (n .gt. 4 * nw) then
+          nw = n / 4
+          call makewt(nw, ip, w)
+      end if
+      nc = ip(1)
+      if (n .gt. nc) then
+          nc = n
+          call makect(nc, ip, w(nw))
+      end if
+      call ddxt3da_sub(n1max, n2max, n1, n2, n3, 1, 
+     &    isgn, a, t, ip, w)
+      call ddxt3db_sub(n1max, n2max, n1, n2, n3, 1, 
+     &    isgn, a, t, ip, w)
+      end
+!
+! -------- child routines --------
+!
+      subroutine xdft3da_sub(n1max, n2max, n1, n2, n3, icr, 
+     &    isgn, a, t, ip, w)
+      integer n1max, n2max, n1, n2, n3, icr, isgn, 
+     &    ip(0 : *), i, j, k
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), 
+     &    t(0 : *), w(0 : *)
+      do k = 0, n3 - 1
+          if (icr .eq. 0) then
+              do j = 0, n2 - 1
+                  call cdft(n1, isgn, a(0, j, k), ip, w)
+              end do
+          else if (isgn .ge. 0) then
+              do j = 0, n2 - 1
+                  call rdft(n1, isgn, a(0, j, k), ip, w)
+              end do
+          end if
+          if (n1 .gt. 4) then
+              do i = 0, n1 - 8, 8
+                  do j = 0, n2 - 1
+                      t(2 * j) = a(i, j, k)
+                      t(2 * j + 1) = a(i + 1, j, k)
+                      t(2 * n2 + 2 * j) = a(i + 2, j, k)
+                      t(2 * n2 + 2 * j + 1) = a(i + 3, j, k)
+                      t(4 * n2 + 2 * j) = a(i + 4, j, k)
+                      t(4 * n2 + 2 * j + 1) = a(i + 5, j, k)
+                      t(6 * n2 + 2 * j) = a(i + 6, j, k)
+                      t(6 * n2 + 2 * j + 1) = a(i + 7, j, k)
+                  end do
+                  call cdft(2 * n2, isgn, t, ip, w)
+                  call cdft(2 * n2, isgn, t(2 * n2), ip, w)
+                  call cdft(2 * n2, isgn, t(4 * n2), ip, w)
+                  call cdft(2 * n2, isgn, t(6 * n2), ip, w)
+                  do j = 0, n2 - 1
+                      a(i, j, k) = t(2 * j)
+                      a(i + 1, j, k) = t(2 * j + 1)
+                      a(i + 2, j, k) = t(2 * n2 + 2 * j)
+                      a(i + 3, j, k) = t(2 * n2 + 2 * j + 1)
+                      a(i + 4, j, k) = t(4 * n2 + 2 * j)
+                      a(i + 5, j, k) = t(4 * n2 + 2 * j + 1)
+                      a(i + 6, j, k) = t(6 * n2 + 2 * j)
+                      a(i + 7, j, k) = t(6 * n2 + 2 * j + 1)
+                  end do
+              end do
+          else if (n1 .eq. 4) then
+              do j = 0, n2 - 1
+                  t(2 * j) = a(0, j, k)
+                  t(2 * j + 1) = a(1, j, k)
+                  t(2 * n2 + 2 * j) = a(2, j, k)
+                  t(2 * n2 + 2 * j + 1) = a(3, j, k)
+              end do
+              call cdft(2 * n2, isgn, t, ip, w)
+              call cdft(2 * n2, isgn, t(2 * n2), ip, w)
+              do j = 0, n2 - 1
+                  a(0, j, k) = t(2 * j)
+                  a(1, j, k) = t(2 * j + 1)
+                  a(2, j, k) = t(2 * n2 + 2 * j)
+                  a(3, j, k) = t(2 * n2 + 2 * j + 1)
+              end do
+          else if (n1 .eq. 2) then
+              do j = 0, n2 - 1
+                  t(2 * j) = a(0, j, k)
+                  t(2 * j + 1) = a(1, j, k)
+              end do
+              call cdft(2 * n2, isgn, t, ip, w)
+              do j = 0, n2 - 1
+                  a(0, j, k) = t(2 * j)
+                  a(1, j, k) = t(2 * j + 1)
+              end do
+          end if
+          if (icr .ne. 0 .and. isgn .lt. 0) then
+              do j = 0, n2 - 1
+                  call rdft(n1, isgn, a(0, j, k), ip, w)
+              end do
+          end if
+      end do
+      end
+!
+      subroutine cdft3db_sub(n1max, n2max, n1, n2, n3, 
+     &    isgn, a, t, ip, w)
+      integer n1max, n2max, n1, n2, n3, isgn, ip(0 : *), 
+     &    i, j, k
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), 
+     &    t(0 : *), w(0 : *)
+      if (n1 .gt. 4) then
+          do j = 0, n2 - 1
+              do i = 0, n1 - 8, 8
+                  do k = 0, n3 - 1
+                      t(2 * k) = a(i, j, k)
+                      t(2 * k + 1) = a(i + 1, j, k)
+                      t(2 * n3 + 2 * k) = a(i + 2, j, k)
+                      t(2 * n3 + 2 * k + 1) = a(i + 3, j, k)
+                      t(4 * n3 + 2 * k) = a(i + 4, j, k)
+                      t(4 * n3 + 2 * k + 1) = a(i + 5, j, k)
+                      t(6 * n3 + 2 * k) = a(i + 6, j, k)
+                      t(6 * n3 + 2 * k + 1) = a(i + 7, j, k)
+                  end do
+                  call cdft(2 * n3, isgn, t, ip, w)
+                  call cdft(2 * n3, isgn, t(2 * n3), ip, w)
+                  call cdft(2 * n3, isgn, t(4 * n3), ip, w)
+                  call cdft(2 * n3, isgn, t(6 * n3), ip, w)
+                  do k = 0, n3 - 1
+                      a(i, j, k) = t(2 * k)
+                      a(i + 1, j, k) = t(2 * k + 1)
+                      a(i + 2, j, k) = t(2 * n3 + 2 * k)
+                      a(i + 3, j, k) = t(2 * n3 + 2 * k + 1)
+                      a(i + 4, j, k) = t(4 * n3 + 2 * k)
+                      a(i + 5, j, k) = t(4 * n3 + 2 * k + 1)
+                      a(i + 6, j, k) = t(6 * n3 + 2 * k)
+                      a(i + 7, j, k) = t(6 * n3 + 2 * k + 1)
+                  end do
+              end do
+          end do
+      else if (n1 .eq. 4) then
+          do j = 0, n2 - 1
+              do k = 0, n3 - 1
+                  t(2 * k) = a(0, j, k)
+                  t(2 * k + 1) = a(1, j, k)
+                  t(2 * n3 + 2 * k) = a(2, j, k)
+                  t(2 * n3 + 2 * k + 1) = a(3, j, k)
+              end do
+              call cdft(2 * n3, isgn, t, ip, w)
+              call cdft(2 * n3, isgn, t(2 * n3), ip, w)
+              do k = 0, n3 - 1
+                  a(0, j, k) = t(2 * k)
+                  a(1, j, k) = t(2 * k + 1)
+                  a(2, j, k) = t(2 * n3 + 2 * k)
+                  a(3, j, k) = t(2 * n3 + 2 * k + 1)
+              end do
+          end do
+      else if (n1 .eq. 2) then
+          do j = 0, n2 - 1
+              do k = 0, n3 - 1
+                  t(2 * k) = a(0, j, k)
+                  t(2 * k + 1) = a(1, j, k)
+              end do
+              call cdft(2 * n3, isgn, t, ip, w)
+              do k = 0, n3 - 1
+                  a(0, j, k) = t(2 * k)
+                  a(1, j, k) = t(2 * k + 1)
+              end do
+          end do
+      end if
+      end
+!
+      subroutine rdft3d_sub(n1max, n2max, n1, n2, n3, isgn, a)
+      integer n1max, n2max, n1, n2, n3, isgn, 
+     &    n2h, n3h, i, j, k, l
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), xi
+      n2h = n2 / 2
+      n3h = n3 / 2
+      if (isgn .lt. 0) then
+          do k = 1, n3h - 1
+              l = n3 - k
+              xi = a(0, 0, k) - a(0, 0, l)
+              a(0, 0, k) = a(0, 0, k) + a(0, 0, l)
+              a(0, 0, l) = xi
+              xi = a(1, 0, l) - a(1, 0, k)
+              a(1, 0, k) = a(1, 0, k) + a(1, 0, l)
+              a(1, 0, l) = xi
+              xi = a(0, n2h, k) - a(0, n2h, l)
+              a(0, n2h, k) = a(0, n2h, k) + a(0, n2h, l)
+              a(0, n2h, l) = xi
+              xi = a(1, n2h, l) - a(1, n2h, k)
+              a(1, n2h, k) = a(1, n2h, k) + a(1, n2h, l)
+              a(1, n2h, l) = xi
+              do i = 1, n2h - 1
+                  j = n2 - i
+                  xi = a(0, i, k) - a(0, j, l)
+                  a(0, i, k) = a(0, i, k) + a(0, j, l)
+                  a(0, j, l) = xi
+                  xi = a(1, j, l) - a(1, i, k)
+                  a(1, i, k) = a(1, i, k) + a(1, j, l)
+                  a(1, j, l) = xi
+                  xi = a(0, i, l) - a(0, j, k)
+                  a(0, i, l) = a(0, i, l) + a(0, j, k)
+                  a(0, j, k) = xi
+                  xi = a(1, j, k) - a(1, i, l)
+                  a(1, i, l) = a(1, i, l) + a(1, j, k)
+                  a(1, j, k) = xi
+              end do
+          end do
+          do i = 1, n2h - 1
+              j = n2 - i
+              xi = a(0, i, 0) - a(0, j, 0)
+              a(0, i, 0) = a(0, i, 0) + a(0, j, 0)
+              a(0, j, 0) = xi
+              xi = a(1, j, 0) - a(1, i, 0)
+              a(1, i, 0) = a(1, i, 0) + a(1, j, 0)
+              a(1, j, 0) = xi
+              xi = a(0, i, n3h) - a(0, j, n3h)
+              a(0, i, n3h) = a(0, i, n3h) + a(0, j, n3h)
+              a(0, j, n3h) = xi
+              xi = a(1, j, n3h) - a(1, i, n3h)
+              a(1, i, n3h) = a(1, i, n3h) + a(1, j, n3h)
+              a(1, j, n3h) = xi
+          end do
+      else
+          do k = 1, n3h - 1
+              l = n3 - k
+              a(0, 0, l) = 0.5d0 * (a(0, 0, k) - a(0, 0, l))
+              a(0, 0, k) = a(0, 0, k) - a(0, 0, l)
+              a(1, 0, l) = 0.5d0 * (a(1, 0, k) + a(1, 0, l))
+              a(1, 0, k) = a(1, 0, k) - a(1, 0, l)
+              a(0, n2h, l) = 0.5d0 * (a(0, n2h, k) - a(0, n2h, l))
+              a(0, n2h, k) = a(0, n2h, k) - a(0, n2h, l)
+              a(1, n2h, l) = 0.5d0 * (a(1, n2h, k) + a(1, n2h, l))
+              a(1, n2h, k) = a(1, n2h, k) - a(1, n2h, l)
+              do i = 1, n2h - 1
+                  j = n2 - i
+                  a(0, j, l) = 0.5d0 * (a(0, i, k) - a(0, j, l))
+                  a(0, i, k) = a(0, i, k) - a(0, j, l)
+                  a(1, j, l) = 0.5d0 * (a(1, i, k) + a(1, j, l))
+                  a(1, i, k) = a(1, i, k) - a(1, j, l)
+                  a(0, j, k) = 0.5d0 * (a(0, i, l) - a(0, j, k))
+                  a(0, i, l) = a(0, i, l) - a(0, j, k)
+                  a(1, j, k) = 0.5d0 * (a(1, i, l) + a(1, j, k))
+                  a(1, i, l) = a(1, i, l) - a(1, j, k)
+              end do
+          end do
+          do i = 1, n2h - 1
+              j = n2 - i
+              a(0, j, 0) = 0.5d0 * (a(0, i, 0) - a(0, j, 0))
+              a(0, i, 0) = a(0, i, 0) - a(0, j, 0)
+              a(1, j, 0) = 0.5d0 * (a(1, i, 0) + a(1, j, 0))
+              a(1, i, 0) = a(1, i, 0) - a(1, j, 0)
+              a(0, j, n3h) = 0.5d0 * (a(0, i, n3h) - a(0, j, n3h))
+              a(0, i, n3h) = a(0, i, n3h) - a(0, j, n3h)
+              a(1, j, n3h) = 0.5d0 * (a(1, i, n3h) + a(1, j, n3h))
+              a(1, i, n3h) = a(1, i, n3h) - a(1, j, n3h)
+          end do
+      end if
+      end
+!
+      subroutine ddxt3da_sub(n1max, n2max, n1, n2, n3, ics, 
+     &    isgn, a, t, ip, w)
+      integer n1max, n2max, n1, n2, n3, ics, isgn, 
+     &    ip(0 : *), i, j, k
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), 
+     &    t(0 : *), w(0 : *)
+      do k = 0, n3 - 1
+          if (ics .eq. 0) then
+              do j = 0, n2 - 1
+                  call ddct(n1, isgn, a(0, j, k), ip, w)
+              end do
+          else
+              do j = 0, n2 - 1
+                  call ddst(n1, isgn, a(0, j, k), ip, w)
+              end do
+          end if
+          if (n1 .gt. 2) then
+              do i = 0, n1 - 4, 4
+                  do j = 0, n2 - 1
+                      t(j) = a(i, j, k)
+                      t(n2 + j) = a(i + 1, j, k)
+                      t(2 * n2 + j) = a(i + 2, j, k)
+                      t(3 * n2 + j) = a(i + 3, j, k)
+                  end do
+                  if (ics .eq. 0) then
+                      call ddct(n2, isgn, t, ip, w)
+                      call ddct(n2, isgn, t(n2), ip, w)
+                      call ddct(n2, isgn, t(2 * n2), ip, w)
+                      call ddct(n2, isgn, t(3 * n2), ip, w)
+                  else
+                      call ddst(n2, isgn, t, ip, w)
+                      call ddst(n2, isgn, t(n2), ip, w)
+                      call ddst(n2, isgn, t(2 * n2), ip, w)
+                      call ddst(n2, isgn, t(3 * n2), ip, w)
+                  end if
+                  do j = 0, n2 - 1
+                      a(i, j, k) = t(j)
+                      a(i + 1, j, k) = t(n2 + j)
+                      a(i + 2, j, k) = t(2 * n2 + j)
+                      a(i + 3, j, k) = t(3 * n2 + j)
+                  end do
+              end do
+          else if (n1 .eq. 2) then
+              do j = 0, n2 - 1
+                  t(j) = a(0, j, k)
+                  t(n2 + j) = a(1, j, k)
+              end do
+              if (ics .eq. 0) then
+                  call ddct(n2, isgn, t, ip, w)
+                  call ddct(n2, isgn, t(n2), ip, w)
+              else
+                  call ddst(n2, isgn, t, ip, w)
+                  call ddst(n2, isgn, t(n2), ip, w)
+              end if
+              do j = 0, n2 - 1
+                  a(0, j, k) = t(j)
+                  a(1, j, k) = t(n2 + j)
+              end do
+          end if
+      end do
+      end
+!
+      subroutine ddxt3db_sub(n1max, n2max, n1, n2, n3, ics, 
+     &    isgn, a, t, ip, w)
+      integer n1max, n2max, n1, n2, n3, ics, isgn, 
+     &    ip(0 : *), i, j, k
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : n3 - 1), 
+     &    t(0 : *), w(0 : *)
+      if (n1 .gt. 2) then
+          do j = 0, n2 - 1
+              do i = 0, n1 - 4, 4
+                  do k = 0, n3 - 1
+                      t(k) = a(i, j, k)
+                      t(n3 + k) = a(i + 1, j, k)
+                      t(2 * n3 + k) = a(i + 2, j, k)
+                      t(3 * n3 + k) = a(i + 3, j, k)
+                  end do
+                  if (ics .eq. 0) then
+                      call ddct(n3, isgn, t, ip, w)
+                      call ddct(n3, isgn, t(n3), ip, w)
+                      call ddct(n3, isgn, t(2 * n3), ip, w)
+                      call ddct(n3, isgn, t(3 * n3), ip, w)
+                  else
+                      call ddst(n3, isgn, t, ip, w)
+                      call ddst(n3, isgn, t(n3), ip, w)
+                      call ddst(n3, isgn, t(2 * n3), ip, w)
+                      call ddst(n3, isgn, t(3 * n3), ip, w)
+                  end if
+                  do k = 0, n3 - 1
+                      a(i, j, k) = t(k)
+                      a(i + 1, j, k) = t(n3 + k)
+                      a(i + 2, j, k) = t(2 * n3 + k)
+                      a(i + 3, j, k) = t(3 * n3 + k)
+                  end do
+              end do
+          end do
+      else if (n1 .eq. 2) then
+          do j = 0, n2 - 1
+              do k = 0, n3 - 1
+                  t(k) = a(0, j, k)
+                  t(n3 + k) = a(1, j, k)
+              end do
+              if (ics .eq. 0) then
+                  call ddct(n3, isgn, t, ip, w)
+                  call ddct(n3, isgn, t(n3), ip, w)
+              else
+                  call ddst(n3, isgn, t, ip, w)
+                  call ddst(n3, isgn, t(n3), ip, w)
+              end if
+              do k = 0, n3 - 1
+                  a(0, j, k) = t(k)
+                  a(1, j, k) = t(n3 + k)
+              end do
+          end do
+      end if
+      end
+!
diff --git a/third_party/tensorflow_dependencies/fft2d/readme2d.txt b/third_party/tensorflow_dependencies/fft2d/readme2d.txt
new file mode 100644
index 0000000..7cc4dfa
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/readme2d.txt
@@ -0,0 +1,77 @@
+General Purpose 2D,3D FFT (Fast Fourier Transform) Package
+
+Files
+    alloc.c    : 2D-array Allocation
+    alloc.h    : 2D-array Allocation
+    fft4f2d.c  : 2D FFT Package in C       - Version I (radix 4, 2)
+    fft4f2d.f  : 2D FFT Package in Fortran - Version I (radix 4, 2)
+    fftsg.c    : 1D FFT Package in C       - Fast Version (Split-Radix)
+    fftsg.f    : 1D FFT Package in Fortran - Fast Version (Split-Radix)
+    fftsg2d.c  : 2D FFT Package in C       - Version II (Split-Radix)
+    fftsg2d.f  : 2D FFT Package in Fortran - Version II (Split-Radix)
+    fftsg3d.c  : 3D FFT Package in C       - Version II (Split-Radix)
+    fftsg3d.f  : 3D FFT Package in Fortran - Version II (Split-Radix)
+    shrtdct.c  : 8x8, 16x16 DCT Package
+    sample2d/
+        Makefile    : for gcc, cc
+        Makefile.f77: for Fortran
+        Makefile.pth: Pthread version
+        fft4f2dt.c  : Test Program for "fft4f2d.c"
+        fft4f2dt.f  : Test Program for "fft4f2d.f"
+        fftsg2dt.c  : Test Program for "fftsg2d.c"
+        fftsg2dt.f  : Test Program for "fftsg2d.f"
+        fftsg3dt.c  : Test Program for "fftsg3d.c"
+        fftsg3dt.f  : Test Program for "fftsg3d.f"
+        shrtdctt.c  : Test Program for "shrtdct.c"
+
+Difference of Files
+    C and Fortran versions are equal and 
+    the same routines are in each version.
+    ---- Difference between "fft4f2d.*" and "fftsg2d.*" ----
+    "fft4f2d.*" are optimized for the old machines that 
+    don't have the large size CPU cache.
+    "fftsg2d.*", "fftsg3d.*" use 1D FFT routines in "fftsg.*".
+    "fftsg2d.*", "fftsg3d.*" are optimized for the machines that 
+    have the multi-level (L1,L2,etc) cache.
+
+Routines in the Package
+    in fft4f2d.*, fftsg2d.*
+        cdft2d: 2-dim Complex Discrete Fourier Transform
+        rdft2d: 2-dim Real Discrete Fourier Transform
+        ddct2d: 2-dim Discrete Cosine Transform
+        ddst2d: 2-dim Discrete Sine Transform
+        rdft2dsort: rdft2d input/output ordering (fftsg2d.*)
+    in fftsg3d.*
+        cdft3d: 3-dim Complex Discrete Fourier Transform
+        rdft3d: 3-dim Real Discrete Fourier Transform
+        ddct3d: 3-dim Discrete Cosine Transform
+        ddst3d: 3-dim Discrete Sine Transform
+        rdft3dsort: rdft3d input/output ordering
+    in fftsg.*
+        cdft: 1-dim Complex Discrete Fourier Transform
+        rdft: 1-dim Real Discrete Fourier Transform
+        ddct: 1-dim Discrete Cosine Transform
+        ddst: 1-dim Discrete Sine Transform
+        dfct: 1-dim Real Symmetric DFT
+        dfst: 1-dim Real Anti-symmetric DFT
+        (these routines are called by fftsg2d.*, fftsg3d.*)
+    in shrtdct.c
+        ddct8x8s  : Normalized 8x8 DCT
+        ddct16x16s: Normalized 16x16 DCT
+        (faster than ddct2d())
+
+Usage
+    Brief explanations are in block comments of each packages.
+    The examples are given in the test programs.
+
+Copyright
+    Copyright(C) 1997,2001 Takuya OOURA (email: ooura@kurims.kyoto-u.ac.jp).
+    You may use, copy, modify this code for any purpose and 
+    without fee. You may distribute this ORIGINAL package.
+
+History
+    ...
+    Nov. 2001  : Add 3D-FFT routines
+    Dec. 2006  : Fix a documentation bug in "fftsg3d.*"
+    Dec. 2006  : Fix a minor bug in "fftsg.f"
+
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/Makefile b/third_party/tensorflow_dependencies/fft2d/sample2d/Makefile
new file mode 100644
index 0000000..130e0a4
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/Makefile
@@ -0,0 +1,72 @@
+# ---- for GNU gcc ----
+
+CC = gcc
+
+CFLAGS = -Wall
+
+OFLAGS = -O2
+
+# ---- for SUN WS cc ----
+#
+#CC = cc
+#
+#CFLAGS = 
+#
+#OFLAGS = -xO2
+
+
+
+
+all: fft4f2dt fftsg2dt fftsg3dt shrtdctt
+
+
+fft4f2dt : fft4f2dt.o fft4f2d.o alloc.o
+	$(CC) fft4f2dt.o fft4f2d.o alloc.o -lm -o fft4f2dt
+
+fftsg2dt : fftsg2dt.o fftsg2d.o fftsg.o alloc.o
+	$(CC) fftsg2dt.o fftsg2d.o fftsg.o alloc.o -lm -o fftsg2dt
+
+fftsg3dt : fftsg3dt.o fftsg3d.o fftsg.o alloc.o
+	$(CC) fftsg3dt.o fftsg3d.o fftsg.o alloc.o -lm -o fftsg3dt
+
+shrtdctt : shrtdctt.o shrtdct.o
+	$(CC) shrtdctt.o shrtdct.o -lm -o shrtdctt
+
+
+fft4f2dt.o : fft4f2dt.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c fft4f2dt.c -o fft4f2dt.o
+
+fftsg2dt.o : fftsg2dt.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c fftsg2dt.c -o fftsg2dt.o
+
+fftsg3dt.o : fftsg3dt.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c fftsg3dt.c -o fftsg3dt.o
+
+shrtdctt.o : shrtdctt.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c shrtdctt.c -o shrtdctt.o
+
+
+fft4f2d.o : ../fft4f2d.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../fft4f2d.c -o fft4f2d.o
+
+fftsg2d.o : ../fftsg2d.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../fftsg2d.c -o fftsg2d.o
+
+fftsg3d.o : ../fftsg3d.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../fftsg3d.c -o fftsg3d.o
+
+fftsg.o : ../fftsg.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../fftsg.c -o fftsg.o
+
+alloc.o : ../alloc.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../alloc.c -o alloc.o
+
+shrtdct.o : ../shrtdct.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../shrtdct.c -o shrtdct.o
+
+
+
+
+clean:
+	rm -f *.o
+
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/Makefile.f77 b/third_party/tensorflow_dependencies/fft2d/sample2d/Makefile.f77
new file mode 100644
index 0000000..0162fdc
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/Makefile.f77
@@ -0,0 +1,60 @@
+# ---- for GNU g77 ----
+
+F77 = g77
+
+FFLAGS = -Wall
+
+OFLAGS = -O2
+
+# ---- for SUN WS f77 ----
+#
+#F77 = f77
+#
+#FFLAGS = 
+#
+#OFLAGS = -xO2
+
+
+
+
+all: fft4f2dt_f fftsg2dt_f fftsg3dt_f
+
+
+fft4f2dt_f : fft4f2dt_f.o fft4f2d_f.o
+	$(F77) fft4f2dt_f.o fft4f2d_f.o -o fft4f2dt_f
+
+fftsg2dt_f : fftsg2dt_f.o fftsg2d_f.o fftsg_f.o
+	$(F77) fftsg2dt_f.o fftsg2d_f.o fftsg_f.o -o fftsg2dt_f
+
+fftsg3dt_f : fftsg3dt_f.o fftsg3d_f.o fftsg_f.o
+	$(F77) fftsg3dt_f.o fftsg3d_f.o fftsg_f.o -o fftsg3dt_f
+
+
+fft4f2dt_f.o : fft4f2dt.f
+	$(F77) $(FFLAGS) $(OFLAGS) -c fft4f2dt.f -o fft4f2dt_f.o
+
+fftsg2dt_f.o : fftsg2dt.f
+	$(F77) $(FFLAGS) $(OFLAGS) -c fftsg2dt.f -o fftsg2dt_f.o
+
+fftsg3dt_f.o : fftsg3dt.f
+	$(F77) $(FFLAGS) $(OFLAGS) -c fftsg3dt.f -o fftsg3dt_f.o
+
+
+fft4f2d_f.o : ../fft4f2d.f
+	$(F77) $(FFLAGS) $(OFLAGS) -c ../fft4f2d.f -o fft4f2d_f.o
+
+fftsg2d_f.o : ../fftsg2d.f
+	$(F77) $(FFLAGS) $(OFLAGS) -c ../fftsg2d.f -o fftsg2d_f.o
+
+fftsg3d_f.o : ../fftsg3d.f
+	$(F77) $(FFLAGS) $(OFLAGS) -c ../fftsg3d.f -o fftsg3d_f.o
+
+fftsg_f.o : ../fftsg.f
+	$(F77) $(FFLAGS) $(OFLAGS) -c ../fftsg.f -o fftsg_f.o
+
+
+
+
+clean:
+	rm -f *.o
+
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/Makefile.pth b/third_party/tensorflow_dependencies/fft2d/sample2d/Makefile.pth
new file mode 100644
index 0000000..d46941e
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/Makefile.pth
@@ -0,0 +1,54 @@
+# ---- for GNU gcc ----
+
+CC = gcc
+
+CFLAGS = -Wall -DUSE_FFT2D_PTHREADS -DUSE_FFT3D_PTHREADS
+
+OFLAGS = -O2
+
+# ---- for SUN WS cc ----
+#
+#CC = cc
+#
+#CFLAGS = -Wall -DUSE_FFT2D_PTHREADS -DUSE_FFT3D_PTHREADS
+#
+#OFLAGS = -xO2
+
+
+
+
+all: fftsg2dt_pt fftsg3dt_pt
+
+
+fftsg2dt_pt : fftsg2dt.o fftsg2dpt.o fftsg.o alloc.o
+	$(CC) fftsg2dt.o fftsg2dpt.o fftsg.o alloc.o -lm -lpthread -o fftsg2dt_pt
+
+fftsg3dt_pt : fftsg3dt.o fftsg3dpt.o fftsg.o alloc.o
+	$(CC) fftsg3dt.o fftsg3dpt.o fftsg.o alloc.o -lm -lpthread -o fftsg3dt_pt
+
+
+fftsg2dt.o : fftsg2dt.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c fftsg2dt.c -o fftsg2dt.o
+
+fftsg3dt.o : fftsg3dt.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c fftsg3dt.c -o fftsg3dt.o
+
+
+fftsg2dpt.o : ../fftsg2d.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../fftsg2d.c -o fftsg2dpt.o
+
+fftsg3dpt.o : ../fftsg3d.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../fftsg3d.c -o fftsg3dpt.o
+
+fftsg.o : ../fftsg.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../fftsg.c -o fftsg.o
+
+alloc.o : ../alloc.c
+	$(CC) $(CFLAGS) $(OFLAGS) -c ../alloc.c -o alloc.o
+
+
+
+
+clean:
+	rm -f *.o
+
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/alloc.h b/third_party/tensorflow_dependencies/fft2d/sample2d/alloc.h
new file mode 100644
index 0000000..3467cc4
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/alloc.h
@@ -0,0 +1,20 @@
+/* ---- memory allocation ---- */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+
+
+int *alloc_1d_int(int n1);
+void free_1d_int(int *i);
+double *alloc_1d_double(int n1);
+void free_1d_double(double *d);
+int **alloc_2d_int(int n1, int n2);
+void free_2d_int(int **ii);
+double **alloc_2d_double(int n1, int n2);
+void free_2d_double(double **dd);
+int ***alloc_3d_int(int n1, int n2, int n3);
+void free_3d_int(int ***iii);
+double ***alloc_3d_double(int n1, int n2, int n3);
+void free_3d_double(double ***ddd);
+
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/fft4f2dt.c b/third_party/tensorflow_dependencies/fft2d/sample2d/fft4f2dt.c
new file mode 100644
index 0000000..2f300fd
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/fft4f2dt.c
@@ -0,0 +1,109 @@
+/* test of fft4f2d.c */
+
+#include <math.h>
+#include <stdio.h>
+#include "alloc.h"
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+/* random number generator, 0 <= RND < 1 */
+#define RND(p) ((*(p) = (*(p) * 7141 + 54773) % 259200) * (1.0 / 259200))
+
+
+int main()
+{
+    void cdft2d(int, int, int, double **, int *, double *);
+    void rdft2d(int, int, int, double **, int *, double *);
+    void ddct2d(int, int, int, double **, double **, int *, double *);
+    void ddst2d(int, int, int, double **, double **, int *, double *);
+    void putdata2d(int n1, int n2, double **a);
+    double errorcheck2d(int n1, int n2, double scale, double **a);
+    int *ip, n1, n2, n, i;
+    double **a, **t, *w, err;
+
+    printf("data length n1=? (n1 = power of 2) \n");
+    scanf("%d", &n1);
+    printf("data length n2=? (n2 = power of 2) \n");
+    scanf("%d", &n2);
+
+    a = alloc_2d_double(n1, n2);
+    t = alloc_2d_double(n1, n2);
+    n = MAX(n1, n2 / 2);
+    ip = alloc_1d_int(2 + (int) sqrt(n + 0.5));
+    n = MAX(n1 / 2, n2 / 4) + MAX(n1, n2);
+    w = alloc_1d_double(n);
+    ip[0] = 0;
+
+    /* check of CDFT */
+    putdata2d(n1, n2, a);
+    cdft2d(n1, n2, 1, a, ip, w);
+    cdft2d(n1, n2, -1, a, ip, w);
+    err = errorcheck2d(n1, n2, 2.0 / n1 / n2, a);
+    printf("cdft2d err= %g \n", err);
+
+    /* check of RDFT */
+    putdata2d(n1, n2, a);
+    rdft2d(n1, n2, 1, a, ip, w);
+    rdft2d(n1, n2, -1, a, ip, w);
+    err = errorcheck2d(n1, n2, 2.0 / n1 / n2, a);
+    printf("rdft2d err= %g \n", err);
+
+    /* check of DDCT */
+    putdata2d(n1, n2, a);
+    ddct2d(n1, n2, 1, a, t, ip, w);
+    ddct2d(n1, n2, -1, a, t, ip, w);
+    for (i = 0; i <= n1 - 1; i++) {
+        a[i][0] *= 0.5;
+    }
+    for (i = 0; i <= n2 - 1; i++) {
+        a[0][i] *= 0.5;
+    }
+    err = errorcheck2d(n1, n2, 4.0 / n1 / n2, a);
+    printf("ddct2d err= %g \n", err);
+
+    /* check of DDST */
+    putdata2d(n1, n2, a);
+    ddst2d(n1, n2, 1, a, t, ip, w);
+    ddst2d(n1, n2, -1, a, t, ip, w);
+    for (i = 0; i <= n1 - 1; i++) {
+        a[i][0] *= 0.5;
+    }
+    for (i = 0; i <= n2 - 1; i++) {
+        a[0][i] *= 0.5;
+    }
+    err = errorcheck2d(n1, n2, 4.0 / n1 / n2, a);
+    printf("ddst2d err= %g \n", err);
+
+    free_1d_double(w);
+    free_1d_int(ip);
+    free_2d_double(t);
+    free_2d_double(a);
+    return 0;
+}
+
+
+void putdata2d(int n1, int n2, double **a)
+{
+    int j1, j2, seed = 0;
+
+    for (j1 = 0; j1 <= n1 - 1; j1++) {
+        for (j2 = 0; j2 <= n2 - 1; j2++) {
+            a[j1][j2] = RND(&seed);
+        }
+    }
+}
+
+
+double errorcheck2d(int n1, int n2, double scale, double **a)
+{
+    int j1, j2, seed = 0;
+    double err = 0, e;
+
+    for (j1 = 0; j1 <= n1 - 1; j1++) {
+        for (j2 = 0; j2 <= n2 - 1; j2++) {
+            e = RND(&seed) - a[j1][j2] * scale;
+            err = MAX(err, fabs(e));
+        }
+    }
+    return err;
+}
+
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/fft4f2dt.f b/third_party/tensorflow_dependencies/fft2d/sample2d/fft4f2dt.f
new file mode 100644
index 0000000..43ac8ac
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/fft4f2dt.f
@@ -0,0 +1,95 @@
+! test of fft4f2d.f
+!
+      program main
+      integer nmax, nmaxsqrt
+      parameter (nmax = 1024)
+      parameter (nmaxsqrt = 32)
+      integer ip(0 : nmaxsqrt + 1), n1, n2, i
+      real*8 a(0 : nmax - 1, 0 : nmax - 1), 
+     &    t(0 : nmax - 1, 0 : nmax - 1), w(0 : nmax * 3 / 2 - 1), 
+     &    err, errorcheck2d
+!
+      write (*, *) 'data length n1=? (n1 = power of 2) '
+      read (*, *) n1
+      write (*, *) 'data length n2=? (n2 = power of 2) '
+      read (*, *) n2
+      ip(0) = 0
+!
+!   check of CDFT
+      call putdata2d(nmax, n1, n2, a)
+      call cdft2d(nmax, n1, n2, 1, a, ip, w)
+      call cdft2d(nmax, n1, n2, -1, a, ip, w)
+      err = errorcheck2d(nmax, n1, n2, 2.0d0 / n1 / n2, a)
+      write (*, *) 'cdft2d err= ', err
+!
+!   check of RDFT
+      call putdata2d(nmax, n1, n2, a)
+      call rdft2d(nmax, n1, n2, 1, a, ip, w)
+      call rdft2d(nmax, n1, n2, -1, a, ip, w)
+      err = errorcheck2d(nmax, n1, n2, 2.0d0 / n1 / n2, a)
+      write (*, *) 'rdft2d err= ', err
+!
+!   check of DDCT
+      call putdata2d(nmax, n1, n2, a)
+      call ddct2d(nmax, n1, n2, 1, a, t, ip, w)
+      call ddct2d(nmax, n1, n2, -1, a, t, ip, w)
+      do i = 0, n1 - 1
+          a(i, 0) = a(i, 0) * 0.5d0
+      end do
+      do i = 0, n2 - 1
+          a(0, i) = a(0, i) * 0.5d0
+      end do
+      err = errorcheck2d(nmax, n1, n2, 4.0d0 / n1 / n2, a)
+      write (*, *) 'ddct2d err= ', err
+!
+!   check of DDST
+      call putdata2d(nmax, n1, n2, a)
+      call ddst2d(nmax, n1, n2, 1, a, t, ip, w)
+      call ddst2d(nmax, n1, n2, -1, a, t, ip, w)
+      do i = 0, n1 - 1
+          a(i, 0) = a(i, 0) * 0.5d0
+      end do
+      do i = 0, n2 - 1
+          a(0, i) = a(0, i) * 0.5d0
+      end do
+      err = errorcheck2d(nmax, n1, n2, 4.0d0 / n1 / n2, a)
+      write (*, *) 'ddst2d err= ', err
+!
+      end
+!
+!
+      subroutine putdata2d(n1max, n1, n2, a)
+      integer n1max, n1, n2, j1, j2, seed
+      real*8 a(0 : n1max - 1, 0 : *), drnd
+      seed = 0
+      do j2 = 0, n2 - 1
+          do j1 = 0, n1 - 1
+              a(j1, j2) = drnd(seed)
+          end do
+      end do
+      end
+!
+!
+      function errorcheck2d(n1max, n1, n2, scale, a)
+      integer n1max, n1, n2, j1, j2, seed
+      real*8 scale, a(0 : n1max - 1, 0 : *), drnd, err, e, 
+     &    errorcheck2d
+      err = 0
+      seed = 0
+      do j2 = 0, n2 - 1
+          do j1 = 0, n1 - 1
+              e = drnd(seed) - a(j1, j2) * scale
+              err = max(err, abs(e))
+          end do
+      end do
+      errorcheck2d = err
+      end
+!
+!
+! random number generator, 0 <= drnd < 1
+      real*8 function drnd(seed)
+      integer seed
+      seed = mod(seed * 7141 + 54773, 259200)
+      drnd = seed * (1.0d0 / 259200)
+      end
+!
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg2dt.c b/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg2dt.c
new file mode 100644
index 0000000..c4cf935
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg2dt.c
@@ -0,0 +1,107 @@
+/* test of fftsg2d.c */
+
+#include <math.h>
+#include <stdio.h>
+#include "alloc.h"
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+/* random number generator, 0 <= RND < 1 */
+#define RND(p) ((*(p) = (*(p) * 7141 + 54773) % 259200) * (1.0 / 259200))
+
+
+int main()
+{
+    void cdft2d(int, int, int, double **, double *, int *, double *);
+    void rdft2d(int, int, int, double **, double *, int *, double *);
+    void ddct2d(int, int, int, double **, double *, int *, double *);
+    void ddst2d(int, int, int, double **, double *, int *, double *);
+    void putdata2d(int n1, int n2, double **a);
+    double errorcheck2d(int n1, int n2, double scale, double **a);
+    int *ip, n1, n2, n, i;
+    double **a, *w, err;
+
+    printf("data length n1=? (n1 = power of 2) \n");
+    scanf("%d", &n1);
+    printf("data length n2=? (n2 = power of 2) \n");
+    scanf("%d", &n2);
+
+    a = alloc_2d_double(n1, n2);
+    n = MAX(n1, n2 / 2);
+    ip = alloc_1d_int(2 + (int) sqrt(n + 0.5));
+    n = MAX(n1, n2) * 3 / 2;
+    w = alloc_1d_double(n);
+    ip[0] = 0;
+
+    /* check of CDFT */
+    putdata2d(n1, n2, a);
+    cdft2d(n1, n2, 1, a, NULL, ip, w);
+    cdft2d(n1, n2, -1, a, NULL, ip, w);
+    err = errorcheck2d(n1, n2, 2.0 / n1 / n2, a);
+    printf("cdft2d err= %g \n", err);
+
+    /* check of RDFT */
+    putdata2d(n1, n2, a);
+    rdft2d(n1, n2, 1, a, NULL, ip, w);
+    rdft2d(n1, n2, -1, a, NULL, ip, w);
+    err = errorcheck2d(n1, n2, 2.0 / n1 / n2, a);
+    printf("rdft2d err= %g \n", err);
+
+    /* check of DDCT */
+    putdata2d(n1, n2, a);
+    ddct2d(n1, n2, 1, a, NULL, ip, w);
+    ddct2d(n1, n2, -1, a, NULL, ip, w);
+    for (i = 0; i <= n1 - 1; i++) {
+        a[i][0] *= 0.5;
+    }
+    for (i = 0; i <= n2 - 1; i++) {
+        a[0][i] *= 0.5;
+    }
+    err = errorcheck2d(n1, n2, 4.0 / n1 / n2, a);
+    printf("ddct2d err= %g \n", err);
+
+    /* check of DDST */
+    putdata2d(n1, n2, a);
+    ddst2d(n1, n2, 1, a, NULL, ip, w);
+    ddst2d(n1, n2, -1, a, NULL, ip, w);
+    for (i = 0; i <= n1 - 1; i++) {
+        a[i][0] *= 0.5;
+    }
+    for (i = 0; i <= n2 - 1; i++) {
+        a[0][i] *= 0.5;
+    }
+    err = errorcheck2d(n1, n2, 4.0 / n1 / n2, a);
+    printf("ddst2d err= %g \n", err);
+
+    free_1d_double(w);
+    free_1d_int(ip);
+    free_2d_double(a);
+    return 0;
+}
+
+
+void putdata2d(int n1, int n2, double **a)
+{
+    int j1, j2, seed = 0;
+
+    for (j1 = 0; j1 <= n1 - 1; j1++) {
+        for (j2 = 0; j2 <= n2 - 1; j2++) {
+            a[j1][j2] = RND(&seed);
+        }
+    }
+}
+
+
+double errorcheck2d(int n1, int n2, double scale, double **a)
+{
+    int j1, j2, seed = 0;
+    double err = 0, e;
+
+    for (j1 = 0; j1 <= n1 - 1; j1++) {
+        for (j2 = 0; j2 <= n2 - 1; j2++) {
+            e = RND(&seed) - a[j1][j2] * scale;
+            err = MAX(err, fabs(e));
+        }
+    }
+    return err;
+}
+
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg2dt.f b/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg2dt.f
new file mode 100644
index 0000000..a375c05
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg2dt.f
@@ -0,0 +1,94 @@
+! test of fftsg2d.f
+!
+      program main
+      integer nmax, nmaxsqrt
+      parameter (nmax = 1024)
+      parameter (nmaxsqrt = 32)
+      integer ip(0 : nmaxsqrt + 1), n1, n2, i
+      real*8 a(0 : nmax - 1, 0 : nmax - 1), t(0 : 8 * nmax - 1), 
+     &    w(0 : nmax * 3 / 2 - 1), err, errorcheck2d
+!
+      write (*, *) 'data length n1=? (n1 = power of 2) '
+      read (*, *) n1
+      write (*, *) 'data length n2=? (n2 = power of 2) '
+      read (*, *) n2
+      ip(0) = 0
+!
+!   check of CDFT
+      call putdata2d(nmax, n1, n2, a)
+      call cdft2d(nmax, n1, n2, 1, a, t, ip, w)
+      call cdft2d(nmax, n1, n2, -1, a, t, ip, w)
+      err = errorcheck2d(nmax, n1, n2, 2.0d0 / n1 / n2, a)
+      write (*, *) 'cdft2d err= ', err
+!
+!   check of RDFT
+      call putdata2d(nmax, n1, n2, a)
+      call rdft2d(nmax, n1, n2, 1, a, t, ip, w)
+      call rdft2d(nmax, n1, n2, -1, a, t, ip, w)
+      err = errorcheck2d(nmax, n1, n2, 2.0d0 / n1 / n2, a)
+      write (*, *) 'rdft2d err= ', err
+!
+!   check of DDCT
+      call putdata2d(nmax, n1, n2, a)
+      call ddct2d(nmax, n1, n2, 1, a, t, ip, w)
+      call ddct2d(nmax, n1, n2, -1, a, t, ip, w)
+      do i = 0, n1 - 1
+          a(i, 0) = a(i, 0) * 0.5d0
+      end do
+      do i = 0, n2 - 1
+          a(0, i) = a(0, i) * 0.5d0
+      end do
+      err = errorcheck2d(nmax, n1, n2, 4.0d0 / n1 / n2, a)
+      write (*, *) 'ddct2d err= ', err
+!
+!   check of DDST
+      call putdata2d(nmax, n1, n2, a)
+      call ddst2d(nmax, n1, n2, 1, a, t, ip, w)
+      call ddst2d(nmax, n1, n2, -1, a, t, ip, w)
+      do i = 0, n1 - 1
+          a(i, 0) = a(i, 0) * 0.5d0
+      end do
+      do i = 0, n2 - 1
+          a(0, i) = a(0, i) * 0.5d0
+      end do
+      err = errorcheck2d(nmax, n1, n2, 4.0d0 / n1 / n2, a)
+      write (*, *) 'ddst2d err= ', err
+!
+      end
+!
+!
+      subroutine putdata2d(n1max, n1, n2, a)
+      integer n1max, n1, n2, j1, j2, seed
+      real*8 a(0 : n1max - 1, 0 : *), drnd
+      seed = 0
+      do j2 = 0, n2 - 1
+          do j1 = 0, n1 - 1
+              a(j1, j2) = drnd(seed)
+          end do
+      end do
+      end
+!
+!
+      function errorcheck2d(n1max, n1, n2, scale, a)
+      integer n1max, n1, n2, j1, j2, seed
+      real*8 scale, a(0 : n1max - 1, 0 : *), drnd, err, e, 
+     &    errorcheck2d
+      err = 0
+      seed = 0
+      do j2 = 0, n2 - 1
+          do j1 = 0, n1 - 1
+              e = drnd(seed) - a(j1, j2) * scale
+              err = max(err, abs(e))
+          end do
+      end do
+      errorcheck2d = err
+      end
+!
+!
+! random number generator, 0 <= drnd < 1
+      real*8 function drnd(seed)
+      integer seed
+      seed = mod(seed * 7141 + 54773, 259200)
+      drnd = seed * (1.0d0 / 259200)
+      end
+!
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg3dt.c b/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg3dt.c
new file mode 100644
index 0000000..87879e6
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg3dt.c
@@ -0,0 +1,128 @@
+/* test of fftsg3d.c */
+
+#include <math.h>
+#include <stdio.h>
+#include "alloc.h"
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+/* random number generator, 0 <= RND < 1 */
+#define RND(p) ((*(p) = (*(p) * 7141 + 54773) % 259200) * (1.0 / 259200))
+
+
+int main()
+{
+    void cdft3d(int, int, int, int, double ***, double *, int *, double *);
+    void rdft3d(int, int, int, int, double ***, double *, int *, double *);
+    void ddct3d(int, int, int, int, double ***, double *, int *, double *);
+    void ddst3d(int, int, int, int, double ***, double *, int *, double *);
+    void putdata3d(int n1, int n2, int n3, double ***a);
+    double errorcheck3d(int n1, int n2, int n3, double scale, double ***a);
+    int *ip, n1, n2, n3, n, nt, i, j;
+    double ***a, *w, err;
+
+    printf("data length n1=? (n1 = power of 2) \n");
+    scanf("%d", &n1);
+    printf("data length n2=? (n2 = power of 2) \n");
+    scanf("%d", &n2);
+    printf("data length n3=? (n3 = power of 2) \n");
+    scanf("%d", &n3);
+
+    a = alloc_3d_double(n1, n2, n3);
+    nt = MAX(n1, n2);
+    n = MAX(nt, n3 / 2);
+    ip = alloc_1d_int(2 + (int) sqrt(n + 0.5));
+    n = MAX(nt, n3) * 3 / 2;
+    w = alloc_1d_double(n);
+    ip[0] = 0;
+
+    /* check of CDFT */
+    putdata3d(n1, n2, n3, a);
+    cdft3d(n1, n2, n3, 1, a, NULL, ip, w);
+    cdft3d(n1, n2, n3, -1, a, NULL, ip, w);
+    err = errorcheck3d(n1, n2, n3, 2.0 / n1 / n2 / n3, a);
+    printf("cdft3d err= %g \n", err);
+
+    /* check of RDFT */
+    putdata3d(n1, n2, n3, a);
+    rdft3d(n1, n2, n3, 1, a, NULL, ip, w);
+    rdft3d(n1, n2, n3, -1, a, NULL, ip, w);
+    err = errorcheck3d(n1, n2, n3, 2.0 / n1 / n2 / n3, a);
+    printf("rdft3d err= %g \n", err);
+
+    /* check of DDCT */
+    putdata3d(n1, n2, n3, a);
+    ddct3d(n1, n2, n3, 1, a, NULL, ip, w);
+    ddct3d(n1, n2, n3, -1, a, NULL, ip, w);
+    for (i = 0; i <= n1 - 1; i++) {
+        for (j = 0; j <= n2 - 1; j++) {
+            a[i][j][0] *= 0.5;
+        }
+        for (j = 0; j <= n3 - 1; j++) {
+            a[i][0][j] *= 0.5;
+        }
+    }
+    for (i = 0; i <= n2 - 1; i++) {
+        for (j = 0; j <= n3 - 1; j++) {
+            a[0][i][j] *= 0.5;
+        }
+    }
+    err = errorcheck3d(n1, n2, n3, 8.0 / n1 / n2 / n3, a);
+    printf("ddct3d err= %g \n", err);
+
+    /* check of DDST */
+    putdata3d(n1, n2, n3, a);
+    ddst3d(n1, n2, n3, 1, a, NULL, ip, w);
+    ddst3d(n1, n2, n3, -1, a, NULL, ip, w);
+    for (i = 0; i <= n1 - 1; i++) {
+        for (j = 0; j <= n2 - 1; j++) {
+            a[i][j][0] *= 0.5;
+        }
+        for (j = 0; j <= n3 - 1; j++) {
+            a[i][0][j] *= 0.5;
+        }
+    }
+    for (i = 0; i <= n2 - 1; i++) {
+        for (j = 0; j <= n3 - 1; j++) {
+            a[0][i][j] *= 0.5;
+        }
+    }
+    err = errorcheck3d(n1, n2, n3, 8.0 / n1 / n2 / n3, a);
+    printf("ddst3d err= %g \n", err);
+
+    free_1d_double(w);
+    free_1d_int(ip);
+    free_3d_double(a);
+    return 0;
+}
+
+
+void putdata3d(int n1, int n2, int n3, double ***a)
+{
+    int j1, j2, j3, seed = 0;
+
+    for (j1 = 0; j1 <= n1 - 1; j1++) {
+        for (j2 = 0; j2 <= n2 - 1; j2++) {
+            for (j3 = 0; j3 <= n3 - 1; j3++) {
+                a[j1][j2][j3] = RND(&seed);
+            }
+        }
+    }
+}
+
+
+double errorcheck3d(int n1, int n2, int n3, double scale, double ***a)
+{
+    int j1, j2, j3, seed = 0;
+    double err = 0, e;
+
+    for (j1 = 0; j1 <= n1 - 1; j1++) {
+        for (j2 = 0; j2 <= n2 - 1; j2++) {
+            for (j3 = 0; j3 <= n3 - 1; j3++) {
+                e = RND(&seed) - a[j1][j2][j3] * scale;
+                err = MAX(err, fabs(e));
+            }
+        }
+    }
+    return err;
+}
+
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg3dt.f b/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg3dt.f
new file mode 100644
index 0000000..bbada0d
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/fftsg3dt.f
@@ -0,0 +1,119 @@
+! test of fftsg3d.f
+!
+      program main
+      integer nmax, nmaxsqrt
+      parameter (nmax = 128)
+      parameter (nmaxsqrt = 16)
+      integer ip(0 : nmaxsqrt + 1), n1, n2, n3, i, j
+      real*8 a(0 : nmax - 1, 0 : nmax - 1, 0 : nmax - 1), 
+     &    t(0 : 8 * nmax - 1), 
+     &    w(0 : nmax * 3 / 2 - 1), err, errorcheck3d
+!
+      write (*, *) 'data length n1=? (n1 = power of 2) '
+      read (*, *) n1
+      write (*, *) 'data length n2=? (n2 = power of 2) '
+      read (*, *) n2
+      write (*, *) 'data length n3=? (n3 = power of 2) '
+      read (*, *) n3
+      ip(0) = 0
+!
+!   check of CDFT
+      call putdata3d(nmax, nmax, n1, n2, n3, a)
+      call cdft3d(nmax, nmax, n1, n2, n3, 1, a, t, ip, w)
+      call cdft3d(nmax, nmax, n1, n2, n3, -1, a, t, ip, w)
+      err = errorcheck3d(nmax, nmax, n1, n2, n3, 
+     &    2.0d0 / n1 / n2 / n3, a)
+      write (*, *) 'cdft3d err= ', err
+!
+!   check of RDFT
+      call putdata3d(nmax, nmax, n1, n2, n3, a)
+      call rdft3d(nmax, nmax, n1, n2, n3, 1, a, t, ip, w)
+      call rdft3d(nmax, nmax, n1, n2, n3, -1, a, t, ip, w)
+      err = errorcheck3d(nmax, nmax, n1, n2, n3, 
+     &    2.0d0 / n1 / n2 / n3, a)
+      write (*, *) 'rdft3d err= ', err
+!
+!   check of DDCT
+      call putdata3d(nmax, nmax, n1, n2, n3, a)
+      call ddct3d(nmax, nmax, n1, n2, n3, 1, a, t, ip, w)
+      call ddct3d(nmax, nmax, n1, n2, n3, -1, a, t, ip, w)
+      do j = 0, n2 - 1
+          do i = 0, n1 - 1
+              a(i, j, 0) = a(i, j, 0) * 0.5d0
+          end do
+      end do
+      do j = 0, n3 - 1
+          do i = 0, n1 - 1
+              a(i, 0, j) = a(i, 0, j) * 0.5d0
+          end do
+          do i = 0, n2 - 1
+              a(0, i, j) = a(0, i, j) * 0.5d0
+          end do
+      end do
+      err = errorcheck3d(nmax, nmax, n1, n2, n3, 
+     &    8.0d0 / n1 / n2 / n3, a)
+      write (*, *) 'ddct3d err= ', err
+!
+!   check of DDST
+      call putdata3d(nmax, nmax, n1, n2, n3, a)
+      call ddst3d(nmax, nmax, n1, n2, n3, 1, a, t, ip, w)
+      call ddst3d(nmax, nmax, n1, n2, n3, -1, a, t, ip, w)
+      do j = 0, n2 - 1
+          do i = 0, n1 - 1
+              a(i, j, 0) = a(i, j, 0) * 0.5d0
+          end do
+      end do
+      do j = 0, n3 - 1
+          do i = 0, n1 - 1
+              a(i, 0, j) = a(i, 0, j) * 0.5d0
+          end do
+          do i = 0, n2 - 1
+              a(0, i, j) = a(0, i, j) * 0.5d0
+          end do
+      end do
+      err = errorcheck3d(nmax, nmax, n1, n2, n3, 
+     &    8.0d0 / n1 / n2 / n3, a)
+      write (*, *) 'ddst3d err= ', err
+!
+      end
+!
+!
+      subroutine putdata3d(n1max, n2max, n1, n2, n3, a)
+      integer n1max, n2max, n1, n2, n3, j1, j2, j3, seed
+      real*8 a(0 : n1max - 1, 0 : n2max - 1, 0 : *), drnd
+      seed = 0
+      do j3 = 0, n3 - 1
+          do j2 = 0, n2 - 1
+              do j1 = 0, n1 - 1
+                  a(j1, j2, j3) = drnd(seed)
+              end do
+          end do
+      end do
+      end
+!
+!
+      function errorcheck3d(n1max, n2max, n1, n2, n3, scale, a)
+      integer n1max, n2max, n1, n2, n3, j1, j2, j3, seed
+      real*8 scale, a(0 : n1max - 1, 0 : n2max - 1, 0 : *), 
+     &    drnd, err, e, errorcheck3d
+      err = 0
+      seed = 0
+      do j3 = 0, n3 - 1
+          do j2 = 0, n2 - 1
+              do j1 = 0, n1 - 1
+                  e = drnd(seed) - a(j1, j2, j3) * scale
+                  err = max(err, abs(e))
+              end do
+          end do
+      end do
+      errorcheck3d = err
+      end
+!
+!
+! random number generator, 0 <= drnd < 1
+      real*8 function drnd(seed)
+      integer seed
+      seed = mod(seed * 7141 + 54773, 259200)
+      drnd = seed * (1.0d0 / 259200)
+      end
+!
diff --git a/third_party/tensorflow_dependencies/fft2d/sample2d/shrtdctt.c b/third_party/tensorflow_dependencies/fft2d/sample2d/shrtdctt.c
new file mode 100644
index 0000000..70d665f
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/sample2d/shrtdctt.c
@@ -0,0 +1,68 @@
+/* test of shrtdct.c */
+
+#include <math.h>
+#include <stdio.h>
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+/* random number generator, 0 <= RND < 1 */
+#define RND(p) ((*(p) = (*(p) * 7141 + 54773) % 259200) * (1.0 / 259200))
+
+#define NMAX 16
+
+int main()
+{
+    void ddct8x8s(int isgn, double **a);
+    void ddct16x16s(int isgn, double **a);
+    void putdata2d(int n1, int n2, double **a);
+    double errorcheck2d(int n1, int n2, double scale, double **a);
+    double err;
+    
+    int i;
+    double aarr[NMAX][NMAX], *a[NMAX], barr[NMAX][NMAX], *b[NMAX];
+    for (i = 0; i < NMAX; i++) a[i] = aarr[i];
+    for (i = 0; i < NMAX; i++) b[i] = barr[i];
+
+    /* check of 8x8 DCT */
+    putdata2d(8, 8, a);
+    ddct8x8s(-1, a);
+    ddct8x8s(1, a);
+    err = errorcheck2d(8, 8, 1.0, a);
+    printf("ddct8x8s   err= %g\n", err);
+
+    /* check of 16x16 DCT */
+    putdata2d(16, 16, a);
+    ddct16x16s(-1, a);
+    ddct16x16s(1, a);
+    err = errorcheck2d(16, 16, 1.0, a);
+    printf("ddct16x16s err= %g\n", err);
+
+    return 0;
+}
+
+
+void putdata2d(int n1, int n2, double **a)
+{
+    int j1, j2, seed = 0;
+
+    for (j1 = 0; j1 <= n1 - 1; j1++) {
+        for (j2 = 0; j2 <= n2 - 1; j2++) {
+            a[j1][j2] = RND(&seed);
+        }
+    }
+}
+
+
+double errorcheck2d(int n1, int n2, double scale, double **a)
+{
+    int j1, j2, seed = 0;
+    double err = 0, e;
+
+    for (j1 = 0; j1 <= n1 - 1; j1++) {
+        for (j2 = 0; j2 <= n2 - 1; j2++) {
+            e = RND(&seed) - a[j1][j2] * scale;
+            err = MAX(err, fabs(e));
+        }
+    }
+    return err;
+}
+
diff --git a/third_party/tensorflow_dependencies/fft2d/shrtdct.c b/third_party/tensorflow_dependencies/fft2d/shrtdct.c
new file mode 100644
index 0000000..455cb4c
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fft2d/shrtdct.c
@@ -0,0 +1,538 @@
+/*
+Short Discrete Cosine Transform
+    data length :8x8, 16x16
+    method      :row-column, radix 4 FFT
+functions
+    ddct8x8s  : 8x8 DCT
+    ddct16x16s: 16x16 DCT
+function prototypes
+    void ddct8x8s(int isgn, double **a);
+    void ddct16x16s(int isgn, double **a);
+*/
+
+
+/*
+-------- 8x8 DCT (Discrete Cosine Transform) / Inverse of DCT --------
+    [definition]
+        <case1> Normalized 8x8 IDCT
+            C[k1][k2] = (1/4) * sum_j1=0^7 sum_j2=0^7 
+                            a[j1][j2] * s[j1] * s[j2] * 
+                            cos(pi*j1*(k1+1/2)/8) * 
+                            cos(pi*j2*(k2+1/2)/8), 0<=k1<8, 0<=k2<8
+                            (s[0] = 1/sqrt(2), s[j] = 1, j > 0)
+        <case2> Normalized 8x8 DCT
+            C[k1][k2] = (1/4) * s[k1] * s[k2] * sum_j1=0^7 sum_j2=0^7 
+                            a[j1][j2] * 
+                            cos(pi*(j1+1/2)*k1/8) * 
+                            cos(pi*(j2+1/2)*k2/8), 0<=k1<8, 0<=k2<8
+                            (s[0] = 1/sqrt(2), s[j] = 1, j > 0)
+    [usage]
+        <case1>
+            ddct8x8s(1, a);
+        <case2>
+            ddct8x8s(-1, a);
+    [parameters]
+        a[0...7][0...7] :input/output data (double **)
+                         output data
+                             a[k1][k2] = C[k1][k2], 0<=k1<8, 0<=k2<8
+*/
+
+
+/* Cn_kR = sqrt(2.0/n) * cos(pi/2*k/n) */
+/* Cn_kI = sqrt(2.0/n) * sin(pi/2*k/n) */
+/* Wn_kR = cos(pi/2*k/n) */
+/* Wn_kI = sin(pi/2*k/n) */
+#define C8_1R   0.49039264020161522456
+#define C8_1I   0.09754516100806413392
+#define C8_2R   0.46193976625564337806
+#define C8_2I   0.19134171618254488586
+#define C8_3R   0.41573480615127261854
+#define C8_3I   0.27778511650980111237
+#define C8_4R   0.35355339059327376220
+#define W8_4R   0.70710678118654752440
+
+
+void ddct8x8s(int isgn, double **a)
+{
+    int j;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+    double xr, xi;
+    
+    if (isgn < 0) {
+        for (j = 0; j <= 7; j++) {
+            x0r = a[0][j] + a[7][j];
+            x1r = a[0][j] - a[7][j];
+            x0i = a[2][j] + a[5][j];
+            x1i = a[2][j] - a[5][j];
+            x2r = a[4][j] + a[3][j];
+            x3r = a[4][j] - a[3][j];
+            x2i = a[6][j] + a[1][j];
+            x3i = a[6][j] - a[1][j];
+            xr = x0r + x2r;
+            xi = x0i + x2i;
+            a[0][j] = C8_4R * (xr + xi);
+            a[4][j] = C8_4R * (xr - xi);
+            xr = x0r - x2r;
+            xi = x0i - x2i;
+            a[2][j] = C8_2R * xr - C8_2I * xi;
+            a[6][j] = C8_2R * xi + C8_2I * xr;
+            xr = W8_4R * (x1i - x3i);
+            x1i = W8_4R * (x1i + x3i);
+            x3i = x1i - x3r;
+            x1i += x3r;
+            x3r = x1r - xr;
+            x1r += xr;
+            a[1][j] = C8_1R * x1r - C8_1I * x1i;
+            a[7][j] = C8_1R * x1i + C8_1I * x1r;
+            a[3][j] = C8_3R * x3r - C8_3I * x3i;
+            a[5][j] = C8_3R * x3i + C8_3I * x3r;
+        }
+        for (j = 0; j <= 7; j++) {
+            x0r = a[j][0] + a[j][7];
+            x1r = a[j][0] - a[j][7];
+            x0i = a[j][2] + a[j][5];
+            x1i = a[j][2] - a[j][5];
+            x2r = a[j][4] + a[j][3];
+            x3r = a[j][4] - a[j][3];
+            x2i = a[j][6] + a[j][1];
+            x3i = a[j][6] - a[j][1];
+            xr = x0r + x2r;
+            xi = x0i + x2i;
+            a[j][0] = C8_4R * (xr + xi);
+            a[j][4] = C8_4R * (xr - xi);
+            xr = x0r - x2r;
+            xi = x0i - x2i;
+            a[j][2] = C8_2R * xr - C8_2I * xi;
+            a[j][6] = C8_2R * xi + C8_2I * xr;
+            xr = W8_4R * (x1i - x3i);
+            x1i = W8_4R * (x1i + x3i);
+            x3i = x1i - x3r;
+            x1i += x3r;
+            x3r = x1r - xr;
+            x1r += xr;
+            a[j][1] = C8_1R * x1r - C8_1I * x1i;
+            a[j][7] = C8_1R * x1i + C8_1I * x1r;
+            a[j][3] = C8_3R * x3r - C8_3I * x3i;
+            a[j][5] = C8_3R * x3i + C8_3I * x3r;
+        }
+    } else {
+        for (j = 0; j <= 7; j++) {
+            x1r = C8_1R * a[1][j] + C8_1I * a[7][j];
+            x1i = C8_1R * a[7][j] - C8_1I * a[1][j];
+            x3r = C8_3R * a[3][j] + C8_3I * a[5][j];
+            x3i = C8_3R * a[5][j] - C8_3I * a[3][j];
+            xr = x1r - x3r;
+            xi = x1i + x3i;
+            x1r += x3r;
+            x3i -= x1i;
+            x1i = W8_4R * (xr + xi);
+            x3r = W8_4R * (xr - xi);
+            xr = C8_2R * a[2][j] + C8_2I * a[6][j];
+            xi = C8_2R * a[6][j] - C8_2I * a[2][j];
+            x0r = C8_4R * (a[0][j] + a[4][j]);
+            x0i = C8_4R * (a[0][j] - a[4][j]);
+            x2r = x0r - xr;
+            x2i = x0i - xi;
+            x0r += xr;
+            x0i += xi;
+            a[0][j] = x0r + x1r;
+            a[7][j] = x0r - x1r;
+            a[2][j] = x0i + x1i;
+            a[5][j] = x0i - x1i;
+            a[4][j] = x2r - x3i;
+            a[3][j] = x2r + x3i;
+            a[6][j] = x2i - x3r;
+            a[1][j] = x2i + x3r;
+        }
+        for (j = 0; j <= 7; j++) {
+            x1r = C8_1R * a[j][1] + C8_1I * a[j][7];
+            x1i = C8_1R * a[j][7] - C8_1I * a[j][1];
+            x3r = C8_3R * a[j][3] + C8_3I * a[j][5];
+            x3i = C8_3R * a[j][5] - C8_3I * a[j][3];
+            xr = x1r - x3r;
+            xi = x1i + x3i;
+            x1r += x3r;
+            x3i -= x1i;
+            x1i = W8_4R * (xr + xi);
+            x3r = W8_4R * (xr - xi);
+            xr = C8_2R * a[j][2] + C8_2I * a[j][6];
+            xi = C8_2R * a[j][6] - C8_2I * a[j][2];
+            x0r = C8_4R * (a[j][0] + a[j][4]);
+            x0i = C8_4R * (a[j][0] - a[j][4]);
+            x2r = x0r - xr;
+            x2i = x0i - xi;
+            x0r += xr;
+            x0i += xi;
+            a[j][0] = x0r + x1r;
+            a[j][7] = x0r - x1r;
+            a[j][2] = x0i + x1i;
+            a[j][5] = x0i - x1i;
+            a[j][4] = x2r - x3i;
+            a[j][3] = x2r + x3i;
+            a[j][6] = x2i - x3r;
+            a[j][1] = x2i + x3r;
+        }
+    }
+}
+
+
+
+/*
+-------- 16x16 DCT (Discrete Cosine Transform) / Inverse of DCT --------
+    [definition]
+        <case1> Normalized 16x16 IDCT
+            C[k1][k2] = (1/8) * sum_j1=0^15 sum_j2=0^15 
+                            a[j1][j2] * s[j1] * s[j2] * 
+                            cos(pi*j1*(k1+1/2)/16) * 
+                            cos(pi*j2*(k2+1/2)/16), 0<=k1<16, 0<=k2<16
+                            (s[0] = 1/sqrt(2), s[j] = 1, j > 0)
+        <case2> Normalized 16x16 DCT
+            C[k1][k2] = (1/8) * s[k1] * s[k2] * sum_j1=0^15 sum_j2=0^15 
+                            a[j1][j2] * 
+                            cos(pi*(j1+1/2)*k1/16) * 
+                            cos(pi*(j2+1/2)*k2/16), 0<=k1<16, 0<=k2<16
+                            (s[0] = 1/sqrt(2), s[j] = 1, j > 0)
+    [usage]
+        <case1>
+            ddct16x16s(1, a);
+        <case2>
+            ddct16x16s(-1, a);
+    [parameters]
+        a[0...15][0...15] :input/output data (double **)
+                           output data
+                               a[k1][k2] = C[k1][k2], 0<=k1<16, 0<=k2<16
+*/
+
+
+/* Cn_kR = sqrt(2.0/n) * cos(pi/2*k/n) */
+/* Cn_kI = sqrt(2.0/n) * sin(pi/2*k/n) */
+/* Wn_kR = cos(pi/2*k/n) */
+/* Wn_kI = sin(pi/2*k/n) */
+#define C16_1R   0.35185093438159561476
+#define C16_1I   0.03465429229977286565
+#define C16_2R   0.34675996133053686546
+#define C16_2I   0.06897484482073575308
+#define C16_3R   0.33832950029358816957
+#define C16_3I   0.10263113188058934529
+#define C16_4R   0.32664074121909413196
+#define C16_4I   0.13529902503654924610
+#define C16_5R   0.31180625324666780814
+#define C16_5I   0.16666391461943662432
+#define C16_6R   0.29396890060483967924
+#define C16_6I   0.19642373959677554532
+#define C16_7R   0.27330046675043937206
+#define C16_7I   0.22429189658565907106
+#define C16_8R   0.25
+#define W16_4R   0.92387953251128675613
+#define W16_4I   0.38268343236508977173
+#define W16_8R   0.70710678118654752440
+
+
+void ddct16x16s(int isgn, double **a)
+{
+    int j;
+    double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+    double x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i;
+    double xr, xi;
+    
+    if (isgn < 0) {
+        for (j = 0; j <= 15; j++) {
+            x4r = a[0][j] - a[15][j];
+            xr = a[0][j] + a[15][j];
+            x4i = a[8][j] - a[7][j];
+            xi = a[8][j] + a[7][j];
+            x0r = xr + xi;
+            x0i = xr - xi;
+            x5r = a[2][j] - a[13][j];
+            xr = a[2][j] + a[13][j];
+            x5i = a[10][j] - a[5][j];
+            xi = a[10][j] + a[5][j];
+            x1r = xr + xi;
+            x1i = xr - xi;
+            x6r = a[4][j] - a[11][j];
+            xr = a[4][j] + a[11][j];
+            x6i = a[12][j] - a[3][j];
+            xi = a[12][j] + a[3][j];
+            x2r = xr + xi;
+            x2i = xr - xi;
+            x7r = a[6][j] - a[9][j];
+            xr = a[6][j] + a[9][j];
+            x7i = a[14][j] - a[1][j];
+            xi = a[14][j] + a[1][j];
+            x3r = xr + xi;
+            x3i = xr - xi;
+            xr = x0r + x2r;
+            xi = x1r + x3r;
+            a[0][j] = C16_8R * (xr + xi);
+            a[8][j] = C16_8R * (xr - xi);
+            xr = x0r - x2r;
+            xi = x1r - x3r;
+            a[4][j] = C16_4R * xr - C16_4I * xi;
+            a[12][j] = C16_4R * xi + C16_4I * xr;
+            x0r = W16_8R * (x1i - x3i);
+            x2r = W16_8R * (x1i + x3i);
+            xr = x0i + x0r;
+            xi = x2r + x2i;
+            a[2][j] = C16_2R * xr - C16_2I * xi;
+            a[14][j] = C16_2R * xi + C16_2I * xr;
+            xr = x0i - x0r;
+            xi = x2r - x2i;
+            a[6][j] = C16_6R * xr - C16_6I * xi;
+            a[10][j] = C16_6R * xi + C16_6I * xr;
+            xr = W16_8R * (x6r - x6i);
+            xi = W16_8R * (x6i + x6r);
+            x6r = x4r - xr;
+            x6i = x4i - xi;
+            x4r += xr;
+            x4i += xi;
+            xr = W16_4I * x7r - W16_4R * x7i;
+            xi = W16_4I * x7i + W16_4R * x7r;
+            x7r = W16_4R * x5r - W16_4I * x5i;
+            x7i = W16_4R * x5i + W16_4I * x5r;
+            x5r = x7r + xr;
+            x5i = x7i + xi;
+            x7r -= xr;
+            x7i -= xi;
+            xr = x4r + x5r;
+            xi = x5i + x4i;
+            a[1][j] = C16_1R * xr - C16_1I * xi;
+            a[15][j] = C16_1R * xi + C16_1I * xr;
+            xr = x4r - x5r;
+            xi = x5i - x4i;
+            a[7][j] = C16_7R * xr - C16_7I * xi;
+            a[9][j] = C16_7R * xi + C16_7I * xr;
+            xr = x6r - x7i;
+            xi = x7r + x6i;
+            a[5][j] = C16_5R * xr - C16_5I * xi;
+            a[11][j] = C16_5R * xi + C16_5I * xr;
+            xr = x6r + x7i;
+            xi = x7r - x6i;
+            a[3][j] = C16_3R * xr - C16_3I * xi;
+            a[13][j] = C16_3R * xi + C16_3I * xr;
+        }
+        for (j = 0; j <= 15; j++) {
+            x4r = a[j][0] - a[j][15];
+            xr = a[j][0] + a[j][15];
+            x4i = a[j][8] - a[j][7];
+            xi = a[j][8] + a[j][7];
+            x0r = xr + xi;
+            x0i = xr - xi;
+            x5r = a[j][2] - a[j][13];
+            xr = a[j][2] + a[j][13];
+            x5i = a[j][10] - a[j][5];
+            xi = a[j][10] + a[j][5];
+            x1r = xr + xi;
+            x1i = xr - xi;
+            x6r = a[j][4] - a[j][11];
+            xr = a[j][4] + a[j][11];
+            x6i = a[j][12] - a[j][3];
+            xi = a[j][12] + a[j][3];
+            x2r = xr + xi;
+            x2i = xr - xi;
+            x7r = a[j][6] - a[j][9];
+            xr = a[j][6] + a[j][9];
+            x7i = a[j][14] - a[j][1];
+            xi = a[j][14] + a[j][1];
+            x3r = xr + xi;
+            x3i = xr - xi;
+            xr = x0r + x2r;
+            xi = x1r + x3r;
+            a[j][0] = C16_8R * (xr + xi);
+            a[j][8] = C16_8R * (xr - xi);
+            xr = x0r - x2r;
+            xi = x1r - x3r;
+            a[j][4] = C16_4R * xr - C16_4I * xi;
+            a[j][12] = C16_4R * xi + C16_4I * xr;
+            x0r = W16_8R * (x1i - x3i);
+            x2r = W16_8R * (x1i + x3i);
+            xr = x0i + x0r;
+            xi = x2r + x2i;
+            a[j][2] = C16_2R * xr - C16_2I * xi;
+            a[j][14] = C16_2R * xi + C16_2I * xr;
+            xr = x0i - x0r;
+            xi = x2r - x2i;
+            a[j][6] = C16_6R * xr - C16_6I * xi;
+            a[j][10] = C16_6R * xi + C16_6I * xr;
+            xr = W16_8R * (x6r - x6i);
+            xi = W16_8R * (x6i + x6r);
+            x6r = x4r - xr;
+            x6i = x4i - xi;
+            x4r += xr;
+            x4i += xi;
+            xr = W16_4I * x7r - W16_4R * x7i;
+            xi = W16_4I * x7i + W16_4R * x7r;
+            x7r = W16_4R * x5r - W16_4I * x5i;
+            x7i = W16_4R * x5i + W16_4I * x5r;
+            x5r = x7r + xr;
+            x5i = x7i + xi;
+            x7r -= xr;
+            x7i -= xi;
+            xr = x4r + x5r;
+            xi = x5i + x4i;
+            a[j][1] = C16_1R * xr - C16_1I * xi;
+            a[j][15] = C16_1R * xi + C16_1I * xr;
+            xr = x4r - x5r;
+            xi = x5i - x4i;
+            a[j][7] = C16_7R * xr - C16_7I * xi;
+            a[j][9] = C16_7R * xi + C16_7I * xr;
+            xr = x6r - x7i;
+            xi = x7r + x6i;
+            a[j][5] = C16_5R * xr - C16_5I * xi;
+            a[j][11] = C16_5R * xi + C16_5I * xr;
+            xr = x6r + x7i;
+            xi = x7r - x6i;
+            a[j][3] = C16_3R * xr - C16_3I * xi;
+            a[j][13] = C16_3R * xi + C16_3I * xr;
+        }
+    } else {
+        for (j = 0; j <= 15; j++) {
+            x5r = C16_1R * a[1][j] + C16_1I * a[15][j];
+            x5i = C16_1R * a[15][j] - C16_1I * a[1][j];
+            xr = C16_7R * a[7][j] + C16_7I * a[9][j];
+            xi = C16_7R * a[9][j] - C16_7I * a[7][j];
+            x4r = x5r + xr;
+            x4i = x5i - xi;
+            x5r -= xr;
+            x5i += xi;
+            x7r = C16_5R * a[5][j] + C16_5I * a[11][j];
+            x7i = C16_5R * a[11][j] - C16_5I * a[5][j];
+            xr = C16_3R * a[3][j] + C16_3I * a[13][j];
+            xi = C16_3R * a[13][j] - C16_3I * a[3][j];
+            x6r = x7r + xr;
+            x6i = x7i - xi;
+            x7r -= xr;
+            x7i += xi;
+            xr = x4r - x6r;
+            xi = x4i - x6i;
+            x4r += x6r;
+            x4i += x6i;
+            x6r = W16_8R * (xi + xr);
+            x6i = W16_8R * (xi - xr);
+            xr = x5r + x7i;
+            xi = x5i - x7r;
+            x5r -= x7i;
+            x5i += x7r;
+            x7r = W16_4I * x5r + W16_4R * x5i;
+            x7i = W16_4I * x5i - W16_4R * x5r;
+            x5r = W16_4R * xr + W16_4I * xi;
+            x5i = W16_4R * xi - W16_4I * xr;
+            xr = C16_4R * a[4][j] + C16_4I * a[12][j];
+            xi = C16_4R * a[12][j] - C16_4I * a[4][j];
+            x2r = C16_8R * (a[0][j] + a[8][j]);
+            x3r = C16_8R * (a[0][j] - a[8][j]);
+            x0r = x2r + xr;
+            x1r = x3r + xi;
+            x2r -= xr;
+            x3r -= xi;
+            x0i = C16_2R * a[2][j] + C16_2I * a[14][j];
+            x2i = C16_2R * a[14][j] - C16_2I * a[2][j];
+            x1i = C16_6R * a[6][j] + C16_6I * a[10][j];
+            x3i = C16_6R * a[10][j] - C16_6I * a[6][j];
+            xr = x0i - x1i;
+            xi = x2i + x3i;
+            x0i += x1i;
+            x2i -= x3i;
+            x1i = W16_8R * (xi + xr);
+            x3i = W16_8R * (xi - xr);
+            xr = x0r + x0i;
+            xi = x0r - x0i;
+            a[0][j] = xr + x4r;
+            a[15][j] = xr - x4r;
+            a[8][j] = xi + x4i;
+            a[7][j] = xi - x4i;
+            xr = x1r + x1i;
+            xi = x1r - x1i;
+            a[2][j] = xr + x5r;
+            a[13][j] = xr - x5r;
+            a[10][j] = xi + x5i;
+            a[5][j] = xi - x5i;
+            xr = x2r + x2i;
+            xi = x2r - x2i;
+            a[4][j] = xr + x6r;
+            a[11][j] = xr - x6r;
+            a[12][j] = xi + x6i;
+            a[3][j] = xi - x6i;
+            xr = x3r + x3i;
+            xi = x3r - x3i;
+            a[6][j] = xr + x7r;
+            a[9][j] = xr - x7r;
+            a[14][j] = xi + x7i;
+            a[1][j] = xi - x7i;
+        }
+        for (j = 0; j <= 15; j++) {
+            x5r = C16_1R * a[j][1] + C16_1I * a[j][15];
+            x5i = C16_1R * a[j][15] - C16_1I * a[j][1];
+            xr = C16_7R * a[j][7] + C16_7I * a[j][9];
+            xi = C16_7R * a[j][9] - C16_7I * a[j][7];
+            x4r = x5r + xr;
+            x4i = x5i - xi;
+            x5r -= xr;
+            x5i += xi;
+            x7r = C16_5R * a[j][5] + C16_5I * a[j][11];
+            x7i = C16_5R * a[j][11] - C16_5I * a[j][5];
+            xr = C16_3R * a[j][3] + C16_3I * a[j][13];
+            xi = C16_3R * a[j][13] - C16_3I * a[j][3];
+            x6r = x7r + xr;
+            x6i = x7i - xi;
+            x7r -= xr;
+            x7i += xi;
+            xr = x4r - x6r;
+            xi = x4i - x6i;
+            x4r += x6r;
+            x4i += x6i;
+            x6r = W16_8R * (xi + xr);
+            x6i = W16_8R * (xi - xr);
+            xr = x5r + x7i;
+            xi = x5i - x7r;
+            x5r -= x7i;
+            x5i += x7r;
+            x7r = W16_4I * x5r + W16_4R * x5i;
+            x7i = W16_4I * x5i - W16_4R * x5r;
+            x5r = W16_4R * xr + W16_4I * xi;
+            x5i = W16_4R * xi - W16_4I * xr;
+            xr = C16_4R * a[j][4] + C16_4I * a[j][12];
+            xi = C16_4R * a[j][12] - C16_4I * a[j][4];
+            x2r = C16_8R * (a[j][0] + a[j][8]);
+            x3r = C16_8R * (a[j][0] - a[j][8]);
+            x0r = x2r + xr;
+            x1r = x3r + xi;
+            x2r -= xr;
+            x3r -= xi;
+            x0i = C16_2R * a[j][2] + C16_2I * a[j][14];
+            x2i = C16_2R * a[j][14] - C16_2I * a[j][2];
+            x1i = C16_6R * a[j][6] + C16_6I * a[j][10];
+            x3i = C16_6R * a[j][10] - C16_6I * a[j][6];
+            xr = x0i - x1i;
+            xi = x2i + x3i;
+            x0i += x1i;
+            x2i -= x3i;
+            x1i = W16_8R * (xi + xr);
+            x3i = W16_8R * (xi - xr);
+            xr = x0r + x0i;
+            xi = x0r - x0i;
+            a[j][0] = xr + x4r;
+            a[j][15] = xr - x4r;
+            a[j][8] = xi + x4i;
+            a[j][7] = xi - x4i;
+            xr = x1r + x1i;
+            xi = x1r - x1i;
+            a[j][2] = xr + x5r;
+            a[j][13] = xr - x5r;
+            a[j][10] = xi + x5i;
+            a[j][5] = xi - x5i;
+            xr = x2r + x2i;
+            xi = x2r - x2i;
+            a[j][4] = xr + x6r;
+            a[j][11] = xr - x6r;
+            a[j][12] = xi + x6i;
+            a[j][3] = xi - x6i;
+            xr = x3r + x3i;
+            xi = x3r - x3i;
+            a[j][6] = xr + x7r;
+            a[j][9] = xr - x7r;
+            a[j][14] = xi + x7i;
+            a[j][1] = xi - x7i;
+        }
+    }
+}
+
diff --git a/third_party/tensorflow_dependencies/flatbuffers b/third_party/tensorflow_dependencies/flatbuffers
new file mode 160000
index 0000000..6d0aae7
--- /dev/null
+++ b/third_party/tensorflow_dependencies/flatbuffers
@@ -0,0 +1 @@
+Subproject commit 6d0aae73cd2ad3f8ee93c952af6b33e99049ad9c
diff --git a/third_party/tensorflow_dependencies/fp16 b/third_party/tensorflow_dependencies/fp16
new file mode 160000
index 0000000..4dfe081
--- /dev/null
+++ b/third_party/tensorflow_dependencies/fp16
@@ -0,0 +1 @@
+Subproject commit 4dfe081cf6bcd15db339cf2680b9281b8451eeb3
diff --git a/third_party/tensorflow_dependencies/gemmlowp b/third_party/tensorflow_dependencies/gemmlowp
new file mode 160000
index 0000000..fda83bd
--- /dev/null
+++ b/third_party/tensorflow_dependencies/gemmlowp
@@ -0,0 +1 @@
+Subproject commit fda83bdc38b118cc6b56753bd540caa49e570745
diff --git a/third_party/tensorflow_dependencies/neon_2_sse b/third_party/tensorflow_dependencies/neon_2_sse
new file mode 160000
index 0000000..42b2beb
--- /dev/null
+++ b/third_party/tensorflow_dependencies/neon_2_sse
@@ -0,0 +1 @@
+Subproject commit 42b2bebacee25452e150095ef4480b3fa26e30f5
diff --git a/third_party/tensorflow_dependencies/ruy b/third_party/tensorflow_dependencies/ruy
new file mode 160000
index 0000000..bebf022
--- /dev/null
+++ b/third_party/tensorflow_dependencies/ruy
@@ -0,0 +1 @@
+Subproject commit bebf022784e9b22277b84373c9877aebff8411a7
diff --git a/tools/convexhull_framework/README.TXT b/tools/convexhull_framework/README.TXT
new file mode 100644
index 0000000..22e1314
--- /dev/null
+++ b/tools/convexhull_framework/README.TXT
@@ -0,0 +1,167 @@
+This testing framework is initial developed by Intel for convex hull study.
+It is contributed to Alliance of Open Media for convex hull study and future AV2
+Common Test Condition (CTC) testing.
+
+For questions and technical support, please contact ryan.lei@intel.com or
+maggie.sun@intel.com
+
+This readme file provides a simple introduction of the framework and steps to
+use it for different studies.
+
+Prerequisites
+1. Inside ./bin folder, make sure you have executables for all external tools,
+   such as vmafossexec, ffmpeg, HDRMetrics, encoder, decoder, etc. You can get
+   the pre-build binary or source code for the following tools that are needed:
+   ffmpeg: https://ffmpeg.org/download.html
+   vmaf tools: https://github.com/Netflix/vmaf
+   HDRMetrics: https://gitlab.com/standards/HDRTools
+   libaom AV1 encoder/decoder: https://aomedia.googlesource.com/aom/
+   SVT HEVC encoder: https://github.com/OpenVisualCloud/SVT-HEVC
+   SVT AV1 encoder: https://github.com/OpenVisualCloud/SVT-AV1
+   Please always follow the manual of these tools to update the command
+   parameters used in the script
+
+2. VMAF requires a model file to run. currently, vmaf_v0.6.1 model files
+   (vmaf_v0.6.1.pkl.model, vmaf_v0.6.1.pkl) are used and stored under the ./bin
+   folder.
+
+3. HDRMetrics requires a template config file (HDRMetricsYUV420_template.cfg).
+   It is also located under the ./bin folder. Test framework will generate
+   individual config file based on this template.
+
+4. VBA macro binary file (vbaProject-AV2.bin) for bdrate calculation is also
+   stored under ./bin folder, which is needed for BDRATE calculation inside the
+   excel file.
+
+5. Test framework is implemented in python 3. It requires few extra python
+   packages, such as xlrd, xlsxwriter, argparse, etc.
+
+Things you need to update inside ./src/Config.py to configure the test.
+1. Update the Clips table following the existing example to provide the list of
+   video sequences that you want to test. The first value is the yuv file name
+   without .yuv suffix. All test video sequences should be in the folder
+   specified by ContentPath. Test framework will extract the string before the
+   first "_" as the short name to identify the test sequence and use that to
+   generate file name of other intermediate files, so please make sure the short
+   name of the test sequence is unique. Currently only 8 bit yuv sequences in
+   yuv420p format are supported.
+
+2. Update FrameNum to specify number of frames you want to process.
+
+3. Update DnScaleRatio list to provide the list of downscaling ratios.
+
+4. Update DnScalingAlgos and UpScalingAlgos list to specify downscaling/upscaling
+   filter types that you want to test. The name of the filter types is that
+   supported by ffmpeg. The size of these two lists must be the same. Filter
+   types for downscaling and upscaling can be different.
+
+5. Update QPs list to specify the list of QPs you want to test for different
+   codecs. QPs must be in the valid QP range for different coding standards.
+   For example, [0, 51] for hevc and [0, 63] for AV1
+
+6. Update QualityEvalMethods to specify tools you want to use to calculate
+   quality metrics in the QualityList. Currently, only VMAF and HDRTools are
+   supported. You also need to update QualityList and QualityEvalMethods to
+   specify what quality metrics you want to calculate. Test framework will filter
+   out other metrics and only report the metrics that are specified.
+
+7. Update LogCmdOnly flag. When it is set to True, the test framework will only
+   capture all process command sequences into a log file (under ./test/testLogs
+   folder with time stamp) without actually running it on your system. This
+   feature is to support the use case in which actual processing tasks need to
+   distributed on to a server cluster and executed in parallel.
+
+8. Update SMOKE_TEST flag, It is used for sanity check purpose, in which only
+   few frames are processed to reduce the test time.
+
+Command lines for running the tests:
+below is the full command line options in help message:
+  -h, --help                   show this help message and exit
+  -f, --function               function to run: clean, scaling, sumscaling,
+                               encode, convexhull, summary
+  -k, --KeepUpscaleOutput      [0|1] in function clean, if keep upscaled yuv
+                               files. It is false by default
+  -s, --SaveMemory             [0|1] save memory mode will delete most files in
+                               intermediate steps and keeps only necessary ones
+                               for RD calculation. It is false by default
+  -l, --LoggingLevel           logging level: 0:No Logging, 1: Critical, 2: Error,
+                               3:Warning, 4: Info, 5: Debug
+  -c, --CodecName              CodecName: av1 or hevc
+  -m, --EncodeMethod           EncodeMethod: ffmpeg, aom, svt
+  -p, --EncodePreset           EncodePreset: medium, slow, fast, etc for ffmpeg,
+                               0,1,2... for aom and svt
+
+Sample command for typical operations:
+1.  python ConvexHullTest.py -f clean
+    This command will clean up all intermediate files under ./test folder
+
+2.  python ConvexHullTest.py -f scaling
+    This command will run the standalone downscaling and upscaling tests.
+    Downscaled YUV files are stored under
+    ./test/downscaledYUVs folder. Upscaled YUV files are stored under
+    ./test/upscaledYUVs folder. Quality metrics log files are stored under
+    ./test/qualityLogs folder. Other processing logs and command logs are stored
+    under ./test/testLogs folder. In case HDRMetric is used, individual config
+    files are generated and stored under ./test/configFiles folder. All
+    intermediate file names indicate the input, output resolution and filter types
+    that are used.
+
+3.  python ConvexHullTest.py -f sumscaling
+    This command will summarize the quality metrics for the scaling test into
+    excel files under ./analysis/scalingresult folder. There are excel files for
+    each individual test sequence and also excel file that summarizes quality
+    result for all test sequences based on classes.
+
+4.  python ConvexHullTest.py -f encode -c hevc -m ffmpeg -p medium
+    This command will run the encoding test. It actually contains downscale
+    (optional), encode, decode, upscale, quality metrics steps. Different
+    encoding method and codec name can be specified. For a particular encoder,
+    encoding preset should also be specified. Downscale step will be skipped if
+    downscaled yuv files already generated in ./test/downscaledYUVs. Encoded
+    bitstreams are stored under ./test/bitstreams folder. Decoded YUV files are
+    stored under ./test/decodedYUVs folder. Decoded and then upscaled YUV files
+    are stored under ./test/decUpscaledYUVs folder. Quality logs are stored under
+    ./test/qualityLogs folder.
+
+5.  python ConvexHullTest.py -f convexhull -c hevc -m ffmpeg -p medium
+    This command will summarize the per sequence quality result based on
+    different scaling ratios and scaling filter types.
+    Please make sure the same encoding method/codec name/preset are used as the
+    previous steps. Output excel files are stored under ./analysis/rdresult
+    folder. For each sequence and downscaling/upscaling filter type, an excel
+    sheet is generated that contains the bitrate and quality metric for different
+    downscaled resolutions. Rate distortion curve for all quality metrics will
+    be drawn in a scatter plot. Convex hull will be calculated and draw on top
+    of the rate distortion curve.
+    In this step, a summary excel file is also generated, which include the
+    convex hull rate distortion points for all test content and downscaling and
+    upscaling algorithm pair. This excel file is needed when you want to
+    calculate bdrate between two encoding runs to evaluate the quality impact on
+    overall convex hull from a coding tool.
+
+6.  python ConvexHullTest.py -f summary -c hevc -m ffmpeg -p medium
+    This command will summarize the quality metrics across all test sequences
+    into an excel file stored under the ./analysis/summary folder. BDRATE between
+    different resolutions will be calculated. Average result based on content
+    classes will be calculated.
+
+In order to calculate BDRATE between convex hull from two encoding test runs
+with different codec or different encoding preset, ./src/ConvexHullBDRate.py
+script is needed. Command line options for this script is as following:
+
+usage: ConvexHullBDRate.py [options]
+
+optional arguments:
+  -h, --help       show this help message and exit
+  -i1 , --input1   convex hull summary excel file for base mode
+  -i2 , --input2   convex hull summary excel file for target mode
+  -o , --output    output excel file with BDRATE for base and target modes
+
+To use this script, the convex hull summary excel files for base and target
+modes are needed. Script will parse the rate distortion data from the convex hull
+for each individual test content, then it will use the bdrate macro to calculate
+the bdrate between two convex hulls.
+
+a sample command is:
+python ConvexHullBDRate.py -i1 ConvexHullRD_ffmpeg_hevc_medium.xlsx
+-i2 ConvexHullRD_ffmpeg_hevc_veryslow.xlsx -o ConvexHullBDRate.xlsm
diff --git a/tools/convexhull_framework/bin/HDRMetricsYUV420_template.cfg b/tools/convexhull_framework/bin/HDRMetricsYUV420_template.cfg
new file mode 100644
index 0000000..3a21c63
--- /dev/null
+++ b/tools/convexhull_framework/bin/HDRMetricsYUV420_template.cfg
@@ -0,0 +1,99 @@
+# HDRMetrics default configuration file
+# format: parameter=value or parameter="stringvalue", no SPACES!
+
+###############################################################################
+#
+# Input file information
+#
+###############################################################################
+Input0File=""    # 1st Input file name
+Input1File=""   # 2nd Input file name
+LogFile=""                                                     # Output Log file name
+NumberOfFrames=10                                              # Number of frames to process
+SilentMode=0                                                   # Enable Silent mode
+MaxSampleValue=10000.0                                         # Maximum sample value for floating point (openEXR) data files
+###############################################################################
+#
+# Metrics
+#
+###############################################################################
+EnableMSSSIM=1                                                 # Enable MSSSIM computation
+EnableSSIM=1 	                                               # Enable SSIM computation
+
+###############################################################################
+#
+# First input parameters
+#
+###############################################################################
+Input0Width=1280                                               # 1st Input source width
+Input0Height=720                                               # 1st Input source height
+Input0ChromaFormat=1                                           # 1st Input Chroma Format
+                                                               # 0 : 400
+                                                               # 1 : 420
+                                                               # 2 : 422
+                                                               # 3 : 444
+Input0Interleaved=0                                            # 1st Input Interleaved Source
+Input0FourCCCode=0                                             # 1st Input Pixel Format
+                                                               # 0: UYVY
+                                                               # 1: YUY2
+                                                               # 2: YUYV
+                                                               # 3: YVYU
+                                                               # 4: BGR
+                                                               # 5: V210
+                                                               # 6: Y444I
+Input0BitDepthCmp0=8                                           # 1st Input Bitdepth Cmp0
+Input0BitDepthCmp1=8                                           # 1st Input Bitdepth Cmp1
+Input0BitDepthCmp2=8                                           # 1st Input Bitdepth Cmp2
+Input0ColorSpace=0                                             # 1st Input Color Space
+                                                               # 0: YUV
+                                                               # 1: RGB
+                                                               # 2: XYZ
+Input0ColorPrimaries=0                                         # 1st Input Color Primaries
+                                                               # 0: BT.709
+                                                               # 1: BT.2020
+                                                               # 2: P3D60
+                                                               # 3: P3D65
+                                                               # 4: None
+Input0FileHeader=0                                             # 1st Input Header (bytes)
+Input0StartFrame=0                                             # 1st Input Start Frame
+Input0FrameSkip=0                                              # 1st Input Frame Skipping
+Input0Rate=24.0                                                # 1st Input Frame Rate
+
+###############################################################################
+#
+# Second input source parameters
+#
+###############################################################################
+Input1Width=1280                                               # 2nd Input source width
+Input1Height=720                                               # 2nd Input source height
+Input1ChromaFormat=1                                           # 2nd Input Chroma Format
+                                                               # 0 : 400
+                                                               # 1 : 420
+                                                               # 2 : 422
+                                                               # 3 : 444
+Input1Interleaved=0                                            # 2nd Input Interleaved Source
+Input1FourCCCode=0                                             # 2nd Input Pixel Format
+                                                               # 0: UYVY
+                                                               # 1: YUY2
+                                                               # 2: YUYV
+                                                               # 3: YVYU
+                                                               # 4: BGR
+                                                               # 5: V210
+                                                               # 6: Y444I
+Input1BitDepthCmp0=8                                           # 2nd Input Bitdepth Cmp0
+Input1BitDepthCmp1=8                                           # 2nd Input Bitdepth Cmp1
+Input1BitDepthCmp2=8                                           # 2nd Input Bitdepth Cmp2
+Input1ColorSpace=0                                             # 2nd Input Color Space
+                                                               # 0: YUV
+                                                               # 1: RGB
+                                                               # 2: XYZ
+Input1ColorPrimaries=0                                         # 2nd Input Color Primaries
+                                                               # 0: BT.709
+                                                               # 1: BT.2020
+                                                               # 2: P3D60
+                                                               # 3: P3D65
+                                                               # 4: None
+Input1FileHeader=0                                             # 2nd Input Header (bytes)
+Input1StartFrame=0                                             # 2nd Input Start Frame
+Input1FrameSkip=0                                              # 2nd Input Frame Skipping
+Input0Rate=24.0                                                # 2nd Input Frame Rate
diff --git a/tools/convexhull_framework/bin/vbaProject-AV2.bin b/tools/convexhull_framework/bin/vbaProject-AV2.bin
new file mode 100644
index 0000000..a9df27b
--- /dev/null
+++ b/tools/convexhull_framework/bin/vbaProject-AV2.bin
Binary files differ
diff --git a/tools/convexhull_framework/bin/vbaProject_JVET-L0242.bin b/tools/convexhull_framework/bin/vbaProject_JVET-L0242.bin
new file mode 100644
index 0000000..0154878
--- /dev/null
+++ b/tools/convexhull_framework/bin/vbaProject_JVET-L0242.bin
Binary files differ
diff --git a/tools/convexhull_framework/bin/vmaf_v0.6.1.pkl b/tools/convexhull_framework/bin/vmaf_v0.6.1.pkl
new file mode 100644
index 0000000..59b880f
--- /dev/null
+++ b/tools/convexhull_framework/bin/vmaf_v0.6.1.pkl
@@ -0,0 +1,108 @@
+(dp0
+S'param_dict'
+p1
+(dp2
+S'C'
+p3
+F4.0
+sS'score_transform'
+p4
+(dp5
+S'p2'
+p6
+F-0.00705305
+sS'out_gte_in'
+p7
+S'true'
+p8
+sS'p0'
+p9
+F1.70674692
+sS'p1'
+p10
+F1.72643844
+ssS'norm_type'
+p11
+S'clip_0to1'
+p12
+sS'score_clip'
+p13
+(lp14
+F0.0
+aF100.0
+asS'nu'
+p15
+F0.9
+sS'gamma'
+p16
+F0.04
+ssS'model_dict'
+p17
+(dp18
+S'model'
+p19
+Nsg4
+g5
+sg11
+S'linear_rescale'
+p20
+sg13
+g14
+sS'feature_names'
+p21
+(lp22
+S'VMAF_feature_adm2_score'
+p23
+aS'VMAF_feature_motion2_score'
+p24
+aS'VMAF_feature_vif_scale0_score'
+p25
+aS'VMAF_feature_vif_scale1_score'
+p26
+aS'VMAF_feature_vif_scale2_score'
+p27
+aS'VMAF_feature_vif_scale3_score'
+p28
+asS'intercepts'
+p29
+(lp30
+F-0.3092981927591963
+aF-1.7993968597186747
+aF-0.003017198086831897
+aF-0.1728125095425364
+aF-0.5294309090081222
+aF-0.7577185792093722
+aF-1.083428597549764
+asS'model_type'
+p31
+S'LIBSVMNUSVR'
+p32
+sS'slopes'
+p33
+(lp34
+F0.012020766332648465
+aF2.8098077502505414
+aF0.06264407466686016
+aF1.222763456258933
+aF1.5360318811084146
+aF1.7620864995501058
+aF2.08656468286432
+asS'feature_dict'
+p35
+(dp36
+S'VMAF_feature'
+p37
+(lp38
+S'vif_scale0'
+p39
+aS'vif_scale1'
+p40
+aS'vif_scale2'
+p41
+aS'vif_scale3'
+p42
+aS'adm2'
+p43
+aS'motion2'
+p44
+asss.
\ No newline at end of file
diff --git a/tools/convexhull_framework/bin/vmaf_v0.6.1.pkl.model b/tools/convexhull_framework/bin/vmaf_v0.6.1.pkl.model
new file mode 100644
index 0000000..25c4a4b
--- /dev/null
+++ b/tools/convexhull_framework/bin/vmaf_v0.6.1.pkl.model
@@ -0,0 +1,218 @@
+svm_type nu_svr
+kernel_type rbf
+gamma 0.04
+nr_class 2
+total_sv 211
+rho -1.33133
+SV
+-4 1:0.65734273 2:0.34681232 3:0.093755557 4:0.60913934 5:0.69117362 6:0.73495824
+4 1:0.8727433 2:0.49612229 3:0.59146724 4:0.78105663 5:0.84916292 6:0.8882561
+4 1:0.89890005 2:0.49612229 3:0.66823667 4:0.86050887 5:0.90873162 6:0.93335071
+4 1:0.20371751 2:0.49612229 3:0.10534315 4:-1.110223e-16 6:2.220446e-16
+4 1:0.33913836 2:0.49612229 3:0.14024497 4:0.074708413 5:0.10231651 6:0.1259153
+4 1:0.66426757 2:0.49612229 3:0.35268026 4:0.4805681 5:0.59603341 6:0.67408692
+4 1:0.59561632 2:0.49612229 3:0.27561601 4:0.33977371 5:0.4325213 6:0.50244952
+4 1:0.50821444 2:0.49612229 3:0.20276685 4:0.2004308 5:0.25758651 6:0.30054029
+4 1:0.77877298 2:0.49612229 3:0.444392 4:0.61630491 5:0.71210086 6:0.77386496
+4 1:0.71666017 2:0.49612229 3:0.35967401 4:0.47825205 5:0.57045236 6:0.63752441
+4 1:0.64025669 2:0.49612229 3:0.27766156 4:0.33407105 5:0.40732401 6:0.46359154
+4 1:0.88343983 2:0.23066177 3:0.65873851 4:0.86090402 5:0.90661213 6:0.93008753
+4 1:0.90822691 2:0.23066177 3:0.71439481 4:0.90904598 5:0.94146542 6:0.95674338
+-4 1:0.49037399 2:0.23066177 3:0.32329421 4:0.33686197 5:0.39456977 6:0.44944683
+-4 1:0.69044383 2:0.23066177 3:0.43933868 4:0.56327049 5:0.65339511 6:0.71348696
+-4 1:0.62390093 2:0.23066177 3:0.3800888 4:0.44927578 5:0.52327759 6:0.57907725
+4 1:0.81887942 2:0.23066177 3:0.56208506 4:0.76164281 5:0.83176644 6:0.86914911
+4 1:0.77189471 2:0.23066177 3:0.50145055 4:0.66525882 5:0.74327951 6:0.79017822
+4 1:0.71405433 2:0.23066177 3:0.43952897 4:0.55736023 5:0.63319876 6:0.68402869
+4 1:0.92114073 3:0.45198963 4:0.97703695 5:0.9907273 6:0.99510256
+4 1:1 3:0.83319067 4:0.98956086 5:0.99577089 6:0.99784595
+4 4:0.10344019 5:0.34323945 6:0.63855969
+4 1:0.19531482 3:0.034330388 4:0.25480402 5:0.54197045 6:0.78020579
+4 1:0.48394064 3:0.11866359 4:0.58816959 5:0.86435738 6:0.96191842
+4 1:0.47628079 3:0.11185039 4:0.56180003 5:0.83415721 6:0.93617329
+4 1:0.46278632 3:0.10308547 4:0.52247575 5:0.78583924 6:0.89392193
+4 1:0.7038079 3:0.2174879 4:0.84423613 5:0.9662906 6:0.98430594
+4 1:0.69596686 3:0.20657211 4:0.81196884 5:0.94140702 6:0.96680805
+4 1:0.68404358 3:0.19261438 4:0.76066415 5:0.89973293 6:0.93660362
+4 1:0.84073022 2:0.34681232 3:0.22411304 4:0.88845644 5:0.94169671 6:0.96221395
+-4 1:0.33900937 2:0.34681232 3:0.027607294 4:0.40659646 5:0.45456869 6:0.48256597
+-4 1:0.44593129 2:0.34681232 3:0.041939301 4:0.45284872 5:0.5157613 6:0.55335821
+-4 1:0.67301747 2:0.34681232 3:0.11526222 4:0.68549511 5:0.78556255 6:0.83507583
+-4 1:0.62833533 2:0.34681232 3:0.092281981 4:0.61278125 5:0.70626575 6:0.75613977
+-4 1:0.57196879 2:0.34681232 3:0.067548447 4:0.53383404 5:0.61287548 6:0.65468717
+-0.3312466607741135 1:0.75125028 2:0.34681232 3:0.1457048 4:0.75791308 5:0.84155109 6:0.88132116
+-4 1:0.71121936 2:0.34681232 3:0.12095689 4:0.68834617 5:0.77453583 6:0.81892861
+-4 1:0.80269544 2:0.25207203 3:0.3681723 4:0.80658472 5:0.8702283 6:0.90583519
+-4 1:0.86095387 2:0.25207203 3:0.52475418 4:0.85053413 5:0.90454501 6:0.93093678
+-4 1:0.5008963 2:0.25207203 3:0.2005129 4:0.41516485 5:0.45282017 6:0.47396143
+-4 1:0.56977992 2:0.25207203 3:0.21631076 4:0.45848604 5:0.51102137 6:0.53823055
+-4 1:0.72779828 2:0.25207203 3:0.3051639 4:0.67537297 5:0.75767261 6:0.80327187
+-4 1:0.68848569 2:0.25207203 3:0.27393051 4:0.60399854 5:0.68000038 6:0.72275152
+-4 1:0.64121401 2:0.25207203 3:0.23994344 4:0.52538719 5:0.5891732 6:0.62164073
+-4 1:0.76673633 2:0.25207203 3:0.33053889 4:0.73085549 5:0.80341439 6:0.84546456
+-4 1:0.73041172 2:0.25207203 3:0.29691153 4:0.66166141 5:0.73408074 6:0.77757209
+-4 1:0.68529047 2:0.25207203 3:0.26283557 4:0.58611788 5:0.65192525 6:0.69015011
+4 1:0.86902267 2:0.48885268 3:0.5143645 4:0.8587242 5:0.91841685 6:0.94498293
+4 1:0.89266106 2:0.48885268 3:0.55208861 4:0.89938377 5:0.94642982 6:0.96615102
+-4 1:0.42554844 2:0.48885268 3:0.2554221 4:0.36916892 5:0.43100226 6:0.50888404
+-4 1:0.52520274 2:0.48885268 3:0.27824915 4:0.42915458 5:0.50850476 6:0.58585271
+-4 1:0.69357445 2:0.48885268 3:0.35289928 4:0.61359907 5:0.7217863 6:0.78790011
+-4 1:0.64679648 2:0.48885268 3:0.31268451 4:0.5167094 5:0.61224976 6:0.68477529
+4 1:0.80595874 2:0.48885268 3:0.44075432 4:0.7803455 5:0.86328719 6:0.90222545
+-4 1:0.7715192 2:0.48885268 3:0.4012577 4:0.70792536 5:0.80063653 6:0.85083872
+4 1:0.82199966 2:0.20629643 3:0.30562098 4:0.80541317 5:0.89285836 6:0.92907353
+-4 1:0.84774006 2:0.20629643 3:0.36755712 4:0.8681203 5:0.93297792 6:0.95700049
+4 1:0.26631905 2:0.20629643 3:0.076468978 4:0.29833807 5:0.37989948 6:0.4576277
+-4 1:0.65439648 2:0.20629643 3:0.19487894 4:0.63045155 5:0.76931142 6:0.83706632
+4 1:0.55295603 2:0.20629643 3:0.13877412 4:0.4724047 5:0.59295828 6:0.66834832
+4 1:0.75448924 2:0.20629643 3:0.24707248 4:0.72284103 5:0.83178838 6:0.88053503
+4 1:0.83852041 2:0.15600331 3:0.1625414 4:0.81948421 5:0.90185357 6:0.9347395
+4 1:0.85805266 2:0.15600331 3:0.19693206 4:0.86294641 5:0.92990351 6:0.95498998
+-4 1:0.43384835 2:0.15600331 3:0.030541611 4:0.37279112 5:0.4588284 6:0.52004828
+-4 1:0.72588966 2:0.48885268 3:0.35394597 4:0.61189191 5:0.70897304 6:0.77099691
+-4 1:0.65865915 2:0.20629643 3:0.1796405 4:0.56432133 5:0.68049028 6:0.74616621
+-4 1:0.53095193 2:0.15600331 3:0.046271684 4:0.4328793 5:0.5309142 6:0.59282089
+-4 1:0.71891465 2:0.15600331 3:0.11085278 4:0.68794624 5:0.80350923 6:0.85660483
+-4 1:0.68635753 2:0.15600331 3:0.091457045 4:0.60849701 5:0.72282659 6:0.78137183
+-4 1:0.64162333 2:0.15600331 3:0.068820233 4:0.51732819 5:0.62198733 6:0.67977328
+4 1:0.78395225 2:0.15600331 3:0.13401869 4:0.75274384 5:0.8506531 6:0.89321405
+-4 1:0.75276337 2:0.15600331 3:0.11289462 4:0.67598462 5:0.78117168 6:0.83259364
+-4 1:0.71345342 2:0.15600331 3:0.089218917 4:0.58797907 5:0.69284768 6:0.74971699
+4 1:0.93500967 2:0.08765484 3:0.72226864 4:0.93291747 5:0.960644 6:0.97304054
+4 1:0.95150668 2:0.08765484 3:0.77391346 4:0.95596295 5:0.97544784 6:0.98405871
+-4 1:0.48148634 2:0.08765484 3:0.36628046 4:0.45852823 5:0.56005228 6:0.65708595
+-4 1:0.59853216 2:0.08765484 3:0.42071301 4:0.56376512 5:0.66454599 6:0.741236
+-4 1:0.79297271 2:0.08765484 3:0.5597726 4:0.80653689 5:0.88996341 6:0.92691132
+-4 1:0.76798941 2:0.08765484 3:0.52069978 4:0.74484555 5:0.83431246 6:0.87935204
+-4 1:0.73225133 2:0.08765484 3:0.47011786 4:0.66069877 5:0.75226598 6:0.80539407
+-4 1:0.87240592 2:0.08765484 3:0.62680052 4:0.88208508 5:0.93041565 6:0.9505376
+-4 1:0.84834872 2:0.08765484 3:0.58154998 4:0.82429855 5:0.8858516 6:0.91563291
+-4 1:0.84365382 2:0.93973481 3:0.36718425 4:0.81512123 5:0.88887359 6:0.92320992
+-4 1:0.89242364 2:0.93973481 3:0.41336953 4:0.88038833 5:0.93688884 6:0.95992879
+-4 1:0.31373571 2:0.93973481 3:0.18757116 4:0.34864297 5:0.3777168 6:0.38922611
+-4 1:0.42490775 2:0.93973481 3:0.20295859 4:0.39290035 5:0.43632323 6:0.45871216
+-4 1:0.66865444 2:0.93973481 3:0.28594627 4:0.63969879 5:0.73360583 6:0.78380069
+-4 1:0.62642524 2:0.93973481 3:0.26141889 4:0.56602175 5:0.64775366 6:0.69263211
+-4 1:0.57430455 2:0.93973481 3:0.23537634 4:0.48984694 5:0.55363885 6:0.5853905
+-4 1:0.76178555 2:0.93973481 3:0.32205372 4:0.7176044 5:0.80237787 6:0.84588741
+-4 1:0.72282163 2:0.93973481 3:0.29554025 4:0.64471949 5:0.72634443 6:0.77062686
+-4 1:0.67693861 2:0.93973481 3:0.2669659 4:0.56720118 5:0.63868728 6:0.67673331
+4 1:0.86023804 2:0.49739676 3:0.53966638 4:0.77392585 5:0.84784447 6:0.89031641
+1.296591709971377 1:0.31779385 2:0.49739676 3:0.17094319 4:0.12195679 5:0.13277563 6:0.14165413
+4 1:0.68317784 2:0.49739676 3:0.37192301 4:0.52750491 5:0.62426522 6:0.6929947
+4 1:0.55611181 2:0.49739676 3:0.24752355 4:0.28326524 5:0.33261781 6:0.37104424
+4 1:0.7772257 2:0.49739676 3:0.43832146 4:0.63397606 5:0.7240692 6:0.78367237
+4 1:0.66186286 2:0.49739676 3:0.30599867 4:0.39201262 5:0.45927759 6:0.51239284
+4 1:0.94601776 2:0.04579546 3:0.69472114 4:0.97790884 5:0.9891237 6:0.993277
+4 1:0.98838404 2:0.04579546 3:0.90293444 4:0.99181622 5:0.99642641 6:0.9978864
+4 1:0.30006056 2:0.04579546 3:0.31879 4:0.45852885 5:0.59717781 6:0.71487885
+-4 1:0.44902891 2:0.04579546 3:0.35412414 4:0.55926446 5:0.70175505 6:0.79649177
+-4 1:0.69856222 2:0.04579546 3:0.45989947 4:0.82115248 5:0.92520734 6:0.9594384
+-4 1:0.67730161 2:0.04579546 3:0.44400319 4:0.77920819 5:0.88713866 6:0.92903178
+-4 1:0.64419192 2:0.04579546 3:0.42297435 4:0.72390263 5:0.83364665 6:0.88344569
+-4 1:0.80781899 2:0.04579546 3:0.52334234 4:0.88859427 5:0.94013924 6:0.95946903
+-4 1:0.78080761 2:0.04579546 3:0.499439 4:0.84012074 5:0.90229375 6:0.92936693
+4 1:0.97128596 2:0.014623935 3:0.90135809 4:0.99584619 5:0.9970631 6:0.99757649
+4 1:0.99645027 2:0.014623935 3:1 4:1 5:1 6:1
+-4 1:0.5326065 2:0.014623935 3:0.75468972 4:0.76017077 5:0.83753774 6:0.92265059
+-4 1:0.62757004 2:0.014623935 3:0.77708563 4:0.84258654 5:0.91016348 6:0.95440359
+-4 1:0.79306842 2:0.014623935 3:0.78900741 4:0.90386551 5:0.96905764 6:0.98466408
+-4 1:0.77722867 2:0.014623935 3:0.78701408 4:0.89679281 5:0.96056131 6:0.977629
+-4 1:0.75934622 2:0.014623935 3:0.78422805 4:0.88268036 5:0.94383829 6:0.96596858
+-4 1:0.8878718 2:0.014623935 3:0.81445984 4:0.96615706 5:0.98858241 6:0.99176534
+-4 1:0.88211614 2:0.014623935 3:0.81253935 4:0.95982371 5:0.98309178 6:0.9870796
+4 1:0.83805466 2:0.22767235 3:0.31750162 4:0.85145925 5:0.9121085 6:0.93772147
+4 1:0.86620985 2:0.22767235 3:0.35742938 4:0.89821492 5:0.94339974 6:0.96076173
+4 1:0.39289606 2:0.22767235 3:0.12019254 4:0.3951559 5:0.44657802 6:0.46771549
+4 1:0.48692411 2:0.22767235 3:0.13362033 4:0.43434224 5:0.49900609 6:0.53177669
+4 1:0.69743918 2:0.22767235 3:0.2263303 4:0.68859985 5:0.78706365 6:0.83662428
+4 1:0.65237548 2:0.22767235 3:0.19328493 4:0.60107975 5:0.69684945 6:0.74949279
+4 1:0.59461718 2:0.22767235 3:0.15963705 4:0.51010642 5:0.59283393 6:0.63883591
+4 1:0.77302727 2:0.22767235 3:0.26078021 4:0.76359704 5:0.8470807 6:0.8858359
+4 1:0.72953038 2:0.22767235 3:0.22331233 4:0.67735915 5:0.77029889 6:0.81802539
+4 1:0.87210923 2:0.16787772 3:0.69408521 4:0.91495146 5:0.94890261 6:0.96269344
+-4 1:0.81595959 2:0.08765484 3:0.52947327 4:0.7501341 5:0.82294191 6:0.86264385
+4 1:0.72562415 2:0.49739676 3:0.37130724 4:0.51472366 5:0.59961357 6:0.66258291
+-4 1:0.87135693 2:0.014623935 3:0.80905852 4:0.94637428 5:0.97242826 6:0.97946694
+-4 1:0.48910215 2:0.16787772 3:0.49792761 4:0.59161372 5:0.62979552 6:0.64254584
+-4 1:0.5685964 2:0.16787772 3:0.5149767 4:0.63026581 5:0.67890679 6:0.69964851
+-4 1:0.75935478 2:0.16787772 3:0.60695536 4:0.80906778 5:0.87125816 6:0.89810007
+-4 1:0.71788601 2:0.16787772 3:0.57600091 4:0.75310216 5:0.81471966 6:0.84249923
+-4 1:0.66516668 2:0.16787772 3:0.54473368 4:0.69254626 5:0.74796983 6:0.77177867
+4 1:0.81880869 2:0.16787772 3:0.64309172 4:0.86078024 5:0.90892223 6:0.92908907
+-4 1:0.78054558 2:0.16787772 3:0.60849279 4:0.80724494 5:0.86183239 6:0.88618408
+4 1:0.95353512 2:0.055921852 3:0.61526026 4:0.94655706 5:0.97211195 6:0.98210701
+4 1:0.98368527 2:0.055921852 3:0.7405327 4:0.96928567 5:0.9853799 6:0.99080378
+4 1:0.11318821 2:0.055921852 3:0.1590151 4:0.30536689 5:0.48614515 6:0.64344462
+4 1:0.30298819 2:0.055921852 3:0.19401703 4:0.41679982 5:0.61495039 6:0.74140301
+4 1:0.60614412 2:0.055921852 3:0.31791569 4:0.72365433 5:0.88324129 6:0.93484545
+4 1:0.58738733 2:0.055921852 3:0.29301498 4:0.67070014 5:0.83429953 6:0.89348041
+4 1:0.79496816 2:0.055921852 3:0.42192974 4:0.86711004 5:0.94030868 6:0.96084539
+4 1:0.77749763 2:0.055921852 3:0.38714172 4:0.81340799 5:0.90059649 6:0.93006702
+4 1:0.75215882 2:0.055921852 3:0.34721658 4:0.73960747 5:0.84370247 6:0.88485372
+4 1:0.89732805 2:0.58937038 3:0.58823535 4:0.80035053 5:0.86988422 6:0.90533033
+-4 1:0.9228759 2:0.58937038 3:0.65797705 4:0.87169952 5:0.92200942 6:0.94454256
+4 1:0.19504362 2:0.58937038 3:0.21585801 4:0.1754362 5:0.20844015 6:0.23846443
+4 1:0.34425894 2:0.58937038 3:0.24672569 4:0.24188506 5:0.29544562 6:0.33843061
+4 1:0.66407117 2:0.58937038 3:0.40045124 4:0.55415203 5:0.66628031 6:0.73418465
+4 1:0.60780044 2:0.58937038 3:0.34931828 4:0.4519606 5:0.54893247 6:0.61355219
+4 1:0.53476258 2:0.58937038 3:0.29851601 4:0.34826788 5:0.42168642 6:0.47203603
+4 1:0.79195776 2:0.58937038 3:0.47493233 4:0.66775916 5:0.76196439 6:0.81489875
+4 1:0.7415564 2:0.58937038 3:0.41507439 4:0.56413083 5:0.65815516 6:0.7166999
+4 1:0.82021207 2:1 3:0.37381485 4:0.7891612 5:0.87031145 6:0.90944281
+-3.795805084530972 1:0.85903236 2:1 3:0.43235998 4:0.86707094 5:0.92632217 6:0.95151451
+-4 1:0.25243046 2:1 3:0.084027451 4:0.15537936 5:0.17410072 6:0.17212333
+-4 1:0.35643487 2:1 3:0.10644455 4:0.21484368 5:0.25587544 6:0.27527817
+-4 1:0.57605414 2:1 3:0.19031962 4:0.43030863 5:0.5277316 6:0.59069772
+-4 1:0.49071444 2:1 3:0.14452095 4:0.31406915 5:0.38353445 6:0.42653517
+4 1:0.73255545 2:1 3:0.28883701 4:0.65284485 5:0.75623242 6:0.81297442
+0.4082706381617505 1:0.67015395 2:1 3:0.2367756 4:0.5367057 5:0.64063877 6:0.70451767
+-4 1:0.84450653 2:0.083369236 3:0.57279245 4:0.85249389 5:0.91751611 6:0.94621989
+-4 1:0.39559773 2:0.083369236 3:0.28184137 4:0.37025203 5:0.46733936 6:0.53517338
+-4 1:0.70621493 2:0.083369236 3:0.42718441 4:0.69347659 5:0.81124449 6:0.87136343
+-4 1:0.65615861 2:0.083369236 3:0.37833052 4:0.59301482 5:0.71772587 6:0.7905538
+-4 1:0.58837863 2:0.083369236 3:0.33229353 4:0.48675881 5:0.60141743 6:0.67458413
+-4 1:0.77687144 2:0.083369236 3:0.48094343 4:0.76665994 5:0.86191893 6:0.90760934
+-1.966116876631112 1:0.72849768 2:0.083369236 3:0.42082971 4:0.66591147 5:0.77995959 6:0.84260661
+-3.906831378063804 1:0.66320082 2:0.083369236 3:0.36350305 4:0.54888271 5:0.66506794 6:0.73685112
+4 1:0.84500499 2:0.42532178 3:0.43562507 4:0.80721931 5:0.87934044 6:0.91434143
+4 1:0.8874543 2:0.42532178 3:0.50912639 4:0.87959883 5:0.93223488 6:0.95450335
+4 1:0.31032192 2:0.42532178 3:0.18976794 4:0.30662908 5:0.34637104 6:0.3661022
+4 1:0.41026349 2:0.42532178 3:0.20589097 4:0.35241209 5:0.40358156 6:0.42577381
+4 1:0.67552108 2:0.42532178 3:0.30879992 4:0.60375124 5:0.70097073 6:0.75507206
+4 1:0.62772585 2:0.42532178 3:0.27349745 4:0.5196735 5:0.60339149 6:0.65103342
+4 1:0.5741386 2:0.42532178 3:0.24033766 4:0.43855753 5:0.50243186 6:0.53322825
+4 1:0.7629976 2:0.42532178 3:0.35347476 4:0.69239941 5:0.78245146 6:0.83117443
+4 1:0.71746409 2:0.42532178 3:0.31296983 4:0.60525302 5:0.69243388 6:0.7432587
+-4 1:0.73137955 2:0.16787772 3:0.57222383 4:0.74405775 5:0.79993424 6:0.82484891
+4 1:0.67383121 2:0.58937038 3:0.35481019 4:0.45269287 5:0.53578336 6:0.59116487
+4 1:0.5905971 2:1 3:0.18559792 4:0.41535212 5:0.50422336 6:0.56173557
+4 1:0.66157018 2:0.42532178 3:0.27479904 4:0.51802649 5:0.59270541 6:0.63560969
+-4 1:0.66827754 2:0.54342577 3:0.18169339 4:0.50290989 5:0.59875259 6:0.65332628
+4 1:0.85027066 2:0.20820673 3:0.40997978 4:0.82462749 5:0.89794736 6:0.93142825
+4 1:0.87892054 2:0.20820673 3:0.45891267 4:0.87823329 5:0.93535353 6:0.95883927
+4 1:0.3986268 2:0.20820673 3:0.17753958 4:0.33495583 5:0.39777832 6:0.44399359
+-4 1:0.48997993 2:0.20820673 3:0.20172681 4:0.39715881 5:0.47368229 6:0.52781628
+4 1:0.7022939 2:0.20820673 3:0.31094767 4:0.6676259 5:0.77726116 6:0.83518027
+-4 1:0.65773092 2:0.20820673 3:0.27420721 4:0.57889989 5:0.68485118 6:0.74837036
+0.2951376518668717 1:0.60031736 2:0.20820673 3:0.23419121 4:0.48018865 5:0.57200972 6:0.63197473
+4 1:0.77623676 2:0.20820673 3:0.3510016 4:0.74206651 5:0.83508543 6:0.88101902
+4 1:0.73562396 2:0.20820673 3:0.31004997 4:0.6557112 5:0.75585014 6:0.81164989
+-4 1:0.67923081 2:0.20820673 3:0.26679137 4:0.55816547 5:0.65579282 6:0.71593631
+4 1:0.83968539 2:0.54342577 3:0.32439292 4:0.78747769 5:0.87303614 6:0.91271252
+4 1:0.86656342 2:0.54342577 3:0.37898741 4:0.85252726 5:0.92049615 6:0.94848246
+-4 1:0.42728303 2:0.54342577 3:0.10123262 4:0.31581962 5:0.38571265 6:0.42827036
+-4 1:0.63194526 2:0.54342577 3:0.18169045 4:0.51611903 5:0.62179755 6:0.68216176
+4 1:0.56954706 2:0.54342577 3:0.14271477 4:0.41491191 5:0.50173488 6:0.55220392
+4 1:0.76753176 2:0.54342577 3:0.26295318 4:0.6905031 5:0.79291823 6:0.84469464
+-4 1:0.72348649 2:0.54342577 3:0.22334634 4:0.60145902 5:0.70573225 6:0.76318544
+4 1:0.83584492 2:0.047285912 3:0.53826775 4:0.933335 5:0.95948954 6:0.96870909
+4 1:0.85530855 2:0.047285912 3:0.55323777 4:0.95113339 5:0.97249918 6:0.9795177
+-4 1:0.53835734 2:0.047285912 3:0.41965074 4:0.71632669 5:0.73953043 6:0.73487553
+-4 1:0.59175144 2:0.047285912 3:0.43113594 4:0.74141738 5:0.76929188 6:0.77018949
+-4 1:0.75962366 2:0.047285912 3:0.49613729 4:0.87838146 5:0.91688438 6:0.93150362
+-4 1:0.72043129 2:0.047285912 3:0.47217411 4:0.83138845 5:0.8704229 6:0.88419439
+-4 1:0.67287449 2:0.047285912 3:0.44652268 4:0.77691812 5:0.81043483 6:0.8177009
+-4 1:0.8023177 2:0.047285912 3:0.51559706 4:0.90512389 5:0.93743101 6:0.9492968
+-4 1:0.76751376 2:0.047285912 3:0.49225957 4:0.86357299 5:0.89948127 6:0.91221155
+-4 1:0.72124785 2:0.047285912 3:0.46606653 4:0.81323145 5:0.84847474 6:0.85892657
diff --git a/tools/convexhull_framework/src/CalcQtyWithFfmpeg.py b/tools/convexhull_framework/src/CalcQtyWithFfmpeg.py
new file mode 100644
index 0000000..0aa1301
--- /dev/null
+++ b/tools/convexhull_framework/src/CalcQtyWithFfmpeg.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+import re
+import logging
+from Config import BinPath, LoggerName, LogCmdOnly, FFMPEG
+from Utils import GetShortContentName, ExecuteCmd
+
+subloggername = "CalcQtyMetrics_FFMPEGTool"
+loggername = LoggerName + '.' + '%s' % subloggername
+logger = logging.getLogger(loggername)
+
+
+FFMPEGMetricsFullList = ['PSNR_Y', 'PSNR_U', 'PSNR_V']
+
+def ParseFfmpegLogFile(psnr_log):
+    floats = len(FFMPEGMetricsFullList) * [0.0]
+    flog = open(psnr_log, 'r')
+    cnt = 0
+    for line in flog:
+        cnt += 1
+        item = re.findall(r"psnr_y:(\d+\.?\d*)", line)
+        floats[0] += 0 if len(item) == 0 else float(item[0])
+        item = re.findall(r"psnr_u:(\d+\.?\d*)", line)
+        floats[1] += 0 if len(item) == 0 else float(item[0])
+        item = re.findall(r"psnr_v:(\d+\.?\d*)", line)
+        floats[2] += 0 if len(item) == 0 else float(item[0])
+
+    floats = [float(i) / cnt for i in floats]
+
+    print_str = "FFMPEG quality metrics: "
+    for metrics, idx in zip(FFMPEGMetricsFullList, range(len(FFMPEGMetricsFullList))):
+        print_str += "%s = %2.5f, " % (metrics, floats[idx])
+    logger.info(print_str)
+
+    return floats[0:len(FFMPEGMetricsFullList)]
+
+
+def GetFfmpegLogFile(recfile, path):
+    filename = GetShortContentName(recfile, False) + '_psnr.log'
+    file = os.path.join(path, filename)
+    return file
+
+################################################################################
+##################### Exposed Functions ########################################
+def FFMPEG_CalQualityMetrics(origfile, recfile, num, w, h, logfilePath):
+    #calculate psnr using ffmpeg filter to get psnr_u and psnr_v
+    #here we have to pass the psnr_log file name to FFMPEG without the target
+    #log path first, then copy the result psnr log to the target log path after
+    #the ffmpeg call. it doesn't work if the full path is directly passed to FFMPEG
+    psnr_log = GetFfmpegLogFile(recfile, BinPath)
+    psnr_log = os.path.basename(psnr_log)
+    args = " -s %dx%d -pix_fmt yuv420p -i %s -s %dx%d -pix_fmt yuv420p -i %s" \
+           " -frames:v %d -lavfi psnr=%s -f null -" % \
+           (w, h, origfile, w, h, recfile, num, psnr_log)
+    cmd = FFMPEG + args
+    ExecuteCmd(cmd, LogCmdOnly)
+    #move the psnr log to the target log path
+    target = os.path.join(logfilePath, psnr_log)
+    cmd = "move %s %s" % (psnr_log, target)
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def FFMPEG_GatherQualityMetrics(recfile, logfilePath):
+    psnr_log = GetFfmpegLogFile(recfile, logfilePath)
+    results = ParseFfmpegLogFile(psnr_log)
+    return results
diff --git a/tools/convexhull_framework/src/CalcQtyWithHdrTools.py b/tools/convexhull_framework/src/CalcQtyWithHdrTools.py
new file mode 100644
index 0000000..9593c87
--- /dev/null
+++ b/tools/convexhull_framework/src/CalcQtyWithHdrTools.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+from shutil import copyfile
+import fileinput
+import os
+import re
+from Utils import GetShortContentName, ExecuteCmd
+from Config import BinPath, LogCmdOnly, LoggerName, HDRTool
+import logging
+
+subloggername = "HDRToolsRun"
+loggername = LoggerName + '.' + '%s' % subloggername
+logger = logging.getLogger(loggername)
+
+
+HDRToolsConfigFileTemplate = os.path.join(BinPath, 'HDRMetricsYUV420_template.cfg')
+HDRToolsMetricsFullList = ['PSNR_Y', 'PSNR_U', 'PSNR_V', 'MS-SSIM_Y', 'MS-SSIM_U',
+                           'MS-SSIM_V', 'SSIM_Y', 'SSIM_U', 'SSIM_V']
+
+def GenerateCfgFile(orig_file, rec_file, num, w, h, configpath):
+    filename = GetShortContentName(rec_file, False) + '_hdrtools.cfg'
+    cfgfile = os.path.join(configpath, filename)
+    copyfile(HDRToolsConfigFileTemplate, cfgfile)
+    fp = fileinput.input(cfgfile, inplace=1)
+    for line in fp:
+        if 'Input0File=' in line:
+            line = 'Input0File="%s"\n' % orig_file
+        if 'Input1File=' in line:
+            line = 'Input1File="%s"\n' % rec_file
+        if 'NumberOfFrames=' in line:
+            line = 'NumberOfFrames=%d\n' % num
+        if 'Input0Width=' in line:
+            line = 'Input0Width=%d\n' % w
+        if 'Input1Width=' in line:
+            line = 'Input1Width=%d\n' % w
+        if 'Input0Height=' in line:
+            line = 'Input0Height=%d\n' % h
+        if 'Input1Height=' in line:
+            line = 'Input1Height=%d\n' % h
+        print(line, end='')
+    fp.close()
+    return cfgfile
+
+def ParseHDRToolLogFile(logfile):
+    flog = open(logfile, 'r')
+    pattern = re.compile(r'\d+\.?\d*')
+    floats = len(HDRToolsMetricsFullList) * [0.0]
+    for line in flog:
+        if 'D_Avg' in line:
+            floats = [float(i) for i in pattern.findall(line)]
+            break
+
+    print_str = "HDRTools qty metrics: "
+    for metrics, idx in zip(HDRToolsMetricsFullList,
+                            range(len(HDRToolsMetricsFullList))):
+        print_str += "%s = %2.5f, " % (metrics, floats[idx])
+    logger.info(print_str)
+    return floats[0:len(HDRToolsMetricsFullList)]
+
+def GetHDRLogFile(recfile, path):
+    filename = GetShortContentName(recfile, False) + '_hdrtools.log'
+    file = os.path.join(path, filename)
+    return file
+
+################################################################################
+##################### Exposed Functions ########################################
+def HDRTool_CalQualityMetrics(origfile, recfile, num, w, h, logfilepath,
+                              cfgfilepath):
+    cfgFile = GenerateCfgFile(origfile, recfile, num, w, h, cfgfilepath)
+    logfile = GetHDRLogFile(recfile, logfilepath)
+    args = " -f %s > %s" % (cfgFile, logfile)
+    cmd = HDRTool + args
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def HDRTool_GatherQualityMetrics(recfile, logfilepath):
+    logfile = GetHDRLogFile(recfile, logfilepath)
+    results = ParseHDRToolLogFile(logfile)
+    return results
diff --git a/tools/convexhull_framework/src/CalcQtyWithVmafTool.py b/tools/convexhull_framework/src/CalcQtyWithVmafTool.py
new file mode 100644
index 0000000..46efc1e
--- /dev/null
+++ b/tools/convexhull_framework/src/CalcQtyWithVmafTool.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+import re
+import logging
+from Config import BinPath, LoggerName, LogCmdOnly, VMAF
+from Utils import GetShortContentName, ExecuteCmd
+
+subloggername = "CalcQtyMetrics_VMAFTool"
+loggername = LoggerName + '.' + '%s' % subloggername
+logger = logging.getLogger(loggername)
+
+Model_Pkg_File = os.path.join(BinPath, 'vmaf_v0.6.1.pkl')
+VMAFMetricsFullList = ['VMAF_Y', 'PSNR_Y', 'SSIM_Y', 'MS-SSIM_Y']
+
+def ParseVMAFLogFile(vmaf_log):
+    floats = len(VMAFMetricsFullList) * [0.0]
+    flog = open(vmaf_log, 'r')
+    for line in flog:
+        if 'aggregateVMAF' in line:
+            item = re.findall(r"aggregateVMAF=\"(\d+\.?\d*)\"", line)
+            floats[0] = 0 if len(item) == 0 else item[0]
+            item = re.findall(r"aggregatePSNR=\"(\d+\.?\d*)\"", line)
+            floats[1] = 0 if len(item) == 0 else item[0]
+            item = re.findall(r"aggregateSSIM=\"(\d+\.?\d*)\"", line)
+            floats[2] = 0 if len(item) == 0 else item[0]
+            item = re.findall(r"aggregateMS_SSIM=\"(\d+\.?\d*)\"", line)
+            floats[3] = 0 if len(item) == 0 else item[0]
+            break
+    flog.close()
+
+    floats = [float(i) for i in floats]
+
+    print_str = "VMAF quality metrics: "
+    for metrics, idx in zip(VMAFMetricsFullList, range(len(VMAFMetricsFullList))):
+        print_str += "%s = %2.5f, " % (metrics, floats[idx])
+    logger.info(print_str)
+
+    return floats[0:len(VMAFMetricsFullList)]
+
+
+def GetVMAFLogFile(recfile, path):
+    filename = GetShortContentName(recfile, False) + '_vmaf.log'
+    file = os.path.join(path, filename)
+    return file
+
+################################################################################
+##################### Exposed Functions ########################################
+def VMAF_CalQualityMetrics(origfile, recfile, num, w, h, logfilePath):
+    vmaf_log = GetVMAFLogFile(recfile, logfilePath)
+    args = " yuv420p %d %d %s %s %s --log %s --psnr --ssim --ms-ssim "\
+           % (w, h, origfile, recfile, Model_Pkg_File, vmaf_log)
+    cmd = VMAF + args
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def VMAF_GatherQualityMetrics(recfile, logfilePath):
+    vmaf_log = GetVMAFLogFile(recfile, logfilePath)
+    results = ParseVMAFLogFile(vmaf_log)
+    return results
diff --git a/tools/convexhull_framework/src/CalculateQualityMetrics.py b/tools/convexhull_framework/src/CalculateQualityMetrics.py
new file mode 100644
index 0000000..4f61fa2
--- /dev/null
+++ b/tools/convexhull_framework/src/CalculateQualityMetrics.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import logging
+from Config import QualityList, LoggerName, QualityEvalMethods
+import Utils
+from CalcQtyWithVmafTool import VMAF_CalQualityMetrics, VMAF_GatherQualityMetrics,\
+     VMAFMetricsFullList
+from CalcQtyWithHdrTools import HDRTool_CalQualityMetrics, HDRToolsMetricsFullList,\
+     HDRTool_GatherQualityMetrics
+from CalcQtyWithFfmpeg import FFMPEG_GatherQualityMetrics, FFMPEGMetricsFullList,\
+     FFMPEG_CalQualityMetrics
+
+subloggername = "CalcQtyMetrics"
+loggername = LoggerName + '.' + '%s' % subloggername
+logger = logging.getLogger(loggername)
+
+MetricsFullDict = {'VMAF': VMAFMetricsFullList, 'HDRTools': HDRToolsMetricsFullList,
+                   'FFMPEG': FFMPEGMetricsFullList}
+
+def CalculateQualityMetric(content, framenum, reconYUV, width, height, logfilePath,
+                           cfgfilePath):
+    Utils.CmdLogger.write("::Quality Metrics\n")
+
+    methods_torun = list(set(QualityEvalMethods))  # remove duplicate items
+    for method in methods_torun:
+        if method == 'VMAF':
+            VMAF_CalQualityMetrics(content, reconYUV, framenum, width, height,
+                                   logfilePath)
+        elif method == 'HDRTools':
+            HDRTool_CalQualityMetrics(content, reconYUV, framenum, width, height,
+                                      logfilePath, cfgfilePath)
+        elif method == 'FFMPEG':
+            FFMPEG_CalQualityMetrics(content, reconYUV, framenum, width, height,
+                                     logfilePath)
+        else:
+            logger.error("invalid quality evaluation method: %s !" % method)
+            return
+
+def GatherQualityMetrics(reconYUV, logfilePath):
+    methods_torun = list(set(QualityEvalMethods))  # remove duplicate items
+    qresult_dict = {}
+    for method in methods_torun:
+        if method == 'VMAF':
+            qresult_dict[method] = VMAF_GatherQualityMetrics(reconYUV, logfilePath)
+        elif method == 'HDRTools':
+            qresult_dict[method] = HDRTool_GatherQualityMetrics(reconYUV, logfilePath)
+        elif method == 'FFMPEG':
+            qresult_dict[method] = FFMPEG_GatherQualityMetrics(reconYUV, logfilePath)
+        else:
+            logger.error("invalid quality evaluation method: %s !" % method)
+            return
+
+    results = []
+    for metric, method in zip(QualityList, QualityEvalMethods):
+        mfullList = MetricsFullDict[method]
+        if metric in mfullList:
+            indx = mfullList.index(metric)
+            results.append(qresult_dict[method][indx])
+        else:
+            logger.error("invalid quality metrics in QualityList")
+            results.append('none')
+
+    return results
diff --git a/tools/convexhull_framework/src/Config.py b/tools/convexhull_framework/src/Config.py
new file mode 100644
index 0000000..5a6ebb0
--- /dev/null
+++ b/tools/convexhull_framework/src/Config.py
@@ -0,0 +1,157 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+
+######################################
+# configuration settings
+######################################
+RootPath = "..\\"
+BinPath = os.path.join(RootPath, 'bin')
+WorkPath = os.path.join(RootPath, 'test')
+SMOKE_TEST = False  # override some parameters to do a quick smoke test
+FrameNum = 60
+
+if SMOKE_TEST:
+    FrameNum = 2
+
+############ test contents #######################################
+ContentPath = "..\\video"
+# when Have_Class_Subfolder is set to True, test clips are put in subfolders
+# under ContentPath, each subfolder name represents a class. Class name in the
+# table of Clips is the subfolder name
+Have_Class_Subfolder = False
+Clips = {
+#    basename           class      width   height  framerate   bitdepth    fmt
+    #"CrowdRun":         ["ClassB",  1920,   1080,   30,     8,      "yuv420p"],
+    #"BasketballDrive":  ["ClassB",  1920,   1080,   30,     8,      "yuv420p"],
+    "NetflixCrosswalk_1920x1080_60fps_8bit_420_60f":    ["ClassB",  1920,   1080,
+     30,         8,          "yuv420p"],
+}
+'''
+    "aspen_1080p_60f":  ["ClassB",  1920,   1080,   30,         8,
+              "yuv420p"],
+    "dark720p_60f":     ["ClassB",  1280,   720,    30,         8,
+              "yuv420p"],
+    "DOTA2_60f_420":    ["ClassB",  1920,   1080,   30,         8,
+              "yuv420p"],
+    "duckstakeoff_1080p50_60f":     ["ClassB",  1920,   1080,   30,         8,
+              "yuv420p"],
+    "KristenAndSara_1280x720_60f":  ["ClassB",  1280,   720,    30,         8,
+              "yuv420p"],
+    "life_1080p30_60f":             ["ClassB",  1920,   1080,   30,         8,
+              "yuv420p"],
+    "MINECRAFT_60f_420":            ["ClassB",  1920,   1080,   30,         8,
+              "yuv420p"],
+    "NetflixAerial_1920x1080_60fps_8bit_420_60f":       ["ClassB",  1920,   1080,
+       30,         8,          "yuv420p"],
+    "NetflixBoat_1920x1080_60fps_8bit_420_60f":         ["ClassB",  1920,   1080,
+       30,         8,          "yuv420p"],
+    "NetflixCrosswalk_1920x1080_60fps_8bit_420_60f":    ["ClassB",  1920,   1080,
+       30,         8,          "yuv420p"],
+    "NetflixDrivingPOV_1280x720_60fps_8bit_420_60f":    ["ClassB",  1280,   720,
+       30,         8,          "yuv420p"],
+    "NetflixFoodMarket_1920x1080_60fps_8bit_420_60f":   ["ClassB",  1920,   1080,
+       30,         8,          "yuv420p"],
+    "NetflixPierSeaside_1920x1080_60fps_8bit_420_60f":  ["ClassB",  1920,   1080,
+       30,         8,          "yuv420p"],
+    "NetflixRollerCoaster_1280x720_60fps_8bit_420_60f": ["ClassB",  1280,   720,
+        30,         8,          "yuv420p"],
+    "NetflixSquareAndTimelapse_1920x1080_60fps_8bit_420_60f":   ["ClassB",  1920,
+       1080,   30,         8,          "yuv420p"],
+    "NetflixTunnelFlag_1920x1080_60fps_8bit_420_60f":           ["ClassB",  1920,
+       1080,   30,         8,          "yuv420p"],
+    "rushhour_1080p25_60f": ["ClassB",  1920,   1080,   30,         8,
+              "yuv420p"],
+    "STARCRAFT_60f_420":    ["ClassB",  1920,   1080,   30,         8,
+              "yuv420p"],
+    "touchdownpass_1080p_60f":  ["ClassB",  1920,   1080,   30,         8,
+              "yuv420p"],
+    "vidyo1_720p_60fps_60f":    ["ClassB",  1280,   720,    30,         8,
+              "yuv420p"],
+    "vidyo4_720p_60fps_60f":    ["ClassB",  1280,   720,    30,         8,
+              "yuv420p"],
+    "wikipedia_420":            ["ClassB",  1920,   1080,   30,         8,
+              "yuv420p"],
+'''
+############## Scaling settings ############################################
+# down scaling ratio
+DnScaleRatio = [1.0, 1.5, 2.0, 3.0, 4.0] #, 6.0]  # downscale ratio
+#down and up scaling algorithm, the 2 lists should be of same size
+DnScalingAlgos = ['bicubic', 'bilinear', 'gauss', 'lanczos', 'sinc']
+UpScalingAlgos = ['bicubic', 'bilinear', 'gauss', 'lanczos', 'sinc']
+
+if SMOKE_TEST:
+    DnScalingAlgos = ['bicubic', 'lanczos', 'sinc']
+    UpScalingAlgos = ['bicubic', 'lanczos', 'sinc']
+
+##################### Encode Config ########################################
+EncodeMethods = ["ffmpeg", "aom", "svt"]
+CodecNames = ["hevc", "av1"]
+SUFFIX = {"hevc": ".265", "av1": ".ivf"}
+FFMPEG = os.path.join(BinPath, 'ffmpeg.exe')
+AOMENC = os.path.join(BinPath, 'aomenc.exe')
+SVTAV1 = os.path.join(BinPath, 'SvtAv1EncApp.exe')
+SVTHEVC = os.path.join(BinPath, 'SvtHevcEncApp.exe')
+AOMDEC = os.path.join(BinPath, 'aomdec.exe')
+
+QPs = list(range(22, 51, 5))  # total 6 QPs
+if SMOKE_TEST:
+    QPs = list(range(12, 45, 10))  # total 4 QPs
+
+######################## quality evalution config #############################
+QualityList = ['VMAF_Y', 'PSNR_Y', 'PSNR_U', 'PSNR_V', 'SSIM_Y', 'MS-SSIM_Y']
+# method should be one of 'VMAF', 'FFMPEG' or 'HDRTools'
+QualityEvalMethods = ['VMAF', 'HDRTools', 'HDRTools', 'HDRTools', 'HDRTools',
+                      'HDRTools']
+VMAF = os.path.join(BinPath, 'vmafossexec.exe')
+HDRTool = os.path.join(BinPath, 'HDRMetrics.exe')
+
+######################## config for exporting data to excel  #################
+#https://xlsxwriter.readthedocs.io/working_with_colors.html#colors
+# line color used, number of colors >= len(DnScaledRes)
+LineColors = ['blue', 'red', 'green', 'orange', 'pink', 'yellow']
+ConvexHullColor = 'white'
+
+# format for exported excel of convexhull test
+# if to modify below 3 variables, need to modify function
+# SaveConvexHullResultsToExcel accordingly
+CvxH_startCol = 1; CvxH_startRow = 2; CvxH_colInterval = 2
+CvxH_WtCols = [(CvxH_colInterval + 1 + len(QualityList)) * i + CvxH_startCol
+               for i in range(len(DnScaleRatio))]
+CvxH_WtRows = [CvxH_startRow + i for i in range(len(QPs))]
+CvxH_WtLastCol = CvxH_WtCols[-1] + len(QualityList)
+CvxH_WtLastRow = CvxH_WtRows[-1]
+######################## post analysis #########################################
+PostAnalysis_Path = os.path.join(RootPath, 'analysis')
+Path_RDResults = os.path.join(PostAnalysis_Path, 'rdresult')
+SummaryOutPath = os.path.join(PostAnalysis_Path, 'summary')
+Path_ScalingResults = os.path.join(PostAnalysis_Path, 'scalingresult')
+# vba file needed when to calculate bdrate
+#VbaBinFile = os.path.join(BinPath, 'vbaProject_JVET-L0242.bin')
+VbaBinFile = os.path.join(BinPath, 'vbaProject-AV2.bin')
+
+# format for exported excel of scaling quality test
+# if to modify below 3 variables, need to modify function SaveScalingResultsToExcel
+# accordingly
+ScalQty_startCol = 6; ScalQty_startRow = 2; ScalQty_colInterval = 1
+ScalSumQty_startCol = 7
+ScalQty_WtCols = [(ScalQty_colInterval +
+                   len(QualityList)) * i + ScalQty_startCol
+                  for i in range(len(DnScalingAlgos))]
+ScalSumQty_WtCols = [(ScalQty_colInterval +
+                      len(QualityList)) * i + ScalQty_startCol + 1
+                     for i in range(len(DnScalingAlgos))]
+######################## logging #########################################
+LoggerName = "ConvexHullTest"
+LogLevels = ['NONE', 'CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG']
+LogCmdOnly = False
diff --git a/tools/convexhull_framework/src/ConvexHullBDRate.py b/tools/convexhull_framework/src/ConvexHullBDRate.py
new file mode 100644
index 0000000..26bc858
--- /dev/null
+++ b/tools/convexhull_framework/src/ConvexHullBDRate.py
@@ -0,0 +1,227 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import sys
+import xlsxwriter
+import xlrd
+import argparse
+from Config import VbaBinFile, QualityList
+
+class ConvexHullData:
+    ContentName = ""
+    ContentClass = ""
+    NumRDPoints = 0
+    RDPoints = {}
+    def __init__(self, Name="", Class="", num=0):
+        self.ContentName = Name
+        self.ContentClass = Class
+        self.NumRDPoints = num
+        self.RDPoints = {}
+
+def ParseArguments(raw_args):
+    parser = argparse.ArgumentParser(prog='ConvexHullBDRate.py',
+                                     usage='%(prog)s [options]', description='')
+    parser.add_argument('-i1', '--input1', dest='Input1', type=str,
+                        required=True, metavar='',
+                        help="convex hull summary excel file for base mode")
+    parser.add_argument('-i2', '--input2', dest='Input2', type=str,
+                        required=True, metavar='',
+                        help="convex hull summary excel file for target mode")
+    parser.add_argument('-o', '--output', dest='Output', type=str,
+                        required=True, metavar='',
+                        help="output excel file with BDRATE for base and target"
+                             " modes")
+    if len(raw_args) == 1:
+        parser.print_help()
+        sys.exit(1)
+    args = parser.parse_args(raw_args[1:])
+
+    global InputBase, InputTarget, Output
+    InputBase = args.Input1
+    InputTarget = args.Input2
+    Output = args.Output
+
+def read_cell_as_str(sht, row, col):
+    cell_val = sht.cell(row, col).value
+    if cell_val == '':
+        return ''
+    else:
+        return str(cell_val)
+
+def read_cell_as_float(sht, row, col):
+    cell_val = sht.cell(row, col).value
+    if cell_val == '':
+        return ''
+    else:
+        return float(cell_val)
+
+def read_cell_as_int(sht, row, col):
+    cell_val = sht.cell(row, col).value
+    if cell_val == '':
+        return ''
+    else:
+        return int(cell_val)
+
+def ParseConvexHullRD(xls):
+    wb = xlrd.open_workbook(xls)
+    shts = wb.sheet_names()   #list of sheet names
+    data = {}   #dict of data, key is the sheet name
+
+    for sht_name in shts:
+        sht = wb.sheet_by_name(sht_name)
+        #skip the title row
+        rows = sht.nrows
+        cols = sht.ncols
+        start_row = 1
+        while start_row < rows:
+            row = start_row
+            cls = read_cell_as_str(sht, row, 0)
+            name = read_cell_as_str(sht, row, 1)
+            num = read_cell_as_int(sht, row, 2)
+            if (cls == '' or name == '' or num == ''):
+                print("Error: read empty cells")
+                exit()
+
+            point = ConvexHullData(name, cls, num)
+
+            rd_data = {}
+            for row in range(num):
+                col = 3
+                for qty in QualityList:
+                    br = read_cell_as_float(sht, start_row+row, col)
+                    q = read_cell_as_float(sht, start_row+row, col + 1)
+                    if (br != '' and q != ''):
+                        if (qty in rd_data.keys()):
+                            rd_data[qty].append((br, q))
+                        else:
+                            rd_data.update({qty:[(br, q)]})
+                    col += 2
+            start_row += num
+            point.RDPoints = rd_data
+            if (sht_name in data.keys()):
+                data[sht_name].append(point)
+            else:
+                data.update({sht_name: [point]})
+
+    #wb.close()
+    return shts, data
+
+def WriteOutputHeaderRow(sht):
+    sht.write(0, 0, 'Content Class')
+    sht.write(0, 1, 'Content Name')
+    sht.write(0, 2, 'Num RD Points')
+    col = 3
+    for qty in QualityList:
+        sht.write(0, col, 'Bitrate(kbps)')
+        sht.write(0, col + 1, qty)
+        col += 2
+    col += 1
+    for qty in QualityList:
+        sht.write(0, col, 'Bitrate(kbps)')
+        sht.write(0, col + 1, qty)
+        col += 2
+    col += 1
+    for (idx, qty) in zip(range(len(QualityList)), QualityList):
+        sht.write(0, col + idx, "BDRATE-%s"%qty)
+
+def WriteRDData(sht, rd_data, start_row, start_col, format):
+    col = start_col
+    max_rows = 0
+    for qty in QualityList:
+        row = start_row
+        for (line, point) in zip(range(len(rd_data.RDPoints[qty])),
+                                 rd_data.RDPoints[qty]):
+            sht.write_number(row + line, col, point[0], format)
+            sht.write_number(row + line, col + 1, point[1], format)
+        col += 2
+        max_rows = max(max_rows, len(rd_data.RDPoints[qty]))
+    return max_rows
+
+def WriteRDRecord(sht, base_data, target_data, start_row, bdrate_fmt, float_fmt):
+    sht.write(start_row, 0, base_data.ContentClass)
+    sht.write(start_row, 1, base_data.ContentName)
+
+    #write base data
+    base_start_col = 3
+    base_max_rows = WriteRDData(sht, base_data, start_row, base_start_col,
+                                float_fmt)
+
+    #write target data
+    target_start_col = base_start_col + 2 * len(QualityList) + 1
+    target_max_rows = WriteRDData(sht, target_data, start_row, target_start_col,
+                                  float_fmt)
+
+    #write bdrate formula
+    bdrate_start_col = target_start_col + 2 * len(QualityList) + 1
+    total_rows = max(base_max_rows, target_max_rows)
+    sht.write(start_row, 2, total_rows)
+    for (qty, col) in zip(QualityList, range(len(QualityList))):
+        refbr_b = xlrd.cellnameabs(start_row, base_start_col + col * 2)
+        refbr_e = xlrd.cellnameabs(start_row + total_rows - 1,
+                                   base_start_col + col * 2)
+        refq_b = xlrd.cellnameabs(start_row, base_start_col + col * 2 + 1)
+        refq_e = xlrd.cellnameabs(start_row + total_rows - 1,
+                                  base_start_col + col * 2 + 1)
+
+        testbr_b = xlrd.cellnameabs(start_row, target_start_col + col * 2)
+        testbr_e = xlrd.cellnameabs(start_row + total_rows - 1,
+                                    target_start_col + col * 2)
+        testq_b = xlrd.cellnameabs(start_row, target_start_col + col * 2 + 1)
+        testq_e = xlrd.cellnameabs(start_row + total_rows - 1,
+                                   target_start_col + col * 2 + 1)
+
+        # formula = '=-bdrate(%s:%s,%s:%s,%s:%s,%s:%s)' % (
+        # refbr_b, refbr_e, refq_b, refq_e, testbr_b, testbr_e, testq_b, testq_e)
+        formula = '=bdRateExtend(%s:%s,%s:%s,%s:%s,%s:%s)'\
+            % (refbr_b, refbr_e, refq_b, refq_e, testbr_b, testbr_e, testq_b, testq_e)
+        sht.write_formula(start_row, bdrate_start_col + col, formula, bdrate_fmt)
+    return total_rows
+
+def FindContent(name, rd_data):
+    for data in rd_data:
+        if (name == data.ContentName):
+            return data
+    return ''
+
+######################################
+# main
+######################################
+if __name__ == "__main__":
+    sys.argv = ["","-i1","ConvexHullRD_ScaleAlgosNum_5_ffmpeg_hevc_medium.xlsx",
+     "-i2","ConvexHullRD_ScaleAlgosNum_5_ffmpeg_hevc_veryslow.xlsx",
+     "-o","ConvexHullBDRate.xlsm"]
+    ParseArguments(sys.argv)
+
+    base_shts, base_rd_data = ParseConvexHullRD(InputBase)
+    target_shts, target_rd_data = ParseConvexHullRD(InputTarget)
+
+    output_wb = xlsxwriter.Workbook(Output)
+    # vba file needed when to calculate bdrate
+    output_wb.add_vba_project(VbaBinFile)
+    bdrate_fmt = output_wb.add_format()
+    bdrate_fmt.set_num_format('0.00%')
+    float_fmt = output_wb.add_format()
+    float_fmt.set_num_format('0.00')
+
+    for sht_name in base_shts:
+        if sht_name in target_shts:
+            sht = output_wb.add_worksheet(sht_name)
+            WriteOutputHeaderRow(sht)
+            start_row = 1
+            for base_data in base_rd_data[sht_name]:
+                ContentName = base_data.ContentName
+                target_data = FindContent(ContentName, target_rd_data[sht_name])
+                if (target_data != ''):
+                    total_rows = WriteRDRecord(sht, base_data, target_data,
+                                               start_row, bdrate_fmt, float_fmt)
+                    start_row += total_rows
+    output_wb.close()
diff --git a/tools/convexhull_framework/src/ConvexHullTest.py b/tools/convexhull_framework/src/ConvexHullTest.py
new file mode 100644
index 0000000..327249b
--- /dev/null
+++ b/tools/convexhull_framework/src/ConvexHullTest.py
@@ -0,0 +1,400 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+import sys
+import xlsxwriter
+import argparse
+from EncDecUpscale import Run_EncDec_Upscale, GetBsReconFileName
+from VideoScaler import GetDownScaledOutFile, DownScaling
+from CalculateQualityMetrics import CalculateQualityMetric, GatherQualityMetrics
+from Utils import GetShortContentName, GetVideoInfo, CreateChart_Scatter,\
+     AddSeriesToChart_Scatter, InsertChartsToSheet, CreateNewSubfolder,\
+     GetContents, SetupLogging, UpdateChart, AddSeriesToChart_Scatter_Rows,\
+     Cleanfolder
+from PostAnalysis_Summary import GenerateSummaryExcelFile,\
+     GenerateSummaryConvexHullExcelFile
+from ScalingTest import Run_Scaling_Test, SaveScalingResultsToExcel
+import Utils
+from Config import LogLevels, FrameNum, DnScaleRatio, QPs, CvxH_WtCols,\
+     CvxH_WtRows, QualityList, LineColors, DnScalingAlgos, UpScalingAlgos,\
+     ContentPath, SummaryOutPath, WorkPath, Path_RDResults, Clips, \
+     ConvexHullColor, EncodeMethods, CodecNames, LoggerName, LogCmdOnly
+
+###############################################################################
+##### Helper Functions ########################################################
+def CleanIntermediateFiles():
+    folders = [Path_DecodedYuv, Path_CfgFiles]
+    if not KeepUpscaledOutput:
+        folders += [Path_DecUpScaleYuv, Path_UpScaleYuv]
+
+    for folder in folders:
+        Cleanfolder(folder)
+
+def GetRDResultExcelFile(content):
+    contentBaseName = GetShortContentName(content)
+    filename = "RDResults_%s_%s_%s_%s.xlsx" % (contentBaseName, EncodeMethod,
+                                               CodecName, EncodePreset)
+    file = os.path.join(Path_RDResults, filename)
+    return file
+
+def setupWorkFolderStructure():
+    global Path_Bitstreams, Path_DecodedYuv, Path_UpScaleYuv, Path_DnScaleYuv,\
+           Path_QualityLog, Path_TestLog, Path_CfgFiles, Path_DecUpScaleYuv
+    Path_Bitstreams = CreateNewSubfolder(WorkPath, "bistreams")
+    Path_DecodedYuv = CreateNewSubfolder(WorkPath, "decodedYUVs")
+    Path_UpScaleYuv = CreateNewSubfolder(WorkPath, "upscaledYUVs")
+    Path_DecUpScaleYuv = CreateNewSubfolder(WorkPath, "decUpscaledYUVs")
+    Path_DnScaleYuv = CreateNewSubfolder(WorkPath, "downscaledYUVs")
+    Path_QualityLog = CreateNewSubfolder(WorkPath, "qualityLogs")
+    Path_TestLog = CreateNewSubfolder(WorkPath, 'testLogs')
+    Path_CfgFiles = CreateNewSubfolder(WorkPath, "configFiles")
+
+'''
+The convex_hull function is adapted based on the original python implementation
+from https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Convex_hull/Monotone_chain
+It is changed to return the lower and upper portions of the convex hull separately
+to get the convex hull based on traditional rd curve, only the upper portion is
+needed.
+'''
+
+def convex_hull(points):
+    """Computes the convex hull of a set of 2D points.
+    Input: an iterable sequence of (x, y) pairs representing the points.
+    Output: a list of vertices of the convex hull in counter-clockwise order,
+      starting from the vertex with the lexicographically smallest coordinates.
+    Implements Andrew's monotone chain algorithm. O(n log n) complexity.
+    """
+
+    # Sort the points lexicographically (tuples are compared lexicographically).
+    # Remove duplicates to detect the case we have just one unique point.
+    points = sorted(set(points))
+
+    # Boring case: no points or a single point, possibly repeated multiple times.
+    if len(points) <= 1:
+        return points
+
+    # 2D cross product of OA and OB vectors, i.e. z-component of their 3D cross
+    # product. Returns a positive value, if OAB makes a counter-clockwise turn,
+    # negative for clockwise turn, and zero if the points are collinear.
+    def cross(o, a, b):
+        return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0])
+
+    # Build lower hull
+    lower = []
+    for p in points:
+        while len(lower) >= 2 and cross(lower[-2], lower[-1], p) <= 0:
+            lower.pop()
+        lower.append(p)
+
+    # Build upper hull
+    upper = []
+    for p in reversed(points):
+        while len(upper) >= 2 and cross(upper[-2], upper[-1], p) <= 0:
+            upper.pop()
+        upper.append(p)
+
+    return lower, upper
+
+def AddConvexHullCurveToCharts(sht, startrow, startcol, charts, rdPoints,
+                               dnScaledRes, sum_sht, sum_start_row):
+    shtname = sht.get_name()
+    sht.write(startrow, startcol, "ConvexHull Data")
+    numitems = 5  # qty, bitrate, qp, resolution, 1 empty row as internal
+    rows = [startrow + 1 + numitems * i for i in range(len(QualityList))]
+
+    hull = {}
+    max_len = 0
+
+    for qty, idx, row in zip(QualityList, range(len(QualityList)), rows):
+        lower, upper = convex_hull(rdPoints[idx])
+        hull[qty] = upper
+        max_len = max(max_len, len(upper))
+        sht.write(row, startcol, qty)
+        sht.write(row + 1, startcol, "Bitrate(kbps)")
+        sht.write(row + 2, startcol, "QP")
+        sht.write(row + 3, startcol, 'Resolution')
+
+        brts = [h[0] for h in hull[qty]]
+        qtys = [h[1] for h in hull[qty]]
+        sht.write_row(row, startcol + 1, qtys)
+        sht.write_row(row + 1, startcol + 1, brts)
+
+        rdpnts_qtys = [rd[1] for rd in rdPoints[idx]]
+        cvh_qidxs = [rdpnts_qtys.index(qty) for qty in qtys]
+        cvh_QPs = [QPs[i % len(QPs)] for i in cvh_qidxs]
+        cvh_Res = [dnScaledRes[i // len(QPs)] for i in cvh_qidxs]
+        cvh_Res_txt = ["%sx%s" % (x, y) for (x, y) in cvh_Res]
+        sht.write_row(row + 2, startcol + 1, cvh_QPs)
+        sht.write_row(row + 3, startcol + 1, cvh_Res_txt)
+
+        cols = [startcol + 1 + i for i in range(len(hull[qty]))]
+        AddSeriesToChart_Scatter_Rows(shtname, cols, row, row + 1, charts[idx],
+                                      'ConvexHull', ConvexHullColor)
+
+    endrow = rows[-1] + numitems
+
+    #add convex hull data into summary sheet. the length of each convex hull
+    #could be different
+    sum_row = sum_start_row
+    sum_sht.write(sum_row, 2, max_len)
+    sum_col = 3
+    for qty in QualityList:
+        row = sum_row
+        for point in hull[qty]:
+            sum_sht.write(row, sum_col, point[0])
+            sum_sht.write(row, sum_col + 1, point[1])
+            row += 1
+        sum_col += 2
+
+    return endrow, sum_start_row + max_len - 1
+
+###############################################################################
+######### Major Functions #####################################################
+def CleanUp_workfolders():
+    folders = [Path_DnScaleYuv, Path_Bitstreams, Path_DecodedYuv, Path_QualityLog,
+               Path_TestLog, Path_CfgFiles]
+    if not KeepUpscaledOutput:
+        folders += [Path_UpScaleYuv, Path_DecUpScaleYuv]
+
+    for folder in folders:
+        Cleanfolder(folder)
+
+def Run_ConvexHull_Test(content, dnScalAlgo, upScalAlgo):
+    Utils.Logger.info("%s %s start running xcode tests with %s" %
+                      (EncodeMethod, CodecName, os.path.basename(content)))
+    cls, width, height, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
+    if totalnum < FrameNum:
+        Utils.Logger.error("content %s includes total %d frames < specified % frames!"
+                           % (content, totalnum, FrameNum))
+        return
+
+    DnScaledRes = [(int(width / ratio), int(height / ratio)) for ratio in DnScaleRatio]
+    for i in range(len(DnScaledRes)):
+        if SaveMemory:
+            CleanIntermediateFiles()
+        DnScaledW = DnScaledRes[i][0]
+        DnScaledH = DnScaledRes[i][1]
+        #downscaling if the downscaled file does not exist
+        dnscalyuv = GetDownScaledOutFile(content, width, height, DnScaledW,
+                                         DnScaledH, Path_DnScaleYuv, dnScalAlgo)
+        if not os.path.isfile(dnscalyuv):
+            dnscalyuv = DownScaling(content, FrameNum, width, height, DnScaledW,
+                                    DnScaledH, Path_DnScaleYuv, dnScalAlgo)
+
+        for QP in QPs:
+            Utils.Logger.info("start transcode and upscale for %dx%d and QP %d"
+                              % (DnScaledW, DnScaledH, QP))
+            #transcode and upscaling
+            reconyuv = Run_EncDec_Upscale(EncodeMethod, CodecName, EncodePreset,
+                                          dnscalyuv, QP, FrameNum, fr, DnScaledW,
+                                          DnScaledH, width, height, Path_Bitstreams,
+                                          Path_DecodedYuv, Path_DecUpScaleYuv,
+                                          upScalAlgo)
+            #calcualte quality distortion
+            Utils.Logger.info("start quality metric calculation for %dx%d and QP %d"
+                              % (DnScaledW, DnScaledH, QP))
+            CalculateQualityMetric(content, FrameNum, reconyuv, width, height,
+                                   Path_QualityLog, Path_CfgFiles)
+
+    if SaveMemory:
+        Cleanfolder(Path_DnScaleYuv)
+
+    Utils.Logger.info("finish running encode test.")
+
+def SaveConvexHullResultsToExcel(content, dnScAlgos, upScAlgos, sum_wb,
+                                 sum_start_row):
+    Utils.Logger.info("start saving RD results to excel file.......")
+    if not os.path.exists(Path_RDResults):
+        os.makedirs(Path_RDResults)
+    excFile = GetRDResultExcelFile(content)
+    wb = xlsxwriter.Workbook(excFile)
+    shts = []
+    for i in range(len(dnScAlgos)):
+        shtname = dnScAlgos[i] + '--' + upScAlgos[i]
+        shts.append(wb.add_worksheet(shtname))
+
+    cls, width, height, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
+    DnScaledRes = [(int(width / ratio), int(height / ratio)) for ratio in DnScaleRatio]
+    contentname = GetShortContentName(content)
+    for sht, indx in zip(shts, list(range(len(dnScAlgos)))):
+        # write QP
+        sht.write(1, 0, "QP")
+        sht.write_column(CvxH_WtRows[0], 0, QPs)
+        shtname = sht.get_name()
+        sum_sht = sum_wb.get_worksheet_by_name(shtname)
+
+        charts = [];  y_mins = {}; y_maxs = {}; RDPoints = {}
+        for qty, x in zip(QualityList, range(len(QualityList))):
+            chart_title = 'RD Curves - %s with %s' % (contentname, shtname)
+            xaxis_name = 'Bitrate - Kbps'
+            chart = CreateChart_Scatter(wb, chart_title, xaxis_name, qty)
+            charts.append(chart)
+            y_mins[x] = []; y_maxs[x] = []; RDPoints[x] = []
+
+        # write RD data
+        for col, i in zip(CvxH_WtCols, range(len(DnScaledRes))):
+            DnScaledW = DnScaledRes[i][0]
+            DnScaledH = DnScaledRes[i][1]
+            sht.write(0, col, "resolution=%dx%d" % (DnScaledW, DnScaledH))
+            sht.write(1, col, "Bitrate(kbps)")
+            sht.write_row(1, col + 1, QualityList)
+
+            bitratesKbps = []; qualities = []
+            for qp in QPs:
+                bs, reconyuv = GetBsReconFileName(EncodeMethod, CodecName,
+                                                  EncodePreset, content, width,
+                                                  height, DnScaledW, DnScaledH,
+                                                  dnScAlgos[indx], upScAlgos[indx],
+                                                  qp, Path_Bitstreams)
+                bitrate = (os.path.getsize(bs) * 8 * fr / FrameNum) / 1000.0
+                bitratesKbps.append(bitrate)
+                quality = GatherQualityMetrics(reconyuv, Path_QualityLog)
+                qualities.append(quality)
+
+            sht.write_column(CvxH_WtRows[0], col, bitratesKbps)
+            for qs, row in zip(qualities, CvxH_WtRows):
+                sht.write_row(row, col + 1, qs)
+
+            seriname = "resolution %dx%d" % (DnScaledW, DnScaledH)
+            for x in range(len(QualityList)):
+                # add RD curves of current resolution to each quality chart
+                AddSeriesToChart_Scatter(shtname, CvxH_WtRows, col + 1 + x, col,
+                                         charts[x], seriname, LineColors[i])
+                # get min and max of y-axis
+                qs = [row[x] for row in qualities]
+                y_mins[x].append(min(qs))
+                y_maxs[x].append(max(qs))
+                # get RD points - (bitrate, quality) for each quality metrics
+                rdpnts = [(brt, qty) for brt, qty in zip(bitratesKbps, qs)]
+                RDPoints[x] = RDPoints[x] + rdpnts
+
+        # add convexhull curve to charts
+        startrow = CvxH_WtRows[-1] + 2; startcol = 0
+
+        sum_startrow = sum_start_row[shtname]
+        sum_sht.write(sum_startrow, 0, cls)
+        sum_sht.write(sum_startrow, 1, contentname)
+        endrow, sum_end_row = AddConvexHullCurveToCharts(sht, startrow, startcol,
+                                                         charts, RDPoints,
+                                                         DnScaledRes, sum_sht,
+                                                         sum_startrow)
+        sum_start_row[shtname] = sum_end_row + 1
+
+        #update RD chart with approprate y axis range
+        for qty, x in zip(QualityList, range(len(QualityList))):
+            ymin = min(y_mins[x])
+            ymax = max(y_maxs[x])
+            margin = 0.1  # add 10% on min and max value for y_axis range
+            num_precsn = 5 if 'MS-SSIM' in qty else 3
+            UpdateChart(charts[x], ymin, ymax, margin, qty, num_precsn)
+
+        startrow = endrow + 2; startcol = 1
+        InsertChartsToSheet(sht, startrow, startcol, charts)
+
+    wb.close()
+    Utils.Logger.info("finish export convex hull results to excel file.")
+
+def ParseArguments(raw_args):
+    parser = argparse.ArgumentParser(prog='ConvexHullTest.py',
+                                     usage='%(prog)s [options]',
+                                     description='')
+    parser.add_argument('-f', '--function', dest='Function', type=str,
+                        required=True, metavar='',
+                        choices=["clean", "scaling", "sumscaling", "encode",
+                                 "convexhull", "summary"],
+                        help="function to run: clean, scaling, sumscaling, encode,"
+                             " convexhull, summary")
+    parser.add_argument('-k', "--KeepUpscaleOutput", dest='KeepUpscaledOutput',
+                        type=bool, default=False, metavar='',
+                        help="in function clean, if keep upscaled yuv files. It"
+                             " is false by default")
+    parser.add_argument('-s', "--SaveMemory", dest='SaveMemory', type=bool,
+                        default=False, metavar='',
+                        help="save memory mode will delete most files in"
+                             " intermediate steps and keeps only necessary "
+                             "ones for RD calculation. It is false by default")
+    parser.add_argument('-l', "--LoggingLevel", dest='LogLevel', type=int,
+                        default=3, choices=range(len(LogLevels)), metavar='',
+                        help="logging level: 0:No Logging, 1: Critical, 2: Error,"
+                             " 3: Warning, 4: Info, 5: Debug")
+    parser.add_argument('-c', "--CodecName", dest='CodecName', type=str,
+                        choices=CodecNames, metavar='',
+                        help="CodecName: av1 or hevc")
+    parser.add_argument('-m', "--EncodeMethod", dest='EncodeMethod', type=str,
+                        choices=EncodeMethods, metavar='',
+                        help="EncodeMethod: ffmpeg, aom, svt")
+    parser.add_argument('-p', "--EncodePreset", dest='EncodePreset', type=str,
+                        metavar='', help="EncodePreset: medium, slow, fast, etc"
+                                         " for ffmpeg, 0,1,2... for aom and svt")
+    if len(raw_args) == 1:
+        parser.print_help()
+        sys.exit(1)
+    args = parser.parse_args(raw_args[1:])
+
+    global Function, KeepUpscaledOutput, SaveMemory, LogLevel, CodecName,\
+        EncodeMethod, EncodePreset
+    Function = args.Function
+    KeepUpscaledOutput = args.KeepUpscaledOutput
+    SaveMemory = args.SaveMemory
+    LogLevel = args.LogLevel
+    CodecName = args.CodecName
+    EncodeMethod = args.EncodeMethod
+    EncodePreset = args.EncodePreset
+
+
+######################################
+# main
+######################################
+if __name__ == "__main__":
+    #sys.argv = ["","-f","convexhull","-c","hevc","-m","ffmpeg","-p","medium"]
+    ParseArguments(sys.argv)
+
+    # preparation for executing functions
+    setupWorkFolderStructure()
+    if Function != 'clean':
+        SetupLogging(LogLevel, LogCmdOnly, LoggerName, Path_TestLog)
+        Contents = GetContents(ContentPath, Clips)
+
+    # execute functions
+    if Function == 'clean':
+        CleanUp_workfolders()
+    elif Function == 'scaling':
+        for content in Contents:
+            for dnScaleAlgo, upScaleAlgo in zip(DnScalingAlgos, UpScalingAlgos):
+                Run_Scaling_Test(content, dnScaleAlgo, upScaleAlgo,
+                                 Path_DnScaleYuv, Path_UpScaleYuv, Path_QualityLog,
+                                 Path_CfgFiles, SaveMemory, KeepUpscaledOutput)
+    elif Function == 'sumscaling':
+        SaveScalingResultsToExcel(DnScalingAlgos, UpScalingAlgos, Path_QualityLog)
+    elif Function == 'encode':
+        for content in Contents:
+            for dnScalAlgo, upScalAlgo in zip(DnScalingAlgos, UpScalingAlgos):
+                Run_ConvexHull_Test(content, dnScalAlgo, upScalAlgo)
+    elif Function == 'convexhull':
+        sum_wb, sum_start_row = GenerateSummaryConvexHullExcelFile(EncodeMethod,
+                                                                   CodecName,
+                                                                   EncodePreset,
+                                                                   SummaryOutPath,
+                                                                   DnScalingAlgos,
+                                                                   UpScalingAlgos)
+        for content in Contents:
+            SaveConvexHullResultsToExcel(content, DnScalingAlgos, UpScalingAlgos,
+                                         sum_wb, sum_start_row)
+        sum_wb.close()
+    elif Function == 'summary':
+        smfile = GenerateSummaryExcelFile(EncodeMethod, CodecName, EncodePreset,
+                                          SummaryOutPath, Path_RDResults,
+                                          ContentPath, Clips)
+        Utils.Logger.info("summary file generated: %s" % smfile)
+    else:
+        Utils.Logger.error("invalid parameter value of Function")
diff --git a/tools/convexhull_framework/src/EncDecUpscale.py b/tools/convexhull_framework/src/EncDecUpscale.py
new file mode 100644
index 0000000..812eade
--- /dev/null
+++ b/tools/convexhull_framework/src/EncDecUpscale.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+from VideoEncoder import VideoEncode
+from VideoDecoder import VideoDecode
+from VideoScaler import UpScaling, GetDownScaledOutFile, GetUpScaledOutFile
+from Config import SUFFIX, LoggerName
+from Utils import GetShortContentName
+import logging
+
+subloggername = "EncDecUpscale"
+loggername = LoggerName + '.' + '%s' % subloggername
+logger = logging.getLogger(loggername)
+
+################################################################################
+##################### Internal Helper Functions ################################
+def GetBitstreamFile(method, codec, preset, yuvfile, qp, outpath):
+    bs_suffix = SUFFIX[codec]
+    Prefix_EncodeCfg = '_%s_%s_Preset_%s' % (method, codec, preset)
+    filename = GetShortContentName(yuvfile, False) + Prefix_EncodeCfg + "_QP_"\
+               + str(qp) + bs_suffix
+    filename = os.path.join(outpath, filename)
+    return filename
+
+def GetDecodedFile(bsfile, outpath):
+    filename = GetShortContentName(bsfile, False) + '_Decoded.yuv'
+    decodedfile = os.path.join(outpath, filename)
+    return decodedfile
+
+################################################################################
+##################### Major Functions ##########################################
+def Encode(method, codec, preset, input, qp, num, framerate, width, height, path):
+    bsfile = GetBitstreamFile(method, codec, preset, input, qp, path)
+    # call VideoEncoder to do the encoding
+    VideoEncode(method, codec, input, qp, num, framerate, width, height, bsfile,
+                preset)
+    return bsfile
+
+
+def Decode(codec, bsfile, path):
+    decodedfile = GetDecodedFile(bsfile, path)
+    #call VideoDecoder to do the decoding
+    VideoDecode(codec, bsfile, decodedfile)
+    return decodedfile
+
+
+def Run_EncDec_Upscale(method, codec, preset, input, QP, num, framerate, inw,
+                       inh, outw, outh, path_bs, path_decoded,
+                       path_upscaled, algo):
+    logger.info("%s %s start encode file %s with QP = %d"
+                % (method, codec, os.path.basename(input), QP))
+    bsFile = Encode(method, codec, preset, input, QP, num, framerate, inw, inh,
+                    path_bs)
+    logger.info("start decode file %s" % os.path.basename(bsFile))
+    decodedYUV = Decode(codec, bsFile, path_decoded)
+    logger.info("start upscale file %s" % os.path.basename(decodedYUV))
+    upscaledYUV = UpScaling(decodedYUV, num, inw, inh, outw, outh, path_upscaled,
+                            algo)
+    logger.info("finish Run Encode, Decode and Upscale")
+    return upscaledYUV
+
+
+def GetBsReconFileName(encmethod, codecname, preset, content, width, height,
+                       dnwidth, dnheight, dnScAlgo,
+                       upScAlgo, qp, path_bs):
+    dsyuv_name = GetDownScaledOutFile(content, width, height, dnwidth, dnheight,
+                                      path_bs, dnScAlgo)
+    # return bitstream file with absolute path
+    bs = GetBitstreamFile(encmethod, codecname, preset, dsyuv_name, qp, path_bs)
+    decoded = GetDecodedFile(bs, path_bs)
+    reconfilename = GetUpScaledOutFile(decoded, dnwidth, dnheight, width, height,
+                                       path_bs, upScAlgo)
+    # return only Recon yuv file name w/o path
+    reconfilename = GetShortContentName(reconfilename, False)
+    return bs, reconfilename
diff --git a/tools/convexhull_framework/src/PostAnalysis_Summary.py b/tools/convexhull_framework/src/PostAnalysis_Summary.py
new file mode 100644
index 0000000..1d711e9
--- /dev/null
+++ b/tools/convexhull_framework/src/PostAnalysis_Summary.py
@@ -0,0 +1,389 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+import xlsxwriter
+import xlrd
+from Config import QPs, DnScaleRatio, QualityList, VbaBinFile, CvxH_WtRows,\
+    CvxH_WtLastCol, LoggerName
+from Utils import GetShortContentName, CalcRowsClassAndContentDict,\
+    SweepScalingAlgosInOneResultFile
+import logging
+
+subloggername = "PostAnalysisSummary"
+loggername = LoggerName + '.' + '%s' % subloggername
+logger = logging.getLogger(loggername)
+
+# give all paths including convex hull result file (only one file for each
+# content) to generate summary file for all contents in Input path
+# assume all content's result has same test settings
+
+################################################################################
+### Helper Functions ###########################################################
+def GetSummaryFileName(encMethod, codecName, preset, path):
+    name = 'ConvexHullSummary_ScaleAlgosNum_%d_%s_%s_%s.xlsm'\
+           % (len(DnScaleRatio), encMethod, codecName, preset)
+    return os.path.join(path, name)
+
+def GetConvexHullRDFileName(encMethod, codecName, preset, path):
+    name = 'ConvexHullRD_ScaleAlgosNum_%d_%s_%s_%s.xlsx'\
+           % (len(DnScaleRatio), encMethod, codecName, preset)
+    return os.path.join(path, name)
+
+def CopyResultDataToSummaryFile_Onesheet(sht, wt_cols, contentsdict, rows_class,
+                                         infile_path):
+    rdrows = CvxH_WtRows
+    rd_endcol = CvxH_WtLastCol
+
+    shtname = sht.get_name()
+    sht.write(1, 0, 'Content Class')
+    sht.write(1, 1, 'Content Name')
+    sht.write(1, 2, 'QP')
+    for residx, col in zip(range(len(DnScaleRatio)), wt_cols):
+        sht.write(0, col, 'Scaling Ratio = %.2f' % (DnScaleRatio[residx]))
+        sht.write(1, col, 'Bitrate(kbps)')
+        qtynames = ['%s' % qty for qty in QualityList]
+        sht.write_row(1, col + 1, qtynames)
+
+    # copy the results data from each content's result file to corresponding
+    # location in summary excel file
+    resultfiles = os.listdir(infile_path)
+    for (cls, contents), row_class in zip(contentsdict.items(), rows_class):
+        sht.write(row_class, 0, cls)
+        rows_content = [i * len(QPs) for i in range(len(contents))]
+        for content, row_cont in zip(contents, rows_content):
+            key = GetShortContentName(content)
+            sht.write(row_class + row_cont, 1, key)
+            rdwb = None
+            for resfile in resultfiles:
+                if key in resfile:
+                    rdwb = xlrd.open_workbook(os.path.join(infile_path, resfile))
+                    rdsht = rdwb.sheet_by_name(shtname)
+                    for i, rdrow in zip(range(len(QPs)), rdrows):
+                        data = rdsht.row_values(rdrow, 0, rd_endcol + 1)
+                        sht.write_row(row_class + row_cont + i, 2, data)
+                    break
+            assert rdwb is not None
+            if rdwb is None:
+                logger.warning("not find convex hull result file for content:%s"
+                               % content)
+
+def CalBDRate_OneSheet(sht,  cols, contentsdict, rows_class, cols_bdmtrs, cellformat):
+    row_refst = 0
+    bdstep = 3
+    for cols_bd, residx in zip(cols_bdmtrs, range(1, len(DnScaleRatio))):
+        sht.write(0, cols_bd, 'BD-Rate %.2f vs. %.2f' % (DnScaleRatio[residx],
+                                                         DnScaleRatio[0]))
+        sht.write_row(1, cols_bd, QualityList)
+        for (cls, contents), row_class in zip(contentsdict.items(), rows_class):
+            rows_content = [i * len(QPs) for i in range(len(contents))]
+            for row_cont in rows_content:
+                for y in range(len(QualityList)):
+                    refbr_b = xlrd.cellnameabs(row_class + row_cont + row_refst,
+                                               cols[0])
+                    refbr_e = xlrd.cellnameabs(row_class + row_cont + row_refst
+                                               + bdstep, cols[0])
+                    refq_b = xlrd.cellnameabs(row_class + row_cont + row_refst,
+                                              cols[0] + 1 + y)
+                    refq_e = xlrd.cellnameabs(row_class + row_cont + row_refst
+                                              + bdstep, cols[0] + 1 + y)
+
+                    testbr_b = xlrd.cellnameabs(row_class + row_cont,
+                                                cols[residx])
+                    testbr_e = xlrd.cellnameabs(row_class + row_cont + bdstep,
+                                                cols[residx])
+                    testq_b = xlrd.cellnameabs(row_class + row_cont,
+                                               cols[residx] + 1 + y)
+                    testq_e = xlrd.cellnameabs(row_class + row_cont + bdstep,
+                                               cols[residx] + 1 + y)
+
+                    #formula = '=-bdrate(%s:%s,%s:%s,%s:%s,%s:%s)' % (
+                    #refbr_b, refbr_e, refq_b, refq_e, testbr_b, testbr_e,
+                    # testq_b, testq_e)
+                    formula = '=bdRateExtend(%s:%s,%s:%s,%s:%s,%s:%s)'\
+                              % (refbr_b, refbr_e, refq_b, refq_e, testbr_b,
+                                 testbr_e, testq_b, testq_e)
+                    sht.write_formula(row_class + row_cont, cols_bd + y, formula,
+                                      cellformat)
+
+def GenerateFormula_SumRows(shtname, rows, col):
+    cells = ''
+    for row in rows:
+        location = xlrd.cellnameabs(row, col)
+        cells = cells + '\'%s\'!%s,' % (shtname, location)
+    cells = cells[:-1]  # remove the last ,
+    formula = '=SUM(%s)/%d' % (cells, len(rows))
+    return formula
+
+def GenerateFormula_SumRows_Weighted(rows, col, weight_rows, weight_col, num):
+    cells = ''
+    for row, wtrow in zip(rows, weight_rows):
+        location = xlrd.cellnameabs(row, col)
+        weight = xlrd.cellnameabs(wtrow, weight_col)
+        cells = cells + '%s * %s,' % (location, weight)
+    cells = cells[:-1]  # remove the last ,
+    formula = '=SUM(%s)/%d' % (cells, num)
+    return formula
+
+def WriteBitrateQtyAverageSheet(wb, rdshts, contentsdict, rd_rows_class, rdcols):
+    avg_sht = wb.add_worksheet('Average')
+    avg_sht.write(2, 0, 'Content Class')
+    avg_sht.write(2, 1, 'Content Number')
+    avg_sht.write(2, 2, 'QP')
+
+    colstart = 3
+    cols_res = [colstart]
+    step = len(QualityList) + 1 + 1  # 1 for bitrate, 1 for interval
+    colres_2nd_start = colstart + step
+    step = len(upScalAlgos) * (len(QualityList) + 1) + 1  # + 1 for interval
+    cols_res += [step * i + colres_2nd_start for i in range(len(DnScaleRatio) - 1)]
+    step = len(QualityList) + 1  # + 1 for bitrate
+    cols_upscl = [step * i for i in range(len(upScalAlgos))]
+    for residx, col_res in zip(range(len(DnScaleRatio)), cols_res):
+        avg_sht.write(0, col_res, 'ScalingRatio = %.2f' % (DnScaleRatio[residx]))
+        if residx == 0:
+            avg_sht.write(1, col_res + 1, 'None')
+            avg_sht.write(2, col_res, 'Bitrate(kbps)')
+            avg_sht.write_row(2, col_res + 1, QualityList)
+        else:
+            for dnsc, upsc, col_upscl in zip(dnScalAlgos, upScalAlgos, cols_upscl):
+                avg_sht.write(1, col_res + col_upscl + 1, '%s--%s' % (dnsc, upsc))
+                avg_sht.write(2, col_res + col_upscl, 'Bitrate(kbps)')
+                avg_sht.write_row(2, col_res + col_upscl + 1, QualityList)
+
+    startrow = 3
+    step = len(QPs)
+    rows_class_avg = [startrow + step * i for i in range(len(contentsdict))]
+    totalnum_content = 0
+    for (cls, contents), row_class, rdclassrow in zip(contentsdict.items(),
+                                                      rows_class_avg,
+                                                      rd_rows_class):
+        avg_sht.write(row_class, 0, cls)
+        totalnum_content = totalnum_content + len(contents)
+        avg_sht.write(row_class, 1, len(contents))
+        avg_sht.write_column(row_class, 2, QPs)
+        rows_content = [i * len(QPs) for i in range(len(contents))]
+
+        for rdcol, col_res, residx in zip(rdcols, cols_res, range(len(DnScaleRatio))):
+            for i in range(len(QPs)):
+                sum_rows = [rdclassrow + row_cont + i for row_cont in rows_content]
+                for col_upscl, sht in zip(cols_upscl, rdshts):
+                    shtname = sht.get_name()
+                    # write bitrate average formula.
+                    formula = GenerateFormula_SumRows(shtname, sum_rows, rdcol)
+                    avg_sht.write_formula(row_class + i, col_res + col_upscl,
+                                          formula)
+                    # write quality average formula
+                    for j in range(len(QualityList)):
+                        formula = GenerateFormula_SumRows(shtname, sum_rows,
+                                                          rdcol + 1 + j)
+                        avg_sht.write_formula(row_class + i,
+                                              col_res + col_upscl + 1 + j,
+                                              formula)
+
+                    # for first resolution, no down and up scaling. only need
+                    # one set of bitrate/quality data
+                    if residx == 0:
+                        break
+
+    # write total average
+    last_class_row = rows_class_avg[-1] + len(QPs) + 1  # 1 for 1 row of interval
+    avg_sht.write(last_class_row, 0, 'Total')
+    avg_sht.write(last_class_row, 1, totalnum_content)
+    avg_sht.write_column(last_class_row, 2, QPs)
+    weight_rows = [row_class for row_class in rows_class_avg]
+    for col_res, residx in zip(cols_res, range(len(DnScaleRatio))):
+        for i in range(len(QPs)):
+            sum_rows = [row_class + i for row_class in rows_class_avg]
+            for col_upscl in cols_upscl:
+                # bitrate average
+                formula = GenerateFormula_SumRows_Weighted(sum_rows,
+                                                           col_res + col_upscl,
+                                                           weight_rows, 1,
+                                                           totalnum_content)
+                avg_sht.write_formula(last_class_row + i, col_res + col_upscl,
+                                      formula)
+                # quality average
+                for j in range(len(QualityList)):
+                    formula = GenerateFormula_SumRows_Weighted(sum_rows,
+                                                               col_res +
+                                                               col_upscl + 1 + j,
+                                                               weight_rows, 1,
+                                                               totalnum_content)
+                    avg_sht.write_formula(last_class_row + i,
+                                          col_res + col_upscl + 1 + j, formula)
+                # for first resolution, no down and up scaling. only need one
+                # set of bitrate/quality data
+                if residx == 0:
+                    break
+
+def WriteBDRateAverageSheet(wb, rdshts, contentsdict, rd_rows_class,
+                            rd_cols_bdmtrs, cellformat):
+    # write bdrate average sheet
+    bdavg_sht = wb.add_worksheet('Average_BDRate')
+    bdavg_sht.write(2, 0, 'Content Class')
+    bdavg_sht.write(2, 1, 'Content Number')
+
+    startcol = 2
+    startrow = 3
+    colintval_scalalgo = 1
+    colintval_dnscalres = 1
+
+    step_upscl = len(QualityList) + colintval_scalalgo
+    cols_upscl_bd = [step_upscl * i for i in range(len(upScalAlgos))]
+    step_res = len(upScalAlgos) * step_upscl + colintval_dnscalres
+    cols_res_bd = [step_res * i + startcol for i in range(len(DnScaleRatio) - 1)]
+    rows_class_rdavg = [startrow + i for i in range(len(contentsdict))]
+
+    for residx, col_res_bd in zip(range(1, len(DnScaleRatio)), cols_res_bd):
+        bdavg_sht.write(0, col_res_bd, 'BD-Rate %.2f vs. %.2f'
+                        % (DnScaleRatio[residx], DnScaleRatio[0]))
+        for dnsc, upsc, col_upscl_bd in zip(dnScalAlgos, upScalAlgos, cols_upscl_bd):
+            bdavg_sht.write(1, col_res_bd + col_upscl_bd, '%s--%s' % (dnsc, upsc))
+            bdavg_sht.write_row(2, col_res_bd + col_upscl_bd, QualityList)
+
+    totalnum_content = 0
+    for (cls, contents), row_class, rdclassrow in zip(contentsdict.items(),
+                                                      rows_class_rdavg,
+                                                      rd_rows_class):
+        bdavg_sht.write(row_class, 0, cls)
+        totalnum_content = totalnum_content + len(contents)
+        bdavg_sht.write(row_class, 1, len(contents))
+        rows_content = [i * len(QPs) for i in range(len(contents))]
+        sum_rows = [rdclassrow + row_cont for row_cont in rows_content]
+        for rdcol, col_res in zip(rd_cols_bdmtrs, cols_res_bd):
+            # write average bd rate
+            for col_upscl, sht in zip(cols_upscl_bd, rdshts):
+                shtname = sht.get_name()
+                for j in range(len(QualityList)):
+                    formula = GenerateFormula_SumRows(shtname, sum_rows, rdcol + j)
+                    bdavg_sht.write_formula(row_class, col_res + col_upscl + j,
+                                            formula, cellformat)
+
+    # write total average
+    last_row = rows_class_rdavg[-1] + 1
+    bdavg_sht.write(last_row, 0, 'Total')
+    bdavg_sht.write(last_row, 1, totalnum_content)
+    sum_rows = [row_class for row_class in rows_class_rdavg]
+    for col_res in cols_res_bd:
+        for col_upscl in cols_upscl_bd:
+            for j in range(len(QualityList)):
+                formula = GenerateFormula_SumRows_Weighted(sum_rows,
+                                                           col_res + col_upscl + j,
+                                                           sum_rows, 1,
+                                                           totalnum_content)
+                bdavg_sht.write_formula(last_row, col_res + col_upscl + j,
+                                        formula, cellformat)
+
+#######################################################################
+#######################################################################
+# GenerateSummaryExcelFile is to
+# 1. summarize all contents convexhull results into one file
+# 2. calculate average of bitrate and quality metrics for each content class
+# 3. calculate BD rate across different scaling ratios for all scaling
+#    algorithms in convex hull test
+# 4. calcualte average BD rate for each content class
+# Arguments description:
+# content_paths is where test contents located, which used for generating convex
+#               hull results.
+# infile_path   is where convex hull results excel files located.
+# classes       is the content classes and  here it requires contents are located
+#               in corresponding subfolder named with its belonged class
+# summary_outpath  is the folder where output summary file will be
+# note: all results files under infile_path should have exactly same test items
+# before running this summary script
+def GenerateSummaryExcelFile(encMethod, codecName, preset, summary_outpath,
+                             infile_path, content_path, clips):
+    global dnScalAlgos, upScalAlgos
+    # find all scaling algos tested in results file, expect they are the same
+    # for every content
+    dnScalAlgos, upScalAlgos = SweepScalingAlgosInOneResultFile(infile_path)
+
+    if not os.path.exists(summary_outpath):
+        os.makedirs(summary_outpath)
+    smfile = GetSummaryFileName(encMethod, codecName, preset, summary_outpath)
+    wb = xlsxwriter.Workbook(smfile)
+
+    # shts is for all scaling algorithms' convex hull test results
+    shts = []
+    for dnsc, upsc in zip(dnScalAlgos, upScalAlgos):
+        shtname = dnsc + '--' + upsc
+        sht = wb.add_worksheet(shtname)
+        shts.append(sht)
+
+    # below variables define summary file data layout format.
+    # if to change them, modify CopyResultsDataToSummaryFile_Onesheet() and
+    # CalcRowsCategAndContentDict() accordingly
+    colstart = 3
+    colInterval = 2
+    rowstart = 2
+
+    # cols is column number of results files
+    step = colInterval + 1 + len(QualityList)  # 1 is for bitrate
+    sum_wtcols = [step * i + colstart for i in range(len(DnScaleRatio))]
+    # to generate rows number of starting of each class: rows_class
+    contentsdict, rows_class = CalcRowsClassAndContentDict(rowstart,
+                                                           content_path,
+                                                           clips, len(QPs))
+
+    wb.add_vba_project(VbaBinFile)
+    cellformat = wb.add_format()
+    cellformat.set_num_format('0.00%')
+    #cols_bdmtrs is the column number to write the bdrate data
+    step = len(QualityList) + 1
+    start_col_bd = sum_wtcols[-1] + step + 1
+    cols_bdmtrs = [start_col_bd + i * step for i in range(len(DnScaleRatio) - 1)]
+    # -1 because first resolution is used as reference
+
+    for sht in shts:
+        CopyResultDataToSummaryFile_Onesheet(sht, sum_wtcols, contentsdict,
+                                             rows_class, infile_path)
+        # calculate bd rate in each scaling sheet
+        CalBDRate_OneSheet(sht, sum_wtcols, contentsdict, rows_class,
+                           cols_bdmtrs, cellformat)
+
+    # calculate average bitrate and quality metrics for each category and
+    # write to "average" sheet
+    WriteBitrateQtyAverageSheet(wb, shts, contentsdict, rows_class, sum_wtcols)
+
+    # calculate average bd metrics and write to a new sheet
+    WriteBDRateAverageSheet(wb, shts, contentsdict, rows_class, cols_bdmtrs,
+                            cellformat)
+
+    wb.close()
+    return smfile
+
+def GenerateSummaryConvexHullExcelFile(encMethod, codecName, preset,
+                                       summary_outpath, DnScalingAlgos,
+                                       UpScalingAlgos):
+    if not os.path.exists(summary_outpath):
+        os.makedirs(summary_outpath)
+    smfile = GetConvexHullRDFileName(encMethod, codecName, preset,
+                                     summary_outpath)
+    sum_wb = xlsxwriter.Workbook(smfile)
+
+    # shts is for all scaling algorithms' convex hull test results
+    sum_start_row = {}
+    #write the header in each sheet
+    for dnsc, upsc in zip(DnScalingAlgos, UpScalingAlgos):
+        shtname = dnsc + '--' + upsc
+        sht = sum_wb.add_worksheet(shtname)
+        sht.write(0, 0, 'Content Class')
+        sht.write(0, 1, 'Content Name')
+        sht.write(0, 2, 'Num RD Points')
+        col = 3
+        for qty in QualityList:
+            sht.write(0, col, 'Bitrate(kbps)')
+            sht.write(0, col + 1, qty)
+            col += 2
+        sum_start_row[shtname] = 1
+    return sum_wb, sum_start_row
diff --git a/tools/convexhull_framework/src/ScalingTest.py b/tools/convexhull_framework/src/ScalingTest.py
new file mode 100644
index 0000000..a8f4a7a
--- /dev/null
+++ b/tools/convexhull_framework/src/ScalingTest.py
@@ -0,0 +1,366 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+import xlsxwriter
+import xlrd
+import logging
+from CalculateQualityMetrics import CalculateQualityMetric, GatherQualityMetrics
+from VideoScaler import GetDownScaledOutFile, DownScaling, UpScaling, GetUpScaledOutFile
+from Config import DnScaleRatio, FrameNum, Clips, QualityList, LoggerName, Path_ScalingResults, ScalQty_WtCols, \
+    ScalQty_startRow, LineColors, ScalSumQty_WtCols, ContentPath
+from Utils import GetVideoInfo, Cleanfolder, GetShortContentName, CreateChart_Scatter, AddSeriesToChart_Scatter, UpdateChart, \
+    InsertChartsToSheet, CalcRowsClassAndContentDict, GetContents, CreateChart_Line, AddSeriesToChart_Line
+
+subloggername = "ScalingTest"
+loggername = LoggerName + '.' + '%s' % subloggername
+logger = logging.getLogger(loggername)
+
+StatsMetrics = ['Max', 'Std+', 'Average', 'Std-', 'Min']
+
+def GetScalingResultExcelFile(rationum, scalAlgoNum):
+    filename = "ScalingResults_RatioNum_%d_AlgoNum_%d.xlsx"\
+               % (rationum, scalAlgoNum)
+    file = os.path.join(Path_ScalingResults, filename)
+    return file
+
+def GetScalingResultExcelFile_PerContent(content):
+    filename = GetShortContentName(content)
+    filename = "ScalingResults_%s.xlsx" % filename
+    file = os.path.join(Path_ScalingResults, filename)
+    return file
+
+def Run_Scaling_Test(content, dnScalAlgo, upScalAlgo, path_dnscl, path_upscl,
+                     path_log, path_cfg, savememory, keepupscaledyuv):
+    logger.info("start running scaling tests with content %s"
+                % os.path.basename(content))
+    cls, width, height, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
+    if totalnum < FrameNum:
+        logger.error("content %s includes total %d frames < specified % frames!"
+                     % (content, totalnum, FrameNum))
+        return
+
+    DnScaledRes = [(int(width / ratio), int(height / ratio))
+                   for ratio in DnScaleRatio]
+    for i in range(len(DnScaledRes)):
+        if savememory:
+            Cleanfolder(path_cfg)
+            if not keepupscaledyuv:
+                Cleanfolder(path_upscl)
+
+        DnScaledW = DnScaledRes[i][0]
+        DnScaledH = DnScaledRes[i][1]
+        logger.info("start downscaling content to %dx%d"
+                    % (DnScaledW, DnScaledH))
+        # downscaling
+        dnscalyuv = GetDownScaledOutFile(content, width, height, DnScaledW,
+                                         DnScaledH, path_dnscl, dnScalAlgo)
+        if not os.path.isfile(dnscalyuv):
+            dnscalyuv = DownScaling(content, FrameNum, width, height, DnScaledW,
+                                    DnScaledH, path_dnscl, dnScalAlgo)
+
+        upscaleyuv = UpScaling(dnscalyuv, FrameNum, DnScaledW, DnScaledH, width,
+                               height, path_upscl, upScalAlgo)
+
+        CalculateQualityMetric(content, FrameNum, upscaleyuv, width, height,
+                               path_log, path_cfg)
+
+    if savememory:
+        Cleanfolder(path_dnscl)
+
+    logger.info("finish running scaling test.")
+
+def GeneratePerContentExcelFile(dnScalAlgos, upScalAlgos, content, scaleRatios,
+                                path_log):
+    contshortname = GetShortContentName(content)
+    logger.info("start generate excel file for content :%s" % contshortname)
+    excFile = GetScalingResultExcelFile_PerContent(content)
+    wb = xlsxwriter.Workbook(excFile)
+    shtname = contshortname
+    sht = wb.add_worksheet(shtname)
+
+    sht.write(1, 0, 'Content Name')
+    sht.write(2, 0, contshortname)
+    sht.write(1, 1, 'Scaling Ratio')
+    sht.write_column(2, 1, scaleRatios)
+    pre_title = ['Width', 'Height', 'DnScaledWidth', 'DnScaledHeight']
+    sht.write_row(1, 2, pre_title)
+    for dn_algo, up_algo, col in zip(dnScalAlgos, upScalAlgos, ScalQty_WtCols):
+        algos = dn_algo + '--' + up_algo
+        sht.write(0, col, algos)
+        sht.write_row(1, col, QualityList)
+
+    cls, w, h, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
+    rows = [ScalQty_startRow + i for i in range(len(scaleRatios))]
+    continfos = []
+    for ratio, row in zip(scaleRatios, rows):
+        dw = int(w / ratio)
+        dh = int(h / ratio)
+        info = [w, h, dw, dh]
+        sht.write_row(row, 2, info)
+        continfos.append(info)
+
+    charts = []; y_mins = {}; y_maxs = {}
+    for qty, x in zip(QualityList, range(len(QualityList))):
+        chart_title = 'Scaling Quality - %s' % qty
+        xaxis_name = 'scaling ratio'
+        chart = CreateChart_Scatter(wb, chart_title, xaxis_name, qty)
+        charts.append(chart)
+        y_mins[x] = []; y_maxs[x] = []
+
+    for dn_algo, up_algo, col, i in zip(dnScalAlgos, upScalAlgos, ScalQty_WtCols,
+                                        range(len(dnScalAlgos))):
+        qualities = []
+        seriname = dn_algo + '--' + up_algo
+        for ratio, row, idx in zip(scaleRatios, rows, range(len(scaleRatios))):
+            w = continfos[idx][0]
+            h = continfos[idx][1]
+            dw = continfos[idx][2]
+            dh = continfos[idx][3]
+            dnScalOut = GetDownScaledOutFile(content, w, h, dw, dh,
+                                             path_log, dn_algo)
+            upScalOut = GetUpScaledOutFile(dnScalOut, dw, dh, w, h,
+                                           path_log, up_algo)
+            qtys = GatherQualityMetrics(upScalOut, path_log)
+            sht.write_row(row, col, qtys)
+            qualities.append(qtys)
+        for x in range(len(QualityList)):
+            AddSeriesToChart_Scatter(shtname, rows, col + x, 1, charts[x],
+                                     seriname, LineColors[i])
+
+        # get min and max of y-axis for a certain dn up scaling algo
+        for qty, x in zip(QualityList, range(len(QualityList))):
+            qs = [row[x] for row in qualities]
+            y_mins[x].append(min(qs))
+            y_maxs[x].append(max(qs))
+
+    for qty, x in zip(QualityList, range(len(QualityList))):
+        ymin = min(y_mins[x])
+        ymax = max(y_maxs[x])
+        margin = 0.1  # add 10% on min and max value for y_axis range
+        num_precsn = 5 if 'MS-SSIM' in qty else 3
+        UpdateChart(charts[x], ymin, ymax, margin, qty, num_precsn)
+
+    startrow = rows[-1] + 2; startcol = 1
+    InsertChartsToSheet(sht, startrow, startcol, charts)
+
+    wb.close()
+    logger.info("finish export scaling quality results to excel file.")
+
+def GenerateSummarySheet(wb, dnScalAlgos, upScalAlgos, ratio, contentsdict,
+                         rows_class, path_log):
+    logger.info("start generate summary sheet for ratio %2.2f" % ratio)
+
+    shts = []
+    shtname = "Ratio=%1.2f" % ratio
+    sht = wb.add_worksheet(shtname)
+    shts.append(sht)
+    sht.write(1, 0, 'Content Class')
+    sht.write(1, 1, 'Content NO.')
+    sht.write(1, 2, 'Content Name')
+    pre_title = ['Width', 'Height', 'DnScaledWidth', 'DnScaledHeight']
+    sht.write_row(1, 3, pre_title)
+
+    for dn_algo, up_algo, col in zip(dnScalAlgos, upScalAlgos, ScalSumQty_WtCols):
+        algos = dn_algo + '--' + up_algo
+        sht.write(0, col, algos)
+        sht.write_row(1, col, QualityList)
+
+    content_infos = {}; totalnum_contents = 0
+    for (clss, contents), row_clss in zip(contentsdict.items(), rows_class):
+        sht.write(row_clss, 0, clss)
+        totalnum_contents = totalnum_contents + len(contents)
+        for content, row_cont in zip(contents, range(len(contents))):
+            cl, w, h, fr, bitdepth, fmt, totalnum = GetVideoInfo(content, Clips)
+            dw = int(w / ratio)
+            dh = int(h / ratio)
+            shortcntname = GetShortContentName(content)
+            sht.write(row_clss + row_cont, 2, shortcntname)
+            infos = [w, h, dw, dh]
+            sht.write_row(row_clss + row_cont, 3, infos)
+            content_infos[shortcntname] = infos
+
+    charts = []; y_mins = {}; y_maxs = {}
+    for qty, x in zip(QualityList, range(len(QualityList))):
+        chart_title = '%s of %s' % (qty, shtname)
+        chart = CreateChart_Scatter(wb, chart_title, 'Contents', qty)
+        charts.append(chart)
+        y_mins[x] = []; y_maxs[x] = []
+
+    rows_all = [ScalQty_startRow + i for i in range(totalnum_contents)]
+    sht.write_column(ScalQty_startRow, 1, range(totalnum_contents))
+    for dn_algo, up_algo, col, i in zip(dnScalAlgos, upScalAlgos,
+                                        ScalSumQty_WtCols,
+                                        range(len(dnScalAlgos))):
+        qualities = []
+        seriname = dn_algo + '--' + up_algo
+        for (clss, contents), row_clss in zip(contentsdict.items(), rows_class):
+            for content, row_cont in zip(contents, range(len(contents))):
+                key = GetShortContentName(content)
+                w = content_infos[key][0]
+                h = content_infos[key][1]
+                dw = content_infos[key][2]
+                dh = content_infos[key][3]
+                dnScalOut = GetDownScaledOutFile(content, w, h, dw, dh,
+                                                 path_log, dn_algo)
+                upScalOut = GetUpScaledOutFile(dnScalOut, dw, dh, w, h,
+                                               path_log, up_algo)
+                qtys = GatherQualityMetrics(upScalOut, path_log)
+                sht.write_row(row_clss + row_cont, col, qtys)
+                qualities.append(qtys)
+
+        for x in range(len(QualityList)):
+            AddSeriesToChart_Scatter(shtname, rows_all, col + x, 1, charts[x],
+                                     seriname, LineColors[i])
+
+        # get min and max of y-axis for a certain dn up scaling algo
+        for qty, x in zip(QualityList, range(len(QualityList))):
+            qs = [row[x] for row in qualities]
+            y_mins[x].append(min(qs))
+            y_maxs[x].append(max(qs))
+
+    for qty, x in zip(QualityList, range(len(QualityList))):
+        ymin = min(y_mins[x])
+        ymax = max(y_maxs[x])
+        margin = 0.1  # add 10% on min and max value for y_axis range
+        num_precsn = 5 if 'MS-SSIM' in qty else 3
+        UpdateChart(charts[x], ymin, ymax, margin, qty, num_precsn)
+
+    startrow = rows_all[-1] + 2; startcol = 1
+    InsertChartsToSheet(sht, startrow, startcol, charts)
+    logger.info("finish average sheet for ratio:%2.2f." % ratio)
+
+    return sht
+
+def GenerateAverageSheet(wb, sumsht, dnScalAlgos, upScalAlgos, ratio,
+                         contentsdict, rows_class):
+    logger.info("start generate average sheet for ratio %2.2f" % ratio)
+
+    rdsht = sumsht
+    rdshtname = rdsht.get_name()
+    shtname = "AverageForRatio=%1.2f" % ratio
+    sht = wb.add_worksheet(shtname)
+    sht.write(1, 0, 'QualityMetrics')
+    sht.write(1, 1, 'Content Class')
+    sht.write(1, 2, 'Content Number')
+
+    interval = 1
+    step = len(StatsMetrics) + interval
+    startcol = 3
+    cols_avg = [startcol + step * i for i in range(len(dnScalAlgos))]
+    for col, dn_algo, up_algo in zip(cols_avg, dnScalAlgos, upScalAlgos):
+        algos = dn_algo + '--' + up_algo
+        sht.write(0, col, algos)
+        sht.write_row(1, col, StatsMetrics)
+
+    step = len(contentsdict) + 1  # 1 extra row for total of each class
+    startrow = 2
+    rows_qtymtr = [startrow + step * i for i in range(len(QualityList))]
+    for qty, row_qm, y in zip(QualityList, rows_qtymtr, range(len(QualityList))):
+        sht.write(row_qm, 0, qty)
+
+        #charts = []
+        #titlename = 'Quality Statistics %s' % qty
+        #chart = CreateChart_Line(wb, titlename, qty)
+        #charts.append(chart)
+
+        totalnum_contents = 0
+        for (cls, contents), idx, rdrow_cls in zip(contentsdict.items(),
+                                                   range(len(contentsdict)),
+                                                   rows_class):
+            sht.write(row_qm + idx, 1, cls)
+            num_content = len(contents)
+            totalnum_contents = totalnum_contents + num_content
+            sht.write(row_qm + idx, 2, num_content)
+            for rdcol, wtcol in zip(ScalSumQty_WtCols, cols_avg):
+                startcell = xlrd.cellname(rdrow_cls, rdcol + y)
+                endcell = xlrd.cellname(rdrow_cls + num_content - 1, rdcol + y)
+                formula = '=MAX(\'%s\'!%s:%s)' % (rdshtname, startcell, endcell)
+                sht.write(row_qm + idx, wtcol, formula)
+
+                formula = '=SUM(\'%s\'!%s:%s)/%d' % (rdshtname, startcell,
+                                                     endcell, num_content)
+                sht.write(row_qm + idx, wtcol + 2, formula)
+
+                formula = '=MIN(\'%s\'!%s:%s)' % (rdshtname, startcell, endcell)
+                sht.write(row_qm + idx, wtcol + 4, formula)
+
+                avgcell = xlrd.cellname(row_qm + idx, wtcol + 2)
+                formula = '= %s + _xlfn.STDEV.P(\'%s\'!%s:%s)'\
+                          % (avgcell, rdshtname, startcell, endcell)
+                sht.write(row_qm + idx, wtcol + 1, formula)
+
+                formula = '= %s - _xlfn.STDEV.P(\'%s\'!%s:%s)'\
+                          % (avgcell, rdshtname, startcell, endcell)
+                sht.write(row_qm + idx, wtcol + 3, formula)
+
+        #write total contents statistics
+        wtrow = row_qm + len(contentsdict)
+        sht.write(wtrow, 1, 'Total')
+        sht.write(wtrow, 2, totalnum_contents)
+        for rdcol, wtcol in zip(ScalSumQty_WtCols, cols_avg):
+            startcell = xlrd.cellname(ScalQty_startRow, rdcol + y)
+            endcell = xlrd.cellname(ScalQty_startRow + totalnum_contents - 1,
+                                    rdcol + y)
+            formula = '=MAX(\'%s\'!%s:%s)' % (rdshtname, startcell, endcell)
+            sht.write(wtrow, wtcol, formula)
+
+            formula = '=SUM(\'%s\'!%s:%s)/%d'\
+                      % (rdshtname, startcell, endcell, totalnum_contents)
+            sht.write(wtrow, wtcol + 2, formula)
+
+            formula = '=MIN(\'%s\'!%s:%s)' % (rdshtname, startcell, endcell)
+            sht.write(wtrow, wtcol + 4, formula)
+
+            avgcell = xlrd.cellname(wtrow, wtcol + 2)
+            formula = '= %s + _xlfn.STDEV.P(\'%s\'!%s:%s)'\
+                      % (avgcell, rdshtname, startcell, endcell)
+            sht.write(wtrow, wtcol + 1, formula)
+
+            formula = '= %s - _xlfn.STDEV.P(\'%s\'!%s:%s)'\
+                      % (avgcell, rdshtname, startcell, endcell)
+            sht.write(wtrow, wtcol + 3, formula)
+
+    logger.info("finish average sheet for ratio:%2.2f." % ratio)
+
+
+def SaveScalingResultsToExcel(dnScalAlgos, upScalAlgos, path_log):
+    logger.info("start saving scaling quality results to excel files.......")
+    if not os.path.exists(Path_ScalingResults):
+        os.makedirs(Path_ScalingResults)
+
+    logger.info("start generating per content scaling quality excel file.......")
+    contents = GetContents(ContentPath, Clips)
+    for content in contents:
+        GeneratePerContentExcelFile(dnScalAlgos, upScalAlgos, content,
+                                    DnScaleRatio, path_log)
+
+    scaleRatios = DnScaleRatio
+    scaleRatios.remove(1.0)
+    logger.info("start generating scaling quality summary excel file.......")
+    sumexcFile = GetScalingResultExcelFile(len(scaleRatios), len(DnScaleRatio))
+    wb = xlsxwriter.Workbook(sumexcFile)
+    # to generate rows number of starting of each class: rows_class
+    contentsdict, rows_class = CalcRowsClassAndContentDict(ScalQty_startRow,
+                                                           ContentPath, Clips)
+
+    sumShts = []
+    for ratio in scaleRatios:
+        sht = GenerateSummarySheet(wb, dnScalAlgos, upScalAlgos, ratio,
+                                   contentsdict, rows_class, path_log)
+        sumShts.append(sht)
+    for ratio, sumsht in zip(scaleRatios, sumShts):
+        GenerateAverageSheet(wb, sumsht, dnScalAlgos, upScalAlgos, ratio,
+                             contentsdict, rows_class)
+
+    wb.close()
+    logger.info("finish saving scaling quality results to excel files.......")
diff --git a/tools/convexhull_framework/src/Utils.py b/tools/convexhull_framework/src/Utils.py
new file mode 100644
index 0000000..632b1a9
--- /dev/null
+++ b/tools/convexhull_framework/src/Utils.py
@@ -0,0 +1,266 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+import re
+import xlrd
+import subprocess
+import time
+import logging
+from Config import LogLevels, Have_Class_Subfolder
+
+def Cleanfolder(folder):
+    if os.path.isdir(folder):
+        for f in os.listdir(folder):
+            file = os.path.join(folder, f)
+            if os.path.isfile(file):
+                os.remove(file)
+
+def CreateNewSubfolder(parent, name):
+    if name == '' or name == None:
+        return None
+    folder = os.path.join(parent, name)
+    if not os.path.exists(folder):
+        os.makedirs(folder)
+    return folder
+
+def GetShortContentName(content, isshort=True):
+    basename = os.path.splitext(os.path.basename(content))[0]
+    if isshort:
+        item = re.findall(r"([a-zA-Z0-9]+)_", basename)
+        if len(item) == 0:
+            name = basename
+        else:
+            name = item[0]
+    else:
+        name = basename
+    return name
+
+def GetContents(contentpath, clips):
+    contents = []
+    for key, val in clips.items():
+        folder = contentpath
+        if Have_Class_Subfolder:
+            cls = val[0]
+            folder = os.path.join(contentpath, cls)
+
+        file = os.path.join(folder, key) + ".yuv"
+        if os.path.isfile(file):
+            contents.append(file)
+
+    return contents
+
+def GetVideoInfo(content, Clips):
+    basename = GetShortContentName(content, False)
+    cls = Clips[basename][0]
+    width = Clips[basename][1]
+    height = Clips[basename][2]
+    fr = Clips[basename][3]
+    bitdepth = Clips[basename][4]
+    fmt = Clips[basename][5]
+
+    #default for 8 bit 420
+    RatioForFrameSize = 3/2
+    if fmt == 'yuv422p':
+        RatioForFrameSize = 2
+    elif fmt == 'yuv444p':
+        RatioForFrameSize = 3
+    if bitdepth > 8:
+        RatioForFrameSize *= 2
+
+    totalnum = os.path.getsize(content) / (width * height * RatioForFrameSize)
+
+    return cls, width, height, fr, bitdepth, fmt, totalnum
+
+def GetContentDict(contentpath, clips):
+    dict = {}
+
+    if Have_Class_Subfolder:
+        for key, val in clips.items():
+            cls = val[0]
+            folder = os.path.join(contentpath, cls)
+            file = os.path.join(folder, key) + ".yuv"
+            if os.path.isfile(file):
+                if cls in dict:
+                    if file not in dict[cls]:
+                        dict[cls].append(file)
+                else:
+                    dict[cls] = [file]
+    else:
+        # this is for case no subfolder/class. As * is forbidden for folder name,
+        # no folder will be same as this
+        cls = "*All"
+        dict[cls] = GetContents(contentpath, clips)
+
+    return dict
+
+def CalcRowsClassAndContentDict(rowstart, content_path, clips, times=1):
+    contentsdict = GetContentDict(content_path, clips)
+
+    ofstc = rowstart
+    rows_class = []
+    for cls, contents in contentsdict.items():
+        rows_class.append(ofstc)
+        ofstc = ofstc + len(contents) * times
+
+    return contentsdict, rows_class
+
+def SweepScalingAlgosInOneResultFile(infile_path):
+    dnscls = []
+    upscls = []
+    resultfiles = os.listdir(infile_path)
+    # here assume all result files includes same combinations of dn and up
+    # scaling algos
+    file = os.path.join(infile_path, resultfiles[0])
+    if os.path.isfile(file):
+        rdwb = xlrd.open_workbook(os.path.join(infile_path, resultfiles[0]))
+    else:
+        return dnscls, upscls
+    if rdwb is not None:
+        shtnms = rdwb.sheet_names()
+        for shtname in shtnms:
+            item = re.findall(r"(.+)\-\-(.+)", shtname)
+            dnsl = item[0][0]
+            upsl = item[0][1]
+            dnscls.append(dnsl)
+            upscls.append(upsl)
+
+    return dnscls, upscls
+
+def CreateChart_Scatter(wb, title, xaxis_name, yaxis_name):
+    chart = wb.add_chart({'type': 'scatter', 'subtype': 'straight_with_markers'})
+    chart.set_title({'name': title, 'name_font': {'color': 'white'}})
+    chart.set_x_axis({'name': xaxis_name,
+                      'major_gridlines': {'visible': True, 'line': {'width': 0.25}},
+                      'name_font': {'color': 'white'},
+                      'num_font': {'color': 'white', 'transparency': 80},
+                      'label_position' : 'low'
+                      })
+    chart.set_y_axis({'name': yaxis_name, 'name_font': {'color': 'white'},
+                      'num_font': {'color': 'white'}})
+    chart.set_style(12)
+    chart.set_size({'x_scale': 1.5, 'y_scale': 1.5})
+    chart.set_chartarea({"fill": {'color': '#505050'}})
+    chart.set_plotarea({"fill": {'color': '#505050'}})
+    chart.set_legend({'position': 'bottom', 'font': {'color': 'white'}})
+    return chart
+
+def CreateChart_Line(wb, titlename, yaxis_name):
+    chart = wb.add_chart({'type': 'line', 'name_font': {'size': 10.5}})
+    chart.set_title({'name': titlename})
+    chart.set_x_axis({'text_axis': True})
+    chart.set_y_axis({'name': yaxis_name, 'name_font': {'size': 11}})
+    chart.set_size({'x_scale': 1.4, 'y_scale': 1.5})
+    chart.set_legend({'position': 'right', 'font': {'size': 10.5}})
+    chart.set_high_low_lines(
+        {'line': {'color': 'black', 'size': 2}}
+    )
+    return chart
+
+def AddSeriesToChart_Scatter(shtname, rows, coly, colx, chart, seriname,
+                             linecolor):
+    yvalues = [shtname, rows[0], coly, rows[-1], coly]
+    xvalues = [shtname, rows[0], colx, rows[-1], colx]
+
+    chart.add_series({
+        'name': seriname,
+        'categories': xvalues,
+        'values': yvalues,
+        'line': {'color': linecolor, 'width': 1.5},
+        'marker': {'type': 'circle', 'size': 5,
+                   'border': {'color': linecolor, 'size': 0.75},
+                   'fill': {'color': linecolor}},
+    })
+
+def AddSeriesToChart_Scatter_Rows(shtname, cols, rowy, rowx, chart, seriname,
+                                  linecolor):
+    yvalues = [shtname, rowy, cols[0], rowy, cols[-1]]
+    xvalues = [shtname, rowx, cols[0], rowx, cols[-1]]
+
+    chart.add_series({
+        'name': seriname,
+        'categories': xvalues,
+        'values': yvalues,
+        'line': {'color': linecolor, 'width': 1.0, 'dash_type': 'dash_dot'},
+        'marker': {'type': 'square', 'size': 5,
+                   'border': {'color': 'white', 'size': 0.75}}
+    })
+
+def AddSeriesToChart_Line(shtname, rows, coly, colx, chart, seriname, shape,
+                          ssize, linecolor):
+    yvalues = [shtname, rows[0], coly, rows[-1], coly]
+    xvalues = [shtname, rows[0], colx, rows[-1], colx]
+    chart.add_series({
+        'name': seriname,
+        'categories': xvalues,
+        'values': yvalues,
+        'line': {'none': True},
+        'marker': {'type': shape,
+                   'size': ssize,
+                   'border': {'color': linecolor, 'size': 2},
+                   'fill': {'color': linecolor}},
+    })
+
+def UpdateChart(chart, ymin, ymax, margin, yaxis_name, precsn):
+    interval = ymax - ymin
+    finalmax = ymax + interval * margin
+    finalmin = ymin - interval * margin
+    floatprecn = "{:.%df}" % precsn
+    finalmin = float(floatprecn.format(finalmin))
+    finalmax = float(floatprecn.format(finalmax))
+    chart.set_y_axis({'name': yaxis_name,
+                      'name_font': {'color': 'white'},
+                      'num_font': {'color': 'white'},
+                      'min': finalmin, 'max': finalmax})
+
+def InsertChartsToSheet(sht, startrow, startcol, charts):
+    height = 22
+    width = 12
+    num = len(charts)
+    row = startrow
+    for i in range(1, num, 2):
+        sht.insert_chart(row, startcol, charts[i - 1])
+        sht.insert_chart(row, startcol + width, charts[i])
+        row = row + height
+
+def ExecuteCmd(cmd, LogCmdOnly):
+    CmdLogger.write(cmd + "\n")
+    ret = 0
+    if not LogCmdOnly:
+        ret = subprocess.call(cmd, shell=True)
+    return ret
+
+def SetupLogging(level, logcmdonly, name, path):
+    global Logger
+    Logger = logging.getLogger(name)
+
+    if logcmdonly or level != 0:
+        global CmdLogger
+        logfilename = os.path.join(path, 'ConvexHullTestCmd_%s.log'
+                                   % time.strftime("%Y%m%d-%H%M%S"))
+        CmdLogger = open(logfilename, 'w')
+
+    if level != 0:
+        logfilename = os.path.join(path, 'ConvexHullTest_%s.log'
+                                   % time.strftime("%Y%m%d-%H%M%S"))
+        hdlr = logging.FileHandler(logfilename)
+        formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
+        hdlr.setFormatter(formatter)
+        Logger.addHandler(hdlr)
+        if level in range(len(LogLevels)):
+            # valid level input parameter
+            lvl = LogLevels[level]
+            levelname = logging.getLevelName(lvl)
+        else:
+            # if not valid, default set to 'INFO'
+            levelname = logging.getLevelName('INFO')
+        Logger.setLevel(levelname)
diff --git a/tools/convexhull_framework/src/VideoDecoder.py b/tools/convexhull_framework/src/VideoDecoder.py
new file mode 100644
index 0000000..84146bd
--- /dev/null
+++ b/tools/convexhull_framework/src/VideoDecoder.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import Utils
+from Config import BinPath, LogCmdOnly, FFMPEG, AOMDEC
+from Utils import ExecuteCmd
+
+#use ffmpeg to decode bitstream
+def DecodeWithFfmpeg(infile, outfile):
+    args = " -y -i %s -pix_fmt yuv420p -c:v rawvideo %s" % (infile, outfile)
+    cmd = FFMPEG + args
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def DecodeWithAOM(infile, outfile):
+    args = " --codec=av1 --i420 --rawvideo --summary -o %s %s" % (outfile, infile)
+    cmd = AOMDEC + args
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def VideoDecode(codec, infile, outfile):
+    Utils.CmdLogger.write("::Decode\n")
+    if codec == 'AV1':
+        DecodeWithAOM(infile, outfile)
+    else:
+        DecodeWithFfmpeg(infile, outfile)
diff --git a/tools/convexhull_framework/src/VideoEncoder.py b/tools/convexhull_framework/src/VideoEncoder.py
new file mode 100644
index 0000000..8bbf32c
--- /dev/null
+++ b/tools/convexhull_framework/src/VideoEncoder.py
@@ -0,0 +1,80 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import Utils
+from Config import BinPath, LogCmdOnly, FFMPEG, AOMENC, SVTAV1, SVTHEVC
+from Utils import ExecuteCmd
+
+#use ffmpeg to encode video
+def EncodeWithFfmpeg_HEVC(infile, QP, num,fr, width, height, outfile, preset):
+    EncodeProfile = 'main'
+    args = " -y -s %dx%d -pix_fmt yuv420p  -r %s -i %s -frames:v %d -g %d -bf 7" \
+           " -bsf:v hevc_mp4toannexb -c:v libx265 -preset %s -profile:v %s " \
+           "-x265-params \"qp=%d:aq-mode=0:b-adapt=0:bframes=7:b-pyramid=1:" \
+           "no-scenecut=1:no-open-gop=1:input-depth=8:output-depth=8\" %s" % (
+            width, height, fr, infile, num, int(2 * fr),  # gop size = 2 seconds
+            preset, EncodeProfile, (QP+3), outfile)
+    cmd = FFMPEG + args
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def EncodeWithAOM_AV1(infile, QP, framenum, framerate, width, height, outfile,
+                      preset):
+    args = " --codec=av1 -v --psnr --ivf  --frame-parallel=0 --cpu-used=%s" \
+           " --limit=%d --auto-alt-ref=1 --passes=2  " \
+           "--threads=1 --lag-in-frames=25 --end-usage=q --cq-level=%d" \
+           " -w %d -h %d -o %s %s"\
+           % (preset, framenum, QP, width, height, outfile, infile)
+    cmd = AOMENC + args
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def EncodeWithSVT_AV1(infile, QP, framenum, framerate, width, height, outfile,
+                      preset):
+    args = " --preset %s --scm 2 --lookahead 0 --hierarchical-levels 3 -n %d" \
+           " --keyint 255 -rc 0 -q %d -w %d -h %d -b %s -i %s"\
+           % (str(preset), framenum, QP, width, height, outfile, infile)
+    cmd = SVTAV1 + args
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def EncodeWithSVT_HEVC(infile, QP, framenum, framerate, width, height, outfile,
+                       preset):
+    args = " -i %s  -w %d -h %d -encMode %s -hierarchical-levels 3" \
+           " -intra-period 100 -scd 0 -rc 0 -q %d -n %d -b %s"\
+           % (infile, width, height, preset, QP, framenum, outfile)
+    cmd = SVTHEVC + args
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def VideoEncode(EncodeMethod, CodecName, infile, QP, framenum, framerate, width,
+                height, outfile, preset):
+    Utils.CmdLogger.write("::Encode\n")
+    if EncodeMethod == "ffmpeg":
+        if CodecName == 'hevc':
+            EncodeWithFfmpeg_HEVC(infile, QP, framenum, framerate, width, height,
+                                  outfile, preset)
+        else:
+            raise ValueError("invalid parameter for encode.")
+    elif EncodeMethod == "aom":
+        if CodecName == 'av1':
+            EncodeWithAOM_AV1(infile, QP, framenum, framerate, width, height,
+                              outfile, preset)
+        else:
+            raise ValueError("invalid parameter for encode.")
+    elif EncodeMethod == "svt":
+        if CodecName == 'av1':
+            EncodeWithSVT_AV1(infile, QP, framenum, framerate, width, height,
+                              outfile, preset)
+        elif CodecName == 'hevc':
+            EncodeWithSVT_HEVC(infile, QP, framenum, framerate, width, height,
+                               outfile, preset)
+        else:
+            raise ValueError("invalid parameter for encode.")
+    else:
+        raise ValueError("invalid parameter for encode.")
diff --git a/tools/convexhull_framework/src/VideoScaler.py b/tools/convexhull_framework/src/VideoScaler.py
new file mode 100644
index 0000000..2420d1b
--- /dev/null
+++ b/tools/convexhull_framework/src/VideoScaler.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+## Copyright (c) 2019, Alliance for Open Media. All rights reserved
+##
+## This source code is subject to the terms of the BSD 2 Clause License and
+## the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+## was not distributed with this source code in the LICENSE file, you can
+## obtain it at www.aomedia.org/license/software. If the Alliance for Open
+## Media Patent License 1.0 was not distributed with this source code in the
+## PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+##
+__author__ = "maggie.sun@intel.com, ryan.lei@intel.com"
+
+import os
+import Utils
+import logging
+from Config import BinPath, LogCmdOnly, LoggerName, FFMPEG
+from Utils import GetShortContentName, ExecuteCmd
+
+subloggername = "VideoScaler"
+loggername = LoggerName + '.' + '%s' % subloggername
+logger = logging.getLogger(loggername)
+
+def ValidAlgo_ffmpeg(algo):
+    if (algo == 'bicubic' or algo == 'lanczos' or algo == 'sinc' or
+        algo == 'bilinear' or algo == 'spline' or algo == 'gauss' or
+        algo == 'bicublin' or algo == 'neighbor'):
+        return True
+    else:
+        return False
+
+#use ffmpeg to do image rescaling for yuv420 8bit
+def RescaleWithFfmpeg(infile, inw, inh, outw, outh, algo, outfile, num, app_path):
+    args = " -y -s:v %dx%d -i %s -vf scale=%dx%d -c:v rawvideo -pix_fmt yuv420p" \
+           " -sws_flags %s+accurate_rnd+full_chroma_int -sws_dither none" \
+           % (inw, inh, infile, outw, outh, algo)
+    if (algo == 'lanczos'):
+        args += " -param0 5 "
+    args += " -frames %d %s" % (num, outfile)
+    cmd = FFMPEG + args
+    ExecuteCmd(cmd, LogCmdOnly)
+
+def VideoRescaling(infile, num, inw, inh, outw, outh, outfile, algo):
+    if ValidAlgo_ffmpeg(algo):
+        RescaleWithFfmpeg(infile, inw, inh, outw, outh, algo, outfile, num, BinPath)
+    # add other tools for scaling here later
+    else:
+        logger.error("unsupported scaling algorithm.")
+
+####################################################################################
+##################### Major Functions ################################################
+def GetDownScaledOutFile(input, inw, inh, dnw, dnh, path, algo):
+    contentBaseName = GetShortContentName(input)
+    actual_algo = 'None' if inw == dnw and inh == dnh else algo
+    filename = contentBaseName + ('_DnScaled_%s_%dx%d.yuv' % (actual_algo, dnw,
+                                                              dnh))
+    dnscaledout = os.path.join(path, filename)
+    return dnscaledout
+
+def GetUpScaledOutFile(infile, inw, inh, outw, outh, path, algo):
+    actual_algo = 'None' if inw == outw and inh == outh else algo
+    filename = GetShortContentName(infile, False) + ('_UpScaled_%s_%dx%d.yuv'
+                                                     % (actual_algo, outw, outh))
+    upscaledout = os.path.join(path, filename)
+    return upscaledout
+
+def DownScaling(input, num, inw, inh, outw, outh, path, algo):
+    dnScalOut = GetDownScaledOutFile(input, inw, inh, outw, outh, path, algo)
+
+    Utils.CmdLogger.write("::Downscaling\n")
+    if (inw == outw and inh == outh):
+        cmd = "copy %s %s" % (input, dnScalOut)
+        ExecuteCmd(cmd, LogCmdOnly)
+    else:
+        # call separate process to do the downscaling
+        VideoRescaling(input, num, inw, inh, outw, outh, dnScalOut, algo)
+    return dnScalOut
+
+def UpScaling(infile, num, inw, inh, outw, outh, path, algo):
+    upScaleOut = GetUpScaledOutFile(infile, inw, inh, outw, outh, path, algo)
+
+    Utils.CmdLogger.write("::Upscaling\n")
+    if (inw == outw and inh == outh):
+        cmd = "copy %s %s" % (infile, upScaleOut)
+        ExecuteCmd(cmd, LogCmdOnly)
+    else:
+        # call separate process to do the upscaling
+        VideoRescaling(infile, num, inw, inh, outw, outh, upScaleOut, algo)
+    return upScaleOut