text:
#include <boost/test/unit_test.hpp>

#include <graphene/app/application.hpp>
#include <graphene/chain/database.hpp>
#include <graphene/chain/exceptions.hpp>
#include <graphene/chain/hardfork.hpp>
#include <graphene/chain/is_authorized_asset.hpp>

#include <graphene/chain/account_object.hpp>
#include <graphene/chain/asset_object.hpp>
#include <graphene/chain/tree.hpp>
#include <graphene/chain/fund_object.hpp>

#include <fc/crypto/digest.hpp>

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

#include <iostream>
#include <string>

using namespace graphene::chain;
using namespace graphene::chain::test;

BOOST_FIXTURE_TEST_SUITE( referral_tests, database_fixture )

BOOST_AUTO_TEST_CASE( tree_create_test )
{
   /*
    *       nathan
    *
    *   /           \
    *
    * ref11       ref12
    *
    *   |           |
    *
    * ref21       ref22
    *
    * |    \      |     \
    *
    * ref31 ref32 ref33  ref34
    */

   try {

      BOOST_TEST_MESSAGE( "=== referral_tests ===" );
      BOOST_TEST_MESSAGE( "=== tree_create_test ===" );

      std::vector<account_test_in> test_accounts = {
            account_test_in("nathan", "committee-account", leaf_info()),
            account_test_in("ref11", "nathan", leaf_info()),
            account_test_in("ref12", "nathan", leaf_info()),
            account_test_in("ref21", "ref11", leaf_info()),
            account_test_in("ref22", "ref12", leaf_info()),
            account_test_in("ref31", "ref21", leaf_info()),
            account_test_in("ref32", "ref21", leaf_info()),
            account_test_in("ref33", "ref22", leaf_info()),
            account_test_in("ref34", "ref22", leaf_info()),

      };

      CREATE_ACCOUNTS(test_accounts);

      auto& acc_idx = db.get_index_type<account_index>();
      auto& bal_idx = db.get_index_type<account_balance_index>();
      referral_tree result(acc_idx, bal_idx, asset_id_type());
      result.form_old();

      std::for_each( test_accounts.begin(), test_accounts.end(), [&](account_test_in acc_parent_pair) {
         BOOST_ASSERT(result.referral_map.count(accounts_map[acc_parent_pair.name].id) > 0);
         BOOST_CHECK(result.referral_map[accounts_map[acc_parent_pair.name].id].node->parent->data.account_id
               == accounts_map[acc_parent_pair.parent].id);
      });

      accounts_map.clear();

   } catch(fc::exception& e) {
      edump((e.to_detail_string()))
      throw;
   }
}

BOOST_AUTO_TEST_CASE( balances_test )
{

   /*
    * nathan (100.000)
    *
    *   |         \
    *
    * ref11(100.000) ref12 (100.000)
    *
    *   |                  |
    *
    * ref21(50.000)            ref22 (50.000)
    *  |       \           |         \
    * ref31(25.000) ref32(25.000)  ref33 (25.000) ref34(25.000)
    */

   try {

      BOOST_TEST_MESSAGE( "=== balances_test ===" );

      std::vector<account_test_in> test_accounts = {
            account_test_in("nathan", "committee-account", leaf_info(account_id_type(), 100000,2,200000,0,2,400000,0)),
            account_test_in("ref11", "nathan", leaf_info(account_id_type(), 100000,0,50000,0,0,100000,0)),
            account_test_in("ref12", "nathan", leaf_info(account_id_type(), 100000,0,50000,0,0,100000,0)),
            account_test_in("ref21", "ref11", leaf_info(account_id_type(), 50000,0,50000,0,0,50000,0)),
            account_test_in("ref22", "ref12", leaf_info(account_id_type(), 50000,0,50000,0,0,50000,0)),
            account_test_in("ref31", "ref21", leaf_info(account_id_type(), 25000,0,0,0,0,0,0)),
            account_test_in("ref32", "ref21", leaf_info(account_id_type(), 25000,0,0,0,0,0,0)),
            account_test_in("ref33", "ref22", leaf_info(account_id_type(), 25000,0,0,0,0,0,0)),
            account_test_in("ref34", "ref22", leaf_info(account_id_type(), 25000,0,0,0,0,0,0))
            };

      CREATE_ACCOUNTS(test_accounts);

      auto& acc_idx = db.get_index_type<account_index>();
      auto& bal_idx = db.get_index_type<account_balance_index>();

      referral_tree result(acc_idx, bal_idx, asset_id_type());
      result.form_old();

      std::for_each( test_accounts.begin(), test_accounts.end(), [&](account_test_in& acc_parent_pair) {
         BOOST_CHECK(result.referral_map[accounts_map[acc_parent_pair.name].id].node->data == acc_parent_pair.exp_data);
      });

      accounts_map.clear();

   } catch(fc::exception& e) {
      edump((e.to_detail_string()))
      throw;
   }
}

BOOST_AUTO_TEST_CASE( level_a_test )
{

   /*
    *    nathan (100)
    *
    *   |            \
    *
    * ref1(100) ... ref4 (100) 1{ref5(50)} 2{ref5(100)}
    */

   try {

      BOOST_TEST_MESSAGE( "=== level_a_test ===" );

      std::vector<account_test_in> test_accounts = {
            account_test_in("nathan", "committee-account", leaf_info(account_id_type(), 100000,4,400000,0,4,400000,0)),
            account_test_in("nathan1", "committee-account", leaf_info(account_id_type(), 200000,4,450000,0,4,450000,0)),
            account_test_in("nathan3", "committee-account", leaf_info(account_id_type(), 200000,5,500000,0,5,500000,0.25)),
            account_test_in("nathan4", "committee-account", leaf_info(account_id_type(), 100000,5,500000,0,5,500000,0))
      };

      std::vector<account_children_in> test_accounts_children = {
            account_children_in("nathan", 1, 4, 100000, "partner"),
            account_children_in("nathan1", 1, 4, 100000, "partner1"),
            account_children_in("nathan1", 1, 1, 50000, "member1"),
            account_children_in("nathan3", 1, 5, 100000, "partner3"),
            account_children_in("nathan4", 1, 5, 100000, "partner4"),
      };

      CREATE_ACCOUNTS(test_accounts);

      APPEND_CHILDREN(test_accounts_children);

      auto& acc_idx = db.get_index_type<account_index>();
      auto& bal_idx = db.get_index_type<account_balance_index>();

      referral_tree result(acc_idx, bal_idx, asset_id_type());
      result.form_old();

      std::for_each( test_accounts.begin(), test_accounts.end(), [&](account_test_in& acc_parent_pair) {
         BOOST_CHECK(result.referral_map[accounts_map[acc_parent_pair.name].id].node->data == acc_parent_pair.exp_data);
      });

      std::list<referral_info> ops_info_expected = {
            referral_info(accounts_map["nathan3"].id, 125000 * 0.0065, ""),
      };
      std::list<referral_info> ops_info = result.scan_old();

      BOOST_CHECK(ops_info_expected.size() <= ops_info.size());

      std::for_each( ops_info_expected.begin(), ops_info_expected.end(), [&](referral_info& op_in) {
         auto it = std::find_if(ops_info.begin(), ops_info.end(), referral_info_finder(op_in));
         BOOST_CHECK(it != ops_info.end());
      });

      accounts_map.clear();

   } catch(fc::exception& e) {
      edump((e.to_detail_string()))
      throw;
   }
}

BOOST_AUTO_TEST_CASE( level_b_test )
{

   try {

      BOOST_TEST_MESSAGE( "=== level_b_test ===" );

      std::vector<account_test_in> test_accounts = {
            account_test_in("nathan", "committee-account", leaf_info(account_id_type(), 500000,4,400000,25,29,2900000,0)),
            account_test_in("nathan1", "committee-account", leaf_info(account_id_type(), 500000,5,500000,24,29,2900000,0.25)),
            account_test_in("nathan2", "committee-account", leaf_info(account_id_type(), 100000,5,500000,25,30,3000000,0)),
            account_test_in("nathan3", "committee-account", leaf_info(account_id_type(), 500000,4,400000,24,28,2800000,0)),
            account_test_in("nathan4", "committee-account", leaf_info(account_id_type(), 500000,5,500000,24,29,2950000,0.25)),
            account_test_in("nathan5", "committee-account", leaf_info(account_id_type(), 500000,4,450000,25,29,2950000,0)),
            account_test_in("nathan6", "committee-account", leaf_info(account_id_type(), 500000,4,450000,24,28,2900000,0)),
            account_test_in("nathan7", "committee-account", leaf_info(account_id_type(), 500000,5,500000,25,30,3000000,0.2))
      };

      std::vector<account_children_in> test_accounts_children = {
            account_children_in("nathan", 1, 4, 100000, "partner"),
            account_children_in("nathan", 2, 25, 100000, "partner"),

            account_children_in("nathan1", 1, 5, 100000, "partner1"),
            account_children_in("nathan1", 2, 24, 100000, "partner1"),

            account_children_in("nathan2", 1, 5, 100000, "partner2"),
            account_children_in("nathan2", 2, 25, 100000, "partner2"),

            account_children_in("nathan3", 1, 4, 100000, "partner3"),
            account_children_in("nathan3", 2, 24, 100000, "partner3"),

            account_children_in("nathan4", 1, 5, 100000, "partner4"),
            account_children_in("nathan4", 2, 24, 100000, "partner4"),
            account_children_in("nathan4", 2, 1, 50000, "member4"),

            account_children_in("nathan5", 1, 4, 100000, "partner5"),
            account_children_in("nathan5", 1, 1, 50000, "member5"),
            account_children_in("nathan5", 2, 25, 100000, "partner5"),

            account_children_in("nathan6", 1, 4, 100000, "partner6"),
            account_children_in("nathan6", 1, 1, 50000, "member6"),
            account_children_in("nathan6", 2, 24, 100000, "partner6"),
            account_children_in("nathan6", 2, 1, 50000, "member6"),

            account_children_in("nathan7", 1, 5, 100000, "partner7"),
            account_children_in("nathan7", 2, 25, 100000, "partner7"),
      };

      CREATE_ACCOUNTS(test_accounts);

      APPEND_CHILDREN(test_accounts_children);

      auto& acc_idx = db.get_index_type<account_index>();
      auto& bal_idx = db.get_index_type<account_balance_index>();

      referral_tree result(acc_idx, bal_idx, asset_id_type());
      result.form_old();

      std::for_each( test_accounts.begin(), test_accounts.end(), [&](account_test_in& acc_parent_pair) {
         BOOST_CHECK(result.referral_map[accounts_map[acc_parent_pair.name].id].node->data == acc_parent_pair.exp_data);
      });

      std::list<referral_info> ops_info_expected = {
            referral_info(accounts_map["nathan1"].id, 125000 * 0.0065, ""),
            referral_info(accounts_map["nathan4"].id, 125000 * 0.0065, ""),
            referral_info(accounts_map["nathan7"].id, 600000 * 0.0065, ""),
      };
      std::list<referral_info> ops_info = result.scan_old();

      BOOST_CHECK(ops_info_expected.size() <= ops_info.size());

      std::for_each( ops_info_expected.begin(), ops_info_expected.end(), [&](referral_info& op_in) {
         auto it = std::find_if(ops_info.begin(), ops_info.end(), referral_info_finder(op_in));
         BOOST_CHECK(it != ops_info.end());
      });

      accounts_map.clear();

   } catch(fc::exception& e) {
      edump((e.to_detail_string()))
      throw;
   }
}

BOOST_AUTO_TEST_CASE( level_c_test )
{

   try {

      BOOST_TEST_MESSAGE( "=== level_c_test ===" );

      std::vector<account_test_in> test_accounts = {
            account_test_in("nathan", "committee-account", leaf_info(account_id_type(), 1000000,5,500000,25,125,12500000,0.15)),
            account_test_in("nathan1", "committee-account", leaf_info(account_id_type(), 900000,5,500000,25,125,12500000,0)),
            account_test_in("nathan2", "committee-account", leaf_info(account_id_type(), 1000000,4,400000,25,124,12400000,0)),
            account_test_in("nathan3", "committee-account", leaf_info(account_id_type(), 900000,5,500000,25,124,12400000,0.20)),
            account_test_in("nathan4", "committee-account", leaf_info(account_id_type(), 1000000,5,500000,25,124,12450000,0.20)),
      };

      std::vector<account_children_in> test_accounts_children = {
            account_children_in("nathan", 1, 5, 100000, "partner"),
            account_children_in("nathan", 2, 25, 100000, "partner"),
            account_children_in("nathan", 3, 95, 100000, "partner"),

            account_children_in("nathan1", 1, 5, 100000, "partner1"),
            account_children_in("nathan1", 2, 25, 100000, "partner1"),
            account_children_in("nathan1", 3, 95, 100000, "partner1"),

            account_children_in("nathan2", 1, 4, 100000, "partner2"),
            account_children_in("nathan2", 2, 25, 100000, "partner2"),
            account_children_in("nathan2", 3, 95, 100000, "partner2"),

            account_children_in("nathan3", 1, 5, 100000, "partner3"),
            account_children_in("nathan3", 2, 25, 100000, "partner3"),
            account_children_in("nathan3", 3, 94, 100000, "partner3"),

            account_children_in("nathan4", 1, 5, 100000, "partner4"),
            account_children_in("nathan4", 2, 25, 100000, "partner4"),
            account_children_in("nathan4", 3, 94, 100000, "partner4"),
            account_children_in("nathan4", 3, 1, 50000, "member4"),
      };

      CREATE_ACCOUNTS(test_accounts);

      APPEND_CHILDREN(test_accounts_children);

      auto& acc_idx = db.get_index_type<account_index>();
      auto& bal_idx = db.get_index_type<account_balance_index>();

      referral_tree result(acc_idx, bal_idx, asset_id_type());
      result.form_old();

      std::for_each( test_accounts.begin(), test_accounts.end(), [&](account_test_in& acc_parent_pair) {
         BOOST_CHECK(result.referral_map[accounts_map[acc_parent_pair.name].id].node->data == acc_parent_pair.exp_data);
      });

      std::list<referral_info> ops_info_expected = {
            referral_info(accounts_map["nathan"].id, 1875000 * 0.0065, ""),
            referral_info(accounts_map["nathan3"].id, 2480000 * 0.0065, ""),
            referral_info(accounts_map["nathan4"].id, 2490000 * 0.0065, ""),
      };
      std::list<referral_info> ops_info = result.scan_old();

      BOOST_CHECK(ops_info_expected.size() <= ops_info.size());

      std::for_each( ops_info_expected.begin(), ops_info_expected.end(), [&](referral_info& op_in) {
         auto it = std::find_if(ops_info.begin(), ops_info.end(), referral_info_finder(op_in));
         BOOST_CHECK(it != ops_info.end());
      });

      accounts_map.clear();

   } catch(fc::exception& e) {
      edump((e.to_detail_string()))
      throw;
   }
}

BOOST_AUTO_TEST_CASE( level_d_test )
{

   try {

      BOOST_TEST_MESSAGE( "=== level_d_test ===" );

      std::vector<account_test_in> test_accounts = {
            account_test_in("nathan", "committee-account", leaf_info(account_id_type(), 2000000,5,500000,25,625,62500000,0.10)),
            account_test_in("nathan1", "committee-account", leaf_info(account_id_type(), 1000000,5,500000,25,625,62500000,0)),
      };

      std::vector<account_children_in> test_accounts_children = {
            account_children_in("nathan", 1, 5, 100000, "partner"),
            account_children_in("nathan", 2, 25, 100000, "partner"),
            account_children_in("nathan", 3, 595, 100000, "partner"),

            account_children_in("nathan1", 1, 5, 100000, "partner1"),
            account_children_in("nathan1", 2, 25, 100000, "partner1"),
            account_children_in("nathan1", 3, 1, 0, "partner1"),
            account_children_in("nathan1", 4, 595, 100000, "partner1"),
      };

      CREATE_ACCOUNTS(test_accounts);

      APPEND_CHILDREN(test_accounts_children);

      auto& acc_idx = db.get_index_type<account_index>();
      auto& bal_idx = db.get_index_type<account_balance_index>();

      referral_tree result(acc_idx, bal_idx, asset_id_type());
      result.form_old();

      std::for_each( test_accounts.begin(), test_accounts.end(), [&](account_test_in& acc_parent_pair) {
         BOOST_CHECK(result.referral_map[accounts_map[acc_parent_pair.name].id].node->data == acc_parent_pair.exp_data);
      });

      std::list<referral_info> ops_info_expected = {
         referral_info(accounts_map["nathan"].id, 6250000 * 0.0065, ""),
      };
      std::list<referral_info> ops_info = result.scan_old();

      BOOST_CHECK(ops_info_expected.size() <= ops_info.size());

      std::for_each( ops_info_expected.begin(), ops_info_expected.end(), [&](referral_info& op_in) {
         auto it = std::find_if(ops_info.begin(), ops_info.end(), referral_info_finder(op_in));
         BOOST_CHECK(it != ops_info.end());
      });

      accounts_map.clear();
   }
   catch(fc::exception& e)
   {
      edump((e.to_detail_string()))
      throw;
   }
}

BOOST_AUTO_TEST_CASE( level_e_test )
{

   try {

      BOOST_TEST_MESSAGE( "=== level_e_test ===" );

      std::vector<account_test_in> test_accounts = {
            account_test_in("nathan", "committee-account", leaf_info(account_id_type(), 3000000,5,500000,25,3125,312500000,0.05)),
            account_test_in("nathan1", "committee-account", leaf_info(account_id_type(), 2000000,5,500000,25,3125,312500000,0)),
      };

      std::vector<account_children_in> test_accounts_children = {
            account_children_in("nathan", 1, 5, 100000, "partner"),
            account_children_in("nathan", 2, 25, 100000, "partner"),
            account_children_in("nathan", 3, 2000, 100000, "partner"),
            account_children_in("nathan", 4, 1000, 100000, "partner"),
            account_children_in("nathan", 5, 1, 0, "partner"),
            account_children_in("nathan", 6, 90, 100000, "partner"),
            account_children_in("nathan", 7, 5, 100000, "partner"),

            account_children_in("nathan1", 1, 5, 100000, "partner1"),
            account_children_in("nathan1", 2, 25, 100000, "partner1"),
            account_children_in("nathan1", 3, 3095, 100000, "partner1"),
      };

      CREATE_ACCOUNTS(test_accounts);

      APPEND_CHILDREN(test_accounts_children);

      auto& acc_idx = db.get_index_type<account_index>();
      auto& bal_idx = db.get_index_type<account_balance_index>();

      referral_tree result(acc_idx, bal_idx, asset_id_type());
      result.form_old();

      std::for_each( test_accounts.begin(), test_accounts.end(), [&](account_test_in& acc_parent_pair) {
         BOOST_CHECK(result.referral_map[accounts_map[acc_parent_pair.name].id].node->data == acc_parent_pair.exp_data);
      });

      std::list<referral_info> ops_info_expected = {
            referral_info(accounts_map["nathan"].id, 15625000 * 0.0065, ""),
      };
      std::list<referral_info> ops_info = result.scan_old();

      BOOST_CHECK(ops_info_expected.size() <= ops_info.size());

      std::for_each( ops_info_expected.begin(), ops_info_expected.end(), [&](referral_info& op_in) {
         auto it = std::find_if(ops_info.begin(), ops_info.end(), referral_info_finder(op_in));
         BOOST_CHECK(it != ops_info.end());
      });

      accounts_map.clear();

   } catch(fc::exception& e) {
      edump((e.to_detail_string()))
      throw;
   }
}

BOOST_AUTO_TEST_CASE( referrals_test )
{
   try
   {
      BOOST_TEST_MESSAGE( "=== referrals_test ===" );

      ACTOR(abcde1) // for needed IDs
      ACTOR(alice) // level 3 (id 1.2.17)

      ACTOR(test1) //-- id 1.2.18
      ACTOR(test2) // level 2
      ACTOR(test3) //--

      ACTOR(test4)  //-- id 1.2.21
      ACTOR(test5)  //
      ACTOR(test6)  //
      ACTOR(test7)  //
      ACTOR(test8)  // level 1
      ACTOR(test9)  //
      ACTOR(test10) //
      ACTOR(test11) //
      ACTOR(test12) //--

      ACTOR(test13) // id 1.2.30
      ACTOR(test14)
      ACTOR(test15)
      ACTOR(test16)
      ACTOR(test17)
      ACTOR(test18)
      ACTOR(test19)
      ACTOR(test20)
      ACTOR(test21)
      ACTOR(test22)
      ACTOR(test23)
      ACTOR(test24)
      ACTOR(test25)
      ACTOR(test26)
      ACTOR(test27)
      ACTOR(test28)
      ACTOR(test29)
      ACTOR(test30)
      ACTOR(test31)
      ACTOR(test32)
      ACTOR(test33)
      ACTOR(test34)
      ACTOR(test35)
      ACTOR(test36)
      ACTOR(test37)
      ACTOR(test38)
      ACTOR(test39)

      ACTOR(bob) // id 1.2.57

      ACTOR(test40)
      ACTOR(test41)
      ACTOR(test42)

      /**
       * initial tree
       *                                                                                alice
       *                                     /                                                                |                                                                   \
       *                                test1                                                               test2                                                                 test3
       *              /                   |                   \                          /                    |                    \                           /                    |                    \
       *          test4                 test5                 test6                  test7                  test8                  test9                  test10                  test11                 test12
       *     /     |     \          /     |     \         /     |      \         /     |      \         /     |      \         /     |      \         /     |      \         /      |      \        /      |      \
       * test13 test14 test15   test16 test17 test18   test19 test20 test21   test22 test23 test24   test25 test26 test27   test28 test29 test30   test31 test32 test33   test34 test35 test36   test37 test38 test39
       *
       */

      /********************** apply new referrers ********************/

      // line 1
      {
         CHANGE_REFERRER_MULTIPLE(("test1")("test2")("test3"), "alice")
         BOOST_CHECK(get_account_by_name("test3").referrer == get_account_by_name("alice").get_id());
      }

      // line 2
      {
         CHANGE_REFERRER_MULTIPLE(("test4")("test5")("test6"), "test1")
         BOOST_CHECK(get_account_by_name("test6").referrer == get_account_by_name("test1").get_id());

         CHANGE_REFERRER_MULTIPLE(("test7")("test8")("test9"), "test2")
         BOOST_CHECK(get_account_by_name("test9").referrer == get_account_by_name("test2").get_id());

         CHANGE_REFERRER_MULTIPLE(("test10")("test11")("test12"), "test3")
         BOOST_CHECK(get_account_by_name("test12").referrer == get_account_by_name("test3").get_id());
      }

      // line 3
      {
         CHANGE_REFERRER_MULTIPLE(("test13")("test14")("test15"), "test4")
         BOOST_CHECK(get_account_by_name("test15").referrer == get_account_by_name("test4").get_id());

         CHANGE_REFERRER_MULTIPLE(("test16")("test17")("test18"), "test5")
         BOOST_CHECK(get_account_by_name("test18").referrer == get_account_by_name("test5").get_id());

         CHANGE_REFERRER_MULTIPLE(("test19")("test20")("test21"), "test6")
         BOOST_CHECK(get_account_by_name("test21").referrer == get_account_by_name("test6").get_id());

         CHANGE_REFERRER_MULTIPLE(("test22")("test23")("test24"), "test7")
         BOOST_CHECK(get_account_by_name("test24").referrer == get_account_by_name("test7").get_id());

         CHANGE_REFERRER_MULTIPLE(("test25")("test26")("test27"), "test8")
         BOOST_CHECK(get_account_by_name("test27").referrer == get_account_by_name("test8").get_id());

         CHANGE_REFERRER_MULTIPLE(("test28")("test29")("test30"), "test9")
         BOOST_CHECK(get_account_by_name("test30").referrer == get_account_by_name("test9").get_id());

         CHANGE_REFERRER_MULTIPLE(("test31")("test32")("test33"), "test10")
         BOOST_CHECK(get_account_by_name("test33").referrer == get_account_by_name("test10").get_id());

         CHANGE_REFERRER_MULTIPLE(("test34")("test35")("test36"), "test11")
         BOOST_CHECK(get_account_by_name("test36").referrer == get_account_by_name("test11").get_id());

         CHANGE_REFERRER_MULTIPLE(("test37")("test38")("test39"), "test12")
         BOOST_CHECK(get_account_by_name("test39").referrer == get_account_by_name("test12").get_id());
      }

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

      // assign privileges for creating_asset_operation
      SET_ACTOR_CAN_CREATE_ASSET(alice_id)

      create_edc(10000000000, asset(100, CORE_ASSET), asset(1, EDC_ASSET));

      issue_uia(alice_id, asset(100000, EDC_ASSET));
      issue_uia(bob_id, asset(100000, EDC_ASSET));
      ISSUES_BY_NAMES(test, 1, 43, asset(100000, EDC_ASSET))
      BOOST_CHECK(get_balance(test42_id, EDC_ASSET) == 100000);

      fc::time_point_sec h_time = HARDFORK_638_TIME;
      generate_blocks(h_time);

      // create fund
      fund_options::fund_rate fr;
      fr.amount = 10000;
      fr.day_percent = 1000;
      fund_options::payment_rate pr;
      pr.period = 50;
      pr.percent = 20000;

      fund_options options;
      options.description = "FUND DESCRIPTION";
      options.period = 100;
      options.min_deposit = 10000;
      options.rates_reduction_per_month = 300;
      options.fund_rates.push_back(std::move(fr));
      options.payment_rates.push_back(std::move(pr));
      make_fund("TESTFUND", options, alice_id);

      auto fund_iter = db.get_index_type<fund_index>().indices().get<by_name>().find("TESTFUND");
      BOOST_CHECK(fund_iter != db.get_index_type<fund_index>().indices().get<by_name>().end());
      const fund_object& fund = *fund_iter;

      // update referral settings
      {
         update_referral_settings_operation op;
         op.fee = asset();
         op.referral_payments_enabled = true;
         op.referral_level1_percent = 5000;
         op.referral_level2_percent = 4000;
         op.referral_level3_percent = 3000;
         op.referral_min_limit_edc_level1 = std::make_pair(10000, 10000);
         op.referral_min_limit_edc_level2 = std::make_pair(10000, 10000);
         op.referral_min_limit_edc_level3 = std::make_pair(10000, 10000);
         set_expiration(db, trx);
         trx.operations.push_back(std::move(op));
         PUSH_TX(db, trx, ~0);
         trx.clear();
      }

      /*********** referral payments 1 ***********/

      // alice
      {
         fund_deposit_operation op;
         op.amount = 10000;
         op.fee = asset();
         op.from_account = alice_id;
         op.period = 50;
         op.fund_id = fund.id;
         set_expiration(db, trx);
         trx.operations.push_back(std::move(op));
         PUSH_TX(db, trx, ~0);
         trx.clear();
      }
      // bob
      {
         fund_deposit_operation op;
         op.amount = 10000;
         op.fee = asset();
         op.from_account = bob_id;
         op.period = 50;
         op.fund_id = fund.id;
         set_expiration(db, trx);
         trx.operations.push_back(std::move(op));
         PUSH_TX(db, trx, ~0);
         trx.clear();
      }

      MAKE_DEPOSIT_PAYMENTS_BY_NAMES(test, 1, 43, 10000)

      BOOST_CHECK(get_account_by_name("alice").edc_in_deposits == 10000);
      BOOST_CHECK(get_account_by_name("test42").edc_in_deposits_daily == 10000);

      h_time = db.head_block_time() + fc::days(1);
      while (db.head_block_time() < h_time) {
         generate_block();
      }

      BOOST_CHECK(get_account_by_name("test3").referral_level == 2);
      BOOST_CHECK(get_account_by_name("alice").referral_level == 3);

      //std::cout << "test3.edc_in_deposits_daily: " << get_account_by_name("test3").edc_in_deposits_daily.value << std::endl;
      //std::cout << "test12 balance: " << get_balance(test12_id, EDC_ASSET) << std::endl;
      //std::cout << "test3 balance: " << get_balance(test3_id, EDC_ASSET) << std::endl;
      //std::cout << "test39 referrer: " << get_account_by_id(get_account_by_id(test39_id).referrer).name << std::endl;

      /**
       * 90000(old balance) + 40(fund_payment) + 1500(referral payment)
       * 1500 = 10000(daily_deposits of each referral) * 0.05(referral_level1_percent) * 3(referrals count of test12);
       */
      BOOST_CHECK(get_balance(test12_id, EDC_ASSET) == 91540);

      /**
       * 90000(old balance) + 40(fund_payment) + 5100(referral payment)
       *
       * 5100:
       * 500 * 3 (level 1 users: test10, test11, test12; leaf_info2::level1_payment field)
       * 400 * 9 (level 0 users: test31, test32, test33, test34, test35, test36, test37, test38, test39; leaf_info2::level2_payment field)
       */
      BOOST_CHECK(get_balance(test3_id, EDC_ASSET) == 95140);

      // change referral of test39
      CHANGE_REFERRER_MULTIPLE(("test39"), "abcde1")

      h_time = db.head_block_time() + fc::days(1);
      while (db.head_block_time() < h_time) {
         generate_block();
      }

      BOOST_CHECK(get_account_by_name("test3").referral_level == 1);
      BOOST_CHECK(get_account_by_name("alice").referral_level == 2);

      CHANGE_REFERRER_MULTIPLE(("test39"), "test12")

      h_time = db.head_block_time() + fc::days(1);
      while (db.head_block_time() < h_time) {
         generate_block();
      }

      BOOST_CHECK(get_account_by_name("test12").referral_level == 1);

      // bob
      CHANGE_REFERRER_MULTIPLE(("test37")("test38")("test39"), "bob")

      h_time = db.head_block_time() + fc::days(1);
      while (db.head_block_time() < h_time) {
         generate_block();
      }

      BOOST_CHECK(get_account_by_id(get_account_by_name("test37").referrer).name == "bob");
      BOOST_CHECK(get_account_by_id(get_account_by_name("test38").referrer).name == "bob");
      BOOST_CHECK(get_account_by_id(get_account_by_name("test39").referrer).name == "bob");

      BOOST_CHECK(get_account_by_name("bob").referral_level == 1);

      // test12
      CHANGE_REFERRER_MULTIPLE(("test37")("test38")("test39"), "test12")

      h_time = db.head_block_time() + fc::days(1);
      while (db.head_block_time() < h_time) {
         generate_block();
      }

      BOOST_CHECK(get_account_by_name("test12").referral_level == 1);
      BOOST_CHECK(get_account_by_name("test3").referral_level == 2);

      // add 0-level users to 2-level user (its level shouldn't be changed)
      CHANGE_REFERRER_MULTIPLE(("test40")("test41")("test42"), "test3")

      h_time = db.head_block_time() + fc::days(1);
      while (db.head_block_time() < h_time) {
         generate_block();
      }

      BOOST_CHECK(get_account_by_name("test3").referral_level == 2);
   }
   catch(fc::exception& e)
   {
      edump((e.to_detail_string()))
      throw;
   }
}

BOOST_AUTO_TEST_CASE( referrals_hf639_test )
{
   try
   {
      BOOST_TEST_MESSAGE( "=== referrals_hf639_test ===" );

      /**
       *                            alice*
       *             /               |                 \
       *          test1*            test2*            test3*
       *      /     |     \
       *  test4*  test5*  test6*
       *
       *  accounts with '*' have been made deposits
       */

      ACTOR(abcde1) // for needed IDs
      ACTOR(alice)
      ACTOR(test1)
      ACTOR(test2)
      ACTOR(test3)
      ACTOR(test4)
      ACTOR(test5)
      ACTOR(test6)

      ACTOR(test7)
      ACTOR(test8)
      ACTOR(test9)
      ACTOR(test10)
      ACTOR(test11)
      ACTOR(test12)

      // line 1
      {
         CHANGE_REFERRER_MULTIPLE(("test1")("test2")("test3"), "alice")
         CHANGE_REFERRER_MULTIPLE(("test1"), "alice")
         BOOST_CHECK(get_account_by_name("test3").referrer == get_account_by_name("alice").get_id());
      }
      // line 2
      {
         CHANGE_REFERRER_MULTIPLE(("test4")("test5")("test6"), "test1")
         BOOST_CHECK(get_account_by_name("test6").referrer == get_account_by_name("test1").get_id());
      }

      // assign privileges for creating_asset_operation
      SET_ACTOR_CAN_CREATE_ASSET(alice_id)

      create_edc(10000000000, asset(100, CORE_ASSET), asset(1, EDC_ASSET));

      issue_uia(alice_id, asset(100000, EDC_ASSET));
      ISSUES_BY_NAMES(test, 1, 13, asset(100000, EDC_ASSET))
      BOOST_CHECK(get_balance(test12_id, EDC_ASSET) == 100000);

      fc::time_point_sec h_time = HARDFORK_640_TIME;
      generate_blocks(h_time);

      // create fund
      fund_options::fund_rate fr;
      fr.amount = 10000;
      fr.day_percent = 1000;
      fund_options::payment_rate pr;
      pr.period = 50;
      pr.percent = 20000;

      fund_options options;
      options.period = 100;
      options.min_deposit = 10000;
      options.rates_reduction_per_month = 300;
      options.fund_rates.push_back(std::move(fr));
      options.payment_rates.push_back(std::move(pr));
      make_fund("TESTFUND", options, alice_id);

      auto fund_iter = db.get_index_type<fund_index>().indices().get<by_name>().find("TESTFUND");
      BOOST_CHECK(fund_iter != db.get_index_type<fund_index>().indices().get<by_name>().end());
      const fund_object& fund = *fund_iter;

      // update referral settings
      {
         update_referral_settings_operation op;
         op.fee = asset();
         op.referral_payments_enabled = true;
         op.referral_level1_percent = 5000;
         op.referral_level2_percent = 4000;
         op.referral_level3_percent = 3000;
         op.referral_min_limit_edc_level1 = std::make_pair(10000, 10000);
         op.referral_min_limit_edc_level2 = std::make_pair(10000, 10000);
         op.referral_min_limit_edc_level3 = std::make_pair(10000, 10000);
         set_expiration(db, trx);
         trx.operations.push_back(std::move(op));
         PUSH_TX(db, trx, ~0);
         trx.clear();
      }

      /********** referral payments **********/

      // alice
      {
         fund_deposit_operation op;
         op.amount = 10000;
         op.fee = asset();
         op.from_account = alice_id;
         op.period = 50;
         op.fund_id = fund.id;
         set_expiration(db, trx);
         trx.operations.push_back(std::move(op));
         PUSH_TX(db, trx, ~0);
         trx.clear();
      }

      MAKE_DEPOSIT_PAYMENTS_BY_NAMES(test, 1, 7, 10000)

      BOOST_CHECK(get_account_by_name("alice").edc_in_deposits == 10000);
      BOOST_CHECK(get_account_by_name("test6").edc_in_deposits_daily == 10000);

      h_time = db.head_block_time() + fc::days(1);
      while (db.head_block_time() < h_time) {
         generate_block();
      }

      BOOST_CHECK(get_account_by_name("alice").referral_level == 1);
      BOOST_CHECK(get_account_by_name("test1").referral_level == 1);

      /**
       * 90000(old balance) + 40(fund_payment) + 1500(referral payments of level 1)
       * 1500 = 10000(daily_deposits of each referral: test4, test5, test6) * 0.05(referral_level1_percent) * 3(referrals count of test1);
       */
      BOOST_CHECK(get_balance(test1_id, EDC_ASSET) == 91540);

      /**
       * 90000(old balance) + 40(fund_payment) + 427(fund owner payment, residual scheme) + 1500(referral payments of level 1)
       * 1500 = 10000(daily_deposits of each referral: test1 + test2 + test3) * 0.05(referral_level1_percent) * 3(referrals count of alice);
       */
      BOOST_CHECK(get_balance(alice_id, EDC_ASSET) == 91967);

      // additional referral payments for alice and test3
      /**
       *                              alice
       *            /                   |                    \
       *          test1               test2                 test3
       *     /     |     \       /      |     \        /      |       \
       *  test4  test5  test6  test7  test8  test9  test10*  test11*  test12*
       */
      {
         CHANGE_REFERRER_MULTIPLE(("test7")("test8")("test9"), "test2")
      }
      {
         CHANGE_REFERRER_MULTIPLE(("test10")("test11")("test12"), "test3")
      }
      MAKE_DEPOSIT_PAYMENTS_BY_NAMES(test, 7, 13, 10000)

      h_time = db.head_block_time() + fc::days(1);
      while (db.head_block_time() < h_time) {
         generate_block();
      }

      /**
       * 90040(old balance) + 40(fund_payment) + 1500(referral payments of level 1)
       * 1500 = 10000(daily_deposits of each referral: test10 - test12) * 0.05(referral_level1_percent) * 3(referrals count of test3)
       */
      BOOST_CHECK(get_balance(test3_id, EDC_ASSET) == 91580);

      /**
       * 91967(old balance) + 40(fund_payment) + 780(fund owner payment, residual scheme) + 2400(referral payments of level 2)
       * 2400 = 10000(daily_deposits of each referral: test7 - test12) * 0.04(referral_level2_percent) * 6(referrals count of test2 and test3);
       */
      BOOST_CHECK(get_balance(alice_id, EDC_ASSET) == 95187);
   }
   catch(fc::exception& e)
   {
      edump((e.to_detail_string()))
      throw;
   }
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: EDCBlockchain/blockchain


text:
#ifndef PYTHONIC_BUILTIN_STR_MOD_HPP
#define PYTHONIC_BUILTIN_STR_MOD_HPP

#include "pythonic/__builtin__/str/__mod__.hpp"

#include "pythonic/types/str.hpp"
#include "pythonic/utils/functor.hpp"

#include <boost/format.hpp>

PYTHONIC_NS_BEGIN

namespace __builtin__
{

  namespace str
  {

    namespace details
    {
      template <class Tuple>
      void fmt(boost::format &f, Tuple const &a, utils::int_<1>)
      {
        f % std::get<std::tuple_size<Tuple>::value - 1>(a);
      }
      template <class Tuple, size_t I>
      void fmt(boost::format &f, Tuple const &a, utils::int_<I>)
      {
        fmt(f % std::get<std::tuple_size<Tuple>::value - I>(a), a,
            utils::int_<I - 1>());
      }
    }

    template <class T>
    types::str __mod__(types::str const &s, T const &arg)
    {
      const boost::format fmter(s.get_data());
      return (boost::format(fmter) % arg).str();
    }

    template <class... Ts>
    types::str __mod__(types::str const &s, std::tuple<Ts...> const &args)
    {
      boost::format fmter(s.get_data());
      details::fmt(fmter, args, utils::int_<sizeof...(Ts)>());
      return fmter.str();
    }
    template <size_t N, class T>
    types::str __mod__(types::str const &s, types::array<T, N> const &args)
    {
      boost::format fmter(s.get_data());
      details::fmt(fmter, args, utils::int_<N>());
      return fmter.str();
    }
  }
}
PYTHONIC_NS_END
#endif

ext: hpp


text:
//          Copyright Ben Pope 2017.
// 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)

#define BOOST_TEST_MODULE pusher-test
#define BOOST_TEST_NO_MAIN
#define BOOST_TEST_ALTERNATIVE_INIT_API

#include "test.hpp"

#include <boost/program_options.hpp>
#include <date/tz.h>
#include <pusher++/event.hpp>
#include <iostream>

namespace
{
    boost::program_options::variables_map options;
}

std::string app_id() { return options["app_id"].as<std::string>(); }
std::string key() { return options["key"].as<std::string>(); }
std::string secret() { return options["secret"].as<std::string>(); }
std::string cluster() { return options["cluster"].as<std::string>(); }

std::ostream& operator<<(std::ostream& os, pusher::event const& ev)
{
    return os
        <<  date::make_zoned(date::current_zone(), std::chrono::system_clock::now()) << ": "
        << "{\"channel\":\"" << ev.channel
        << "\",\"event\":\"" << ev.name
        << "\",\"data\":\"" << ev.data << "\"}\n";
}

namespace
{
    boost::program_options::variables_map parse_options(int argc, char* argv[])
    {
        namespace po = boost::program_options;
        po::options_description desc{"Options"};
        desc.add_options()
            ("app_id", po::value<std::string>(), "Application ID")
            ("key", po::value<std::string>(), "Key")
            ("secret", po::value<std::string>(), "Secret")
            ("cluster", po::value<std::string>()->default_value("eu"), "Cluster: [ap1|ap2|eu|us2]mt1]")
        ;
        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).options(desc).allow_unregistered().run(), vm);
        po::notify(vm);

        if(!vm.count("app_id") || !vm.count("key") || !vm.count("secret"))
        {
            std::cout << desc << '\n';
            std::exit(1);
        }

        return vm;
    }
}

int main(int argc, char* argv[])
{
    options = parse_options(argc - 1, argv + 1);
    return boost::unit_test::unit_test_main(init_unit_test, argc, argv);
}

ext: cpp

max_stars_repo_name: thereisnosun/pusher-cpp

max_issues_repo_name: thereisnosun/pusher-cpp

max_forks_repo_name: thereisnosun/pusher-cpp


text:
#pragma once

#include <memory>
#include <boost/asio.hpp>
#include "eventReceiver.hpp"
#include "serviceParamTypes.h"

namespace mln::net {

	struct NetCommonObjects
	{
	public:
		NetCommonObjects(ServiceParams& svcParams)
			: _ioc(svcParams.ioc_)
			, _strand(svcParams.ioc_)
			, _keepAliveTimeMs(svcParams.keepAliveTimeMs_)
			, _updateTimeMs(svcParams.serviceUpdateTimeMs_)
			, _updater(svcParams.ioc_, boost::posix_time::milliseconds(svcParams.serviceUpdateTimeMs_))
		{
			_packetProc = std::make_unique<PacketProcedure>(svcParams.packetParser_, svcParams.manip_);

			svcParams.receiver_.clone(&_eventReceiver);
			_eventReceiver.initHandler(_packetProc.get());
		}

		boost::asio::io_context& _ioc;
		boost::asio::io_context::strand _strand;
		std::unique_ptr<PacketProcedure> _packetProc;
		EventReceiver _eventReceiver;
		size_t _keepAliveTimeMs = 0;
		size_t _updateTimeMs = 0;

		size_t _index;

	private:
		boost::chrono::system_clock::time_point		_prevTime;
		boost::asio::deadline_timer					_updater;

	public:
		size_t getIndex() const { return _index; }
		void setIndex(const size_t idx) { _index = idx; }

		void expireTimerReady() {
			if (0 != _updateTimeMs) {
				_prevTime = boost::chrono::system_clock::now();
				_updater.expires_from_now(boost::posix_time::milliseconds(_updateTimeMs));

				_updater.async_wait(boost::asio::bind_executor(_strand, boost::bind(
					&NetCommonObjects::handleUpdate, this, boost::asio::placeholders::error)));
			}
		}

		void handleUpdate(const boost::system::error_code& ec) {
			if (ec) {
				[[unlikely]]
				LOGW("failed handleUpdate. code:{}, msg:{}", ec.value(), ec.message());
			}
			else {
				boost::chrono::system_clock::time_point now = boost::chrono::system_clock::now();
				unsigned long elapse
					= (unsigned long)boost::chrono::duration_cast<boost::chrono::milliseconds>(now - _prevTime).count();
				_prevTime = now;

				_eventReceiver.onUpdate(elapse);

				_updater.expires_from_now(boost::posix_time::milliseconds(_updateTimeMs));
				_updater.async_wait(boost::asio::bind_executor(_strand, boost::bind(
					&NetCommonObjects::handleUpdate, this, boost::asio::placeholders::error)));
			}
		}
	};

}//namespace mln::net {

max_stars_repo_name: lazychase/mlnsdk

max_issues_repo_name: lazychase/mlnsdk

max_forks_repo_name: lazychase/mlnsdk


text:
// Copyright (c) 2014-2019 The Dash-Core Developers, The BiblePay Developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "spork.h"
#include "utilmoneystr.h"
#include "masternode-payments.h"
#include "masternodeconfig.h"
#include "activemasternode.h"
#include "masternodeman.h"
#include "governance-classes.h"
#include "masternode-sync.h"
#include "smartcontract-server.h"
#include "rpcpog.h"
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
#include <boost/algorithm/string.hpp> // for trim()
#include <boost/date_time/posix_time/posix_time.hpp> // for StringToUnixTime()
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <openssl/crypto.h>
#include <stdint.h>
#include <univalue.h>
#include <fstream>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>

extern CWallet* pwalletMain;

std::string GetSANDirectory2()
{
	 boost::filesystem::path pathConfigFile(GetArg("-conf", "biblepay.conf"));
     if (!pathConfigFile.is_complete()) pathConfigFile = GetDataDir(false) / pathConfigFile;
	 boost::filesystem::path dir = pathConfigFile.parent_path();
	 std::string sDir = dir.string() + "/SAN/";
	 boost::filesystem::path pathSAN(sDir);
	 if (!boost::filesystem::exists(pathSAN))
	 {
		 boost::filesystem::create_directory(pathSAN);
	 }
	 return sDir;
}

std::string ToYesNo(bool bValue)
{
	std::string sYesNo = bValue ? "Yes" : "No";
	return sYesNo;
}

std::string strReplace(std::string& str, const std::string& oldStr, const std::string& newStr)
{
  size_t pos = 0;
  while((pos = str.find(oldStr, pos)) != std::string::npos){
     str.replace(pos, oldStr.length(), newStr);
     pos += newStr.length();
  }
  return str;
}

bool SignStake(std::string sBitcoinAddress, std::string strMessage, std::string& sError, std::string& sSignature)
{
	LOCK(cs_main);
	{
		// Unlock wallet if SecureKey is available
		bool bTriedToUnlock = false;
		if (pwalletMain->IsLocked() && !msEncryptedString.empty())
		{
			bTriedToUnlock = true;
			if (!pwalletMain->Unlock(msEncryptedString, false))
			{
				static int nNotifiedOfUnlockIssue = 0;
				if (nNotifiedOfUnlockIssue == 0)
					LogPrintf("\nUnable to unlock wallet with SecureString.\n");
				nNotifiedOfUnlockIssue++;
				sError = "Unable to unlock wallet with autounlock password provided";
				return false;
			}
		}

		CBitcoinAddress addr(sBitcoinAddress);
		CKeyID keyID;
		if (!addr.GetKeyID(keyID))
		{
			sError = "Address does not refer to key";
			if (bTriedToUnlock)		{ pwalletMain->Lock();	}
			return false;
		}
		CKey key;
		if (!pwalletMain->GetKey(keyID, key))
		{
			sError = "Private key not available";
			if (bTriedToUnlock)		{ pwalletMain->Lock();	}
			return false;
		}
		CHashWriter ss(SER_GETHASH, 0);
		ss << strMessageMagic;
		ss << strMessage;
		std::vector<unsigned char> vchSig;
		if (!key.SignCompact(ss.GetHash(), vchSig))
		{
			sError = "Sign failed";
			if (bTriedToUnlock)		{ pwalletMain->Lock();	}
			return false;
		}
		sSignature = EncodeBase64(&vchSig[0], vchSig.size());
		if (bTriedToUnlock)
		{
			pwalletMain->Lock();
		}
		return true;
	}
}

std::string SendBlockchainMessage(std::string sType, std::string sPrimaryKey, std::string sValue, double dStorageFee, bool Sign, std::string sExtraPayload, std::string& sError)
{
	const Consensus::Params& consensusParams = Params().GetConsensus();
    std::string sAddress = consensusParams.FoundationAddress;
    CBitcoinAddress address(sAddress);
    if (!address.IsValid())
	{
		sError = "Invalid Destination Address";
		return sError;
	}
    CAmount nAmount = CAmountFromValue(dStorageFee);
	CAmount nMinimumBalance = CAmountFromValue(dStorageFee);
    CWalletTx wtx;
	boost::to_upper(sPrimaryKey); // DC Message can't be found if not uppercase
	boost::to_upper(sType);
	std::string sNonceValue = RoundToString(GetAdjustedTime(), 0);
 	std::string sMessageType      = "<MT>" + sType  + "</MT>";
    std::string sMessageKey       = "<MK>" + sPrimaryKey   + "</MK>";
	std::string sMessageValue     = "<MV>" + sValue + "</MV>";
	std::string sNonce            = "<NONCE>" + sNonceValue + "</NONCE>";
	std::string sMessageSig = "";
	if (Sign)
	{
		std::string sSignature = "";
		bool bSigned = SignStake(consensusParams.FoundationAddress, sValue + sNonceValue, sError, sSignature);
		if (bSigned)
		{
			sMessageSig = "<SPORKSIG>" + sSignature + "</SPORKSIG>";
			sMessageSig += "<BOSIG>" + sSignature + "</BOSIG>";
			sMessageSig += "<BOSIGNER>" + consensusParams.FoundationAddress + "</BOSIGNER>";
		}
		if (!bSigned) LogPrintf("Unable to sign spork %s ", sError);
		LogPrintf(" Signing Nonce%f , With spork Sig %s on message %s  \n", (double)GetAdjustedTime(),
			 sMessageSig.c_str(), sValue.c_str());
	}
	std::string s1 = sMessageType + sMessageKey + sMessageValue + sNonce + sMessageSig + sExtraPayload;
	LogPrintf("SendBlockchainMessage %s", s1);
	bool fSubtractFee = false;
	bool fInstantSend = false;
	bool fSent = RPCSendMoney(sError, address.Get(), nAmount, fSubtractFee, wtx, fInstantSend, s1);

	if (!sError.empty())
		return std::string();
    return wtx.GetHash().GetHex().c_str();
}

std::string GetGithubVersion()
{
	std::string sURL = "https://" + GetSporkValue("pool");
	std::string sRestfulURL = "SAN/LastMandatoryVersion.htm";
	std::string sResponse = BiblepayHTTPSPost(false, 0, "", "", "", sURL, sRestfulURL, 443, "", 25, 10000, 1);
	if (sResponse.length() > 11)
	{
		sResponse = sResponse.substr(sResponse.length() - 11, 11);
		sResponse = strReplace(sResponse, "\n", "");
		sResponse = strReplace(sResponse, "\r", "");
    }
	return sResponse;
}

double GetCryptoPrice(std::string sSymbol)
{
	int SSL_PORT = 443;
	int CONNECTION_TIMEOUT = 15;
	int TRANSMISSION_TIMEOUT = 15000;
	int TERM_TYPE = 1;
	std::string sC1 = BiblepayHTTPSPost(false, 0, "", "", "api", GetSporkValue("pool"), GetSporkValue("getcryptoprice" + sSymbol), SSL_PORT, "", CONNECTION_TIMEOUT, TRANSMISSION_TIMEOUT, TERM_TYPE);
	double dDebugLevel = cdbl(GetArg("-debuglevel", "0"), 0);
	if (dDebugLevel == 1)
		LogPrintf("CryptoPrice %s", sC1);
	std::string sPrice = ExtractXML(sC1, "<MIDPOINT>", "</MIDPOINT>");
	double dMid = cdbl(sPrice, 12);
	return dMid;
}

double GetPBase(double& out_BTC)
{
	// Get the BiblePay market price based on midpoint of bid-ask in Satoshi * BTC price in USD
	double dBBP = GetCryptoPrice("bbp");
	double dBTC = GetCryptoPrice("btc");
	out_BTC = dBTC;
	double dPriceUSD = dBTC * dBBP;
	return dPriceUSD;
}

bool VerifySigner(std::string sXML)
{
	std::string sSignature = ExtractXML(sXML, "<sig>", "</sig>");
	std::string sSigner = ExtractXML(sXML, "<signer>", "</signer>");
	std::string sMessage = ExtractXML(sXML, "<message>", "</message>");
	std::string sError;
	bool fValid = CheckStakeSignature(sSigner, sSignature, sMessage, sError);
	return fValid;
}

double GetQTPhase(bool fInFuture, double dPrice, int nEventHeight, double& out_PriorPrice, double& out_PriorPhase)
{
	// fInFuture is required to calculate tomorrow's qt phase.  (Otherwise, return the historical value).
	// If a -1 dPrice is passed in, the caller wants the prior days QT level and price.
    double nMaximumTighteningPercentage = GetSporkDouble("qtmaxpercentage", 0);
	double nQTStartHeight = GetSporkDouble("qtheight", 0);
	bool fEnabled = sporkManager.IsSporkActive(SPORK_20_QUANTITATIVE_TIGHTENING_ENABLED) && nQTStartHeight > 0 && nEventHeight > nQTStartHeight;
	if (nMaximumTighteningPercentage == 0 || !fEnabled)
		return 0;
	double nPriceThreshhold = GetSporkDouble("qtpricethreshhold", 0);
	if (dPrice == 0)
		return 0;
	// Step 3: Get yesterdays phase
	const Consensus::Params& consensusParams = Params().GetConsensus();
	int iNextSuperblock = 0;
	int nTotalSamples = 5;
	// Check N Samples for the first valid price and phase
	for (int iDay = 0; iDay < nTotalSamples; iDay++)
	{
		int iLastSuperblock = GetLastGSCSuperblockHeight(nEventHeight, iNextSuperblock);
		if (nEventHeight < consensusParams.QTHeight || iLastSuperblock < consensusParams.QTHeight || iLastSuperblock > chainActive.Tip()->nHeight)
			return 0;
		std::string sXML;
		CBlockIndex* pindex = FindBlockByHeight(iLastSuperblock);
		if (pindex == NULL) return 0;
		CBlock block;
		out_PriorPhase = 0;
		if (ReadBlockFromDisk(block, pindex, consensusParams))
		{
			for (unsigned int i = 0; i < block.vtx[0]->vout.size(); i++)
			{
				std::string sRecipient = PubKeyToAddress(block.vtx[0]->vout[i].scriptPubKey);
				if (sRecipient == consensusParams.FoundationQTAddress)
				{
					out_PriorPhase = ((double)block.vtx[0]->vout[i].nValue / COIN) * 100;
					if (out_PriorPhase > 100) out_PriorPhase = 0;
					if (out_PriorPhase < 000) out_PriorPhase = 0;
				}
				sXML += block.vtx[0]->vout[i].sTxOutMessage;
			}
		}
		else
		{
			LogPrintf("GetQtPhase::ERROR Unable to read block from disk %f ", iLastSuperblock);
			return 0;
		}
		out_PriorPrice = cdbl(ExtractXML(sXML, "<price>", "</price>"), 12);
		if (out_PriorPhase > 0)
			break;
		nEventHeight -= consensusParams.nDCCSuperblockCycle;
		if (nEventHeight < consensusParams.nSuperblockStartBlock)
			break;
	}

	if (!fInFuture)
	{
		return out_PriorPhase;
	}

	// Otherwise, Calculate the new phase
	double dModifier = (dPrice >= nPriceThreshhold) ? -1 : 1;
	double dPhase = out_PriorPhase + dModifier;
	if (dPhase > nMaximumTighteningPercentage) dPhase = nMaximumTighteningPercentage;
	if (dPhase <  0) dPhase = 0;
	if (dPhase > 90) dPhase = 90;
	return dPhase;
}

bool GetTransactionTimeAndAmount(uint256 txhash, int nVout, int64_t& nTime, CAmount& nAmount)
{
	uint256 hashBlock = uint256();
	CTransactionRef tx2;
	if (GetTransaction(txhash, tx2, Params().GetConsensus(), hashBlock, true))
	{
		   BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
           if (mi != mapBlockIndex.end() && (*mi).second)
		   {
              CBlockIndex* pMNIndex = (*mi).second;
			  nTime = pMNIndex->GetBlockTime();
		      nAmount = tx2->vout[nVout].nValue;
			  return true;
		   }
	}
	return false;
}

/*
int64_t GetDCCFileTimestamp()
{
	std::string sDailyMagnitudeFile = GetSANDirectory2() + "magnitude";
	boost::filesystem::path pathFiltered(sDailyMagnitudeFile);
	if (!boost::filesystem::exists(pathFiltered)) return GetAdjustedTime() - 0;
	int64_t nTime = last_write_time(pathFiltered);
	return nTime;
}
*/

bool IsMature(int64_t nTime, int64_t nMaturityAge)
{
	int64_t nNow = GetAdjustedTime();
	int64_t nRemainder = nNow % nMaturityAge;
	int64_t nCutoff = nNow - nRemainder;
	bool bMature = nTime <= nCutoff;
	return bMature;
}

int64_t GetDCCFileAge()
{
	std::string sDailyMagnitudeFile = GetSANDirectory2() + "magnitude";
	boost::filesystem::path pathFiltered(sDailyMagnitudeFile);
	// ST13 last_write_time error:  Rob A. - Biblepay - 2/8/2018
	if (!boost::filesystem::exists(pathFiltered)) return GetAdjustedTime() - 0;
	int64_t nTime = last_write_time(pathFiltered);
	int64_t nAge = GetAdjustedTime() - nTime;
	return nAge;
}

CAmount GetMoneySupplyEstimate(int nHeight)
{
	int64_t nAvgReward = 13657;
	double nUnlockedSupplyPercentage = .40;
	CAmount nSupply = nAvgReward * nHeight * nUnlockedSupplyPercentage * COIN;
	return nSupply;
}

std::string rPad(std::string data, int minWidth)
{
	if ((int)data.length() >= minWidth) return data;
	int iPadding = minWidth - data.length();
	std::string sPadding = std::string(iPadding,' ');
	std::string sOut = data + sPadding;
	return sOut;
}

max_stars_repo_name: sieb/biblepay-evolution-tester

max_issues_repo_name: sieb/biblepay-evolution-tester

max_forks_repo_name: sieb/biblepay-evolution-tester


text:
/**
 ** Copyright (c) 2014 Illumina, Inc.
 **
 ** This file is part of Illumina's Enhanced Artificial Genome Engine (EAGLE),
 ** covered by the "BSD 2-Clause License" (see accompanying LICENSE file)
 **
 ** \description Top level component to deal with reference genomes.
 **
 ** \author Mauricio Varea
 **/

#include "common/Logger.hh"
#include "model/Struct.hh"
#include "genome/Reference.hh"

#include <boost/assert.hpp>
#include <boost/format.hpp>
#include <boost/io/ios_state.hpp>
#include <boost/foreach.hpp>
#include <boost/filesystem.hpp>
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/property_tree/xml_parser.hpp>

#include <numeric>
#include <algorithm>

namespace eagle
{
namespace genome
{

FastaReference::FastaReference (
    const eagle::io::FastaMetadata& metadata
    )
    : reader_(metadata)
    , mode_(std::ios_base::in)
    , local2globalCache_roundRobin( 0 )
    , local2globalCache_chr(2)
    , local2globalCache_pos(2)
{
    inputStructure(metadata);
#ifdef EAGLE_DEBUG_MODE
    std::cout << "FASTA index Metadata:" << std::endl
              << reader_.index() << std::endl;
#endif
}

FastaReference::FastaReference (
    const boost::filesystem::path& outputDir,
    const bool overwrite
    )
    : writer_(outputDir,overwrite)
    , mode_(std::ios_base::out)
    , local2globalCache_roundRobin( 0 )
    , local2globalCache_chr(2)
    , local2globalCache_pos(2)
{
    outputStructure(outputDir);
}

FastaReference::FastaReference (
    const eagle::io::FastaMetadata& metadata,
    const boost::filesystem::path& outputDir,
    const bool overwrite
    )
    : reader_(metadata)
    , writer_(outputDir,overwrite)
    , mode_(std::ios_base::in & std::ios_base::out)
    , local2globalCache_roundRobin( 0 )
    , local2globalCache_chr(2)
    , local2globalCache_pos(2)
{
    inputStructure(metadata);
    outputStructure(outputDir);
}

void FastaReference::inputStructure( const eagle::io::FastaMetadata& metadata )
{
    BOOST_FOREACH(const eagle::io::FastaIndex &idx, metadata)
    {
        if (!boost::filesystem::exists( idx.first ))
        {
            EAGLE_WARNING("Input reference genome file does not exist: " + idx.first.string());
        }
    }
}

void FastaReference::outputStructure( const boost::filesystem::path& outputDir )
{
    if (!boost::filesystem::exists(outputDir) && !boost::filesystem::create_directory(outputDir) && !boost::filesystem::exists(outputDir))
    {
        BOOST_THROW_EXCEPTION(common::IoException(errno, (boost::format("Failed to create directory %s for sample genome") % outputDir).str()));
    }
    std::clog << "+ Output path to sample genome: " << outputDir << std::endl;
}

void FastaReference::inputMode()
{
    if (!(mode_ & std::ios_base::in))
    {
        BOOST_THROW_EXCEPTION(common::PreConditionException(
                              (boost::format("Not allowed to read from %s" ) % reader_.file()).str() ));
    }
}
// TODO: print out the file name
void FastaReference::outputMode()
{
    if (!(mode_ & std::ios_base::out))
    {
        BOOST_THROW_EXCEPTION(common::PreConditionException(
                              (boost::format("Not allowed to write to FASTA file" )).str() ));
    }
}

unsigned long FastaReference::local2global( const eagle::model::Locus& location)
{
    assert( location.pos() > 0 );
    if (location.chr() == local2globalCache_chr[0])
    {
        return local2globalCache_pos[0] + location.pos() - 1;
    }
    else if (location.chr() == local2globalCache_chr[1])
    {
        return local2globalCache_pos[1] + location.pos() - 1;
    }
    else
    {
        static bool firstTime = true;
        if (firstTime && local2globalCache_chr[local2globalCache_roundRobin] != "")
        {
            static int maxMsgCount = 100;
            if (maxMsgCount > 0)
            {
                --maxMsgCount;
                std::clog << "Warning: degraded performance due to more than 2 alleles - local2global: updating cache pos " << local2globalCache_roundRobin << " from " <<  local2globalCache_chr[local2globalCache_roundRobin]<< " to " << location.chr() << std::endl;
            }
            firstTime = false;
        }
        eagle::io::FastaInfo info;
        boost::filesystem::path file = reader_.find(location.chr(),info);
        if (file.empty())
        {
            std::stringstream message;
            message << "Could not convert local position " << location << " into global" << std::endl;
            EAGLE_ERROR( message.str() );
        }
        local2globalCache_chr[local2globalCache_roundRobin] = location.chr();
        local2globalCache_pos[local2globalCache_roundRobin] = info.position.first;
        local2globalCache_roundRobin = (local2globalCache_roundRobin+1)%2;
        return info.position.first + location.pos() - 1;
    }
}

eagle::model::Locus FastaReference::global2local(unsigned long globalPos)
{
    if ( !global2localCache.within(globalPos) )
    {
        std::clog << "global2local: updating cache from " << global2localCache.contigName;
        //    eagle::io::FastaInfo info;
        boost::filesystem::path file = reader_.find(globalPos,global2localCache);
        if (file.empty())
        {
            EAGLE_ERROR( (boost::format("Could not convert global location %lu into local") % globalPos).str() );
        }
        std::clog << " to " << global2localCache.contigName << std::endl;
    }

    return eagle::model::Locus( global2localCache.contigName, globalPos - global2localCache.position.first + 1);
}

void FastaReference::convertFromGlobalPos( const unsigned long globalPos, int& refId, unsigned long& posInContig )
{
    eagle::model::Locus location = global2local(globalPos);
    refId       = reader_.getGlobalContigId();
    posInContig = location.pos();
}

char FastaReference::get( const unsigned long globalPos, const unsigned long offset, bool &overlapContigBoundary )
{
    inputMode();
    if ( globalPos < currentGetInfo_.position.first || globalPos >= (currentGetInfo_.position.first + currentGetInfo_.contigSize) )
    {
        boost::filesystem::path file = reader_.find(globalPos,currentGetInfo_);
        if (reader_.open( file ) || reader_.cache().empty())
        {
            if (! reader_.read(currentGetInfo_) )
            {
                BOOST_THROW_EXCEPTION(common::IoException(errno, (boost::format("Failed to read block of data from %s (cache has %lu bases)")
                                                                  % file % reader_.cache().size()).str()));
            }
        }
    }
    assert( globalPos >= currentGetInfo_.position.first && globalPos < (currentGetInfo_.position.first + currentGetInfo_.contigSize) && "Global position needs to be situated within contig's range" );
    overlapContigBoundary = !reader_.inCache( globalPos + offset );
    return reader_[globalPos + offset];
}

char FastaReference::get( const eagle::model::Locus location, const unsigned long offset, bool &overlapContigBoundary )
{
    inputMode();
    if (location.chr() != currentGetInfo_.contigName)
    {
        boost::filesystem::path file = reader_.find(location.chr(),currentGetInfo_);
        if (reader_.open( file ) || reader_.cache().empty())
        {
            if (! reader_.read(currentGetInfo_) )
            {
                BOOST_THROW_EXCEPTION(common::IoException(errno, (boost::format("Failed to read block of data from %s (cache has %lu bases)")
                                                                  % file % reader_.cache().size()).str()));
            }
        }
    }
    assert( location.pos() + currentGetInfo_.position.first + offset >= currentGetInfo_.position.first && "Global position needs to be situated within contig's range" );
    overlapContigBoundary = !reader_.inCache( location.pos() + offset );
    return reader_[location.pos() + offset];
}

// read entire contig
unsigned long FastaReference::read( eagle::model::Contig &contig, const std::string &contigName )
{
    inputMode();
    eagle::io::FastaInfo info;
    boost::filesystem::path file = reader_.find(contigName,info);
    if (reader_.open( file ))
    {
        if (! reader_.read(info) )
        {
            BOOST_THROW_EXCEPTION(common::IoException(errno, (boost::format("Failed to read block of data from %s") % file).str()));
        }
    }
    std::vector<char> segment;
    contig.reset();
    contig.name(contigName);
    segment.resize( std::remove_copy( reader_.cache().begin(), reader_.cache().end(), segment.begin(), '\n' ) - segment.begin() );
    contig.append(segment);
    return segment.size();
}

// TODO: use cache() and read() to make it go faster
void FastaReference::load()
{
    inputMode();
    char base;
    bool newContig = false;
    eagle::model::Contig contig;
    unsigned long position = 0L;
    unsigned long prevPos = position;
    if (! reader_.seekg( 0, std::ios_base::beg ))
    {
        BOOST_THROW_EXCEPTION(common::IoException(errno, (boost::format("Failed to rewind FASTA file %s") % reader_.file()).str()));
    }
    boost::filesystem::path lastPath = reader_.file();
    while (reader_.get(base, newContig))
    {
        if (newContig)
        {
            if (contig.size())
            {
                reference_.push_back( contig );
                std::clog << "....loaded '" << contig.name() << "' in memory" << std::endl;
                reader_.index().update( lastPath,
                                        eagle::io::FastaInfo(contig.id(),contig.size(),prevPos) );
                lastPath = reader_.file();
                contig.reset();
            }
            std::clog << "..Contig #" << reader_.getLocalContigId() << " begins at position " << position << std::endl;
            prevPos = position;
            contig.name( reader_.getContigName() );
        }
        contig.put( base );
        ++position;
    }
    if (contig.size())
    {
        reference_.push_back(contig);
        std::clog << "....loaded '" << contig.name() << "' in memory" << std::endl;
        eagle::io::FastaInfo info;
        reader_.index().update( lastPath,
                                eagle::io::FastaInfo(contig.id(),contig.size(),prevPos) );
    }
#ifdef EAGLE_DEBUG_MODE
    std::cout << "Merged FASTA Metadata:" << std::endl
              << reader_.index() << std::endl;
#endif
}

void FastaReference::save()
{
    outputMode();
    ReferenceIterator contig(reference_.begin());
    unsigned int contigCount(0);
    std::pair<unsigned long, unsigned long> location(1,0);  // (logical,phisical)
    while(reference_.end() != contig)
    {
        std::clog << "..Writing contig (#" << contigCount << "): '" << contig->name() << "'" << std::endl;
        location.second += contig->name().size() + 2;
        writer_.write( *contig,
                       eagle::io::FastaInfo( contig->id(), contig->size(), location.first, location.second, FASTA_CONTIG_WIDTH ));
        // TODO: overload FastaInfo::operator+() to do this when chromosome name hasn't changed
        location.first  += contig->size();
        location.second = 0; // This was used when we wanted to dump multiple contigs in one file:
                             // location.second += 1 + contig->size() + contig->size() / FASTA_CONTIG_WIDTH;
        ++contig;
        ++contigCount;
    }
    metadata_ = writer_.index();  // transfer metadata, so that *.fai info can be replicated in genome_size.xml
}

size_t FastaReference::length()
{
    size_t length = 0;
    for (auto& contig: reference_)
    {
        length += contig.size();
    }
    return length;
}

std::vector<std::string> FastaReference::allContigNames() const
{
    using boost::lambda::_1;
    using boost::lambda::bind;
    std::vector<std::string> contigNames;
    if (reader_.size())
    {
        for (eagle::io::FastaMetadata::const_iterator idx = reader_.index().begin();
             idx != reader_.index().end(); ++idx)
        {
            //std::transform( idx->second.begin(), idx->second.end(), std::back_inserter(contigNames),
              //              bind( &eagle::io::FastaInfo::contigName, _1 ));
	    for (auto& contig: idx->second)
            {
                contigNames.push_back(contig.contigName);
            }
        }
    } else {
        BOOST_FOREACH(const eagle::model::Contig& contig, reference_)
        {
            contigNames.push_back(contig.id());
        }
    }
    return contigNames;
}

std::vector<unsigned long> FastaReference::allContigLengths() const
{
    using boost::lambda::_1;
    using boost::lambda::bind;
    std::vector<unsigned long> contigLengths;
    if (reader_.size())
    {
        for (eagle::io::FastaMetadata::const_iterator idx = reader_.index().begin();
             idx != reader_.index().end(); ++idx)
        {
            //std::transform( idx->second.begin(), idx->second.end(), std::back_inserter(contigLengths),
              //              bind( &eagle::io::FastaInfo::contigSize, _1 ));
	    for (auto& contig: idx->second)
            {
                contigLengths.push_back(contig.contigSize);
            }
        }
    } else {
        BOOST_FOREACH(const eagle::model::Contig& contig, reference_)
        {
            contigLengths.push_back(contig.size());
        }
    }
    return contigLengths;
}

unsigned long FastaReference::getContigLength(const std::string &contigName) const
{
    const std::vector<std::string> contigNames = allContigNames();
    const std::vector<unsigned long> contigLengths = allContigLengths();
    for (unsigned int i = 0; i < contigNames.size(); ++i)
    {
        if (contigName == contigNames[i])
        {
            assert( i < contigLengths.size() );
            return contigLengths[i];
        }
    }
    BOOST_THROW_EXCEPTION(eagle::common::EagleException(0, (boost::format("Contig '%s' not found") % contigName).str()));
}

eagle::model::Contig & FastaReference::getContig(const std::string &contigName)
{
    iterator contig(reference_.begin());
    for (; reference_.end() != contig; ++contig)
    {
        if (contig->name() == contigName)
            return *contig;
    }
    BOOST_THROW_EXCEPTION(eagle::common::EagleException(0, (boost::format("Contig '%s' not found") % contigName).str()));
}

eagle::model::Contig const & FastaReference::getContig(const std::string &contigName) const
{
    ReferenceIterator contig(reference_.begin());
    for (; reference_.end() != contig; ++contig)
    {
        if (contig->name() == contigName)
            return *contig;
    }
    BOOST_THROW_EXCEPTION(eagle::common::EagleException(0, (boost::format("Contig '%s' not found") % contigName).str()));
}

/***************************/
/**  MultiFastaReference  **/
/***************************/

eagle::io::FastaMetadata MultiFastaReference::initialize( const std::vector<boost::filesystem::path>& inputPaths )
{
    eagle::io::FastaMetadata metadata;
    BOOST_FOREACH(const boost::filesystem::path &p, inputPaths)
    {
        if (boost::filesystem::is_directory(p))
        {
            GenomeSizeXml GS(p / GENOMESIZE_XML);
            if (GS.enabled())
            {
                GS.load( metadata );
            } else {
                eagle::common::Glob FS(".*\\.fa(sta)?$");
                metadata.init( FS.glob(p) );
            }
        } else {
            metadata.update( p, eagle::io::FastaInfo() );
        }
    }
#ifdef EAGLE_DEBUG_MODE
    std::cout << "Genome Metadata:" << std::endl
              << metadata << std::endl;
#endif
    return metadata;
}

eagle::io::FastaMetadata MultiFastaReference::initialize( const boost::filesystem::path& inputPath )
{
    return initialize( std::vector< boost::filesystem::path >( 1, inputPath ) );
}

void MultiFastaReference::saveMetadata()
{
    if (!metadata().empty())
    {   // Hack: we are writing genome_size.xml into the parent directory of the 1st chromosome
        GenomeSizeXml GS( metadata().begin()->first.parent_path() / GENOMESIZE_XML, overwrite_ );
        GS.save( metadata() );
    } else {
        EAGLE_WARNING("No metadata available!");
        EAGLE_WARNING_CONT("*** Will not write " GENOMESIZE_XML " ***");
    }
}

/*****************/
/**  METADATA   **/
/*****************/

GenomeSizeXml::GenomeSizeXml(
    const boost::filesystem::path& indexPath
    )
    : path_(indexPath)
    , mode_(std::ios_base::in)
{
    if (!path_.empty() && !boost::filesystem::exists(path_))
    {
        EAGLE_WARNING( (boost::format("Cannot read FASTA metadata from %s") % path_).str() );
        path_ = boost::filesystem::path();
    }
}
GenomeSizeXml::GenomeSizeXml(
    const boost::filesystem::path& indexPath,
    const bool overwrite
    )
    : path_(indexPath)
    , mode_(std::ios_base::out)
{
    if ( path_.empty() ) {
        BOOST_THROW_EXCEPTION(common::PreConditionException( "Path to '" GENOMESIZE_XML "' cannot be empty" ));
    } else if ( boost::filesystem::exists(path_) ) {
        if (overwrite)
        {
            EAGLE_WARNING( "Overwriting " << path_ << " due to the --force switch." );
        } else {
            BOOST_THROW_EXCEPTION(common::IoException(errno, (boost::format("Cannot write FASTA metadata file %s: File already exists!") % path_).str()));
        }
    }
}

void GenomeSizeXml::load(eagle::io::FastaMetadata &metadata)
{
    if (!(mode_ & std::ios_base::in))
    {
        BOOST_THROW_EXCEPTION(common::PreConditionException( (boost::format("Not allowed to read from %s") % path_ ).str() ));
    }
    if (boost::filesystem::exists(path_))
    {
        std::ifstream is(path_.string().c_str());
        if (!is)
        {
            BOOST_THROW_EXCEPTION(common::IoException(errno, (boost::format("Failed to open %s for reading") % path_).str()));
        }
        this->load(is,metadata);
    } else {
        if (! path_.empty() )
        {
            EAGLE_WARNING( "Could not find " << path_ );
            EAGLE_WARNING_CONT( "*** Will not pre-load FASTA metadata ***" );
        }
    }
}

void GenomeSizeXml::load(std::istream &is, eagle::io::FastaMetadata &metadata)
{
    using namespace boost::property_tree;
    ptree tree;
    read_xml< ptree >(is, tree);
    unsigned long absolutePos(0);
    EAGLE_DEBUG(0, "Reading metadata from " << path_ );
    if( boost::optional< ptree & > sequenceSizes = tree.get_child_optional( "sequenceSizes" ) )
    {
        BOOST_FOREACH(const ptree::value_type& chromosome, sequenceSizes.get())
        {
            if (chromosome.first != "chromosome")
            {
                BOOST_THROW_EXCEPTION(common::CorruptedFileException("XML.GenomeSize",
                                     (boost::format("*** Expected <chromosome/> element in %s. Found <%s/> ***") % path_ % chromosome.first).str() ));
            }
            boost::filesystem::path fileName = chromosome.second.get<boost::filesystem::path>("<xmlattr>.fileName");
            std::string chrName = chromosome.second.get<std::string>("<xmlattr>.contigName");
            unsigned long chrSize = chromosome.second.get<unsigned long>("<xmlattr>.totalBases");
            metadata.update( path_.parent_path() / fileName,
                             eagle::io::FastaInfo( chrName, chrSize, absolutePos ) );
            absolutePos += chrSize;
        }
    } else if (boost::optional< ptree & > sequenceSizesLegacy = tree.get_child_optional( "SequenceSizes" )) {
        BOOST_FOREACH(const ptree::value_type& chromosome, sequenceSizesLegacy.get())
        {
            boost::filesystem::path fileName = chromosome.first;
            std::string chrName = fileName.stem().string();
            unsigned long chrSize = boost::lexical_cast<unsigned long>( chromosome.second.data() );
            metadata.update( path_.parent_path() / fileName,
                             eagle::io::FastaInfo( chrName, chrSize, absolutePos ) );
            absolutePos += chrSize;
        }
    } else {
        BOOST_THROW_EXCEPTION(common::CorruptedFileException("XML.GenomeSize","*** either <sequenceSizes/> or <SequenceSizes/> missing at the top level ***"));
    }
}

void GenomeSizeXml::save(const eagle::io::FastaMetadata &index)
{
    if (!(mode_ & std::ios_base::out))
    {
        BOOST_THROW_EXCEPTION(common::PreConditionException( (boost::format("Not allowed to write to %s") % path_ ).str() ));
    }
    this->save(path_,index);
}

void GenomeSizeXml::save(const boost::filesystem::path &file, const eagle::io::FastaMetadata &index)
{
    using namespace boost::property_tree;
    ptree tree;
    tree.clear();
    tree.put("sequenceSizes", "");
    ptree &sequenceSizes = tree.get_child("sequenceSizes");
    EAGLE_DEBUG(0, "Writing metadata to " << path_ );
    for ( eagle::io::FastaMetadata::const_iterator chromosome = index.begin();
          chromosome != index.end();
          ++chromosome)
    {
        for (unsigned int i=0; i < chromosome->second.size(); i++)
        {
            ptree node;
            node.put( "<xmlattr>.fileName",   chromosome->first.filename().string() );
            node.put( "<xmlattr>.contigName", chromosome->second[i].contigName );
            node.put( "<xmlattr>.totalBases", chromosome->second[i].contigSize );
            sequenceSizes.add_child("chromosome",node);
        }
    }
    write_xml(file.string(), tree, std::locale(), xml_writer_settings<std::string>(' ', 4));
}

} // namespace genome
} // namespace eagle

max_stars_repo_name: sequencing/EAGLE

max_issues_repo_name: sequencing/EAGLE

max_forks_repo_name: sequencing/EAGLE


text:
#include "intcode.hpp"

#include <sstream>
#include <vector>
#include <algorithm>
#include <boost/multiprecision/cpp_int.hpp>

enum class Directions {
    UP, DOWN, LEFT, RIGHT
};

using Data = boost::multiprecision::cpp_int;  // It appears this didn't need big integers. (if needed:
// boost::multiprecision::cpp_int);
using Grid = std::vector<std::vector<char>>;
using Path = std::vector<Directions>;

void print_grid(const Grid& grid) {
    for (const auto& row : grid) {
        for (auto c : row) {
            std::cout << c;
        }
        std::cout << '\n';
    }
}

std::pair<int, int> find_robot(const Grid& grid) {
    for (int i = 0; i < (int) grid.size(); ++i)
        for (int j = 0; j < (int) grid[i].size(); ++j)
            if (grid[i][j] == '^')
                return {i, j};
    return {-1, -1};
}

int part2(Grid grid) {
    auto[y, x] = find_robot(grid);

    int dy = -1;
    int dx = 0;

    while (true) {

        bool done = true;
        for (int v = -1; v <= 1; v += 2) {
            if (v == -1) {
                int tmp = dx;
                dx = -dy * v;
                dy = tmp * v;
            } else {
                dx = -dx;
                dy = -dy;
            }

            int count = 0;
            while (y + dy >= 0 && y + dy < grid.size() && x + dx >= 0 && x + dx < grid[y + dy].size() &&
                   grid[y + dy][x + dx] != '.') {
                y += dy;
                x += dx;
                count++;
            }
            if (count) {
                std::cout << (v < 0 ? 'L' : 'R') << "," << count << "," << std::flush;
                done = false;
                break;
            }
        }

        if (done)
            break;
    }
    return 0;
}

int main() {
    // Part 1
    {
        Process<Data> process{"input.txt"};
        auto inputs = process.get_inputs();
        auto outputs = process.get_outputs();

        Grid grid{1, std::vector<char>{}};
        while (true) {
            auto maybe_output = outputs->pop_if_alive(process);

            if (!maybe_output.has_value())
                break;

            char c = (int) *maybe_output;

            if (c == '\n')
                grid.emplace_back();
            else
                grid.back().push_back(c);

//            std::cout << c;
        }

        int sum = 0;
        for (int i = 1; i + 3 < (int) grid.size(); ++i) {
            for (int j = 1; j + 1 < (int) grid[i].size(); ++j) {
                if (grid[i][j] == '#' && grid[i - 1][j] == '#' && grid[i + 1][j] == '#' && grid[i][j - 1] == '#' &&
                    grid[i][j + 1] == '#') {
                    sum += i * j;
                }
            }
        }
        std::cout << "Part 1: " << sum << std::endl;
        process.join();
    }

    // Part 2
    // To solve this, run this line first:
    // std::cout << "Path: " << part2(grid) << std::endl;
    // This prints the sequence of steps. Then compress this by hand. See compress.py for how this was done.
    // Then adjust the below hardcoded steps.
    {
        auto assembly = read_program<Data>("input.txt");
        assembly[0] = 2;
        Process<Data> process{assembly};
        auto inputs = process.get_inputs();
        auto outputs = process.get_outputs();

        for (char c : std::string{"A,C,A,B,A,B,C,B,B,C\n"})   // Main sequence
            inputs->push((int) c );
        for (char c : std::string{"L,4,L,4,L,10,R,4\n"})      // A
            inputs->push((int) c );
        for (char c : std::string{"R,4,L,10,R,10\n"})         // B
            inputs->push((int) c );
        for (char c : std::string{"R,4,L,4,L,4,R,8,R,10\n"})  // C
            inputs->push((int) c );

        inputs->push((int) 'n');  // No video feed required.
        inputs->push((int) '\n');

        // Let the process run its course, print only the last output (some grid mapping at first).
        Data last = 0;
        while (true) {
            auto maybe = outputs->pop_if_alive(process);
            if (!maybe.has_value())
                break;
            last = *maybe;
        }
        std::cout << "Part 2: " << last << std::endl;
        process.join();
    }
}

max_forks_repo_name: bsamseth/advent-of-code