// Copyright (C) 2018 The Regents of the University of California (Regents).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//
//     * Neither the name of The Regents or University of California nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Please contact the author of this library if you have any questions.
// Author: Victor Fragoso (victor.fragoso@mail.wvu.edu)

#include <Eigen/Core>
#include <Eigen/Geometry>
#include <glog/logging.h>

#include <algorithm>
#include <vector>

#include "gtest/gtest.h"

#include "theia/math/util.h"
#include "theia/sfm/camera/camera.h"
#include "theia/sfm/estimators/camera_and_feature_correspondence_2d_3d.h"
#include "theia/sfm/estimators/estimate_rigid_transformation_2d_3d.h"
#include "theia/sfm/estimators/feature_correspondence_2d_3d.h"
#include "theia/solvers/sample_consensus_estimator.h"
#include "theia/sfm/pose/test_util.h"
#include "theia/sfm/pose/util.h"
#include "theia/sfm/rigid_transformation.h"
#include "theia/test/test_utils.h"
#include "theia/util/random.h"
#include "theia/util/timer.h"

namespace theia {
namespace {
using Eigen::AngleAxisd;
using Eigen::Matrix3d;
using Eigen::Vector2d;
using Eigen::Vector3d;

static const int kNumCameras = 10;
static const int kNumPoints = 100;
static const double kFocalLength = 1000.0;
static const double kReprojectionError = 6.0;
static const double kAngularErrorThresh = 5.0;  // In degrees.
static const int kSeed = 67;
static const int kNumTrials = 3;

inline Camera RandomCamera(RandomNumberGenerator* rng) {
  Camera camera;
  camera.SetPosition(rng->RandVector3d(-10.0, 10.0));
  camera.SetOrientationFromRotationMatrix(RandomRotation(10.0, rng));
  camera.SetImageSize(1000, 1000);
  camera.SetFocalLength(kFocalLength);
  camera.SetPrincipalPoint(kFocalLength / 2.0, kFocalLength / 2.0);
  return camera;
}

void ExecuteRandomCentralCameraTest(const RansacParameters& options,
                                    const RigidTransformation& rigid_transformation,
                                    const double inlier_ratio,
                                    const double noise,
                                    const double tolerance,
                                    RandomNumberGenerator* rng) {
  // Create feature correspondences (inliers and outliers) and add noise if
  // appropriate.
  std::vector<FeatureCorrespondence2D3D> correspondences;
  for (int i = 0; i < kNumPoints; i++) {
    FeatureCorrespondence2D3D correspondence;
    correspondence.world_point = Vector3d(rng->RandDouble(-2.0, 2.0),
                                          rng->RandDouble(-2.0, 2.0),
                                          rng->RandDouble(6.0, 10.0));

    // Add an inlier or outlier.
    if (i < inlier_ratio * kNumPoints) {
      // Make sure the point is in front of the camera.
      correspondence.feature =
          (rigid_transformation.rotation * correspondence.world_point +
           rigid_transformation.translation).hnormalized();
    } else {
      correspondence.feature = rng->RandVector2d();
    }
    correspondences.emplace_back(correspondence);
  }

  // Add noise to the projections.
  if (noise) {
    for (int i = 0; i < kNumPoints; i++) {
      AddNoiseToProjection(noise / kFocalLength, rng,
                           &correspondences[i].feature);
    }
  }

  int num_trials_passed = 0;
  for (int i = 0; i < kNumTrials; ++i) {
    // Estimate the rigid transform.
    RigidTransformation estimated_rigid_transformation;
    RansacSummary ransac_summary;
    Timer timer;
    timer.Reset();
    EXPECT_TRUE(EstimateRigidTransformation2D3D(options,
                                                RansacType::RANSAC,
                                                correspondences,
                                                &estimated_rigid_transformation,
                                                &ransac_summary));
    const double elapsed_time = timer.ElapsedTimeInSeconds();

    VLOG(3) << "Ransac summary: \n Number of inliers: "
            << ransac_summary.inliers.size()
            << "\n Num. input data points: "
            << ransac_summary.num_input_data_points
            << "\n Num. iterations: "
            << ransac_summary.num_iterations
            << "\n Confidence: " << ransac_summary.confidence
            << "\n Time [sec]: " << elapsed_time;

    // Expect that the inlier ratio is close to the ground truth.
    VLOG_IF(3, static_cast<double>(ransac_summary.inliers.size()) < 3)
        << "Not enough inliers.";

    // Expect rigid transforms are close.
    const Eigen::Quaterniond gt_rotation(rigid_transformation.rotation);
    const Eigen::Quaterniond estimated_rotation(
        estimated_rigid_transformation.rotation);
    const double rotation_error =
        std::abs(RadToDeg(gt_rotation.angularDistance(estimated_rotation)));
    const bool good_rotation = rotation_error < kAngularErrorThresh;
    const double translation_error =
        (estimated_rigid_transformation.translation -
         rigid_transformation.translation).norm();
    const bool good_translation = translation_error < tolerance;
    const bool good_run = good_translation && good_rotation;
    VLOG_IF(3, !good_run) << ">>> Trial: " << i
                          << "\nExpected rotation: \n"
                          << rigid_transformation.rotation
                          << "\n Estimated rotation: \n"
                          << estimated_rigid_transformation.rotation
                          << "\n Rotation error [deg]: " << rotation_error
                          << "\n Expected translation: "
                          << rigid_transformation.translation.transpose()
                          << "\n Estimated translation: "
                          << estimated_rigid_transformation.translation.transpose()
                          << "\n Translation error: " << translation_error;

    if (good_run) {
      ++num_trials_passed;
    }
  }
  const float success_ratio =
      num_trials_passed / static_cast<float>(kNumTrials);
  VLOG(3) << ">>> Success ratio: " << success_ratio
          << " good trials: " << num_trials_passed
          << " num. trials:  " << kNumTrials;
  EXPECT_TRUE(success_ratio > 0.60f);
}

void ExecuteRandomTest(const RansacParameters& options,
                       const RigidTransformation& rigid_transformation,
                       const double inlier_ratio,
                       const double noise,
                       const double tolerance,
                       const int num_cameras,
                       RandomNumberGenerator* rng) {
  // Generate cameras.
  std::vector<Camera> cameras(num_cameras);
  for (int i = 0; i < cameras.size(); ++i) {
    cameras[i] = RandomCamera(rng);
  }

  // Create feature correspondences (inliers and outliers) and add noise if
  // appropriate.
  std::vector<CameraAndFeatureCorrespondence2D3D> correspondences;
  correspondences.reserve(kNumPoints);
  for (int i = 0; i < kNumPoints; i++) {
    CameraAndFeatureCorrespondence2D3D correspondence;

    // Set up random camera.
    correspondence.camera = cameras[i % cameras.size()];

    // Set up random 3D point and reproject it into the image. Make sure the
    // point is in front of the camera.
    double depth = -1;
    do {
      // Create a 3D point randomly, and try to ensure that it is in front of
      // the camera.
      correspondence.point3d = Eigen::Vector4d(rng->RandDouble(-5, 5),
                                               rng->RandDouble(-5, 5),
                                               rng->RandDouble(10, 20),
                                               1.0);

      depth = correspondence.camera.ProjectPoint(correspondence.point3d,
                                                 &correspondence.observation);
    } while (depth < 0);
    correspondences.emplace_back(std::move(correspondence));
  }

  // Add noise to the image observations.
  if (noise) {
    for (int i = 0; i < kNumPoints; ++i) {
      correspondences[i].observation += noise * rng->RandVector2d();
    }
  }

  // Add outliers.
  for (int i = 0; i < kNumPoints; ++i) {
    if (i > inlier_ratio * kNumPoints) {
      correspondences[i].observation = kFocalLength * rng->RandVector2d();
    }
  }

  // Apply the rigid transformation to the 3d points.
  for (int i = 0; i < kNumPoints; ++i) {
    const Eigen::Vector3d old_point = correspondences[i].point3d.hnormalized();
    // Since the 3d point is in front of the camera, we need to calculate the
    // final 3d point to estimate the rigid transformation.
    const Eigen::Vector3d new_point =
        rigid_transformation.rotation.transpose() *
        (old_point - rigid_transformation.translation);
    correspondences[i].point3d = new_point.homogeneous();
  }

  int num_trials_passed = 0;
  for (int i = 0; i < kNumTrials; ++i) {
    // Estimate the rigid transform.
    RigidTransformation estimated_rigid_transformation;
    RansacSummary ransac_summary;
    Timer timer;
    timer.Reset();
    EXPECT_TRUE(EstimateRigidTransformation2D3D(options,
                                                RansacType::RANSAC,
                                                correspondences,
                                                &estimated_rigid_transformation,
                                                &ransac_summary));
    const double elapsed_time = timer.ElapsedTimeInSeconds();

    VLOG(3) << "Ransac summary: \n Number of inliers: "
            << ransac_summary.inliers.size()
            << "\n Num. input data points: "
            << ransac_summary.num_input_data_points
            << "\n Num. iterations: "
            << ransac_summary.num_iterations
            << "\n Confidence: " << ransac_summary.confidence
            << "\n Time [sec]: " << elapsed_time;

    // Expect that the inlier ratio is close to the ground truth.
    VLOG_IF(3, static_cast<double>(ransac_summary.inliers.size()) < 3)
        << "Not enough inliers.";

    // Expect rigid transforms are close.
    const Eigen::Quaterniond gt_rotation(rigid_transformation.rotation);
    const Eigen::Quaterniond estimated_rotation(
        estimated_rigid_transformation.rotation);
    const double rotation_error =
        std::abs(RadToDeg(gt_rotation.angularDistance(estimated_rotation)));
    const bool good_rotation = rotation_error < kAngularErrorThresh;
    const double translation_error =
        (estimated_rigid_transformation.translation -
         rigid_transformation.translation).norm();
    const bool good_translation = translation_error < tolerance;
    const bool good_run = good_translation && good_rotation;
    VLOG_IF(3, !good_run) << ">>> Trial: " << i
                          << "\nExpected rotation: \n"
                          << rigid_transformation.rotation
                          << "\n Estimated rotation: \n"
                          << estimated_rigid_transformation.rotation
                          << "\n Rotation error [deg]: " << rotation_error
                          << "\n Expected translation: "
                          << rigid_transformation.translation.transpose()
                          << "\n Estimated translation: "
                          << estimated_rigid_transformation.translation.transpose()
                          << "\n Translation error: " << translation_error;

    if (good_run) {
      ++num_trials_passed;
    }
  }
  const float success_ratio =
      num_trials_passed / static_cast<float>(kNumTrials);
  VLOG(3) << ">>> Success ratio: " << success_ratio
          << " good trials: " << num_trials_passed
          << " num. trials:  " << kNumTrials;
  EXPECT_TRUE(success_ratio > 0.60f);
}

}  // namespace

class EstimateRigidTransformation : public ::testing::Test {
 public:
  static void SetUpTestCase() {
    CHECK_GT(kNumPoints, kNumCameras);
    rng = new RandomNumberGenerator(kSeed);
  }

  static void TearDownTestCase() {
    delete rng;
  }

  // TODO(vfragoso): Generate the cameras and points once.
  static RandomNumberGenerator* rng;
};

RandomNumberGenerator* EstimateRigidTransformation::rng = nullptr;

TEST_F(EstimateRigidTransformation, AllInliersNoNoiseNonCentralCamera) {
  RansacParameters options;
  options.rng = std::make_shared<RandomNumberGenerator>(kSeed);
  options.use_mle = true;
  options.error_thresh = kReprojectionError;
  options.failure_probability = 0.001;
  options.max_iterations = 300;
  const double kInlierRatio = 1.0;
  const double kNoise = 0.0;
  const double kPoseTolerance = 1e-4;

  RigidTransformation rigid_transformation;
  rigid_transformation.rotation =
      Eigen::AngleAxisd(DegToRad(12.0), Eigen::Vector3d(1.0, 0.2, -0.8)
                        .normalized()).toRotationMatrix();
  rigid_transformation.translation = Eigen::Vector3d(-1.3, 2.1, 0.5);
  ExecuteRandomTest(options,
                    rigid_transformation,
                    kInlierRatio,
                    kNoise,
                    kPoseTolerance,
                    kNumCameras,
                    rng);
}

TEST_F(EstimateRigidTransformation, AllInliersWithNoiseNonCentralCamera) {
  RansacParameters options;
  options.rng = std::make_shared<RandomNumberGenerator>(kSeed);
  options.use_mle = true;
  options.error_thresh = kReprojectionError;
  options.failure_probability = 0.001;
  options.max_iterations = 300;
  const double kInlierRatio = 1.0;
  const double kNoise = 1.0;
  const double kPoseTolerance = 1e-2;

  RigidTransformation rigid_transformation;
  rigid_transformation.rotation =
      Eigen::AngleAxisd(DegToRad(12.0), Eigen::Vector3d(1.0, 0.2, -0.8)
                        .normalized()).toRotationMatrix();
  rigid_transformation.translation = Eigen::Vector3d(-1.3, 2.1, 0.5);
  ExecuteRandomTest(options,
                    rigid_transformation,
                    kInlierRatio,
                    kNoise,
                    kPoseTolerance,
                    kNumCameras,
                    rng);
}

TEST_F(EstimateRigidTransformation, OutliersWithNoiseNonCentralCamera) {
  RansacParameters options;
  options.rng = std::make_shared<RandomNumberGenerator>(kSeed);
  options.use_mle = true;
  options.error_thresh = kReprojectionError;
  options.failure_probability = 0.001;
  options.max_iterations = 2000;
  const double kInlierRatio = 0.8;
  const double kNoise = 1.0;
  const double kPoseTolerance = 1.5;

  RigidTransformation rigid_transformation;
    rigid_transformation.rotation =
      Eigen::AngleAxisd(DegToRad(12.0), Eigen::Vector3d(1.0, 0.2, -0.8)
                        .normalized()).toRotationMatrix();
  rigid_transformation.translation = Eigen::Vector3d(-1.3, 2.1, 0.5);
  ExecuteRandomTest(options,
                    rigid_transformation,
                    kInlierRatio,
                    kNoise,
                    kPoseTolerance,
                    kNumCameras,
                    rng);
}

TEST_F(EstimateRigidTransformation, AllInliersNoNoiseCentralCamera) {
  RansacParameters options;
  options.rng = std::make_shared<RandomNumberGenerator>(kSeed);
  options.use_mle = true;
  options.error_thresh = kReprojectionError;
  options.failure_probability = 0.001;
  options.max_iterations = 300;
  const double kInlierRatio = 1.0;
  const double kNoise = 0.0;
  const double kPoseTolerance = 1e-4;

  RigidTransformation rigid_transformation;
  rigid_transformation.rotation =
      Eigen::AngleAxisd(DegToRad(12.0), Eigen::Vector3d(1.0, 0.2, -0.8)
                        .normalized()).toRotationMatrix();
  rigid_transformation.translation = Eigen::Vector3d(-1.3, 2.1, 0.5);
  ExecuteRandomCentralCameraTest(options,
                                 rigid_transformation,
                                 kInlierRatio,
                                 kNoise,
                                 kPoseTolerance,
                                 rng);
}

TEST_F(EstimateRigidTransformation, AllInliersWithNoiseCentralCamera) {
  RansacParameters options;
  options.rng = std::make_shared<RandomNumberGenerator>(kSeed);
  options.use_mle = true;
  options.error_thresh = kReprojectionError;
  options.failure_probability = 0.001;
  options.max_iterations = 300;
  const double kInlierRatio = 1.0;
  const double kNoise = 1.0;
  const double kPoseTolerance = 1e-2;

  RigidTransformation rigid_transformation;
  rigid_transformation.rotation =
      Eigen::AngleAxisd(DegToRad(12.0), Eigen::Vector3d(1.0, 0.2, -0.8)
                        .normalized()).toRotationMatrix();
  rigid_transformation.translation = Eigen::Vector3d(-1.3, 2.1, 0.5);
  ExecuteRandomCentralCameraTest(options,
                                 rigid_transformation,
                                 kInlierRatio,
                                 kNoise,
                                 kPoseTolerance,
                                 rng);
}

}  // namespace theia


/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include "aod_sketch_c_adapter.h"
#include "allocator.h"
#include "postgres_h_substitute.h"
#include "kll_float_sketch_c_adapter.h"

#include <array_of_doubles_sketch.hpp>
#include <array_of_doubles_union.hpp>
#include <array_of_doubles_intersection.hpp>
#include <array_of_doubles_a_not_b.hpp>

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/variance.hpp>
#include <boost/math/distributions/students_t.hpp>

using update_aod_sketch_pg = datasketches::update_array_of_doubles_sketch_alloc<palloc_allocator<double>>;
using compact_aod_sketch_pg = datasketches::compact_array_of_doubles_sketch_alloc<palloc_allocator<double>>;
using aod_union_pg = datasketches::array_of_doubles_union_alloc<palloc_allocator<double>>;
// using the union policy in the intersection since this is how it is done in Druid
using aod_intersection_pg = datasketches::array_of_doubles_intersection<datasketches::array_of_doubles_union_policy_alloc<palloc_allocator<double>>, palloc_allocator<double>>;
using aod_a_not_b_pg = datasketches::array_of_doubles_a_not_b_alloc<palloc_allocator<double>>;

std::ostream& operator<<(std::ostream& os, const datasketches::aod<palloc_allocator<double>>& v) {
  os << "(";
  for (size_t i = 0; i < v.size(); ++i) {
    if (i != 0) os << ", ";
    os << v[i];
  }
  os << ")";
  return os;
}

void* aod_sketch_new(unsigned num_values) {
  try {
    return new (palloc(sizeof(update_aod_sketch_pg))) update_aod_sketch_pg(update_aod_sketch_pg::builder(num_values).build());
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void* aod_sketch_new_lgk(unsigned num_values, unsigned lg_k) {
  try {
    return new (palloc(sizeof(update_aod_sketch_pg))) update_aod_sketch_pg(update_aod_sketch_pg::builder(num_values).set_lg_k(lg_k).build());
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void* aod_sketch_new_lgk_p(unsigned num_values, unsigned lg_k, float p) {
  try {
    return new (palloc(sizeof(update_aod_sketch_pg))) update_aod_sketch_pg(update_aod_sketch_pg::builder(num_values).set_lg_k(lg_k).set_p(p).build());
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void update_aod_sketch_delete(void* sketchptr) {
  try {
    static_cast<update_aod_sketch_pg*>(sketchptr)->~update_aod_sketch_pg();
    pfree(sketchptr);
  } catch (std::exception& e) {
    pg_error(e.what());
  }
}

void compact_aod_sketch_delete(void* sketchptr) {
  try {
    static_cast<compact_aod_sketch_pg*>(sketchptr)->~compact_aod_sketch_pg();
    pfree(sketchptr);
  } catch (std::exception& e) {
    pg_error(e.what());
  }
}

void aod_sketch_update(void* sketchptr, const void* data, unsigned length, const double* values) {
  try {
    static_cast<update_aod_sketch_pg*>(sketchptr)->update(data, length, values);
  } catch (std::exception& e) {
    pg_error(e.what());
  }
}

void* aod_sketch_compact(void* sketchptr) {
  try {
    auto newptr = new (palloc(sizeof(compact_aod_sketch_pg))) compact_aod_sketch_pg(static_cast<update_aod_sketch_pg*>(sketchptr)->compact());
    static_cast<update_aod_sketch_pg*>(sketchptr)->~update_aod_sketch_pg();
    pfree(sketchptr);
    return newptr;
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

double update_aod_sketch_get_estimate(const void* sketchptr) {
  try {
    return static_cast<const update_aod_sketch_pg*>(sketchptr)->get_estimate();
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

double compact_aod_sketch_get_estimate(const void* sketchptr) {
  try {
    return static_cast<const compact_aod_sketch_pg*>(sketchptr)->get_estimate();
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

Datum* aod_sketch_get_estimate_and_bounds(const void* sketchptr, unsigned num_std_devs) {
  try {
    Datum* est_and_bounds = (Datum*) palloc(sizeof(Datum) * 3);
    est_and_bounds[0] = pg_float8_get_datum(static_cast<const compact_aod_sketch_pg*>(sketchptr)->get_estimate());
    est_and_bounds[1] = pg_float8_get_datum(static_cast<const compact_aod_sketch_pg*>(sketchptr)->get_lower_bound(num_std_devs));
    est_and_bounds[2] = pg_float8_get_datum(static_cast<const compact_aod_sketch_pg*>(sketchptr)->get_upper_bound(num_std_devs));
    return est_and_bounds;
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

char* aod_sketch_to_string(const void* sketchptr, bool print_entries) {
  try {
    auto str = static_cast<const compact_aod_sketch_pg*>(sketchptr)->to_string(print_entries);
    const size_t len = str.length() + 1;
    char* buffer = (char*) palloc(len);
    strncpy(buffer, str.c_str(), len);
    return buffer;
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

ptr_with_size aod_sketch_serialize(const void* sketchptr, unsigned header_size) {
  try {
    ptr_with_size p;
    auto bytes = new (palloc(sizeof(compact_aod_sketch_pg::vector_bytes))) compact_aod_sketch_pg::vector_bytes(
      static_cast<const compact_aod_sketch_pg*>(sketchptr)->serialize(header_size)
    );
    p.ptr = bytes->data();
    p.size = bytes->size();
    return p;
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void* aod_sketch_deserialize(const char* buffer, unsigned length) {
  try {
    return new (palloc(sizeof(compact_aod_sketch_pg))) compact_aod_sketch_pg(compact_aod_sketch_pg::deserialize(buffer, length));
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void* aod_union_new(unsigned num_values) {
  try {
    return new (palloc(sizeof(aod_union_pg))) aod_union_pg(aod_union_pg::builder(num_values).build());
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void* aod_union_new_lgk(unsigned num_values, unsigned lg_k) {
  try {
    return new (palloc(sizeof(aod_union_pg))) aod_union_pg(aod_union_pg::builder(num_values).set_lg_k(lg_k).build());
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void aod_union_delete(void* unionptr) {
  try {
    static_cast<aod_union_pg*>(unionptr)->~aod_union_pg();
    pfree(unionptr);
  } catch (std::exception& e) {
    pg_error(e.what());
  }
}

void aod_union_update(void* unionptr, const void* sketchptr) {
  try {
    static_cast<aod_union_pg*>(unionptr)->update(std::move(*static_cast<const compact_aod_sketch_pg*>(sketchptr)));
  } catch (std::exception& e) {
    pg_error(e.what());
  }
}

void* aod_union_get_result(const void* unionptr) {
  try {
    return new (palloc(sizeof(compact_aod_sketch_pg))) compact_aod_sketch_pg(static_cast<const aod_union_pg*>(unionptr)->get_result());
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void* aod_intersection_new(unsigned num_values) {
  try {
    return new (palloc(sizeof(aod_intersection_pg))) aod_intersection_pg(datasketches::DEFAULT_SEED, num_values);
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void aod_intersection_delete(void* interptr) {
  try {
    static_cast<aod_intersection_pg*>(interptr)->~aod_intersection_pg();
    pfree(interptr);
  } catch (std::exception& e) {
    pg_error(e.what());
  }
}

void aod_intersection_update(void* interptr, const void* sketchptr) {
  try {
    static_cast<aod_intersection_pg*>(interptr)->update(*static_cast<const compact_aod_sketch_pg*>(sketchptr));
  } catch (std::exception& e) {
    pg_error(e.what());
  }
}

void* aod_intersection_get_result(const void* interptr) {
  try {
    return new (palloc(sizeof(compact_aod_sketch_pg))) compact_aod_sketch_pg(static_cast<const aod_intersection_pg*>(interptr)->get_result());
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void* aod_a_not_b(const void* sketchptr1, const void* sketchptr2) {
  try {
    aod_a_not_b_pg a_not_b;
    return new (palloc(sizeof(compact_aod_sketch_pg))) compact_aod_sketch_pg(a_not_b.compute(
      *static_cast<const compact_aod_sketch_pg*>(sketchptr1),
      *static_cast<const compact_aod_sketch_pg*>(sketchptr2)
    ));
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

void* aod_sketch_to_kll_float_sketch(const void* sketchptr, unsigned column_index, unsigned k) {
  try {
    auto kllptr = kll_float_sketch_new(k);
    for (const auto& entry: *static_cast<const compact_aod_sketch_pg*>(sketchptr)) {
      kll_float_sketch_update(kllptr, entry.second[column_index]);
    }
    return kllptr;
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

double t_test_unequal_sd(double m1, double v1, uint64_t n1, double m2, double v2, uint64_t n2) {
  double degrees_of_freedom = v1 / n1 + v2 / n2;
  degrees_of_freedom *= degrees_of_freedom;
  double t1 = v1 / n1;
  t1 *= t1;
  t1 /= (n1 - 1);
  double t2 = v2 / n2;
  t2 *= t2;
  t2 /= (n2 - 1);
  degrees_of_freedom /= (t1 + t2);
  double t_stat = (m1 - m2) / sqrt(v1 / n1 + v2 / n2);
  using boost::math::students_t;
  students_t distribution(degrees_of_freedom);
  return 2 * cdf(complement(distribution, fabs(t_stat))); // double to match 2-sided test in Java (commons-math3)
}

Datum* aod_sketch_students_t_test(const void* sketchptr1, const void* sketchptr2, unsigned* arr_len_out) {
  try {
    const auto& sketch1 = *static_cast<const compact_aod_sketch_pg*>(sketchptr1);
    const auto& sketch2 = *static_cast<const compact_aod_sketch_pg*>(sketchptr2);
    if (sketch1.get_num_values() != sketch2.get_num_values()) pg_error("aod_sketch_students_t_test: number of values mismatch");
    unsigned num_values = sketch1.get_num_values();
    Datum* p_values = (Datum*) palloc(sizeof(Datum) * num_values);
    *arr_len_out = num_values;

    using namespace boost::accumulators;
    using Accum = accumulator_set<double, stats<tag::mean, tag::variance>>;

    std::vector<Accum, palloc_allocator<Accum>> stats1(num_values);
    for (const auto& entry: sketch1) {
      for (unsigned i = 0; i < num_values; ++i) stats1[i](entry.second[i]);
    }

    std::vector<Accum, palloc_allocator<Accum>> stats2(num_values);
    for (const auto& entry: sketch2) {
      for (unsigned i = 0; i < num_values; ++i) stats2[i](entry.second[i]);
    }

    for (unsigned i = 0; i < num_values; ++i) {
      p_values[i] = pg_float8_get_datum(t_test_unequal_sd(
        mean(stats1[i]), variance(stats1[i]), sketch1.get_num_retained(),
        mean(stats2[i]), variance(stats2[i]), sketch2.get_num_retained()
      ));
    }

    return p_values;
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

Datum* aod_sketch_to_means(const void* sketchptr, unsigned* arr_len_out) {
  try {
    const auto& sketch = *static_cast<const compact_aod_sketch_pg*>(sketchptr);
    unsigned num_values = sketch.get_num_values();
    Datum* means = (Datum*) palloc(sizeof(Datum) * num_values);
    *arr_len_out = num_values;

    using namespace boost::accumulators;
    using Accum = accumulator_set<double, stats<tag::mean>>;

    std::vector<Accum, palloc_allocator<Accum>> stats(num_values);
    for (const auto& entry: sketch) {
      for (unsigned i = 0; i < num_values; ++i) stats[i](entry.second[i]);
    }

    for (unsigned i = 0; i < num_values; ++i) {
      means[i] = pg_float8_get_datum(mean(stats[i]));
    }

    return means;
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}

Datum* aod_sketch_to_variances(const void* sketchptr, unsigned* arr_len_out) {
  try {
    const auto& sketch = *static_cast<const compact_aod_sketch_pg*>(sketchptr);
    unsigned num_values = sketch.get_num_values();
    Datum* variances = (Datum*) palloc(sizeof(Datum) * num_values);
    *arr_len_out = num_values;

    using namespace boost::accumulators;
    using Accum = accumulator_set<double, stats<tag::variance>>;

    std::vector<Accum, palloc_allocator<Accum>> stats(num_values);
    for (const auto& entry: sketch) {
      for (unsigned i = 0; i < num_values; ++i) stats[i](entry.second[i]);
    }

    for (unsigned i = 0; i < num_values; ++i) {
      variances[i] = pg_float8_get_datum(variance(stats[i]));
    }

    return variances;
  } catch (std::exception& e) {
    pg_error(e.what());
  }
  pg_unreachable();
}


//  (C) Copyright Raffi Enficiaud 2014.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.

//[example_code
#define BOOST_TEST_MODULE boost_test_containers_c_arrays
#include <boost/test/included/unit_test.hpp>
#include <vector>

namespace tt = boost::test_tools;

BOOST_AUTO_TEST_CASE( test_collections_on_c_arrays )
{
  int a[] = {1, 2, 3};
  int b[] = {1, 5, 3, 4};
  std::vector<long> c{1, 5, 3, 4};
  BOOST_TEST(a == a); // element-wise compare
  BOOST_TEST(a == b); // element-wise compare
  BOOST_TEST(a != b);
  BOOST_TEST(a < b); // lexicographical compare
  BOOST_TEST(b < c); // lexicographical compare
  BOOST_TEST(c < a); // lexicographical compare
}
//]


/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/integral.hpp>

#include <boost/hana/constant/laws.hpp>
#include <boost/hana/detail/assert.hpp>
#include <boost/hana/list/instance.hpp>
using namespace boost::hana;

template <typename T>
void test() {
    BOOST_HANA_CONSTANT_ASSERT(Constant::laws::check(
        list(
            integral<T, 0>,
            integral<T, 1>,
            integral<T, 2>,
            integral<T, 3>
        )
    ));
}

int main() {
    test<int>();
    test<long>();
}


/*
 * index_raveling.hpp
 *
 *  Created on: Mar 3, 2019
 *      Author: Gregory Kramida
 *   Copyright: 2019 Gregory Kramida
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */

#pragma once
#include <Eigen/Dense>

namespace traversal {
inline static void unravel_3d_index(int& x, int& y, int& z, Eigen::Index i_element,
		const int& y_stride, const int& z_stride) {
	z = i_element / z_stride;
	int remainder = i_element % z_stride;
	y = remainder / y_stride;
	x = remainder % y_stride;
}
} //namespace traversal


/* Copyright (c) 2012, Julian Straub <jstraub@csail.mit.edu>
 * Licensed under the MIT license. See LICENSE.txt or
 * http://www.opensource.org/licenses/mit-license.php */

#pragma once

#include "random.hpp"
#include "baseMeasure.hpp"
//#include "dp.hpp"
#include "probabilityHelpers.hpp"

#include <stddef.h>
#include <stdint.h>
#include <typeinfo>

#include <boost/math/special_functions/gamma.hpp>
#include <boost/math/special_functions/digamma.hpp>
#include <boost/math/special_functions/beta.hpp>
#include <armadillo>

using namespace std;
using namespace arma;

// base HDP for using sufficient statistics
template <class U>
class HDP_ss // : public DP<U>
{
  public:
    HDP_ss(const BaseMeasure<U>& base, double alpha, double omega)
      : mH(base), mAlpha(alpha), mOmega(omega)
    {
      //    cout<<"Creating "<<typeid(this).name()<<endl;
    };

    ~HDP_ss()
    { };

    //virtual Row<double> logP_w(uint32_t d) const=0;

    // compute the perplexity given a heldout data from document x_ho and the model paremeters of it (after incorporating x)
    double perplexity(const Row<U>& x_ho, const Row<double>& logP)
    {
//      //cout<<"Computing Perplexity"<<endl;
      uint32_t Nw = x_ho.n_cols;
      uint32_t N = sum(x_ho);
      double perp = 0.0;
      for (uint32_t w=0; w<Nw; ++w){
        //cout<<"c_z_n = "<<c[z[w]]<<" z_n="<<z[w]<<" w="<<w<<" N="<<N<<" x_w="<<x_ho[w]<<" topics.shape="<<topics.n_rows<<" "<<topics.n_cols;
        if (x_ho[w] > 0) {
          perp -= x_ho[w]*logP[w];
          cout<<"w="<<w<<"\tx_ho_w="<<x_ho[w]<<"\tlogP="<<logP[w]<<"\tperp+="<<-double(x_ho[w])*logP[w]<<endl;
        }
      } cout<<endl;
      perp /= double(N);
      perp /= log(2.0); // since it is log base 2 in the perplexity formulation!
      perp = pow(2.0,perp);

      return perp;
    }

protected:

    const BaseMeasure<U>& mH; // base measure
    double mAlpha;
    double mOmega;
    Mat<U> mX; // training data
    Mat<U> mX_ho; // held out data
    Mat<U> mX_te; // test data

};

/*
 * this one assumes that the number of words per document are bigger
 * than the number of individual words and is optimized for that case
 *
 * http://en.wikipedia.org/wiki/Virtual_inheritance
 */
class HDP_var_ss: public HDP_ss<uint32_t>, public virtual HDP_var_base
{
  public:

    HDP_var_ss(const BaseMeasure<uint32_t>& base, double alpha, double omega)
      : HDP_var_base(0,0,0), HDP_ss<uint32_t>(base, alpha, omega)
    {};

    ~HDP_var_ss()
    {};

    // method for "one shot" computation without storing data in this class
    //  Nw: number of different words
    //  kappa=0.9: forgetting rate
    //  uint32_t T=10; // truncation on document level
    //  uint32_t K=100; // truncation on corpus level
    // S = batch size
    void densityEst(const Mat<uint32_t>& x, const Mat<uint32_t>& x_test, double kappa, uint32_t K, uint32_t T, uint32_t S)
    {

      // From: Online Variational Inference for the HDP
      // TODO: think whether it makes sense to use columns and Mat here
      //double mAlpha = 1;
      //double mGamma = 1;
      //double nu = ((Dir*)(&mH))->mAlpha0; // get nu from vbase measure (Dir)
      //double kappa = 0.75;
      // TODO: x is a matrix of counts of words per row. so columns are counts of specific words

      // T-1 gamma draws determine a T dim multinomial
      // T = T-1;

      mX = x;
      mT = T;
      mK = K;
      mNw = x.n_cols;
      uint32_t D=x.n_rows;

      // split up x_test into test and held out data points
      mX_te.set_size(x_test.n_rows,x_test.n_cols);
      mX_ho.set_size(x_test.n_rows,x_test.n_cols);
      for (uint32_t d=0; d<x_test.n_rows; ++d){
        uint32_t N=sum(x_test.row(d));
        Row<uint32_t> x_s(N); // construct a vector with the words as given by the counts in x_test
        uint32_t offset =0;
        for (uint32_t w=0; w<mNw; ++w){
          for (uint32_t i=0; i<x_test(d,w); ++i){
            x_s[i+offset] = w;
          }
          offset += x_test(d,w);
        }
        vector<uint32_t> rndInds(N);
        for (uint32_t i=0; i<N; ++i)
          rndInds[i]=i;
        std::random_shuffle(rndInds.begin(),rndInds.end());

        Row<uint32_t> x_te(N/2);
        Row<uint32_t> x_ho(N/2+N%2);
        for (uint32_t i=0; i<N/2; ++i)
          x_te[i] = x_s[rndInds[i]];
        for (uint32_t i=N/2; i<N; ++i)
          x_ho[i-(N/2)] = x_s[rndInds[i]];

        cout<<"x_te.n_elem="<<x_te.n_elem<<" x_ho.n_elem="<<x_ho.n_elem<<" N="<<N<<endl;
        // now convert back into counts
        for (uint32_t w=0; w<mNw; ++w){
          mX_ho.row(d)[w] = sum(x_ho == w);
          mX_te.row(d)[w] = sum(x_te == w);
        }
        cout<<"x_test=\t"<<x_test.row(d)<<endl;
        cout<<"x_te+ho=\t"<<mX_te.row(d)+mX_ho.row(d)<<endl;
        cout<<"x_ho=\t"<<mX_ho.row(d)<<endl;
        cout<<"x_te=\t"<<mX_te.row(d)<<endl;
      }

      Mat<double> a(K,2);
      a.ones();
      a.col(1) *= mOmega;

      // initialize lambda
      GammaRnd gammaRnd(1.0,1.0);
      Mat<double> lambda(K,mNw);
      for (uint32_t k=0; k<K; ++k){
        for (uint32_t w=0; w<mNw; ++w) lambda(k,w) = gammaRnd.draw();
        lambda.row(k) *= double(D)*100.0/double(K*mNw);
        lambda.row(k) += ((Dir*)(&mH))->mAlphas;
      }

      mZeta.resize(D,Mat<double>());
      mPhi.resize(D,Mat<double>());
      mGamma.resize(D,Mat<double>());
      mPerp.zeros(D);

      vector<Col<uint32_t> > z_dn(D);
      Col<uint32_t> ind = shuffle(linspace<Col<uint32_t> >(0,D-1,D),0);
//#pragma omp parallel private(dd,db)
//#pragma omp parallel private(d,dd,N,zeta,phi,converged,gamma,gamma_prev,o,d_lambda,d_a,ro,i,perp_i)
      //shared(x,mZeta,mPhi,mGamma,mOmega,D,T,K,Nw,mA,mLambda,mPerp,mX_ho)
      {
//#pragma omp for schedule(dynamic)
//#pragma omp for schedule(dynamic) ordered
        for (uint32_t dd=0; dd<D; dd += S)
        {
          Mat<double> db_lambda(K,mNw); // batch updates
          Mat<double> db_a(K,2);
#pragma omp parallel for schedule(dynamic)
          for (uint32_t db=dd; db<min(dd+S,D); db++)
          {
            uint32_t d=ind[db];
            uint32_t N=sum(x.row(d));
            //      cout<<"---------------- Document "<<d<<" N="<<N<<" -------------------"<<endl;

            cout<<"-- db="<<db<<" d="<<d<<" N="<<N<<endl;
            Mat<double> zeta(T,K);
            Mat<double> phi(mNw,T);
            initZeta(zeta,lambda,x.row(d));
            initPhi(phi,zeta,lambda,x.row(d));

            //cout<<" ------------------------ doc level updates --------------------"<<endl;
            Mat<double> gamma(T,2);
            Mat<double> gamma_prev(T,2);
            gamma_prev.ones();
            gamma_prev.col(1) += mAlpha;
            bool converged = false;
            uint32_t o=0;
            while(!converged){
              //           cout<<"-------------- Iterating local params #"<<o<<" -------------------------"<<endl;
              updateGamma(gamma,phi);
              updateZeta(zeta,phi,a,lambda,x.row(d));
              updatePhi(phi,zeta,gamma,lambda,x.row(d));

              converged = (accu(gamma_prev != gamma))==0 || o>60 ;
              gamma_prev = gamma;
              ++o;
            }

            mZeta[d] = Mat<double>(zeta);
            mPhi[d] = Mat<double>(phi);
            mGamma[d] = Mat<double>(gamma);
            Mat<double> d_lambda(K,mNw);
            Mat<double> d_a(K,2);
            //      cout<<" --------------------- natural gradients --------------------------- "<<endl;
            computeNaturalGradients(d_lambda, d_a, zeta, phi, mOmega, D, x.row(d));
 #pragma omp critical
            {
              db_lambda += d_lambda;
              db_a += d_a;
            }
          }
          //for (uint32_t k=0; k<K; ++k)
          //  cout<<"delta lambda_"<<k<<" min="<<min(d_lambda.row(k))<<" max="<< max(d_lambda.row(k))<<" #greater 0.1="<<sum(d_lambda.row(k)>0.1)<<endl;
          // ----------------------- update global params -----------------------
          double bS = min(S,D-dd); // necessary for the last batch, which migth not form a complete batch
          double ro = exp(-kappa*log(1+double(dd)+double(bS)/2.0)); // as "time" use the middle of the batch
          cout<<" -- global parameter updates dd="<<dd<<" bS="<<bS<<" ro="<<ro<<endl;
          //cout<<"d_a="<<d_a<<endl;
          //cout<<"a="<<a<<endl;
          lambda = (1.0-ro)*lambda + (ro/S)*db_lambda;
          a = (1.0-ro)*a + (ro/S)*db_a;
          mA=a;
          mLambda = lambda;

          mPerp[dd+bS/2] = 0.0;
          if (mX_ho.n_rows > 0) {
            cout<<"computing "<<mX_ho.n_rows<<" perplexities"<<endl;
#pragma omp parallel for schedule(dynamic)
            for (uint32_t i=0; i<mX_ho.n_rows; ++i)
            {
              double perp_i =  perplexity(mX_te.row(i),mX_ho.row(i),dd+bS/2+1,kappa); //perplexity(mX_ho[i], mZeta[d], mPhi[d], mGamma[d], lambda);
              cout<<"perp_"<<i<<"="<<perp_i<<endl;
#pragma omp critical
              {
                mPerp[dd] += perp_i;
              }
            }
            mPerp[dd] /= double(mX_ho.n_rows);
            //cout<<"Perplexity="<<mPerp[d]<<endl;
          }
        }
      }

      //return z_dn; //TODO: return p_d(x)
    };

//    // compute density estimate based on data previously fed into the class using addDoc
//    bool densityEst(double kappa, uint32_t K, uint32_t T, uint32_t S)
//    {
//      if(mX.n_rows > 0)
//      {
//        densityEst(mX,kappa,K,T,S);
//        //TODO: return p_d(x)
//        return true;
//      }else{
//        return false;
//      }
//    };

    // after an initial densitiy estimate has been made using densityEst()
    // can use this to update the estimate with information from additional x
    bool  updateEst(const Mat<uint32_t>& x, double kappa)
    {
      if (mX.n_rows > 0 && mX.n_rows == mPhi.size()) { // this should indicate that there exists a estimate already
        uint32_t d = mX.n_rows;
        uint32_t Nw = x.n_cols;
        uint32_t T = mT; //mZeta[0].n_rows;
        uint32_t K = mK; //mZeta[0].n_cols;
        mX.resize(d+1,mX.n_cols);
        mX.row(d) = x;
        mZeta.push_back(Mat<double>(T,K));
        mPhi.push_back(Mat<double>(Nw,T));
        //    mZeta.set_size(T,K);
        //    mPhi.set_size(N,T);
        mGamma.push_back(Mat<double>(T,2));
        mPerp.resize(d+1);

        if(updateEst(mX.row(d),mZeta[d],mPhi[d],mGamma[d],mA,mLambda,mOmega,d,kappa))
        {
          mPerp[d] = 0.0;
          for (uint32_t i=0; i<mX_ho.n_rows; ++i)
            mPerp[d] += perplexity(mX_te.row(i),mX_ho.row(i), d, kappa); //mZeta[d], mPhi[d], mGamma[d], mLambda);
          mPerp[d] /= double(mX_ho.n_rows);
          cout<<"Perplexity="<<mPerp[d]<<endl;
          return true;
        }else{
          return false;
        }
      }else{
        return false;
      }
    };

    // compute the perplexity of a given document x_ho (held out for perplexity) and x_te (used to update the model) of a document
    double perplexity(const Row<uint32_t>& x_te, const Row<uint32_t>& x_ho, uint32_t d, double kappa)
    {
      if (mX.n_rows > 0 && mX.n_rows == mPhi.size()) { // this should indicate that there exists a estimate already
        uint32_t Nw = x_te.n_cols;
        //uint32_t Nw = mLambda.n_cols;
        uint32_t T = mT; //mZeta[0].n_rows;
        uint32_t K = mK; //mZeta[0].n_cols;

        Mat<double> zeta(T,K);
        Mat<double> phi(Nw,T);
        Mat<double> gamma(T,2);
        //uint32_t d = mX.size()-1;

        Mat<double> a(mA);// DONE: make deep copy here!
        Mat<double> lambda(mLambda);
        double omega = mOmega;

        //cout<<"updating copied model with x"<<endl;
        updateEst(x_te,zeta,phi,gamma,a,lambda,omega,d,kappa);
        //cout<<"computing perplexity under updated model"<<endl;
        Row<double> logP=logP_w(phi, zeta, gamma, lambda);
        return HDP_ss<uint32_t>::perplexity(x_ho, logP);
        //return perplexity(x_ho, zeta, phi, gamma, lambda);
      }else{
        return 1.0/0.0;
      }
    };

    // compute the perplexity given a heldout data from document x_ho and the model paremeters of it (after incorporating x)
    double perplexity(const Row<uint32_t>& x_ho, const Mat<double>& zeta, const Mat<double>& phi, const Mat<double>& gamma, const vector<BaseMeasure<uint32_t>* >& lambda)
    {
      Row<double> logP=logP_w(phi, zeta, gamma, lambda);

//      //cout<<"Computing Perplexity"<<endl;
      uint32_t Nw = x_ho.n_cols;
      uint32_t N = sum(x_ho);
      double perp = 0.0;
      for (uint32_t w=0; w<Nw; ++w){
        //cout<<"c_z_n = "<<c[z[w]]<<" z_n="<<z[w]<<" w="<<w<<" N="<<N<<" x_w="<<x_ho[w]<<" topics.shape="<<topics.n_rows<<" "<<topics.n_cols;
        if (x_ho[w] > 0) {
          perp -= x_ho[w]*logP[w];
          cout<<"w="<<w<<"\tx_ho_w="<<x_ho[w]<<"\tlogP="<<logP[w]<<"\tperp+="<<-double(x_ho[w])*logP[w]<<endl;
        }
      } cout<<endl;
      perp /= double(N);
      perp /= log(2.0); // since it is log base 2 in the perplexity formulation!
      perp = pow(2.0,perp);

      return perp;
    }

    bool updateEst(const Row<uint32_t>& x, Mat<double>& zeta, Mat<double>& phi, Mat<double>& gamma, Mat<double>& a, vector<BaseMeasure<uint32_t>* >& lambda, double omega, uint32_t d, double kappa)
    {
      uint32_t D = d+1; // assume that doc d is appended to the end
      //uint32_t N = x.n_rows;
      uint32_t Nw = x.n_cols;
      uint32_t T = zeta.n_rows;
      uint32_t K = zeta.n_cols;

      //    cout<<"---------------- Document "<<d<<" N="<<N<<" -------------------"<<endl;
      //    cout<<"a=\t"<<a.t();
      //    for (uint32_t k=0; k<K; ++k)
      //    {
      //      cout<<"@"<<k<<" lambda=\t"<<lambda.row(k);
      //    }

      initZeta(zeta,lambda,x);
      initPhi(phi,zeta,lambda,x);

      // ------------------------ doc level updates --------------------
      bool converged = false;
      Mat<double> gamma_prev(T,2);
      gamma_prev.ones();

      uint32_t o=0;
      while(!converged){
        //      cout<<"-------------- Iterating local params #"<<o<<" -------------------------"<<endl;
        updateGamma(gamma,phi);
        updateZeta(zeta,phi,a,lambda,x);
        updatePhi(phi,zeta,gamma,lambda,x);

        converged = (accu(gamma_prev != gamma))==0 || o>60 ;

        gamma_prev = gamma;
        //cout<<"zeta>"<<endl<<zeta<<"<zeta"<<endl;
        //cout<<"phi>"<<endl<<phi<<"<phi"<<endl;
        ++o;
      }

      //    cout<<" --------------------- natural gradients --------------------------- "<<endl;
      //    cout<<"\tD="<<D<<" omega="<<omega<<endl;

      Mat<double> d_lambda(K,Nw);
      Mat<double> d_a(K,2);
      computeNaturalGradients(d_lambda, d_a, zeta, phi, omega, D, x);

      //    cout<<" ------------------- global parameter updates: ---------------"<<endl;
      //cout<<"delta a= "<<d_a.t()<<endl;
      //for (uint32_t k=0; k<K; ++k)
      //  cout<<"delta lambda_"<<k<<" min="<<min(d_lambda.row(k))<<" max="<< max(d_lambda.row(k))<<" #greater 0.1="<<sum(d_lambda.row(k)>0.1)<<endl;

      // ----------------------- update global params -----------------------
      double ro = exp(-kappa*log(1+double(d+1)));
      //    cout<<"\tro="<<ro<<endl;
      lambda = (1.0-ro)*lambda + ro*d_lambda;
      a = (1.0-ro)*a+ ro*d_a;

      return true;
    };

    /* Probability distribution over the words in document d
     *
     * TODO: so is that here not some MAP or ML estimate?!
     */
    Row<double> logP_w(uint32_t d) const {
      return logP_w(mPhi[d],mZeta[d],mGamma[d],mLambda);
    };

    /*
     * probability of assuming the use of suffient statistics
     */
    // TODO: this was the old version - presumably working for sufficient statistics
    Row<double> logP_w(const Mat<double>& phi, const Mat<double>& zeta, const Mat<double>& gamma, const vector<BaseMeasure<uint32_t>* >& lambda) const
    {
      Row<double> p(mNw);
      p.zeros();

      cout<<"phi:\t"<<size(phi);
      cout<<"zeta:\t"<<size(zeta);
      cout<<"gamma:\t"<<size(gamma);
      cout<<"lambda:\t"<<lambda.size();

      Col<double> pi;
      Col<double> sigPi;
      Col<uint32_t> c;
      getDocTopics(pi,sigPi,c,gamma,zeta);
      cout<<"getDocTopics done"<<endl;
      cout<<"c="<<c<<endl;
      Col<uint32_t> z(mNw);
      getWordTopics(z, phi);
      cout<<"getWordTopics done"<<endl;
      cout<<"z="<<z<<endl;
      Mat<double> beta;
      getCorpTopics(beta,lambda);
      cout<<"getCorpTopics done"<<endl;
      cout<<"beta:\t"<<size(beta);

      for (uint32_t w=0; w<mNw; ++w){
        cout<<"w="<<w<<"; Nw="<<mNw<<endl;
        cout<<"z_w="<<z[w]<<endl;
        p[w] = logCat(w, beta.row( c[ z[w] ]));
        cout<<"p_"<<w<<"="<<p[w]<<endl;
      }
      cout<<"p="<<p<<endl;
      return p;
    };

    /* TODO: its not realy the joint... or is it?!
     * joint probability distribution
     */
//    Row<double> logP_joint(uint32_t d) const {
//      return logP_joint(mPhi[d],mZeta[d],mGamma[d],mLambda,mA);
//    };
//    Row<double> logP_joint(const Mat<double>& phi, const Mat<double>& zeta, const Mat<double>& gamma, const Mat<double>& lambda, const Mat<double>& a) const
//    {
//      Row<double> p(mNw);
//      p.zeros();
//
//      Col<double> v;
//      Col<double> sigV;
//      getCorpTopicProportions(v,sigV,a);
//      Col<double> pi;
//      Col<double> sigPi;
//      Col<uint32_t> c;
//      getDocTopics(pi,sigPi,c,gamma,zeta);
//      Col<uint32_t> z(mNw);
//      getWordTopics(z, phi);
//      Mat<double> beta;
//      getCorpTopics(beta,lambda);
//
//      for (uint32_t w=0; w<mNw; ++w){
//        p[w] = logCat(w, beta.row( c[ z[w] ]).t()) +
//          logCat(c[ z[w] ], sigV.t()) +
//          logBeta(v, 1.0, mOmega) +
//          logCat(z[w], sigPi.t()) +
//          logBeta(pi, 1.0, mAlpha) +
//          logDir(beta.row( c[ z[w] ]).t(), mLambda.t());
//      }
//      return p;
//    };

  private:

    void initZeta(Mat<double>& zeta, const Mat<double>& lambda, const Row<uint32_t>& x_d)
    {
      uint32_t Nw = x_d.n_cols;
      uint32_t T = zeta.n_rows;
      uint32_t K = zeta.n_cols;
      //cerr<<"\tinit zeta"<<endl;
      for (uint32_t i=0; i<T; ++i) {
        for (uint32_t k=0; k<K; ++k) {
          zeta(i,k)=0.0;
          for (uint32_t w=0; w<Nw; ++w) {
            //if(i==0 && k==0) cout<<zeta(i,k)<<" -> ";
            zeta(i,k) += x_d(w) * ElogBeta(lambda, k, w);
          }
        }
        normalizeLogDistribution(zeta.row(i));
      }
      //cerr<<"zeta>"<<endl<<zeta<<"<zeta"<<endl;
      //cerr<<"normalization check:"<<endl<<sum(zeta,1).t()<<endl; // sum over rows
    };

    void initPhi(Mat<double>& phi, const Mat<double>& zeta, const Mat<double>& lambda, const Row<uint32_t>& x_d)
    {
      uint32_t Nw = x_d.n_cols;
      uint32_t T = zeta.n_rows;
      uint32_t K = zeta.n_cols;
      //cout<<"\tinit phi"<<endl;
      for (uint32_t w=0; w<Nw; ++w){
        for (uint32_t i=0; i<T; ++i) {
          phi(w,i)=0.0;
          for (uint32_t k=0; k<K; ++k) {
            phi(w,i)+=zeta(i,k)* x_d(w) * ElogBeta(lambda, k, w);
          }
        }
        normalizeLogDistribution(phi.row(w));
      }
      //cerr<<"phi>"<<endl<<phi<<"<phi"<<endl;
    };

    void updateGamma(Mat<double>& gamma, const Mat<double>& phi)
    {
      uint32_t N = phi.n_rows;
      uint32_t T = phi.n_cols;

      gamma.ones();
      gamma.col(1) *= mAlpha;
      for (uint32_t i=0; i<T; ++i)
      {
        for (uint32_t n=0; n<N; ++n){
          gamma(i,0) += phi(n,i);
          for (uint32_t j=i+1; j<T; ++j) {
            gamma(i,1) += phi(n,j);
          }
        }
      }
      //cout<<gamma.t()<<endl;
    };

    void updateZeta(Mat<double>& zeta, const Mat<double>& phi, const Mat<double>& a, const Mat<double>& lambda, const Row<uint32_t>& x_d)
    {
      uint32_t Nw = x_d.n_cols;
      uint32_t T = zeta.n_rows;
      uint32_t K = zeta.n_cols;

      for (uint32_t i=0; i<T; ++i){
        //zeta(i,k)=0.0;
        for (uint32_t k=0; k<K; ++k) {
          zeta(i,k) = ElogSigma(a,k);
          //cout<<zeta(i,k)<<endl;
          for (uint32_t w=0; w<Nw; ++w){
            zeta(i,k) += phi(w,i)*x_d(w)*ElogBeta(lambda,k,w);
          }
        }
        normalizeLogDistribution(zeta.row(i));
      }
    }

    void updatePhi(Mat<double>& phi, const Mat<double>& zeta, const Mat<double>& gamma, const Mat<double>& lambda, const Row<uint32_t>& x_d)
    {
      uint32_t Nw = x_d.n_cols;
      uint32_t T = zeta.n_rows;
      uint32_t K = zeta.n_cols;

      for (uint32_t w=0; w<Nw; ++w){
        //phi(n,i)=0.0;
        for (uint32_t i=0; i<T; ++i) {
          phi(w,i) = ElogSigma(gamma,i);
          for (uint32_t k=0; k<K; ++k) {
            phi(w,i) += zeta(i,k)*x_d(w)*ElogBeta(lambda,k,w) ;
          }
        }
        normalizeLogDistribution(phi.row(w));
      }
    }

    void computeNaturalGradients(Mat<double>& d_lambda, Mat<double>& d_a, const Mat<double>& zeta, const Mat<double>&  phi, double omega, uint32_t D, const Row<uint32_t>& x_d)
    {
      uint32_t Nw = x_d.n_cols;
      uint32_t T = zeta.n_rows;
      uint32_t K = zeta.n_cols;

      d_lambda.zeros();
      d_a.zeros();
      for (uint32_t k=0; k<K; ++k) { // for all K corpus level topics
        for (uint32_t i=0; i<T; ++i) {
          Row<double> _lambda(Nw); _lambda.zeros();
          for (uint32_t w=0; w<Nw; ++w){
            _lambda(w) += phi(w,i); // i think if I multiply by x_d(w) again here I am doing it twice (see updatePhi)  x_d(w)*phi(w,i);
          }
          d_lambda.row(k) += zeta(i,k) * _lambda;
          d_a(k,0) += zeta(i,k);
          for (uint32_t l=k+1; l<K; ++l) {
            d_a(k,1) += zeta(i,l);
          }
        }
        d_lambda.row(k) = D*d_lambda.row(k);
        //cout<<"lambda-nu="<<d_lambda[k].t()<<endl;
        d_lambda.row(k) += ((Dir*)(&mH))->mAlphas;
        //cout<<"lambda="<<d_lambda[k].t()<<endl;
        d_a(k,0) = D*d_a(k,0)+1.0;
        d_a(k,1) = D*d_a(k,1)+omega;
      }
      //cout<<"da="<<d_a<<endl;
    }

    double digamma(double x)
    {
      //http://en.wikipedia.org/wiki/Digamma_function#Computation_and_approximation
      if(x<1e-50){
        //cerr<<"\tdigamma param x near zero: "<<x<<" cutting of"<<endl;
        x=1e-50;
      }
      //double x_sq = x*x;
      //return log(x)-1.0/(2.0*x)-1.0/(12.0*x_sq)+1.0/(12*x_sq*x_sq)-1.0/(252.0*x_sq*x_sq*x_sq);
      return boost::math::digamma(x);
    }

    double ElogBeta(const Mat<double>& lambda, uint32_t k, uint32_t w)
    {
      //if(lambda[k](w_dn)<1e-6){
      //  cout<<"\tlambda[k]("<<w_dn<<") near zero: "<<lambda[k](w_dn)<<endl;
      //}
      return digamma(lambda(k,w)) - digamma(sum(lambda.row(k)));
    }

    double ElogSigma(const Mat<double>& a, uint32_t k)
    {
      double e=digamma(a(k,0)) - digamma(a(k,0) + a(k,1));
      for (uint32_t l=0; l<k; ++l)
        e+=digamma(a(k,1)) - digamma(a(k,0) + a(k,1));
      return e;
    }

    //bool normalizeLogDistribution(Row<double>& r)
    bool normalizeLogDistribution(arma::subview_row<double> r)
    {
      //r.row(i)=exp(r.row(i));
      //cout<<" r="<<r<<endl;
      double minR = as_scalar(min(r));
      //cout<<" minR="<<minR<<endl;
      if(minR > -100.0) {
        //cout<<" logDenom="<<sum(exp(r),1)<<endl;
        double denom = as_scalar(sum(exp(r),1));
        //cout<<" logDenom="<<denom<<endl;
        r -= log(denom); // avoid division by 0
        //cout<<" r - logDenom="<<r<<endl;
        r = exp(r);
        r /= sum(r);
        //cout<<" exp(r - logDenom)="<<r<<endl;
        return true;
      }else{ // cannot compute this -> set the smallest r to 1.0 and the rest to 0
        double maxR = as_scalar(max(r));
        //cout<<"maxR="<<maxR<<" <-" <<arma::max(r) <<endl;
        uint32_t kMax=as_scalar(find(r==maxR,1));
        //cout<<"maxR="<<maxR<<" kMax="<<kMax<<" <-" <<arma::max(r) <<endl;
        r.zeros();
        r(kMax)=1.0;
        //cout<<" r ="<<r<<endl;
        return false;
      }
    }

};


//  (C) Copyright John Maddock 2000.
//  Use, modification and distribution are subject to the
//  Boost Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/type_traits/is_void.hpp>
#include <boost/mpl/if.hpp>
#include <boost/static_assert.hpp>

template <class T>
struct if_test
{
   typedef typename boost::mpl::if_<
      boost::is_void<T>,
      int, T>::type type;
};

if_test<void>::type t1 = 0;
if_test<double>::type t2 = 0;

int main()
{
   return (int)(t1 + t2);
}


#include <bits/stdc++.h>
#include <boost/multiprecision/cpp_int.hpp>
using namespace boost::multiprecision;
// #include <atcoder/all>
// using namespace atcoder;
typedef long long ll;
typedef long double ld;
#define rep(i,n) for(ll i=0;i<(n);i++)
#define repr(i,n) for(ll i=(n-1);i>=0;i--)
#define all(x) x.begin(),x.end()
#define br cout << "\n";
using namespace std;
const long long INF = 1e18;
const long long MOD = 1e9+7;
using Graph = vector<vector<ll>>;
template<class T> inline bool chmin(T &a, T b) { if(a > b){ a = b; return true;} return false;}
template<class T> inline bool chmax(T &a, T b) { if(a < b){ a = b; return true;} return false;}
ll ceilll(ll a, ll b) {return (a + b-1) / b;} // if(a%b != 0) (a/b) + 1
ll get_digit(ll a) {ll digit = 0; while(a != 0){a /= 10; digit++;} return digit;} // a != 0
template<typename T> void vecdbg(vector<T>& v){ rep(i, v.size()){cerr << v[i] << " ";} br;}
template<typename T> void vecvecdbg(vector<vector<T>>& v){ rep(i, v.size()){rep(j, v[i].size()){cerr << v[i][j] << " ";} br;}}
ll POW(ll a, ll n){ ll res = 1; while(n > 0){ if(n & 1){ res = res * a; } a *= a; n >>= 1; } return res; }
__int128 POW2(__int128 a, __int128 n){ __int128 res = 1; while(n > 0){ if(n & 1){ res = res * a; } a *= a; n >>= 1; } return res; }
cpp_int POW3(cpp_int a, cpp_int n){ cpp_int res = 1; while(n > 0){ if(n & 1){ res = res * a; } a *= a; n >>= 1; } return res; }
using P = pair<ll, ll>;

// 0 false, 1 true
// string number to int : -48 or - '0'
// a to A : -32
// ceil(a)  1.2->2.0
// c++17	g++ -std=c++17 a.cpp
// global vector -> 0 initialization
// DONT FORGET TO INTIALIZE
// The type of GRID is CHAR. DONT USE STRING
// If the result in local and judge is different, USE CODETEST!!
// (a * b)over flow?   if(a > INF / b){ /* overflow */}

std::ostream &operator<<(std::ostream &dest, __int128_t value) {
  std::ostream::sentry s(dest);
  if (s) {
    __uint128_t tmp = value < 0 ? -value : value;
    char buffer[128];
    char *d = std::end(buffer);
    do {
      --d;
      *d = "0123456789"[tmp % 10];
      tmp /= 10;
    } while (tmp != 0);
    if (value < 0) {
      --d;
      *d = '-';
    }
    int len = std::end(buffer) - d;
    if (dest.rdbuf()->sputn(d, len) != len) {
      dest.setstate(std::ios_base::badbit);
    }
  }
  return dest;
}

__int128 parse(string &s) {
  __int128 ret = 0;
  for (int i = 0; i < s.length(); i++)
    if ('0' <= s[i] && s[i] <= '9')
      ret = 10 * ret + s[i] - '0';
  return ret;
}

string to_string128(__int128 x){
  string res = "";
  while(x > 0){
    res += (x % 10) + '0';
    x /= 10;
  }
  reverse(all(res));
  return res;
}

int main() {
    std::cout << std::fixed << std::setprecision(15);
    ll t; cin >> t;
    rep(T, t){
        ll n; cin >> n;
        vector<cpp_int> x(n);
        for(long long i = 0; i < n; i ++){
            // string s; cin >> s;
            // cpp_int tmp = parse(s);
            // x[i] = tmp;
            cin >> x[i];
        }

        ll ans = 0;
        for(ll i = 1; i < n; i ++){

            if(x[i] > x[i-1]){
                continue;
            }else if(x[i] == x[i-1]){
                x[i] = x[i]*10;
                ans += 1;
                continue;
            }

            // ll maeketa = to_string128(x[i-1]).size();
            // ll atoketa = to_string128(x[i]).size();
            ll maeketa = x[i-1].str().size();
            ll atoketa = x[i].str().size();

            if(maeketa < atoketa){
                continue;
            }else if(maeketa == atoketa){
                // add 0
                x[i] = x[i]*10;
                ans += 1;
            }else{
                // maeketa > atoketa
                string bef = x[i-1].str();
                string aft = x[i].str();
                ll flag = 0; // 0同じ　1大 2小
                for(ll j = 0; j < atoketa; j++){

                    if(bef[j] == aft[j]){
                        continue;
                    }else if((bef[j] - '0') < (aft[j] - '0')){
                        flag = 1; break;
                    }else{
                        flag = 2; break;
                    }
                }

                if(flag == 0){
                    //cerr << "flag = 0 " << endl;
                    bool nine = true;
                    for(ll j = atoketa; j < maeketa; j++){
                        //cerr << bef[j] << endl;
                        if(bef[j] != '9'){
                            nine = false; break;
                        }
                    }
                    //後ろ全部9だったらダメ
                    //全部9じゃなかったら+1
                    if(nine){
                        x[i] = x[i] * POW3(10, (maeketa - atoketa + 1));
                        ans += (maeketa - atoketa + 1);
                    }else{
                        x[i] = x[i-1] + 1;
                        ans += (maeketa - atoketa);
                        //cerr << "in" << endl;
                    }
                }else if(flag == 1){
                    x[i] = x[i] * POW3(10, (maeketa - atoketa));
                    ans += (maeketa - atoketa);
                }else{
                    // cout << x[i] << endl;
                    // cout << POW2(10, (maeketa - atoketa + 1)) << endl;
                    x[i] = x[i] * POW3(10, (maeketa - atoketa + 1));

                    ans += (maeketa - atoketa + 1);
                }

            }

        }

        vecdbg(x);

        cout << "Case #" << (T+1) << ": " << ans << endl;

    }

}


// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.

#include "common.h"
#include <Eigen/Eigenvalues>

// computes an LU factorization of a general M-by-N matrix A using partial pivoting with row interchanges
EIGEN_LAPACK_FUNC(syev,(char *jobz, char *uplo, int* n, Scalar* a, int *lda, Scalar* w, Scalar* /*work*/, int* lwork, int *info))
{
  // TODO exploit the work buffer
  bool query_size = *lwork==-1;

  *info = 0;
        if(*jobz!='N' && *jobz!='V')                    *info = -1;
  else  if(UPLO(*uplo)==INVALID)                        *info = -2;
  else  if(*n<0)                                        *info = -3;
  else  if(*lda<std::max(1,*n))                         *info = -5;
  else  if((!query_size) && *lwork<std::max(1,3**n-1))  *info = -8;

//   if(*info==0)
//   {
//     int nb = ILAENV( 1, 'SSYTRD', UPLO, N, -1, -1, -1 )
//          LWKOPT = MAX( 1, ( NB+2 )*N )
//          WORK( 1 ) = LWKOPT
// *
//          IF( LWORK.LT.MAX( 1, 3*N-1 ) .AND. .NOT.LQUERY )
//      $      INFO = -8
//       END IF
// *
//       IF( INFO.NE.0 ) THEN
//          CALL XERBLA( 'SSYEV ', -INFO )
//          RETURN
//       ELSE IF( LQUERY ) THEN
//          RETURN
//       END IF

  if(*info!=0)
  {
    int e = -*info;
    return xerbla_(SCALAR_SUFFIX_UP"SYEV ", &e, 6);
  }

  if(query_size)
  {
    *lwork = 0;
    return 0;
  }

  if(*n==0)
    return 0;

  PlainMatrixType mat(*n,*n);
  if(UPLO(*uplo)==UP) mat = matrix(a,*n,*n,*lda).adjoint();
  else                mat = matrix(a,*n,*n,*lda);

  bool computeVectors = *jobz=='V' || *jobz=='v';
  SelfAdjointEigenSolver<PlainMatrixType> eig(mat,computeVectors?ComputeEigenvectors:EigenvaluesOnly);

  if(eig.info()==NoConvergence)
  {
    vector(w,*n).setZero();
    if(computeVectors)
      matrix(a,*n,*n,*lda).setIdentity();
    //*info = 1;
    return 0;
  }

  vector(w,*n) = eig.eigenvalues();
  if(computeVectors)
    matrix(a,*n,*n,*lda) = eig.eigenvectors();

  return 0;
}


//
// Created by Alex Beccaro on 18/01/18.
//

#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include "../../src/problems/51-100/77/problem77.hpp"

BOOST_AUTO_TEST_SUITE( Problem77 )

    BOOST_AUTO_TEST_CASE( Example ) {
        auto res = problems::problem77::solve(4);
        BOOST_CHECK_EQUAL(res, 10);
    }

    BOOST_AUTO_TEST_CASE( Solution ) {
        auto res = problems::problem77::solve();
        BOOST_CHECK_EQUAL(res, 71);
    }

BOOST_AUTO_TEST_SUITE_END()


#ifndef CGTO_HH
#define CGTO_HH

/*!
 * \file CGTO.hh
 * \brief Definition of the CGTO class
 */

#include <Eigen/Dense>
#include "AbstractBF.hh"
#include "CGTOShellList.hh"
#include "exceptions.hh"

/*!
 * \brief Contracted Gaussian Type Orbital
 *
 * Class CGTO defines a contraction of primitive (cartesian) Gaussian
 * type orbitals, each of the form
 * \f[
 * G(x,y,z) = (x-x_c)^{l_x} (y-y_c)^{l_y} (z-z_c)^{l_z}
 *            \exp(-\alpha * |{\bf r}-{\bf r}_c|^2)
 * \f]
 */
class CGTO: public AbstractBF
{
public:
	//! Unique class ID, used in looking up integral calculation functions
	static const size_t cid;

	/*!
	 * \brief Constructor
	 *
	 * Create a new contraction of primitive Gaussian type orbitals with
	 * angular momentum quantum numbers \a ls on position \a center.
	 * \param ls     Angular momentum of the orbital
 	 * \param shell  This orbital's shell
	 */
	CGTO(const Eigen::Array3i& ls, const CGTOShell& shell):
		AbstractBF(cid), _ls(ls), _shell(shell) {}

	//! Return the number of primitives in this contraction
	int size() const { return shell().size(); }
	//! Return the angular momentum quantum numbers
	const Eigen::Array3i& ls() const { return _ls; }
	//! Return the angular momentum in the \f$x\f$ direction
	int lx() const { return _ls.x(); }
	//! Return the angular momentum in the \f$y\f$ direction
	int ly() const { return _ls.y(); }
	//! Return the angular momentum in the \f$z\f$ direction
	int lz() const { return _ls.z(); }
	//! Return the angular momentum in the \a i direction
	int l(int i) const { return _ls[i]; }
	//! Return the total angular momentum
	int lsum() const { return _ls.sum(); }
	//! Return the shell of this orbital
	const CGTOShell& shell() const { return _shell; }
	//! Return the width of the \a i'th primitive
	double width(int i) const { return shell().width(i); }
	//! Return the widths of all primitives in this orbital
	const Eigen::ArrayXd& widths() const { return shell().widths(); }
	//! Return the position ID of this orbital's center
	int positionId() const { return shell().positionId(); }
	//! Return the center position of this orbital
	const Eigen::Vector3d& center() const { return shell().center(); }
	//! Return the \a i coordinate of the center of this orbital
	double center(int i) const { return shell().center(i); }

	/*!
	 * \brief Print this CGTO
	 *
	 * Write a textual representation of this orbital to output
	 * stream \a os.
	 * \param os The output stream to write to
	 * \return The updated output stream
	 */
	std::ostream& print(std::ostream& os) const;

	/*!
	 * \brief Evaluate this CGTO
	 *
	 * Compute the value of this orbital at position \a pos.
	 * \param pos The point in which to evaluate the orbital
	 * \return The value of this CGTO in \a pos
	 */
	double eval(const Eigen::Vector3d& pos) const;

private:
	//! The angular momentum quantum numbers
	Eigen::Array3i _ls;
	//! The shell parameters: widths, weights, and position
	const CGTOShell& _shell;

	/*!
	 * \brief Check if a primitive index is valid
	 *
	 * Check if \a idx is a valid primitive index for this contraction. The
	 * check is only performed if the program is compiled with the DEBUG
	 * symbol defined.
	 * \param idx The index to check
	 * \exception InvalidIndex thrown when the index is out of bounds.
	 */
#ifdef DEBUG
	void checkIndex(int idx) const
	{
		if (idx < 0 || idx >= size())
			throw InvalidIndex(idx);
	}
#else
	void checkIndex(int) const {}
#endif
};

#endif // CGTO_HH


#ifndef PYTHONIC_SCIPY_SPECIAL_YV_HPP
#define PYTHONIC_SCIPY_SPECIAL_YV_HPP

#include "pythonic/include/scipy/special/yv.hpp"

#include "pythonic/types/ndarray.hpp"
#include "pythonic/utils/functor.hpp"
#include "pythonic/utils/numpy_traits.hpp"

#define BOOST_MATH_THREAD_LOCAL thread_local
#include <boost/math/special_functions/bessel.hpp>

PYTHONIC_NS_BEGIN

namespace scipy
{
  namespace special
  {
    namespace details
    {
      template <class T0, class T1>
      double yv(T0 x, T1 y)
      {
        using namespace boost::math::policies;
        return boost::math::cyl_neumann(x, y,
                                        make_policy(promote_double<true>()));
      }
    }

#define NUMPY_NARY_FUNC_NAME yv
#define NUMPY_NARY_FUNC_SYM details::yv
#include "pythonic/types/numpy_nary_expr.hpp"
  }
}
PYTHONIC_NS_END

#endif


// This file is part of libigl, a simple c++ geometry processing library.
//
// Copyright (C) 2013 Alec Jacobson <alecjacobson@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.
#include "faces_first.h"

#include <vector>
#include <Eigen/Dense>

template <typename MatV, typename MatF, typename VecI>
IGL_INLINE void igl::faces_first(
  const MatV & V,
  const MatF & F,
  MatV & RV,
  MatF & RF,
  VecI & IM)
{
  assert(&V != &RV);
  assert(&F != &RF);
  using namespace std;
  using namespace Eigen;
  vector<bool> in_face(V.rows());
  for(int i = 0; i<F.rows(); i++)
  {
    for(int j = 0; j<F.cols(); j++)
    {
      in_face[F(i,j)] = true;
    }
  }
  // count number of vertices not in faces
  int num_in_F = 0;
  for(int i = 0;i<V.rows();i++)
  {
    num_in_F += (in_face[i]?1:0);
  }
  // list of unique vertices that occur in F
  VectorXi U(num_in_F);
  // list of unique vertices that do not occur in F
  VectorXi NU(V.rows()-num_in_F);
  int Ui = 0;
  int NUi = 0;
  // loop over vertices
  for(int i = 0;i<V.rows();i++)
  {
    if(in_face[i])
    {
      U(Ui) = i;
      Ui++;
    }else
    {
      NU(NUi) = i;
      NUi++;
    }
  }
  IM.resize(V.rows());
  // reindex vertices that occur in faces to be first
  for(int i = 0;i<U.size();i++)
  {
    IM(U(i)) = i;
  }
  // reindex vertices that do not occur in faces to come after those that do
  for(int i = 0;i<NU.size();i++)
  {
    IM(NU(i)) = i+U.size();
  }
  RF.resize(F.rows(),F.cols());
  // Reindex faces
  for(int i = 0; i<F.rows(); i++)
  {
    for(int j = 0; j<F.cols(); j++)
    {
      RF(i,j) = IM(F(i,j));
    }
  }
  RV.resize(V.rows(),V.cols());
  // Reorder vertices
  for(int i = 0;i<V.rows();i++)
  {
    RV.row(IM(i)) = V.row(i);
  }
}

template <typename MatV, typename MatF, typename VecI>
IGL_INLINE void igl::faces_first(
  MatV & V,
  MatF & F,
  VecI & IM)
{
  MatV RV;
  // Copying F may not be needed, seems RF = F is safe (whereas RV = V is not)
  MatF RF;
  igl::faces_first(V,F,RV,RF,IM);
  V = RV;
  F = RF;
}

#ifdef IGL_STATIC_LIBRARY
// Explicit template specialization
template void igl::faces_first<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1> >(Eigen::Matrix<double, -1, -1, 0, -1, -1>&, Eigen::Matrix<int, -1, -1, 0, -1, -1>&, Eigen::Matrix<int, -1, 1, 0, -1, 1>&);
#endif


// Copyright (c) Jeremy Siek 2001
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// NOTE: this final is generated by libs/graph/doc/transitive_closure.w

#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION
#error The transitive closure algorithm uses partial specialization.
#endif

#include <boost/graph/transitive_closure.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/graph_utility.hpp>

int main(int, char*[])
{
    using namespace boost;
    typedef property< vertex_name_t, char > Name;
    typedef property< vertex_index_t, std::size_t, Name > Index;
    typedef adjacency_list< listS, listS, directedS, Index > graph_t;
    typedef graph_traits< graph_t >::vertex_descriptor vertex_t;
    graph_t G;
    std::vector< vertex_t > verts(4);
    for (int i = 0; i < 4; ++i)
        verts[i] = add_vertex(Index(i, Name('a' + i)), G);
    add_edge(verts[1], verts[2], G);
    add_edge(verts[1], verts[3], G);
    add_edge(verts[2], verts[1], G);
    add_edge(verts[3], verts[2], G);
    add_edge(verts[3], verts[0], G);

    std::cout << "Graph G:" << std::endl;
    print_graph(G, get(vertex_name, G));

    adjacency_list<> TC;
    transitive_closure(G, TC);

    std::cout << std::endl << "Graph G+:" << std::endl;
    char name[] = "abcd";
    print_graph(TC, name);
    std::cout << std::endl;

    std::ofstream out("tc-out.dot");
    write_graphviz(out, TC, make_label_writer(name));

    return 0;
}


//////////////////////////////////////////////////////////////////////////////////////
// distribution::toolkit::distributions::uniform::description.hpp                    //
//                                                                                //
//  (C) Copyright 2009 Erwann Rogard                                                //
//  Use, modification and distribution are subject to the                           //
//  Boost Software License, Version 1.0. (See accompanying file                     //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)                //
//////////////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_UNIFORM_DESCRIPTION_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_DISTRIBUTION_TOOLKIT_UNIFORM_DESCRIPTION_HPP_ER_2009
#include <string>
#include <boost/format.hpp>
#include <boost/math/distributions/uniform.hpp>

namespace boost{
namespace math{

    template<typename T,typename P>
    std::string
    description(const boost::math::uniform_distribution<T,P>& dist)
    {
        static const char* msg = "uniform(%1%,%2%)";
        format f(msg); f%dist.lower()%dist.upper();
        return (f.str());
    }

}// math
}// boost

#endif


#ifndef _INCLUDED_SPHERE_HPP_
#define _INCLUDED_SPHERE_HPP_

/**
 * @file   sphere.hpp
 * @author Aron Ahmadia <aron@valkyrie.appmath.columbia.edu>
 * @date   Thu Apr 24 19:48:43 2008
 *
 * @brief  Sphere - an object for building point (and region?) decompositions on a sphere
 *
 *
 */

#include <vector>
#include "hyper_sample/utility/geometry.hpp"
#include <boost/dynamic_bitset.hpp>
#include "boost_ext/dynamic_bitset.hpp"

namespace smo {

  typedef int DimIndex;
  typedef int PointIndex;
  typedef std::pair<PointIndex,PointIndex> pointRange;
  typedef boost::dynamic_bitset ReflectableDim;
  typedef vector<pointRange> PointRanges;
  typedef vector<ReflectableDim> ReflectableDims;

  class Sphere
  {
  public:
    Sphere(DimCount dimCount, PointCount pointCount);
    void buildPointsRecurse(DimIndex dimIndex, PointCount localPointCount, PointRange pointRange);

  private:
    SpherePoints spherePoints;
    ReflectableDims reflectableDims;
    PointRanges circleOwnerRanges;
  };

}

#endif


/*
@copyright Louis Dionne 2015
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/assert.hpp>
#include <boost/hana/count.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/tuple.hpp>
#include <boost/hana/type.hpp>
namespace hana = boost::hana;

int main() {
    constexpr auto ints = hana::tuple_c<int, 1, 2, 3, 2, 2, 4, 2>;
    BOOST_HANA_CONSTANT_CHECK(hana::count(ints, hana::int_c<2>) == hana::size_c<4>);
    static_assert(hana::count(ints, 2) == 4, "");

    constexpr auto types = hana::tuple_t<int, char, long, short, char, double>;
    BOOST_HANA_CONSTANT_CHECK(hana::count(types, hana::type_c<char>) == hana::size_c<2>);
}


#include <iostream>
#include <mtl/dense1D.h>
#include <mtl/mtl.h>

/*

  example output:

  1

  */

int
main()
{
  //begin
  mtl::dense1D<float> x(10, 0.0);
  x[5] = 1.0;
  float s = mtl::max(x);
  //end
  std::cout << s << std::endl;
  return 0;
}


//---------------------------------------------------------------------------//
// Copyright (c) 2013-2014 Kyle Lutz <kyle.r.lutz@gmail.com>
//
// Distributed under the Boost Software License, Version 1.0
// See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt
//
// See http://boostorg.github.com/compute for more information.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE TestUniformRealDistribution
#include <boost/test/unit_test.hpp>

#include <boost/compute/system.hpp>
#include <boost/compute/command_queue.hpp>
#include <boost/compute/algorithm/count_if.hpp>
#include <boost/compute/algorithm/transform.hpp>
#include <boost/compute/function.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/random/default_random_engine.hpp>
#include <boost/compute/random/uniform_real_distribution.hpp>
#include <boost/compute/lambda.hpp>
#include <boost/compute/types/fundamental.hpp>

#include "context_setup.hpp"

BOOST_AUTO_TEST_CASE(uniform_real_distribution_doctest)
{
    using boost::compute::lambda::_1;

    boost::compute::vector<float> vec(128, context);

//! [generate]
// initialize the default random engine
boost::compute::default_random_engine engine(queue);

// setup the uniform distribution to produce floats between 1 and 100
boost::compute::uniform_real_distribution<float> distribution(1.0f, 100.0f);

// generate the random values and store them to 'vec'
distribution.generate(vec.begin(), vec.end(), engine, queue);
//! [generate]

    BOOST_CHECK_EQUAL(
        boost::compute::count_if(
            vec.begin(), vec.end(), _1 < 1.0f || _1 >= 100.0f, queue
        ),
        size_t(0)
    );
}

template<class T>
class range_test_engine
{
public:
    explicit range_test_engine(boost::compute::command_queue &queue) {
        (void) queue;
    }

    template<class OutputIterator, class Function>
    void generate(OutputIterator first, OutputIterator last, Function op, boost::compute::command_queue &queue)
    {
        boost::compute::vector<T> tmp(std::distance(first, last), queue.get_context());

        BOOST_COMPUTE_FUNCTION(T, max_random, (const T x),
        {
            if(get_global_id(0) < 1)
                return (ValueType) MAX_RANDOM;
            else
                return (ValueType) 0;
        });

        max_random.define("MAX_RANDOM", "UINT_MAX");
        max_random.define("ValueType", boost::compute::type_name<T>());

        boost::compute::transform(tmp.begin(), tmp.end(), tmp.begin(), max_random, queue);
        boost::compute::transform(tmp.begin(), tmp.end(), first, op, queue);
    }
};

// For checking if result is in the correct range [low, hi)
BOOST_AUTO_TEST_CASE(uniform_real_distribution_range)
{
    using boost::compute::lambda::_1;

    boost::compute::vector<float> vec(32, context);

    // initialize the range_test_engine
    range_test_engine<boost::compute::uint_> engine(queue);

    // setup the uniform distribution to produce floats between 0.9 and 1.0
    boost::compute::uniform_real_distribution<float> distribution(0.9f, 1.0f);

    // generate the random values and store them to 'vec'
    distribution.generate(vec.begin(), vec.end(), engine, queue);

    BOOST_CHECK_EQUAL(
        boost::compute::count_if(
            vec.begin(), vec.end(), _1 < 0.9f || _1 >= 1.0f, queue
        ),
        size_t(0)
    );
}

BOOST_AUTO_TEST_SUITE_END()


//
//	HSPVAR core module
//	onion software/onitama 2003/4
//
#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#define NOMINMAX
#include <Windows.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <memory>
#include "../HSP_Includes/hsp3plugin.h"
#include "../HSP_Includes/hspvar_core.h"
#include "../HSP_Includes/hsp3debug.h"
#include <boost/multiprecision/cpp_int.hpp>
#include "Boost-FixedPoint-Core.h"
int N;

/*------------------------------------------------------------*/
/*
HSPVAR core interface (Boost_Fixedpoint)
*/
/*------------------------------------------------------------*/

#define GetPtr(pval) ((boost_fixedpoint *)pval)
#define sbAlloc hspmalloc
#define sbFree hspfree
static int mytype;
static boost_fixedpoint conv;
static short *aftertype;
static std::string sstr;
static int sint;
static double sdouble;

// Core
static PDAT *HspVarBoost_Fixedpoint_GetPtr(PVal *pval)
{
	return (PDAT *)(((boost_fixedpoint *)(pval->pt)) + pval->offset);
}

static void *HspVarBoost_Fixedpoint_Cnv(const void *buffer, int flag)
{
	//		リクエストされた型 -> 自分の型への変換を行なう
	//		(組み込み型にのみ対応でOK)
	//		(参照元のデータを破壊しないこと)
	//
	switch (flag) {
	case HSPVAR_FLAG_STR:
		conv = boost_fixedpoint(std::string((char*)buffer));
		return &conv;
	case HSPVAR_FLAG_INT:
		conv = (boost_fixedpoint)(*(int *)buffer);
		return &conv;
	case HSPVAR_FLAG_DOUBLE:
		conv = (boost_fixedpoint)(*(double *)buffer);
		break;
	default:
		throw HSPVAR_ERROR_TYPEMISS;
	}
	return (void *)buffer;
}

static void *HspVarBoost_Fixedpoint_CnvCustom(const void *buffer, int flag)
{
	//		(カスタムタイプのみ)
	//		自分の型 -> リクエストされた型 への変換を行なう
	//		(組み込み型に対応させる)
	//		(参照元のデータを破壊しないこと)
	//
	boost_fixedpoint p;
	p = *(boost_fixedpoint *)buffer;
	std::string sdata;
	switch (flag) {
	case HSPVAR_FLAG_STR:
		sstr = p.str();
		return const_cast<char *>(sstr.c_str());
		break;
	case HSPVAR_FLAG_INT:
		sint = static_cast<int>(p);
		return &sint;
		break;
	case HSPVAR_FLAG_DOUBLE:
		sdouble = static_cast<double>(p);
		return &sdouble;
		break;
	default:
		throw HSPVAR_ERROR_TYPEMISS;
	}
	return nullptr;
}

static int GetVarSize(PVal *pval)
{
	//		PVALポインタの変数が必要とするサイズを取得する
	//		(sizeフィールドに設定される)
	//
	int size;
	size = pval->len[1];
	if (pval->len[2]) size *= pval->len[2];
	if (pval->len[3]) size *= pval->len[3];
	if (pval->len[4]) size *= pval->len[4];
	size *= sizeof(boost_fixedpoint);
	return size;
}

static void HspVarBoost_Fixedpoint_Free(PVal *pval)
{
	//		PVALポインタの変数メモリを解放する
	//
	if (pval->mode == HSPVAR_MODE_MALLOC) { sbFree(pval->pt); }
	pval->pt = NULL;
	pval->mode = HSPVAR_MODE_NONE;
}

static void HspVarBoost_Fixedpoint_Alloc(PVal *pval, const PVal *pval2)
{
	//		pval変数が必要とするサイズを確保する。
	//		(pvalがすでに確保されているメモリ解放は呼び出し側が行なう)
	//		(flagの設定は呼び出し側が行なう)
	//		(pval2がNULLの場合は、新規データ)
	//		(pval2が指定されている場合は、pval2の内容を継承して再確保)
	//
	int i, size;
	char *pt;
	boost_fixedpoint *fv;
	if (pval->len[1] < 1) pval->len[1] = 1;		// 配列を最低1は確保する
	size = GetVarSize(pval);
	pval->mode = HSPVAR_MODE_MALLOC;
	pt = sbAlloc(size);
	fv = (boost_fixedpoint *)pt;
	for (i = 0; i < (int)(size / sizeof(boost_fixedpoint)); i++) { fv[i] = 0; }
	if (pval2 != NULL) {
		memcpy(pt, pval->pt, pval->size);
		sbFree(pval->pt);
	}
	pval->pt = pt;
	pval->size = size;
}

/*
static void *HspVarBoost_Fixedpoint_ArrayObject( PVal *pval, int *mptype )
{
//		配列要素の指定 (文字列/連想配列用)
//
throw HSPERR_UNSUPPORTED_FUNCTION;
return NULL;
}
*/

// Size
static int HspVarBoost_Fixedpoint_GetSize(const PDAT *pval)
{
	return sizeof(boost_fixedpoint);
}

// Set
static void HspVarBoost_Fixedpoint_Set(PVal *pval, PDAT *pdat, const void *in)
{
	*GetPtr(pdat) = *((boost_fixedpoint *)(in));
}

// Add
static void HspVarBoost_Fixedpoint_AddI(PDAT *pval, const void *val)
{
	*GetPtr(pval) += *((boost_fixedpoint *)(val));
	*aftertype = mytype;
}

// Sub
static void HspVarBoost_Fixedpoint_SubI(PDAT *pval, const void *val)
{
	*GetPtr(pval) -= *((boost_fixedpoint *)(val));
	*aftertype = mytype;
}

// Mul
static void HspVarBoost_Fixedpoint_MulI(PDAT *pval, const void *val)
{
	*GetPtr(pval) *= *((boost_fixedpoint *)(val));
	*aftertype = mytype;
}

// Div
static void HspVarBoost_Fixedpoint_DivI(PDAT *pval, const void *val)
{
	boost_fixedpoint p = *((boost_fixedpoint *)(val));
	if (p == boost_fixedpoint(0)) throw(HSPVAR_ERROR_DIVZERO);
	*GetPtr(pval) /= p;
	*aftertype = mytype;
}
//// Mod
//static void HspVarBoost_Fixedpoint_ModI(PDAT *pval, const void *val)
//{
//	boost_fixedpoint p = *((boost_fixedpoint *)(val));
//	if (p == 0) throw(HSPVAR_ERROR_DIVZERO);
//	*GetPtr(pval) %= p;
//	*aftertype = mytype;
//}
//// And
//static void HspVarBoost_Fixedpoint_AndI(PDAT *pval, const void *val)
//{
//	boost_fixedpoint p = *((boost_fixedpoint *)(val));
//	if (p == 0) throw(HSPVAR_ERROR_DIVZERO);
//	*GetPtr(pval) &= p;
//	*aftertype = mytype;
//}
//// Or
//static void HspVarBoost_Fixedpoint_OrI(PDAT *pval, const void *val)
//{
//	boost_fixedpoint p = *((boost_fixedpoint *)(val));
//	if (p == 0) throw(HSPVAR_ERROR_DIVZERO);
//	*GetPtr(pval) |= p;
//	*aftertype = mytype;
//}
//// Xor
//static void HspVarBoost_Fixedpoint_XorI(PDAT *pval, const void *val)
//{
//	boost_fixedpoint p = *((boost_fixedpoint *)(val));
//	if (p == 0) throw(HSPVAR_ERROR_DIVZERO);
//	*GetPtr(pval) ^= p;
//	*aftertype = mytype;
//}
//static void HspVarBoost_Fixedpoint_RrI(PDAT *pval, const void *val)
//{
//	boost_fixedpoint p = *((boost_fixedpoint *)(val));
//	if (p == 0) throw(HSPVAR_ERROR_DIVZERO);
//	*GetPtr(pval) >>= static_cast<int>(p);
//	*aftertype = mytype;
//}
//// Xor
//static void HspVarBoost_Fixedpoint_LrI(PDAT *pval, const void *val)
//{
//	boost_fixedpoint p = *((boost_fixedpoint *)(val));
//	if (p == 0) throw(HSPVAR_ERROR_DIVZERO);
//	*GetPtr(pval) <<= static_cast<int>(p);
//	*aftertype = mytype;
//}
// Eq
static void HspVarBoost_Fixedpoint_EqI(PDAT *pval, const void *val)
{
	*((int *)pval) = (*GetPtr(pval) == *((boost_fixedpoint *)(val)));
	*aftertype = HSPVAR_FLAG_INT;
}

// Ne
static void HspVarBoost_Fixedpoint_NeI(PDAT *pval, const void *val)
{
	*((int *)pval) = (*GetPtr(pval) != *((boost_fixedpoint *)(val)));
	*aftertype = HSPVAR_FLAG_INT;
}

// Gt
static void HspVarBoost_Fixedpoint_GtI(PDAT *pval, const void *val)
{
	*((int *)pval) = (*GetPtr(pval) > *((boost_fixedpoint *)(val)));
	*aftertype = HSPVAR_FLAG_INT;
}

// Lt
static void HspVarBoost_Fixedpoint_LtI(PDAT *pval, const void *val)
{
	*((int *)pval) = (*GetPtr(pval) < *((boost_fixedpoint *)(val)));
	*aftertype = HSPVAR_FLAG_INT;
}

// GtEq
static void HspVarBoost_Fixedpoint_GtEqI(PDAT *pval, const void *val)
{
	*((int *)pval) = (*GetPtr(pval) >= *((boost_fixedpoint *)(val)));
	*aftertype = HSPVAR_FLAG_INT;
}

// LtEq
static void HspVarBoost_Fixedpoint_LtEqI(PDAT *pval, const void *val)
{
	*((int *)pval) = (*GetPtr(pval) <= *((boost_fixedpoint *)(val)));
	*aftertype = HSPVAR_FLAG_INT;
}

/*
// INVALID
static void HspVarBoost_Fixedpoint_Invalid( PDAT *pval, const void *val )
{
throw( HSPVAR_ERROR_INVALID );
}
*/

static void *GetBlockSize(PVal *pval, PDAT *pdat, int *size)
{
	*size = pval->size - (((char *)pdat) - pval->pt);
	return (pdat);
}

static void AllocBlock(PVal *pval, PDAT *pdat, int size)
{
}

/*------------------------------------------------------------*/

EXPORT int HspVarBoost_Fixedpoint_typeid(void)
{
	return mytype;
}

EXPORT void HspVarBoost_Fixedpoint_Init(HspVarProc *p)
{
	aftertype = &p->aftertype;

	p->Set = HspVarBoost_Fixedpoint_Set;
	p->Cnv = HspVarBoost_Fixedpoint_Cnv;
	p->GetPtr = HspVarBoost_Fixedpoint_GetPtr;
	p->CnvCustom = HspVarBoost_Fixedpoint_CnvCustom;
	p->GetSize = HspVarBoost_Fixedpoint_GetSize;
	p->GetBlockSize = GetBlockSize;
	p->AllocBlock = AllocBlock;

	//	p->ArrayObject = HspVarBoost_Fixedpoint_ArrayObject;
	p->Alloc = HspVarBoost_Fixedpoint_Alloc;
	p->Free = HspVarBoost_Fixedpoint_Free;

	p->AddI = HspVarBoost_Fixedpoint_AddI;
	p->SubI = HspVarBoost_Fixedpoint_SubI;
	p->MulI = HspVarBoost_Fixedpoint_MulI;
	p->DivI = HspVarBoost_Fixedpoint_DivI;
	//p->ModI = HspVarBoost_Fixedpoint_ModI;

	//p->AndI = HspVarBoost_Fixedpoint_AndI;
	//p->OrI = HspVarBoost_Fixedpoint_OrI;
	//p->XorI = HspVarBoost_Fixedpoint_XorI;

	p->EqI = HspVarBoost_Fixedpoint_EqI;
	p->NeI = HspVarBoost_Fixedpoint_NeI;
	p->GtI = HspVarBoost_Fixedpoint_GtI;
	p->LtI = HspVarBoost_Fixedpoint_LtI;
	p->GtEqI = HspVarBoost_Fixedpoint_GtEqI;
	p->LtEqI = HspVarBoost_Fixedpoint_LtEqI;

	//p->RrI = HspVarBoost_Fixedpoint_RrI;
	//p->LrI = HspVarBoost_Fixedpoint_LrI;

	p->vartype_name = "boost_fixedpoint";				// タイプ名
	p->version = 0x001;					// 型タイプランタイムバージョン(0x100 = 1.0)
	p->support = HSPVAR_SUPPORT_STORAGE | HSPVAR_SUPPORT_FLEXARRAY;
	// サポート状況フラグ(HSPVAR_SUPPORT_*)
	p->basesize = sizeof(boost_fixedpoint);		// １つのデータが使用するサイズ(byte) / 可変長の時は-1
	mytype = p->flag;
}

/*------------------------------------------------------------*/