text:
//=========================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt for details.
//
//  This software is distributed WITHOUT ANY WARRANTY; without even
//  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
//  PURPOSE.  See the above copyright notice for more information.
//=========================================================================

// .NAME qtAttributePreview - Standalone test program for qtRootView instances
// .SECTION Description
// .SECTION See Also

#include "smtk/extension/qt/qtUIManager.h"

#include "smtk/io/AttributeReader.h"
#include "smtk/io/AttributeWriter.h"
#include "smtk/io/Logger.h"

#include "smtk/common/View.h"

#include "smtk/attribute/Attribute.h"
#include "smtk/attribute/Definition.h"
#include "smtk/attribute/System.h"

#include "smtk/model/Manager.h"

#include <QApplication>
#include <QFrame>
#include <QVBoxLayout>
#include <QWidget>

#ifndef _MSC_VER
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored"-Wunused-parameter"
#endif
#include <boost/lexical_cast.hpp>
#ifndef _MSC_VER
#  pragma GCC diagnostic pop
#endif

#include <iostream>

int main(int argc, char *argv[])
{
  if (argc < 2)
    {
    std::cout << "\n"
              << "Simple program to load attribute system and display corresponding editor panel" << "\n"
              << "Usage: qtAttributePreview attribute_filename  [output_filename]"
              << "  [view_name | view_number]" << "\n"
              << std::endl;
    return -1;
    }

  // Instantiate and load attribute system
  smtk::attribute::System system;
  char *inputPath = argv[1];
  std::cout << "Loading simulation file: " << inputPath << std::endl;
  smtk::io::AttributeReader reader;
  smtk::io::Logger inputLogger;
  bool  err = reader.read(system, inputPath, true, inputLogger);
  if (err)
    {
    std::cout << "Error loading simulation file -- exiting" << "\n";
    std::cout << inputLogger.convertToString() << std::endl;
    return -2;
    }

  // If system contains no views, create InstancedView by default
  // assume there is at most one root type view
  smtk::common::ViewPtr root = system.findTopLevelView();

  if (!root)
    {
    root = smtk::common::View::New("Group", "RootView");
    root->details().setAttribute("TopLevel", "true");
    system.addView(root);
    smtk::common::View::Component& temp = root->details().addChild("Views");
    (void)temp;
    int viewsIndex = root->details().findChild("Views");

    //  Lets add instances of all
    // non-abstract attributE definitions
    smtk::common::View::Component &viewsComp = root->details().child(viewsIndex);
    std::vector<smtk::attribute::DefinitionPtr> defs;
    std::vector<smtk::attribute::DefinitionPtr> baseDefinitions;
    system.findBaseDefinitions(baseDefinitions);
    std::vector<smtk::attribute::DefinitionPtr>::const_iterator baseIter;

    for (baseIter = baseDefinitions.begin();
         baseIter != baseDefinitions.end();
         baseIter++)
      {
      // Add def if not abstract
      if (!(*baseIter)->isAbstract())
        {
        //defs.push_back(*baseIter);
        }

      std::vector<smtk::attribute::DefinitionPtr> derivedDefs;
      system.findAllDerivedDefinitions(*baseIter, true, derivedDefs);
      defs.insert(defs.end(), derivedDefs.begin(), derivedDefs.end());
      }

    // Instantiate attribute for each concrete definition
    std::vector<smtk::attribute::DefinitionPtr>::const_iterator defIter;
    for (defIter = defs.begin(); defIter != defs.end(); defIter++)
      {
      smtk::common::ViewPtr instanced =
        smtk::common::View::New("Instanced", (*defIter)->type());

      smtk::common::View::Component &comp =
        instanced->details().addChild("InstancedAttributes").addChild("Att");
      comp.setAttribute("Type", (*defIter)->type());
      comp.setAttribute("Name", (*defIter)->type());
      system.addView(instanced);
      smtk::attribute::AttributePtr instance =
        system.createAttribute((*defIter)->type());
      comp.setContents(instance->name());
      viewsComp.addChild("View").setAttribute("Title", (*defIter)->type());
      }
    }

  smtk::model::ManagerPtr modelManager = system.refModelManager();

  // Instantiate Qt application
  QApplication *app = new QApplication(argc, argv);

  // Instantiate smtk's qtUIManager
  smtk::attribute::qtUIManager *uiManager =
    new smtk::attribute::qtUIManager(system);

  // Instantiate empty widget as containter for qtUIManager
  QWidget *widget = new QWidget();
  QVBoxLayout *layout = new QVBoxLayout();
  widget->setLayout(layout);

  bool useInternalFileBrowser = true;

  smtk::common::ViewPtr view;

  // Check for input "view" argument
  if (argc <= 3)
    {
    // Generate tab group with all views (standard)
    uiManager->setSMTKView(root, widget, useInternalFileBrowser);
    }
  else
    {
    // Render one view (experimental)
    // First check if argv[3] isa name
    std::string input = argv[3];
    view = system.findView(input);
    if (!view)
      {
      std::cout << "ERROR: View \"" << input << "\" not found" << std::endl;
      return -4;
      }
    }

  if (view)
    {
    // Add simple panel (QFrame) for aesthetics
    QFrame *frame = new QFrame();
    frame->setFrameShadow(QFrame::Raised);
    frame->setFrameShape(QFrame::Panel);
    //frame->setStyleSheet("QFrame { background-color: pink; }");

    QVBoxLayout *frameLayout = new QVBoxLayout();
    frame->setLayout(frameLayout);
    uiManager->setSMTKView(view, frame, useInternalFileBrowser);
    widget->layout()->addWidget(frame);
    }

  widget->show();
//  uiManager->rootView()->showAdvanceLevel(0);
  int retcode = app->exec();

  if (argc > 2)
    {
    char *outputPath = argv[2];
    std::cout << "Writing resulting simulation file: " << outputPath << std::endl;
    smtk::io::AttributeWriter writer;
    smtk::io::Logger outputLogger;
    bool  outputErr = writer.write(system, outputPath, outputLogger);
    if (outputErr)
      {
      std::cout << "Error writing simulation file -- exiting" << "\n"
                << outputLogger.convertToString() << std::endl;
      return -3;
      }
    }

  // Done
  return retcode;
}

ext: cxx

max_stars_repo_name: yumin/SMTK

max_issues_repo_name: yumin/SMTK

max_forks_repo_name: yumin/SMTK


text:
//==================================================================================================
/*!

  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/scalar/exponentbits.hpp>
#include <scalar_test.hpp>
#include <boost/simd/detail/dispatch/meta/as_integer.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/zero.hpp>

STF_CASE_TPL (" exponentbits float",  (float))
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::exponentbits;
  using r_t = decltype(exponentbits(T()));

  // return type conformity test
  STF_TYPE_IS(r_t, bd::as_integer_t<T>);

  // specific values tests
  for(int i=1, k = 0; i < 10; i*= 2, ++k)
  {
  namespace bs = boost::simd;
    STF_EQUAL(1065353216+k*8388608, exponentbits(T(i)));
  }
}

STF_CASE_TPL (" exponentbits double",  (double))
{
  namespace bd = boost::dispatch;
  namespace bs = boost::simd;
  using bs::exponentbits;
  using r_t = decltype(exponentbits(T()));

  // return type conformity test
  using r_t = decltype(exponentbits(T()));

  // return type conformity test
  STF_TYPE_IS(r_t, bd::as_integer_t<T>);

  // specific values tests
  for(int i=1, k = 0; i < 10; i*= 2, ++k)
   {
  namespace bs = boost::simd;
     STF_EQUAL(4607182418800017408ll+k*4503599627370496ll, exponentbits(T(i)));
   }
}

ext: cpp


text:
//
// Copyright (c) 2015-2019 CNRS INRIA
//

#ifndef __pinocchio_multibody_parsers_urdf_geometry_hxx__
#define __pinocchio_multibody_parsers_urdf_geometry_hxx__

#include "pinocchio/parsers/urdf.hpp"

#include <iostream>
#include <sstream>
#include <iomanip>
#include <boost/shared_ptr.hpp>

#ifdef PINOCCHIO_WITH_HPP_FCL
  #include <hpp/fcl/mesh_loader/loader.h>
  #include <hpp/fcl/mesh_loader/assimp.h>
#endif // PINOCCHIO_WITH_HPP_FCL

namespace pinocchio
{
  namespace urdf
  {
    namespace details
    {
      struct UrdfGeomVisitorBase
      {
        typedef FrameTpl<urdf_scalar_type, 0> Frame;

        virtual Frame getBodyFrame (const std::string& name, FrameIndex& fid) const = 0;
      };

      template<typename _Scalar, int _Options, template<typename,int> class JointCollectionTpl>
      struct UrdfGeomVisitor : UrdfGeomVisitorBase
      {
        typedef ModelTpl<_Scalar,_Options,JointCollectionTpl> Model;
        const Model& model;

        UrdfGeomVisitor (const Model& model) : model(model) {}

        Frame getBodyFrame (const std::string& link_name, FrameIndex& fid) const
        {
          if (!model.existFrame(link_name, BODY))
          {
            throw std::invalid_argument("No link " + link_name + " in model");
          }
          fid = model.getFrameId(link_name, BODY);
          PINOCCHIO_CHECK_INPUT_ARGUMENT(model.frames[fid].type == BODY);
          return model.frames[fid].template cast<urdf_scalar_type>();
        }
      };

      /**
       * @brief      Recursive procedure for reading the URDF tree, looking for geometries
       *             This function fill the geometric model whith geometry objects retrieved from the URDF tree
       *
       * @param[in]  tree           The URDF kinematic tree
       * @param[in]  meshLoader     The FCL mesh loader to avoid duplications of already loaded geometries
       * @param[in]  link           The current URDF link
       * @param      model          The model to which is the GeometryModel associated
       * @param      geomModel      The GeometryModel where the Collision Objects must be added
       * @param[in]  package_dirs   A vector containing the different directories where to search for packages
       * @param[in]  type           The type of objects that must be loaded ( can be VISUAL or COLLISION)
       *
       */
      PINOCCHIO_DLLAPI void parseTreeForGeom(UrdfGeomVisitorBase& visitor,
                            const std::istream& xmlStream,
                            const GeometryType type,
                            GeometryModel & geomModel,
                            const std::vector<std::string> & package_dirs,
                            ::hpp::fcl::MeshLoaderPtr meshLoader);

      } // namespace details

      template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl>
      GeometryModel& buildGeom(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
                               const std::string & filename,
                               const GeometryType type,
                               GeometryModel & geomModel,
                               const std::vector<std::string> & package_dirs,
                               ::hpp::fcl::MeshLoaderPtr meshLoader)
      {
        std::ifstream xmlStream(filename.c_str());
        if (! xmlStream.is_open())
        {
          const std::string exception_message (filename + " does not seem to be a valid file.");
          throw std::invalid_argument(exception_message);
        }
        return buildGeom (model, xmlStream, type, geomModel, package_dirs, meshLoader);
      }

      template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl>
      GeometryModel& buildGeom(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
                               const std::istream& xmlStream,
                               const GeometryType type,
                               GeometryModel & geomModel,
                               const std::vector<std::string> & package_dirs,
                               ::hpp::fcl::MeshLoaderPtr meshLoader)
      {
        details::UrdfGeomVisitor<Scalar, Options, JointCollectionTpl> visitor (model);
        details::parseTreeForGeom (visitor, xmlStream, type, geomModel,
            package_dirs, meshLoader);
        return geomModel;
      }

  } // namespace urdf
} // namespace pinocchio

#endif // ifndef __pinocchio_multibody_parsers_urdf_geometry_hxx__

ext: hxx


text:
/**
 *  @file
 *  @copyright defined in enumivo/LICENSE.txt
 */
#include <boost/test/unit_test.hpp>

#include "../../common/database_fixture.hpp"

#include <rate_limit_auth/rate_limit_auth.wast.hpp>
#include <currency/currency.wast.hpp>

using namespace enumivo;
using namespace chain;

BOOST_AUTO_TEST_SUITE(wasm_tests)

//Test rate limiting with single authority
BOOST_FIXTURE_TEST_CASE(rate_limit_single_authority_test, testing_fixture)
{ try {
      Make_Blockchain(chain);
      chain.produce_blocks(10);
      Make_Account(chain, currency);
      Make_Account(chain, test1);
      Make_Account(chain, test2);
      chain.produce_blocks(1);

      types::setcode handler;
      handler.account = "currency";

      auto wasm = testing_blockchain::assemble_wast( currency_wast );
      handler.code.resize(wasm.size());
      memcpy( handler.code.data(), wasm.data(), wasm.size() );

      {
         enumivo::chain::signed_transaction txn;
         txn.scope = {"currency"};
         txn.messages.resize(1);
         txn.messages[0].code = config::enu_contract_name;
         txn.messages[0].authorization.emplace_back(types::account_permission{"currency","active"});
         transaction_set_message(txn, 0, "setcode", handler);
         txn.expiration = chain.head_block_time() + 100;
         transaction_set_reference_block(txn, chain.head_block_id());
         chain.push_transaction(txn);
         chain.produce_blocks(1);

         txn.scope = sort_names({"test1","currency"});
         txn.messages.clear();
         transaction_emplace_message(txn, "currency",
                            vector<types::account_permission>{ {"currency","active"} },
                            "transfer", types::transfer{"currency", "test1", 10000000,""});
         chain.push_transaction(txn);

         txn.scope = sort_names({"test2","currency"});
         txn.messages.clear();
         transaction_emplace_message(txn, "currency",
                            vector<types::account_permission>{ {"currency","active"} },
                            "transfer", types::transfer{"currency", "test2", 10000000,""});
         chain.push_transaction(txn);
         chain.produce_blocks(1);
      }

      enumivo::chain::signed_transaction txn;
      txn.scope = sort_names({"test1","inita"});
      txn.expiration = chain.head_block_time() + 100;
      transaction_set_reference_block(txn, chain.head_block_id());

      enumivo::chain::signed_transaction txn2;
      txn2.scope = sort_names({"test2","inita"});
      txn2.expiration = chain.head_block_time() + 100;
      transaction_set_reference_block(txn2, chain.head_block_id());

      // sending 1800 transaction messages for 2 authorization accounts, which is the rate limit for this second
      // since there were no previous messages before this
      uint32_t i = 0;
      for (; i < 1800; ++i)
      {
         txn.messages.clear();
         transaction_emplace_message(txn, "currency",
                            vector<types::account_permission>{ {"test1","active"} },
                            "transfer", types::transfer{"test1", "inita", 1+i,""});
         chain.push_transaction(txn);

         txn2.messages.clear();
         transaction_emplace_message(txn2, "currency",
                            vector<types::account_permission>{ {"test2","active"} },
                            "transfer", types::transfer{"test2", "inita", 1+i,""});
         chain.push_transaction(txn2);
      }

      // at rate limit for test1, so it will be rejected
      try
      {
         txn.messages.clear();
         transaction_emplace_message(txn, "currency",
                            vector<types::account_permission>{ {"test1","active"} },
                            "transfer", types::transfer{"test1", "inita", i+1,""});
         chain.push_transaction(txn);
         BOOST_FAIL("Should have gotten tx_msgs_auth_exceeded exception.");
      }
      catch (const tx_msgs_auth_exceeded& ex)
      {
      }

      // at rate limit for test2, so it will be rejected
      try
      {
         txn.messages.clear();
         transaction_emplace_message(txn2, "currency",
                            vector<types::account_permission>{ {"test2","active"} },
                            "transfer", types::transfer{"test2", "inita", i+1,""});
         chain.push_transaction(txn2);
         BOOST_FAIL("Should have gotten tx_msgs_auth_exceeded exception.");
      }
      catch (const tx_msgs_auth_exceeded& ex)
      {
      }

} FC_LOG_AND_RETHROW() }

//Test rate limiting with multiple authorities
BOOST_FIXTURE_TEST_CASE(rate_limit_multi_authority_test, testing_fixture)
{ try {
   Make_Blockchain(chain);
   chain.produce_blocks(10);
   Make_Account(chain, currency);
   Make_Account(chain, test1);
   Make_Account(chain, test2);
   Make_Account(chain, test3);
   Make_Account(chain, test4);
   Make_Account(chain, test5);
   Make_Account(chain, test11);
   Make_Account(chain, test12);
   Make_Account(chain, test13);
   Make_Account(chain, test14);
   Make_Account(chain, test15);
   Make_Account(chain, test21);
   Make_Account(chain, test22);
   Make_Account(chain, test23);
   Make_Account(chain, test24);
   Make_Account(chain, test25);
   Make_Account(chain, test31);
   Make_Account(chain, test32);
   Make_Account(chain, test33);
   Make_Account(chain, test34);
   Make_Account(chain, test35);
   Make_Account(chain, test41);
   Make_Account(chain, test42);
   Make_Account(chain, test43);
   Make_Account(chain, test44);
   Make_Account(chain, test45);
   chain.produce_blocks(1);

   types::setcode handler;
   handler.account = "currency";

   auto wasm = testing_blockchain::assemble_wast( currency_wast );
   handler.code.resize(wasm.size());
   memcpy( handler.code.data(), wasm.data(), wasm.size() );

   types::setcode handler2;
   handler2.account = "test1";

   auto wasm2 = testing_blockchain::assemble_wast( rate_limit_auth_wast );
   handler2.code.resize(wasm2.size());
   memcpy( handler2.code.data(), wasm2.data(), wasm2.size() );

   // setup currency code, and transfer tokens so accounts can do transfers
   if (false)
   {
      enumivo::chain::signed_transaction txn;
      txn.scope = {"currency"};
      txn.messages.resize(1);
      txn.messages[0].code = config::enu_contract_name;
      txn.messages[0].authorization.emplace_back(types::account_permission{"currency","active"});
      transaction_set_message(txn, 0, "setcode", handler);
      txn.expiration = chain.head_block_time() + 100;
      transaction_set_reference_block(txn, chain.head_block_id());
      chain.push_transaction(txn);
      chain.produce_blocks(1);
   }

   // setup test1 code account, this will be used for most messages, to reach code account rate limit
   {
      enumivo::chain::signed_transaction txn;
      txn.scope = {"test1"};
      txn.messages.resize(1);
      txn.messages[0].code = config::enu_contract_name;
      txn.messages[0].authorization.emplace_back(types::account_permission{"test1","active"});
      transaction_set_message(txn, 0, "setcode", handler2);
      txn.expiration = chain.head_block_time() + 100;
      transaction_set_reference_block(txn, chain.head_block_id());
      chain.push_transaction(txn);
      chain.produce_blocks(1);
   }

   // setup test5 code account, this will be used to verify that code account rate limit only affects individual code accounts
   {
      handler2.account = "test5";
      enumivo::chain::signed_transaction txn;
      txn.scope = {"test5"};
      txn.messages.resize(1);
      txn.messages[0].code = config::enu_contract_name;
      txn.messages[0].authorization.emplace_back(types::account_permission{"test5","active"});
      transaction_set_message(txn, 0, "setcode", handler2);
      txn.expiration = chain.head_block_time() + 100;
      transaction_set_reference_block(txn, chain.head_block_id());
      chain.push_transaction(txn);
      chain.produce_blocks(1);
   }

   // setup transactions
   enumivo::chain::signed_transaction txn;
   txn.scope = sort_names({"test1","test2"});
   txn.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn, chain.head_block_id());

   enumivo::chain::signed_transaction txn2;
   txn2.scope = sort_names({"test2","test3"});
   txn2.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn2, chain.head_block_id());

   enumivo::chain::signed_transaction txn3;
   txn3.scope = sort_names({"test1","test4"});
   txn3.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn3, chain.head_block_id());

   // looping 900 times to put some accounts (test1 and test2) at 1800 rate limit
   for (uint32_t i = 0; i < 900; ++i)
   {
      txn.messages.clear();
      transaction_emplace_message(txn, "test1",
                         vector<types::account_permission>{ {"test1","active"},{"test2","active"} },
                         "transfer", types::transfer{"test1", "test2", i+1, ""});
      chain.push_transaction(txn);

      txn2.messages.clear();
      transaction_emplace_message(txn2, "test1",
                         vector<types::account_permission>{ {"test2","active"},{"test3","active"} },
                         "transfer", types::transfer{"test2", "test3", i+1, ""});
      chain.push_transaction(txn2);

      txn3.messages.clear();
      transaction_emplace_message(txn3, "test1",
                         vector<types::account_permission>{ {"test1","active"},{"test4","active"} },
                         "transfer", types::transfer{"test1", "test4", i+1, ""});
      chain.push_transaction(txn3);

   }
   // auth test1 - 1800 transaction messages
   // auth test2 - 1800 transaction messages
   // auth test3 - 900 transaction messages
   // auth test4 - 900 transaction messages
   // code test1 - 5400 transaction messages

   // test1 at rate limit, should be rejected
   try
   {
      txn.scope = sort_names({"test1"});
      txn.messages.clear();
      transaction_emplace_message(txn, "currency",
                         vector<types::account_permission>{ {"test1","active"} },
                         "transfer", types::transfer{"test1", "test4", 1000,""});
      chain.push_transaction(txn);
      BOOST_FAIL("Should have gotten tx_msgs_auth_exceeded exception.");
   }
   catch (const tx_msgs_auth_exceeded& ex)
   {
   }

   // test2 at rate limit, should be rejected
   try
   {
      txn2.scope = sort_names({"test2"});
      txn2.messages.clear();
      transaction_emplace_message(txn2, "currency",
                         vector<types::account_permission>{ {"test2","active"} },
                         "transfer", types::transfer{"test2", "test3", 1000,""});
      chain.push_transaction(txn2);
      BOOST_FAIL("Should have gotten tx_msgs_auth_exceeded exception.");
   }
   catch (const tx_msgs_auth_exceeded& ex)
   {
   }

   enumivo::chain::signed_transaction txn4;
   txn4.scope = sort_names({"test3","test4"});
   txn4.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn4, chain.head_block_id());

   // looping 900 times to put remaining accounts at 1800 rate limit
   for (uint32_t i = 0; i < 900; ++i)
   {
      txn4.messages.clear();
      transaction_emplace_message(txn4, "test1",
                         vector<types::account_permission>{ {"test3","active"},{"test4","active"} },
                         "transfer", types::transfer{"test3", "test4", i+1, ""});
      chain.push_transaction(txn4);

   }
   // auth test1 - 1800 transaction messages
   // auth test2 - 1800 transaction messages
   // auth test3 - 1800 transaction messages
   // auth test4 - 1800 transaction messages
   // code test1 - 7200 transaction messages

   // test3 at rate limit, should be rejected
   try
   {
      txn.scope = sort_names({"test3"});
      txn.messages.clear();
      transaction_emplace_message(txn, "currency",
                         vector<types::account_permission>{ {"test3","active"} },
                         "transfer", types::transfer{"test3", "test5", 1000,""});
      chain.push_transaction(txn);
      BOOST_FAIL("Should have gotten tx_msgs_auth_exceeded exception.");
   }
   catch (const tx_msgs_auth_exceeded& ex)
   {
   }

   // test4 at rate limit, should be rejected
   try
   {
      txn2.scope = sort_names({"test4"});
      txn2.messages.clear();
      transaction_emplace_message(txn2, "currency",
                         vector<types::account_permission>{ {"test4","active"} },
                         "transfer", types::transfer{"test4", "test5", 1000,""});
      chain.push_transaction(txn2);
      BOOST_FAIL("Should have gotten tx_msgs_auth_exceeded exception.");
   }
   catch (const tx_msgs_auth_exceeded& ex)
   {
   }

   enumivo::chain::signed_transaction txn11;
   txn11.scope = sort_names({"test11","test31"});
   txn11.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn11, chain.head_block_id());

   enumivo::chain::signed_transaction txn12;
   txn12.scope = sort_names({"test12","test32"});
   txn12.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn12, chain.head_block_id());

   enumivo::chain::signed_transaction txn13;
   txn13.scope = sort_names({"test13","test33"});
   txn13.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn13, chain.head_block_id());

   enumivo::chain::signed_transaction txn14;
   txn14.scope = sort_names({"test14","test34"});
   txn14.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn14, chain.head_block_id());

   enumivo::chain::signed_transaction txn15;
   txn15.scope = sort_names({"test15","test35"});
   txn15.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn15, chain.head_block_id());

   enumivo::chain::signed_transaction txn21;
   txn21.scope = sort_names({"test21","test41"});
   txn21.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn21, chain.head_block_id());

   enumivo::chain::signed_transaction txn22;
   txn22.scope = sort_names({"test22","test42"});
   txn22.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn22, chain.head_block_id());

   enumivo::chain::signed_transaction txn23;
   txn23.scope = sort_names({"test23","test43"});
   txn23.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn23, chain.head_block_id());

   enumivo::chain::signed_transaction txn24;
   txn24.scope = sort_names({"test24","test44"});
   txn24.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn24, chain.head_block_id());

   enumivo::chain::signed_transaction txn25;
   txn25.scope = sort_names({"test25","test45"});
   txn25.expiration = chain.head_block_time() + 100;
   transaction_set_reference_block(txn25, chain.head_block_id());

   // looping 1440 times with 10 different transactions to get to the 18000 code account rate limit for test1
   uint32_t i = 0;
   for (; i < 1440; ++i)
   {
      txn11.messages.clear();
      transaction_emplace_message(txn11, "test1",
                         vector<types::account_permission>{ {"test11","active"},{"test31","active"} },
                         "transfer", types::transfer{"test11", "test31", i+1, ""});
      chain.push_transaction(txn11);

      txn12.messages.clear();
      transaction_emplace_message(txn12, "test1",
                         vector<types::account_permission>{ {"test12","active"},{"test32","active"} },
                         "transfer", types::transfer{"test12", "test32", i+1, ""});
      chain.push_transaction(txn12);

      txn13.messages.clear();
      transaction_emplace_message(txn13, "test1",
                         vector<types::account_permission>{ {"test13","active"},{"test33","active"} },
                         "transfer", types::transfer{"test13", "test33", i+1, ""});
      chain.push_transaction(txn13);

      txn14.messages.clear();
      transaction_emplace_message(txn14, "test1",
                         vector<types::account_permission>{ {"test14","active"},{"test34","active"} },
                         "transfer", types::transfer{"test14", "test34", i+1, ""});
      chain.push_transaction(txn14);

      txn15.messages.clear();
      transaction_emplace_message(txn15, "test1",
                         vector<types::account_permission>{ {"test15","active"},{"test35","active"} },
                         "transfer", types::transfer{"test15", "test35", i+1, ""});
      chain.push_transaction(txn15);

      txn21.messages.clear();
      transaction_emplace_message(txn21, "test1",
                         vector<types::account_permission>{ {"test21","active"},{"test41","active"} },
                         "transfer", types::transfer{"test21", "test41", i+1, ""});
      chain.push_transaction(txn21);

      txn22.messages.clear();
      transaction_emplace_message(txn22, "test1",
                         vector<types::account_permission>{ {"test22","active"},{"test42","active"} },
                         "transfer", types::transfer{"test22", "test42", i+1, ""});
      chain.push_transaction(txn22);

      txn23.messages.clear();
      transaction_emplace_message(txn23, "test1",
                         vector<types::account_permission>{ {"test23","active"},{"test43","active"} },
                         "transfer", types::transfer{"test23", "test43", i+1, ""});
      chain.push_transaction(txn23);

      txn24.messages.clear();
      transaction_emplace_message(txn24, "test1",
                         vector<types::account_permission>{ {"test24","active"},{"test44","active"} },
                         "transfer", types::transfer{"test24", "test44", i+1, ""});
      chain.push_transaction(txn24);

      txn25.messages.clear();
      transaction_emplace_message(txn25, "test1",
                         vector<types::account_permission>{ {"test25","active"},{"test45","active"} },
                         "transfer", types::transfer{"test25", "test45", i+1, ""});
      chain.push_transaction(txn25);

   }
   // code test1 - 18000 transaction messages

   // reached rate limit, should be rejected
   try
   {
      txn11.messages.clear();
      transaction_emplace_message(txn11, "test1",
                         vector<types::account_permission>{ {"test11","active"},{"test21","active"} },
                         "transfer", types::transfer{"test11", "test21", i+1, ""});
      chain.push_transaction(txn11);
      BOOST_FAIL("Should have gotten tx_msgs_code_exceeded exception.");
   }
   catch (const tx_msgs_code_exceeded& ex)
   {
   }

   // verify that only test1 is blocked
   txn11.messages.clear();
   transaction_emplace_message(txn11, "test5",
                      vector<types::account_permission>{ {"test11","active"},{"test31","active"} },
                      "transfer", types::transfer{"test11", "test31", i+1, ""});
   chain.push_transaction(txn11);

   // still should be rejected
   try
   {
      txn11.messages.clear();
      transaction_emplace_message(txn11, "test1",
                         vector<types::account_permission>{ {"test11","active"},{"test21","active"} },
                         "transfer", types::transfer{"test11", "test21", i+1, ""});
      chain.push_transaction(txn11);
      BOOST_FAIL("Should have gotten tx_msgs_code_exceeded exception.");
   }
   catch (const tx_msgs_code_exceeded& ex)
   {
   }

   chain.produce_blocks(1);

   // rate limit calculation will now be at least at a new second, so will always be able to handle a new transaction message
   txn11.messages.clear();
   transaction_emplace_message(txn11, "test1",
                      vector<types::account_permission>{ {"test11","active"},{"test21","active"} },
                      "transfer", types::transfer{"test11", "test21", i+1, ""});
   chain.push_transaction(txn11);

} FC_LOG_AND_RETHROW() }

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: momokapapa/hanakapapa

max_issues_repo_name: momokapapa/hanakapapa

max_forks_repo_name: momokapapa/hanakapapa


text:
/*
	A thread-safe singleton class which can be used build up a graph of a
	program's locking order for mutexes, and check it for locking
	order violations.

	begin: Frank Mori Hess <fmhess@users.sourceforge.net>  2007-10-26
	copyright (c) Frank Mori Hess 2007-2008
*/

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

#ifndef _POET_MUTEX_GRAPHER_DECL_HPP
#define _POET_MUTEX_GRAPHER_DECL_HPP

#include <boost/function.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/optional.hpp>
#include <boost/thread/once.hpp>
#include <boost/thread/tss.hpp>
#include <boost/noncopyable.hpp>
#include <cassert>
#include <list>
#include <map>
#include <poet/detail/template_static.hpp>
#include <poet/monitor_ptr.hpp>
#include <poet/monitor_locks.hpp>
#include <string>

namespace poet
{
	// forward declarations
	class acyclic_mutex_base;
	template<typename Mutex, typename Key, typename KeyCompare>
	class acyclic_mutex;
	namespace detail
	{
		template<typename AcyclicMutex>
		class acyclic_scoped_lock;

		template<typename AssociatedClass> class template_static_once_flag
		{
		public:
			static boost::once_flag flag;
		};
		template<typename AssociatedClass>
			boost::once_flag template_static_once_flag<AssociatedClass>::flag = BOOST_ONCE_INIT;
	};

	class mutex_grapher: public boost::noncopyable
	{
		typedef monitor_ptr<mutex_grapher, boost::mutex> monitor_type;
	public:
		struct vertex_properties
		{
			vertex_properties(): use_count(0) {}
			std::string name;
			int use_count;
		};
		struct edge_properties
		{
			edge_properties(): locking_order_violation(false) {}
			bool locking_order_violation;
		};
		typedef boost::adjacency_list<boost::setS, boost::vecS, boost::bidirectionalS, vertex_properties, edge_properties>
			locking_order_graph;
		typedef std::list<const acyclic_mutex_base *> mutex_list_type;
		class unique_lock: public monitor_unique_lock<monitor_type>
		{
		public:
			unique_lock(): monitor_unique_lock<monitor_type>(mutex_grapher::instance())
			{}
		};
		typedef unique_lock scoped_lock;

		const locking_order_graph& graph() const {return _graph;}
		inline void write_graphviz(std::ostream &out_stream) const;
		template<typename Func>
		void set_cycle_handler(Func func)
		{
			_cycle_handler = func;
		}
		const mutex_list_type& locked_mutexes() const
		{
			check_locked_mutexes_init();
			return *_locked_mutexes;
		}
	private:
		class vertex_labeler
		{
		public:
			vertex_labeler(const locking_order_graph &graph): _graph(graph)
			{}
			void operator()(std::ostream &output_stream, locking_order_graph::vertex_descriptor vertex)
			{
				output_stream << "[label=\"" << _graph[vertex].name << "\"]";
			}
		private:
			const locking_order_graph &_graph;
		};
		class edge_colorer
		{
		public:
			edge_colorer(const locking_order_graph &graph): _graph(graph)
			{}
			void operator()(std::ostream &output_stream, locking_order_graph::edge_descriptor edge)
			{
				output_stream << "[color=\"";
				if(_graph[edge].locking_order_violation)
				{
					output_stream << "red";
				}else
				{
					output_stream << "black";
				}
				output_stream << "\"]";
			}
		private:
			const locking_order_graph &_graph;
		};
		template<typename Mutex, bool recursive, enum mutex_model model, typename Key, typename KeyCompare>
		friend class detail::specialized_acyclic_mutex;
		template<typename Mutex, typename Key, typename KeyCompare>
		friend class acyclic_mutex;

		inline mutex_grapher();

		void check_locked_mutexes_init() const
		{
			if(_locked_mutexes.get() == 0)
			{
				_locked_mutexes.reset(new mutex_list_type);
			}
		}
		mutex_list_type& internal_locked_mutexes()
		{
			check_locked_mutexes_init();
			return *_locked_mutexes;
		}
		template<typename AcyclicMutex>
		inline bool track_lock(AcyclicMutex &_mutex);
		inline void track_unlock(const acyclic_mutex_base &_mutex);
		inline void check_for_cycles() const;
		template<typename AcyclicMutex>
		inline void acquire_vertex(AcyclicMutex &mutex);
		template<typename AcyclicMutex>
		inline void release_vertex(const AcyclicMutex &mutex);

		// static functions
		static monitor_type& instance()
		{
			static monitor_type *grapher;
			boost::call_once(detail::template_static_once_flag<mutex_grapher>::flag,
				boost::bind(&mutex_grapher::init_grapher_instance, &grapher));
			return *grapher;
		}
		static void init_grapher_instance(monitor_type **grapher)
		{
			*grapher = new monitor_type(new mutex_grapher);
		}
		inline static void default_cycle_handler();

		locking_order_graph _graph;
		bool _cycle_detected;
		mutable boost::thread_specific_ptr<mutex_list_type> _locked_mutexes;
		boost::function<void ()> _cycle_handler;
		std::vector<locking_order_graph::vertex_descriptor> _free_vertices;
	};

	namespace detail
	{
		template<typename Key, typename KeyCompare>
		class vertex_finder
		{
			typedef monitor_ptr<vertex_finder, boost::mutex> monitor_type;
		public:
			typedef Key key_type;
			typedef KeyCompare key_compare;
			typedef mutex_grapher::locking_order_graph::vertex_descriptor vertex_descriptor_type;
			typedef std::map<Key, vertex_descriptor_type, KeyCompare> vertex_map_type;
			class scoped_lock: public monitor_type::scoped_lock
			{
			public:
				scoped_lock(): monitor_type::scoped_lock(vertex_finder::instance())
				{}
			};

			const vertex_descriptor_type* find_vertex(const Key &key) const
			{
				typename vertex_map_type::const_iterator it = _vertex_map.find(key);
				if(it == _vertex_map.end())
				{
					return 0;
				}else
				{
					return &it->second;
				}
			}
			void add_vertex(const Key &key, vertex_descriptor_type vertex)
			{
				_vertex_map[key] = vertex;
			}
			void remove_vertex(const Key &key)
			{
				_vertex_map.erase(key);
			}
		private:
			vertex_finder() {}

			// static functions
			static monitor_type& instance()
			{
				static monitor_type *finder;

				boost::call_once(_instance_init_flag,
					boost::bind(&vertex_finder::init_finder_instance, &finder));

				return *finder;
			}
			static void init_finder_instance(monitor_type **finder)
			{
				*finder = new monitor_type(new vertex_finder);
			}

			vertex_map_type _vertex_map;
			static boost::once_flag _instance_init_flag;
		};
		template<typename Key, typename KeyCompare>
			boost::once_flag vertex_finder<Key, KeyCompare>::_instance_init_flag;
	};
};

#endif // _POET_MUTEX_GRAPHER_DECL_HPP

ext: hpp

max_stars_repo_name: fmhess/libpoet

max_issues_repo_name: fmhess/libpoet

max_forks_repo_name: fmhess/libpoet


text:
#include <sstream>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/export.hpp>

#include "Epsilon.hxx"
BOOST_CLASS_EXPORT(Epsilon)
#include "eval.hxx"

Epsilon::Epsilon(char const i1, char const i2, char const i3, char const i4) : i1(i1), i2(i2), i3(i3), i4(i4) {}

template<class Archive>
void Epsilon::serialize (Archive & ar, unsigned int const) {
  ar & boost::serialization::base_object<Node>(*this);
  ar & i1;
  ar & i2;
  ar & i3;
  ar & i4;
}

void Epsilon::exchangeTensorIndices (std::map<char, char> const & exchange_map) {
  auto it = exchange_map.find(i1);
  if (it != exchange_map.end()) {
    i1 = it->second;
  }
  it = exchange_map.find(i2);
  if (it != exchange_map.end()) {
    i2 = it->second;
  }
  it = exchange_map.find(i3);
  if (it != exchange_map.end()) {
    i3 = it->second;
  }
  it = exchange_map.find(i4);
  if (it != exchange_map.end()) {
    i4 = it->second;
  }
}

int Epsilon::sortIndices() {
  int ret = 1;
  bool swapped = true;
  do {
    swapped = false;
    if (i1 == i2) {
      ret = 0;
    } else if (i1 > i2) {
      std::swap (i1, i2);
      swapped = true;
      ret *= -1;
    }
    if (i2 == i3) {
      ret = 0;
    } else if (i2 > i3) {
      std::swap (i2, i3);
      swapped = true;
      ret *= -1;
    }
    if (i3 == i4) {
      ret = 0;
    } else if (i3 > i4) {
      std::swap (i3, i4);
      swapped = true;
      ret *= -1;
    }
  } while (swapped);

  return ret;
}

char Epsilon::order () const { return 0; }

std::string Epsilon::print () const {
  std::stringstream ss;
  ss << "Epsilon {" << i1 << i2 << i3 << i4 << "}";

  return ss.str();
}

std::string Epsilon::printMaple () const {
  std::stringstream ss;
  ss << "LeviCivita[~" << i1 << ", ~" << i2 << ", ~" << i3 << ", ~" << i4 << "]";

  return ss.str();
}

int Epsilon::evaluate(std::map <char, char> const & eval_map) const {
  return (- 1 * epsilon_eval.at(64 * eval_map.at(i1)
                       + 16 * eval_map.at(i2)
                       +  4 * eval_map.at(i3)
                       +      eval_map.at(i4)));
}

mpq_class Epsilon::symmetrize() {
  return sortIndices();
}

bool Epsilon::containsIndex (char i) const {
  return (i == i1 || i == i2 || i == i3 || i == i4);
}

int Epsilon::applyTensorSymmetries (int parity) {
  int epsilon_parity = sortIndices();
  return (parity * epsilon_parity);
}

bool Epsilon::lessThan(Node const * other) const {
  auto other_eps = static_cast<Epsilon const *>(other);
  if (i1 < other_eps->i1) {
    return true;
  } else if (i1 > other_eps->i1) {
    return false;
  } else if (i2 < other_eps->i2) {
    return true;
  } else if (i2 > other_eps->i2) {
    return false;
  } else if (i3 < other_eps->i3) {
    return true;
  } else if (i3 > other_eps->i3) {
    return false;
  } else if (i4 < other_eps->i4) {
    return true;
  } else {
    return false;
  }
}

bool Epsilon::equals(Node const * other) const {
  if (other == nullptr) {
    return false;
  } else if (typeid(*this) != typeid(*other)) {
    return false;
  } else {
    auto other_eps = static_cast<Epsilon const *> (other);
    return (i1 == other_eps->i1 && i2 == other_eps->i2 && i3 == other_eps->i3 && i4 == other_eps->i4);
  }
}

std::unique_ptr<Node> Epsilon::clone () const {
  return std::unique_ptr<Node>(new Epsilon(*this));
}

std::tuple<int, char, std::map<char, char>> Epsilon::multiplyWithOther3 (char j1, char j2, char j3) const {
  assert (j1 != j2 && j1 != j3 && j2 != j3);
  bool const is_contained_j1 = containsIndex (j1);
  bool const is_contained_j2 = containsIndex (j2);
  bool const is_contained_j3 = containsIndex (j3);
  std::vector<char> contained;
  if (is_contained_j1) { contained.push_back (j1); }
  if (is_contained_j2) { contained.push_back (j2); }
  if (is_contained_j3) { contained.push_back (j3); }

  if (contained.size() == 0) {
    return {-24, i1, {{j1, i2}, {j2, i3}, {j3, i4}}};
  } else if (contained.size() == 3) {
    std::vector<char> i_vec {i1, i2, i3, i4};
    int parity = 1;
    if (std::find (contained.cbegin(), contained.cend(), i_vec[0]) == contained.cend()) {
      // do nothing
    } else if (std::find (contained.cbegin(), contained.cend(), i_vec[1]) == contained.cend()) {
      std::iter_swap (i_vec.begin(), i_vec.begin() + 1);
      parity *= -1;
    } else if (std::find (contained.cbegin(), contained.cend(), i_vec[2]) == contained.cend()) {
      std::iter_swap (i_vec.begin(), i_vec.begin() + 2);
      parity *= -1;
    } else {
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[3]) == contained.cend());
      std::iter_swap (i_vec.begin(), i_vec.begin() + 3);
      parity *= -1;
    }
    if ( (i_vec[1] == j1 && i_vec[2] == j2 && i_vec[3] == j3) ||
         (i_vec[2] == j1 && i_vec[3] == j2 && i_vec[1] == j3) ||
         (i_vec[3] == j1 && i_vec[1] == j2 && i_vec[2] == j3) ) {
      // sorted
    } else {
      // unsorted
      assert ( (i_vec[1] == j1 && i_vec[3] == j2 && i_vec[2] == j3) ||
               (i_vec[3] == j1 && i_vec[2] == j2 && i_vec[1] == j3) ||
               (i_vec[2] == j1 && i_vec[1] == j2 && i_vec[3] == j3));
      parity *= -1;
    }
    return {-6 * parity, i_vec[0], { }};
  } else if (contained.size() == 2) {
    std::vector<char> i_vec {i1, i2, i3, i4};
    std::vector<char> j_vec {j1, j2, j3};
    int parity = 1;
    if (!is_contained_j1) {
      // nothing
    } else if (!is_contained_j2) {
      j_vec = {j2, j1, j3};
      parity *= -1;
    } else {
      assert (!is_contained_j3);
      j_vec = {j3, j1, j2};
    }
    if (std::find (contained.cbegin(), contained.cend(), i_vec[0]) == contained.cend()) {
      if (std::find (contained.cbegin(), contained.cend(), i_vec[1]) == contained.cend()) {
        // do nothing
      } else if (std::find (contained.cbegin(), contained.cend(), i_vec[2]) == contained.cend()) {
        std::iter_swap (i_vec.begin() + 1, i_vec.begin() + 2);
        parity *= -1;
      } else {
        assert (std::find (contained.cbegin(), contained.cend(), i_vec[3]) == contained.cend());
        std::iter_swap (i_vec.begin() + 1, i_vec.begin() + 3);
        parity *= -1;
      }
    } else if (std::find (contained.cbegin(), contained.cend(), i_vec[1]) == contained.cend()) {
      std::iter_swap (i_vec.begin(), i_vec.begin() + 1);
      parity *= -1;
      if (std::find (contained.cbegin(), contained.cend(), i_vec[2]) == contained.cend()) {
        std::iter_swap (i_vec.begin() + 1, i_vec.begin() + 2);
        parity *= -1;
      } else {
        assert (std::find (contained.cbegin(), contained.cend(), i_vec[3]) == contained.cend());
        std::iter_swap (i_vec.begin() + 1, i_vec.begin() + 3);
        parity *= -1;
      }
    } else {
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[2]) == contained.cend());
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[3]) == contained.cend());
      std::iter_swap (i_vec.begin(), i_vec.begin() + 2);
      std::iter_swap (i_vec.begin() + 1, i_vec.begin() + 3);
    }
    if (i_vec[2] == contained[0]) {
      // sorted
    } else {
      assert (i_vec[2] == contained[1]);
      parity *= -1;
    }
    return { -4 * parity, i_vec[0], {{j_vec[0], i_vec[1]}} };
  } else if (contained.size() == 1) {
    std::vector<char> i_vec {i1, i2, i3, i4};
    std::vector<char> j_vec {j1, j2, j3};
    int parity = 1;
    if (!is_contained_j1) {
      if (!is_contained_j2) {
        // nothing
      } else {
        assert (!is_contained_j3);
        j_vec = {j1, j3, j2};
        parity *= -1;
      }
    } else {
      assert (!is_contained_j2);
      assert (!is_contained_j3);
      j_vec = {j2, j3, j1};
    }
    if (std::find (contained.cbegin(), contained.cend(), i_vec[0]) != contained.cend()) {
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[1]) == contained.cend());
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[2]) == contained.cend());
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[3]) == contained.cend());
      std::iter_swap (i_vec.begin(), i_vec.begin() + 1);
      std::iter_swap (i_vec.begin() + 1, i_vec.begin() + 2);
      std::iter_swap (i_vec.begin() + 2, i_vec.begin() + 3);
      parity *= -1;
    } else if (std::find (contained.cbegin(), contained.cend(), i_vec[1]) != contained.cend()) {
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[0]) == contained.cend());
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[2]) == contained.cend());
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[3]) == contained.cend());
      std::iter_swap (i_vec.begin() + 1, i_vec.begin() + 2);
      std::iter_swap (i_vec.begin() + 2, i_vec.begin() + 3);
    } else if (std::find (contained.cbegin(), contained.cend(), i_vec[2]) != contained.cend()) {
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[0]) == contained.cend());
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[1]) == contained.cend());
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[3]) == contained.cend());
      std::iter_swap (i_vec.begin() + 2, i_vec.begin() + 3);
      parity *= -1;
    } else if (std::find (contained.cbegin(), contained.cend(), i_vec[3]) != contained.cend()) {
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[0]) == contained.cend());
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[1]) == contained.cend());
      assert (std::find (contained.cbegin(), contained.cend(), i_vec[2]) == contained.cend());
      // nothing
    }
    return { -6 * parity, i_vec[0], {{j_vec[0], i_vec[1]}, {j_vec[1], i_vec[2]}} };
  } else {
    assert (false);
    return {0, 0, {}};
  }
}

max_stars_repo_name: nilsalex/tensor-trees

max_issues_repo_name: nilsalex/tensor-trees

max_forks_repo_name: nilsalex/tensor-trees


text:
/* Copyright (C) 2012-2017 IBM Corp.
 * This program is Licensed under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. See accompanying LICENSE file.
 */
#include <NTL/BasicThreadPool.h>

#include "recryption.h"
#include "EncryptedArray.h"
#include "EvalMap.h"
#include "powerful.h"
#include "CtPtrs.h"
#include "intraSlot.h"
#include "norms.h"
#include "sample.h"
#include "debugging.h"

NTL_CLIENT

#ifdef DEBUG_PRINTOUT
#include "debugging.h"
long printFlag = FLAG_PRINT_VEC;
#endif

/************************ Some local functions ***********************/
/*********************************************************************/
static void
printSizesPowerful(const vector<ZZX>& zzParts, const DoubleCRT& sKey,
                   const RecryptData& rcData, long q, double noise);

// Return in poly a polynomial with X^i encoded in all the slots
static void x2iInSlots(ZZX& poly, long i,
		       vector<ZZX>& xVec, const EncryptedArray& ea)
{
  xVec.resize(ea.size());
  ZZX x2i = ZZX(i,1);
  for (long j=0; j<(long)xVec.size(); j++) xVec[j] = x2i;
  ea.encode(poly, xVec);
}

// Make every entry of vec divisible by p2e by adding/subtracting
// multiples of p2r and q, while keeping the added multiples small.
// Specifically, for q = 1 mod p2e and any a < p2e/(2*p2r), any
// integer z can be made divisible by p2e via z' = z + u*p2r + v*q,
// with |u|*p2r <= a and |v| <= p2e/2 -a.
// Returns the largest absolute values of the u's and the new entries.

#if 1
static long makeDivisible(vec_ZZ& vec, long p2e, long p2r, long q, long a,
                          double& U_norm, const PAlgebra& palg, PowerfulDCRT* p2dConv = 0)
{
  assert(q>0 && p2e>0 && p2r>0 && a>=0
         && q % p2e == 1 && a % p2r == 0 && a*2 < p2e);
  long aa = a / p2r;

#ifdef DEBUG_PRINTOUT
  zzX uVec(INIT_SIZE, vec.length());
  zzX vVec(INIT_SIZE, vec.length());
#endif

  long maxU = 0;
  for (long i=0; i<vec.length(); i++) {
    ZZ& z = vec[i];
    long u, v;

    // What to add to z to make it divisible by p2e?
    long zMod = rem(z, p2e); // zMod is in [0,p2e-1]
    if (zMod > p2e/2) { // need to add a positive number
      zMod = p2e - zMod;
      u = zMod/p2r;
      if (u > aa) u = aa;
    }
    else {              // need to add a negative number
      u = -(zMod/p2r);
      if (u < -aa) u = -aa;
      zMod = -zMod;
    }
    v = zMod - u*p2r;
    z += u*p2r + to_ZZ(q)*v; // make z divisible by p2e

    if (rem(z,p2e) != 0) { // sanity check
      cerr << "**error: original z["<<i<<"]=" << (z-(u*p2r+to_ZZ(q)*v))
	   << std::dec << ", p^r="<<p2r << ", p^e="<<p2e << endl;
      cerr << "z' = z + "<<u<<"*p^r +"<<v<<"*q = "<<z<<endl;
      exit(1);
    }
    if (abs(u) > maxU) maxU = abs(u);
#ifdef DEBUG_PRINTOUT
    uVec[i] = u;
    vVec[i] = v;
#endif
  }

#ifdef DEBUG_PRINTOUT
  if (dbgEa) {
    const PAlgebra& palg = dbgEa->getPAlgebra();
    double V_norm;
    if (p2dConv) {
       ZZX poly;
       p2dConv->powerfulToZZX(poly, conv<Vec<ZZ>>(uVec));
       U_norm = conv<double>(embeddingLargestCoeff(poly, palg)) *p2r;
       p2dConv->powerfulToZZX(poly, conv<Vec<ZZ>>(vVec));
       V_norm = conv<double>(embeddingLargestCoeff(poly, palg));
    }
    else {
      U_norm = conv<double>(embeddingLargestCoeff(uVec, palg)) *p2r;
      V_norm = conv<double>(embeddingLargestCoeff(vVec, palg));
    }
    cerr << "  makeDivisible: maxU=" << (maxU*p2r)
         << ", U_norm=" << U_norm
         << ", V_norm=" << V_norm << endl;
  }
#endif

  return maxU;
}
#else
// experimental, randomized version
static long makeDivisible(vec_ZZ& vec, long p2e, long p2r, long q, long a,
                          double& U_norm, const PAlgebra& palg)
{
  assert(q>0 && p2e>0 && p2r>0 && a>=0
         && q % p2e == 1 && a % p2r == 0 && a*2 < p2e);
  long aa = a / p2r;

  vec_ZZ orig_vec = vec;
  double sum_norm;
  long maxU;

  zzX uVec(INIT_SIZE, vec.length());
  zzX vVec(INIT_SIZE, vec.length());

  for (long trials = 0; trials < 10; trials++) {
    vec_ZZ try_vec = orig_vec;

    long maxU1 = 0;
    for (long i=0; i<vec.length(); i++) {
      ZZ& z = try_vec[i];
      long u, v;

      // What to add to z to make it divisible by p2e?
      long zMod = rem(z, p2e); // zMod is in [0,p2e-1]
      if (zMod > p2e/2) { // need to add a positive number
	zMod = p2e - zMod;
	u = zMod/p2r;
	if (u > aa) u = aa;
        if (u > 0) {
          long ran = RandomBnd(u+1); //0..u
          u -= ran;
        }
      }
      else {              // need to add a negative number
	u = -(zMod/p2r);
	if (u < -aa) u = -aa;
        if (-u > 0) {
          long ran = RandomBnd(-u+1); //0..|u|
          u += ran;
        }
	zMod = -zMod;
      }
      v = zMod - u*p2r;
      z += u*p2r + to_ZZ(q)*v; // make z divisible by p2e

      if (rem(z,p2e) != 0) { // sanity check
	cerr << "**error: original z["<<i<<"]=" << (z-(u*p2r+to_ZZ(q)*v))
	     << std::dec << ", p^r="<<p2r << ", p^e="<<p2e << endl;
	cerr << "z' = z + "<<u<<"*p^r +"<<v<<"*q = "<<z<<endl;
	exit(1);
      }
      if (abs(u) > maxU1) maxU1 = abs(u);

      uVec[i] = u;
      vVec[i] = v;
    }

    double U_norm1 = conv<double>(embeddingLargestCoeff(uVec, palg)) *p2r;
    double V_norm1 = conv<double>(embeddingLargestCoeff(vVec, palg));
    double sum_norm1 = U_norm1 + V_norm1;
#ifdef DEBUG_PRINTOUT
      cerr << "  makeDivisible: maxU1=" << (maxU1*p2r)
	   << ", U_norm1=" << U_norm1
	   << ", V_norm1=" << V_norm1
           << ", sum=" << (U_norm1+V_norm1)
           << "\n";
#endif

    if (trials == 0 || sum_norm1 < sum_norm) {
      sum_norm = sum_norm1;
      maxU = maxU1;
      U_norm = U_norm1;
      vec = try_vec;
    }
  }

  return maxU;
}

#endif

static inline double pow(long a, long b) {return pow(double(a), double(b));}
/*********************************************************************/
/*********************************************************************/

RecryptData::~RecryptData()
{
  if (alMod!=NULL)     delete alMod;
  if (ea!=NULL)        delete ea;
  if (firstMap!=NULL)  delete firstMap;
  if (secondMap!=NULL) delete secondMap;
  if (p2dConv!=NULL)   delete p2dConv;
}

/**
 * Fix the "ring constant" cM, a target norm tau for the secret key,
 * and plaintext space mod p^r. We want to find e,e' that minimize
 * e-e', subject to the constraint
 *
 *    (1) (p^{e'}/2 + 2(p^r+1))(tau+1)*cM <= (q-1)/2  = p^e/2
 *
 * Note that as we let e,e' tend to infinity the constraint above
 * degenerates to (tau+1)*cM < p^{e-e'}, so the smallest value
 * of e-e' that we can hope for is
 *
 *    (2) e-e' = 1 + floor( log_p( (tau+1)*cM) )
 *
 * The setAE procedure tries to minimize e-e' subject to (1), and
 * in addition subject to the constraint that e is "not too big".
 * Specifically, it tries to ensure p^e<2^{30}, and failing that it
 * uses the smallest e for which 2(p^r+1)(tau+1)*cM*2 <= p^e, and the
 * largest e' for that value of e.
 *
 * Once e,e' are set, it splits p^{e'}/2=a+b with a,b about equal and
 * a divisible by p^r. Then it computes and returns the largest Hamming
 * weight for the key (that implies the norm tau') for which constraint
 * (1) still holds.
 *
 * NOTE: setAE returns the Hamming weight, *not* the norm tau'. The norm
 * can be computed from the weight using sampleHWtBoundedEffectiveBound.
 **/
long RecryptData::setAE(long& a, long& e, long& ePrime,
                    const FHEcontext& context, long targetWeight)
{
  bool default_target=false;
  if (targetWeight<=0) {
    targetWeight = RecryptData::defSkHwt;
    default_target=true;
  }

  double skSize = sampleHWtBoundedEffectiveBound(context, targetWeight);

  double magicConst = context.zMStar.get_cM();
  double factor = (skSize+1) * magicConst * 2;
  long p = context.zMStar.getP();
  long p2r = context.alMod.getPPowR();
  long frstTerm = 2*(p2r+1);
  double logp = log(p);    // log p

  // Start with the smallest e s.t. p^e > (2p^r+1)(skSize+1)*magicCons
  ePrime = 0;
  e = ceil( log(frstTerm*factor) /logp );
  assert(e*logp < log(NTL_SP_BOUND));
         // p^e for this smallest e must be single precision

  // Loop to try and find better solutions, that still satisfy
  //          (p^{e'}/2 + 2*p^r+1) * (s+1)*magicConst*2 <= p^e
  //          <=> p^{e'}/2 + 2*p^r +1 <= p^e/factor
  //          <=> p^{e'} <=  2*(p^e/factor - 2*p^r -1)
  // stop when you hit p^e > 2^30
  long eTry = e;
  long eMinusEprime = e; // want to minimize e-e'
  do {
    double p2e = pow(p,eTry);

    // Solve for the largest e' satisfying constraints (1)
    // p^{e'} <=  2*(p^e/factor - 2*p^r -1)
    long ePrimeTry = floor( log(2*(p2e/factor -frstTerm)) / logp);
    if (eTry - ePrimeTry < eMinusEprime) {
      e = eTry;
      ePrime = ePrimeTry;
      eMinusEprime = e - ePrime;
    }
  } while ((++eTry)*log(p) <= log(1L<<30));

  // Split p^{e'}/2 into a+b with a divisible by p^r
  long pToEprimeOver2 = floor(pow(p,ePrime)/2.0);
  a = 2*pToEprimeOver2 /5; // empirically we want a little below a half
  a -= (a % p2r);

  if (default_target) {
    // Try to increase t, while maintaining constraint (1)
    double bound = (frstTerm + pToEprimeOver2)*magicConst * 2;
    long p2e = pow(p,e);
    while (targetWeight++) {
      skSize = sampleHWtBoundedEffectiveBound(context, targetWeight);
      if (targetWeight>256 || bound*(skSize+1) >= p2e)
	break;
    }
    targetWeight--;
  }

#ifdef DEBUG_PRINTOUT
  long b = pToEprimeOver2 - a;
  cerr << "RecryptData::setAE(): e="<<e<<", e'="<<ePrime
       << ", a="<<a<<", b="<<b<<", sk-hwt="<<targetWeight
       << " (size="<<skSize<<"), magicConst="<<magicConst<<endl;
#endif
  return targetWeight;
}

bool RecryptData::operator==(const RecryptData& other) const
{
  if (mvec != other.mvec) return false;
  if (skHwt != other.skHwt) return false;

  return true;
}

// The main method
void RecryptData::init(const FHEcontext& context, const Vec<long>& mvec_,
                  bool enableThick, long t, bool build_cache_, bool minimal)
{
  if (alMod != NULL) { // were we called for a second time?
    cerr << "@Warning: multiple calls to RecryptData::init\n";
    return;
  }
  assert(computeProd(mvec_) == (long)context.zMStar.getM()); // sanity check

  // Record the arguments to this function
  mvec = mvec_;
  build_cache = build_cache_;

  skHwt = setAE(a, e, ePrime, context, t);
  long p = context.zMStar.getP();
  long r = context.alMod.getR();

  // First part of Bootstrapping works wrt plaintext space p^{r'}
  alMod = new PAlgebraMod(context.zMStar, e-ePrime+r);
  ea = new EncryptedArray(context, *alMod);
         // Polynomial defaults to F0, PAlgebraMod explicitly given

  p2dConv = new PowerfulDCRT(context, mvec);

  if (!enableThick) return;

  // Initialize the linear polynomial for unpacking the slots
  zz_pBak bak; bak.save(); ea->getAlMod().restoreContext();
  long nslots = ea->size();
  long d = ea->getDegree();

  const Mat<zz_p>& CBi=ea->getDerived(PA_zz_p()).getNormalBasisMatrixInverse();

  vector<ZZX> LM;
  LM.resize(d);
  for (long i = 0; i < d; i++) // prepare the linear polynomial
    LM[i] = rep(CBi[i][0]);

  vector<ZZX> C;
  ea->buildLinPolyCoeffs(C, LM); // "build" the linear polynomial

  unpackSlotEncoding.resize(d);  // encode the coefficients

  for (long j = 0; j < d; j++) {
    vector<ZZX> v(nslots);
    for (long k = 0; k < nslots; k++) v[k] = C[j];
    ea->encode(unpackSlotEncoding[j], v);
  }
  firstMap = new EvalMap(*ea, minimal, mvec, true, build_cache);
  secondMap = new EvalMap(*context.ea, minimal, mvec, false, build_cache);
}

/********************************************************************/
/********************************************************************/

// Extract digits from fully packed slots
void extractDigitsPacked(Ctxt& ctxt, long botHigh, long r, long ePrime,
			 const vector<ZZX>& unpackSlotEncoding);

// Extract digits from unpacked slots
void extractDigitsThin(Ctxt& ctxt, long botHigh, long r, long ePrime);

// bootstrap a ciphertext to reduce noise
void FHEPubKey::reCrypt(Ctxt &ctxt)
{
  FHE_TIMER_START;

  // Some sanity checks for dummy ciphertext
  long ptxtSpace = ctxt.getPtxtSpace();
  if (ctxt.isEmpty()) return;
  if (ctxt.parts.size()==1 && ctxt.parts[0].skHandle.isOne()) {
    // Dummy encryption, just ensure that it is reduced mod p
    ZZX poly = to_ZZX(ctxt.parts[0]);
    for (long i=0; i<poly.rep.length(); i++)
      poly[i] = to_ZZ( rem(poly[i],ptxtSpace) );
    poly.normalize();
    ctxt.DummyEncrypt(poly);
    return;
  }

  assert(recryptKeyID>=0); // check that we have bootstrapping data

  long p = getContext().zMStar.getP();
  long r = getContext().alMod.getR();
  long p2r = getContext().alMod.getPPowR();

  long intFactor = ctxt.intFactor;

  // the bootstrapping key is encrypted relative to plaintext space p^{e-e'+r}.
  const RecryptData& rcData = getContext().rcData;
  long e = rcData.e;
  long ePrime = rcData.ePrime;
  long p2ePrime = power_long(p,ePrime);
  long q = power_long(p,e)+1;
  assert(e>=r);

#ifdef DEBUG_PRINTOUT
  cerr << "reCrypt: p="<<p<<", r="<<r<<", e="<<e<<" ePrime="<<ePrime
       << ", q="<<q<<endl;
  CheckCtxt(ctxt, "init");
#endif

  // can only bootstrap ciphertext with plaintext-space dividing p^r
  assert(p2r % ptxtSpace == 0);

  ctxt.dropSmallAndSpecialPrimes();

#ifdef DEBUG_PRINTOUT
  CheckCtxt(ctxt, "after mod down");
#endif

  FHE_NTIMER_START(AAA_preProcess);

  // Make sure that this ciphertxt is in canonical form
  if (!ctxt.inCanonicalForm()) ctxt.reLinearize();

  // Mod-switch down if needed
  IndexSet s = ctxt.getPrimeSet() / context.specialPrimes;
  assert(s <= context.ctxtPrimes);
  if (s.card()>2) { // leave only bottom two primes
    long frst = s.first();
    long scnd = s.next(frst);
    IndexSet s2(frst,scnd);
    s.retain(s2); // retain only first two primes
  }
  ctxt.modDownToSet(s);

  // key-switch to the bootstrapping key
  ctxt.reLinearize(recryptKeyID);

  // "raw mod-switch" to the bootstrapping mosulus q=p^e+1.
  vector<ZZX> zzParts; // the mod-switched parts, in ZZX format
  double noise = ctxt.rawModSwitch(zzParts, q);

#ifdef DEBUG_PRINTOUT
  if (dbgKey) {
    cerr << "  before makeDivisible (recryption modulus q="<<q
         << "), noise_bnd=" << noise<<endl;
    printSizesPowerful(zzParts, dbgKey->sKeys[recryptKeyID],
                       ctxt.getContext().rcData, q, noise);
  }
#endif

  // Add multiples of p2r and q to make the zzParts divisible by p^{e'}
  long maxU=0;
  double maxU_norm = 0;
  for (long i=0; i<(long)zzParts.size(); i++) {
    // make divisible by p^{e'}
    double U_norm;
    long newMax = makeDivisible(zzParts[i].rep, p2ePrime, p2r, q,
				rcData.a, U_norm, context.zMStar);
    zzParts[i].normalize();   // normalize after working directly on the rep
    if (maxU < newMax)  maxU = newMax;
    if (maxU_norm < U_norm)  maxU_norm = U_norm;
  }
#ifdef DEBUG_PRINTOUT
  double newNoise = noise + maxU_norm*p2r*(skBounds[recryptKeyID]+1);
  cerr << "  after makeDivisible, maxU=" << maxU
       << ", maxU_norm="<<maxU_norm<<", p2r="<<p2r
       << ", noise_bnd="<<newNoise<<", sk_bnd="<< skBounds[recryptKeyID]
       << endl;
   if (dbgKey)
     printSizesPowerful(zzParts, dbgKey->sKeys[recryptKeyID],
                        ctxt.getContext().rcData, q, newNoise);
#endif

  for (long i=0; i<(long)zzParts.size(); i++)
    zzParts[i] /= p2ePrime;   // divide by p^{e'}

  // Multiply the post-processed cipehrtext by the encrypted sKey

  double p0size = to_double(embeddingLargestCoeff(zzParts[0], context.zMStar));
  double p1size = to_double(embeddingLargestCoeff(zzParts[1], context.zMStar));
  // FIXME: This might be slow without Armadillo

  // NOTE: here we lose the intFactor associated with ctxt.
  // We will restore it below.
  ctxt = recryptEkey;

  ctxt.multByConstant(zzParts[1]);
  ctxt.addConstant(zzParts[0]);

#ifdef DEBUG_PRINTOUT
  CheckCtxt(ctxt, "after preProcess");
#endif
  FHE_NTIMER_STOP(AAA_preProcess);

  // Move the powerful-basis coefficients to the plaintext slots
  FHE_NTIMER_START(AAA_LinearTransform1);
  ctxt.getContext().rcData.firstMap->apply(ctxt);
  FHE_NTIMER_STOP(AAA_LinearTransform1);

#ifdef DEBUG_PRINTOUT
  CheckCtxt(ctxt, "after LinearTransform1");
#endif

  // Extract the digits e-e'+r-1,...,e-e' (from fully packed slots)
  FHE_NTIMER_START(AAA_extractDigitsPacked);
  extractDigitsPacked(ctxt, e-ePrime, r, ePrime,
		      context.rcData.unpackSlotEncoding);
  FHE_NTIMER_STOP(AAA_extractDigitsPacked);

#ifdef DEBUG_PRINTOUT
  CheckCtxt(ctxt, "after extractDigitsPacked");
#endif

  // Move the slots back to powerful-basis coefficients
  FHE_NTIMER_START(AAA_LinearTransform2);
  ctxt.getContext().rcData.secondMap->apply(ctxt);
  FHE_NTIMER_STOP(AAA_LinearTransform2);

#ifdef DEBUG_PRINTOUT
  CheckCtxt(ctxt, "after linearTransform2");
#endif

  // restore intFactor
  if (intFactor != 1)
    ctxt.intFactor = MulMod(ctxt.intFactor, intFactor, ptxtSpace);
}

#ifdef FHE_BOOT_THREADS

// Extract digits from fully packed slots, multithreaded version
void extractDigitsPacked(Ctxt& ctxt, long botHigh, long r, long ePrime,
			 const vector<ZZX>& unpackSlotEncoding)
{
  FHE_TIMER_START;

  // Step 1: unpack the slots of ctxt
  FHE_NTIMER_START(unpack);
  ctxt.cleanUp();

  // Apply the d automorphisms and store them in scratch area
  long d = ctxt.getContext().zMStar.getOrdP();

  vector<Ctxt> unpacked(d, Ctxt(ZeroCtxtLike, ctxt));
  { // explicit scope to force all temporaries to be released
    vector< shared_ptr<DoubleCRT> > coeff_vector;
    coeff_vector.resize(d);

    FHE_NTIMER_START(unpack1);
    for (long i = 0; i < d; i++)
      coeff_vector[i] = shared_ptr<DoubleCRT>(new
        DoubleCRT(unpackSlotEncoding[i], ctxt.getContext(), ctxt.getPrimeSet()) );
    FHE_NTIMER_STOP(unpack1);

    FHE_NTIMER_START(unpack2);
    vector<Ctxt> frob(d, Ctxt(ZeroCtxtLike, ctxt));

    NTL_EXEC_RANGE(d, first, last)
    // FIXME: implement using hoisting!
        for (long j = first; j < last; j++) { // process jth Frobenius
          frob[j] = ctxt;
          frob[j].frobeniusAutomorph(j);
          frob[j].cleanUp();
          // FIXME: not clear if we should call cleanUp here
        }
    NTL_EXEC_RANGE_END

    FHE_NTIMER_STOP(unpack2);

    FHE_NTIMER_START(unpack3);
    Ctxt tmp1(ZeroCtxtLike, ctxt);
    for (long i = 0; i < d; i++) {
      for (long j = 0; j < d; j++) {
        tmp1 = frob[j];
        tmp1.multByConstant(*coeff_vector[mcMod(i+j, d)]);
        unpacked[i] += tmp1;
      }
    }
    FHE_NTIMER_STOP(unpack3);
  }
  FHE_NTIMER_STOP(unpack);

  //#ifdef DEBUG_PRINTOUT
  //  CheckCtxt(unpacked[0], "after unpack");
  //#endif

  NTL_EXEC_RANGE(d, first, last)
  for (long i = first; i < last; i++) {
    extractDigitsThin(unpacked[i], botHigh, r, ePrime);
  }
  NTL_EXEC_RANGE_END

  //#ifdef DEBUG_PRINTOUT
  //CheckCtxt(unpacked[0], "before repack");
  //#endif

  // Step 3: re-pack the slots
  FHE_NTIMER_START(repack);
  const EncryptedArray& ea2 = *ctxt.getContext().ea;
  ZZX xInSlots;
  vector<ZZX> xVec(ea2.size());
  ctxt = unpacked[0];
  for (long i=1; i<d; i++) {
    x2iInSlots(xInSlots, i, xVec, ea2);
    unpacked[i].multByConstant(xInSlots);
    ctxt += unpacked[i];
  }
  FHE_NTIMER_STOP(repack);
  //#ifdef DEBUG_PRINTOUT
  //CheckCtxt(ctxt, "after repack");
  //#endif
}

#else

// Extract digits from fully packed slots
void extractDigitsPacked(Ctxt& ctxt, long botHigh, long r, long ePrime,
			 const vector<ZZX>& unpackSlotEncoding)
{
  FHE_TIMER_START;

  // Step 1: unpack the slots of ctxt
  FHE_NTIMER_START(unpack);
  ctxt.cleanUp();

  // Apply the d automorphisms and store them in scratch area
  long d = ctxt.getContext().zMStar.getOrdP();

  vector<Ctxt> unpacked(d, Ctxt(ZeroCtxtLike, ctxt));
  { // explicit scope to force all temporaries to be released
    vector< shared_ptr<DoubleCRT> > coeff_vector;
    coeff_vector.resize(d);
    for (long i = 0; i < d; i++)
      coeff_vector[i] = shared_ptr<DoubleCRT>(new
        DoubleCRT(unpackSlotEncoding[i], ctxt.getContext(), ctxt.getPrimeSet()) );
    Ctxt tmp1(ZeroCtxtLike, ctxt);
    Ctxt tmp2(ZeroCtxtLike, ctxt);

    // FIXME: implement using hoisting!
    for (long j = 0; j < d; j++) { // process jth Frobenius
      tmp1 = ctxt;
      tmp1.frobeniusAutomorph(j);
      tmp1.cleanUp();
      // FIXME: not clear if we should call cleanUp here

      for (long i = 0; i < d; i++) {
        tmp2 = tmp1;
        tmp2.multByConstant(*coeff_vector[mcMod(i+j, d)]);
        unpacked[i] += tmp2;
      }
    }
  }
  FHE_NTIMER_STOP(unpack);

  //#ifdef DEBUG_PRINTOUT
  //  CheckCtxt(unpacked[0], "after unpack");
  //#endif

  for (long i=0; i<(long)unpacked.size(); i++) {
    extractDigitsThin(unpacked[i], botHigh, r, ePrime);
  }

  //#ifdef DEBUG_PRINTOUT
  //  CheckCtxt(unpacked[0], "before repack");
  //#endif

  // Step 3: re-pack the slots
  FHE_NTIMER_START(repack);
  const EncryptedArray& ea2 = *ctxt.getContext().ea;
  ZZX xInSlots;
  vector<ZZX> xVec(ea2.size());
  ctxt = unpacked[0];
  for (long i=1; i<d; i++) {
    x2iInSlots(xInSlots, i, xVec, ea2);
    unpacked[i].multByConstant(xInSlots);
    ctxt += unpacked[i];
  }
  FHE_NTIMER_STOP(repack);
}

#endif

// Use packed bootstrapping, so we can bootstrap all in just one go.
void packedRecrypt(const CtPtrs& cPtrs,
                   const std::vector<zzX>& unpackConsts,
                   const EncryptedArray& ea)
{
  FHEPubKey& pKey = (FHEPubKey&)cPtrs[0]->getPubKey();

  // Allocate temporary ciphertexts for the recryption
  int nPacked = divc(cPtrs.size(), ea.getDegree()); // ceil(totoalNum/d)
  std::vector<Ctxt> cts(nPacked, Ctxt(pKey));

  repack(CtPtrs_vectorCt(cts), cPtrs, ea);  // pack ciphertexts
  //  cout << "@"<< lsize(cts)<<std::flush;
  for (Ctxt& c: cts) {     // then recrypt them
    c.reducePtxtSpace(2);  // we only have recryption data for binary ctxt
    pKey.reCrypt(c);
  }
  unpack(cPtrs, CtPtrs_vectorCt(cts), ea, unpackConsts);
}

// recrypt all ctxt at level < belowLvl
void packedRecrypt(const CtPtrs& array,
                   const std::vector<zzX>& unpackConsts,
                   const EncryptedArray& ea, long belowLvl)
{
  std::vector<Ctxt*> v;
  for (long i=0; i<array.size(); i++)
    if ( array.isSet(i) && !array[i]->isEmpty()
         && array[i]->bitCapacity()<belowLvl*(array[i]->getContext().BPL()) )
      v.push_back(array[i]);
  packedRecrypt(CtPtrs_vectorPt(v), unpackConsts, ea);
}
void packedRecrypt(const CtPtrMat& m,
                   const std::vector<zzX>& unpackConsts,
                   const EncryptedArray& ea, long belowLvl)
{
  std::vector<Ctxt*> v;
  for (long i=0; i<m.size(); i++)
    for (long j=0; j<m[i].size(); j++)
      if ( m[i].isSet(j) && !m[i][j]->isEmpty()
           && m[i][j]->bitCapacity()<belowLvl*(m[i][j]->getContext().BPL()) )
        v.push_back(m[i][j]);
  packedRecrypt(CtPtrs_vectorPt(v), unpackConsts, ea);
}

//===================== Thin Bootstrapping stuff ==================

ThinRecryptData::~ThinRecryptData()
{
  if (coeffToSlot!=NULL)  delete coeffToSlot;
  if (slotToCoeff!=NULL) delete slotToCoeff;
}

// This code was copied from RecryptData::init, and is mostly
// the same, except for the linear-map-related stuff.
// FIXME: There is really too much code (and data!) duplication here.
void ThinRecryptData::init(const FHEcontext& context, const Vec<long>& mvec_,
                      bool alsoThick, long t, bool build_cache_, bool minimal)
{
  RecryptData::init(context, mvec_, alsoThick, t, build_cache_, minimal);
  coeffToSlot = new ThinEvalMap(*ea, minimal, mvec, true, build_cache);
  slotToCoeff = new ThinEvalMap(*context.ea, minimal, mvec, false, build_cache);
}

// Extract digits from thinly packed slots

long fhe_force_chen_han = 0;

void extractDigitsThin(Ctxt& ctxt, long botHigh, long r, long ePrime)
{
  FHE_TIMER_START;

  Ctxt unpacked(ctxt);
  unpacked.cleanUp();

  vector<Ctxt> scratch;

  long p = ctxt.getContext().zMStar.getP();
  long p2r = power_long(p,r);
  long topHigh = botHigh + r-1;

  // degree Chen/Han technique is p^{bot-1}(p-1)r
  // degree of basic technique is p^{bot-1}p^r,
  //     or p^{bot-1}p^{r-1} if p==2, r > 1, and bot+r > 2

  bool use_chen_han = false;
  if (r > 1) {
    double chen_han_cost = log(p-1) + log(r);
    double basic_cost;
    if (p == 2 && botHigh + r > 2)
       basic_cost = (r-1)*log(p);
    else
       basic_cost = r*log(p);

    //cerr << "*** basic: " << basic_cost << "\n";
    //cerr << "*** chen/han: " << chen_han_cost << "\n";

    double thresh = 1.5;
    if (p == 2) thresh = 1.75;
    // increasing thresh makes chen_han less likely to be chosen.
    // For p == 2, the basic algorithm is just squaring,
    // and so is a bit cheaper, so we raise thresh a bit.
    // This is all a bit heuristic.

    if (basic_cost > thresh*chen_han_cost)
      use_chen_han = true;
  }

  if (fhe_force_chen_han > 0)
    use_chen_han = true;
  else if (fhe_force_chen_han < 0)
    use_chen_han = false;

  if (use_chen_han) {
    // use Chen and Han technique

    extendExtractDigits(scratch, unpacked, botHigh, r);

#if 0
    for (long i: range(scratch.size())) {
      CheckCtxt(scratch[i], "**");
    }
#endif

    for (long j = 0; j < botHigh; j++) {
      unpacked -= scratch[j];
      unpacked.divideByP();
    }

    if (p==2 && botHigh>0)   // For p==2, subtract also the previous bit
      unpacked += scratch[botHigh-1];
    unpacked.negate();

    if (r>ePrime) {          // Add in digits from the bottom part, if any
      long topLow = r-1 - ePrime;
      Ctxt tmp = scratch[topLow];
      for (long j=topLow-1; j>=0; --j) {
	tmp.multByP();
	tmp += scratch[j];
      }
      if (ePrime>0)
	tmp.multByP(ePrime); // multiply by p^e'
      unpacked += tmp;
    }
    unpacked.reducePtxtSpace(p2r); // Our plaintext space is now mod p^r

    ctxt = unpacked;
  }
  else {

    if (p==2 && r>1 && topHigh+1 > 2)
      topHigh--; // For p==2 we sometime get a bit for free

    extractDigits(scratch, unpacked, topHigh+1);

    // set upacked = -\sum_{j=botHigh}^{topHigh} scratch[j] * p^{j-botHigh}
    if (topHigh >= LONG(scratch.size())) {
      topHigh = scratch.size() -1;
      cerr << " @ suspect: not enough digits in extractDigitsPacked\n";
    }

    unpacked = scratch[topHigh];
    for (long j=topHigh-1; j>=botHigh; --j) {
      unpacked.multByP();
      unpacked += scratch[j];
    }
    if (p==2 && botHigh>0)   // For p==2, subtract also the previous bit
      unpacked += scratch[botHigh-1];
    unpacked.negate();

    if (r>ePrime) {          // Add in digits from the bottom part, if any
      long topLow = r-1 - ePrime;
      Ctxt tmp = scratch[topLow];
      for (long j=topLow-1; j>=0; --j) {
	tmp.multByP();
	tmp += scratch[j];
      }
      if (ePrime>0)
	tmp.multByP(ePrime); // multiply by p^e'
      unpacked += tmp;
    }
    unpacked.reducePtxtSpace(p2r); // Our plaintext space is now mod p^r
    ctxt = unpacked;
  }

}

// Hack to get at private fields of public key
struct FHEPubKeyHack { // The public key
  const FHEcontext& context; // The context

  //! @var Ctxt pubEncrKey
  //! The public encryption key is an encryption of 0,
  //! relative to the first secret key
  Ctxt pubEncrKey;

  std::vector<long> skHwts; // The Hamming weight of the secret keys
  std::vector<KeySwitch> keySwitching; // The key-switching matrices

  // The keySwitchMap structure contains pointers to key-switching matrices
  // for re-linearizing automorphisms. The entry keySwitchMap[i][n] contains
  // the index j such that keySwitching[j] is the first matrix one needs to
  // use when re-linearizing s_i(X^n).
  std::vector< std::vector<long> > keySwitchMap;

  NTL::Vec<int> KS_strategy; // NTL Vec's support I/O, which is
                             // more convenient

  // bootstrapping data

  long recryptKeyID; // index of the bootstrapping key
  Ctxt recryptEkey;  // the key itself, encrypted under key #0

};

// bootstrap a ciphertext to reduce noise
void FHEPubKey::thinReCrypt(Ctxt &ctxt)
{
  FHE_TIMER_START;

  // Some sanity checks for dummy ciphertext
  long ptxtSpace = ctxt.getPtxtSpace();
  if (ctxt.isEmpty()) return;

  if (ctxt.parts.size()==1 && ctxt.parts[0].skHandle.isOne()) {
    // Dummy encryption, just ensure that it is reduced mod p
    ZZX poly = to_ZZX(ctxt.parts[0]);
    for (long i=0; i<poly.rep.length(); i++)
      poly[i] = to_ZZ( rem(poly[i],ptxtSpace) );
    poly.normalize();
    ctxt.DummyEncrypt(poly);
    return;
  }

  assert(recryptKeyID>=0); // check that we have bootstrapping data

  long p = ctxt.getContext().zMStar.getP();
  long r = ctxt.getContext().alMod.getR();
  long p2r = ctxt.getContext().alMod.getPPowR();

  long intFactor = ctxt.intFactor;

  const ThinRecryptData& trcData = ctxt.getContext().rcData;

  // the bootstrapping key is encrypted relative to plaintext space p^{e-e'+r}.
  long e = trcData.e;
  long ePrime = trcData.ePrime;
  long p2ePrime = power_long(p,ePrime);
  long q = power_long(p,e)+1;
  assert(e>=r);

  // can only bootstrap ciphertext with plaintext-space dividing p^r
  assert(p2r % ptxtSpace == 0);

#ifdef DEBUG_PRINTOUT
  CheckCtxt(ctxt, "init");
#endif

  ctxt.dropSmallAndSpecialPrimes();

#ifdef DROP_BEFORE_THIN_RECRYPT
  // experimental code...we should drop down to a reasonably low level
  // before doing the first linear map.
  long first = context.ctxtPrimes.first();
  long last = min(context.ctxtPrimes.last(),
                  first + thinRecrypt_initial_level - 1);
  ctxt.bringToSet(IndexSet(first, last));
#endif

#ifdef DEBUG_PRINTOUT
  CheckCtxt(ctxt, "after mod down");
#endif

  // Move the slots to powerful-basis coefficients
  FHE_NTIMER_START(AAA_slotToCoeff);
  trcData.slotToCoeff->apply(ctxt);
  FHE_NTIMER_STOP(AAA_slotToCoeff);

#ifdef DEBUG_PRINTOUT
  CheckCtxt(ctxt, "after slotToCoeff");
#endif

  FHE_NTIMER_START(AAA_bootKeySwitch);

  // Make sure that this ciphertxt is in canonical form
  if (!ctxt.inCanonicalForm()) ctxt.reLinearize();

  // Mod-switch down if needed
  IndexSet s = ctxt.getPrimeSet() / context.specialPrimes;
  assert(s <= context.ctxtPrimes);
  if (s.card()>2) { // leave only bottom two primes
    long frst = s.first();
    long scnd = s.next(frst);
    IndexSet s2(frst,scnd);
    s.retain(s2); // retain only first two primes
  }
  ctxt.modDownToSet(s);

  // key-switch to the bootstrapping key
  ctxt.reLinearize(recryptKeyID);

  // "raw mod-switch" to the bootstrapping mosulus q=p^e+1.
  vector<ZZX> zzParts; // the mod-switched parts, in ZZX format
  double noise = ctxt.rawModSwitch(zzParts, q);
  assert(zzParts.size() == 2);

#ifdef DEBUG_PRINTOUT
  if (dbgKey) {
    cerr << "  before makeDivisible (recryption modulus q="<<q
         << "), noise_bnd=" << noise<<endl;
    printSizesPowerful(zzParts, dbgKey->sKeys[recryptKeyID],
                       ctxt.getContext().rcData, q, noise);
  }
#endif

#if 1

  // Add multiples of p2r and q to make the zzParts divisible by p^{e'}
  double maxU_norm = 0;
  long maxU=0;
  for (long i=0; i<(long)zzParts.size(); i++) {
    // make divisible by p^{e'}
    double U_norm;

    long newMax = makeDivisible(zzParts[i].rep, p2ePrime, p2r, q,
				trcData.a, U_norm, context.zMStar);

    zzParts[i].normalize(); // normalize after working directly on the rep
    if (maxU < newMax)  maxU = newMax;
    if (maxU_norm < U_norm)  maxU_norm = U_norm;
  }
#ifdef DEBUG_PRINTOUT
  double newNoise = noise + maxU_norm*(skBounds[recryptKeyID]+1);
  cerr << "  after makeDivisible, maxU=" << maxU
       << ", maxU_norm="<<maxU_norm<<", p2r="<<p2r
       << ", noise_bnd="<<newNoise<<", sk_bnd="<< skBounds[recryptKeyID]
       << endl;
   if (dbgKey)
     printSizesPowerful(zzParts, dbgKey->sKeys[recryptKeyID],
                        ctxt.getContext().rcData, q, newNoise);
#endif

  for (long i=0; i<(long)zzParts.size(); i++)
    zzParts[i] /= p2ePrime;   // divide by p^{e'}

#else
  // Experimental version
  // Add multiples of p2r and q to make the zzParts divisible by p^{e'}
  double maxU_norm = 0;
  long maxU=0;
  for (long i=0; i<(long)zzParts.size(); i++) {
    // make divisible by p^{e'}
    double U_norm;

    Vec<ZZ> pwrfl;
    trcData.p2dConv->ZZXtoPowerful(pwrfl, zzParts[i]);

    long newMax = makeDivisible(pwrfl, p2ePrime, p2r, q,
				trcData.a, U_norm, context.zMStar);

    trcData.p2dConv->powerfulToZZX(zzParts[i], pwrfl);

    zzParts[i].normalize(); // normalize after working directly on the rep
    if (maxU < newMax)  maxU = newMax;
    if (maxU_norm < U_norm)  maxU_norm = U_norm;
  }
#ifdef DEBUG_PRINTOUT
  double newNoise = noise + maxU_norm*(skBounds[recryptKeyID]+1);
  cerr << "  after makeDivisible, maxU=" << maxU
       << ", maxU_norm="<<maxU_norm<<", p2r="<<p2r
       << ", noise_bnd="<<newNoise<<", sk_bnd="<< skBounds[recryptKeyID]
       << endl;
   if (dbgKey)
     printSizesPowerful(zzParts, dbgKey->sKeys[recryptKeyID],
                        ctxt.getContext().rcData, q, newNoise);
#endif

  for (long i=0; i<(long)zzParts.size(); i++)
    zzParts[i] /= p2ePrime;   // divide by p^{e'}

#endif

  // Multiply the post-processed cipehrtext by the encrypted sKey

  double p0size = to_double(embeddingLargestCoeff(zzParts[0], context.zMStar));
  double p1size = to_double(embeddingLargestCoeff(zzParts[1], context.zMStar));
  // FIXME: This might be slow without Armadillo

  // NOTE: here we lose the intFactor associated with ctxt.
  // We will restore it below.
  ctxt = recryptEkey;

  ctxt.multByConstant(zzParts[1], p1size);
  ctxt.addConstant(zzParts[0], p0size);

#ifdef DEBUG_PRINTOUT
   CheckCtxt(ctxt, "after bootKeySwitch");
#endif

  FHE_NTIMER_STOP(AAA_bootKeySwitch);

  // Move the powerful-basis coefficients to the plaintext slots
  FHE_NTIMER_START(AAA_coeffToSlot);
  trcData.coeffToSlot->apply(ctxt);
  FHE_NTIMER_STOP(AAA_coeffToSlot);

#ifdef DEBUG_PRINTOUT
   CheckCtxt(ctxt, "after coeffToSlot");
#endif

  // Extract the digits e-e'+r-1,...,e-e' (from fully packed slots)
  FHE_NTIMER_START(AAA_extractDigitsThin);
  extractDigitsThin(ctxt, e-ePrime, r, ePrime);
  FHE_NTIMER_STOP(AAA_extractDigitsThin);

#ifdef DEBUG_PRINTOUT
   CheckCtxt(ctxt, "after extractDigitsThin");
#endif

  // restore intFactor
  if (intFactor != 1)
    ctxt.intFactor = MulMod(ctxt.intFactor, intFactor, ptxtSpace);
}

static void
printSizesPowerful(const vector<ZZX>& zzParts, const DoubleCRT& sKey,
                   const RecryptData& rcData, long q, double noise)
{
  ZZX ptxt;
  //  const RecryptData& rcData = ctxt.getContext().rcData;
  const PAlgebra& palg = rcData.ea->getPAlgebra();
  rawDecrypt(ptxt, zzParts, sKey); // no mod q

  Vec<ZZ> powerful;
  rcData.p2dConv->ZZXtoPowerful(powerful, ptxt);
  xdouble max_pwrfl = conv<xdouble>(largestCoeff(powerful));
  xdouble max_canon = embeddingLargestCoeff(ptxt, palg);
  double ratio = log(max_pwrfl/max_canon)/log(2.0);
  xdouble critical_value = (max_pwrfl/q)/q;
  cerr << "                     max_pwrfl/q^2=" << ((max_pwrfl/q)/q)
       << ", log2(max_pwrfl/max_canon)=" << ratio;
  if (ratio > 0) cerr << " BAD-BOUND";
  cerr << endl;

  vecRed(powerful, powerful, q, false);
  max_pwrfl = conv<xdouble>(largestCoeff(powerful));
  rcData.p2dConv->powerfulToZZX(ptxt, powerful);
  max_canon = embeddingLargestCoeff(ptxt, palg);
  ratio = log(max_pwrfl/max_canon)/log(2.0);
  critical_value += max_pwrfl/q;
  cerr << "        after mod q, max_pwrfl/q=" << (max_pwrfl/q);
  if (critical_value > 0.5) cerr << " BAD-BOUND";
  cerr << ", log2(max_pwrfl/max_canon)=" << ratio;
  if (ratio > 0) cerr << " BAD-BOUND";

  ratio = log(max_canon/noise)/log(2.0);
  cerr << "\n        log2(max_canon/noiseEst)=" << ratio;
  if (ratio > 0) cerr << " BAD-BOUND";

  ratio = log(noise/q)/log(2.0);
  cerr << ", log2(noiseEst/q)=" << ratio;

  cerr << endl;
}

max_stars_repo_name: usafchn/DiPSI

max_issues_repo_name: usafchn/DiPSI

max_forks_repo_name: usafchn/DiPSI


text:
//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#ifndef BOOST_SIMD_FUNCTION_BITS_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_BITS_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

 /*!

    @ingroup group-bitwise
    Function object implementing bits capabilities

    Returns an unsigned integer value which has the same bits as the input

    @par semantic:
    For any given value @c x of type @c T:

    @code
    T r = bits(x);
    @endcode

    is equivalent to:

    @code
    as_integer_t<T,unsigned> r = bitwise_cast< as_integer_t<T,unsigned>>(x);
    @endcode

  **/
  as_integer_t<T, unsigned> bits(Value const & v0);
} }
#endif

#include <boost/simd/function/scalar/bits.hpp>
#include <boost/simd/function/simd/bits.hpp>

#endif

max_stars_repo_name: xmar/pythran

max_issues_repo_name: xmar/pythran

max_forks_repo_name: xmar/pythran


text:
/*
 * Copyright (C) 2011-2019 Daniel Scharrer
 *
 * This software is provided 'as-is', without any express or implied
 * warranty.  In no event will the author(s) be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 */

/*!
 * \file
 *
 * Filters to be used with boost::iostreams for undoing transformations Inno Setup applies
 * to stored executable files to make them more compressible.
 */
#ifndef INNOEXTRACT_STREAM_EXEFILTER_HPP
#define INNOEXTRACT_STREAM_EXEFILTER_HPP

#include <stddef.h>
#include <iosfwd>
#include <cassert>

#include <boost/cstdint.hpp>
#include <boost/iostreams/char_traits.hpp>
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/get.hpp>
#include <boost/iostreams/read.hpp>

namespace stream {

/*!
 * Filter to decode executable files stored by Inno Setup versions before 5.2.0.
 *
 * Essentially, it tries to change the addresses stored for x86 CALL and JMP instructions
 * to be relative to the instruction's position.
 */
class inno_exe_decoder_4108 : public boost::iostreams::multichar_input_filter {

private:

	typedef boost::iostreams::multichar_input_filter base_type;

public:

	typedef base_type::char_type char_type;
	typedef base_type::category category;

	inno_exe_decoder_4108() { close(0); }

	template <typename Source>
	std::streamsize read(Source & src, char * dest, std::streamsize n);

	template <typename Source>
	void close(const Source & /* source */) {
		addr = 0, addr_bytes_left = 0, addr_offset = 5;
	}

private:

	boost::uint32_t addr;
	size_t addr_bytes_left;
	boost::uint32_t addr_offset;

};

/*!
 * Filter to decode executable files stored by Inno Setup versions after 5.2.0.
 *
 * It tries to change the addresses stored for x86 CALL and JMP instructions to be
 * relative to the instruction's position, plus a few other tweaks.
 */
class inno_exe_decoder_5200 : public boost::iostreams::multichar_input_filter {

private:

	typedef boost::iostreams::multichar_input_filter base_type;

public:

	typedef base_type::char_type char_type;
	typedef base_type::category category;

	/*!
	 * \param flip_high_bytes true if the high byte of addresses is flipped if bit 23 is set.
	 *                        This optimization is used in Inno Setup 5.3.9 and later.
	 */
	explicit inno_exe_decoder_5200(bool flip_high_bytes)
		: flip_high_byte(flip_high_bytes) { close(0); }

	template <typename Source>
	std::streamsize read(Source & src, char * dest, std::streamsize n);

	template <typename Source>
	void close(const Source & /* source */) {
		offset = 0, flush_bytes = 0;
	}

private:

	/*
	 * call_instruction_decoder_5200 has three states:
	 *
	 * "initial" (flush_bytes == 0)
	 *  - Read individual bytes and write them directly to output.
	 *  - If the byte could be the start of a CALL or JMP instruction that doesn't span blocks,
	 *    set addr_bytes_left to -4.
	 *
	 * "address" (flush_bytes < 0 && flush_bytes >= -4)
	 *  - Read all four address bytes into buffer, incrementing flush_bytes for each byte read.
	 *  - Once the last byte has been read, transform the address and set flush_bytes to 4.
	 *  - If an EOF is encountered before all four bytes have been read, set flush_bytes to
	 *    4 + flush_bytes.
	 *
	 * "flush" (flush_bytes > 0 && flush_bytes <= 4)
	 *  - Write the first flush_bytes bytes of buffer to output.
	 *  - If there is not enough output space, write as much as possible and move to rest to
	 *    the start of buffer.
	 */

	static const size_t block_size = 0x10000;
	const bool flip_high_byte;

	boost::uint32_t offset; //! Total number of bytes read from the source.

	boost::int8_t flush_bytes;
	boost::uint8_t buffer[4];

};

// Implementation:

template <typename Source>
std::streamsize inno_exe_decoder_4108::read(Source & src, char * dest, std::streamsize n) {

	for(std::streamsize i = 0; i < n; i++, addr_offset++) {

		int byte = boost::iostreams::get(src);
		if(byte == EOF) { return i ? i : EOF; }
		if(byte == boost::iostreams::WOULD_BLOCK) { return i; }

		if(addr_bytes_left == 0) {

			// Check if this is a CALL or JMP instruction.
			if(byte == 0xe8 || byte == 0xe9) {
				addr = ~addr_offset + 1;
				addr_bytes_left = 4;
			}

		} else {
			addr += boost::uint8_t(byte);
			byte = boost::uint8_t(addr);
			addr >>= 8;
			addr_bytes_left--;
		}

		*dest++ = char(boost::uint8_t(byte));
	}

	return n;
}

template <typename Source>
std::streamsize inno_exe_decoder_5200::read(Source & src, char * dest, std::streamsize n) {

	char * end = dest + n;

	//! Total number of filtered bytes read and written to dest.
#define total_read     (n - (end - dest))

#define flush(N) \
	{ \
		if((N) > 0) { \
			flush_bytes = (N); \
			size_t buffer_i = 0; \
			do { \
				if(dest == end) { \
					memmove(buffer, buffer + buffer_i, size_t(flush_bytes)); \
					return total_read; \
				} \
				*dest++ = char(buffer[buffer_i++]); \
			} while(--flush_bytes); \
		} \
	} (void)0

	// Flush already processed address bytes.
	flush(flush_bytes);

	while(dest != end) {

		if(!flush_bytes) {

			// Check if this is a CALL or JMP instruction.
			int byte = boost::iostreams::get(src);
			if(byte == EOF) { return total_read ? total_read : EOF; }
			if(byte == boost::iostreams::WOULD_BLOCK) { return total_read; }
			*dest++ = char(byte);
			offset++;
			if(byte != 0xe8 && byte != 0xe9) {
				// Not a CALL or JMP instruction.
				continue;
			}

			const size_t block_size_left = block_size - ((offset - 1) % block_size);
			if(block_size_left < 5) {
				// Ignore instructions that span blocks.
				continue;
			}

			flush_bytes = -4;
		}

		assert(flush_bytes < 0);

		// Read all four address bytes.
		char * dst = reinterpret_cast<char *>(buffer + 4 + flush_bytes);
		std::streamsize nread = boost::iostreams::read(src, dst, -flush_bytes);
		if(nread == EOF) {
			flush(boost::int8_t(4 + flush_bytes));
			return total_read ? total_read : EOF;
		}
		flush_bytes = boost::int8_t(flush_bytes + nread), offset += boost::uint32_t(nread);
		if(flush_bytes) { return total_read; }

		// Verify that the high byte of the address is 0x00 or 00xff.
		if(buffer[3] == 0x00 || buffer[3] == 0xff) {

			boost::uint32_t addr = offset & 0xffffff; // may wrap, but OK

			boost::uint32_t rel = buffer[0] | (boost::uint32_t(buffer[1]) << 8)
			                                | (boost::uint32_t(buffer[2]) << 16);
			rel -= addr;
			buffer[0] = boost::uint8_t(rel);
			buffer[1] = boost::uint8_t(rel >> 8);
			buffer[2] = boost::uint8_t(rel >> 16);

			if(flip_high_byte) {
				// For a slightly higher compression ratio, we want the resulting high
				// byte to be 0x00 for both forward and backward jumps. The high byte
				// of the original relative address is likely to be the sign extension
				// of bit 23, so if bit 23 is set, toggle all bits in the high byte.
				if(rel & 0x800000) {
					buffer[3] = boost::uint8_t(~buffer[3]);
				}
			}

		} else {
			// This is most likely not a CALL or JUMP.
		}

		flush(4);
	}

	return total_read;

#undef flush
#undef total_read

}

} // namespace stream

#endif // INNOEXTRACT_STREAM_EXEFILTER_HPP