<commit_before>#include "imgui.h"

#include <babylon/buffers/vertex_buffer.h>
#include <babylon/cameras/arc_rotate_camera.h>
#include <babylon/core/random.h>
#include <babylon/interfaces/irenderable_scene_with_hud.h>
#include <babylon/lights/hemispheric_light.h>
#include <babylon/materials/pbr/pbr_material.h>
#include <babylon/materials/textures/hdr_cube_texture.h>
#include <babylon/meshes/mesh.h>
#include <babylon/meshes/vertex_data.h>
#include <babylon/morph/morph_target_manager.h>
#include <babylon/samples/babylon_register_sample.h>

namespace BABYLON {
namespace Samples {

using MeshPtr        = std::shared_ptr<Mesh>;
using MorphTargetPtr = std::shared_ptr<MorphTarget>;

/**
 * @brief Morph Targets Scene. Example demonstrating how to morph a mesh between multiple targets
 * @see https://www.babylonjs-playground.com/#2JDN66#7
 * @see https://doc.babylonjs.com/how_to/how_to_use_morphtargets
 */
struct MorphTargetsScene : public IRenderableSceneWithHud {

  MorphTargetsScene(ICanvas* iCanvas = nullptr) : IRenderableSceneWithHud(iCanvas)
  {
  }

  ~MorphTargetsScene() override = default;

  const char* getName() override
  {
    return "Materials Scene";
  }

  void initializeScene(ICanvas* canvas, Scene* scene) override
  {
    // This creates and positions a free camera (non-mesh)
    auto camera
      = ArcRotateCamera::New(std::string("camera1"), 1.14f, 1.13f, 10.f, Vector3::Zero(), scene);

    // This targets the camera to scene origin
    camera->setTarget(Vector3::Zero());

    // This attaches the camera to the canvas
    camera->attachControl(canvas, true);

    // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
    auto light = HemisphericLight::New("light1", Vector3(0.f, 1.f, 0.f), scene);

    // Default intensity is 1. Let's dim the light a small amount
    light->intensity = 0.f;

    // Our built-in 'sphere' shape. Params: name, subdivs, size, scene
    auto sphere = Mesh::CreateSphere("sphere1", 16, 2.f, scene);

    auto hdrTexture = HDRCubeTexture::New("/textures/room.hdr", scene, 512);

    auto exposure                         = 0.6f;
    auto contrast                         = 1.6f;
    auto glass                            = PBRMaterial::New("glass", scene);
    glass->reflectionTexture              = hdrTexture;
    glass->refractionTexture              = hdrTexture;
    glass->linkRefractionWithTransparency = true;
    glass->indexOfRefraction              = 0.52f;
    glass->alpha                          = 0.f;
    glass->cameraExposure                 = exposure;
    glass->cameraContrast                 = contrast;
    glass->microSurface                   = 1.f;
    glass->reflectivityColor              = Color3(0.2f, 0.2f, 0.2f);
    glass->albedoColor                    = Color3(0.85f, 0.85f, 0.85f);
    sphere->material                      = glass;

    auto sphere2 = Mesh::CreateSphere("sphere2", 16, 2.f, scene);
    sphere2->setEnabled(false);
    _addSpike(sphere2);

    auto sphere3 = Mesh::CreateSphere("sphere3", 16, 2.f, scene);
    sphere3->setEnabled(false);
    _addSpike(sphere3);

    auto sphere4 = Mesh::CreateSphere("sphere4", 16, 2.f, scene);
    sphere4->setEnabled(false);
    _addSpike(sphere4);

    auto sphere5 = Mesh::CreateSphere("sphere5", 16, 2.f, scene);
    sphere5->setEnabled(false);
    _addSpike(sphere5);

    auto manager               = MorphTargetManager::New();
    sphere->morphTargetManager = manager;

    _target0 = MorphTarget::FromMesh(sphere2, "sphere2", 0.25f);
    manager->addTarget(_target0);

    _target1 = MorphTarget::FromMesh(sphere3, "sphere3", 0.25f);
    manager->addTarget(_target1);

    _target2 = MorphTarget::FromMesh(sphere4, "sphere4", 0.25f);
    manager->addTarget(_target2);

    _target3 = MorphTarget::FromMesh(sphere5, "sphere5", 0.25f);
    manager->addTarget(_target3);

    // Set influences
    _target0->influence = 0.25f;
    _target1->influence = 0.50f;
    _target2->influence = 0.75f;
    _target3->influence = 1.00f;

    hudGui = [=]() {
      auto addSlider
        = [](const std::string& label, auto& floatProperty, float min = 0.f, float max = 1.f) {
            float currentValue = floatProperty;
            if (ImGui::SliderFloat(label.c_str(), &currentValue, min, max))
              floatProperty = currentValue;
          };
      addSlider("Influence #1", _target0->influence);
      addSlider("Influence #2", _target1->influence);
      addSlider("Influence #3", _target2->influence);
      addSlider("Influence #4", _target3->influence);
      addSlider("cameraContrast", glass->cameraContrast);
      addSlider("cameraExposure", glass->cameraExposure, 0., 2.);
      addSlider("microSurface", glass->microSurface, 0., 2.);
      addSlider("indexOfRefraction", glass->indexOfRefraction, 0., 2.);
      addSlider("alpha", glass->alpha, 0., 2.);
    };
  }

private:
  void _addSpike(const MeshPtr& mesh)
  {
    auto positions = mesh->getVerticesData(VertexBuffer::PositionKind);
    auto normals   = mesh->getVerticesData(VertexBuffer::NormalKind);
    auto indices   = mesh->getIndices();

    for (size_t index = 0; index < 5; ++index) {
      auto randomVertexID = static_cast<unsigned int>(mesh->getTotalVertices() * Math::random());
      auto position       = Vector3::FromArray(positions, randomVertexID * 3);
      auto normal         = Vector3::FromArray(normals, randomVertexID * 3);

      position.addInPlace(normal);

      position.toArray(positions, randomVertexID * 3);
    }

    VertexData::ComputeNormals(positions, indices, normals);
    mesh->updateVerticesData(VertexBuffer::PositionKind, positions, false, false);
    mesh->updateVerticesData(VertexBuffer::NormalKind, normals, false, false);
  }

private:
  using MeshPtr        = std::shared_ptr<Mesh>;
  using MorphTargetPtr = std::shared_ptr<MorphTarget>;

  MorphTargetPtr _target0;
  MorphTargetPtr _target1;
  MorphTargetPtr _target2;
  MorphTargetPtr _target3;
};

std::shared_ptr<BABYLON::IRenderableScene> MakeMorphTargetsScene(ICanvas* iCanvas)
{
  return std::make_shared<MorphTargetsScene>(iCanvas);
}

BABYLON_REGISTER_SAMPLE("Animations", MorphTargetsScene)

} // end of namespace Samples
} // end of namespace BABYLON
<commit_msg>Using macro for forward declarations<commit_after>#include "imgui.h"

#include <babylon/babylon_fwd.h>
#include <babylon/buffers/vertex_buffer.h>
#include <babylon/cameras/arc_rotate_camera.h>
#include <babylon/core/random.h>
#include <babylon/interfaces/irenderable_scene_with_hud.h>
#include <babylon/lights/hemispheric_light.h>
#include <babylon/materials/pbr/pbr_material.h>
#include <babylon/materials/textures/hdr_cube_texture.h>
#include <babylon/meshes/mesh.h>
#include <babylon/meshes/vertex_data.h>
#include <babylon/morph/morph_target_manager.h>
#include <babylon/samples/babylon_register_sample.h>

namespace BABYLON {
namespace Samples {

FWD_CLASS_SPTR(Mesh)
FWD_CLASS_SPTR(MorphTarget)

/**
 * @brief Morph Targets Scene. Example demonstrating how to morph a mesh between multiple targets
 * @see https://www.babylonjs-playground.com/#2JDN66#7
 * @see https://doc.babylonjs.com/how_to/how_to_use_morphtargets
 */
struct MorphTargetsScene : public IRenderableSceneWithHud {

  MorphTargetsScene(ICanvas* iCanvas = nullptr) : IRenderableSceneWithHud(iCanvas)
  {
  }

  ~MorphTargetsScene() override = default;

  const char* getName() override
  {
    return "Materials Scene";
  }

  void initializeScene(ICanvas* canvas, Scene* scene) override
  {
    // This creates and positions a free camera (non-mesh)
    auto camera
      = ArcRotateCamera::New(std::string("camera1"), 1.14f, 1.13f, 10.f, Vector3::Zero(), scene);

    // This targets the camera to scene origin
    camera->setTarget(Vector3::Zero());

    // This attaches the camera to the canvas
    camera->attachControl(canvas, true);

    // This creates a light, aiming 0,1,0 - to the sky (non-mesh)
    auto light = HemisphericLight::New("light1", Vector3(0.f, 1.f, 0.f), scene);

    // Default intensity is 1. Let's dim the light a small amount
    light->intensity = 0.f;

    // Our built-in 'sphere' shape. Params: name, subdivs, size, scene
    auto sphere = Mesh::CreateSphere("sphere1", 16, 2.f, scene);

    auto hdrTexture = HDRCubeTexture::New("/textures/room.hdr", scene, 512);

    auto exposure                         = 0.6f;
    auto contrast                         = 1.6f;
    auto glass                            = PBRMaterial::New("glass", scene);
    glass->reflectionTexture              = hdrTexture;
    glass->refractionTexture              = hdrTexture;
    glass->linkRefractionWithTransparency = true;
    glass->indexOfRefraction              = 0.52f;
    glass->alpha                          = 0.f;
    glass->cameraExposure                 = exposure;
    glass->cameraContrast                 = contrast;
    glass->microSurface                   = 1.f;
    glass->reflectivityColor              = Color3(0.2f, 0.2f, 0.2f);
    glass->albedoColor                    = Color3(0.85f, 0.85f, 0.85f);
    sphere->material                      = glass;

    auto sphere2 = Mesh::CreateSphere("sphere2", 16, 2.f, scene);
    sphere2->setEnabled(false);
    _addSpike(sphere2);

    auto sphere3 = Mesh::CreateSphere("sphere3", 16, 2.f, scene);
    sphere3->setEnabled(false);
    _addSpike(sphere3);

    auto sphere4 = Mesh::CreateSphere("sphere4", 16, 2.f, scene);
    sphere4->setEnabled(false);
    _addSpike(sphere4);

    auto sphere5 = Mesh::CreateSphere("sphere5", 16, 2.f, scene);
    sphere5->setEnabled(false);
    _addSpike(sphere5);

    auto manager               = MorphTargetManager::New();
    sphere->morphTargetManager = manager;

    _target0 = MorphTarget::FromMesh(sphere2, "sphere2", 0.25f);
    manager->addTarget(_target0);

    _target1 = MorphTarget::FromMesh(sphere3, "sphere3", 0.25f);
    manager->addTarget(_target1);

    _target2 = MorphTarget::FromMesh(sphere4, "sphere4", 0.25f);
    manager->addTarget(_target2);

    _target3 = MorphTarget::FromMesh(sphere5, "sphere5", 0.25f);
    manager->addTarget(_target3);

    // Set influences
    _target0->influence = 0.25f;
    _target1->influence = 0.50f;
    _target2->influence = 0.75f;
    _target3->influence = 1.00f;

    hudGui = [=]() {
      auto addSlider
        = [](const std::string& label, auto& floatProperty, float min = 0.f, float max = 1.f) {
            float currentValue = floatProperty;
            if (ImGui::SliderFloat(label.c_str(), &currentValue, min, max))
              floatProperty = currentValue;
          };
      addSlider("Influence #1", _target0->influence);
      addSlider("Influence #2", _target1->influence);
      addSlider("Influence #3", _target2->influence);
      addSlider("Influence #4", _target3->influence);
      addSlider("cameraContrast", glass->cameraContrast);
      addSlider("cameraExposure", glass->cameraExposure, 0., 2.);
      addSlider("microSurface", glass->microSurface, 0., 2.);
      addSlider("indexOfRefraction", glass->indexOfRefraction, 0., 2.);
      addSlider("alpha", glass->alpha, 0., 2.);
    };
  }

private:
  void _addSpike(const MeshPtr& mesh)
  {
    auto positions = mesh->getVerticesData(VertexBuffer::PositionKind);
    auto normals   = mesh->getVerticesData(VertexBuffer::NormalKind);
    auto indices   = mesh->getIndices();

    for (size_t index = 0; index < 5; ++index) {
      auto randomVertexID = static_cast<unsigned int>(mesh->getTotalVertices() * Math::random());
      auto position       = Vector3::FromArray(positions, randomVertexID * 3);
      auto normal         = Vector3::FromArray(normals, randomVertexID * 3);

      position.addInPlace(normal);

      position.toArray(positions, randomVertexID * 3);
    }

    VertexData::ComputeNormals(positions, indices, normals);
    mesh->updateVerticesData(VertexBuffer::PositionKind, positions, false, false);
    mesh->updateVerticesData(VertexBuffer::NormalKind, normals, false, false);
  }

private:
  using MeshPtr        = std::shared_ptr<Mesh>;
  using MorphTargetPtr = std::shared_ptr<MorphTarget>;

  MorphTargetPtr _target0;
  MorphTargetPtr _target1;
  MorphTargetPtr _target2;
  MorphTargetPtr _target3;
};

std::shared_ptr<BABYLON::IRenderableScene> MakeMorphTargetsScene(ICanvas* iCanvas)
{
  return std::make_shared<MorphTargetsScene>(iCanvas);
}

BABYLON_REGISTER_SAMPLE("Animations", MorphTargetsScene)

} // end of namespace Samples
} // end of namespace BABYLON
<|endoftext|>


<commit_before>#include "chrono_parallel/lcp/ChLcpSolverParallel.h"
#include "chrono_parallel/math/ChThrustLinearAlgebra.h"

#include "chrono_parallel/solver/ChSolverAPGD.h"
#include "chrono_parallel/solver/ChSolverAPGDREF.h"
#include "chrono_parallel/solver/ChSolverBiCG.h"
#include "chrono_parallel/solver/ChSolverBiCGStab.h"
#include "chrono_parallel/solver/ChSolverCG.h"
#include "chrono_parallel/solver/ChSolverCGS.h"
#include "chrono_parallel/solver/ChSolverMinRes.h"
#include "chrono_parallel/solver/ChSolverSD.h"
#include "chrono_parallel/solver/ChSolverGD.h"
#include "chrono_parallel/solver/ChSolverPGS.h"
#include "chrono_parallel/solver/ChSolverJacobi.h"
#include "chrono_parallel/solver/ChSolverPDIP.h"
using namespace chrono;

void ChLcpSolverParallelDVI::RunTimeStep(real step)
{
  // Setup constants and other values for system
  data_container->settings.step_size = step;
  data_container->settings.solver.tol_speed = step * data_container->settings.solver.tolerance;

  // Compute the offsets and number of constrains depending on the solver mode
  if (data_container->settings.solver.solver_mode == NORMAL) {
    rigid_rigid.offset = 1;
    data_container->num_unilaterals = 1 * data_container->num_contacts;
  } else if (data_container->settings.solver.solver_mode == SLIDING) {
    rigid_rigid.offset = 3;
    data_container->num_unilaterals = 3 * data_container->num_contacts;
  } else if (data_container->settings.solver.solver_mode == SPINNING) {
    rigid_rigid.offset = 6;
    data_container->num_unilaterals = 6 * data_container->num_contacts;
  }
  // This is the total number of constraints
  data_container->num_constraints = data_container->num_unilaterals + data_container->num_bilaterals;
  // This is the total number of degrees of freedom in the system
  data_container->num_dof = data_container->num_bodies * 6 + data_container->num_shafts;

  // Generate the mass matrix and compute M_inv_k
  ComputeMassMatrix();

  data_container->host_data.gamma.resize(data_container->num_constraints);
  data_container->host_data.gamma.reset();

  // Perform any setup tasks for all constraint types
  rigid_rigid.Setup(data_container);
  bilateral.Setup(data_container);
  // Clear and reset solver history data and counters
  solver->current_iteration = 0;
  data_container->measures.solver.total_iteration = 0;
  data_container->measures.solver.maxd_hist.clear();
  data_container->measures.solver.maxdeltalambda_hist.clear();
  data_container->measures.solver.iter_hist.clear();
  // Set pointers to constraint objects and perform setup actions for solver
  solver->rigid_rigid = &rigid_rigid;
  solver->bilateral = &bilateral;
  solver->Setup(data_container);

  ComputeD();
  ComputeE();
  ComputeR();

  // solve for the normal
  if (data_container->settings.solver.solver_mode == NORMAL || data_container->settings.solver.solver_mode == SLIDING || data_container->settings.solver.solver_mode == SPINNING) {
    if (data_container->settings.solver.max_iteration_normal > 0) {
      solver->SetMaxIterations(data_container->settings.solver.max_iteration_normal);
      data_container->settings.solver.local_solver_mode = NORMAL;
      data_container->system_timer.start("ChLcpSolverParallel_Solve");
      PerformStabilization();
      solver->Solve();
      data_container->system_timer.stop("ChLcpSolverParallel_Solve");
    }
  }
  if (data_container->settings.solver.solver_mode != NORMAL) {
    if (data_container->settings.solver.max_iteration_sliding > 0) {
      solver->SetMaxIterations(data_container->settings.solver.max_iteration_sliding);
      data_container->settings.solver.local_solver_mode = SLIDING;
      data_container->system_timer.start("ChLcpSolverParallel_Solve");
      PerformStabilization();
      solver->Solve();
      data_container->system_timer.stop("ChLcpSolverParallel_Solve");
    }
  }
  if (data_container->settings.solver.solver_mode == SPINNING) {
    if (data_container->settings.solver.max_iteration_spinning > 0) {
      solver->SetMaxIterations(data_container->settings.solver.max_iteration_spinning);
      data_container->settings.solver.local_solver_mode = SPINNING;
      data_container->system_timer.start("ChLcpSolverParallel_Solve");
      PerformStabilization();
      solver->Solve();
      data_container->system_timer.stop("ChLcpSolverParallel_Solve");
    }
  }

  ComputeImpulses();

  for (int i = 0; i < data_container->measures.solver.iter_hist.size(); i++) {
    AtIterationEnd(data_container->measures.solver.maxd_hist[i], data_container->measures.solver.maxdeltalambda_hist[i], data_container->measures.solver.iter_hist[i]);
  }
  tot_iterations = data_container->measures.solver.iter_hist.size();

#if PRINT_LEVEL == 2
  std::cout << "Solve Done: " << residual << std::endl;
#endif
}

void ChLcpSolverParallelDVI::ComputeD()
{
  uint num_constraints = data_container->num_constraints;
  if (num_constraints <= 0) {
    return;
  }

  uint num_bodies = data_container->num_bodies;
  uint num_shafts = data_container->num_shafts;
  uint num_dof = data_container->num_dof;
  uint num_contacts = data_container->num_contacts;
  uint num_bilaterals = data_container->num_bilaterals;
  uint nnz_bilaterals = data_container->nnz_bilaterals;

  int nnz_normal = 6 * 2 * data_container->num_contacts;
  int nnz_tangential = 6 * 4 * data_container->num_contacts;
  int nnz_spinning = 6 * 3 * data_container->num_contacts;

  int num_normal = 1 * data_container->num_contacts;
  int num_tangential = 2 * data_container->num_contacts;
  int num_spinning = 3 * data_container->num_contacts;

  CompressedMatrix<real>& D_n_T = data_container->host_data.D_n_T;
  CompressedMatrix<real>& D_t_T = data_container->host_data.D_t_T;
  CompressedMatrix<real>& D_s_T = data_container->host_data.D_s_T;
  CompressedMatrix<real>& D_b_T = data_container->host_data.D_b_T;

  CompressedMatrix<real>& D_n = data_container->host_data.D_n;
  CompressedMatrix<real>& D_t = data_container->host_data.D_t;
  CompressedMatrix<real>& D_s = data_container->host_data.D_s;
  CompressedMatrix<real>& D_b = data_container->host_data.D_b;

  CompressedMatrix<real>& M_invD_n = data_container->host_data.M_invD_n;
  CompressedMatrix<real>& M_invD_t = data_container->host_data.M_invD_t;
  CompressedMatrix<real>& M_invD_s = data_container->host_data.M_invD_s;
  CompressedMatrix<real>& M_invD_b = data_container->host_data.M_invD_b;

  const CompressedMatrix<real>& M_inv = data_container->host_data.M_inv;

  switch (data_container->settings.solver.solver_mode) {
    case NORMAL:
      clear(D_n_T);

      D_n_T.reserve(nnz_normal);

      D_n_T.resize(num_normal, num_dof, false);
      break;
    case SLIDING:
      clear(D_n_T);
      clear(D_t_T);

      D_n_T.reserve(nnz_normal);
      D_t_T.reserve(nnz_tangential);

      D_n_T.resize(num_normal, num_dof, false);
      D_t_T.resize(num_tangential, num_dof, false);
      break;
    case SPINNING:
      clear(D_n_T);
      clear(D_t_T);
      clear(D_s_T);

      D_n_T.reserve(nnz_normal);
      D_t_T.reserve(nnz_tangential);
      D_s_T.reserve(nnz_spinning);

      D_n_T.resize(num_normal, num_dof, false);
      D_t_T.resize(num_tangential, num_dof, false);
      D_s_T.resize(num_spinning, num_dof, false);
      break;
  }

  clear(D_b_T);
  D_b_T.reserve(nnz_bilaterals);

  rigid_rigid.GenerateSparsity();
  bilateral.GenerateSparsity();
  rigid_rigid.Build_D();
  bilateral.Build_D();

  D_n = trans(D_n_T);
  D_t = trans(D_t_T);
  D_s = trans(D_s_T);
  D_b = trans(D_b_T);

  M_invD_n = M_inv * D_n;
  M_invD_t = M_inv * D_t;
  M_invD_s = M_inv * D_s;
  M_invD_b = M_inv * D_b;
}

void ChLcpSolverParallelDVI::ComputeE()
{
  if (data_container->num_constraints <= 0) {
    return;
  }

  data_container->host_data.E.resize(data_container->num_constraints);
  reset(data_container->host_data.E);

  rigid_rigid.Build_E();
  bilateral.Build_E();
}

void ChLcpSolverParallelDVI::ComputeR()
{
  if (data_container->num_constraints <= 0) {
    return;
  }

  data_container->host_data.b.resize(data_container->num_constraints);
  reset(data_container->host_data.b);

  rigid_rigid.Build_b();
  bilateral.Build_b();

  data_container->host_data.R = -data_container->host_data.b - data_container->host_data.D_T * data_container->host_data.M_invk;
}

void ChLcpSolverParallelDVI::ChangeSolverType(SOLVERTYPE type) {
  data_container->settings.solver.solver_type = type;

  if (this->solver) {
    delete (this->solver);
  }
  if (type == STEEPEST_DESCENT) {
    solver = new ChSolverSD();
  } else if (type == GRADIENT_DESCENT) {
    solver = new ChSolverGD();
  } else if (type == CONJUGATE_GRADIENT) {
    solver = new ChSolverCG();
  } else if (type == CONJUGATE_GRADIENT_SQUARED) {
    solver = new ChSolverCGS();
  } else if (type == BICONJUGATE_GRADIENT) {
    solver = new ChSolverBiCG();
  } else if (type == BICONJUGATE_GRADIENT_STAB) {
    solver = new ChSolverBiCGStab();
  } else if (type == MINIMUM_RESIDUAL) {
    solver = new ChSolverMinRes();
  } else if (type == QUASAI_MINIMUM_RESIDUAL) {
    //         // This solver has not been implemented yet
    //         //SolveQMR(data_container->gpu_data.device_gam_data, rhs, max_iteration);
  } else if (type == APGD) {
    solver = new ChSolverAPGD();
  } else if (type == APGDREF) {
    solver = new ChSolverAPGDREF();
  } else if (type == JACOBI) {
    solver = new ChSolverJacobi();
  } else if (type == GAUSS_SEIDEL) {
    solver = new ChSolverPGS();
  } else if (type == PDIP) {
    solver = new ChSolverPDIP();
  }
}
<commit_msg>Update the ComputeR function with the new split jacobians<commit_after>#include "chrono_parallel/lcp/ChLcpSolverParallel.h"
#include "chrono_parallel/math/ChThrustLinearAlgebra.h"

#include "chrono_parallel/solver/ChSolverAPGD.h"
#include "chrono_parallel/solver/ChSolverAPGDREF.h"
#include "chrono_parallel/solver/ChSolverBiCG.h"
#include "chrono_parallel/solver/ChSolverBiCGStab.h"
#include "chrono_parallel/solver/ChSolverCG.h"
#include "chrono_parallel/solver/ChSolverCGS.h"
#include "chrono_parallel/solver/ChSolverMinRes.h"
#include "chrono_parallel/solver/ChSolverSD.h"
#include "chrono_parallel/solver/ChSolverGD.h"
#include "chrono_parallel/solver/ChSolverPGS.h"
#include "chrono_parallel/solver/ChSolverJacobi.h"
#include "chrono_parallel/solver/ChSolverPDIP.h"
using namespace chrono;

void ChLcpSolverParallelDVI::RunTimeStep(real step)
{
  // Setup constants and other values for system
  data_container->settings.step_size = step;
  data_container->settings.solver.tol_speed = step * data_container->settings.solver.tolerance;

  // Compute the offsets and number of constrains depending on the solver mode
  if (data_container->settings.solver.solver_mode == NORMAL) {
    rigid_rigid.offset = 1;
    data_container->num_unilaterals = 1 * data_container->num_contacts;
  } else if (data_container->settings.solver.solver_mode == SLIDING) {
    rigid_rigid.offset = 3;
    data_container->num_unilaterals = 3 * data_container->num_contacts;
  } else if (data_container->settings.solver.solver_mode == SPINNING) {
    rigid_rigid.offset = 6;
    data_container->num_unilaterals = 6 * data_container->num_contacts;
  }
  // This is the total number of constraints
  data_container->num_constraints = data_container->num_unilaterals + data_container->num_bilaterals;
  // This is the total number of degrees of freedom in the system
  data_container->num_dof = data_container->num_bodies * 6 + data_container->num_shafts;

  // Generate the mass matrix and compute M_inv_k
  ComputeMassMatrix();

  data_container->host_data.gamma.resize(data_container->num_constraints);
  data_container->host_data.gamma.reset();

  // Perform any setup tasks for all constraint types
  rigid_rigid.Setup(data_container);
  bilateral.Setup(data_container);
  // Clear and reset solver history data and counters
  solver->current_iteration = 0;
  data_container->measures.solver.total_iteration = 0;
  data_container->measures.solver.maxd_hist.clear();
  data_container->measures.solver.maxdeltalambda_hist.clear();
  data_container->measures.solver.iter_hist.clear();
  // Set pointers to constraint objects and perform setup actions for solver
  solver->rigid_rigid = &rigid_rigid;
  solver->bilateral = &bilateral;
  solver->Setup(data_container);

  ComputeD();
  ComputeE();
  ComputeR();

  // solve for the normal
  if (data_container->settings.solver.solver_mode == NORMAL || data_container->settings.solver.solver_mode == SLIDING || data_container->settings.solver.solver_mode == SPINNING) {
    if (data_container->settings.solver.max_iteration_normal > 0) {
      solver->SetMaxIterations(data_container->settings.solver.max_iteration_normal);
      data_container->settings.solver.local_solver_mode = NORMAL;
      data_container->system_timer.start("ChLcpSolverParallel_Solve");
      PerformStabilization();
      solver->Solve();
      data_container->system_timer.stop("ChLcpSolverParallel_Solve");
    }
  }
  if (data_container->settings.solver.solver_mode != NORMAL) {
    if (data_container->settings.solver.max_iteration_sliding > 0) {
      solver->SetMaxIterations(data_container->settings.solver.max_iteration_sliding);
      data_container->settings.solver.local_solver_mode = SLIDING;
      data_container->system_timer.start("ChLcpSolverParallel_Solve");
      PerformStabilization();
      solver->Solve();
      data_container->system_timer.stop("ChLcpSolverParallel_Solve");
    }
  }
  if (data_container->settings.solver.solver_mode == SPINNING) {
    if (data_container->settings.solver.max_iteration_spinning > 0) {
      solver->SetMaxIterations(data_container->settings.solver.max_iteration_spinning);
      data_container->settings.solver.local_solver_mode = SPINNING;
      data_container->system_timer.start("ChLcpSolverParallel_Solve");
      PerformStabilization();
      solver->Solve();
      data_container->system_timer.stop("ChLcpSolverParallel_Solve");
    }
  }

  ComputeImpulses();

  for (int i = 0; i < data_container->measures.solver.iter_hist.size(); i++) {
    AtIterationEnd(data_container->measures.solver.maxd_hist[i], data_container->measures.solver.maxdeltalambda_hist[i], data_container->measures.solver.iter_hist[i]);
  }
  tot_iterations = data_container->measures.solver.iter_hist.size();

#if PRINT_LEVEL == 2
  std::cout << "Solve Done: " << residual << std::endl;
#endif
}

void ChLcpSolverParallelDVI::ComputeD()
{
  uint num_constraints = data_container->num_constraints;
  if (num_constraints <= 0) {
    return;
  }

  uint num_bodies = data_container->num_bodies;
  uint num_shafts = data_container->num_shafts;
  uint num_dof = data_container->num_dof;
  uint num_contacts = data_container->num_contacts;
  uint num_bilaterals = data_container->num_bilaterals;
  uint nnz_bilaterals = data_container->nnz_bilaterals;

  int nnz_normal = 6 * 2 * data_container->num_contacts;
  int nnz_tangential = 6 * 4 * data_container->num_contacts;
  int nnz_spinning = 6 * 3 * data_container->num_contacts;

  int num_normal = 1 * data_container->num_contacts;
  int num_tangential = 2 * data_container->num_contacts;
  int num_spinning = 3 * data_container->num_contacts;

  CompressedMatrix<real>& D_n_T = data_container->host_data.D_n_T;
  CompressedMatrix<real>& D_t_T = data_container->host_data.D_t_T;
  CompressedMatrix<real>& D_s_T = data_container->host_data.D_s_T;
  CompressedMatrix<real>& D_b_T = data_container->host_data.D_b_T;

  CompressedMatrix<real>& D_n = data_container->host_data.D_n;
  CompressedMatrix<real>& D_t = data_container->host_data.D_t;
  CompressedMatrix<real>& D_s = data_container->host_data.D_s;
  CompressedMatrix<real>& D_b = data_container->host_data.D_b;

  CompressedMatrix<real>& M_invD_n = data_container->host_data.M_invD_n;
  CompressedMatrix<real>& M_invD_t = data_container->host_data.M_invD_t;
  CompressedMatrix<real>& M_invD_s = data_container->host_data.M_invD_s;
  CompressedMatrix<real>& M_invD_b = data_container->host_data.M_invD_b;

  const CompressedMatrix<real>& M_inv = data_container->host_data.M_inv;

  switch (data_container->settings.solver.solver_mode) {
    case NORMAL:
      clear(D_n_T);

      D_n_T.reserve(nnz_normal);

      D_n_T.resize(num_normal, num_dof, false);
      break;
    case SLIDING:
      clear(D_n_T);
      clear(D_t_T);

      D_n_T.reserve(nnz_normal);
      D_t_T.reserve(nnz_tangential);

      D_n_T.resize(num_normal, num_dof, false);
      D_t_T.resize(num_tangential, num_dof, false);
      break;
    case SPINNING:
      clear(D_n_T);
      clear(D_t_T);
      clear(D_s_T);

      D_n_T.reserve(nnz_normal);
      D_t_T.reserve(nnz_tangential);
      D_s_T.reserve(nnz_spinning);

      D_n_T.resize(num_normal, num_dof, false);
      D_t_T.resize(num_tangential, num_dof, false);
      D_s_T.resize(num_spinning, num_dof, false);
      break;
  }

  clear(D_b_T);
  D_b_T.reserve(nnz_bilaterals);

  rigid_rigid.GenerateSparsity();
  bilateral.GenerateSparsity();
  rigid_rigid.Build_D();
  bilateral.Build_D();

  D_n = trans(D_n_T);
  D_t = trans(D_t_T);
  D_s = trans(D_s_T);
  D_b = trans(D_b_T);

  M_invD_n = M_inv * D_n;
  M_invD_t = M_inv * D_t;
  M_invD_s = M_inv * D_s;
  M_invD_b = M_inv * D_b;
}

void ChLcpSolverParallelDVI::ComputeE()
{
  if (data_container->num_constraints <= 0) {
    return;
  }

  data_container->host_data.E.resize(data_container->num_constraints);
  reset(data_container->host_data.E);

  rigid_rigid.Build_E();
  bilateral.Build_E();
}

void ChLcpSolverParallelDVI::ComputeR()
{
  if (data_container->num_constraints <= 0) {
    return;
  }

  const CompressedMatrix<real>& D_n_T = data_container->host_data.D_n_T;
  const CompressedMatrix<real>& D_t_T = data_container->host_data.D_t_T;
  const CompressedMatrix<real>& D_s_T = data_container->host_data.D_s_T;
  const CompressedMatrix<real>& D_b_T = data_container->host_data.D_b_T;

  const DynamicVector<real>& M_invk = data_container->host_data.M_invk;

  DynamicVector<real>& R = data_container->host_data.R;
  DynamicVector<real>& b = data_container->host_data.b;

  b.resize(data_container->num_constraints);
  reset(b);

  rigid_rigid.Build_b();
  bilateral.Build_b();

  switch (data_container->settings.solver.solver_mode) {
    case NORMAL: {
      R = -b - D_n_T * M_invk;
    } break;

    case SLIDING: {
      R = -b - D_n_T * M_invk - D_t_T * M_invk;
    } break;

    case SPINNING: {
      R = -b - D_n_T * M_invk - D_t_T * M_invk - D_s_T * M_invk;
    } break;
  }
}

void ChLcpSolverParallelDVI::ChangeSolverType(SOLVERTYPE type) {
  data_container->settings.solver.solver_type = type;

  if (this->solver) {
    delete (this->solver);
  }
  if (type == STEEPEST_DESCENT) {
    solver = new ChSolverSD();
  } else if (type == GRADIENT_DESCENT) {
    solver = new ChSolverGD();
  } else if (type == CONJUGATE_GRADIENT) {
    solver = new ChSolverCG();
  } else if (type == CONJUGATE_GRADIENT_SQUARED) {
    solver = new ChSolverCGS();
  } else if (type == BICONJUGATE_GRADIENT) {
    solver = new ChSolverBiCG();
  } else if (type == BICONJUGATE_GRADIENT_STAB) {
    solver = new ChSolverBiCGStab();
  } else if (type == MINIMUM_RESIDUAL) {
    solver = new ChSolverMinRes();
  } else if (type == QUASAI_MINIMUM_RESIDUAL) {
    //         // This solver has not been implemented yet
    //         //SolveQMR(data_container->gpu_data.device_gam_data, rhs, max_iteration);
  } else if (type == APGD) {
    solver = new ChSolverAPGD();
  } else if (type == APGDREF) {
    solver = new ChSolverAPGDREF();
  } else if (type == JACOBI) {
    solver = new ChSolverJacobi();
  } else if (type == GAUSS_SEIDEL) {
    solver = new ChSolverPGS();
  } else if (type == PDIP) {
    solver = new ChSolverPDIP();
  }
}
<|endoftext|>


<commit_before>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2009 Scientific Computing and Imaging Institute,
   University of Utah.

   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.

   author: Moritz Dannhauer
   last change: 04/14/14
   TODO: improve the pointer arithmetic (from SCIRun4) in template class
*/

#include <Core/Algorithms/Legacy/Fields/Mapping/ApplyMappingMatrix.h>
#include <Core/Datatypes/SparseRowMatrix.h>
#include <Core/Datatypes/SparseRowMatrixFromMap.h>
#include <Core/Algorithms/Base/AlgorithmPreconditions.h>
#include <Core/Datatypes/DenseMatrix.h>
#include <Core/Datatypes/Legacy/Field/VMesh.h>
#include <Core/Datatypes/Legacy/Field/VField.h>
#include <Core/Datatypes/Legacy/Field/Mesh.h>
#include <Core/Datatypes/MatrixTypeConversions.h>
#include <Core/GeometryPrimitives/Point.h>
#include <Core/GeometryPrimitives/Tensor.h>
#include <Core/Datatypes/Legacy/Field/FieldInformation.h>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

//namespace SCIRunAlgo {

using namespace SCIRun;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Core::Algorithms::Fields;
using namespace SCIRun::Core::Algorithms;
using namespace SCIRun::Core::Geometry;
<<<<<<< HEAD
//! Internal function to this algorithm: no need for this function to be
//! public. It is called from the algorithm class only.

=======
/// Internal function to this algorithm: no need for this function to be
/// public. It is called from the algorithm class only.
#ifdef SCIRUN4_CODE_TO_BE_ENABLED_LATER
>>>>>>> c2521af3852c84caa0e1e82b864408ff78ca7429
template <class DATA>
/*bool
ApplyMappingMatrixT(const ApplyMappingMatrixAlgo* algo,
                    VField* input, VField* output,
<<<<<<< HEAD
                    SparseRowMatrix* mapping);*/

bool
ApplyMappingMatrixT(const ApplyMappingMatrixAlgo* algo,
                    const VField* input, VField* output,
                    SparseRowMatrixHandle mapping);

//! This is the basic algorithm behind the mapping algorithm
=======
                    SparseRowMatrix* mapping);

/// This is the basic algorithm behind the mapping algorithm
>>>>>>> c2521af3852c84caa0e1e82b864408ff78ca7429
template <class DATA>
bool
ApplyMappingMatrixT(const ApplyMappingMatrixAlgo* algo,
                    const VField* input, VField* output,
                    SparseRowMatrixHandle mapping)
/*bool
ApplyMappingMatrixT(const ApplyMappingMatrixAlgo* algo,
                    VField* input, VField* output,
                    SparseRowMatrix* mapping)*/
{
  double* vals = mapping->valuePtr();
  const index_type* rows = mapping->get_rows();
  const index_type* columns = mapping->get_cols();
  const size_type m = mapping->nrows();

 index_type cnt=0;
 for (index_type idx=0; idx<m; idx++)
 {
  DATA val(0);
  index_type rr = rows[idx];
  size_type  ss = rows[idx+1]-rows[idx];
  input->get_weighted_value(val,&(columns[rr]),&(vals[rr]),ss);

<<<<<<< HEAD
  output->set_value(val,idx);
  cnt++; if (cnt==400) {algo->update_progress((double)idx/m); cnt=0;}

 }

  return true;
=======
  /// Algorithm succeeded
  algo->algo_end(); return (true);
>>>>>>> c2521af3852c84caa0e1e82b864408ff78ca7429
}

/// Actual Algorithm class

ApplyMappingMatrixAlgo::ApplyMappingMatrixAlgo()
{
<<<<<<< HEAD

}

FieldHandle ApplyMappingMatrixAlgo::run(FieldHandle& isrc, FieldHandle& idst, MatrixHandle& mapping) const
{
  FieldHandle output;

  if (!isrc)
=======
  #ifdef SCIRUN4_CODE_TO_BE_ENABLED_LATER
  algo_start("ApplyMappingMatrix");

  /// safety check
  if (isrc.get_rep() == 0)
>>>>>>> c2521af3852c84caa0e1e82b864408ff78ca7429
  {
    THROW_ALGORITHM_INPUT_ERROR("No input source field");
    return FieldHandle();
  }

<<<<<<< HEAD
  if (!isrc)
=======
  /// safety check
  if (idst.get_rep() == 0)
>>>>>>> c2521af3852c84caa0e1e82b864408ff78ca7429
  {
    THROW_ALGORITHM_INPUT_ERROR("No input destination field");
    return FieldHandle();
  }

  if (!isrc)
  {
    THROW_ALGORITHM_INPUT_ERROR("No input mapping field");
    return FieldHandle();
  }

  auto matrix = matrix_cast::as_sparse(mapping);

  if (!matrix)
  {
    THROW_ALGORITHM_INPUT_ERROR("Mapping matrix needs to be sparse");
    return FieldHandle();
  }

  VField* ifsrc =  isrc->vfield();
  VField* ifdst =  idst->vfield();
  VMesh*  imdst =  idst->vmesh();
<<<<<<< HEAD

  //! Get information about field types
=======

  /// Get information about field types
>>>>>>> c2521af3852c84caa0e1e82b864408ff78ca7429
  FieldInformation fi(isrc);
  FieldInformation fo(idst);

  fo.set_data_type(fi.get_data_type());
  size_type m = mapping->nrows();
  size_type n = mapping->ncols();

  size_type dst_num_nodes = imdst->num_nodes();
  size_type dst_num_elems = imdst->num_elems();
  size_type dst_num_values = ifdst->num_values();
  size_type src_num_values = ifsrc->num_values();

  if (dst_num_values == m)
  {
    // do nothing
  }
  if (m == dst_num_nodes)
  {
    fo.make_lineardata();
  }
  else if (m == dst_num_elems)
  {
    fo.make_constantdata();
  }
  else
  {
    THROW_ALGORITHM_INPUT_ERROR("The number of columns in the matrix does not match number of nodes or elements in the destination field");
    return FieldHandle();
  }

  if (src_num_values != n)
  {
    std::cerr << "n="<<n<<"\n";
    std::cerr << "num_values="<<src_num_values<<"\n";
    THROW_ALGORITHM_INPUT_ERROR("The number of columns in the matrix does not match number of values in the source field");
    return FieldHandle();
  }
<<<<<<< HEAD

  //! Create output field
=======

  /// Create output field
>>>>>>> c2521af3852c84caa0e1e82b864408ff78ca7429
  output = CreateField(fo,idst->mesh());

  VField* ofield = output->vfield();
  ofield->resize_values();

<<<<<<< HEAD
  if (!output)
=======
  /// Check whether output field was created
  if (output.get_rep() == 0)
>>>>>>> c2521af3852c84caa0e1e82b864408ff78ca7429
  {
    THROW_ALGORITHM_INPUT_ERROR("Could not create output field");
    return FieldHandle();
  }

<<<<<<< HEAD
  //! Simple table to deal with the various data type formats
  //! Note that not every data type is handled, all char, shorts etc,
  //! are automatically handled by the int, and unsigned int case, by
  //! casting the data on input (these should be the less frequently
  //! used datatypes and hence have no specific algorithm in place).
  //! Similarly floats are casted to doubles.

=======
  /// Simple table to deal with the various data type formats
  /// Note that not every data type is handled, all char, shorts etc,
  /// are automatically handled by the int, and unsigned int case, by
  /// casting the data on input (these should be the less frequently
  /// used datatypes and hence have no specific algorithm in place).
  /// Similarly floats are casted to doubles.

>>>>>>> c2521af3852c84caa0e1e82b864408ff78ca7429
  if (isrc->vfield()->is_char())
    if (ApplyMappingMatrixT<char>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_unsigned_char())
    if (ApplyMappingMatrixT<unsigned char>(this,ifsrc,ofield,matrix))
        return output;
  if (isrc->vfield()->is_short())
    if (ApplyMappingMatrixT<short>(this,ifsrc,ofield,matrix))
        return output;
  if (isrc->vfield()->is_unsigned_short())
    if (ApplyMappingMatrixT<unsigned short>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_int())
    if (ApplyMappingMatrixT<int>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_unsigned_int())
    if (ApplyMappingMatrixT<unsigned int>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_longlong())
    if (ApplyMappingMatrixT<long long>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_unsigned_longlong())
    if (ApplyMappingMatrixT<unsigned long long>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_float())
    if (ApplyMappingMatrixT<float>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_double())
    if (ApplyMappingMatrixT<double>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_vector())
    if (ApplyMappingMatrixT<Vector>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_tensor())
    if (ApplyMappingMatrixT<Tensor>(this,ifsrc,ofield,matrix))
       return output;

  return output;
}

AlgorithmInputName ApplyMappingMatrixAlgo::Source("Source");
AlgorithmInputName ApplyMappingMatrixAlgo::Destination("Destination");
AlgorithmInputName ApplyMappingMatrixAlgo::Mapping("Mapping");
AlgorithmOutputName ApplyMappingMatrixAlgo::Output("Output");

AlgorithmOutput ApplyMappingMatrixAlgo::run_generic(const AlgorithmInput & input) const
{
 AlgorithmOutput output;

  auto src = input.get<Field>(Source);
  auto dest = input.get<Field>(Destination);
  auto mapp = input.get<Matrix>(Mapping);

  FieldHandle output_field;
  output_field = run(src,dest,mapp);

  output[Output] = output_field;

 return output;
}

//ApplyMappingMatrixAlgo::~ApplyMappingMatrixAlgo() {}
//} // namespace SCIRunAlgo
<commit_msg>changes to make ApplyMappingMatrix to work<commit_after>/*
   For more information, please see: http://software.sci.utah.edu

   The MIT License

   Copyright (c) 2009 Scientific Computing and Imaging Institute,
   University of Utah.

   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.

   author: Moritz Dannhauer
   last change: 04/14/14
   TODO: improve the pointer arithmetic (from SCIRun4) in template class
*/

#include <Core/Algorithms/Legacy/Fields/Mapping/ApplyMappingMatrix.h>
#include <Core/Datatypes/SparseRowMatrix.h>
#include <Core/Datatypes/SparseRowMatrixFromMap.h>
#include <Core/Algorithms/Base/AlgorithmPreconditions.h>
#include <Core/Datatypes/DenseMatrix.h>
#include <Core/Datatypes/Legacy/Field/VMesh.h>
#include <Core/Datatypes/Legacy/Field/VField.h>
#include <Core/Datatypes/Legacy/Field/Mesh.h>
#include <Core/Datatypes/MatrixTypeConversions.h>
#include <Core/GeometryPrimitives/Point.h>
#include <Core/GeometryPrimitives/Tensor.h>
#include <Core/Datatypes/Legacy/Field/FieldInformation.h>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace SCIRun;
using namespace SCIRun::Core::Datatypes;
using namespace SCIRun::Core::Algorithms::Fields;
using namespace SCIRun::Core::Algorithms;
using namespace SCIRun::Core::Geometry;
/// Internal function to this algorithm: no need for this function to be
/// public. It is called from the algorithm class only.
template <class DATA>
bool
ApplyMappingMatrixT(const ApplyMappingMatrixAlgo* algo,
                    const VField* input, VField* output,
                    SparseRowMatrixHandle mapping);

//! This is the basic algorithm behind the mapping algorithm
template <class DATA>
bool
ApplyMappingMatrixT(const ApplyMappingMatrixAlgo* algo,
                    const VField* input, VField* output,
                    SparseRowMatrixHandle mapping)
{
  double* vals = mapping->valuePtr();
  const index_type* rows = mapping->get_rows();
  const index_type* columns = mapping->get_cols();
  const size_type m = mapping->nrows();

 index_type cnt=0;
 for (index_type idx=0; idx<m; idx++)
 {
  DATA val(0);
  index_type rr = rows[idx];
  size_type  ss = rows[idx+1]-rows[idx];
  input->get_weighted_value(val,&(columns[rr]),&(vals[rr]),ss);

  output->set_value(val,idx);
  cnt++; if (cnt==400) {algo->update_progress((double)idx/m); cnt=0;}

 }

  return true;
}

/// Actual Algorithm class
ApplyMappingMatrixAlgo::ApplyMappingMatrixAlgo()
{

}

FieldHandle ApplyMappingMatrixAlgo::run(FieldHandle& isrc, FieldHandle& idst, MatrixHandle& mapping) const
{
  FieldHandle output;

  if (!isrc)
  {
    THROW_ALGORITHM_INPUT_ERROR("No input source field");
    return FieldHandle();
  }

  if (!isrc)
  {
    THROW_ALGORITHM_INPUT_ERROR("No input destination field");
    return FieldHandle();
  }

  if (!isrc)
  {
    THROW_ALGORITHM_INPUT_ERROR("No input mapping field");
    return FieldHandle();
  }

  auto matrix = matrix_cast::as_sparse(mapping);

  if (!matrix)
  {
    THROW_ALGORITHM_INPUT_ERROR("Mapping matrix needs to be sparse");
    return FieldHandle();
  }

  VField* ifsrc =  isrc->vfield();
  VField* ifdst =  idst->vfield();
  VMesh*  imdst =  idst->vmesh();

  /// Get information about field types
  FieldInformation fi(isrc);
  FieldInformation fo(idst);

  fo.set_data_type(fi.get_data_type());
  size_type m = mapping->nrows();
  size_type n = mapping->ncols();

  size_type dst_num_nodes = imdst->num_nodes();
  size_type dst_num_elems = imdst->num_elems();
  size_type dst_num_values = ifdst->num_values();
  size_type src_num_values = ifsrc->num_values();

  if (dst_num_values == m)
  {
    // do nothing
  }
  if (m == dst_num_nodes)
  {
    fo.make_lineardata();
  }
  else if (m == dst_num_elems)
  {
    fo.make_constantdata();
  }
  else
  {
    THROW_ALGORITHM_INPUT_ERROR("The number of columns in the matrix does not match number of nodes or elements in the destination field");
    return FieldHandle();
  }

  if (src_num_values != n)
  {
    std::cerr << "n="<<n<<"\n";
    std::cerr << "num_values="<<src_num_values<<"\n";
    THROW_ALGORITHM_INPUT_ERROR("The number of columns in the matrix does not match number of values in the source field");
    return FieldHandle();
  }

  /// Create output field
  output = CreateField(fo,idst->mesh());

  VField* ofield = output->vfield();
  ofield->resize_values();

  if (!output)
  {
    THROW_ALGORITHM_INPUT_ERROR("Could not create output field");
    return FieldHandle();
  }

  /// Simple table to deal with the various data type formats
  /// Note that not every data type is handled, all char, shorts etc,
  /// are automatically handled by the int, and unsigned int case, by
  /// casting the data on input (these should be the less frequently
  /// used datatypes and hence have no specific algorithm in place).
  /// Similarly floats are casted to doubles.

  if (isrc->vfield()->is_char())
    if (ApplyMappingMatrixT<char>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_unsigned_char())
    if (ApplyMappingMatrixT<unsigned char>(this,ifsrc,ofield,matrix))
        return output;
  if (isrc->vfield()->is_short())
    if (ApplyMappingMatrixT<short>(this,ifsrc,ofield,matrix))
        return output;
  if (isrc->vfield()->is_unsigned_short())
    if (ApplyMappingMatrixT<unsigned short>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_int())
    if (ApplyMappingMatrixT<int>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_unsigned_int())
    if (ApplyMappingMatrixT<unsigned int>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_longlong())
    if (ApplyMappingMatrixT<long long>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_unsigned_longlong())
    if (ApplyMappingMatrixT<unsigned long long>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_float())
    if (ApplyMappingMatrixT<float>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_double())
    if (ApplyMappingMatrixT<double>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_vector())
    if (ApplyMappingMatrixT<Vector>(this,ifsrc,ofield,matrix))
       return output;
  if (isrc->vfield()->is_tensor())
    if (ApplyMappingMatrixT<Tensor>(this,ifsrc,ofield,matrix))
       return output;

  return output;
}

AlgorithmInputName ApplyMappingMatrixAlgo::Source("Source");
AlgorithmInputName ApplyMappingMatrixAlgo::Destination("Destination");
AlgorithmInputName ApplyMappingMatrixAlgo::Mapping("Mapping");
AlgorithmOutputName ApplyMappingMatrixAlgo::Output("Output");

AlgorithmOutput ApplyMappingMatrixAlgo::run_generic(const AlgorithmInput & input) const
{
 AlgorithmOutput output;

  auto src = input.get<Field>(Source);
  auto dest = input.get<Field>(Destination);
  auto mapp = input.get<Matrix>(Mapping);

  FieldHandle output_field;
  output_field = run(src,dest,mapp);

  output[Output] = output_field;

 return output;
}

<|endoftext|>


<commit_before>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Syscoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chain.h"
// SYSCOIN for auxpow
#include "main.h"
using namespace std;
// SYSCOIN moved and added auxpow check
CBlockHeader GetBlockHeader() const
{
    CBlockHeader block;
	/* The CBlockIndex object's block header is missing the auxpow.
	   So if this is an auxpow block, read it from disk instead.  We only
	   have to read the actual *header*, not the full block.  */
	if (nVersion.IsAuxpow())
	{
		ReadBlockHeaderFromDisk(block, this, consensusParams);
		return block;
	}
    block.nVersion       = nVersion;
    if (pprev)
        block.hashPrevBlock = pprev->GetBlockHash();
    block.hashMerkleRoot = hashMerkleRoot;
    block.nTime          = nTime;
    block.nBits          = nBits;
    block.nNonce         = nNonce;
    return block;
}
/**
 * CChain implementation
 */
void CChain::SetTip(CBlockIndex *pindex) {
    if (pindex == NULL) {
        vChain.clear();
        return;
    }
    vChain.resize(pindex->nHeight + 1);
    while (pindex && vChain[pindex->nHeight] != pindex) {
        vChain[pindex->nHeight] = pindex;
        pindex = pindex->pprev;
    }
}

CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const {
    int nStep = 1;
    std::vector<uint256> vHave;
    vHave.reserve(32);

    if (!pindex)
        pindex = Tip();
    while (pindex) {
        vHave.push_back(pindex->GetBlockHash());
        // Stop when we have added the genesis block.
        if (pindex->nHeight == 0)
            break;
        // Exponentially larger steps back, plus the genesis block.
        int nHeight = std::max(pindex->nHeight - nStep, 0);
        if (Contains(pindex)) {
            // Use O(1) CChain index if possible.
            pindex = (*this)[nHeight];
        } else {
            // Otherwise, use O(log n) skiplist.
            pindex = pindex->GetAncestor(nHeight);
        }
        if (vHave.size() > 10)
            nStep *= 2;
    }

    return CBlockLocator(vHave);
}

const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
    if (pindex == NULL) {
        return NULL;
    }
    if (pindex->nHeight > Height())
        pindex = pindex->GetAncestor(Height());
    while (pindex && !Contains(pindex))
        pindex = pindex->pprev;
    return pindex;
}

/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
int static inline InvertLowestOne(int n) { return n & (n - 1); }

/** Compute what height to jump back to with the CBlockIndex::pskip pointer. */
int static inline GetSkipHeight(int height) {
    if (height < 2)
        return 0;

    // Determine which height to jump back to. Any number strictly lower than height is acceptable,
    // but the following expression seems to perform well in simulations (max 110 steps to go back
    // up to 2**18 blocks).
    return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height);
}

CBlockIndex* CBlockIndex::GetAncestor(int height)
{
    if (height > nHeight || height < 0)
        return NULL;

    CBlockIndex* pindexWalk = this;
    int heightWalk = nHeight;
    while (heightWalk > height) {
        int heightSkip = GetSkipHeight(heightWalk);
        int heightSkipPrev = GetSkipHeight(heightWalk - 1);
        if (pindexWalk->pskip != NULL &&
            (heightSkip == height ||
             (heightSkip > height && !(heightSkipPrev < heightSkip - 2 &&
                                       heightSkipPrev >= height)))) {
            // Only follow pskip if pprev->pskip isn't better than pskip->pprev.
            pindexWalk = pindexWalk->pskip;
            heightWalk = heightSkip;
        } else {
            pindexWalk = pindexWalk->pprev;
            heightWalk--;
        }
    }
    return pindexWalk;
}

const CBlockIndex* CBlockIndex::GetAncestor(int height) const
{
    return const_cast<CBlockIndex*>(this)->GetAncestor(height);
}

void CBlockIndex::BuildSkip()
{
    if (pprev)
        pskip = pprev->GetAncestor(GetSkipHeight(nHeight));
}
<commit_msg>auxpow<commit_after>// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2014 The Syscoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "chain.h"
// SYSCOIN for auxpow
#include "main.h"
using namespace std;
// SYSCOIN moved and added auxpow check
CBlockHeader CBlockIndex::GetBlockHeader() const
{
    CBlockHeader block;
	/* The CBlockIndex object's block header is missing the auxpow.
	   So if this is an auxpow block, read it from disk instead.  We only
	   have to read the actual *header*, not the full block.  */
	if (nVersion.IsAuxpow())
	{
		ReadBlockHeaderFromDisk(block, this, consensusParams);
		return block;
	}
    block.nVersion       = nVersion;
    if (pprev)
        block.hashPrevBlock = pprev->GetBlockHash();
    block.hashMerkleRoot = hashMerkleRoot;
    block.nTime          = nTime;
    block.nBits          = nBits;
    block.nNonce         = nNonce;
    return block;
}
/**
 * CChain implementation
 */
void CChain::SetTip(CBlockIndex *pindex) {
    if (pindex == NULL) {
        vChain.clear();
        return;
    }
    vChain.resize(pindex->nHeight + 1);
    while (pindex && vChain[pindex->nHeight] != pindex) {
        vChain[pindex->nHeight] = pindex;
        pindex = pindex->pprev;
    }
}

CBlockLocator CChain::GetLocator(const CBlockIndex *pindex) const {
    int nStep = 1;
    std::vector<uint256> vHave;
    vHave.reserve(32);

    if (!pindex)
        pindex = Tip();
    while (pindex) {
        vHave.push_back(pindex->GetBlockHash());
        // Stop when we have added the genesis block.
        if (pindex->nHeight == 0)
            break;
        // Exponentially larger steps back, plus the genesis block.
        int nHeight = std::max(pindex->nHeight - nStep, 0);
        if (Contains(pindex)) {
            // Use O(1) CChain index if possible.
            pindex = (*this)[nHeight];
        } else {
            // Otherwise, use O(log n) skiplist.
            pindex = pindex->GetAncestor(nHeight);
        }
        if (vHave.size() > 10)
            nStep *= 2;
    }

    return CBlockLocator(vHave);
}

const CBlockIndex *CChain::FindFork(const CBlockIndex *pindex) const {
    if (pindex == NULL) {
        return NULL;
    }
    if (pindex->nHeight > Height())
        pindex = pindex->GetAncestor(Height());
    while (pindex && !Contains(pindex))
        pindex = pindex->pprev;
    return pindex;
}

/** Turn the lowest '1' bit in the binary representation of a number into a '0'. */
int static inline InvertLowestOne(int n) { return n & (n - 1); }

/** Compute what height to jump back to with the CBlockIndex::pskip pointer. */
int static inline GetSkipHeight(int height) {
    if (height < 2)
        return 0;

    // Determine which height to jump back to. Any number strictly lower than height is acceptable,
    // but the following expression seems to perform well in simulations (max 110 steps to go back
    // up to 2**18 blocks).
    return (height & 1) ? InvertLowestOne(InvertLowestOne(height - 1)) + 1 : InvertLowestOne(height);
}

CBlockIndex* CBlockIndex::GetAncestor(int height)
{
    if (height > nHeight || height < 0)
        return NULL;

    CBlockIndex* pindexWalk = this;
    int heightWalk = nHeight;
    while (heightWalk > height) {
        int heightSkip = GetSkipHeight(heightWalk);
        int heightSkipPrev = GetSkipHeight(heightWalk - 1);
        if (pindexWalk->pskip != NULL &&
            (heightSkip == height ||
             (heightSkip > height && !(heightSkipPrev < heightSkip - 2 &&
                                       heightSkipPrev >= height)))) {
            // Only follow pskip if pprev->pskip isn't better than pskip->pprev.
            pindexWalk = pindexWalk->pskip;
            heightWalk = heightSkip;
        } else {
            pindexWalk = pindexWalk->pprev;
            heightWalk--;
        }
    }
    return pindexWalk;
}

const CBlockIndex* CBlockIndex::GetAncestor(int height) const
{
    return const_cast<CBlockIndex*>(this)->GetAncestor(height);
}

void CBlockIndex::BuildSkip()
{
    if (pprev)
        pskip = pprev->GetAncestor(GetSkipHeight(nHeight));
}
<|endoftext|>


<commit_before>/*
   Copyright (C) 2018 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

 */

#include "ClientRpcLayer.hpp"
#include "ClientRpcUpdatesLayer.hpp"
#include "IgnoredMessageNotification.hpp"
#include "SendPackageHelper.hpp"
#include "Debug_p.hpp"
#include "CAppInformation.hpp"
#include "PendingRpcOperation.hpp"
#include "RandomGenerator.hpp"
#include "UpdatesLayer.hpp"

#include "MTProto/MessageHeader.hpp"
#include "MTProto/Stream.hpp"

#include <QLoggingCategory>

Q_LOGGING_CATEGORY(c_clientRpcLayerCategory, "telegram.client.rpclayer", QtWarningMsg)
Q_LOGGING_CATEGORY(c_clientRpcDumpPackageCategory, "telegram.client.rpclayer.dump", QtWarningMsg)

namespace Telegram {

namespace Client {

RpcLayer::RpcLayer(QObject *parent) :
    BaseRpcLayer(parent)
{
}

RpcLayer::~RpcLayer()
{
    qDeleteAll(m_messages);
}

void RpcLayer::setAppInformation(AppInformation *appInfo)
{
    m_appInfo = appInfo;
}

void RpcLayer::installUpdatesHandler(UpdatesInternalApi *updatesHandler)
{
    m_UpdatesInternalApi = updatesHandler;
}

void RpcLayer::setSessionData(quint64 sessionId, quint32 contentRelatedMessagesNumber)
{
    m_sessionId = sessionId;
    m_contentRelatedMessages = contentRelatedMessagesNumber;
}

void RpcLayer::setServerSalt(quint64 serverSalt)
{
    m_serverSalt = serverSalt;
}

void RpcLayer::startNewSession()
{
    m_sessionId = RandomGenerator::instance()->generate<quint64>();
    m_contentRelatedMessages = 0;
}

bool RpcLayer::processMTProtoMessage(const MTProto::Message &message)
{
    if (message.sequenceNumber & 1) {
        addMessageToAck(message.messageId);
    }

    const TLValue firstValue = message.firstValue();
    if (firstValue.isTypeOf<TLUpdates>()) {
        return processUpdates(message);
    }

    bool result = false;

    switch (firstValue) {
    case TLValue::NewSessionCreated:
        result = processSessionCreated(message.skipTLValue());
        break;
    case TLValue::MsgContainer:
        result = processMsgContainer(message.skipTLValue());
        break;
    case TLValue::RpcResult:
        result = processRpcResult(message.skipTLValue());
        break;
    case TLValue::MsgsAck:
        result = processMessageAck(message.skipTLValue());
        break;
    case TLValue::BadMsgNotification:
    case TLValue::BadServerSalt:
        result = processIgnoredMessageNotification(message);
        break;
    case TLValue::GzipPacked:
        qCWarning(c_clientRpcLayerCategory) << CALL_INFO
                                            << "GzipPacked should be processed in the base class";
        break;
    case TLValue::Pong:
    {
        MTProto::Stream stream(message.data);
        TLPong pong;
        stream >> pong;
        PendingRpcOperation *op = m_operations.take(pong.msgId);
        if (op) {
            op->setFinishedWithReplyData(message.data);
            result = true;
        } else {
            qCWarning(c_clientRpcLayerCategory) << "Unexpected pong?!" << pong.msgId << pong.pingId;
        }
    }
        break;
    default:
        qCDebug(c_clientRpcLayerCategory) << Q_FUNC_INFO << "value:" << message.firstValue();
        break;
    }

    if (!result) {
        qCWarning(c_clientRpcLayerCategory) << CALL_INFO << "Unable to process" << message.firstValue();
    }

    return result;
}

bool RpcLayer::processRpcResult(const MTProto::Message &message)
{
    qCDebug(c_clientRpcLayerCategory) << "processRpcQuery(stream);";
    MTProto::Stream stream(message.data);
    quint64 messageId = 0;
    stream >> messageId;
    PendingRpcOperation *op = m_operations.take(messageId);
    if (!op) {
        qCWarning(c_clientRpcLayerCategory) << "processRpcQuery():"
                                            << "Unhandled RPC result for messageId"
                                            << hex << showbase << messageId;
        return false;
    }
    op->setFinishedWithReplyData(stream.readAll());
#define DUMP_CLIENT_RPC_PACKETS
#ifdef DUMP_CLIENT_RPC_PACKETS
    qCDebug(c_clientRpcLayerCategory) << "Client: Answer for message"
                                      << messageId << "op:" << op;
    qCDebug(c_clientRpcLayerCategory).noquote() << "Client: RPC Reply bytes:"
                                                << op->replyData().size() << op->replyData().toHex();
#endif
    qCDebug(c_clientRpcLayerCategory) << "processRpcQuery():" << "Set finished op" << op
                                      << "messageId:" << hex << showbase << messageId
                                      << "error:" << op->errorDetails();
    return true;
}

bool RpcLayer::processUpdates(const MTProto::Message &message)
{
    qCDebug(c_clientRpcLayerCategory) << "processUpdates()" << message.firstValue();
    MTProto::Stream stream(message.data);

    TLUpdates updates;
    stream >> updates;
    return m_UpdatesInternalApi->processUpdates(updates);
}

bool RpcLayer::processMessageAck(const MTProto::Message &message)
{
    MTProto::Stream stream(message.data);
    TLVector<quint64> idsVector;
    stream >> idsVector;
    qCDebug(c_clientRpcLayerCategory) << "processMessageAck():" << idsVector;

    return true;
}

bool RpcLayer::processSessionCreated(const MTProto::Message &message)
{
    MTProto::Stream stream(message.data);
    // https://core.telegram.org/mtproto/service_messages#new-session-creation-notification
    quint64 firstMsgId;
    quint64 uniqueId;
    quint64 serverSalt;

    stream >> firstMsgId;
    stream >> uniqueId;
    stream >> serverSalt;
    qCDebug(c_clientRpcLayerCategory) << "processSessionCreated(stream) {"
                                      << hex << showbase
                                      << "    firstMsgId:" << firstMsgId
                                      << "    uniqueId:" << uniqueId
                                      << "    serverSalt:" << serverSalt;

    return true;
}

bool RpcLayer::processIgnoredMessageNotification(const MTProto::Message &message)
{
    MTProto::Stream stream(message.data);
    TLBadMsgNotification tlNotification;
    stream >> tlNotification;
    // https://core.telegram.org/mtproto/service_messages_about_messages#notice-of-ignored-error-message
    MTProto::IgnoredMessageNotification notification(tlNotification);
    qCDebug(c_clientRpcLayerCategory) << CALL_INFO << notification.toString();

    MTProto::Message *m = m_messages.value(notification.messageId);
    if (!m) {
        qCWarning(c_clientRpcLayerCategory) << CALL_INFO
                                            << notification.toString() << "for unknown message id"
                                            << hex << showbase << notification.messageId;
        return false;
    }

    switch (notification.errorCode) {
    case MTProto::IgnoredMessageNotification::IncorrectServerSalt:
        // We sync local serverSalt value in processDecryptedMessageHeader().
        // Resend message will automatically apply the new salt
        return resendIgnoredMessage(notification.messageId);
    case MTProto::IgnoredMessageNotification::MessageIdTooOld:
        return resendIgnoredMessage(notification.messageId);
    case MTProto::IgnoredMessageNotification::SequenceNumberTooHigh:
        qCDebug(c_clientRpcLayerCategory) << "processIgnoredMessageNotification(SequenceNumberTooHigh):"
                                             " reduce seq num"
                                          << hex << showbase
                                          << " from" << m->sequenceNumber
                                          << " to" << (m->sequenceNumber - 2);
        m->sequenceNumber -= 2;
        return resendIgnoredMessage(notification.messageId);
    case MTProto::IgnoredMessageNotification::SequenceNumberTooLow:
        qCDebug(c_clientRpcLayerCategory) << "processIgnoredMessageNotification(SequenceNumberTooLow):"
                                             " increase seq num"
                                          << hex << showbase
                                          << " from" << m->sequenceNumber
                                          << " to" << (m->sequenceNumber + 2);
        m->sequenceNumber += 2;
    {
        quint32 messageContentNumber = m->sequenceNumber / 2;
        if (m_contentRelatedMessages <= messageContentNumber) {
            m_contentRelatedMessages = messageContentNumber + 1;
        }
    }
        return resendIgnoredMessage(notification.messageId);
    case MTProto::IgnoredMessageNotification::IncorrectTwoLowerOrderMessageIdBits:
        qCCritical(c_clientRpcLayerCategory) << "How we ever managed to mess with"
                                                " the lower messageId bytes?!";
        // Just resend the message. We regenerate message id, so it can help.
        return resendIgnoredMessage(notification.messageId);
    default:
        break;
    }

    qCWarning(c_clientRpcLayerCategory) << "Unhandled error:" << notification.toString();
    return false;
}

bool RpcLayer::processMessageHeader(const MTProto::FullMessageHeader &header)
{
    if (serverSalt() != header.serverSalt) {
        qCDebug(c_clientRpcLayerCategory).noquote()
                << QStringLiteral("Received different server salt: %1 (remote) vs %2 (local)."
                                  " Fix local to remote.")
                   .arg(toHex(header.serverSalt))
                   .arg(toHex(serverSalt()));
        setServerSalt(header.serverSalt);
    }

    if (m_sessionId != header.sessionId) {
        qCWarning(c_clientRpcLayerCategory) << CALL_INFO << "Session Id is wrong.";
        return false;
    }
    return true;
}

QByteArray RpcLayer::getEncryptionKeyPart() const
{
    return m_sendHelper->getClientKeyPart();
}

QByteArray RpcLayer::getVerificationKeyPart() const
{
    return m_sendHelper->getServerKeyPart();
}

quint64 RpcLayer::sendRpc(PendingRpcOperation *operation)
{
    operation->setConnection(m_sendHelper->getConnection());

    MTProto::Message *message = new MTProto::Message();
    message->messageId = m_sendHelper->newMessageId(SendMode::Client);
    if (operation->isContentRelated()) {
        message->sequenceNumber = m_contentRelatedMessages * 2 + 1;
        ++m_contentRelatedMessages;
    } else {
        if (m_contentRelatedMessages == 0) {
            qCCritical(c_clientRpcLayerCategory) << CALL_INFO
                                                 << "First message should be content related!";
        }
        message->sequenceNumber = m_contentRelatedMessages * 2;
    }

    // We have to add InitConnection here because
    // sendPackage() implementation is shared with server
    if (message->sequenceNumber == 1) {
        message->setData(getInitConnection() + operation->requestData());
    } else {
        message->setData(operation->requestData());
    }
    m_operations.insert(message->messageId, operation);
    m_messages.insert(message->messageId, message);
    sendPacket(*message);
    return message->messageId;
}

bool RpcLayer::resendIgnoredMessage(quint64 messageId)
{
    MTProto::Message *message = m_messages.take(messageId);
    PendingRpcOperation *operation = m_operations.take(messageId);
    if (!operation) {
        qCCritical(c_clientRpcLayerCategory) << CALL_INFO
                                             << "Unable to find the message to resend"
                                             << hex << messageId;
        delete message;
        return false;
    }
    qCDebug(c_clientRpcLayerCategory) << "Resend message"
                                      << hex << messageId
                                      << message->firstValue();
    message->messageId = m_sendHelper->newMessageId(SendMode::Client);
    m_operations.insert(message->messageId, operation);
    m_messages.insert(message->messageId, message);
    sendPacket(*message);
    emit operation->resent(messageId, message->messageId);
    return message->messageId;
}

void RpcLayer::acknowledgeMessages()
{
    MTProto::Stream outputStream(MTProto::Stream::WriteOnly);
    TLVector<quint64> idsVector = m_messagesToAck;
    m_messagesToAck.clear();
    outputStream << TLValue::MsgsAck;
    outputStream << idsVector;

    MTProto::Message *message = new MTProto::Message();
    message->messageId = m_sendHelper->newMessageId(SendMode::Client);
    message->sequenceNumber = m_contentRelatedMessages * 2;
    message->setData(outputStream.getData());

    m_messages.insert(message->messageId, message);
    sendPacket(*message);
}

void RpcLayer::onConnectionFailed()
{
    for (PendingRpcOperation *op : m_operations) {
        if (!op->isFinished()) {
            op->setFinishedWithTextError(QLatin1String("Connection failed"));
        }
    }
    m_operations.clear();
    qDeleteAll(m_messages);
    m_messages.clear();
}

QByteArray RpcLayer::getInitConnection() const
{
#ifdef DEVELOPER_BUILD
    qCDebug(c_clientRpcLayerCategory) << CALL_INFO << "layer" << TLValue::CurrentLayer;
#endif
    MTProto::Stream outputStream(MTProto::Stream::WriteOnly);
    outputStream << TLValue::InvokeWithLayer;
    outputStream << TLValue::CurrentLayer;
    outputStream << TLValue::InitConnection;
    outputStream << m_appInfo->appId();
    outputStream << m_appInfo->deviceInfo();
    outputStream << m_appInfo->osInfo();
    outputStream << m_appInfo->appVersion();
#if TELEGRAMQT_LAYER >= 67
    outputStream << m_appInfo->languageCode(); // System language
    outputStream << QString(); // Langpack
#endif
    outputStream << m_appInfo->languageCode(); // Lang code
    return outputStream.getData();
}

void RpcLayer::addMessageToAck(quint64 messageId)
{
    if (m_messagesToAck.isEmpty()) {
        QMetaObject::invokeMethod(this, "acknowledgeMessages", Qt::QueuedConnection);
    }
    m_messagesToAck.append(messageId);
}

} // Client namespace

} // Telegram namespace
<commit_msg>ClientRpcLayer: Use invokeMethod(Functor) if available<commit_after>/*
   Copyright (C) 2018 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

 */

#include "ClientRpcLayer.hpp"
#include "ClientRpcUpdatesLayer.hpp"
#include "IgnoredMessageNotification.hpp"
#include "SendPackageHelper.hpp"
#include "Debug_p.hpp"
#include "CAppInformation.hpp"
#include "PendingRpcOperation.hpp"
#include "RandomGenerator.hpp"
#include "UpdatesLayer.hpp"

#include "MTProto/MessageHeader.hpp"
#include "MTProto/Stream.hpp"

#include <QLoggingCategory>

Q_LOGGING_CATEGORY(c_clientRpcLayerCategory, "telegram.client.rpclayer", QtWarningMsg)
Q_LOGGING_CATEGORY(c_clientRpcDumpPackageCategory, "telegram.client.rpclayer.dump", QtWarningMsg)

namespace Telegram {

namespace Client {

RpcLayer::RpcLayer(QObject *parent) :
    BaseRpcLayer(parent)
{
}

RpcLayer::~RpcLayer()
{
    qDeleteAll(m_messages);
}

void RpcLayer::setAppInformation(AppInformation *appInfo)
{
    m_appInfo = appInfo;
}

void RpcLayer::installUpdatesHandler(UpdatesInternalApi *updatesHandler)
{
    m_UpdatesInternalApi = updatesHandler;
}

void RpcLayer::setSessionData(quint64 sessionId, quint32 contentRelatedMessagesNumber)
{
    m_sessionId = sessionId;
    m_contentRelatedMessages = contentRelatedMessagesNumber;
}

void RpcLayer::setServerSalt(quint64 serverSalt)
{
    m_serverSalt = serverSalt;
}

void RpcLayer::startNewSession()
{
    m_sessionId = RandomGenerator::instance()->generate<quint64>();
    m_contentRelatedMessages = 0;
}

bool RpcLayer::processMTProtoMessage(const MTProto::Message &message)
{
    if (message.sequenceNumber & 1) {
        addMessageToAck(message.messageId);
    }

    const TLValue firstValue = message.firstValue();
    if (firstValue.isTypeOf<TLUpdates>()) {
        return processUpdates(message);
    }

    bool result = false;

    switch (firstValue) {
    case TLValue::NewSessionCreated:
        result = processSessionCreated(message.skipTLValue());
        break;
    case TLValue::MsgContainer:
        result = processMsgContainer(message.skipTLValue());
        break;
    case TLValue::RpcResult:
        result = processRpcResult(message.skipTLValue());
        break;
    case TLValue::MsgsAck:
        result = processMessageAck(message.skipTLValue());
        break;
    case TLValue::BadMsgNotification:
    case TLValue::BadServerSalt:
        result = processIgnoredMessageNotification(message);
        break;
    case TLValue::GzipPacked:
        qCWarning(c_clientRpcLayerCategory) << CALL_INFO
                                            << "GzipPacked should be processed in the base class";
        break;
    case TLValue::Pong:
    {
        MTProto::Stream stream(message.data);
        TLPong pong;
        stream >> pong;
        PendingRpcOperation *op = m_operations.take(pong.msgId);
        if (op) {
            op->setFinishedWithReplyData(message.data);
            result = true;
        } else {
            qCWarning(c_clientRpcLayerCategory) << "Unexpected pong?!" << pong.msgId << pong.pingId;
        }
    }
        break;
    default:
        qCDebug(c_clientRpcLayerCategory) << Q_FUNC_INFO << "value:" << message.firstValue();
        break;
    }

    if (!result) {
        qCWarning(c_clientRpcLayerCategory) << CALL_INFO << "Unable to process" << message.firstValue();
    }

    return result;
}

bool RpcLayer::processRpcResult(const MTProto::Message &message)
{
    qCDebug(c_clientRpcLayerCategory) << "processRpcQuery(stream);";
    MTProto::Stream stream(message.data);
    quint64 messageId = 0;
    stream >> messageId;
    PendingRpcOperation *op = m_operations.take(messageId);
    if (!op) {
        qCWarning(c_clientRpcLayerCategory) << "processRpcQuery():"
                                            << "Unhandled RPC result for messageId"
                                            << hex << showbase << messageId;
        return false;
    }
    op->setFinishedWithReplyData(stream.readAll());
#define DUMP_CLIENT_RPC_PACKETS
#ifdef DUMP_CLIENT_RPC_PACKETS
    qCDebug(c_clientRpcLayerCategory) << "Client: Answer for message"
                                      << messageId << "op:" << op;
    qCDebug(c_clientRpcLayerCategory).noquote() << "Client: RPC Reply bytes:"
                                                << op->replyData().size() << op->replyData().toHex();
#endif
    qCDebug(c_clientRpcLayerCategory) << "processRpcQuery():" << "Set finished op" << op
                                      << "messageId:" << hex << showbase << messageId
                                      << "error:" << op->errorDetails();
    return true;
}

bool RpcLayer::processUpdates(const MTProto::Message &message)
{
    qCDebug(c_clientRpcLayerCategory) << "processUpdates()" << message.firstValue();
    MTProto::Stream stream(message.data);

    TLUpdates updates;
    stream >> updates;
    return m_UpdatesInternalApi->processUpdates(updates);
}

bool RpcLayer::processMessageAck(const MTProto::Message &message)
{
    MTProto::Stream stream(message.data);
    TLVector<quint64> idsVector;
    stream >> idsVector;
    qCDebug(c_clientRpcLayerCategory) << "processMessageAck():" << idsVector;

    return true;
}

bool RpcLayer::processSessionCreated(const MTProto::Message &message)
{
    MTProto::Stream stream(message.data);
    // https://core.telegram.org/mtproto/service_messages#new-session-creation-notification
    quint64 firstMsgId;
    quint64 uniqueId;
    quint64 serverSalt;

    stream >> firstMsgId;
    stream >> uniqueId;
    stream >> serverSalt;
    qCDebug(c_clientRpcLayerCategory) << "processSessionCreated(stream) {"
                                      << hex << showbase
                                      << "    firstMsgId:" << firstMsgId
                                      << "    uniqueId:" << uniqueId
                                      << "    serverSalt:" << serverSalt;

    return true;
}

bool RpcLayer::processIgnoredMessageNotification(const MTProto::Message &message)
{
    MTProto::Stream stream(message.data);
    TLBadMsgNotification tlNotification;
    stream >> tlNotification;
    // https://core.telegram.org/mtproto/service_messages_about_messages#notice-of-ignored-error-message
    MTProto::IgnoredMessageNotification notification(tlNotification);
    qCDebug(c_clientRpcLayerCategory) << CALL_INFO << notification.toString();

    MTProto::Message *m = m_messages.value(notification.messageId);
    if (!m) {
        qCWarning(c_clientRpcLayerCategory) << CALL_INFO
                                            << notification.toString() << "for unknown message id"
                                            << hex << showbase << notification.messageId;
        return false;
    }

    switch (notification.errorCode) {
    case MTProto::IgnoredMessageNotification::IncorrectServerSalt:
        // We sync local serverSalt value in processDecryptedMessageHeader().
        // Resend message will automatically apply the new salt
        return resendIgnoredMessage(notification.messageId);
    case MTProto::IgnoredMessageNotification::MessageIdTooOld:
        return resendIgnoredMessage(notification.messageId);
    case MTProto::IgnoredMessageNotification::SequenceNumberTooHigh:
        qCDebug(c_clientRpcLayerCategory) << "processIgnoredMessageNotification(SequenceNumberTooHigh):"
                                             " reduce seq num"
                                          << hex << showbase
                                          << " from" << m->sequenceNumber
                                          << " to" << (m->sequenceNumber - 2);
        m->sequenceNumber -= 2;
        return resendIgnoredMessage(notification.messageId);
    case MTProto::IgnoredMessageNotification::SequenceNumberTooLow:
        qCDebug(c_clientRpcLayerCategory) << "processIgnoredMessageNotification(SequenceNumberTooLow):"
                                             " increase seq num"
                                          << hex << showbase
                                          << " from" << m->sequenceNumber
                                          << " to" << (m->sequenceNumber + 2);
        m->sequenceNumber += 2;
    {
        quint32 messageContentNumber = m->sequenceNumber / 2;
        if (m_contentRelatedMessages <= messageContentNumber) {
            m_contentRelatedMessages = messageContentNumber + 1;
        }
    }
        return resendIgnoredMessage(notification.messageId);
    case MTProto::IgnoredMessageNotification::IncorrectTwoLowerOrderMessageIdBits:
        qCCritical(c_clientRpcLayerCategory) << "How we ever managed to mess with"
                                                " the lower messageId bytes?!";
        // Just resend the message. We regenerate message id, so it can help.
        return resendIgnoredMessage(notification.messageId);
    default:
        break;
    }

    qCWarning(c_clientRpcLayerCategory) << "Unhandled error:" << notification.toString();
    return false;
}

bool RpcLayer::processMessageHeader(const MTProto::FullMessageHeader &header)
{
    if (serverSalt() != header.serverSalt) {
        qCDebug(c_clientRpcLayerCategory).noquote()
                << QStringLiteral("Received different server salt: %1 (remote) vs %2 (local)."
                                  " Fix local to remote.")
                   .arg(toHex(header.serverSalt))
                   .arg(toHex(serverSalt()));
        setServerSalt(header.serverSalt);
    }

    if (m_sessionId != header.sessionId) {
        qCWarning(c_clientRpcLayerCategory) << CALL_INFO << "Session Id is wrong.";
        return false;
    }
    return true;
}

QByteArray RpcLayer::getEncryptionKeyPart() const
{
    return m_sendHelper->getClientKeyPart();
}

QByteArray RpcLayer::getVerificationKeyPart() const
{
    return m_sendHelper->getServerKeyPart();
}

quint64 RpcLayer::sendRpc(PendingRpcOperation *operation)
{
    operation->setConnection(m_sendHelper->getConnection());

    MTProto::Message *message = new MTProto::Message();
    message->messageId = m_sendHelper->newMessageId(SendMode::Client);
    if (operation->isContentRelated()) {
        message->sequenceNumber = m_contentRelatedMessages * 2 + 1;
        ++m_contentRelatedMessages;
    } else {
        if (m_contentRelatedMessages == 0) {
            qCCritical(c_clientRpcLayerCategory) << CALL_INFO
                                                 << "First message should be content related!";
        }
        message->sequenceNumber = m_contentRelatedMessages * 2;
    }

    // We have to add InitConnection here because
    // sendPackage() implementation is shared with server
    if (message->sequenceNumber == 1) {
        message->setData(getInitConnection() + operation->requestData());
    } else {
        message->setData(operation->requestData());
    }
    m_operations.insert(message->messageId, operation);
    m_messages.insert(message->messageId, message);
    sendPacket(*message);
    return message->messageId;
}

bool RpcLayer::resendIgnoredMessage(quint64 messageId)
{
    MTProto::Message *message = m_messages.take(messageId);
    PendingRpcOperation *operation = m_operations.take(messageId);
    if (!operation) {
        qCCritical(c_clientRpcLayerCategory) << CALL_INFO
                                             << "Unable to find the message to resend"
                                             << hex << messageId;
        delete message;
        return false;
    }
    qCDebug(c_clientRpcLayerCategory) << "Resend message"
                                      << hex << messageId
                                      << message->firstValue();
    message->messageId = m_sendHelper->newMessageId(SendMode::Client);
    m_operations.insert(message->messageId, operation);
    m_messages.insert(message->messageId, message);
    sendPacket(*message);
    emit operation->resent(messageId, message->messageId);
    return message->messageId;
}

void RpcLayer::acknowledgeMessages()
{
    MTProto::Stream outputStream(MTProto::Stream::WriteOnly);
    TLVector<quint64> idsVector = m_messagesToAck;
    m_messagesToAck.clear();
    outputStream << TLValue::MsgsAck;
    outputStream << idsVector;

    MTProto::Message *message = new MTProto::Message();
    message->messageId = m_sendHelper->newMessageId(SendMode::Client);
    message->sequenceNumber = m_contentRelatedMessages * 2;
    message->setData(outputStream.getData());

    m_messages.insert(message->messageId, message);
    sendPacket(*message);
}

void RpcLayer::onConnectionFailed()
{
    for (PendingRpcOperation *op : m_operations) {
        if (!op->isFinished()) {
            op->setFinishedWithTextError(QLatin1String("Connection failed"));
        }
    }
    m_operations.clear();
    qDeleteAll(m_messages);
    m_messages.clear();
}

QByteArray RpcLayer::getInitConnection() const
{
#ifdef DEVELOPER_BUILD
    qCDebug(c_clientRpcLayerCategory) << CALL_INFO << "layer" << TLValue::CurrentLayer;
#endif
    MTProto::Stream outputStream(MTProto::Stream::WriteOnly);
    outputStream << TLValue::InvokeWithLayer;
    outputStream << TLValue::CurrentLayer;
    outputStream << TLValue::InitConnection;
    outputStream << m_appInfo->appId();
    outputStream << m_appInfo->deviceInfo();
    outputStream << m_appInfo->osInfo();
    outputStream << m_appInfo->appVersion();
#if TELEGRAMQT_LAYER >= 67
    outputStream << m_appInfo->languageCode(); // System language
    outputStream << QString(); // Langpack
#endif
    outputStream << m_appInfo->languageCode(); // Lang code
    return outputStream.getData();
}

void RpcLayer::addMessageToAck(quint64 messageId)
{
    if (m_messagesToAck.isEmpty()) {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
        QMetaObject::invokeMethod(this, &RpcLayer::acknowledgeMessages, Qt::QueuedConnection);
#else
        QMetaObject::invokeMethod(this, "acknowledgeMessages", Qt::QueuedConnection);
#endif
    }
    m_messagesToAck.append(messageId);
}

} // Client namespace

} // Telegram namespace
<|endoftext|>