/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2020 NKI/AVL, Netherlands Cancer Institute
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer
 * 2. 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.
 *
 * 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 OWNER 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.
 */

#pragma once

#include <functional>

#if HAVE_LIBCLIPPER
#include <clipper/core/coords.h>
#endif

#include <boost/math/quaternion.hpp>

namespace mmcif
{

typedef boost::math::quaternion<float>	Quaternion;

const double
	kPI = 3.141592653589793238462643383279502884;

// --------------------------------------------------------------------

//	Point, a location with x, y and z coordinates as floating point.
//	This one is derived from a tuple<float,float,float> so
//	you can do things like:
//
//	float x, y, z;
//	tie(x, y, z) = atom.loc();

template<typename F>
struct PointF
{
	typedef F FType;

	FType mX, mY, mZ;

	PointF()							: mX(0), mY(0), mZ(0) {}
	PointF(FType x, FType y, FType z)	: mX(x), mY(y), mZ(z) {}

	template<typename PF>
	PointF(const PointF<PF>& pt)
		: mX(static_cast<F>(pt.mX))
		, mY(static_cast<F>(pt.mY))
		, mZ(static_cast<F>(pt.mZ)) {}

#if HAVE_LIBCLIPPER
	PointF(const clipper::Coord_orth& pt): mX(pt[0]), mY(pt[1]), mZ(pt[2]) {}

	PointF& operator=(const clipper::Coord_orth& rhs)
	{
		mX = rhs[0];
		mY = rhs[1];
		mZ = rhs[2];
		return *this;
	}
#endif

	template<typename PF>
	PointF& operator=(const PointF<PF>& rhs)
	{
		mX = static_cast<F>(rhs.mX);
		mY = static_cast<F>(rhs.mY);
		mZ = static_cast<F>(rhs.mZ);
		return *this;
	}

	FType& getX()			{ return mX; }
	FType getX() const		{ return mX; }
	void setX(FType x)		{ mX = x; }

	FType& getY()			{ return mY; }
	FType getY() const		{ return mY; }
	void setY(FType y)		{ mY = y; }

	FType& getZ()			{ return mZ; }
	FType getZ() const		{ return mZ; }
	void setZ(FType z)		{ mZ = z; }

	PointF& operator+=(const PointF& rhs)
	{
		mX += rhs.mX;
		mY += rhs.mY;
		mZ += rhs.mZ;

		return *this;
	}

	PointF& operator+=(FType d)
	{
		mX += d;
		mY += d;
		mZ += d;

		return *this;
	}

	PointF& operator-=(const PointF& rhs)
	{
		mX -= rhs.mX;
		mY -= rhs.mY;
		mZ -= rhs.mZ;

		return *this;
	}

	PointF& operator-=(FType d)
	{
		mX -= d;
		mY -= d;
		mZ -= d;

		return *this;
	}

	PointF& operator*=(FType rhs)
	{
		mX *= rhs;
		mY *= rhs;
		mZ *= rhs;
		return *this;
	}

	PointF& operator/=(FType rhs)
	{
		mX /= rhs;
		mY /= rhs;
		mZ /= rhs;
		return *this;
	}

	FType normalize()
	{
		auto length = mX * mX + mY * mY + mZ * mZ;
		if (length > 0)
		{
			length = std::sqrt(length);
			operator/=(length);
		}
		return length;
	}

	void rotate(const boost::math::quaternion<FType>& q)
	{
		boost::math::quaternion<FType> p(0, mX, mY, mZ);

		p = q * p * boost::math::conj(q);

		mX = p.R_component_2();
		mY = p.R_component_3();
		mZ = p.R_component_4();
	}

#if HAVE_LIBCLIPPER
	operator clipper::Coord_orth() const
	{
		return clipper::Coord_orth(mX, mY, mZ);
	}
#endif

	operator std::tuple<const FType&, const FType&, const FType&>() const
	{
		return std::make_tuple(std::ref(mX), std::ref(mY), std::ref(mZ));
	}

	operator std::tuple<FType&,FType&,FType&>()
	{
		return std::make_tuple(std::ref(mX), std::ref(mY), std::ref(mZ));
	}

	bool operator==(const PointF& rhs) const
	{
		return mX == rhs.mX and mY == rhs.mY and mZ == rhs.mZ;
	}

	// consider point as a vector... perhaps I should rename Point?
	FType lengthsq() const
	{
		return mX * mX + mY * mY + mZ * mZ;
	}

	FType length() const
	{
		return sqrt(mX * mX + mY * mY + mZ * mZ);
	}
};

typedef PointF<float> Point;
typedef PointF<double> DPoint;

template<typename F>
inline std::ostream& operator<<(std::ostream& os, const PointF<F>& pt)
{
	os << '(' << pt.mX << ',' << pt.mY << ',' << pt.mZ << ')';
	return os;
}

template<typename F>
inline PointF<F> operator+(const PointF<F>& lhs, const PointF<F>& rhs)
{
	return PointF<F>(lhs.mX + rhs.mX, lhs.mY + rhs.mY, lhs.mZ + rhs.mZ);
}

template<typename F>
inline PointF<F> operator-(const PointF<F>& lhs, const PointF<F>& rhs)
{
	return PointF<F>(lhs.mX - rhs.mX, lhs.mY - rhs.mY, lhs.mZ - rhs.mZ);
}

template<typename F>
inline PointF<F> operator-(const PointF<F>& pt)
{
	return PointF<F>(-pt.mX, -pt.mY, -pt.mZ);
}

template<typename F>
inline PointF<F> operator*(const PointF<F>& pt, F f)
{
	return PointF<F>(pt.mX * f, pt.mY * f, pt.mZ * f);
}

template<typename F>
inline PointF<F> operator*(F f, const PointF<F>& pt)
{
	return PointF<F>(pt.mX * f, pt.mY * f, pt.mZ * f);
}

template<typename F>
inline PointF<F> operator/(const PointF<F>& pt, F f)
{
	return PointF<F>(pt.mX / f, pt.mY / f, pt.mZ / f);
}

// --------------------------------------------------------------------
// several standard 3d operations

template<typename F>
inline double DistanceSquared(const PointF<F>& a, const PointF<F>& b)
{
	return
		(a.mX - b.mX) * (a.mX - b.mX) +
		(a.mY - b.mY) * (a.mY - b.mY) +
		(a.mZ - b.mZ) * (a.mZ - b.mZ);
}

template<typename F>
inline double Distance(const PointF<F>& a, const PointF<F>& b)
{
	return sqrt(
		(a.mX - b.mX) * (a.mX - b.mX) +
		(a.mY - b.mY) * (a.mY - b.mY) +
		(a.mZ - b.mZ) * (a.mZ - b.mZ));
}

template<typename F>
inline F DotProduct(const PointF<F>& a, const PointF<F>& b)
{
	return a.mX * b.mX + a.mY * b.mY + a.mZ * b.mZ;
}

template<typename F>
inline PointF<F> CrossProduct(const PointF<F>& a, const PointF<F>& b)
{
	return PointF<F>(a.mY * b.mZ - b.mY * a.mZ,
				  a.mZ * b.mX - b.mZ * a.mX,
				  a.mX * b.mY - b.mX * a.mY);
}

template<typename F>
double Angle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3)
{
	PointF<F> v1 = p1 - p2;
	PointF<F> v2 = p3 - p2;

	return std::acos(DotProduct(v1, v2) / (v1.length() * v2.length())) * 180 / kPI;
}

template<typename F>
double DihedralAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3, const PointF<F>& p4)
{
	PointF<F> v12 = p1 - p2;	// vector from p2 to p1
	PointF<F> v43 = p4 - p3;	// vector from p3 to p4

	PointF<F> z = p2 - p3;		// vector from p3 to p2

	PointF<F> p = CrossProduct(z, v12);
	PointF<F> x = CrossProduct(z, v43);
	PointF<F> y = CrossProduct(z, x);

	double u = DotProduct(x, x);
	double v = DotProduct(y, y);

	double result = 360;
	if (u > 0 and v > 0)
	{
		u = DotProduct(p, x) / sqrt(u);
		v = DotProduct(p, y) / sqrt(v);
		if (u != 0 or v != 0)
			result = atan2(v, u) * 180 / kPI;
	}

	return result;
}

template<typename F>
double CosinusAngle(const PointF<F>& p1, const PointF<F>& p2, const PointF<F>& p3, const PointF<F>& p4)
{
	PointF<F> v12 = p1 - p2;
	PointF<F> v34 = p3 - p4;

	double result = 0;

	double x = DotProduct(v12, v12) * DotProduct(v34, v34);
	if (x > 0)
		result = DotProduct(v12, v34) / sqrt(x);

	return result;
}

template<typename F>
auto DistancePointToLine(const PointF<F> &l1, const PointF<F> &l2, const PointF<F> &p)
{
	auto line       = l2 - l1;
    auto p_to_l1    = p - l1;
    auto p_to_l2    = p - l2;
    auto cross      = CrossProduct(p_to_l1, p_to_l2);
    return cross.length() / line.length();
}

// --------------------------------------------------------------------
// For e.g. simulated annealing, returns a new point that is moved in
// a random direction with a distance randomly chosen from a normal
// distribution with a stddev of offset.

template<typename F>
PointF<F> Nudge(PointF<F> p, F offset);

// --------------------------------------------------------------------
// We use quaternions to do rotations in 3d space

Quaternion Normalize(Quaternion q);

std::tuple<double,Point> QuaternionToAngleAxis(Quaternion q);
Point Centroid(std::vector<Point>& Points);
Point CenterPoints(std::vector<Point>& Points);
Quaternion AlignPoints(const std::vector<Point>& a, const std::vector<Point>& b);
double RMSd(const std::vector<Point>& a, const std::vector<Point>& b);

// --------------------------------------------------------------------
// Helper class to generate evenly divided Points on a sphere
// we use a fibonacci sphere to calculate even distribution of the dots

template<int N>
class SphericalDots
{
  public:
	enum { P = 2 * N + 1 };
	typedef typename std::array<Point,P>	array_type;
	typedef typename array_type::const_iterator	iterator;

	static SphericalDots& instance()
	{
		static SphericalDots sInstance;
		return sInstance;
	}

	size_t size() const							{ return mPoints.size(); }
	const Point operator[](uint32_t inIx) const	{ return mPoints[inIx]; }
	iterator begin() const						{ return mPoints.begin(); }
	iterator end() const						{ return mPoints.end(); }

	double weight() const						{ return mWeight; }

	SphericalDots()
	{

		const double
			kGoldenRatio = (1 + std::sqrt(5.0)) / 2;

		mWeight = (4 * kPI) / P;

		auto p = mPoints.begin();

		for (int32_t i = -N; i <= N; ++i)
		{
			double lat = std::asin((2.0 * i) / P);
			double lon = std::fmod(i, kGoldenRatio) * 2 * kPI / kGoldenRatio;

			p->mX = sin(lon) * cos(lat);
			p->mY = cos(lon) * cos(lat);
			p->mZ =            sin(lat);

			++p;
		}
	}

  private:

	array_type				mPoints;
	double					mWeight;
};

typedef SphericalDots<50> SphericalDots_50;

}


//#include <dirent.h>
//#include <iostream>
//#include <vector>
//#include <string>
//#include <utility>
//
//#include <Eigen/Core>
//#include <Eigen/LU>
//#include <Eigen/Geometry>
//
//#include <opencv2/opencv.hpp>
//#define pi acos(-1)
//
//const int G_land_num = 74;
//const int G_train_pic_id_num = 3300;
//const int G_nShape = 47;
//const int G_nVerts = 11510;
//const int G_nFaces = 11540;
//const int G_test_num = 77;
//const int G_iden_num = 77;
//const int G_inner_land_num = 59;
//const int G_line_num = 50;
//const int G_jaw_land_num = 20;
//#define normalization
//struct Target_type {
//	Eigen::VectorXf exp;
//	Eigen::RowVector3f tslt;
//	Eigen::Matrix3f rot;
//	Eigen::MatrixX2f dis;
//	Eigen::Vector3f angle;
//};
//
//struct DataPoint
//{
//	cv::Mat image;
//	cv::Rect face_rect;
//	std::vector<cv::Point2d> landmarks;
//	//std::vector<cv::Point2d> init_shape;
//	Target_type shape, init_shape;
//	Eigen::VectorXf user;
//	Eigen::RowVector2f center;
//	Eigen::MatrixX2f land_2d;
//#ifdef posit
//	float f;
//#endif // posit
//#ifdef normalization
//	Eigen::MatrixX3f s;
//#endif
//
//	Eigen::VectorXi land_cor;
//};
//
//
//void load_lv(std::string name, DataPoint &temp) {
//	std::cout << "load coefficients...file:" << name << "\n";
//	FILE *fp;
//	fopen_s(&fp, name.c_str(), "rb");
//
//	temp.user.resize(G_iden_num);
//	for (int j = 0; j < G_iden_num; j++)
//		fread(&temp.user(j), sizeof(float), 1, fp);
//	std::cout << temp.user << "\n";
//	//system("pause");
//	temp.land_2d.resize(G_land_num, 2);
//	for (int i_v = 0; i_v < G_land_num; i_v++) {
//		fread(&temp.land_2d(i_v, 0), sizeof(float), 1, fp);
//		fread(&temp.land_2d(i_v, 1), sizeof(float), 1, fp);
//	}
//
//
//	fread(&temp.center(0), sizeof(float), 1, fp);
//	fread(&temp.center(1), sizeof(float), 1, fp);
//
//	temp.shape.exp.resize(G_nShape);
//	for (int i_shape = 0; i_shape < G_nShape; i_shape++)
//		fread(&temp.shape.exp(i_shape), sizeof(float), 1, fp);
//
//	for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++)
//		fread(&temp.shape.rot(i, j), sizeof(float), 1, fp);
//
//	for (int i = 0; i < 3; i++) fread(&temp.shape.tslt(i), sizeof(float), 1, fp);
//
//	temp.land_cor.resize(G_land_num);
//	for (int i_v = 0; i_v < G_land_num; i_v++) fread(&temp.land_cor(i_v), sizeof(int), 1, fp);
//
//	temp.s.resize(2, 3);
//	for (int i = 0; i < 2; i++) for (int j = 0; j < 3; j++)
//		fread(&temp.s(i, j), sizeof(float), 1, fp);
//
//	temp.shape.dis.resize(G_land_num, 2);
//	for (int i_v = 0; i_v < G_land_num; i_v++) {
//		fread(&temp.shape.dis(i_v, 0), sizeof(float), 1, fp);
//		fread(&temp.shape.dis(i_v, 1), sizeof(float), 1, fp);
//	}
//	std::cout << temp.shape.dis << "\n";
//	//system("pause");
//	fclose(fp);
//	puts("load successful!");
//}
//
//
////assume the be could not be more than 90
//void cal_uler_angle(Eigen::Matrix3f R) {
//	Eigen::Vector3f x, y, z,t;
//	x = R.row(0).transpose();
//	y = R.row(1).transpose();
//	z = R.row(2).transpose();
//	float al, be, ga, gaw;
//	if (fabs(1 - z(2)*z(2)) < 1e-3) {
//		ga=gaw=be = 0;
//		al = acos(x(0));
//		if (y(0) < 0) al = 2 * pi - al;
//	}
//	else {
//
//		be = acos(z(2));
//		al = acos(std::max(std::min(float(1.0),z(1) / sqrt(1 - z(2)*z(2))),float(-1.0)));
//
//		if (z(0) < 0) al = 2 * pi - al;//according to the sin(al)
//
//
//		t(0) = cos(al), t(1) = sin(al), t(2) = 0;
//		t.normalize();
//		x.normalize();
//		//t.normalized();
//		ga = acos(t.dot(x));
//		gaw = acos(std::max(std::min(float(1.0), -y(2) / sqrt(1 - z(2)*z(2))), float(-1.0)));
//
//		printf("%.10f %.10f %.10f\n", -y(2), sqrt(1 - z(2)*z(2)), -y(2) / sqrt(1 - z(2)*z(2)));
//		if (x(2) < 0) ga = 2 * pi - ga, gaw = 2 * pi - gaw;//according to the sin(ga)
//	}
//	std::cout << R << "\n----------------------\n";
//	printf("%.10f %.10f %.10f %.10f %.10f\n",z(2), al/pi*180, be / pi * 180, ga / pi * 180, gaw / pi * 180);
//	system("pause");
//}
//
//Eigen::Matrix3f get_r_from_angle(float angle, int axis) {
//	Eigen::Matrix3f ans;
//	ans.setZero();
//	ans(axis, axis) = 1;
//	int idx_x = 0, idx_y = 1;
//	if (axis == 0)
//		idx_x = 1, idx_y = 2;
//	else
//		if (axis == 2)
//			idx_x = 0, idx_y = 1;
//		else
//			idx_x = 0, idx_y = 2;
//	ans(idx_x, idx_x) = cos(angle), ans(idx_x, idx_y) = -sin(angle), ans(idx_y, idx_x) = sin(angle), ans(idx_y, idx_y) = cos(angle);
//	return ans;
//}
//
//
//Eigen::Matrix3f get_r_from_angle(const Eigen::Vector3f &angle) {
//	Eigen::Matrix3f ans;
//	float Sa = sin(angle(0)), Ca = cos(angle(0)), Sb = sin(angle(1)),
//		Cb = cos(angle(1)), Sc = sin(angle(2)), Cc = cos(angle(2));
//
//	ans(0, 0) = Ca * Cc - Sa * Cb*Sc;
//	ans(0, 1) = -Sa * Cc - Ca * Cb*Sc;
//	ans(0, 2) = Sb * Sc;
//	ans(1, 0) = Ca * Sc + Sa * Cb*Cc;
//	ans(1, 1) = -Sa * Sc + Ca * Cb*Cc;
//	ans(1, 2) = -Sb * Cc;
//	ans(2, 0) = Sa * Sb;
//	ans(2, 1) = Ca * Sb;
//	ans(2, 2) = Cb;
//	return ans;
//}
//
////assume the be could not be more than 90
//Eigen::Vector3f cal_uler_angle_zyx(Eigen::Matrix3f R) {
//	Eigen::Vector3f x, y, z, t;
//	x = R.row(0).transpose();
//	y = R.row(1).transpose();
//	z = R.row(2).transpose();
//	float al, be, ga;
//	if (fabs(1 - x(2)*x(2)) < 1e-3) {
//		be = asin(x(2));
//		al = ga = 0;
//		exit(1);
//	}
//	else {
//
//		be = asin(std::max(std::min(1.0, double(x(2))), -1.0));
//		al = asin(std::max(std::min(1.0, double(-x(1) / sqrt(1 - x(2)*x(2)))), -1.0));
//		ga = asin(std::max(std::min(1.0, double(-y(2) / sqrt(1 - x(2)*x(2)))), -1.0));
//
//	}
//	std::cout << R << "\n----------------------\n";
//	printf("%.10f %.10f %.10f %.10f\n", x(2), al / pi * 180, be / pi * 180, ga / pi * 180);
//	Eigen::Vector3f ans;
//	ans << al, be, ga;
//	return ans;
//	//system("pause");
//}
//
//Eigen::Matrix3f get_r_from_angle_zyx(const Eigen::Vector3f &angle) {
//	Eigen::Matrix3f ans;
//	float Sa = sin(angle(0)), Ca = cos(angle(0)), Sb = sin(angle(1)),
//		Cb = cos(angle(1)), Sc = sin(angle(2)), Cc = cos(angle(2));
//
//	ans(0, 0) = Ca * Cb;
//	ans(0, 1) = -Sa * Cb;
//	ans(0, 2) = Sb;
//	ans(1, 0) = Sa * Cc + Ca * Sb*Sc;
//	ans(1, 1) = Ca * Cc - Sa * Sb*Sc;
//	ans(1, 2) = -Cb * Sc;
//	ans(2, 0) = Sa * Sc - Ca * Sb*Cc;
//	ans(2, 1) = Ca * Sc + Sa * Sb*Cc;
//	ans(2, 2) = Cb * Cc;
//	return ans;
//}
//
//
//
//
//void test_r(DataPoint data) {
//	Eigen::Matrix3f rot;
//	//rot = get_r_from_angle(data.shape.tslt(2), 2)*get_r_from_angle(data.shape.tslt(1), 0)*get_r_from_angle(data.shape.tslt(0), 2);
//	//std::cout << rot << "\n";
//	std::cout << get_r_from_angle_zyx(data.shape.angle) << "\n";
//	system("pause");
//}
//
//int main() {
//
//	DataPoint data;
//	/*data.shape.angle << 1, 20, 0.5;
//	test_r(data);*/
//
//
//	load_lv("data/lv_mp4_1.lv",data);//data/test_debug_lv_005_04_03_051_05
//	//lv_mp4_1.
//	data.shape.angle=cal_uler_angle_zyx(data.shape.rot);
//	test_r(data);
//	return 0;
//}
////g++ -Wall -std=c++11 `pkg-config --cflags opencv` -o deal deal_falut.cpp `pkg-config --libs opencv`


#include <stdio.h>
#include <cfloat>
#include <list>
#include <vector>
#include <math.h>
#include <iostream>
#include <algorithm>
#include <numeric>
#include <boost/algorithm/clamp.hpp>
#include <random>
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "unsupported/Eigen/CXX11/src/Tensor/TensorDeviceCuda.h"
//#include "unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/framework/common_shape_fns.h"
#include "tensorflow/core/util/work_sharder.h"
#include "bboxes.h"
#include "bboxes_encode.h"
#include "wtoolkit.h"
#include "wtoolkit_cuda.h"
#include <future>

using namespace tensorflow;
using namespace std;

typedef Eigen::ThreadPoolDevice CPUDevice;
typedef Eigen::GpuDevice GPUDevice;
/*
 * scale_weights:[4](基意义与BoxesEncode的prio_scaling互为倒数
 * boxes:[1,X,4]/[batch_size,X,4](ymin,xmin,ymax,xmax) 候选box,相对坐标
 * gboxes:[batch_size,Y,4](ymin,xmin,ymax,xmax)ground truth box相对坐标
 * labels:[batch_size,X] 0为背景,-1表示忽略
 * indices:[batch_size,X] 与box相对应的gtbox索引
 * output_boxes:[batch_size,X,4] regs(cy,cx,h,w)
 */
REGISTER_OP("GetBoxesDeltas")
    .Attr("T: {float,double,int32,int64}")
 	.Attr("scale_weights: list(float)")
    .Input("boxes: T")
    .Input("gboxes: T")
    .Input("labels: int32")
    .Input("indices: int32")
	.Output("output_boxes:T")
	.SetShapeFn([](shape_inference::InferenceContext* c) {
			const auto batch_size = c->Dim(c->input(1),0);
			const auto box_nr = c->Dim(c->input(0),1);
			const auto box_dim = c->Dim(c->input(0),2);
            auto shape0 = c->MakeShape({batch_size,box_nr,box_dim});

			c->set_output(0, shape0);
			return Status::OK();
			});

template <typename Device, typename T>
class GetBoxesDeltasOp: public OpKernel {
	public:
		using tensor_2d = Eigen::Tensor<T,2,Eigen::RowMajor>;
	public:
		explicit GetBoxesDeltasOp(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("scale_weights", &scale_weights));
			OP_REQUIRES(context, scale_weights.size() == 4, errors::InvalidArgument("scale_weights data must be shape[4]"));
		}

		void Compute(OpKernelContext* context) override
		{
			const Tensor &_bottom_boxes   = context->input(0);
			const Tensor &_bottom_gboxes  = context->input(1);
			const Tensor &_bottom_labels  = context->input(2);
			const Tensor &_bottom_indices = context->input(3);

			OP_REQUIRES(context, _bottom_boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_gboxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_labels.dims() == 2, errors::InvalidArgument("labels data must be 2-dimensional"));
			OP_REQUIRES(context, _bottom_indices.dims() == 2, errors::InvalidArgument("index data must be 1-dimensional"));

			auto          bottom_boxes    = _bottom_boxes.template tensor<T,3>();
			auto          bottom_gboxes   = _bottom_gboxes.template tensor<T,3>();
			auto          bottom_labels   = _bottom_labels.template tensor<int,2>();
			auto          bottom_indices  = _bottom_indices.template tensor<int,2>();
			const auto    batch_size      = _bottom_gboxes.dim_size(0);
			const auto    data_nr         = _bottom_boxes.dim_size(1);

			int dims_3d[] = {batch_size,data_nr,_bottom_boxes.dim_size(2)};
			TensorShape   outshape0 = _bottom_boxes.shape();
			Tensor       *output_boxes          = NULL;

			TensorShapeUtils::MakeShape(dims_3d, 3, &outshape0);
			OP_REQUIRES_OK(context, context->allocate_output(0, outshape0, &output_boxes));

			auto output_boxes_tensor          =  output_boxes->template tensor<T,3>();

			output_boxes_tensor.setZero();

            auto shard = [&](int64 start,int64 limit){
				for(auto i=start; i<limit; ++i) {

					auto boxes   = tensor_2d(bottom_boxes.chip(bottom_boxes.dimension(0)==batch_size?i:0,0));
					auto gboxes  = tensor_2d(bottom_gboxes.chip(i,0));
					/*
					 * 计算所有正样本gbox到所对应的box的回归参数
					 */
					for(auto j=0; j<data_nr; ++j) {
						if((bottom_labels(i,j)<1)) {
							continue;
						}
						Eigen::Tensor<T,1,Eigen::RowMajor> box     = boxes.chip(j,0);
						Eigen::Tensor<T,1,Eigen::RowMajor> gbox    = gboxes.chip(bottom_indices(i,j),0);
						auto  yxhw    = box_minmax_to_cxywh(box.data());
						auto  yref    = std::get<0>(yxhw);
						auto  xref    = std::get<1>(yxhw);
						auto  href    = std::get<2>(yxhw);
						auto  wref    = std::get<3>(yxhw);

						if((href<1E-8) || (wref<1E-8)) {
							continue;
						}

						auto gyxhw = box_minmax_to_cxywh(gbox.data());
						auto gcy =  std::get<0>(gyxhw);
						auto gcx =  std::get<1>(gyxhw);
						auto gh  =  std::get<2>(gyxhw);
						auto gw  =  std::get<3>(gyxhw);
						auto feat_cy  =  scale_weights[0]*(gcy-yref)/href;
						auto feat_cx  =  scale_weights[1]*(gcx-xref)/wref;
						auto feat_h   =  log(gh/href)*scale_weights[2];
						auto feat_w   =  log(gw/wref)*scale_weights[3];

						output_boxes_tensor(i,j,0) = feat_cy;
						output_boxes_tensor(i,j,1) = feat_cx;
						output_boxes_tensor(i,j,2) = feat_h;
						output_boxes_tensor(i,j,3) = feat_w;
					}
				}
            };
            list<future<void>> results;
            for(auto i=0; i<batch_size; ++i) {
                results.emplace_back(async(launch::async,[i,&shard](){ shard(i,i+1);}));
            }
        }
	private:
		vector<float> scale_weights;
};
REGISTER_KERNEL_BUILDER(Name("GetBoxesDeltas").Device(DEVICE_CPU).TypeConstraint<float>("T"), GetBoxesDeltasOp<CPUDevice, float>);
///////////
/*
 * prio_scaling:[4]
 * max_overlap_as_pos:是否将与ground truth bbox交叉面积最大的box设置为正样本
 * bottom_boxes:[1,X,4]/[batch_size,X,4](ymin,xmin,ymax,xmax) 候选box,相对坐标
 * bottom_gboxes:[batch_size,Y,4](ymin,xmin,ymax,xmax)ground truth box相对坐标
 * bottom_glabels:[batch_size,Y] 0为背景
 * bottom_glength:[batch_size] 为每一个batch中gboxes的有效数量
 * output_boxes:[batch_size,X,4] regs(cy,cx,h,w)
 * output_labels:[batch_size,X], 当前anchorbox的标签，背景为0,不为背景时为相应最大jaccard得分
 * output_scores:[batch_size,X], 当前anchorbox与groundtruthbox的jaccard得分，当jaccard得分高于threshold时就不为背影
 * output_remove_indict:[batch_size,X], anchorbox是否有效(一般为iou处理中间部分的无效)
 * output_indict:[batch_size,X], 当anchorbox有效时，与它对应的gboxes(从0开始)序号,无效时为-1
 */
REGISTER_OP("BoxesEncode")
    .Attr("T: {float,double,int32,int64}")
	.Attr("pos_threshold:float")
	.Attr("neg_threshold:float")
 	.Attr("prio_scaling: list(float)")
    .Attr("max_overlap_as_pos:bool")
    .Input("bottom_boxes: T")
    .Input("bottom_gboxes: T")
    .Input("bottom_glabels: int32")
    .Input("bottom_glength: int32")
	.Output("output_boxes:T")
	.Output("output_labels:int32")
	.Output("output_scores:T")
	.Output("remove_indict:bool")
	.Output("indict:int32")
	.SetShapeFn([](shape_inference::InferenceContext* c) {
            const auto input_shape0 = c->input(0);
            const auto input_shape1 = c->input(1);
            const auto batch_size = c->Dim(input_shape1,0);
            const auto boxes_nr  = c->Dim(input_shape0,1);
            auto shape0 = c->MakeShape({batch_size,boxes_nr,4});
            auto shape1 = c->MakeShape({batch_size,boxes_nr});

			c->set_output(0, shape0);
            for(auto i=1; i<5; ++i)
			    c->set_output(i, shape1);
			return Status::OK();
			});

template <typename Device, typename T>
class BoxesEncodeOp: public OpKernel {
};
template <typename T>
class BoxesEncodeOp<CPUDevice,T>: public OpKernel {
	public:
		explicit BoxesEncodeOp(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("pos_threshold", &pos_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("neg_threshold", &neg_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("prio_scaling", &prio_scaling));
			OP_REQUIRES_OK(context, context->GetAttr("max_overlap_as_pos", &max_overlap_as_pos_));
			OP_REQUIRES(context, prio_scaling.size() == 4, errors::InvalidArgument("prio scaling data must be shape[4]"));
		}

		void Compute(OpKernelContext* context) override
		{
            TIME_THISV1("BoxesEncode");
			const Tensor &_bottom_boxes   = context->input(0);
			const Tensor &_bottom_gboxes  = context->input(1);
			const Tensor &_bottom_glabels = context->input(2);
			const Tensor &_bottom_gsize   = context->input(3);
			auto          bottom_boxes    = _bottom_boxes.template tensor<T,3>();
			auto          bottom_gboxes   = _bottom_gboxes.template tensor<T,3>();
			auto          bottom_glabels  = _bottom_glabels.template tensor<int,2>();
			auto          bottom_gsize    = _bottom_gsize.template tensor<int,1>();
			const auto    batch_size      = _bottom_gboxes.dim_size(0);
			const auto    data_nr         = _bottom_boxes.dim_size(1);

			OP_REQUIRES(context, _bottom_boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_gboxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_glabels.dims() == 2, errors::InvalidArgument("labels data must be 2-dimensional"));
			OP_REQUIRES(context, _bottom_gsize.dims() == 1, errors::InvalidArgument("gsize data must be 1-dimensional"));

			int           dims_3d[3]            = {int(batch_size),int(data_nr),4};
			int           dims_2d[2]            = {int(batch_size),int(data_nr)};
			TensorShape   outshape0;
			TensorShape   outshape1;
			Tensor       *output_boxes          = NULL;
			Tensor       *output_labels         = NULL;
			Tensor       *output_scores         = NULL;
			Tensor       *output_remove_indict  = NULL;
			Tensor       *output_indict         = NULL;

			TensorShapeUtils::MakeShape(dims_3d, 3, &outshape0);
			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape1);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape0, &output_boxes));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape1, &output_labels));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape1, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(3, outshape1, &output_remove_indict));
			OP_REQUIRES_OK(context, context->allocate_output(4, outshape1, &output_indict));

			auto output_boxes_tensor          =  output_boxes->template tensor<T,3>();
			auto output_labels_tensor         =  output_labels->template tensor<int,2>();
			auto output_scores_tensor         =  output_scores->template tensor<T,2>();
			auto output_remove_indict_tensor  =  output_remove_indict->template tensor<bool,2>();
			auto output_indict_tensor         =  output_indict->template tensor<int,2>();

            BoxesEncodeUnit<CPUDevice,T> encode_unit(pos_threshold,neg_threshold,prio_scaling,max_overlap_as_pos_);
            auto shard = [&](int64 start,int64 limit){
                for(auto i=start; i<limit; ++i) {

                    auto size     = bottom_gsize(i);
                    auto boxes    = bottom_boxes.chip(bottom_boxes.dimension(0)==batch_size?i:0,0);
                    auto _gboxes  = bottom_gboxes.chip(i,0);
                    auto _glabels = bottom_glabels.chip(i,0);
                    Eigen::array<long,2> offset={0,0};
                    Eigen::array<long,2> extents={size,4};
                    Eigen::array<long,1> offset1={0};
                    Eigen::array<long,1> extents1={size};
                    auto gboxes             = _gboxes.slice(offset,extents);
                    auto glabels            = _glabels.slice(offset1,extents1);
                    auto out_boxes          = output_boxes_tensor.chip(i,0);
                    auto out_labels         = output_labels_tensor.chip(i,0);
                    auto out_scores         = output_scores_tensor.chip(i,0);
                    auto out_remove_indices = output_remove_indict_tensor.chip(i,0);
                    auto out_indices        = output_indict_tensor.chip(i,0);
                    auto res                = encode_unit(boxes,gboxes,glabels);

                    out_boxes           =  std::get<0>(res);
                    out_labels          =  std::get<1>(res);
                    out_scores          =  std::get<2>(res);
                    out_remove_indices  =  std::get<3>(res);
                    out_indices         =  std::get<4>(res);
                }
            };
            list<future<void>> results;
            for(auto i=0; i<batch_size; ++i) {
                results.emplace_back(async(launch::async,[i,&shard](){ shard(i,i+1);}));
            }
        }
	private:
		float         pos_threshold;
		float         neg_threshold;
		vector<float> prio_scaling;
        bool          max_overlap_as_pos_ = true;
};
#ifdef GOOGLE_CUDA
template <typename T>
class BoxesEncodeOp<GPUDevice,T>: public OpKernel {
	public:
		explicit BoxesEncodeOp(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("pos_threshold", &pos_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("neg_threshold", &neg_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("prio_scaling", &prio_scaling));
			OP_REQUIRES_OK(context, context->GetAttr("max_overlap_as_pos", &max_overlap_as_pos_));
			OP_REQUIRES(context, prio_scaling.size() == 4, errors::InvalidArgument("prio scaling data must be shape[4]"));
		}

		void Compute(OpKernelContext* context) override
		{
            TIME_THISV1("BoxesEncodeGPU");
			const Tensor &_bottom_boxes   = context->input(0);
			const Tensor &_bottom_gboxes  = context->input(1);
			const Tensor &_bottom_glabels = context->input(2);
			const Tensor &_bottom_gsize   = context->input(3);
			auto          bottom_boxes    = _bottom_boxes.template tensor<T,3>();
			auto          bottom_gboxes   = _bottom_gboxes.template tensor<T,3>();
			auto          bottom_glabels  = _bottom_glabels.template tensor<int,2>();
			auto          d_bottom_gsize  = _bottom_gsize.template tensor<int,1>();
			const auto    batch_size      = _bottom_gboxes.dim_size(0);
			const auto    data_nr         = _bottom_boxes.dim_size(1);
		    Eigen::Tensor<int,1,Eigen::RowMajor> bottom_gsize;
            assign_tensor<CPUDevice,GPUDevice>(bottom_gsize,d_bottom_gsize);

			OP_REQUIRES(context, _bottom_boxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_gboxes.dims() == 3, errors::InvalidArgument("box data must be 3-dimensional"));
			OP_REQUIRES(context, _bottom_glabels.dims() == 2, errors::InvalidArgument("labels data must be 2-dimensional"));
			OP_REQUIRES(context, _bottom_gsize.dims() == 1, errors::InvalidArgument("gsize data must be 1-dimensional"));

			int           dims_3d[3]            = {int(batch_size),int(data_nr),4};
			int           dims_2d[2]            = {int(batch_size),int(data_nr)};
			TensorShape   outshape0;
			TensorShape   outshape1;
			Tensor       *output_boxes          = NULL;
			Tensor       *output_labels         = NULL;
			Tensor       *output_scores         = NULL;
			Tensor       *output_remove_indict  = NULL;
			Tensor       *output_indict         = NULL;

			TensorShapeUtils::MakeShape(dims_3d, 3, &outshape0);
			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape1);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape0, &output_boxes));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape1, &output_labels));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape1, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(3, outshape1, &output_remove_indict));
			OP_REQUIRES_OK(context, context->allocate_output(4, outshape1, &output_indict));

			auto output_boxes_tensor          =  output_boxes->template tensor<T,3>();
			auto output_labels_tensor         =  output_labels->template tensor<int,2>();
			auto output_scores_tensor         =  output_scores->template tensor<T,2>();
			auto output_remove_indict_tensor  =  output_remove_indict->template tensor<bool,2>();
			auto output_indict_tensor         =  output_indict->template tensor<int,2>();

            BoxesEncodeUnit<GPUDevice,T> encode_unit(pos_threshold,neg_threshold,prio_scaling,max_overlap_as_pos_);
            for(auto i=0; i<batch_size; ++i) {

                    auto size    = bottom_gsize(i);
                    auto boxes   = bottom_boxes.dimension(0)==batch_size?chip_data(bottom_boxes,i):chip_data(bottom_boxes,0);
                    auto gboxes  = chip_data(bottom_gboxes,i);
                    auto glabels = chip_data(bottom_glabels,i);
                    encode_unit(boxes,gboxes,glabels,
                            chip_data(output_boxes_tensor,i),
                            chip_data(output_labels_tensor,i),
                            chip_data(output_scores_tensor,i),
                            chip_data(output_remove_indict_tensor,i),
                            chip_data(output_indict_tensor,i),
                            size,bottom_boxes.dimension(1)
                            );
                }
        }
	private:
		float         pos_threshold;
		float         neg_threshold;
		vector<float> prio_scaling;
        bool          max_overlap_as_pos_ = true;
};
#endif
REGISTER_KERNEL_BUILDER(Name("BoxesEncode").Device(DEVICE_CPU).TypeConstraint<float>("T"), BoxesEncodeOp<CPUDevice, float>);
#ifdef GOOGLE_CUDA
REGISTER_KERNEL_BUILDER(Name("BoxesEncode").Device(DEVICE_GPU).TypeConstraint<float>("T"), BoxesEncodeOp<GPUDevice, float>);
#endif
/*
 * prio_scaling:[4]
 * bottom_boxes:[X,4](ymin,xmin,ymax,xmax) 候选box,相对坐标
 * bottom_gboxes:[Y,4](ymin,xmin,ymax,xmax)ground truth box相对坐标
 * bottom_glabels:[Y] 0为背景
 * output_boxes:[X,4] regs(cy,cx,h,w)
 * output_labels:[X], 当前anchorbox的标签，背景为0,不为背景时为相应最大jaccard得分
 * output_scores:[X], 当前anchorbox与groundtruthbox的jaccard得分，当jaccard得分高于threshold时就不为背影
 */
REGISTER_OP("BoxesEncode1")
    .Attr("T: {float,double,int32,int64}")
	.Attr("pos_threshold:float")
	.Attr("neg_threshold:float")
 	.Attr("prio_scaling: list(float)")
 	.Attr("max_overlap_as_pos: bool")
    .Input("bottom_boxes: T")
    .Input("bottom_gboxes: T")
    .Input("bottom_glabels: int32")
	.Output("output_boxes:T")
	.Output("output_labels:int32")
	.Output("output_scores:T")
	.Output("remove_indict:bool")
    .SetShapeFn([](shape_inference::InferenceContext* c){
            auto shape0 = c->input(0);
            auto shape1 = c->Vector(c->Dim(shape0,0));

            c->set_output(0,shape0);

            for(auto i=1; i<4; ++i) c->set_output(i,shape1);

            return Status::OK();
            });

template <typename Device, typename T>
class BoxesEncode1Op{
};
template <typename T>
class BoxesEncode1Op<CPUDevice,T>: public OpKernel {
	public:
        struct IOUIndex{
            int index;
            float iou;
        };
		explicit BoxesEncode1Op(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("pos_threshold", &pos_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("neg_threshold", &neg_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("prio_scaling", &prio_scaling));
			OP_REQUIRES_OK(context, context->GetAttr("max_overlap_as_pos", &max_overlap_as_pos_));
			OP_REQUIRES(context, prio_scaling.size() == 4, errors::InvalidArgument("prio scaling data must be shape[4]"));
		}

		void Compute(OpKernelContext* context) override
		{
            TIME_THISV1("BoxesEncode1");
			const Tensor &_bottom_boxes   = context->input(0);
			const Tensor &_bottom_gboxes  = context->input(1);
			const Tensor &_bottom_glabels = context->input(2);
			auto          bottom_boxes    = _bottom_boxes.template tensor<T,2>();
			auto          bottom_gboxes   = _bottom_gboxes.template tensor<T,2>();
			auto          bottom_glabels  = _bottom_glabels.template tensor<int,1>();
			const auto    data_nr         = _bottom_boxes.dim_size(0);

			OP_REQUIRES(context, _bottom_boxes.dims() == 2, errors::InvalidArgument("box data must be 2-dimensional"));
			OP_REQUIRES(context, _bottom_gboxes.dims() == 2, errors::InvalidArgument("box data must be 2-dimensional"));
			OP_REQUIRES(context, _bottom_glabels.dims() == 1, errors::InvalidArgument("labels data must be 1-dimensional"));

			int           dims_2d[2]            = {int(data_nr),4};
			int           dims_1d[1]            = {int(data_nr)};
			TensorShape   outshape0;
			TensorShape   outshape1;
			Tensor       *output_boxes          = NULL;
			Tensor       *output_labels         = NULL;
			Tensor       *output_scores         = NULL;
			Tensor       *output_remove_indict  = NULL;
			vector<IOUIndex>   iou_indexs(data_nr,IOUIndex({-1,0.0})); //默认box不与任何ground truth box相交，iou为0

			TensorShapeUtils::MakeShape(dims_2d, 2, &outshape0);
			TensorShapeUtils::MakeShape(dims_1d, 1, &outshape1);

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape0, &output_boxes));
			OP_REQUIRES_OK(context, context->allocate_output(1, outshape1, &output_labels));
			OP_REQUIRES_OK(context, context->allocate_output(2, outshape1, &output_scores));
			OP_REQUIRES_OK(context, context->allocate_output(3, outshape1, &output_remove_indict));
			auto output_boxes_tensor         = output_boxes->template tensor<T,2>();
			auto output_labels_tensor        = output_labels->template tensor<int,1>();
			auto output_scores_tensor        = output_scores->template tensor<T,1>();
			auto output_remove_indict_tensor = output_remove_indict->template tensor<bool,1>();

			BoxesEncodeUnit<CPUDevice,T> encode_unit(pos_threshold,neg_threshold,prio_scaling,max_overlap_as_pos_);
			auto &boxes              = bottom_boxes;
			auto &gboxes             = bottom_gboxes;
			auto &glabels            = bottom_glabels;
			auto &out_boxes          = output_boxes_tensor;
			auto &out_labels         = output_labels_tensor;
			auto &out_scores         = output_scores_tensor;
			auto &out_remove_indices = output_remove_indict_tensor;
			auto  res                = encode_unit(boxes,gboxes,glabels);

			out_boxes           =  std::get<0>(res);
			out_labels          =  std::get<1>(res);
			out_scores          =  std::get<2>(res);
			out_remove_indices  =  std::get<3>(res);
		}
	private:
		float         pos_threshold;
		float         neg_threshold;
		vector<float> prio_scaling;
        bool max_overlap_as_pos_ = true;
};
template <typename T>
class BoxesEncode1Op<GPUDevice,T>: public OpKernel {
	public:
        struct IOUIndex{
            int index;
            float iou;
        };
		explicit BoxesEncode1Op(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("pos_threshold", &pos_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("neg_threshold", &neg_threshold));
			OP_REQUIRES_OK(context, context->GetAttr("prio_scaling", &prio_scaling));
			OP_REQUIRES_OK(context, context->GetAttr("max_overlap_as_pos", &max_overlap_as_pos_));
			OP_REQUIRES(context, prio_scaling.size() == 4, errors::InvalidArgument("prio scaling data must be shape[4]"));
		}

		void Compute(OpKernelContext* context) override
        {
            TIME_THISV1("BoxesEncode1");
            const Tensor &_bottom_boxes   = context->input(0);
            const Tensor &_bottom_gboxes  = context->input(1);
            const Tensor &_bottom_glabels = context->input(2);
            auto          bottom_boxes    = _bottom_boxes.template tensor<T,2>();
            auto          bottom_gboxes   = _bottom_gboxes.template tensor<T,2>();
            auto          bottom_glabels  = _bottom_glabels.template tensor<int,1>();
            const auto    data_nr         = _bottom_boxes.dim_size(0);

            OP_REQUIRES(context, _bottom_boxes.dims() == 2, errors::InvalidArgument("box data must be 2-dimensional"));
            OP_REQUIRES(context, _bottom_gboxes.dims() == 2, errors::InvalidArgument("box data must be 2-dimensional"));
            OP_REQUIRES(context, _bottom_glabels.dims() == 1, errors::InvalidArgument("labels data must be 1-dimensional"));

            int           dims_2d[2]            = {int(data_nr),4};
            int           dims_1d[1]            = {int(data_nr)};
            TensorShape   outshape0;
            TensorShape   outshape1;
            Tensor       *output_boxes          = NULL;
            Tensor       *output_labels         = NULL;
            Tensor       *output_scores         = NULL;
            Tensor       *output_remove_indict  = NULL;
            vector<IOUIndex>   iou_indexs(data_nr,IOUIndex({-1,0.0})); //默认box不与任何ground truth box相交，iou为0

            TensorShapeUtils::MakeShape(dims_2d, 2, &outshape0);
            TensorShapeUtils::MakeShape(dims_1d, 1, &outshape1);

            OP_REQUIRES_OK(context, context->allocate_output(0, outshape0, &output_boxes));
            OP_REQUIRES_OK(context, context->allocate_output(1, outshape1, &output_labels));
            OP_REQUIRES_OK(context, context->allocate_output(2, outshape1, &output_scores));
            OP_REQUIRES_OK(context, context->allocate_output(3, outshape1, &output_remove_indict));
            auto output_boxes_tensor         = output_boxes->template tensor<T,2>();
            auto output_labels_tensor        = output_labels->template tensor<int,1>();
            auto output_scores_tensor        = output_scores->template tensor<T,1>();
            auto output_remove_indict_tensor = output_remove_indict->template tensor<bool,1>();

            BoxesEncodeUnit<GPUDevice,T> encode_unit(pos_threshold,neg_threshold,prio_scaling,max_overlap_as_pos_);
            auto size    = bottom_gboxes.dimension(0);
            auto boxes   = bottom_boxes.data();
            auto gboxes  = bottom_gboxes.data();
            auto glabels = bottom_glabels.data();
            encode_unit(boxes,gboxes,glabels,
                    output_boxes_tensor.data(),
                    output_labels_tensor.data(),
                    output_scores_tensor.data(),
                    output_remove_indict_tensor.data(),
                    nullptr,
                    size,bottom_boxes.dimension(0)
                    );
        }
	private:
		float         pos_threshold;
		float         neg_threshold;
		vector<float> prio_scaling;
        bool max_overlap_as_pos_ = true;
};

REGISTER_OP("BoxesEncode1Grad")
    .Attr("T: {float,double,int32,int64}")
	.Attr("threshold:float")
 	.Attr("prio_scaling: list(float)")
    .Input("bottom_boxes: T")
    .Input("bottom_gboxes: T")
    .Input("bottom_glabels: int32")
    .Input("grad: T")
	.Output("output:T");
template <typename Device, typename T>
class BoxesEncode1GradOp: public OpKernel {
	public:
		explicit BoxesEncode1GradOp(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context, context->GetAttr("threshold", &threshold));
			OP_REQUIRES_OK(context, context->GetAttr("prio_scaling", &prio_scaling));
			OP_REQUIRES(context, prio_scaling.size() == 4, errors::InvalidArgument("prio scaling data must be shape[4]"));
		}

		void Compute(OpKernelContext* context) override
		{
			const Tensor &bottom_boxes        = context->input(0);

			OP_REQUIRES(context, bottom_boxes.dims() == 2, errors::InvalidArgument("box data must be 2-dimensional"));

			TensorShape  outshape0   = bottom_boxes.shape();
			Tensor      *output_grad = nullptr;

			OP_REQUIRES_OK(context, context->allocate_output(0, outshape0, &output_grad));

			auto output_grad_flat = output_grad->template flat<T>();
			auto num_elements     = output_grad->NumElements();
			//cout<<"num elements:"<<num_elements<<","<<bottom_boxes.dims()<<","<<bottom_boxes.dim_size(0)<<","<<bottom_boxes.dim_size(1)<<endl;

            for(auto i=0; i<num_elements; ++i)
                output_grad_flat(i) = 0.0f;
		}
	private:
		float threshold;
		vector<float> prio_scaling;
};
REGISTER_KERNEL_BUILDER(Name("BoxesEncode1").Device(DEVICE_CPU).TypeConstraint<float>("T"), BoxesEncode1Op<CPUDevice, float>);
REGISTER_KERNEL_BUILDER(Name("BoxesEncode1Grad").Device(DEVICE_CPU).TypeConstraint<float>("T"), BoxesEncode1GradOp<CPUDevice, float>);
#ifdef GOOGLE_CUDA
REGISTER_KERNEL_BUILDER(Name("BoxesEncode1").Device(DEVICE_GPU).TypeConstraint<float>("T"), BoxesEncode1Op<GPUDevice, float>);
#endif
/*
 * bottom_boxes:[Nr,4](ymin,xmin,ymax,xmax) proposal box,相对坐标
 * bottom_regs:[Nr,4],(y,x,h,w)
 * prio_scaling:[4]
 * output:[Nr,4] 相对坐标(ymin,xmin,ymax,xmax)
 */
REGISTER_OP("DecodeBoxes1")
    .Attr("T: {float, double}")
	.Attr("prio_scaling:list(float)")
    .Input("bottom_boxes: T")
    .Input("bottom_regs: T")
	.Output("output:T")
    .SetShapeFn(shape_inference::UnchangedShape);

template <typename Device, typename T>
class DecodeBoxes1Op{
};
template <typename T>
class DecodeBoxes1Op<CPUDevice,T>: public OpKernel {
	public:
		explicit DecodeBoxes1Op(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context,
					context->GetAttr("prio_scaling", &prio_scaling));
		}

		void Compute(OpKernelContext* context) override
		{
            TIME_THISV1("DecodeBoxes1");
			const Tensor &bottom_boxes       = context->input(0);
			const Tensor &bottom_regs        = context->input(1);
			auto          bottom_regs_flat   = bottom_regs.flat<T>();
			auto          bottom_boxes_flat  = bottom_boxes.flat<T>();

			OP_REQUIRES(context, bottom_regs.dims() == 2, errors::InvalidArgument("regs data must be 2-dimensional"));
			OP_REQUIRES(context, bottom_boxes.dims() == 2, errors::InvalidArgument("pos data must be 2-dimensional"));
			OP_REQUIRES(context, bottom_boxes.dim_size(0)==bottom_regs.dim_size(0), errors::InvalidArgument("First dim size must be equal."));
			OP_REQUIRES(context, bottom_boxes.dim_size(1)==4, errors::InvalidArgument("Boxes second dim size must be 4."));
			OP_REQUIRES(context, bottom_regs.dim_size(1)==4, errors::InvalidArgument("Regs second dim size must be 4."));
			const auto nr = bottom_regs.dim_size(0);

			TensorShape output_shape = bottom_regs.shape();
			// Create output tensors
			Tensor* output_tensor = NULL;
			OP_REQUIRES_OK(context, context->allocate_output(0, output_shape, &output_tensor));

			auto output = output_tensor->template flat<T>();
			for (auto b = 0; b < nr; ++b) {
				const auto base_offset = b *4;
				const auto regs_data   = bottom_regs_flat.data()+base_offset;
				const auto box_data    = bottom_boxes_flat.data()+base_offset;
				float      y;
				float      x;
				float      href;
				float      wref;

				std::tie(y,x,href,wref) = box_minmax_to_cxywh(box_data);

				auto cy          = boost::algorithm::clamp<T>(regs_data[0]*prio_scaling[0],-10.0f,10.0f)*href+y;
				auto cx          = boost::algorithm::clamp<T>(regs_data[1]*prio_scaling[1],-10.0f,10.0f)*wref+x;
				auto h           = href *exp(boost::algorithm::clamp<T>(regs_data[2]*prio_scaling[2],-10.0,10.0));
				auto w           = wref *exp(boost::algorithm::clamp<T>(regs_data[3]*prio_scaling[3],-10.0,10.0));
				auto output_data = output.data() + base_offset;

				std::tie(output_data[0],output_data[1],output_data[2],output_data[3]) = box_cxywh_to_minmax(cy,cx,h,w);
				transform(output_data,output_data+4,output_data,[](T& v) { return boost::algorithm::clamp<T>(v,0.0,1.0);});
				if(output_data[0]>output_data[2])
					output_data[2] = output_data[0];
				if(output_data[1]>output_data[3])
					output_data[3] = output_data[1];
			}
		}
	private:
		std::vector<float> prio_scaling;
};
template <typename T>
class DecodeBoxes1Op<GPUDevice,T>: public OpKernel {
	public:
		explicit DecodeBoxes1Op(OpKernelConstruction* context) : OpKernel(context) {
			OP_REQUIRES_OK(context,
					context->GetAttr("prio_scaling", &prio_scaling));
		}

		void Compute(OpKernelContext* context) override
		{
            TIME_THISV1("DecodeBoxesGPU1");
			const Tensor &bottom_boxes       = context->input(0);
			const Tensor &bottom_regs        = context->input(1);
			auto          bottom_regs_flat   = bottom_regs.flat<T>();
			auto          bottom_boxes_flat  = bottom_boxes.flat<T>();

			OP_REQUIRES(context, bottom_regs.dims() == 2, errors::InvalidArgument("regs data must be 2-dimensional"));
			OP_REQUIRES(context, bottom_boxes.dims() == 2, errors::InvalidArgument("pos data must be 2-dimensional"));
			OP_REQUIRES(context, bottom_boxes.dim_size(0)==bottom_regs.dim_size(0), errors::InvalidArgument("First dim size must be equal."));
			OP_REQUIRES(context, bottom_boxes.dim_size(1)==4, errors::InvalidArgument("Boxes second dim size must be 4."));
			OP_REQUIRES(context, bottom_regs.dim_size(1)==4, errors::InvalidArgument("Regs second dim size must be 4."));
			const auto nr = bottom_regs.dim_size(0);

			TensorShape output_shape = bottom_regs.shape();
			// Create output tensors
			Tensor* output_tensor = NULL;
			OP_REQUIRES_OK(context, context->allocate_output(0, output_shape, &output_tensor));

			auto output = output_tensor->template flat<T>();
            if(nr>0)
                bboxes_decode_by_gpu(bottom_boxes_flat.data(),bottom_regs_flat.data(),prio_scaling.data(),output.data(),nr);
		}
	private:
		std::vector<float> prio_scaling;
};
REGISTER_KERNEL_BUILDER(Name("DecodeBoxes1").Device(DEVICE_CPU).TypeConstraint<float>("T"), DecodeBoxes1Op<CPUDevice, float>);
#ifdef GOOGLE_CUDA
REGISTER_KERNEL_BUILDER(Name("DecodeBoxes1").Device(DEVICE_GPU).TypeConstraint<float>("T"), DecodeBoxes1Op<GPUDevice, float>);
#endif


/* 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 */

#include "hdp_var.hpp"
#include "hdp_gibbs.hpp"

#include <iostream>
#include <fstream>
#include <vector>

#include <armadillo>

using namespace std;
using namespace arma;

int main(int argc, char** argv)
{

//  cout<<" ------------------------ Dir base measure online ------------------- "<<endl;
//  // inverse normal wishart base measure
////  mat x_rand(90,1);
////  x_rand.randu();
////  x_rand += 12.0;
//  vector<Mat<uint32_t> > x(12,zeros<Mat<uint32_t> >(1,90));
//  for (uint32_t d=0; d<12; d+=3)
//  {
//    x[d].zeros();
//    x[d].cols(0,29) += 1;
//    x[d].cols(30,59) += 2;
//    x[d+1].zeros();
//    x[d+1].cols(0,29) += 1;
//    x[d+1].cols(60,89) += 1;
//    x[d+2].zeros();
//    //x[d+2].rows(0,29) += 1;
//    //x[d+2].rows(60,89) += 1;
//  }
//  vector<Mat<uint32_t> > x_te(1,zeros<Mat<uint32_t> >(1,90));
//  x[0].zeros();
//  x[0].cols(0,29) += 1;
//  x[0].cols(30,59) += 2;
//
//  uint32_t Nw = 3;
////  x[2].randn();
////  x[2].rows(0,29) -= 8;
////  x[3].randn();
////  x[3].rows(0,59) += 4;
////  x[4].randn();
////  x[4].rows(0,29) += 4;
//  Row<double> alphas(Nw);
//  alphas.ones();
//  //alphas *= 1.1; // smaller alpha means more uncertainty in the where the good distributions are
//  double alpha =1.0, gamma=100.0;
//  Dir dir(alphas);
//  HDP_var<uint32_t> hdp_onl(dir, alpha, gamma);
//
//  hdp_onl.addHeldOut(x_te[0]);
//  hdp_onl.densityEst(x,Nw,0.9,10,2,1);
//
//  cout<<"x:"<<endl;
//  for(uint32_t j=0; j<x.size(); ++j)
//  {
//    for(uint32_t i=0; i<x[j].n_cols; ++i)
//      cout<<x[j](i)<<" ";
//    cout<<endl;
//  }

  cout<<" ---------------------------------- NIW base measure ---------------------------" <<endl;

  Col<double> vtheta(2);
  vtheta << 0.0 << 0.0;
  Mat<double> delta(2,2);
  delta << 1.0 << 0.0 <<endr
    << 0.0 << 1.0 <<endr;
  double alpha =1.0, gamma=100.0;

  NIW niw(vtheta,4.2,delta,2+2+0.2);
  HDP_var<double> hdp_onl_NIW(niw, alpha, gamma);

  vector<Mat<double> > xc(12,zeros<Mat<double> >(2,100));
  for (uint32_t d=0; d<12; d+=2)
  {
    xc[d].randn(2,100);
    xc[d+1].randn(2,100);
    xc[d+1] += 4;
  }
  vector<Mat<double> > xc_te(1,zeros<Mat<double> >(2,100));
  xc_te[0].randn(2,100);

  hdp_onl_NIW.addHeldOut(xc_te[0]);
  hdp_onl_NIW.densityEst(xc,1,0.9,30,10,1);

//  return 0;
//
//  cout<<" ---------------------------------- Dir base measure ---------------------------" <<endl;
//  HDP_gibbs<uint32_t> hdp_dir(dir, alpha, gamma);
//
//  vector<Row<uint32_t> > z_ji = hdp_dir.densityEst(x,Nw,10,10,100);
//  uint32_t J=z_ji.size();
//
//  cout<<"z_ji:"<<endl;
//  for(uint32_t j=0; j<J; ++j)
//  {
//    for(uint32_t i=0; i<z_ji[j].n_elem; ++i)
//      cout<<z_ji[j](i)<<" ";
//    cout<<endl;
//  }
//  cout<<"x:"<<endl;
//  for(uint32_t j=0; j<x.size(); ++j)
//  {
//    for(uint32_t i=0; i<x[j].n_cols; ++i)
//      cout<<x[j](i)<<" ";
//    cout<<endl;
//  }
//
//
//  return 0;

// not working for now - do not see a use for it currently!
//
//  cout<<" ------------------------ inverse normal wishart base measure ------------------- "<<endl;
//  // inverse normal wishart base measure
//  vector<mat> xx(2,zeros<mat>(90,2));
//  xx[0].randn();
//  xx[0].rows(0,29) += 8;
//  xx[0].rows(30,59) -= 8;
//  xx[1].randn();
//  xx[1].rows(0,29) += 8;
////  xx[2].randn();
////  xx[2].rows(0,29) -= 8;
////  xx[3].randn();
////  xx[3].rows(0,59) += 4;
////  xx[4].randn();
////  xx[4].rows(0,29) += 4;
////
//  colvec vtheta;
//  vtheta << 0.0 << 0.0;
//  mat Delta;
//  Delta << 2.0 << 0.0 <<endr
//        << 0.0 << 2.0 <<endr;
//  double kappa=1.0, nu=3.1;
//  alpha=1.0;
//  gamma=1.0;
//  InvNormWishart inw(vtheta, kappa,Delta, nu);
//  HDP_gibbs<double> hdp_inw(inw, alpha, gamma);
//
//  z_ji = hdp_inw.densityEst(xx,10,10,20);
//  J=z_ji.size();
//  cout<<"z_ji:"<<endl;
//  for(uint32_t j=0; j<J; ++j)
//  {
//    for(uint32_t i=0; i<z_ji[j].n_elem; ++i)
//      cout<<z_ji[j](i)<<" ";
//    cout<<endl;
//  }

  return 0;
}


/* Author: Moritz Allmaras, Texas A&M University, 2007 */

/* $Id: step-29.cc 27661 2012-11-21 14:38:52Z bangerth $ */
/*    Copyright (C) 2007-2008, 2010-2012 by the deal.II authors and M. Allmaras   */
/*                                                                */
/*    This file is subject to QPL and may not be  distributed     */
/*    without copyright and license information. Please refer     */
/*    to the file deal.II/doc/license.html for the text and       */
/*    further information on this license.                        */

// @sect3{Include files}

// The following header files are unchanged from step-7 and have been
// discussed before:

#include <deal.II/base/quadrature_lib.h>
#include <deal.II/base/function.h>
#include <deal.II/base/logstream.h>

#include <deal.II/lac/vector.h>
#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/sparse_matrix.h>

#include <deal.II/grid/tria.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/tria_accessor.h>
#include <deal.II/grid/tria_iterator.h>
#include <deal.II/grid/tria_boundary_lib.h>

#include <deal.II/dofs/dof_handler.h>
#include <deal.II/dofs/dof_accessor.h>
#include <deal.II/dofs/dof_tools.h>

#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_values.h>

#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/data_out.h>
#include <deal.II/numerics/vector_tools.h>

#include <fstream>

// This header file contains the necessary declarations for the
// ParameterHandler class that we will use to read our parameters from a
// configuration file:
#include <deal.II/base/parameter_handler.h>

// For solving the linear system, we'll use the sparse LU-decomposition
// provided by UMFPACK (see the SparseDirectUMFPACK class), for which the
// following header file is needed.  Note that in order to compile this
// tutorial program, the deal.II-library needs to be built with UMFPACK
// support, which can be most easily achieved by giving the
// --with-umfpack switch when configuring the library:
#include <deal.II/lac/sparse_direct.h>

// The FESystem class allows us to stack several FE-objects to one compound,
// vector-valued finite element field. The necessary declarations for this
// class are provided in this header file:
#include <deal.II/fe/fe_system.h>

// Finally, include the header file that declares the Timer class that we will
// use to determine how much time each of the operations of our program takes:
#include <deal.II/base/timer.h>

// As the last step at the beginning of this program, we put everything that
// is in this program into its namespace and, within it, make everything that
// is in the deal.II namespace globally available, without the need to prefix
// everything with dealii:::
namespace Step29
{
  using namespace dealii;

  // @sect3{The DirichletBoundaryValues class}

  // First we define a class for the function representing the Dirichlet
  // boundary values. This has been done many times before and therefore does
  // not need much explanation.
  //
  // Since there are two values $v$ and $w$ that need to be prescribed at the
  // boundary, we have to tell the base class that this is a vector-valued
  // function with two components, and the vector_value function
  // and its cousin vector_value_list must return vectors with
  // two entries. In our case the function is very simple, it just returns 1
  // for the real part $v$ and 0 for the imaginary part $w$ regardless of the
  // point where it is evaluated.
  template <int dim>
  class DirichletBoundaryValues : public Function<dim>
  {
  public:
    DirichletBoundaryValues() : Function<dim> (2) {};

    virtual void vector_value (const Point<dim> &p,
                               Vector<double>   &values) const;

    virtual void vector_value_list (const std::vector<Point<dim> > &points,
                                    std::vector<Vector<double> >   &value_list) const;
  };

  template <int dim>
  inline
  void DirichletBoundaryValues<dim>::vector_value (const Point<dim> & /*p*/,
                                                   Vector<double>   &values) const
  {
    Assert (values.size() == 2, ExcDimensionMismatch (values.size(), 2));

    values(0) = 1;
    values(1) = 0;
  }

  template <int dim>
  void DirichletBoundaryValues<dim>::vector_value_list (const std::vector<Point<dim> > &points,
                                                        std::vector<Vector<double> >   &value_list) const
  {
    Assert (value_list.size() == points.size(),
            ExcDimensionMismatch (value_list.size(), points.size()));

    for (unsigned int p=0; p<points.size(); ++p)
      DirichletBoundaryValues<dim>::vector_value (points[p], value_list[p]);
  }

  // @sect3{The ParameterReader class}

  // The next class is responsible for preparing the ParameterHandler object
  // and reading parameters from an input file.  It includes a function
  // declare_parameters that declares all the necessary
  // parameters and a read_parameters function that is called
  // from outside to initiate the parameter reading process.
  class ParameterReader : public Subscriptor
  {
  public:
    ParameterReader(ParameterHandler &);
    void read_parameters(const std::string);

  private:
    void declare_parameters();
    ParameterHandler &prm;
  };

  // The constructor stores a reference to the ParameterHandler object that is
  // passed to it:
  ParameterReader::ParameterReader(ParameterHandler &paramhandler)
    :
    prm(paramhandler)
  {}

  // @sect4{ParameterReader::declare_parameters}

  // The declare_parameters function declares all the parameters
  // that our ParameterHandler object will be able to read from input files,
  // along with their types, range conditions and the subsections they appear
  // in. We will wrap all the entries that go into a section in a pair of
  // braces to force the editor to indent them by one level, making it simpler
  // to read which entries together form a section:
  void ParameterReader::declare_parameters()
  {
    // Parameters for mesh and geometry include the number of global
    // refinement steps that are applied to the initial coarse mesh and the
    // focal distance $d$ of the transducer lens. For the number of refinement
    // steps, we allow integer values in the range $[0,\infty)$, where the
    // omitted second argument to the Patterns::Integer object denotes the
    // half-open interval.  For the focal distance any number greater than
    // zero is accepted:
    prm.enter_subsection ("Mesh & geometry parameters");
    {
      prm.declare_entry("Number of refinements", "6",
                        Patterns::Integer(0),
                        "Number of global mesh refinement steps "
                        "applied to initial coarse grid");

      prm.declare_entry("Focal distance", "0.3",
                        Patterns::Double(0),
                        "Distance of the focal point of the lens "
                        "to the x-axis");
    }
    prm.leave_subsection ();

    // The next subsection is devoted to the physical parameters appearing in
    // the equation, which are the frequency $\omega$ and wave speed
    // $c$. Again, both need to lie in the half-open interval $[0,\infty)$
    // represented by calling the Patterns::Double class with only the left
    // end-point as argument:
    prm.enter_subsection ("Physical constants");
    {
      prm.declare_entry("c", "1.5e5",
                        Patterns::Double(0),
                        "Wave speed");

      prm.declare_entry("omega", "5.0e7",
                        Patterns::Double(0),
                        "Frequency");
    }
    prm.leave_subsection ();

    // Last but not least we would like to be able to change some properties
    // of the output, like filename and format, through entries in the
    // configuration file, which is the purpose of the last subsection:
    prm.enter_subsection ("Output parameters");
    {
      prm.declare_entry("Output file", "solution",
                        Patterns::Anything(),
                        "Name of the output file (without extension)");

      // Since different output formats may require different parameters for
      // generating output (like for example, postscript output needs
      // viewpoint angles, line widths, colors etc), it would be cumbersome if
      // we had to declare all these parameters by hand for every possible
      // output format supported in the library. Instead, each output format
      // has a FormatFlags::declare_parameters function, which
      // declares all the parameters specific to that format in an own
      // subsection. The following call of
      // DataOutInterface<1>::declare_parameters executes
      // declare_parameters for all available output formats, so
      // that for each format an own subsection will be created with
      // parameters declared for that particular output format. (The actual
      // value of the template parameter in the call, @<1@>
      // above, does not matter here: the function does the same work
      // independent of the dimension, but happens to be in a
      // template-parameter-dependent class.)  To find out what parameters
      // there are for which output format, you can either consult the
      // documentation of the DataOutBase class, or simply run this program
      // without a parameter file present. It will then create a file with all
      // declared parameters set to their default values, which can
      // conveniently serve as a starting point for setting the parameters to
      // the values you desire.
      DataOutInterface<1>::declare_parameters (prm);
    }
    prm.leave_subsection ();
  }

  // @sect4{ParameterReader::read_parameters}

  // This is the main function in the ParameterReader class.  It gets called
  // from outside, first declares all the parameters, and then reads them from
  // the input file whose filename is provided by the caller. After the call
  // to this function is complete, the prm object can be used to
  // retrieve the values of the parameters read in from the file:
  void ParameterReader::read_parameters (const std::string parameter_file)
  {
    declare_parameters();

    prm.read_input (parameter_file);
  }

  // @sect3{The ComputeIntensity class}

  // As mentioned in the introduction, the quantity that we are really after
  // is the spatial distribution of the intensity of the ultrasound wave,
  // which corresponds to $|u|=\sqrt{v^2+w^2}$. Now we could just be content
  // with having $v$ and $w$ in our output, and use a suitable visualization
  // or postprocessing tool to derive $|u|$ from the solution we
  // computed. However, there is also a way to output data derived from the
  // solution in deal.II, and we are going to make use of this mechanism here.

  // So far we have always used the DataOut::add_data_vector function to add
  // vectors containing output data to a DataOut object.  There is a special
  // version of this function that in addition to the data vector has an
  // additional argument of type DataPostprocessor. What happens when this
  // function is used for output is that at each point where output data is to
  // be generated, the DataPostprocessor::compute_derived_quantities_scalar or
  // DataPostprocessor::compute_derived_quantities_vector function of the
  // specified DataPostprocessor object is invoked to compute the output
  // quantities from the values, the gradients and the second derivatives of
  // the finite element function represented by the data vector (in the case
  // of face related data, normal vectors are available as well). Hence, this
  // allows us to output any quantity that can locally be derived from the
  // values of the solution and its derivatives.  Of course, the ultrasound
  // intensity $|u|$ is such a quantity and its computation doesn't even
  // involve any derivatives of $v$ or $w$.

  // In practice, the DataPostprocessor class only provides an interface to
  // this functionality, and we need to derive our own class from it in order
  // to implement the functions specified by the interface. In the most
  // general case one has to implement several member functions but if the
  // output quantity is a single scalar then some of this boilerplate code can
  // be handled by a more specialized class, DataPostprocessorScalar and we
  // can derive from that one instead. This is what the
  // ComputeIntensity class does:
  template <int dim>
  class ComputeIntensity : public DataPostprocessorScalar<dim>
  {
  public:
    ComputeIntensity ();

    virtual
    void
    compute_derived_quantities_vector (const std::vector< Vector< double > > &uh,
                                       const std::vector< std::vector< Tensor< 1, dim > > > &duh,
                                       const std::vector< std::vector< Tensor< 2, dim > > > &dduh,
                                       const std::vector< Point< dim > > &normals,
                                       const std::vector<Point<dim> > &evaluation_points,
                                       std::vector< Vector< double > > &computed_quantities) const;
  };

  // In the constructor, we need to call the constructor of the base class
  // with two arguments. The first denotes the name by which the single scalar
  // quantity computed by this class should be represented in output files. In
  // our case, the postprocessor has $|u|$ as output, so we use "Intensity".
  //
  // The second argument is a set of flags that indicate which data is needed
  // by the postprocessor in order to compute the output quantities.  This can
  // be any subset of update_values, update_gradients and update_hessians
  // (and, in the case of face data, also update_normal_vectors), which are
  // documented in UpdateFlags.  Of course, computation of the derivatives
  // requires additional resources, so only the flags for data that is really
  // needed should be given here, just as we do when we use FEValues objects.
  // In our case, only the function values of $v$ and $w$ are needed to
  // compute $|u|$, so we're good with the update_values flag.
  template <int dim>
  ComputeIntensity<dim>::ComputeIntensity ()
    :
    DataPostprocessorScalar<dim> ("Intensity",
                                  update_values)
  {}

  // The actual prostprocessing happens in the following function.  Its inputs
  // are a vector representing values of the function (which is here
  // vector-valued) representing the data vector given to
  // DataOut::add_data_vector, evaluated at all evaluation points where we
  // generate output, and some tensor objects representing derivatives (that
  // we don't use here since $|u|$ is computed from just $v$ and $w$, and for
  // which we assign no name to the corresponding function argument).  The
  // derived quantities are returned in the computed_quantities
  // vector.  Remember that this function may only use data for which the
  // respective update flag is specified by
  // get_needed_update_flags. For example, we may not use the
  // derivatives here, since our implementation of
  // get_needed_update_flags requests that only function values
  // are provided.
  template <int dim>
  void
  ComputeIntensity<dim>::compute_derived_quantities_vector (
    const std::vector< Vector< double > >                  &uh,
    const std::vector< std::vector< Tensor< 1, dim > > >  & /*duh*/,
    const std::vector< std::vector< Tensor< 2, dim > > >  & /*dduh*/,
    const std::vector< Point< dim > >                     & /*normals*/,
    const std::vector<Point<dim> >                        & /*evaluation_points*/,
    std::vector< Vector< double > >                        &computed_quantities
  ) const
  {
    Assert(computed_quantities.size() == uh.size(),
           ExcDimensionMismatch (computed_quantities.size(), uh.size()));

    // The computation itself is straightforward: We iterate over each entry
    // in the output vector and compute $|u|$ from the corresponding values of
    // $v$ and $w$:
    for (unsigned int i=0; i<computed_quantities.size(); i++)
      {
        Assert(computed_quantities[i].size() == 1,
               ExcDimensionMismatch (computed_quantities[i].size(), 1));
        Assert(uh[i].size() == 2, ExcDimensionMismatch (uh[i].size(), 2));

        computed_quantities[i](0) = sqrt(uh[i](0)*uh[i](0) + uh[i](1)*uh[i](1));
      }
  }

  // @sect3{The UltrasoundProblem class}

  // Finally here is the main class of this program.  It's member functions
  // are very similar to the previous examples, in particular step-4, and the
  // list of member variables does not contain any major surprises either.
  // The ParameterHandler object that is passed to the constructor is stored
  // as a reference to allow easy access to the parameters from all functions
  // of the class.  Since we are working with vector valued finite elements,
  // the FE object we are using is of type FESystem.
  template <int dim>
  class UltrasoundProblem
  {
  public:
    UltrasoundProblem (ParameterHandler &);
    ~UltrasoundProblem ();
    void run ();

  private:
    void make_grid ();
    void setup_system ();
    void assemble_system ();
    void solve ();
    void output_results () const;

    ParameterHandler      &prm;

    Triangulation<dim>     triangulation;
    DoFHandler<dim>        dof_handler;
    FESystem<dim>          fe;

    SparsityPattern        sparsity_pattern;
    SparseMatrix<double>   system_matrix;
    Vector<double>         solution, system_rhs;
  };

  // The constructor takes the ParameterHandler object and stores it in a
  // reference. It also initializes the DoF-Handler and the finite element
  // system, which consists of two copies of the scalar Q1 field, one for $v$
  // and one for $w$:
  template <int dim>
  UltrasoundProblem<dim>::UltrasoundProblem (ParameterHandler &param)
    :
    prm(param),
    dof_handler(triangulation),
    fe(FE_Q<dim>(1), 2)
  {}

  template <int dim>
  UltrasoundProblem<dim>::~UltrasoundProblem ()
  {
    dof_handler.clear();
  }

  // @sect4{UltrasoundProblem::make_grid}

  // Here we setup the grid for our domain.  As mentioned in the exposition,
  // the geometry is just a unit square (in 2d) with the part of the boundary
  // that represents the transducer lens replaced by a sector of a circle.
  template <int dim>
  void UltrasoundProblem<dim>::make_grid ()
  {
    // First we generate some logging output and start a timer so we can
    // compute execution time when this function is done:
    deallog << "Generating grid... ";
    Timer timer;
    timer.start ();

    // Then we query the values for the focal distance of the transducer lens
    // and the number of mesh refinement steps from our ParameterHandler
    // object:
    prm.enter_subsection ("Mesh & geometry parameters");

    const double                focal_distance = prm.get_double("Focal distance");
    const unsigned int  n_refinements  = prm.get_integer("Number of refinements");

    prm.leave_subsection ();

    // Next, two points are defined for position and focal point of the
    // transducer lens, which is the center of the circle whose segment will
    // form the transducer part of the boundary. We compute the radius of this
    // circle in such a way that the segment fits in the interval [0.4,0.6] on
    // the x-axis.  Notice that this is the only point in the program where
    // things are slightly different in 2D and 3D.  Even though this tutorial
    // only deals with the 2D case, the necessary additions to make this
    // program functional in 3D are so minimal that we opt for including them:
    const Point<dim>    transducer = (dim == 2) ?
                                     Point<dim> (0.5, 0.0) :
                                     Point<dim> (0.5, 0.5, 0.0),
                                     focal_point = (dim == 2) ?
                                                   Point<dim> (0.5, focal_distance) :
                                                   Point<dim> (0.5, 0.5, focal_distance);

    const double radius = std::sqrt( (focal_point.distance(transducer) *
                                      focal_point.distance(transducer)) +
                                     ((dim==2) ? 0.01 : 0.02));

    // As initial coarse grid we take a simple unit square with 5 subdivisions
    // in each direction. The number of subdivisions is chosen so that the
    // line segment $[0.4,0.6]$ that we want to designate as the transducer
    // boundary is spanned by a single face. Then we step through all cells to
    // find the faces where the transducer is to be located, which in fact is
    // just the single edge from 0.4 to 0.6 on the x-axis. This is where we
    // want the refinements to be made according to a circle shaped boundary,
    // so we mark this edge with a different boundary indicator.
    GridGenerator::subdivided_hyper_cube (triangulation, 5, 0, 1);

    typename Triangulation<dim>::cell_iterator
    cell = triangulation.begin (),
    endc = triangulation.end();

    for (; cell!=endc; ++cell)
      for (unsigned int face=0; face<GeometryInfo<dim>::faces_per_cell; ++face)
        if ( cell->face(face)->at_boundary() &&
             ((cell->face(face)->center() - transducer).square() < 0.01) )

          cell->face(face)->set_boundary_indicator (1);

    // For the circle part of the transducer lens, a hyper-ball object is used
    // (which, of course, in 2D just represents a circle), with radius and
    // center as computed above. By marking this object as
    // static, we ensure that it lives until the end of the
    // program and thereby longer than the triangulation object we will
    // associated with it. We then assign this boundary-object to the part of
    // the boundary with boundary indicator 1:
    static const HyperBallBoundary<dim> boundary(focal_point, radius);
    triangulation.set_boundary(1, boundary);

    // Now global refinement is executed. Cells near the transducer location
    // will be automatically refined according to the circle shaped boundary
    // of the transducer lens:
    triangulation.refine_global (n_refinements);

    // Lastly, we generate some more logging output. We stop the timer and
    // query the number of CPU seconds elapsed since the beginning of the
    // function:
    timer.stop ();
    deallog << "done ("
            << timer()
            << "s)"
            << std::endl;

    deallog << "  Number of active cells:  "
            << triangulation.n_active_cells()
            << std::endl;
  }

  // @sect4{UltrasoundProblem::setup_system}
  //
  // Initialization of the system matrix, sparsity patterns and vectors are
  // the same as in previous examples and therefore do not need further
  // comment. As in the previous function, we also output the run time of what
  // we do here:
  template <int dim>
  void UltrasoundProblem<dim>::setup_system ()
  {
    deallog << "Setting up system... ";
    Timer timer;
    timer.start();

    dof_handler.distribute_dofs (fe);

    sparsity_pattern.reinit (dof_handler.n_dofs(),
                             dof_handler.n_dofs(),
                             dof_handler.max_couplings_between_dofs());

    DoFTools::make_sparsity_pattern (dof_handler, sparsity_pattern);
    sparsity_pattern.compress();

    system_matrix.reinit (sparsity_pattern);
    system_rhs.reinit (dof_handler.n_dofs());
    solution.reinit (dof_handler.n_dofs());

    timer.stop ();
    deallog << "done ("
            << timer()
            << "s)"
            << std::endl;

    deallog << "  Number of degrees of freedom: "
            << dof_handler.n_dofs()
            << std::endl;
  }

  // @sect4{UltrasoundProblem::assemble_system}

  // As before, this function takes care of assembling the system matrix and
  // right hand side vector:
  template <int dim>
  void UltrasoundProblem<dim>::assemble_system ()
  {
    deallog << "Assembling system matrix... ";
    Timer timer;
    timer.start ();

    // First we query wavespeed and frequency from the ParameterHandler object
    // and store them in local variables, as they will be used frequently
    // throughout this function.

    prm.enter_subsection ("Physical constants");

    const double omega = prm.get_double("omega"),
                 c     = prm.get_double("c");

    prm.leave_subsection ();

    // As usual, for computing integrals ordinary Gauss quadrature rule is
    // used. Since our bilinear form involves boundary integrals on
    // $\Gamma_2$, we also need a quadrature rule for surface integration on
    // the faces, which are $dim-1$ dimensional:
    QGauss<dim>    quadrature_formula(2);
    QGauss<dim-1>  face_quadrature_formula(2);

    const unsigned int n_q_points       = quadrature_formula.size(),
                       n_face_q_points  = face_quadrature_formula.size(),
                       dofs_per_cell    = fe.dofs_per_cell;

    // The FEValues objects will evaluate the shape functions for us.  For the
    // part of the bilinear form that involves integration on $\Omega$, we'll
    // need the values and gradients of the shape functions, and of course the
    // quadrature weights.  For the terms involving the boundary integrals,
    // only shape function values and the quadrature weights are necessary.
    FEValues<dim>  fe_values (fe, quadrature_formula,
                              update_values | update_gradients |
                              update_JxW_values);

    FEFaceValues<dim> fe_face_values (fe, face_quadrature_formula,
                                      update_values | update_JxW_values);

    // As usual, the system matrix is assembled cell by cell, and we need a
    // matrix for storing the local cell contributions as well as an index
    // vector to transfer the cell contributions to the appropriate location
    // in the global system matrix after.
    FullMatrix<double> cell_matrix (dofs_per_cell, dofs_per_cell);
    std::vector<unsigned int> local_dof_indices (dofs_per_cell);

    typename DoFHandler<dim>::active_cell_iterator
    cell = dof_handler.begin_active(),
    endc = dof_handler.end();

    for (; cell!=endc; ++cell)
      {

        // On each cell, we first need to reset the local contribution matrix
        // and request the FEValues object to compute the shape functions for
        // the current cell:
        cell_matrix = 0;
        fe_values.reinit (cell);

        for (unsigned int i=0; i<dofs_per_cell; ++i)
          {
            for (unsigned int j=0; j<dofs_per_cell; ++j)
              {

                // At this point, it is important to keep in mind that we are
                // dealing with a finite element system with two
                // components. Due to the way we constructed this FESystem,
                // namely as the cartesian product of two scalar finite
                // element fields, each shape function has only a single
                // nonzero component (they are, in deal.II lingo, @ref
                // GlossPrimitive "primitive").  Hence, each shape function
                // can be viewed as one of the $\phi$'s or $\psi$'s from the
                // introduction, and similarly the corresponding degrees of
                // freedom can be attributed to either $\alpha$ or $\beta$.
                // As we iterate through all the degrees of freedom on the
                // current cell however, they do not come in any particular
                // order, and so we cannot decide right away whether the DoFs
                // with index $i$ and $j$ belong to the real or imaginary part
                // of our solution.  On the other hand, if you look at the
                // form of the system matrix in the introduction, this
                // distinction is crucial since it will determine to which
                // block in the system matrix the contribution of the current
                // pair of DoFs will go and hence which quantity we need to
                // compute from the given two shape functions.  Fortunately,
                // the FESystem object can provide us with this information,
                // namely it has a function
                // FESystem::system_to_component_index, that for each local
                // DoF index returns a pair of integers of which the first
                // indicates to which component of the system the DoF
                // belongs. The second integer of the pair indicates which
                // index the DoF has in the scalar base finite element field,
                // but this information is not relevant here. If you want to
                // know more about this function and the underlying scheme
                // behind primitive vector valued elements, take a look at
                // step-8 or the @ref vector_valued module, where these topics
                // are explained in depth.
                if (fe.system_to_component_index(i).first ==
                    fe.system_to_component_index(j).first)
                  {

                    // If both DoFs $i$ and $j$ belong to same component,
                    // i.e. their shape functions are both $\phi$'s or both
                    // $\psi$'s, the contribution will end up in one of the
                    // diagonal blocks in our system matrix, and since the
                    // corresponding entries are computed by the same formula,
                    // we do not bother if they actually are $\phi$ or $\psi$
                    // shape functions. We can simply compute the entry by
                    // iterating over all quadrature points and adding up
                    // their contributions, where values and gradients of the
                    // shape functions are supplied by our FEValues object.

                    for (unsigned int q_point=0; q_point<n_q_points; ++q_point)
                      cell_matrix(i,j) += (((fe_values.shape_value(i,q_point) *
                                             fe_values.shape_value(j,q_point)) *
                                            (- omega * omega)
                                            +
                                            (fe_values.shape_grad(i,q_point) *
                                             fe_values.shape_grad(j,q_point)) *
                                            c * c) *
                                           fe_values.JxW(q_point));

                    // You might think that we would have to specify which
                    // component of the shape function we'd like to evaluate
                    // when requesting shape function values or gradients from
                    // the FEValues object. However, as the shape functions
                    // are primitive, they have only one nonzero component,
                    // and the FEValues class is smart enough to figure out
                    // that we are definitely interested in this one nonzero
                    // component.
                  }
              }
          }

        // We also have to add contributions due to boundary terms. To this
        // end, we loop over all faces of the current cell and see if first it
        // is at the boundary, and second has the correct boundary indicator
        // associated with $\Gamma_2$, the part of the boundary where we have
        // absorbing boundary conditions:
        for (unsigned int face=0; face<GeometryInfo<dim>::faces_per_cell; ++face)
          if (cell->face(face)->at_boundary() &&
              (cell->face(face)->boundary_indicator() == 0) )
            {

              // These faces will certainly contribute to the off-diagonal
              // blocks of the system matrix, so we ask the FEFaceValues
              // object to provide us with the shape function values on this
              // face:
              fe_face_values.reinit (cell, face);

              // Next, we loop through all DoFs of the current cell to find
              // pairs that belong to different components and both have
              // support on the current face:
              for (unsigned int i=0; i<dofs_per_cell; ++i)
                for (unsigned int j=0; j<dofs_per_cell; ++j)
                  if ((fe.system_to_component_index(i).first !=
                       fe.system_to_component_index(j).first) &&
                      fe.has_support_on_face(i, face) &&
                      fe.has_support_on_face(j, face))
                    // The check whether shape functions have support on a
                    // face is not strictly necessary: if we don't check for
                    // it we would simply add up terms to the local cell
                    // matrix that happen to be zero because at least one of
                    // the shape functions happens to be zero. However, we can
                    // save that work by adding the checks above.

                    // In either case, these DoFs will contribute to the
                    // boundary integrals in the off-diagonal blocks of the
                    // system matrix. To compute the integral, we loop over
                    // all the quadrature points on the face and sum up the
                    // contribution weighted with the quadrature weights that
                    // the face quadrature rule provides.  In contrast to the
                    // entries on the diagonal blocks, here it does matter
                    // which one of the shape functions is a $\psi$ and which
                    // one is a $\phi$, since that will determine the sign of
                    // the entry.  We account for this by a simple conditional
                    // statement that determines the correct sign. Since we
                    // already checked that DoF $i$ and $j$ belong to
                    // different components, it suffices here to test for one
                    // of them to which component it belongs.
                    for (unsigned int q_point=0; q_point<n_face_q_points; ++q_point)
                      cell_matrix(i,j) += ((fe.system_to_component_index(i).first == 0) ? -1 : 1) *
                                          fe_face_values.shape_value(i,q_point) *
                                          fe_face_values.shape_value(j,q_point) *
                                          c *
                                          omega *
                                          fe_face_values.JxW(q_point);
            }

        // Now we are done with this cell and have to transfer its
        // contributions from the local to the global system matrix. To this
        // end, we first get a list of the global indices of the this cells
        // DoFs...
        cell->get_dof_indices (local_dof_indices);

        // ...and then add the entries to the system matrix one by one:
        for (unsigned int i=0; i<dofs_per_cell; ++i)
          for (unsigned int j=0; j<dofs_per_cell; ++j)
            system_matrix.add (local_dof_indices[i],
                               local_dof_indices[j],
                               cell_matrix(i,j));
      }

    // The only thing left are the Dirichlet boundary values on $\Gamma_1$,
    // which is characterized by the boundary indicator 1. The Dirichlet
    // values are provided by the DirichletBoundaryValues class
    // we defined above:
    std::map<unsigned int,double> boundary_values;
    VectorTools::interpolate_boundary_values (dof_handler,
                                              1,
                                              DirichletBoundaryValues<dim>(),
                                              boundary_values);

    MatrixTools::apply_boundary_values (boundary_values,
                                        system_matrix,
                                        solution,
                                        system_rhs);

    timer.stop ();
    deallog << "done ("
            << timer()
            << "s)"
            << std::endl;
  }

  // @sect4{UltrasoundProblem::solve}

  // As already mentioned in the introduction, the system matrix is neither
  // symmetric nor definite, and so it is not quite obvious how to come up
  // with an iterative solver and a preconditioner that do a good job on this
  // matrix.  We chose instead to go a different way and solve the linear
  // system with the sparse LU decomposition provided by UMFPACK. This is
  // often a good first choice for 2D problems and works reasonably well even
  // for a large number of DoFs.  The deal.II interface to UMFPACK is given by
  // the SparseDirectUMFPACK class, which is very easy to use and allows us to
  // solve our linear system with just 3 lines of code.

  // Note again that for compiling this example program, you need to have the
  // deal.II library built with UMFPACK support, which can be achieved by
  // providing the  --with-umfpack switch to the configure script
  // prior to compilation of the library.
  template <int dim>
  void UltrasoundProblem<dim>::solve ()
  {
    deallog << "Solving linear system... ";
    Timer timer;
    timer.start ();

    // The code to solve the linear system is short: First, we allocate an
    // object of the right type. The following initialize call
    // provides the matrix that we would like to invert to the
    // SparseDirectUMFPACK object, and at the same time kicks off the
    // LU-decomposition. Hence, this is also the point where most of the
    // computational work in this program happens.
    SparseDirectUMFPACK  A_direct;
    A_direct.initialize(system_matrix);

    // After the decomposition, we can use A_direct like a matrix
    // representing the inverse of our system matrix, so to compute the
    // solution we just have to multiply with the right hand side vector:
    A_direct.vmult (solution, system_rhs);

    timer.stop ();
    deallog << "done ("
            << timer ()
            << "s)"
            << std::endl;
  }

  // @sect4{UltrasoundProblem::output_results}

  // Here we output our solution $v$ and $w$ as well as the derived quantity
  // $|u|$ in the format specified in the parameter file. Most of the work for
  // deriving $|u|$ from $v$ and $w$ was already done in the implementation of
  // the ComputeIntensity class, so that the output routine is
  // rather straightforward and very similar to what is done in the previous
  // tutorials.
  template <int dim>
  void UltrasoundProblem<dim>::output_results () const
  {
    deallog << "Generating output... ";
    Timer timer;
    timer.start ();

    // Define objects of our ComputeIntensity class and a DataOut
    // object:
    ComputeIntensity<dim> intensities;
    DataOut<dim> data_out;

    data_out.attach_dof_handler (dof_handler);

    // Next we query the output-related parameters from the ParameterHandler.
    // The DataOut::parse_parameters call acts as a counterpart to the
    // DataOutInterface<1>::declare_parameters call in
    // ParameterReader::declare_parameters. It collects all the
    // output format related parameters from the ParameterHandler and sets the
    // corresponding properties of the DataOut object accordingly.
    prm.enter_subsection("Output parameters");

    const std::string output_file    = prm.get("Output file");
    data_out.parse_parameters(prm);

    prm.leave_subsection ();

    // Now we put together the filename from the base name provided by the
    // ParameterHandler and the suffix which is provided by the DataOut class
    // (the default suffix is set to the right type that matches the one set
    // in the .prm file through parse_parameters()):
    const std::string filename = output_file +
                                 data_out.default_suffix();

    std::ofstream output (filename.c_str());

    // The solution vectors $v$ and $w$ are added to the DataOut object in the
    // usual way:
    std::vector<std::string> solution_names;
    solution_names.push_back ("Re_u");
    solution_names.push_back ("Im_u");

    data_out.add_data_vector (solution, solution_names);

    // For the intensity, we just call add_data_vector again, but
    // this with our ComputeIntensity object as the second
    // argument, which effectively adds $|u|$ to the output data:
    data_out.add_data_vector (solution, intensities);

    // The last steps are as before. Note that the actual output format is now
    // determined by what is stated in the input file, i.e. one can change the
    // output format without having to re-compile this program:
    data_out.build_patches ();
    data_out.write (output);

    timer.stop ();
    deallog << "done ("
            << timer()
            << "s)"
            << std::endl;
  }

  // @sect4{UltrasoundProblem::run}

  // Here we simply execute our functions one after the other:
  template <int dim>
  void UltrasoundProblem<dim>::run ()
  {
    make_grid ();
    setup_system ();
    assemble_system ();
    solve ();
    output_results ();
  }
}

// @sect4{The main function}

// Finally the main function of the program. It has the same
// structure as in almost all of the other tutorial programs. The only
// exception is that we define ParameterHandler and
// ParameterReader objects, and let the latter read in the
// parameter values from a textfile called step-29.prm. The
// values so read are then handed over to an instance of the UltrasoundProblem
// class:
int main ()
{
  try
    {
      using namespace dealii;
      using namespace Step29;

      ParameterHandler  prm;
      ParameterReader   param(prm);
      param.read_parameters("step-29.prm");

      UltrasoundProblem<2>  ultrasound_problem (prm);
      ultrasound_problem.run ();
    }
  catch (std::exception &exc)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Exception on processing: " << std::endl
                << exc.what() << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }
  catch (...)
    {
      std::cerr << std::endl << std::endl
                << "----------------------------------------------------"
                << std::endl;
      std::cerr << "Unknown exception!" << std::endl
                << "Aborting!" << std::endl
                << "----------------------------------------------------"
                << std::endl;
      return 1;
    }
  return 0;
}