<commit_before>/*************************************************************************/
/*  gd_mono_internals.cpp                                                */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "gd_mono_internals.h"

#include "../csharp_script.h"
#include "../mono_gc_handle.h"
#include "../utils/macros.h"
#include "../utils/thread_local.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"

#include <mono/metadata/exception.h>

namespace GDMonoInternals {

void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {

	// This method should not fail

	CRASH_COND(!unmanaged);

	// All mono objects created from the managed world (e.g.: 'new Player()')
	// need to have a CSharpScript in order for their methods to be callable from the unmanaged side

	Reference *ref = Object::cast_to<Reference>(unmanaged);

	GDMonoClass *klass = GDMonoUtils::get_object_class(managed);

	CRASH_COND(!klass);

	GDMonoClass *native = GDMonoUtils::get_class_native_base(klass);

	CRASH_COND(native == NULL);

	if (native == klass) {
		// If it's just a wrapper Godot class and not a custom inheriting class, then attach a
		// script binding instead. One of the advantages of this is that if a script is attached
		// later and it's not a C# script, then the managed object won't have to be disposed.
		// Another reason for doing this is that this instance could outlive CSharpLanguage, which would
		// be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621

		CSharpScriptBinding script_binding;

		script_binding.inited = true;
		script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
		script_binding.wrapper_class = klass;
		script_binding.gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed);
		script_binding.owner = unmanaged;

		if (ref) {
			// Unsafe refcount increment. The managed instance also counts as a reference.
			// This way if the unmanaged world has no references to our owner
			// but the managed instance is alive, the refcount will be 1 instead of 0.
			// See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)

			// May not me referenced yet, so we must use init_ref() instead of reference()
			if (ref->init_ref()) {
				CSharpLanguage::get_singleton()->post_unsafe_reference(ref);
			}
		}

		// The object was just created, no script instance binding should have been attached
		CRASH_COND(unmanaged->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()));

		void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding);

		// Should be thread safe because the object was just created and nothing else should be referencing it
		unmanaged->set_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index(), data);

		return;
	}

	Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed);

	Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native);

	CRASH_COND(script.is_null());

	ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);

	unmanaged->set_script_and_instance(script.get_ref_ptr(), si);
}

void unhandled_exception(MonoException *p_exc) {
	mono_unhandled_exception((MonoObject *)p_exc); // prints the exception as well

	if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
		// Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
		GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
		GD_UNREACHABLE();
	} else {
#ifdef DEBUG_ENABLED
		GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
		if (ScriptDebugger::get_singleton())
			ScriptDebugger::get_singleton()->idle_poll();
#endif
	}
}

} // namespace GDMonoInternals
<commit_msg>Mono/C#: Fix unhandled exception not being printed<commit_after>/*************************************************************************/
/*  gd_mono_internals.cpp                                                */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/

#include "gd_mono_internals.h"

#include "../csharp_script.h"
#include "../mono_gc_handle.h"
#include "../utils/macros.h"
#include "../utils/thread_local.h"
#include "gd_mono_class.h"
#include "gd_mono_marshal.h"
#include "gd_mono_utils.h"

#include <mono/metadata/exception.h>

namespace GDMonoInternals {

void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {

	// This method should not fail

	CRASH_COND(!unmanaged);

	// All mono objects created from the managed world (e.g.: 'new Player()')
	// need to have a CSharpScript in order for their methods to be callable from the unmanaged side

	Reference *ref = Object::cast_to<Reference>(unmanaged);

	GDMonoClass *klass = GDMonoUtils::get_object_class(managed);

	CRASH_COND(!klass);

	GDMonoClass *native = GDMonoUtils::get_class_native_base(klass);

	CRASH_COND(native == NULL);

	if (native == klass) {
		// If it's just a wrapper Godot class and not a custom inheriting class, then attach a
		// script binding instead. One of the advantages of this is that if a script is attached
		// later and it's not a C# script, then the managed object won't have to be disposed.
		// Another reason for doing this is that this instance could outlive CSharpLanguage, which would
		// be problematic when using a script. See: https://github.com/godotengine/godot/issues/25621

		CSharpScriptBinding script_binding;

		script_binding.inited = true;
		script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
		script_binding.wrapper_class = klass;
		script_binding.gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed);
		script_binding.owner = unmanaged;

		if (ref) {
			// Unsafe refcount increment. The managed instance also counts as a reference.
			// This way if the unmanaged world has no references to our owner
			// but the managed instance is alive, the refcount will be 1 instead of 0.
			// See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)

			// May not me referenced yet, so we must use init_ref() instead of reference()
			if (ref->init_ref()) {
				CSharpLanguage::get_singleton()->post_unsafe_reference(ref);
			}
		}

		// The object was just created, no script instance binding should have been attached
		CRASH_COND(unmanaged->has_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index()));

		void *data = (void *)CSharpLanguage::get_singleton()->insert_script_binding(unmanaged, script_binding);

		// Should be thread safe because the object was just created and nothing else should be referencing it
		unmanaged->set_script_instance_binding(CSharpLanguage::get_singleton()->get_language_index(), data);

		return;
	}

	Ref<MonoGCHandle> gchandle = ref ? MonoGCHandle::create_weak(managed) : MonoGCHandle::create_strong(managed);

	Ref<CSharpScript> script = CSharpScript::create_for_managed_type(klass, native);

	CRASH_COND(script.is_null());

	ScriptInstance *si = CSharpInstance::create_for_managed_type(unmanaged, script.ptr(), gchandle);

	unmanaged->set_script_and_instance(script.get_ref_ptr(), si);
}

void unhandled_exception(MonoException *p_exc) {
	mono_print_unhandled_exception((MonoObject *)p_exc);

	if (GDMono::get_singleton()->get_unhandled_exception_policy() == GDMono::POLICY_TERMINATE_APP) {
		// Too bad 'mono_invoke_unhandled_exception_hook' is not exposed to embedders
		GDMono::unhandled_exception_hook((MonoObject *)p_exc, NULL);
		GD_UNREACHABLE();
	} else {
#ifdef DEBUG_ENABLED
		GDMonoUtils::debug_send_unhandled_exception_error((MonoException *)p_exc);
		if (ScriptDebugger::get_singleton())
			ScriptDebugger::get_singleton()->idle_poll();
#endif
	}
}

} // namespace GDMonoInternals
<|endoftext|>


<commit_before>#include "testing/testing.hpp"

#include "routing/online_cross_fetcher.hpp"

#include "geometry/point2d.hpp"

#include "std/algorithm.hpp"
#include "std/vector.hpp"

using namespace routing;

namespace
{
UNIT_TEST(UrlGeneratorTest)
{
  TEST_EQUAL(GenerateOnlineRequest("http://mapsme.test.ru:10012", ms::LatLon(37.726536, 55.690105),
                                   ms::LatLon(39.902344, 44.527843)),
             "http://mapsme.test.ru:10012/mapsme?loc=55.6901,37.7265&loc=44.5278,39.9023",
             ("Url parsed"));
}

UNIT_TEST(GoodResponseParse)
{
  vector<m2::PointD> outPoints;
  TEST(ParseResponse(R"({"used_mwms":[[39.522671,94.009263], [37.4809, 67.7244]]})", outPoints),
       ("Valid response can't be parsed"));
  TEST_EQUAL(outPoints.size(), 2, ());
  TEST(
      find(outPoints.begin(), outPoints.end(), m2::PointD(39.522671, 94.009263)) != outPoints.end(),
      ("Can't find point in server's response."));
  TEST(find(outPoints.begin(), outPoints.end(), m2::PointD(37.4809, 67.7244)) != outPoints.end(),
       ("Can't find point in server's response."));
}

UNIT_TEST(BadResponseParse)
{
  vector<m2::PointD> outPoints;
  TEST(!ParseResponse("{\"used_mwms\":[]}", outPoints), ("Inval response should not be parsed"));
  TEST_EQUAL(outPoints.size(), 0, ("Found mwm points in invalid request"));
}

UNIT_TEST(GarbadgeInputToResponseParser)
{
  vector<m2::PointD> outPoints;
  TEST(!ParseResponse("{\"usedsfblbvlshbvldshbvfvmdfknvksvbksvk", outPoints),
       ("Malformed response should not be processed."));
  TEST_EQUAL(outPoints.size(), 0, ("Found mwm points in invalid request"));
}
}
<commit_msg>Routing test fixes<commit_after>#include "testing/testing.hpp"

#include "routing/online_cross_fetcher.hpp"

#include "geometry/point2d.hpp"

#include "std/algorithm.hpp"
#include "std/vector.hpp"

using namespace routing;

namespace
{
UNIT_TEST(UrlGeneratorTest)
{
  TEST_EQUAL(GenerateOnlineRequest("http://mapsme.test.ru:10012", ms::LatLon(55.690105, 37.726536),
                                   ms::LatLon(44.527843, 39.902344)),
             "http://mapsme.test.ru:10012/mapsme?loc=55.6901,37.7265&loc=44.5278,39.9023",
             ("Url parsed"));
}

UNIT_TEST(GoodResponseParse)
{
  vector<m2::PointD> outPoints;
  TEST(ParseResponse(R"({"used_mwms":[[39.522671,94.009263], [37.4809, 67.7244]]})", outPoints),
       ("Valid response can't be parsed"));
  TEST_EQUAL(outPoints.size(), 2, ());
  TEST(
      find(outPoints.begin(), outPoints.end(), m2::PointD(39.522671, 94.009263)) != outPoints.end(),
      ("Can't find point in server's response."));
  TEST(find(outPoints.begin(), outPoints.end(), m2::PointD(37.4809, 67.7244)) != outPoints.end(),
       ("Can't find point in server's response."));
}

UNIT_TEST(BadResponseParse)
{
  vector<m2::PointD> outPoints;
  TEST(!ParseResponse("{\"used_mwms\":[]}", outPoints), ("Inval response should not be parsed"));
  TEST_EQUAL(outPoints.size(), 0, ("Found mwm points in invalid request"));
}

UNIT_TEST(GarbadgeInputToResponseParser)
{
  vector<m2::PointD> outPoints;
  TEST(!ParseResponse("{\"usedsfblbvlshbvldshbvfvmdfknvksvbksvk", outPoints),
       ("Malformed response should not be processed."));
  TEST_EQUAL(outPoints.size(), 0, ("Found mwm points in invalid request"));
}
}
<|endoftext|>


<commit_before>#include "CHPFCRFF.h"
#include "MathUtils.h"
using namespace MathUtils;

template<>
InputParameters validParams<CHPFCRFF>()
{
  InputParameters params = validParams<Kernel>();
  params.addRequiredCoupledVar("v", "Array of names of the real parts of the L variables");
  MooseEnum log_options("tolerance, cancelation, expansion, nothing");
  params.addRequiredParam<MooseEnum>("log_approach", log_options, "Which approach will be used to handle the natural log");
  params.addParam<Real>("tol", 1.0e-9, "Tolerance used when the tolerance approach is chosen");
  params.addParam<Real>("n_exp_terms", 4, "Number of terms used in the Taylor expansion of the natural log term");
  params.addParam<std::string>("mob_name", "M", "The mobility used with the kernel");
  params.addParam<std::string>("Dmob_name", "DM", "The D mobility used with the kernel");
  params.addParam<bool>("has_MJac",false,"Jacobian information for the mobility is defined");
  params.addParam<Real>("a", 1.0, "Constants on Taylor Series");
  params.addParam<Real>("b", 1.0, "Constants on Taylor Series");
  params.addParam<Real>("c", 1.0, "Constants on Taylor Series");
  return params;
}

CHPFCRFF::CHPFCRFF(const std::string & name, InputParameters parameters) :
    Kernel(name, parameters),
    _mob_name(getParam<std::string>("mob_name")),
    _Dmob_name(getParam<std::string>("Dmob_name")),
    _M(getMaterialProperty<Real>(_mob_name)),
    _has_MJac(getParam<bool>("has_MJac")),
    _DM(_has_MJac ? &getMaterialProperty<Real>(_Dmob_name) : NULL),
    _log_approach(getParam<MooseEnum>("log_approach")),
    _tol(getParam<Real>("tol")),
    _n_exp_terms(getParam<Real>("n_exp_terms")),
    _a(getParam<Real>("a")),
    _b(getParam<Real>("b")),
    _c(getParam<Real>("c")),
    _num_L(coupledComponents("v")) // number of L variables
{
  _grad_vals.resize(_num_L); // Resize variable array
  _vals_var.resize(_num_L);

  // Loop through grains and load coupled gradients into the arrays
  for (unsigned int i = 0; i < _num_L; ++i)
  {
    _vals_var[i] = coupled("v",i);
    _grad_vals[i] = &coupledGradient("v", i);
  }
}

Real
CHPFCRFF::computeQpResidual()
{
  Real c = _u[_qp];
  RealGradient grad_c = _grad_u[_qp];
  RealGradient sum_grad_L;

  for (unsigned int i = 0; i < _num_L; ++i)
    sum_grad_L += (*_grad_vals[i])[_qp];

  Real frac;
  Real ln_expansion = 0.0;

  switch(_log_approach)
  {
    case 0: //approach using tolerance
      if (1.0 + c < _tol)
        frac = 1.0/_tol;
      else
        frac = 1.0/(1.0 + c);
      break;

    case 2:
      for (unsigned int i=2; i<(_n_exp_terms + 2.0); ++i)
      {
        // Apply Coefficents to Taylor Series defined in input file
        Real temp_coeff;
        if (i == 2)
          temp_coeff = _c;
        else if (i == 3)
          temp_coeff = _a;
        else if (i == 4)
          temp_coeff = _b;
        else
          temp_coeff = 1.0;

        ln_expansion += temp_coeff * std::pow(-1.0, Real(i)) * std::pow(_u[_qp], Real(i)-2.0);
      }
      break;
  }

  RealGradient GradDFDCons;

  switch(_log_approach)
  {
    case 0: //approach using tolerance
      GradDFDCons = grad_c*frac - sum_grad_L;
      break;

    case 1: //approach using cancelation from the mobility
      GradDFDCons = grad_c - (1.0 + c)*sum_grad_L;
      break;

    case 2: //appraoch using substitution
      GradDFDCons = ln_expansion*grad_c - sum_grad_L;
      break;

    case 3: //Just using the log
      GradDFDCons = grad_c/(1.0 + c) - sum_grad_L;
      break;
  }

  Real residual = _M[_qp]*GradDFDCons*_grad_test[_i][_qp];
  return residual;
}

Real
CHPFCRFF::computeQpJacobian()
{
  Real c = _u[_qp];
  RealGradient grad_c = _grad_u[_qp];
  RealGradient sum_grad_L;

  for (unsigned int i = 0; i < _num_L; ++i)
    sum_grad_L += (*_grad_vals[i])[_qp];

  Real frac, dfrac;
  Real ln_expansion = 0.0;

  switch(_log_approach)
  {
    case 0: //approach using tolerance
      if (1.0 + c < _tol)
      {
        frac = 1.0/_tol;
        dfrac = -1.0/(_tol*_tol);
      }
      else
      {
        frac = 1.0/(1.0 + c);
        dfrac = -1.0/((1.0 + c)*(1.0 + c));
      }
      break;

    case 2:

      for (unsigned int i=2; i<(_n_exp_terms + 2.0); ++i)
      {
        // Apply Coefficents to Taylor Series defined in input file
        Real temp_coeff;
        if (i == 2)
          temp_coeff = _c;
        else if (i == 3)
          temp_coeff = _a;
        else if (i == 4)
          temp_coeff = _b;
        else
          temp_coeff = 1.0;

        ln_expansion += temp_coeff * std::pow(-1.0, Real(i)) * std::pow(_u[_qp], Real(i)-2.0);
      }
      break;
  }

  RealGradient dGradDFDConsdC;
  Real Dln_expansion = 0.0;

  switch(_log_approach)
  {
    case 0: //approach using tolerance
      dGradDFDConsdC = _grad_phi[_j][_qp]*frac + _phi[_j][_qp]*grad_c*dfrac;
      break;

    case 1: //approach using cancelation from the mobility
      dGradDFDConsdC = _grad_phi[_j][_qp] - _phi[_j][_qp]*sum_grad_L;
      break;

    case 2: //appraoch using substitution
      for (unsigned int i=2; i<(_n_exp_terms + 2.0); ++i)
      {
        Real temp_coeff;
        if (i == 2)
          temp_coeff = _c;
        else if (i == 3)
          temp_coeff = _a;
        else if (i == 4)
          temp_coeff = _b;
        else
          temp_coeff = 1.0;

        Dln_expansion += temp_coeff*std::pow((Real) -1, Real(i)) * (Real(i) - 2.0) * std::pow(_u[_qp], Real(i) - 3.0);
      }

      dGradDFDConsdC = ln_expansion*_grad_phi[_j][_qp] + _phi[_j][_qp]*Dln_expansion*grad_c;
      break;

    case 3: //Nothing special
      dGradDFDConsdC = _grad_phi[_j][_qp]/(1.0 + c) - grad_c/((1.0 + c)*(1.0 + c))*_phi[_j][_qp];
      break;
  }

  return _M[_qp] * dGradDFDConsdC * _grad_test[_i][_qp];
}

Real
CHPFCRFF::computeQpOffDiagJacobian(unsigned int jvar)
{
  Real c = _u[_qp];

  for (unsigned int i=0; i<_num_L; ++i)
    if (jvar == _vals_var[i])
    {
      RealGradient dsum_grad_L = _grad_phi[_j][_qp];
      RealGradient dGradDFDConsdL;
      switch(_log_approach)
      {
        case 0: //approach using tolerance
          dGradDFDConsdL = -dsum_grad_L;
          break;

        case 1:  //approach using cancelation from the mobility
          dGradDFDConsdL = -(1.0 + c)*dsum_grad_L;
          break;

        case 2: //appraoch using substitution
          dGradDFDConsdL = -dsum_grad_L;
          break;

        case 3: //nothing special
          dGradDFDConsdL = -dsum_grad_L;
          break;
      }

      return _M[_qp]*dGradDFDConsdL*_grad_test[_i][_qp];
    }

  return 0.0;
}
<commit_msg>PFCRFF fixed<commit_after>#include "CHPFCRFF.h"
#include "MathUtils.h"
using namespace MathUtils;

template<>
InputParameters validParams<CHPFCRFF>()
{
  InputParameters params = validParams<Kernel>();
  params.addRequiredCoupledVar("v", "Array of names of the real parts of the L variables");
  MooseEnum log_options("tolerance, cancelation, expansion, nothing");
  params.addRequiredParam<MooseEnum>("log_approach", log_options, "Which approach will be used to handle the natural log");
  params.addParam<Real>("tol", 1.0e-9, "Tolerance used when the tolerance approach is chosen");
  params.addParam<Real>("n_exp_terms", 4, "Number of terms used in the Taylor expansion of the natural log term");
  params.addParam<std::string>("mob_name", "M", "The mobility used with the kernel");
  params.addParam<std::string>("Dmob_name", "DM", "The D mobility used with the kernel");
  params.addParam<bool>("has_MJac",false,"Jacobian information for the mobility is defined");
  params.addParam<Real>("a", 1.0, "Constants on Taylor Series");
  params.addParam<Real>("b", 1.0, "Constants on Taylor Series");
  params.addParam<Real>("c", 1.0, "Constants on Taylor Series");
  return params;
}

CHPFCRFF::CHPFCRFF(const std::string & name, InputParameters parameters) :
    Kernel(name, parameters),
    _mob_name(getParam<std::string>("mob_name")),
    _Dmob_name(getParam<std::string>("Dmob_name")),
    _M(getMaterialProperty<Real>(_mob_name)),
    _has_MJac(getParam<bool>("has_MJac")),
    _DM(_has_MJac ? &getMaterialProperty<Real>(_Dmob_name) : NULL),
    _log_approach(getParam<MooseEnum>("log_approach")),
    _tol(getParam<Real>("tol")),
    _n_exp_terms(getParam<Real>("n_exp_terms")),
    _a(getParam<Real>("a")),
    _b(getParam<Real>("b")),
    _c(getParam<Real>("c")),
    _num_L(coupledComponents("v")) // number of L variables
{
  _grad_vals.resize(_num_L); // Resize variable array
  _vals_var.resize(_num_L);

  // Loop through grains and load coupled gradients into the arrays
  for (unsigned int i = 0; i < _num_L; ++i)
  {
    _vals_var[i] = coupled("v",i);
    _grad_vals[i] = &coupledGradient("v", i);
  }
}

Real
CHPFCRFF::computeQpResidual()
{
  Real c = _u[_qp];
  RealGradient grad_c = _grad_u[_qp];
  RealGradient sum_grad_L;

  for (unsigned int i = 0; i < _num_L; ++i)
  {
    sum_grad_L += (*_grad_vals[i])[_qp]*0.5;
  }

  Real frac;
  Real ln_expansion = 0.0;

  switch(_log_approach)
  {
    case 0: //approach using tolerance
      if (1.0 + c < _tol)
        frac = 1.0/_tol;
      else
        frac = 1.0/(1.0 + c);
      break;

    case 2:
      for (unsigned int i=2; i<(_n_exp_terms + 2.0); ++i)
      {
        // Apply Coefficents to Taylor Series defined in input file
        Real temp_coeff;
        if (i == 2)
          temp_coeff = _c;
        else if (i == 3)
          temp_coeff = _a;
        else if (i == 4)
          temp_coeff = _b;
        else
          temp_coeff = 1.0;

        ln_expansion += temp_coeff * std::pow(-1.0, Real(i)) * std::pow(_u[_qp], Real(i)-2.0);
      }
      break;
  }

  RealGradient GradDFDCons;

  switch(_log_approach)
  {
    case 0: //approach using tolerance
      GradDFDCons = grad_c*frac - sum_grad_L;
      break;

    case 1: //approach using cancelation from the mobility
      GradDFDCons = grad_c - (1.0 + c)*sum_grad_L;
      break;

    case 2: //appraoch using substitution
      GradDFDCons = ln_expansion*grad_c - sum_grad_L;
      break;

    case 3: //Just using the log
      GradDFDCons = grad_c/(1.0 + c) - sum_grad_L;
      break;
  }

  Real residual = _M[_qp]*GradDFDCons*_grad_test[_i][_qp];
  return residual;
}

Real
CHPFCRFF::computeQpJacobian()
{
  Real c = _u[_qp];
  RealGradient grad_c = _grad_u[_qp];
  RealGradient sum_grad_L;

  for (unsigned int i = 0; i < _num_L; ++i)
  {
    sum_grad_L += (*_grad_vals[i])[_qp]*0.5;
  }

  Real frac, dfrac;
  Real ln_expansion = 0.0;

  switch(_log_approach)
  {
    case 0: //approach using tolerance
      if (1.0 + c < _tol)
      {
        frac = 1.0/_tol;
        dfrac = -1.0/(_tol*_tol);
      }
      else
      {
        frac = 1.0/(1.0 + c);
        dfrac = -1.0/((1.0 + c)*(1.0 + c));
      }
      break;

    case 2:

      for (unsigned int i=2; i<(_n_exp_terms + 2.0); ++i)
      {
        // Apply Coefficents to Taylor Series defined in input file
        Real temp_coeff;
        if (i == 2)
          temp_coeff = _c;
        else if (i == 3)
          temp_coeff = _a;
        else if (i == 4)
          temp_coeff = _b;
        else
          temp_coeff = 1.0;

        ln_expansion += temp_coeff * std::pow(-1.0, Real(i)) * std::pow(_u[_qp], Real(i)-2.0);
      }
      break;
  }

  RealGradient dGradDFDConsdC;
  Real Dln_expansion = 0.0;

  switch(_log_approach)
  {
    case 0: //approach using tolerance
      dGradDFDConsdC = _grad_phi[_j][_qp]*frac + _phi[_j][_qp]*grad_c*dfrac;
      break;

    case 1: //approach using cancelation from the mobility
      dGradDFDConsdC = _grad_phi[_j][_qp] - _phi[_j][_qp]*sum_grad_L;
      break;

    case 2: //appraoch using substitution
      for (unsigned int i=2; i<(_n_exp_terms + 2.0); ++i)
      {
        Real temp_coeff;
        if (i == 2)
          temp_coeff = _c;
        else if (i == 3)
          temp_coeff = _a;
        else if (i == 4)
          temp_coeff = _b;
        else
          temp_coeff = 1.0;

        Dln_expansion += temp_coeff*std::pow((Real) -1, Real(i)) * (Real(i) - 2.0) * std::pow(_u[_qp], Real(i) - 3.0);
      }

      dGradDFDConsdC = ln_expansion*_grad_phi[_j][_qp] + _phi[_j][_qp]*Dln_expansion*grad_c;
      break;

    case 3: //Nothing special
      dGradDFDConsdC = _grad_phi[_j][_qp]/(1.0 + c) - grad_c/((1.0 + c)*(1.0 + c))*_phi[_j][_qp];
      break;
  }

  return _M[_qp] * dGradDFDConsdC * _grad_test[_i][_qp];
}

Real
CHPFCRFF::computeQpOffDiagJacobian(unsigned int jvar)
{
  Real c = _u[_qp];

  for (unsigned int i=0; i<_num_L; ++i)
    if (jvar == _vals_var[i])
    {

      RealGradient dsum_grad_L = _grad_phi[_j][_qp]*0.5;
      RealGradient dGradDFDConsdL;
      switch(_log_approach)
      {
        case 0: //approach using tolerance
          dGradDFDConsdL = -dsum_grad_L;
          break;

        case 1:  //approach using cancelation from the mobility
          dGradDFDConsdL = -(1.0 + c)*dsum_grad_L;
          break;

        case 2: //appraoch using substitution
          dGradDFDConsdL = -dsum_grad_L;
          break;

        case 3: //nothing special
          dGradDFDConsdL = -dsum_grad_L;
          break;
      }

      return _M[_qp]*dGradDFDConsdL*_grad_test[_i][_qp];
    }

  return 0.0;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "modules/skshaper/include/SkShaper.h"

#ifdef SK_BUILD_FOR_MAC
#import <ApplicationServices/ApplicationServices.h>
#endif

#ifdef SK_BUILD_FOR_IOS
#include <CoreText/CoreText.h>
#include <CoreText/CTFontManager.h>
#include <CoreGraphics/CoreGraphics.h>
#include <CoreFoundation/CoreFoundation.h>
#endif

#include "include/ports/SkTypeface_mac.h"
#include "src/core/SkArenaAlloc.h"
#include "src/utils/mac/SkCGBase.h"
#include "src/utils/mac/SkUniqueCFRef.h"

#include <vector>

class SkShaper_CoreText : public SkShaper {
public:
    SkShaper_CoreText() {}
private:
    void shape(const char* utf8, size_t utf8Bytes,
               const SkFont& srcFont,
               bool leftToRight,
               SkScalar width,
               RunHandler*) const override;

    void shape(const char* utf8, size_t utf8Bytes,
               FontRunIterator&,
               BiDiRunIterator&,
               ScriptRunIterator&,
               LanguageRunIterator&,
               SkScalar width,
               RunHandler*) const override;

    void shape(const char* utf8, size_t utf8Bytes,
               FontRunIterator&,
               BiDiRunIterator&,
               ScriptRunIterator&,
               LanguageRunIterator&,
               const Feature*, size_t featureSize,
               SkScalar width,
               RunHandler*) const override;
};

std::unique_ptr<SkShaper> SkShaper::MakeCoreText() {
    return std::make_unique<SkShaper_CoreText>();
}

void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
                              FontRunIterator& font,
                              BiDiRunIterator& bidi,
                              ScriptRunIterator&,
                              LanguageRunIterator&,
                              SkScalar width,
                              RunHandler* handler) const
{
    SkFont skfont;
    if (!font.atEnd()) {
        font.consume();
        skfont = font.currentFont();
    } else {
        skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault()));
    }
    SkASSERT(skfont.getTypeface());
    bool skbidi = 0;
    if (!bidi.atEnd()) {
        bidi.consume();
        skbidi = (bidi.currentLevel() % 2) == 0;
    }
    return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
}

void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
                              FontRunIterator& font,
                              BiDiRunIterator& bidi,
                              ScriptRunIterator&,
                              LanguageRunIterator&,
                              const Feature*, size_t,
                              SkScalar width,
                              RunHandler* handler) const {
    font.consume();
    SkASSERT(font.currentFont().getTypeface());
    bidi.consume();
    return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
                       width, handler);
}

// CTFramesetter/CTFrame can do this, but require version 10.14
class LineBreakIter {
    CTTypesetterRef fTypesetter;
    double          fWidth;
    CFIndex         fStart;

public:
    LineBreakIter(CTTypesetterRef ts, SkScalar width) : fTypesetter(ts), fWidth(width) {
        fStart = 0;
    }

    SkUniqueCFRef<CTLineRef> nextLine() {
        CFRange stringRange {fStart, CTTypesetterSuggestLineBreak(fTypesetter, fStart, fWidth)};
        if (stringRange.length == 0) {
            return nullptr;
        }
        fStart += stringRange.length;
        return SkUniqueCFRef<CTLineRef>(CTTypesetterCreateLine(fTypesetter, stringRange));
    }
};

static void dict_add_double(CFMutableDictionaryRef d, const void* name, double value) {
    SkUniqueCFRef<CFNumberRef> number(
            CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
    CFDictionaryAddValue(d, name, number.get());
}

static SkUniqueCFRef<CTFontRef> create_ctfont_from_font(const SkFont& font) {
    auto typeface = font.getTypefaceOrDefault();
    auto ctfont = SkTypeface_GetCTFontRef(typeface);
    return SkUniqueCFRef<CTFontRef>(
            CTFontCreateCopyWithAttributes(ctfont, font.getSize(), nullptr, nullptr));
}

static SkFont run_to_font(CTRunRef run, const SkFont& orig) {
    CFDictionaryRef attr = CTRunGetAttributes(run);
    CTFontRef ct = (CTFontRef)CFDictionaryGetValue(attr, kCTFontAttributeName);
    if (!ct) {
        SkDebugf("no ctfont in Run Attributes\n");
        CFShow(attr);
        return orig;
    }
    // Do I need to add a local cache, or allow the caller to manage this lookup?
    SkFont font(orig);
    font.setTypeface(SkMakeTypefaceFromCTFont(ct));
    return font;
}

// kCTTrackingAttributeName not available until 10.12
const CFStringRef kCTTracking_AttributeName = CFSTR("CTTracking");

void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
                              const SkFont& font,
                              bool /* leftToRight */,
                              SkScalar width,
                              RunHandler* handler) const {
    SkUniqueCFRef<CFStringRef> textString(
            CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)utf8, utf8Bytes,
                                    kCFStringEncodingUTF8, false));

    SkUniqueCFRef<CTFontRef> ctfont = create_ctfont_from_font(font);

    SkUniqueCFRef<CFMutableDictionaryRef> attr(
            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                      &kCFTypeDictionaryKeyCallBacks,
                                      &kCFTypeDictionaryValueCallBacks));
    CFDictionaryAddValue(attr.get(), kCTFontAttributeName, ctfont.get());
    if (false) {
        // trying to see what these affect
        dict_add_double(attr.get(), kCTTracking_AttributeName, 1);
        dict_add_double(attr.get(), kCTKernAttributeName, 0.0);
    }

    SkUniqueCFRef<CFAttributedStringRef> attrString(
            CFAttributedStringCreate(kCFAllocatorDefault, textString.get(), attr.get()));

    SkUniqueCFRef<CTTypesetterRef> typesetter(
            CTTypesetterCreateWithAttributedString(attrString.get()));

    // We have to compute RunInfos in a loop, and then reuse them in a 2nd loop,
    // so we store them in an array (we reuse the array's storage for each line).
    std::vector<SkFont> fontStorage;
    std::vector<SkShaper::RunHandler::RunInfo> infos;

    LineBreakIter iter(typesetter.get(), width);
    while (SkUniqueCFRef<CTLineRef> line = iter.nextLine()) {
        CFArrayRef run_array = CTLineGetGlyphRuns(line.get());
        CFIndex runCount = CFArrayGetCount(run_array);
        if (runCount == 0) {
            continue;
        }
        handler->beginLine();
        fontStorage.clear();
        infos.clear();
        for (CFIndex j = 0; j < runCount; ++j) {
            CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
            CFIndex runGlyphs = CTRunGetGlyphCount(run);

            SkASSERT(sizeof(CGGlyph) == sizeof(uint16_t));

            SkSTArenaAlloc<4096> arena;
            CGSize* advances = arena.makeArrayDefault<CGSize>(runGlyphs);
            CTRunGetAdvances(run, {0, runGlyphs}, advances);
            SkScalar adv = 0;
            for (CFIndex k = 0; k < runGlyphs; ++k) {
                adv += advances[k].width;
            }

            CFRange range = CTRunGetStringRange(run);

            fontStorage.push_back(run_to_font(run, font));
            infos.push_back({
                fontStorage.back(), // info just stores a ref to the font
                0,                  // need fBidiLevel
                {adv, 0},
                (size_t)runGlyphs,
                {(size_t)range.location, (size_t)range.length},
            });
            handler->runInfo(infos.back());
        }
        handler->commitRunInfo();

        // Now loop through again and fill in the buffers
        for (CFIndex j = 0; j < runCount; ++j) {
            const auto& info = infos[j];
            auto buffer = handler->runBuffer(info);

            CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
            CFIndex runGlyphs = info.glyphCount;
            SkASSERT(CTRunGetGlyphCount(run) == (CFIndex)info.glyphCount);

            CTRunGetGlyphs(run, {0, runGlyphs}, buffer.glyphs);

            SkSTArenaAlloc<4096> arena;
            CGPoint* positions = arena.makeArrayDefault<CGPoint>(runGlyphs);
            CTRunGetPositions(run, {0, runGlyphs}, positions);
            CFIndex* indices = nullptr;
            if (buffer.clusters) {
                indices = arena.makeArrayDefault<CFIndex>(runGlyphs);
                CTRunGetStringIndices(run, {0, runGlyphs}, indices);
            }

            for (CFIndex k = 0; k < runGlyphs; ++k) {
                buffer.positions[k] = {
                    buffer.point.fX + SkScalarFromCGFloat(positions[k].x),
                    buffer.point.fY,
                };
                if (buffer.offsets) {
                    buffer.offsets[k] = {0, 0}; // offset relative to the origin for this glyph
                }
                if (buffer.clusters) {
                    buffer.clusters[k] = indices[k];
                }
            }
            handler->commitRunBuffer(info);
        }
        handler->commitLine();
    }
}
<commit_msg>[skshaper] Fix CoreText segfault and convert UTF16 indices to UTF8<commit_after>/*
 * Copyright 2020 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "modules/skshaper/include/SkShaper.h"

#ifdef SK_BUILD_FOR_MAC
#import <ApplicationServices/ApplicationServices.h>
#endif

#ifdef SK_BUILD_FOR_IOS
#include <CoreText/CoreText.h>
#include <CoreText/CTFontManager.h>
#include <CoreGraphics/CoreGraphics.h>
#include <CoreFoundation/CoreFoundation.h>
#endif

#include "include/ports/SkTypeface_mac.h"
#include "src/core/SkArenaAlloc.h"
#include "src/utils/SkUTF.h"
#include "src/utils/mac/SkCGBase.h"
#include "src/utils/mac/SkUniqueCFRef.h"

#include <vector>
#include <utility>

class SkShaper_CoreText : public SkShaper {
public:
    SkShaper_CoreText() {}
private:
    void shape(const char* utf8, size_t utf8Bytes,
               const SkFont& srcFont,
               bool leftToRight,
               SkScalar width,
               RunHandler*) const override;

    void shape(const char* utf8, size_t utf8Bytes,
               FontRunIterator&,
               BiDiRunIterator&,
               ScriptRunIterator&,
               LanguageRunIterator&,
               SkScalar width,
               RunHandler*) const override;

    void shape(const char* utf8, size_t utf8Bytes,
               FontRunIterator&,
               BiDiRunIterator&,
               ScriptRunIterator&,
               LanguageRunIterator&,
               const Feature*, size_t featureSize,
               SkScalar width,
               RunHandler*) const override;
};

std::unique_ptr<SkShaper> SkShaper::MakeCoreText() {
    return std::make_unique<SkShaper_CoreText>();
}

void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
                              FontRunIterator& font,
                              BiDiRunIterator& bidi,
                              ScriptRunIterator&,
                              LanguageRunIterator&,
                              SkScalar width,
                              RunHandler* handler) const
{
    SkFont skfont;
    if (!font.atEnd()) {
        font.consume();
        skfont = font.currentFont();
    } else {
        skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault()));
    }
    SkASSERT(skfont.getTypeface());
    bool skbidi = 0;
    if (!bidi.atEnd()) {
        bidi.consume();
        skbidi = (bidi.currentLevel() % 2) == 0;
    }
    return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
}

void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
                              FontRunIterator& font,
                              BiDiRunIterator& bidi,
                              ScriptRunIterator&,
                              LanguageRunIterator&,
                              const Feature*, size_t,
                              SkScalar width,
                              RunHandler* handler) const {
    font.consume();
    SkASSERT(font.currentFont().getTypeface());
    bidi.consume();
    return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
                       width, handler);
}

// CTFramesetter/CTFrame can do this, but require version 10.14
class LineBreakIter {
    CTTypesetterRef fTypesetter;
    double          fWidth;
    CFIndex         fStart;

public:
    LineBreakIter(CTTypesetterRef ts, SkScalar width) : fTypesetter(ts), fWidth(width) {
        fStart = 0;
    }

    SkUniqueCFRef<CTLineRef> nextLine() {
        CFRange stringRange {fStart, CTTypesetterSuggestLineBreak(fTypesetter, fStart, fWidth)};
        if (stringRange.length == 0) {
            return nullptr;
        }
        fStart += stringRange.length;
        return SkUniqueCFRef<CTLineRef>(CTTypesetterCreateLine(fTypesetter, stringRange));
    }
};

static void dict_add_double(CFMutableDictionaryRef d, const void* name, double value) {
    SkUniqueCFRef<CFNumberRef> number(
            CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
    CFDictionaryAddValue(d, name, number.get());
}

static SkUniqueCFRef<CTFontRef> create_ctfont_from_font(const SkFont& font) {
    auto typeface = font.getTypefaceOrDefault();
    auto ctfont = SkTypeface_GetCTFontRef(typeface);
    return SkUniqueCFRef<CTFontRef>(
            CTFontCreateCopyWithAttributes(ctfont, font.getSize(), nullptr, nullptr));
}

static SkFont run_to_font(CTRunRef run, const SkFont& orig) {
    CFDictionaryRef attr = CTRunGetAttributes(run);
    CTFontRef ct = (CTFontRef)CFDictionaryGetValue(attr, kCTFontAttributeName);
    if (!ct) {
        SkDebugf("no ctfont in Run Attributes\n");
        CFShow(attr);
        return orig;
    }
    // Do I need to add a local cache, or allow the caller to manage this lookup?
    SkFont font(orig);
    font.setTypeface(SkMakeTypefaceFromCTFont(ct));
    return font;
}

namespace {
class UTF16ToUTF8IndicesMap {
public:
    /** Builds a UTF-16 to UTF-8 indices map; the text is not retained
     * @return true if successful
     */
    bool setUTF8(const char* utf8, size_t size) {
        SkASSERT(utf8 != nullptr);

        if (!SkTFitsIn<int32_t>(size)) {
            SkDEBUGF("UTF16ToUTF8IndicesMap: text too long");
            return false;
        }

        auto utf16Size = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, size);
        if (utf16Size < 0) {
            SkDEBUGF("UTF16ToUTF8IndicesMap: Invalid utf8 input");
            return false;
        }

        // utf16Size+1 to also store the size
        fUtf16ToUtf8Indices = std::vector<size_t>(utf16Size + 1);
        auto utf16 = fUtf16ToUtf8Indices.begin();
        auto utf8Begin = utf8, utf8End = utf8 + size;
        while (utf8Begin < utf8End) {
            *utf16 = utf8Begin - utf8;
            utf16 += SkUTF::ToUTF16(SkUTF::NextUTF8(&utf8Begin, utf8End), nullptr);
        }
        *utf16 = size;

        return true;
    }

    size_t mapIndex(size_t index) const {
        SkASSERT(index < fUtf16ToUtf8Indices.size());
        return fUtf16ToUtf8Indices[index];
    }

    std::pair<size_t, size_t> mapRange(size_t start, size_t size) const {
        auto utf8Start = mapIndex(start);
        return {utf8Start, mapIndex(start + size) - utf8Start};
    }
private:
    std::vector<size_t> fUtf16ToUtf8Indices;
};
} // namespace

// kCTTrackingAttributeName not available until 10.12
const CFStringRef kCTTracking_AttributeName = CFSTR("CTTracking");

void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
                              const SkFont& font,
                              bool /* leftToRight */,
                              SkScalar width,
                              RunHandler* handler) const {
    SkUniqueCFRef<CFStringRef> textString(
            CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)utf8, utf8Bytes,
                                    kCFStringEncodingUTF8, false));

    UTF16ToUTF8IndicesMap utf8IndicesMap;
    if (!utf8IndicesMap.setUTF8(utf8, utf8Bytes)) {
        return;
    }

    SkUniqueCFRef<CTFontRef> ctfont = create_ctfont_from_font(font);

    SkUniqueCFRef<CFMutableDictionaryRef> attr(
            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
                                      &kCFTypeDictionaryKeyCallBacks,
                                      &kCFTypeDictionaryValueCallBacks));
    CFDictionaryAddValue(attr.get(), kCTFontAttributeName, ctfont.get());
    if (false) {
        // trying to see what these affect
        dict_add_double(attr.get(), kCTTracking_AttributeName, 1);
        dict_add_double(attr.get(), kCTKernAttributeName, 0.0);
    }

    SkUniqueCFRef<CFAttributedStringRef> attrString(
            CFAttributedStringCreate(kCFAllocatorDefault, textString.get(), attr.get()));

    SkUniqueCFRef<CTTypesetterRef> typesetter(
            CTTypesetterCreateWithAttributedString(attrString.get()));

    // We have to compute RunInfos in a loop, and then reuse them in a 2nd loop,
    // so we store them in an array (we reuse the array's storage for each line).
    std::vector<SkFont> fontStorage;
    std::vector<SkShaper::RunHandler::RunInfo> infos;

    LineBreakIter iter(typesetter.get(), width);
    while (SkUniqueCFRef<CTLineRef> line = iter.nextLine()) {
        CFArrayRef run_array = CTLineGetGlyphRuns(line.get());
        CFIndex runCount = CFArrayGetCount(run_array);
        if (runCount == 0) {
            continue;
        }
        handler->beginLine();
        fontStorage.clear();
        fontStorage.reserve(runCount); // ensure the refs won't get invalidated
        infos.clear();
        for (CFIndex j = 0; j < runCount; ++j) {
            CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
            CFIndex runGlyphs = CTRunGetGlyphCount(run);

            SkASSERT(sizeof(CGGlyph) == sizeof(uint16_t));

            SkSTArenaAlloc<4096> arena;
            CGSize* advances = arena.makeArrayDefault<CGSize>(runGlyphs);
            CTRunGetAdvances(run, {0, runGlyphs}, advances);
            SkScalar adv = 0;
            for (CFIndex k = 0; k < runGlyphs; ++k) {
                adv += advances[k].width;
            }

            CFRange cfRange = CTRunGetStringRange(run);
            auto range = utf8IndicesMap.mapRange(cfRange.location, cfRange.length);

            fontStorage.push_back(run_to_font(run, font));
            infos.push_back({
                fontStorage.back(), // info just stores a ref to the font
                0,                  // need fBidiLevel
                {adv, 0},
                (size_t)runGlyphs,
                {range.first, range.second},
            });
            handler->runInfo(infos.back());
        }
        handler->commitRunInfo();

        // Now loop through again and fill in the buffers
        SkScalar lineAdvance = 0;
        for (CFIndex j = 0; j < runCount; ++j) {
            const auto& info = infos[j];
            auto buffer = handler->runBuffer(info);

            CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
            CFIndex runGlyphs = info.glyphCount;
            SkASSERT(CTRunGetGlyphCount(run) == (CFIndex)info.glyphCount);

            CTRunGetGlyphs(run, {0, runGlyphs}, buffer.glyphs);

            SkSTArenaAlloc<4096> arena;
            CGPoint* positions = arena.makeArrayDefault<CGPoint>(runGlyphs);
            CTRunGetPositions(run, {0, runGlyphs}, positions);
            CFIndex* indices = nullptr;
            if (buffer.clusters) {
                indices = arena.makeArrayDefault<CFIndex>(runGlyphs);
                CTRunGetStringIndices(run, {0, runGlyphs}, indices);
            }

            for (CFIndex k = 0; k < runGlyphs; ++k) {
                buffer.positions[k] = {
                    buffer.point.fX + SkScalarFromCGFloat(positions[k].x) - lineAdvance,
                    buffer.point.fY,
                };
                if (buffer.offsets) {
                    buffer.offsets[k] = {0, 0}; // offset relative to the origin for this glyph
                }
                if (buffer.clusters) {
                    buffer.clusters[k] = utf8IndicesMap.mapIndex(indices[k]);
                }
            }
            handler->commitRunBuffer(info);
            lineAdvance += info.fAdvance.fX;
        }
        handler->commitLine();
    }
}
<|endoftext|>


<commit_before>#include "ArkEchoPlayerView.h"
#include "ArkEchoPlayerModel.h"
#include "MusicSongList.h"

#include <QLabel>

const QString DIALOGTITLE = "ArkEcho Media Player";
const int DEFAULT_VOLUME = 100;

enum TableTrackListColumns
{
    TRACKL_ALBUMTITLE = 0,
    TRACKL_ALBUMNUMBER,
    TRACKL_SONGTITLE,
    TRACKL_SONGINTERPRET,
    TRACKL_SONGDURATION,
    TRACKL_MAX_COLUMN_COUNT
};

ArkEchoPlayerView::ArkEchoPlayerView(QWidget *parent)
    :QMainWindow(parent)
    ,ui_(0)
    ,model_(0)
    ,webSocketStatus_(0)
    ,player_(0)
{
    model_ = new ArkEchoPlayerModel();
    connect(model_, SIGNAL(updateView(int)), this, SLOT(onUpdateView(int)));

    player_ = new QMediaPlayer();
    connect(player_, SIGNAL(positionChanged(qint64)), this, SLOT(onPlayerPositionChanged(qint64)));

    ui_ = new Ui::ArkEchoPlayerViewClass();
    ui_->setupUi(this);
    connect(ui_->pbBackward, SIGNAL(clicked()), this, SLOT(onPbBackwardClicked()));
    connect(ui_->pbForward, SIGNAL(clicked()), this, SLOT(onPbForwardClicked()));
    connect(ui_->pbPlay_Pause, SIGNAL(clicked()), this, SLOT(onPbPlay_PauseClicked()));
    connect(ui_->pbStop, SIGNAL(clicked()), this, SLOT(onPbStopClicked()));
    connect(ui_->twTrackList, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(onTwTrackListItemDoubleClicked(QTableWidgetItem*)));
    connect(ui_->sliderVolume, SIGNAL(valueChanged(int)), this, SLOT(onSliderVolumeValueChanged(int)));
    connect(ui_->sliderDuration, SIGNAL(sliderPressed()), this, SLOT(onSliderDurationPressed()));
    connect(ui_->sliderDuration, SIGNAL(sliderReleased()), this, SLOT(onSliderDurationReleased()));
    // UI initialisieren; Grssen, Texte, Inhalt etc.
    initUi();
}

ArkEchoPlayerView::~ArkEchoPlayerView()
{
    delete model_;
    delete player_;
    delete webSocketStatus_;
    delete ui_;
}

void ArkEchoPlayerView::initUi()
{
    this->setWindowTitle(DIALOGTITLE);

    // WebSocket Statusanzeige initalisieren
    webSocketStatus_ = new QLabel();
    ui_->statusBar->addPermanentWidget(webSocketStatus_);
    setWebSocketStatusLabel(false);

    // TableWidget initialisieren
    ui_->twTrackList->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui_->twTrackList->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui_->twTrackList->verticalHeader()->setVisible(false);
    ui_->twTrackList->setColumnCount(TRACKL_MAX_COLUMN_COUNT);
    ui_->twTrackList->setHorizontalHeaderLabels(QString("Album;Number;Titel;Interpret;Dauer").split(";"));
    setTWTrackList();

    // Duration Anzeige
    ui_->lblDuration->setText("");

    // Volume initialisieren
    ui_->lblVolume->setText(QString::number(DEFAULT_VOLUME));
    ui_->sliderVolume->setValue(DEFAULT_VOLUME);
}

void ArkEchoPlayerView::setWebSocketStatusLabel(bool connected)
{
    if (!webSocketStatus_) return;
    QString message, color;
    if (connected)
    {
        message = "Verbunden!";
        color = "rgb(24,220,44)";
    }
    else
    {
        message = "Nicht Verbunden!";
        color = "red";
    }
    webSocketStatus_->setText(message);
    webSocketStatus_->setStyleSheet("color: "+color+";");
}

void ArkEchoPlayerView::setTWTrackList()
{
    if (!model_ || !ui_ || !model_->getMusicSongList()) return;

    QMap<int,MusicSong*> map = model_->getMusicSongList()->getSongList();
    int mapSize = map.size();
    if (mapSize == 0) return;
    ui_->twTrackList->setRowCount(mapSize);

    for (int i = 0; i < mapSize; ++i)
    {
        MusicSong* song = map.value(i);
        if (!song) continue;
        ui_->twTrackList->setItem(i, TRACKL_ALBUMTITLE, new QTableWidgetItem(song->getAlbumTitle(), i));
        ui_->twTrackList->setItem(i, TRACKL_ALBUMNUMBER, new QTableWidgetItem(QString::number(song->getAlbumSongNumber()), i));
        ui_->twTrackList->setItem(i, TRACKL_SONGTITLE, new QTableWidgetItem(song->getSongTitle(), i));
        ui_->twTrackList->setItem(i, TRACKL_SONGINTERPRET, new QTableWidgetItem(song->getSongInterpret(), i));
        ui_->twTrackList->setItem(i, TRACKL_SONGDURATION, new QTableWidgetItem(MusicSong::convertSongDurationToMinuteSecond(song->getSongDuration()), i));
    }
}

void ArkEchoPlayerView::setLblDuration()
{
    if (!player_) return;
    // MetaData und Player Song Lnge unterscheiden sich um 1000ms
    QString durationOverall = MusicSong::convertSongDurationToMinuteSecond(player_->duration()-1000);
    qint64 position = player_->position();
    if (position >= 1000) position -= 1000;
    QString durationNow = MusicSong::convertSongDurationToMinuteSecond(position);
    QString text = durationNow + "/" + durationOverall;
    ui_->lblDuration->setText(text);
}

void ArkEchoPlayerView::onUpdateView(const int &uve)
{
    switch(uve)
    {
    case UVE_WEBSOCKET_CONNECTED:
        setWebSocketStatusLabel(true);
        break;
    case UVE_WEBSOCKET_DISCONNECTED:
        setWebSocketStatusLabel(false);
        break;
    case REMOTE_BUTTON_BACKWARD:
        onPbPlay_PauseClicked();
        break;
    case REMOTE_BUTTON_FORWARD:
        onPbPlay_PauseClicked();
        break;
    case REMOTE_BUTTON_PLAY_PAUSE:
        onPbPlay_PauseClicked();
        break;
    }
    qApp->processEvents();
}

void ArkEchoPlayerView::on_actionManuelle_Verbindung_triggered()
{
    if (!model_) return;
    model_->showConnectManualDialog();
}

void ArkEchoPlayerView::on_actionQR_Code_Verbindung_triggered()
{
    if (!model_) return;
    model_->showConnectQrDialog();
}

void ArkEchoPlayerView::onPbBackwardClicked()
{
}

void ArkEchoPlayerView::onPbForwardClicked()
{
}

void ArkEchoPlayerView::onPbPlay_PauseClicked()
{
    if (!player_) return;
    if (player_->state() == QMediaPlayer::PlayingState) player_->pause();
    else player_->play();
}

void ArkEchoPlayerView::onPbStopClicked()
{
    if (!player_) return;
    player_->stop();
}

void ArkEchoPlayerView::onTwTrackListItemDoubleClicked(QTableWidgetItem * item)
{
    if (!item) return;
    int type = item->type();

    if (!model_ || !model_->getMusicSongList()) return;
    MusicSong* song = model_->getMusicSongList()->getSongList()[type];

    if (!player_ || !song) return;
    player_->setMedia(song->getUrl());
    player_->play();
    setLblDuration();
}

void ArkEchoPlayerView::onSliderVolumeValueChanged(const int &value)
{
    if (!player_) return;
    player_->setVolume(value);
    ui_->lblVolume->setText(QString::number(value));
}

void ArkEchoPlayerView::onPlayerPositionChanged(const qint64 & position)
{
    if (!player_) return;
    setLblDuration();
    double duration = (double)player_->duration();
    double positionD = (double)position;

    if (duration == 0) return;
    double value = (positionD / duration) * 100;
    ui_->sliderDuration->setValue(value);
}

void ArkEchoPlayerView::onSliderDurationPressed()
{
    if (!player_) return;
    player_->pause();
}

void ArkEchoPlayerView::onSliderDurationReleased()
{
    if (!player_) return;
    double duration = (double)player_->duration();
    double value = ui_->sliderDuration->value();
    double newPosition = (duration / 100) * value;
    player_->setPosition(newPosition);
    player_->play();
    setLblDuration();
}
<commit_msg>Player: Corrected Mediaplayer Duration difference and edited Row and Column Size<commit_after>#include "ArkEchoPlayerView.h"
#include "ArkEchoPlayerModel.h"
#include "MusicSongList.h"

#include <QLabel>

const QString DIALOGTITLE = "ArkEcho Media Player";
const int MEDIAPLAYER_BUFFER_DURATION = 560;
const int DEFAULT_VOLUME = 100;
const int ROW_HEIGHT = 20;

enum TableTrackListColumns
{
    TRACKL_ALBUMTITLE = 0,
    TRACKL_ALBUMNUMBER,
    TRACKL_SONGTITLE,
    TRACKL_SONGINTERPRET,
    TRACKL_SONGDURATION,
    TRACKL_MAX_COLUMN_COUNT
};

ArkEchoPlayerView::ArkEchoPlayerView(QWidget *parent)
    :QMainWindow(parent)
    ,ui_(0)
    ,model_(0)
    ,webSocketStatus_(0)
    ,player_(0)
{
    model_ = new ArkEchoPlayerModel();
    connect(model_, SIGNAL(updateView(int)), this, SLOT(onUpdateView(int)));

    player_ = new QMediaPlayer();
    connect(player_, SIGNAL(positionChanged(qint64)), this, SLOT(onPlayerPositionChanged(qint64)));

    ui_ = new Ui::ArkEchoPlayerViewClass();
    ui_->setupUi(this);
    connect(ui_->pbBackward, SIGNAL(clicked()), this, SLOT(onPbBackwardClicked()));
    connect(ui_->pbForward, SIGNAL(clicked()), this, SLOT(onPbForwardClicked()));
    connect(ui_->pbPlay_Pause, SIGNAL(clicked()), this, SLOT(onPbPlay_PauseClicked()));
    connect(ui_->pbStop, SIGNAL(clicked()), this, SLOT(onPbStopClicked()));
    connect(ui_->twTrackList, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(onTwTrackListItemDoubleClicked(QTableWidgetItem*)));
    connect(ui_->sliderVolume, SIGNAL(valueChanged(int)), this, SLOT(onSliderVolumeValueChanged(int)));
    connect(ui_->sliderDuration, SIGNAL(sliderPressed()), this, SLOT(onSliderDurationPressed()));
    connect(ui_->sliderDuration, SIGNAL(sliderReleased()), this, SLOT(onSliderDurationReleased()));
    // UI initialisieren; Grssen, Texte, Inhalt etc.
    initUi();
}

ArkEchoPlayerView::~ArkEchoPlayerView()
{
    delete model_;
    delete player_;
    delete webSocketStatus_;
    delete ui_;
}

void ArkEchoPlayerView::initUi()
{
    this->setWindowTitle(DIALOGTITLE);

    // WebSocket Statusanzeige initalisieren
    webSocketStatus_ = new QLabel();
    ui_->statusBar->addPermanentWidget(webSocketStatus_);
    setWebSocketStatusLabel(false);

    // TableWidget initialisieren
    ui_->twTrackList->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui_->twTrackList->setSelectionBehavior(QAbstractItemView::SelectRows);
    ui_->twTrackList->verticalHeader()->setVisible(false);
    ui_->twTrackList->setColumnCount(TRACKL_MAX_COLUMN_COUNT);
    ui_->twTrackList->setHorizontalHeaderLabels(QString("Album;Nummer;Titel;Interpret;Dauer").split(";"));
    ui_->twTrackList->setColumnWidth(TRACKL_ALBUMTITLE, 200);
    ui_->twTrackList->setColumnWidth(TRACKL_ALBUMNUMBER, 55);
    ui_->twTrackList->setColumnWidth(TRACKL_SONGTITLE, 200);
    ui_->twTrackList->setColumnWidth(TRACKL_SONGINTERPRET, 200);
    ui_->twTrackList->setColumnWidth(TRACKL_SONGDURATION, 55);
    setTWTrackList();

    // Duration Anzeige
    ui_->lblDuration->setText("");

    // Volume initialisieren
    ui_->lblVolume->setText(QString::number(DEFAULT_VOLUME));
    ui_->sliderVolume->setValue(DEFAULT_VOLUME);
}

void ArkEchoPlayerView::setWebSocketStatusLabel(bool connected)
{
    if (!webSocketStatus_) return;
    QString message, color;
    if (connected)
    {
        message = "Verbunden!";
        color = "rgb(24,220,44)";
    }
    else
    {
        message = "Nicht Verbunden!";
        color = "red";
    }
    webSocketStatus_->setText(message);
    webSocketStatus_->setStyleSheet("color: "+color+";");
}

void ArkEchoPlayerView::setTWTrackList()
{
    if (!model_ || !ui_ || !model_->getMusicSongList()) return;

    QMap<int,MusicSong*> map = model_->getMusicSongList()->getSongList();
    int mapSize = map.size();
    if (mapSize == 0) return;
    ui_->twTrackList->setRowCount(mapSize);

    for (int i = 0; i < mapSize; ++i)
    {
        MusicSong* song = map.value(i);
        if (!song) continue;
        ui_->twTrackList->setRowHeight(i, ROW_HEIGHT);
        ui_->twTrackList->setItem(i, TRACKL_ALBUMTITLE, new QTableWidgetItem(song->getAlbumTitle(), i));
        ui_->twTrackList->setItem(i, TRACKL_ALBUMNUMBER, new QTableWidgetItem(QString::number(song->getAlbumSongNumber()), i));
        ui_->twTrackList->setItem(i, TRACKL_SONGTITLE, new QTableWidgetItem(song->getSongTitle(), i));
        ui_->twTrackList->setItem(i, TRACKL_SONGINTERPRET, new QTableWidgetItem(song->getSongInterpret(), i));
        ui_->twTrackList->setItem(i, TRACKL_SONGDURATION, new QTableWidgetItem(MusicSong::convertSongDurationToMinuteSecond(song->getSongDuration()), i));
    }
}

void ArkEchoPlayerView::setLblDuration()
{
    if (!player_) return;
    // MetaData und Player Song Lnge unterscheiden sich etwa um 560ms
    qint64 duration = player_->duration();
    if (duration == 0) return;
    QString durationOverall = MusicSong::convertSongDurationToMinuteSecond(duration - MEDIAPLAYER_BUFFER_DURATION);

    qint64 position = player_->position();
    if (position >= MEDIAPLAYER_BUFFER_DURATION) position -= MEDIAPLAYER_BUFFER_DURATION;
    QString durationNow = MusicSong::convertSongDurationToMinuteSecond(position);

    QString text = durationNow + "/" + durationOverall;
    ui_->lblDuration->setText(text);
}

void ArkEchoPlayerView::onUpdateView(const int &uve)
{
    switch(uve)
    {
    case UVE_WEBSOCKET_CONNECTED:
        setWebSocketStatusLabel(true);
        break;
    case UVE_WEBSOCKET_DISCONNECTED:
        setWebSocketStatusLabel(false);
        break;
    case REMOTE_BUTTON_BACKWARD:
        onPbPlay_PauseClicked();
        break;
    case REMOTE_BUTTON_FORWARD:
        onPbPlay_PauseClicked();
        break;
    case REMOTE_BUTTON_PLAY_PAUSE:
        onPbPlay_PauseClicked();
        break;
    }
    qApp->processEvents();
}

void ArkEchoPlayerView::on_actionManuelle_Verbindung_triggered()
{
    if (!model_) return;
    model_->showConnectManualDialog();
}

void ArkEchoPlayerView::on_actionQR_Code_Verbindung_triggered()
{
    if (!model_) return;
    model_->showConnectQrDialog();
}

void ArkEchoPlayerView::onPbBackwardClicked()
{
}

void ArkEchoPlayerView::onPbForwardClicked()
{
}

void ArkEchoPlayerView::onPbPlay_PauseClicked()
{
    if (!player_) return;
    if (player_->state() == QMediaPlayer::PlayingState) player_->pause();
    else player_->play();
}

void ArkEchoPlayerView::onPbStopClicked()
{
    if (!player_) return;
    player_->stop();
}

void ArkEchoPlayerView::onTwTrackListItemDoubleClicked(QTableWidgetItem * item)
{
    if (!item) return;
    int type = item->type();

    if (!model_ || !model_->getMusicSongList()) return;
    MusicSong* song = model_->getMusicSongList()->getSongList()[type];

    if (!player_ || !song) return;
    player_->setMedia(song->getUrl());
    player_->play();
    setLblDuration();
}

void ArkEchoPlayerView::onSliderVolumeValueChanged(const int &value)
{
    if (!player_) return;
    player_->setVolume(value);
    ui_->lblVolume->setText(QString::number(value));
}

void ArkEchoPlayerView::onPlayerPositionChanged(const qint64 & position)
{
    if (!player_) return;
    setLblDuration();
    double duration = (double)player_->duration();
    double positionD = (double)position;

    if (duration == 0) return;
    double value = (positionD / duration) * 100;
    ui_->sliderDuration->setValue(value);
}

void ArkEchoPlayerView::onSliderDurationPressed()
{
    if (!player_) return;
    player_->pause();
}

void ArkEchoPlayerView::onSliderDurationReleased()
{
    if (!player_) return;
    double duration = (double)player_->duration();
    double value = ui_->sliderDuration->value();
    double newPosition = (duration / 100) * value;
    player_->setPosition(newPosition);
    player_->play();
    setLblDuration();
}
<|endoftext|>


<commit_before>/*++
Copyright (c) 2006 Microsoft Corporation

Module Name:

    func_interp.cpp

Abstract:
    See func_interp.h

Author:

    Leonardo de Moura (leonardo) 2010-12-30.

Revision History:

--*/
#include"func_interp.h"
#include"var_subst.h"
#include"obj_hashtable.h"
#include"ast_pp.h"
#include"ast_smt2_pp.h"

func_entry::func_entry(ast_manager & m, unsigned arity, expr * const * args, expr * result):
    m_args_are_values(true),
    m_result(result) {
    SASSERT(is_ground(result));
    m.inc_ref(result);
    for (unsigned i = 0; i < arity; i++) {
        expr * arg = args[i];
        SASSERT(is_ground(arg));
        if (!m.is_value(arg))
            m_args_are_values = false;
        m.inc_ref(arg);
        m_args[i] = arg;
    }
}

func_entry * func_entry::mk(ast_manager & m, unsigned arity, expr * const * args, expr * result) {
    small_object_allocator & allocator = m.get_allocator();
    unsigned sz = get_obj_size(arity);
    void * mem  = allocator.allocate(sz);
    return new (mem) func_entry(m, arity, args, result);
}

void func_entry::set_result(ast_manager & m, expr * r) {
    m.inc_ref(r);
    m.dec_ref(m_result);
    m_result = r;
}

bool func_entry::eq_args(ast_manager & m, unsigned arity, expr * const * args) const {
    unsigned i = 0;
    for (; i < arity; i++) {
        if (!m.are_equal(m_args[i], args[i]))
            return false;
    }
    return true;
}

void func_entry::deallocate(ast_manager & m, unsigned arity) {
    for (unsigned i = 0; i < arity; i++) {
        m.dec_ref(m_args[i]);
    }
    m.dec_ref(m_result);
    small_object_allocator & allocator = m.get_allocator();
    unsigned sz = get_obj_size(arity);
    allocator.deallocate(sz, this);
}

func_interp::func_interp(ast_manager & m, unsigned arity):
    m_manager(m),
    m_arity(arity),
    m_else(0),
    m_args_are_values(true),
    m_interp(0) {
}

func_interp::~func_interp() {
    ptr_vector<func_entry>::iterator it  = m_entries.begin();
    ptr_vector<func_entry>::iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        curr->deallocate(m_manager, m_arity);
    }
    m_manager.dec_ref(m_else);
    m_manager.dec_ref(m_interp);
}

func_interp * func_interp::copy() const {
    func_interp * new_fi = alloc(func_interp, m_manager, m_arity);

    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        new_fi->insert_new_entry(curr->get_args(), curr->get_result());
    }
    new_fi->set_else(m_else);
    return new_fi;
}

void func_interp::reset_interp_cache() {
    m_manager.dec_ref(m_interp);
    m_interp = 0;
}

bool func_interp::is_fi_entry_expr(expr * e, ptr_vector<expr> & args) {
    args.reset();
    expr* c, *t, *f, *a0, *a1;
    if (!m().is_ite(e, c, t, f)) {
        return false;
    }

    if ((m_arity == 0) ||
        (m_arity == 1 && !m().is_eq(c, a1, a2)) ||
        (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity)))
        return false;

    args.resize(m_arity, 0);
    for (unsigned i = 0; i < m_arity; i++) {
        expr * ci = (m_arity == 1 && i == 0) ? c : to_app(c)->get_arg(i);

        if (!m().is_eq(ci, a0, a1))
            return false;

        if (is_var(a0) && to_var(a0)->get_idx() == i)
            args[i] = a1;
        else if (is_var(a1) && to_var(a1)->get_idx() == i)
            args[i] = a0;
        else
            return false;
    }

    return true;
}

void func_interp::set_else(expr * e) {
    if (e == m_else)
        return;

    reset_interp_cache();

    ptr_vector<expr> args;
    while (e && is_fi_entry_expr(e, args)) {
        TRACE("func_interp", tout << "fi entry expr: " << mk_ismt2_pp(e, m()) << std::endl;);
        insert_entry(args.c_ptr(), to_app(e)->get_arg(1));
        e = to_app(e)->get_arg(2);
    }

    m_manager.inc_ref(e);
    m_manager.dec_ref(m_else);
    m_else = e;
}

/**
   \brief Return true if the interpretation represents the constant function.
*/
bool func_interp::is_constant() const {
    if (is_partial())
        return false;
    if (!is_ground(m_else))
        return false;
    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        if (curr->get_result() != m_else)
            return false;
    }
    return true;
}

/**
   \brief Return a func_entry e such that m().are_equal(e.m_args[i], args[i]) for all i in [0, m_arity).
   If such entry does not exist then return 0, and store set
   args_are_values to true if for all entries e e.args_are_values() is true.
*/
func_entry * func_interp::get_entry(expr * const * args) const {
    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        if (curr->eq_args(m(), m_arity, args))
            return curr;
    }
    return 0;
}

void func_interp::insert_entry(expr * const * args, expr * r) {
    reset_interp_cache();
    func_entry * entry = get_entry(args);
    if (entry != 0) {
        entry->set_result(m_manager, r);
        return;
    }
    insert_new_entry(args, r);
}

void func_interp::insert_new_entry(expr * const * args, expr * r) {
    reset_interp_cache();
    CTRACE("func_interp_bug", get_entry(args) != 0,
           tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n";
           tout << "Args:";
           for (unsigned i = 0; i < m_arity; i++) {
               tout << mk_ismt2_pp(get_entry(args)->get_arg(i), m_manager) << "\n";
           }
           tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n";
           tout << "Args:";
           for (unsigned i = 0; i < m_arity; i++) {
               tout << mk_ismt2_pp(args[i], m_manager) << "\n";
           }
           tout << "Old: " << mk_ismt2_pp(get_entry(args)->get_result(), m_manager) << "\n";
           );
    SASSERT(get_entry(args) == 0);
    func_entry * new_entry = func_entry::mk(m_manager, m_arity, args, r);
    if (!new_entry->args_are_values())
        m_args_are_values = false;
    m_entries.push_back(new_entry);
}

bool func_interp::eval_else(expr * const * args, expr_ref & result) const {
    if (m_else == 0)
        return false;
    var_subst s(m_manager, false);
    SASSERT(!s.std_order()); // (VAR 0) <- args[0], (VAR 1) <- args[1], ...
    s(m_else, m_arity, args, result);
    return true;
}

/**
   \brief Return the result with the maximal number of occurrencies in m_entries.
*/
expr * func_interp::get_max_occ_result() const {
    if (m_entries.empty())
        return 0;
    obj_map<expr, unsigned> num_occs;
    expr *   r_max = 0;
    unsigned max   = 0;
    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        expr * r = curr->get_result();
        unsigned occs = 0;
        num_occs.find(r, occs);
        occs++;
        num_occs.insert(r, occs);
        if (occs > max) {
            max   = occs;
            r_max = r;
        }
    }
    return r_max;
}

/**
   \brief Remove entries e such that e.get_result() == m_else.
*/
void func_interp::compress() {
    if (m_else == 0 || m_entries.empty())
        return; // nothing to be done
    if (!is_ground(m_else))
        return; // forall entries e in m_entries e.get_result() is ground
    unsigned i = 0;
    unsigned j = 0;
    unsigned sz = m_entries.size();
    m_args_are_values = true;
    for (; i < sz; i++) {
        func_entry * curr = m_entries[i];
        if (curr->get_result() != m_else) {
            m_entries[j] = curr;
            j++;
            if (!curr->args_are_values())
                m_args_are_values = false;
        }
        else {
            curr->deallocate(m_manager, m_arity);
        }
    }
    if (j < sz) {
        reset_interp_cache();
        m_entries.shrink(j);
    }
}

expr * func_interp::get_interp_core() const {
    if (m_else == 0)
        return 0;
    expr * r = m_else;
    ptr_buffer<expr> vars;
    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        if (vars.empty()) {
            for (unsigned i = 0; i < m_arity; i++) {
                vars.push_back(m_manager.mk_var(i, m_manager.get_sort(curr->get_arg(i))));
            }
        }
        ptr_buffer<expr> eqs;
        for (unsigned i = 0; i < m_arity; i++) {
            eqs.push_back(m_manager.mk_eq(vars[i], curr->get_arg(i)));
        }
        SASSERT(eqs.size() == m_arity);
        expr * cond;
        if (m_arity == 1)
            cond = eqs.get(0);
        else
            cond = m_manager.mk_and(eqs.size(), eqs.c_ptr());
        r = m_manager.mk_ite(cond, curr->get_result(), r);
    }
    return r;
}

expr * func_interp::get_interp() const {
    if (m_interp != 0)
        return m_interp;
    expr * r = get_interp_core();
    if (r != 0) {
        const_cast<func_interp*>(this)->m_interp = r;
        m_manager.inc_ref(m_interp);
    }
    return r;
}

func_interp * func_interp::translate(ast_translation & translator) const {
    func_interp * new_fi = alloc(func_interp, translator.to(), m_arity);

    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        ptr_buffer<expr> new_args;
        for (unsigned i=0; i<m_arity; i++)
            new_args.push_back(translator(curr->get_arg(i)));
        new_fi->insert_new_entry(new_args.c_ptr(), translator(curr->get_result()));
    }
    new_fi->set_else(translator(m_else));
    return new_fi;
}
<commit_msg>update func_interp code<commit_after>/*++
Copyright (c) 2006 Microsoft Corporation

Module Name:

    func_interp.cpp

Abstract:
    See func_interp.h

Author:

    Leonardo de Moura (leonardo) 2010-12-30.

Revision History:

--*/
#include"func_interp.h"
#include"var_subst.h"
#include"obj_hashtable.h"
#include"ast_pp.h"
#include"ast_smt2_pp.h"

func_entry::func_entry(ast_manager & m, unsigned arity, expr * const * args, expr * result):
    m_args_are_values(true),
    m_result(result) {
    SASSERT(is_ground(result));
    m.inc_ref(result);
    for (unsigned i = 0; i < arity; i++) {
        expr * arg = args[i];
        SASSERT(is_ground(arg));
        if (!m.is_value(arg))
            m_args_are_values = false;
        m.inc_ref(arg);
        m_args[i] = arg;
    }
}

func_entry * func_entry::mk(ast_manager & m, unsigned arity, expr * const * args, expr * result) {
    small_object_allocator & allocator = m.get_allocator();
    unsigned sz = get_obj_size(arity);
    void * mem  = allocator.allocate(sz);
    return new (mem) func_entry(m, arity, args, result);
}

void func_entry::set_result(ast_manager & m, expr * r) {
    m.inc_ref(r);
    m.dec_ref(m_result);
    m_result = r;
}

bool func_entry::eq_args(ast_manager & m, unsigned arity, expr * const * args) const {
    unsigned i = 0;
    for (; i < arity; i++) {
        if (!m.are_equal(m_args[i], args[i]))
            return false;
    }
    return true;
}

void func_entry::deallocate(ast_manager & m, unsigned arity) {
    for (unsigned i = 0; i < arity; i++) {
        m.dec_ref(m_args[i]);
    }
    m.dec_ref(m_result);
    small_object_allocator & allocator = m.get_allocator();
    unsigned sz = get_obj_size(arity);
    allocator.deallocate(sz, this);
}

func_interp::func_interp(ast_manager & m, unsigned arity):
    m_manager(m),
    m_arity(arity),
    m_else(0),
    m_args_are_values(true),
    m_interp(0) {
}

func_interp::~func_interp() {
    ptr_vector<func_entry>::iterator it  = m_entries.begin();
    ptr_vector<func_entry>::iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        curr->deallocate(m_manager, m_arity);
    }
    m_manager.dec_ref(m_else);
    m_manager.dec_ref(m_interp);
}

func_interp * func_interp::copy() const {
    func_interp * new_fi = alloc(func_interp, m_manager, m_arity);

    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        new_fi->insert_new_entry(curr->get_args(), curr->get_result());
    }
    new_fi->set_else(m_else);
    return new_fi;
}

void func_interp::reset_interp_cache() {
    m_manager.dec_ref(m_interp);
    m_interp = 0;
}

bool func_interp::is_fi_entry_expr(expr * e, ptr_vector<expr> & args) {
    args.reset();
    expr* c, *t, *f, *a0, *a1;
    if (!m().is_ite(e, c, t, f)) {
        return false;
    }

    if ((m_arity == 0) ||
        (m_arity == 1 && !m().is_eq(c, a0, a1)) ||
        (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity)))
        return false;

    args.resize(m_arity, 0);
    for (unsigned i = 0; i < m_arity; i++) {
        expr * ci = (m_arity == 1 && i == 0) ? c : to_app(c)->get_arg(i);

        if (!m().is_eq(ci, a0, a1))
            return false;

        if (is_var(a0) && to_var(a0)->get_idx() == i)
            args[i] = a1;
        else if (is_var(a1) && to_var(a1)->get_idx() == i)
            args[i] = a0;
        else
            return false;
    }

    return true;
}

void func_interp::set_else(expr * e) {
    if (e == m_else)
        return;

    reset_interp_cache();

    ptr_vector<expr> args;
    while (e && is_fi_entry_expr(e, args)) {
        TRACE("func_interp", tout << "fi entry expr: " << mk_ismt2_pp(e, m()) << std::endl;);
        insert_entry(args.c_ptr(), to_app(e)->get_arg(1));
        e = to_app(e)->get_arg(2);
    }

    m_manager.inc_ref(e);
    m_manager.dec_ref(m_else);
    m_else = e;
}

/**
   \brief Return true if the interpretation represents the constant function.
*/
bool func_interp::is_constant() const {
    if (is_partial())
        return false;
    if (!is_ground(m_else))
        return false;
    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        if (curr->get_result() != m_else)
            return false;
    }
    return true;
}

/**
   \brief Return a func_entry e such that m().are_equal(e.m_args[i], args[i]) for all i in [0, m_arity).
   If such entry does not exist then return 0, and store set
   args_are_values to true if for all entries e e.args_are_values() is true.
*/
func_entry * func_interp::get_entry(expr * const * args) const {
    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        if (curr->eq_args(m(), m_arity, args))
            return curr;
    }
    return 0;
}

void func_interp::insert_entry(expr * const * args, expr * r) {
    reset_interp_cache();
    func_entry * entry = get_entry(args);
    if (entry != 0) {
        entry->set_result(m_manager, r);
        return;
    }
    insert_new_entry(args, r);
}

void func_interp::insert_new_entry(expr * const * args, expr * r) {
    reset_interp_cache();
    CTRACE("func_interp_bug", get_entry(args) != 0,
           tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n";
           tout << "Args:";
           for (unsigned i = 0; i < m_arity; i++) {
               tout << mk_ismt2_pp(get_entry(args)->get_arg(i), m_manager) << "\n";
           }
           tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n";
           tout << "Args:";
           for (unsigned i = 0; i < m_arity; i++) {
               tout << mk_ismt2_pp(args[i], m_manager) << "\n";
           }
           tout << "Old: " << mk_ismt2_pp(get_entry(args)->get_result(), m_manager) << "\n";
           );
    SASSERT(get_entry(args) == 0);
    func_entry * new_entry = func_entry::mk(m_manager, m_arity, args, r);
    if (!new_entry->args_are_values())
        m_args_are_values = false;
    m_entries.push_back(new_entry);
}

bool func_interp::eval_else(expr * const * args, expr_ref & result) const {
    if (m_else == 0)
        return false;
    var_subst s(m_manager, false);
    SASSERT(!s.std_order()); // (VAR 0) <- args[0], (VAR 1) <- args[1], ...
    s(m_else, m_arity, args, result);
    return true;
}

/**
   \brief Return the result with the maximal number of occurrencies in m_entries.
*/
expr * func_interp::get_max_occ_result() const {
    if (m_entries.empty())
        return 0;
    obj_map<expr, unsigned> num_occs;
    expr *   r_max = 0;
    unsigned max   = 0;
    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        expr * r = curr->get_result();
        unsigned occs = 0;
        num_occs.find(r, occs);
        occs++;
        num_occs.insert(r, occs);
        if (occs > max) {
            max   = occs;
            r_max = r;
        }
    }
    return r_max;
}

/**
   \brief Remove entries e such that e.get_result() == m_else.
*/
void func_interp::compress() {
    if (m_else == 0 || m_entries.empty())
        return; // nothing to be done
    if (!is_ground(m_else))
        return; // forall entries e in m_entries e.get_result() is ground
    unsigned i = 0;
    unsigned j = 0;
    unsigned sz = m_entries.size();
    m_args_are_values = true;
    for (; i < sz; i++) {
        func_entry * curr = m_entries[i];
        if (curr->get_result() != m_else) {
            m_entries[j] = curr;
            j++;
            if (!curr->args_are_values())
                m_args_are_values = false;
        }
        else {
            curr->deallocate(m_manager, m_arity);
        }
    }
    if (j < sz) {
        reset_interp_cache();
        m_entries.shrink(j);
    }
}

expr * func_interp::get_interp_core() const {
    if (m_else == 0)
        return 0;
    expr * r = m_else;
    ptr_buffer<expr> vars;
    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        if (vars.empty()) {
            for (unsigned i = 0; i < m_arity; i++) {
                vars.push_back(m_manager.mk_var(i, m_manager.get_sort(curr->get_arg(i))));
            }
        }
        ptr_buffer<expr> eqs;
        for (unsigned i = 0; i < m_arity; i++) {
            eqs.push_back(m_manager.mk_eq(vars[i], curr->get_arg(i)));
        }
        SASSERT(eqs.size() == m_arity);
        expr * cond;
        if (m_arity == 1)
            cond = eqs.get(0);
        else
            cond = m_manager.mk_and(eqs.size(), eqs.c_ptr());
        r = m_manager.mk_ite(cond, curr->get_result(), r);
    }
    return r;
}

expr * func_interp::get_interp() const {
    if (m_interp != 0)
        return m_interp;
    expr * r = get_interp_core();
    if (r != 0) {
        const_cast<func_interp*>(this)->m_interp = r;
        m_manager.inc_ref(m_interp);
    }
    return r;
}

func_interp * func_interp::translate(ast_translation & translator) const {
    func_interp * new_fi = alloc(func_interp, translator.to(), m_arity);

    ptr_vector<func_entry>::const_iterator it  = m_entries.begin();
    ptr_vector<func_entry>::const_iterator end = m_entries.end();
    for (; it != end; ++it) {
        func_entry * curr = *it;
        ptr_buffer<expr> new_args;
        for (unsigned i=0; i<m_arity; i++)
            new_args.push_back(translator(curr->get_arg(i)));
        new_fi->insert_new_entry(new_args.c_ptr(), translator(curr->get_result()));
    }
    new_fi->set_else(translator(m_else));
    return new_fi;
}
<|endoftext|>