#include <boost/filesystem.hpp>

#include "bvsma/include/functions.hpp"
#include "bvsma/include/ZKProofs.hpp"
#include "bvsma/include/SchnorrSignature.hpp"
#include "bvsma/include/fileFunctions.hpp"
#include "bvsma/include/functionsParallel.hpp"

using namespace boost::filesystem;

using namespace std;
typedef unsigned char byte;

/* Gen exp */

// ./server 0 192 501 1 BMDB
// ./server 0 224 501 1 BMDB
// ./server 0 256 501 1 BMDB

// ./server 0 192 501 1 PUT
// ./server 0 224 501 1 PUT
// ./server 0 256 501 1 PUT

// ./server 0 192 501 1 FRGC
// ./server 0 224 501 1 FRGC
// ./server 0 256 501 1 FRGC

int main(int argc, char* argv[]){
    cout << "Run Malicious Server ..." << endl;

    boost::asio::io_service io_service, io_service2;

    auto clientIp = IpAddress::from_string("127.0.0.1");
	auto serverIp = IpAddress::from_string("127.0.0.1");
    auto enrollServerIp = IpAddress::from_string("127.0.0.1");

    SocketPartyData clientParty(clientIp, 1222);
    SocketPartyData enrollServerParty(enrollServerIp, 8004);

    SocketPartyData serverParty(serverIp, 1223);
    SocketPartyData serverParty2(serverIp, 8003);

    shared_ptr<CommParty> withEnrollServer, server;

    if (stoi(argv[1]) == 0){
        withEnrollServer = make_shared<CommPartyTCPSynced>(io_service, serverParty2, enrollServerParty);
    }
    else if(stoi(argv[1]) == 1){
        server = make_shared<CommPartyTCPSynced>(io_service, serverParty, clientParty);
    }

    boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));

    string dirServer = "../../../data/server/"+ string(argv[5]) +"/P"+ string(argv[2]);
    string enrLogTimer = dirServer +"/MALenrServerLogTimer.csv";
    string verLogTimer = dirServer +"/MALverServerLogTimer.csv";
    string keyStorage;
    string dirUser;

	string configFile = "../../../../libscapi/include/configFiles/NISTEC.txt";
    string curve = "P-"+string(argv[2]);

	auto dlog = make_shared<OpenSSLDlogECFp>(configFile, curve);
    auto g = dlog->getGenerator();
    auto q = dlog->getOrder();

    shared_ptr<ElGamalOnGroupElementEnc> elgamalTH = make_shared<ElGamalOnGroupElementEnc>(dlog);
    shared_ptr<ElGamalOnGroupElementEnc> elgamal = make_shared<ElGamalOnGroupElementEnc>(dlog);

    int resultInt;
    bool resultBool;
    string str;

    std::chrono::time_point<std::chrono::system_clock> globalStart;
    std::chrono::time_point<std::chrono::system_clock> localStart;
    std::chrono::time_point<std::chrono::system_clock> proofStart;
    std::ofstream out_file;

    try
    {

        if (stoi(argv[1]) == 0)
        {

            withEnrollServer->join(500, 5000);

            int count = stoi(argv[3]);
            int step = stoi(argv[4]);

            for (size_t i = 1; i < count; i=i+step)
            {

                out_file.open(enrLogTimer, ios::app);
                if (!out_file)
                {
                    throw "Error creating file"s;
                }

                cout << "======= Enrollment =======" << endl;
                localStart = std::chrono::system_clock::now();
                globalStart = localStart;

                auto userId = receiveBigint(withEnrollServer);
                cout << "-> User " << userId << " Enrollment Request ..." << endl;
                dirUser = dirServer +"/MAL/USERS/user"+userId.str();
                path pathUser(dirUser);
                create_directory(pathUser);
                dirUser = dirUser+"/";
                keyStorage = dirUser+"keystorage.txt";

                auto pairTH = elgamalTH->generateKey();
                auto s2 = dynamic_cast<ElGamalPrivateKey*>(pairTH.second.get())->getX();
                writeBigintinFile(keyStorage, "server", "PrivTH", s2);

                auto serverPKeyTH = pairTH.first;
                writePubKeyinFile(keyStorage, "server", "PubTH", serverPKeyTH);
                cout << "-> Server PrivTH and PubTH Generated and Stored ..." << endl;

                sendElGamalPubKey(withEnrollServer, serverPKeyTH);
                cout << "-> Server PubTH Sent ..." << endl;

                auto verKey = receiveGpElt(withEnrollServer, dlog);
                writeGPELTinFile(keyStorage, "enrollServer", "VerKey", verKey);
                cout << "-> EnrollmentServer VerKey Received and stored ..." << endl;

                auto clientPKeyTH = receiveElGamalPubKey(withEnrollServer, dlog, elgamalTH);
                writePubKeyinFile(keyStorage, "client", "PubTH", clientPKeyTH);
                cout << "-> Client PubTH Received and Stored ..." << endl;

                auto cltPk = receiveElGamalPubKey(withEnrollServer, dlog, elgamalTH);
                writePubKeyinFile(keyStorage, "client", "PubKS", cltPk);
                cout << "-> Client PubKS Received and Stored ..." << endl;

                auto jointPubKey = receiveElGamalPubKey(withEnrollServer, dlog, elgamalTH);
                elgamalTH->setKey(jointPubKey, pairTH.second);
                writePubKeyinFile(keyStorage, "joint", "jointTH", jointPubKey);
                writeCSVNew(out_file, localStart, false);
                cout << "-> Joint jointTH Received and stored ..." << endl;
                cout << "----> Keys Establishment DONE ..." << endl;

                cout << "-------------------------------------------" << endl;

                localStart = std::chrono::system_clock::now();
                auto thresholdEncSet = receiveVectElGamalCipher(withEnrollServer, dlog, elgamalTH);
                writeVectCipherinFile(dirUser+"thresholdEncSet.txt", thresholdEncSet);
                writeCSVNew(out_file, localStart, false);
                cout << "----> Threshold Set Encrypted and Permuted Received and Stored ..." << endl;

                cout << "-------------------------------------------" << endl;

                localStart = std::chrono::system_clock::now();
                auto finalTemp = receiveMapOfVectComponent(withEnrollServer, dlog, elgamal,elgamalTH);
                writeTemplate(dirUser, finalTemp);
                writeCSVNew(out_file, localStart, false);
                writeCSVNew(out_file, globalStart, true);
                cout << "-> Template Received and Stored ..." << endl;
                cout << "======= Enrollment END =======" << endl;

            }

        }
        else if(stoi(argv[1]) == 1){

            server->join(500, 5000);

            int count = stoi(argv[3]);
            int step = stoi(argv[4]);

            for (size_t i = 1; i < count; i=i+step)
            {

                out_file.open(verLogTimer, ios::app);
                if (!out_file)
                {
                    throw "Error creating file"s;
                }
                cout << "======= Verification =======" << endl;
                localStart = std::chrono::system_clock::now();
                globalStart = localStart;

                cout << "--> Step 1: User's existence verification ..." << endl;
                auto userId = receiveBigint(server);
                dirUser = dirServer +"/MAL/USERS/user"+userId.str()+"/";
                keyStorage = dirUser+"keystorage.txt";
                path p(dirUser+userId.str()+"Template.txt");
                if(!exists(p)){
                    server->writeWithSize("Unregistered");
                    throw "Unregistered User"s;
                }
                server->writeWithSize("Registered");
                cout << "User " << userId << " exists" << endl;
                writeCSVNew(out_file, localStart, false);

                localStart = std::chrono::system_clock::now();
                auto s2 = readBigintFromfile(keyStorage, "server", "PrivTH");
                ElGamalPrivateKey serPrivKeyTH(s2);
                shared_ptr<PrivateKey> serverPrivKeyTH = make_shared<ElGamalPrivateKey>(serPrivKeyTH);
                auto clientPKeyTH = readPubKeyFromfile(keyStorage, dlog, "client", "PubTH");
                auto serverPKeyTH = readPubKeyFromfile(keyStorage, dlog, "server", "PubTH");
                auto jointPubKey = readPubKeyFromfile(keyStorage, dlog, "joint", "jointTH");
                elgamalTH->setKey(jointPubKey, serverPrivKeyTH);

                auto clientPKey = readPubKeyFromfile(keyStorage, dlog, "client", "PubKS");
                elgamal->setKey(clientPKey);

                auto thresholdEncSet = readVectCipherFromFile(dirUser+"thresholdEncSet.txt", dlog);

                writeCSVNew(out_file, localStart, false);
                cout << "--> Keys and Threshold Set loaded ..." << endl;

                cout << "--> Step 1: DONE ..." << endl;
                cout << endl;

                cout << "-------------------------------------------" << endl;
                cout << "--> Step 2: Probe verification ..." << endl;
                localStart = std::chrono::system_clock::now();

                auto probeEnc = receiveVectElGamalCipher(server, dlog, elgamal);
                auto indexCompVect = receiveIntVect(server);

                proofStart = std::chrono::system_clock::now();
                resultBool = zkPoKMultiANDPlainVerifier_parallel(server, configFile, curve, clientPKey, probeEnc);
                str = receiveStr(server);
                if( "Incorrect Commitment" == str){
                    throw "Incorrect Commitment"s;
                }
                cout << str << endl;

                if(resultBool == false){
                    server->writeWithSize("Proof Failure");
                    throw "Proof Failure"s;
                }
                server->writeWithSize("Successful Proof");
                writeCSVNew(out_file, proofStart, false);
                cout << "Successful Proof" << endl;
                writeCSVNew(out_file, localStart, false);
                cout << "--> Step 2: DONE ..." << endl;
                cout << endl;

                cout << "-------------------------------------------" << endl;
                cout << "--> Step 3: Protected individual scores exchange ..." << endl;
                localStart = std::chrono::system_clock::now();

                auto requestedComp = readSpecificCompTemplate(dirUser+userId.str()+"Template.txt", dlog, indexCompVect);
                sendVectComponent(server, requestedComp);
                auto colEncFromTemp = extractColEncFromTemp_parallel(requestedComp);
                auto scoreEncFromTemp = extractScoreEncFromTemp_parallel(requestedComp);
                // auto colEncFromTemp = extractColEncFromTemp(requestedComp);
                // auto scoreEncFromTemp = extractScoreEncFromTemp(requestedComp);

                proofStart = std::chrono::system_clock::now();
                str = receiveStr(server);
                if( "Incorrect Signature" == str){
                    throw "Incorrect Signature"s;
                }
                cout << str << endl;
                writeCSVNew(out_file, proofStart, false);

                auto cipherDecZeroVect = subtractTwoCipherVect_parallel(configFile, curve, clientPKey, colEncFromTemp, probeEnc);
                // auto cipherDecZeroVect = subtractTwoCipherVect(dlog, elgamal, colEncFromTemp, probeEnc);

                proofStart = std::chrono::system_clock::now();
                resultBool = niZKMultiANDDecZeroVerifier_parallel(server, configFile, curve, clientPKey, cipherDecZeroVect);
                if(resultBool == false){
                    throw "Proof Failure"s;
                }
                cout << "Successful Proof" << endl;
                writeCSVNew(out_file, proofStart, false);

                writeCSVNew(out_file, localStart, false);
                cout << "--> Step 3: DONE ..." << endl;
                cout << endl;

                cout << "-------------------------------------------" << endl;
                cout << "--> Step 4: Comparison of the similarity score with the threshold set ..." << endl;
                localStart = std::chrono::system_clock::now();

                auto finalScore = multiplyCiphersOfVect(elgamalTH, scoreEncFromTemp);
                // auto finalScore = multiplyCiphersOfVect_parallel(configFile, curve, jointPubKey, scoreEncFromTemp);
                auto comparisonVect = subtractOneCipherFromCipherVect_parallel(configFile, curve, jointPubKey, thresholdEncSet, finalScore);

                auto blindRVect = generateVectBigintRand_parallel(q, comparisonVect.size());
                auto comparisonVectBlinded = blindVectCipherElGamal_parallel(configFile, curve, comparisonVect, blindRVect);
                auto partialDecBlindedCompVect = partiallyDecryptVectCiphers_parallel(configFile, curve, jointPubKey, serverPrivKeyTH, comparisonVectBlinded);

                // auto comparisonVect = subtractOneCipherFromCipherVect(dlog, elgamalTH, thresholdEncSet, finalScore);
                // auto blindRVect = generateVectBigintRand(q, comparisonVect.size());
                // auto comparisonVectBlinded = blindVectCipherElGamal(dlog, comparisonVect, blindRVect);
                // auto partialDecBlindedCompVect = partiallyDecryptVectCiphers(elgamalTH, comparisonVectBlinded);

                sendVectElGamalCipher(server, comparisonVectBlinded);
                sendVectGpElt(server, partialDecBlindedCompVect);

                proofStart = std::chrono::system_clock::now();
                // resultInt = niZKMultiANDBlindedProver_parallel(server, configFile, curve, jointPubKey, comparisonVect, blindRVect);
                resultInt = niZKMultiANDBlindedProver(server, dlog, elgamalTH, comparisonVect, blindRVect);
                if( resultInt == 0){
                    throw "Proof Failure"s;
                }
                cout << "Successful Proof" << endl;
                writeCSVNew(out_file, proofStart, false);

                proofStart = std::chrono::system_clock::now();
                // resultInt = niZKMultiANDPartialDecProver_parallel(server, configFile, curve, serverPKeyTH, comparisonVectBlinded, s2);
                resultInt = niZKMultiANDPartialDecProver(server, dlog, serverPKeyTH, comparisonVectBlinded, s2);
                if( resultInt == 0){
                    throw "Proof Failure"s;
                }
                cout << "Successful Proof" << endl;
                writeCSVNew(out_file, proofStart, false);

                writeCSVNew(out_file, localStart, false);
                cout << "--> Step 4: DONE ..." << endl;
                cout << endl;

                cout << "-------------------------------------------" << endl;
                cout << "--> Step 5: Verification of the similarity score with the threshold ..." << endl;
                localStart = std::chrono::system_clock::now();

                str = receiveStr(server);
                cout << str << endl;
                writeCSVNew(out_file, localStart, false);

                writeCSVNew(out_file, globalStart, true);
                cout << "--> Step 5: DONE ..." << endl;
                cout << endl;
                cout << "======= Verification END =======" << endl;
            }

        }

    }
    catch (const logic_error& e) {
        out_file.close();
        io_service.stop();
	    t.join();
		cerr << e.what();
	}
    catch (string &e){
        out_file.close();
        io_service.stop();
        t.join();
        cerr << e << endl;
    }

    io_service.stop();
    t.join();

    cout << "END Malicious Server ..." << endl;
    return 0;

}


/* CirKit: A circuit toolkit
 * Copyright (C) 2009-2015  University of Bremen
 * Copyright (C) 2015-2017  EPFL
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

#include "quantified_exact_synthesis.hpp"

#include <cmath>
#include <vector>

#include <boost/assign/std/vector.hpp>
#include <boost/dynamic_bitset.hpp>
#include <boost/pending/integer_log2.hpp>
#include <boost/range/combine.hpp>
#include <boost/range/numeric.hpp>

#include <core/utils/bdd_utils.hpp>
#include <core/utils/range_utils.hpp>
#include <core/utils/timer.hpp>
#include <reversible/gate.hpp>
#include <reversible/functions/add_gates.hpp>
#include <reversible/functions/copy_metadata.hpp>

#include <cuddObj.hh>

using namespace boost::assign;

namespace cirkit
{

/******************************************************************************
 * Private functions                                                          *
 ******************************************************************************/
inline std::vector<BDD> create_variables( Cudd& manager, unsigned count )
{
  return generate_vector<BDD>( count, [&manager]() { return manager.bddVar(); } );
}

std::vector<BDD> perform_gate( Cudd& manager, const std::vector<BDD>& inputs, const std::vector<BDD>& gate, bool negative )
{
  const auto n = inputs.size();

  /* Match? */
  BDD match = manager.bddOne();
  for ( auto input : index( inputs ) )
  {
    const auto pos_ctrl = gate[input.index];
    if ( negative )
    {
      const auto neg_ctrl = gate[n + input.index];
      match &= ( !pos_ctrl | input.value ) & ( !neg_ctrl | !input.value ) & ( !pos_ctrl | !neg_ctrl );
    }
    else
    {
      match &= ( !pos_ctrl | input.value );
    }
  }

  /* Outputs */
  std::vector<BDD> outputs;
  const auto offset = negative ? ( n << 1u ) : n;

  for ( auto i = 0u; i < n; ++i )
  {
    boost::dynamic_bitset<> target( gate.size() - offset, i );
    BDD is_target = manager.bddOne();
    for ( unsigned j = 0u; j < target.size(); ++j )
    {
      is_target &= target[j] ? gate[offset + j] : !gate[offset + j];
    }

    outputs += inputs[i] ^ ( match & is_target );
  }

  return outputs;
}

std::pair<std::vector<BDD>, std::vector<BDD>> bdd_for_function( Cudd& manager, const binary_truth_table& spec, const std::vector<BDD>& inputs )
{
  unsigned n = spec.num_inputs();
  std::vector<BDD> result( n, manager.bddZero() );
  std::vector<BDD> care( n, manager.bddZero() );

  for ( const auto& row : spec )
  {
    BDD cube = manager.bddOne();
    unsigned index = 0u;
    for ( const auto& x : boost::make_iterator_range( row.first ) )
    {
      assert( (bool)x );
      if ( (bool)x )
      {
        cube &= *x ? inputs[index] : !inputs[index];
      }
      ++index;
    }

    index = 0u;
    for ( const auto& y : boost::make_iterator_range( row.second ) )
    {
      if ( (bool)y )
      {
        care[index] |= cube;
        if ( *y )
        {
          result[index] |= cube;
        }
      }
      ++index;
    }
  }

  return std::make_pair( result, care );
}

inline BDD create_result_function( Cudd& manager, const std::vector<BDD>& circ, const std::vector<BDD>& function, const std::vector<BDD>& care )
{
  BDD f = manager.bddOne();

  for ( auto i = 0u; i < (unsigned)circ.size(); ++i )
  {
    f &= ( circ.at( i ) & care.at( i ) ).Xnor( function.at( i ) );
  }

  return f;
}

template<typename T>
void extract_solution( circuit& circ, T * str, unsigned num_gates, unsigned n, unsigned nbits, bool negative, bool verbose )
{
  if ( verbose )
  {
    std::cout << "[i] extract solution for " << num_gates << " gates." << std::endl;
  }

  const auto cbits = negative ? ( n << 1u ) : n;

  for ( auto i = 0u; i < num_gates; ++i )
  {
    auto offset = n + i * ( cbits + nbits );

    /* controls */
    gate::control_container controls;
    for ( auto j = 0u; j < n; ++j )
    {
      if ( str[offset + j] == 1 )
      {
        controls += make_var( j );
        assert( !( negative && str[offset + n + j] == 1 ) );
      }

      if ( negative && str[offset + n + j] == 1 )
      {
        controls += make_var( j, false );
      }
    }

    /* target */
    offset += cbits;
    boost::dynamic_bitset<> target( nbits );
    for ( unsigned j = 0u; j < nbits; ++j )
    {
      target.set( j, str[offset + j] == 1 );
    }

    append_toffoli( circ, controls, target.to_ulong() );
  }
}

/******************************************************************************
 * Public functions                                                           *
 ******************************************************************************/

bool quantified_exact_synthesis( circuit& circ, const binary_truth_table& spec, properties::ptr settings, properties::ptr statistics )
{
  const auto max_depth     = get( settings, "max_depth",     20u );
  const auto negative      = get( settings, "negative",      false );
  const auto all_solutions = get( settings, "all_solutions", false );
  const auto verbose       = get( settings, "verbose",       false );

  properties_timer t( statistics );

  const auto n = spec.num_inputs();
  const auto nbits = (unsigned)ceil( log( n ) / log( 2.0) );
  const auto cbits = negative ? ( n << 1u ) : n;

  circ.set_lines( n );

  Cudd manager;

  /* input variables and gate variables */
  const auto xs = create_variables( manager, n );
  const BDD xscube = make_cube( manager, xs );

  /* specification */
  std::vector<BDD> function, care;
  std::tie( function, care ) = bdd_for_function( manager, spec, xs );

  /* find circuit */
  auto current    = xs;
  auto gate_count = 0u;
  auto result     = false;

  do
  {
    if ( verbose )
    {
      std::cout << "[i] check for depth " << gate_count << std::endl;
    }

    auto f = create_result_function( manager, current, function, care ).UnivAbstract( xscube );

    if ( f != manager.bddZero() )
    {
      const auto num_vars = n + gate_count * ( cbits + nbits );
      char * str = new char[num_vars];
      f.PickOneCube( str );
      extract_solution( circ, str, gate_count, n, nbits, negative, verbose );
      delete[] str;

      //set( statistics, "num_circuits", (unsigned)( f.CountMinterm( num_vars ) / ( 1u << n ) ) );

      if ( all_solutions )
      {
        std::vector<circuit> solutions;

        DdGen *gen;
        CUDD_VALUE_TYPE value;
        int * cube;
        Cudd_ForeachCube( manager.getManager(), f.getNode(), gen, cube, value )
        {
          circuit sol_circ( n );
          copy_metadata( spec, sol_circ );
          extract_solution( sol_circ, cube, gate_count, n, nbits, negative, verbose );
          solutions += sol_circ;
        }

        set( statistics, "solutions", solutions );
      }

      result = true;
    }
    else
    {
      /* extend circuit */
      auto gate = create_variables( manager, cbits + nbits );
      current = perform_gate( manager, current, gate, negative );
    }
  } while ( !result && ++gate_count < max_depth );

  if ( result )
  {
    copy_metadata( spec, circ );
  }
  else
  {
    set_error_message( statistics,
        "Could not find a circuit within the predefined depth." );
  }

  return result;
}

truth_table_synthesis_func quantified_exact_synthesis_func( properties::ptr settings, properties::ptr statistics )
{
  truth_table_synthesis_func f = [&settings, &statistics]( circuit& circ, const binary_truth_table& spec ) {
    return quantified_exact_synthesis( circ, spec, settings, statistics );
  };
  f.init( settings, statistics );
  return f;
}

}

// Local Variables:
// c-basic-offset: 2
// eval: (c-set-offset 'substatement-open 0)
// eval: (c-set-offset 'innamespace 0)
// End:


#include "syanten.hpp"

#include <boost/dll.hpp>
#include <spdlog/spdlog.h>

#include <fstream>

#include "bitutils.hpp"

namespace mahjong
{

SyantenCalculator::SyantenCalculator() { initialize(); }

/**
 * @brief 向聴数を計算する。
 *
 * @param[in] hand 手牌
 * @param[in] type 計算対象の向聴数の種類
 * @return std::tuple<SyantenType, int> (向聴数の種類, 向聴数)
 */
std::tuple<int, int> SyantenCalculator::calc(const Hand &hand, int type)
{
#ifdef CHECK_ARGUMENT
    if (type < 0 || type > 7) {
        spdlog::warn("Invalid type {} passed.", type);
        return -2;
    }
#endif

    std::tuple<int, int> ret = {SyantenType::Null, std::numeric_limits<int>::max()};

    if (type & SyantenType::Normal) {
        int syanten = calc_normal(hand);
        if (syanten < std::get<1>(ret))
            ret = {SyantenType::Normal, syanten};
    }
    if (type & SyantenType::Tiitoi) {
        int syanten = calc_tiitoi(hand);
        if (syanten < std::get<1>(ret))
            ret = {SyantenType::Tiitoi, syanten};
    }
    if (type & SyantenType::Kokusi) {
        int syanten = calc_kokusi(hand);
        if (syanten < std::get<1>(ret))
            ret = {SyantenType::Kokusi, syanten};
    }

    return ret;
}

/**
 * @brief 初期化する。
 *
 * @return 初期化に成功した場合は true、そうでない場合は false を返す。
 */
bool SyantenCalculator::initialize()
{
    boost::filesystem::path exe_path = boost::dll::this_line_location().parent_path();

#ifdef USE_UNORDERED_MAP
    if (s_tbl_.empty()) {
        boost::filesystem::path path = exe_path / "syupai_table.bin";
        std::ifstream ifs(path.string(), std::ios::binary);
        for (size_t i = 0; i < ShuupaiPatternSize; ++i) {
            unsigned int key;
            Pattern pattern;
            ifs.read((char *)&key, sizeof(unsigned int));
            ifs.read((char *)&pattern, sizeof(Pattern));

            s_tbl_[key] = pattern;
        }
    }

    if (z_tbl_.empty()) {
        boost::filesystem::path path = exe_path / "zihai_table.bin";
        std::ifstream ifs(path.string(), std::ios::binary);

        for (size_t i = 0; i < ZihaiPatternSize; ++i) {
            unsigned int key;
            Pattern pattern;
            ifs.read((char *)&key, sizeof(unsigned int));
            ifs.read((char *)&pattern, sizeof(Pattern));

            z_tbl_[key] = pattern;
        }
    }
#else
    if (s_tbl_.empty()) {
        s_tbl_.resize(ShuupaiTableSize);

        boost::filesystem::path path = exe_path / "syupai_table.bin";
        std::ifstream ifs(path.string(), std::ios::binary);
        for (size_t i = 0; i < ShuupaiPatternSize; ++i) {
            unsigned int key;
            ifs.read((char *)&key, sizeof(unsigned int));
            ifs.read((char *)&s_tbl_[key], sizeof(Pattern));
        }
    }

    if (z_tbl_.empty()) {
        z_tbl_.resize(ZihaiTableSize);

        boost::filesystem::path path = exe_path / "zihai_table.bin";
        std::ifstream ifs(path.string(), std::ios::binary);

        for (size_t i = 0; i < ZihaiPatternSize; ++i) {
            unsigned int key;
            ifs.read((char *)&key, sizeof(unsigned int));
            ifs.read((char *)&z_tbl_[key], sizeof(Pattern));
        }
    }
#endif

    return true;
}

/**
 * @brief 通常手の向聴数を計算する。
 *
 * @param[in] hand 手牌
 * @return int 向聴数
 */
int SyantenCalculator::calc_normal(const Hand &hand)
{
    // 制約条件「面子 + 候補 <= 4」で「面子数 * 2 + 候補」の最大値を計算する。
    int n_melds = int(hand.melds.size());
    int n_mentu_base = n_melds + s_tbl_[hand.manzu].n_mentu +
                       s_tbl_[hand.pinzu].n_mentu + s_tbl_[hand.sozu].n_mentu +
                       z_tbl_[hand.zihai].n_mentu;
    int n_kouho_base = s_tbl_[hand.manzu].n_kouho + s_tbl_[hand.pinzu].n_kouho +
                       s_tbl_[hand.sozu].n_kouho + z_tbl_[hand.zihai].n_kouho;

    // 雀頭なし
    int max = n_mentu_base * 2 + std::min(4 - n_mentu_base, n_kouho_base);

    if (s_tbl_[hand.manzu].head) {
        // 萬子の雀頭有り
        int n_mentu = n_mentu_base + s_tbl_[hand.manzu].n_mentu_diff;
        int n_kouho = n_kouho_base + s_tbl_[hand.manzu].n_kouho_diff;
        max = std::max(max, n_mentu * 2 + std::min(4 - n_mentu, n_kouho) + 1);
    }

    if (s_tbl_[hand.pinzu].head) {
        // 筒子の雀頭有り
        int n_mentu = n_mentu_base + s_tbl_[hand.pinzu].n_mentu_diff;
        int n_kouho = n_kouho_base + s_tbl_[hand.pinzu].n_kouho_diff;
        max = std::max(max, n_mentu * 2 + std::min(4 - n_mentu, n_kouho) + 1);
    }

    if (s_tbl_[hand.sozu].head) {
        // 索子の雀頭有り
        int n_mentu = n_mentu_base + s_tbl_[hand.sozu].n_mentu_diff;
        int n_kouho = n_kouho_base + s_tbl_[hand.sozu].n_kouho_diff;
        max = std::max(max, n_mentu * 2 + std::min(4 - n_mentu, n_kouho) + 1);
    }

    if (z_tbl_[hand.zihai].head) {
        // 字牌の雀頭有り
        int n_mentu = n_mentu_base + z_tbl_[hand.zihai].n_mentu_diff;
        int n_kouho = n_kouho_base + z_tbl_[hand.zihai].n_kouho_diff;
        max = std::max(max, n_mentu * 2 + std::min(4 - n_mentu, n_kouho) + 1);
    }

    return 8 - max;
}

/**
 * @brief 七対子手の向聴数を計算する。
 *
 * @param[in] hand 手牌
 * @return int 向聴数
 */
int SyantenCalculator::calc_tiitoi(const Hand &hand)
{
    // 牌の種類 (1枚以上の牌) を数える。
    int n_types = s_tbl_[hand.manzu].n_ge1 + s_tbl_[hand.pinzu].n_ge1 +
                  s_tbl_[hand.sozu].n_ge1 + z_tbl_[hand.zihai].n_ge1;
    // 対子の数 (2枚以上の牌) を数える。
    int n_toitu = s_tbl_[hand.manzu].n_ge2 + s_tbl_[hand.pinzu].n_ge2 +
                  s_tbl_[hand.sozu].n_ge2 + z_tbl_[hand.zihai].n_ge2;

    int syanten = 6 - n_toitu;
    if (n_types < 7)
        syanten += 7 - n_types; // 4枚持ちを考慮

    return syanten;
}

/**
 * @brief 国士無双の向聴数を計算する。
 *
 * @param[in] hand 手牌
 * @return int 向聴数
 */
int SyantenCalculator::calc_kokusi(const Hand &hand)
{
    // 老頭牌を抽出する。
    int manzu19 = hand.manzu & Bit::RotohaiMask;
    int pinzu19 = hand.pinzu & Bit::RotohaiMask;
    int sozu19 = hand.sozu & Bit::RotohaiMask;

    // 幺九牌の種類 (1枚以上の牌) を数える。
    int n_yaotyuhai = s_tbl_[manzu19].n_ge1 + s_tbl_[pinzu19].n_ge1 +
                      s_tbl_[sozu19].n_ge1 + z_tbl_[hand.zihai].n_ge1;

    // 幺九牌の対子があるかどうか
    int toitu = ((manzu19 & 0b110'000'000'000'000'000'000'000'110) |
                 (pinzu19 & 0b110'000'000'000'000'000'000'000'110) |
                 (sozu19 & 0b110'000'000'000'000'000'000'000'110) |
                 (hand.zihai & 0b110'110'110'110'110'110'110)) > 0;

    return 13 - toitu - n_yaotyuhai;
}

#ifdef USE_UNORDERED_MAP
std::unordered_map<unsigned int, SyantenCalculator::Pattern> SyantenCalculator::s_tbl_;
std::unordered_map<unsigned int, SyantenCalculator::Pattern> SyantenCalculator::z_tbl_;
#else
std::vector<SyantenCalculator::Pattern> SyantenCalculator::s_tbl_;
std::vector<SyantenCalculator::Pattern> SyantenCalculator::z_tbl_;
#endif

static SyantenCalculator inst;

} // namespace mahjong


#include "derivebinaryfeat.h"
#include <math.h>
#include <fstream>
#include <boost/lexical_cast.hpp>

cBinaryFeat::cBinaryFeat(sTrainData* data, sParams* params)
{
	m_TrainData = data;
	m_Params = params;
}

cv::Mat_<int> cBinaryFeat::DerivBinaryfeat(sRandomForest *randf, int stage)
{
	if (!randf || !randf->__RF){
		cv::Mat_<int> mat;
		return mat;
	}

	sRF** rf = randf->__RF;

	cv::Mat_<double>* __feats;
	cv::Mat_<double>* __threshs;
	cv::Mat_<int>*    __isleaf;
	cv::Mat_<int>*    __cnodes;

	try{
		__feats   = new cv::Mat_<double>[m_TrainData->__numlandmarks];
		__threshs = new cv::Mat_<double>[m_TrainData->__numlandmarks];
		__isleaf  = new cv::Mat_<int>[m_TrainData->__numlandmarks];
		__cnodes  = new cv::Mat_<int>[m_TrainData->__numlandmarks];
	}catch (std::exception e){
		std::cout << e.what() << std::endl;
	}

	for (int l=0; l < m_TrainData->__numlandmarks; l++){
		int num_rfnodes = 0;

		for (int t = 0; t < m_Params->__max_numtrees; t++){
			num_rfnodes += rf[l][t].__num_nodes;
		}

		int low = 0, high = 0;

		__feats[l] = cv::Mat::zeros(num_rfnodes, 4, CV_64FC1);
		__threshs[l] = cv::Mat::zeros(num_rfnodes, 1, CV_64FC1);
		__isleaf[l] = cv::Mat::zeros(num_rfnodes, 1, CV_32SC1);
		__cnodes[l] = cv::Mat::zeros(num_rfnodes, 2, CV_32SC1);

		for (int t = 0; t < m_Params->__max_numtrees; t++){
			high = low + m_Params->__max_nodes;
			rf[l][t].__feat.copyTo(__feats[l].rowRange(low,high));
			rf[l][t].__thresh.copyTo(__threshs[l].rowRange(low, high));
			rf[l][t].__isleafnode.copyTo(__isleaf[l].rowRange(low, high));
			rf[l][t].__cnodes.copyTo(__cnodes[l].rowRange(low, high));
			low = high;
		}

	}

	cv::Mat_<int> binfeatures;

	for (int i=0; i < m_TrainData->__dbsizes; i++){
		cv::Mat_<int>* binfeature_lmarks = new cv::Mat_<int>[m_TrainData->__numlandmarks];
		cv::Mat_<int>  num_leafnodes = cv::Mat::zeros(1, m_TrainData->__numlandmarks, CV_32SC1);

		for (int l =0; l < m_TrainData->__numlandmarks; l++){
			binfeature_lmarks[l] = __lbf_fast(rf[l], __feats[l], __threshs[l], __isleaf[l], __cnodes[l], &(m_TrainData->__data[i]), stage,l);
			num_leafnodes(0, l) = binfeature_lmarks[l].cols;
		}

		int num_cols = (int)cv::sum(num_leafnodes).val[0];

		cv::Mat_<int> binfeature_alllmarks = cv::Mat::zeros(1, num_cols, CV_32SC1);
		int low = 0, high = 0;

		for (int l = 0; l < m_TrainData->__numlandmarks; l++){
			high = low + binfeature_lmarks[l].cols;
			binfeature_lmarks[l].copyTo(binfeature_alllmarks.colRange(low, high));
			low = high;
		}
		binfeatures.push_back(binfeature_alllmarks);

		delete[] binfeature_lmarks;
	}

	delete[] __feats;
	delete[] __threshs;
	delete[] __isleaf;
	delete[] __cnodes;

	return binfeatures;
}

cv::Mat_<int> cBinaryFeat::__lbf_fast(const sRF*rf, const cv::Mat_<double>&feat, const cv::Mat_<double>&threshs, \
	const cv::Mat_<int>& isleaf, const cv::Mat_<int>& cnode, const sData* data, int stage, int landID)
{
	if (!data || !rf){
		cv::Mat_<int> m;
		return m;
	}

	int width  = data->__width;
	int height = data->__height;

	cv::Mat_<double> anglepairs  = feat.colRange(0, 2);
	cv::Mat_<double> radiuspairs = feat.colRange(2, 4);

	cv::Mat_<double> pixel_a_x_imgcoord,pixel_a_y_imgcoord,pixel_b_x_imgcoord, pixel_b_y_imgcoord;

	cv::Mat_<double> angles_cos = cv::Mat::zeros(anglepairs.rows, 2, CV_64FC1);
	cv::Mat_<double> angles_sin = cv::Mat::zeros(anglepairs.rows, 2, CV_64FC1);

	for (int i = 0; i < radiuspairs.rows; i++){
		angles_cos(i, 0) = cos(anglepairs(i, 0));
		angles_cos(i, 1) = cos(anglepairs(i, 1));
		angles_sin(i, 0) = sin(anglepairs(i, 0));
		angles_sin(i, 1) = sin(anglepairs(i, 1));
	}

	cv::multiply(angles_cos.col(0), radiuspairs.col(0), pixel_a_x_imgcoord);
	cv::multiply(angles_sin.col(0), radiuspairs.col(0), pixel_a_y_imgcoord);
	pixel_a_x_imgcoord *= (m_Params->__max_raio_radius[stage] * data->__intermediate_bboxes[stage].width);
	pixel_a_y_imgcoord *= (m_Params->__max_raio_radius[stage] * data->__intermediate_bboxes[stage].height);

	cv::multiply(angles_cos.col(1), radiuspairs.col(1), pixel_b_x_imgcoord);
	cv::multiply(angles_sin.col(1), radiuspairs.col(1), pixel_b_y_imgcoord);
	pixel_b_x_imgcoord *= (m_Params->__max_raio_radius[stage] * data->__intermediate_bboxes[stage].width);
	pixel_b_y_imgcoord *= (m_Params->__max_raio_radius[stage] * data->__intermediate_bboxes[stage].height);

	//pixel_a_x_imgcoord = pixel_a_x_imgcoord * data->__tf2meanshape.at<double>(0, 0) + pixel_a_y_imgcoord * data->__tf2meanshape.at<double>(0, 1) + data->__tf2meanshape.at<double>(0, 2);
	//pixel_a_y_imgcoord = pixel_a_x_imgcoord * data->__tf2meanshape.at<double>(1, 0) + pixel_a_y_imgcoord * data->__tf2meanshape.at<double>(1, 1) + data->__tf2meanshape.at<double>(1, 2);

	//pixel_b_x_imgcoord = pixel_b_x_imgcoord * data->__tf2meanshape.at<double>(0, 0) + pixel_b_y_imgcoord * data->__tf2meanshape.at<double>(0, 1) + data->__tf2meanshape.at<double>(0, 2);
	//pixel_b_y_imgcoord = pixel_b_x_imgcoord * data->__tf2meanshape.at<double>(1, 0) + pixel_b_y_imgcoord * data->__tf2meanshape.at<double>(1, 1) + data->__tf2meanshape.at<double>(1, 2);

	cv::Mat_<double> pixel_a_x, pixel_a_y, pixel_b_x, pixel_b_y;

	pixel_a_x = pixel_a_x_imgcoord + data->__intermediate_shapes[stage](landID,0);
	pixel_a_y = pixel_a_y_imgcoord + data->__intermediate_shapes[stage](landID,1);

	pixel_b_x = pixel_b_x_imgcoord + data->__intermediate_shapes[stage](landID, 0);
	pixel_b_y = pixel_b_y_imgcoord + data->__intermediate_shapes[stage](landID, 1);

	cv::Mat_<double> pdfeats = cv::Mat::zeros(pixel_a_x.rows, 1, CV_64FC1);

	for (int k = 0; k < pixel_a_x.rows; k++){
		int a_x = (int)(pixel_a_x(k, 0) + 0.5);
		int a_y = (int)(pixel_a_y(k, 0) + 0.5);
		int b_x = (int)(pixel_b_x(k, 0) + 0.5);
		int b_y = (int)(pixel_b_y(k, 0) + 0.5);

		a_x = MAX(0, MIN(a_x, width - 1));
		a_y = MAX(0, MIN(a_y, height - 1));
		b_x = MAX(0, MIN(b_x, width - 1));
		b_y = MAX(0, MIN(b_y, height - 1));

		double pixel_v_a = data->__img_gray(cv::Point(a_x, a_y));
		double pixel_v_b = data->__img_gray(cv::Point(b_x, b_y));

		pdfeats(k, 0) = pixel_v_a - pixel_v_b;
	}

	cv::Mat_<int> cind = cv::Mat::zeros(pdfeats.rows, 1, CV_32SC1);

	for (int i = 0; i < cind.rows; i++){
		if (pdfeats(i, 0) >= threshs(i,0)){
			cind(i, 0) = 1;
		}else{
			cind(i, 0) = 0;
		}
	}

	cv::Mat_<int> binfeature = cv::Mat::zeros(1,(int)cv::sum(isleaf).val[0],CV_32SC1);

	int cumnum_nodes = 0;
	int cumnum_leafnodes = 0;

	for (int t=0;  t < m_Params->__max_numtrees; t++){
		int num_nodes = rf[t].__num_nodes;
		int id_cnode = 0;
		while (1){
			if (isleaf(id_cnode + cumnum_nodes)){
				binfeature(0, cumnum_leafnodes + __getindex(rf[t].__id_leafnodes, id_cnode)) = 1;
				cumnum_nodes     = cumnum_nodes + num_nodes;
				cumnum_leafnodes = cumnum_leafnodes + rf[t].__num_leafnodes;
				break;
			}
			id_cnode = cnode(cumnum_nodes + id_cnode, cind(cumnum_nodes + id_cnode,0));
		}
	}
	return binfeature;
}

int cBinaryFeat::__getindex(const cv::Mat_<int>&src, int val)
{
	for (int i =0; i < src.rows; i++){
		if (src(i, 0) == val){
			return i;
		}
	}
	return -1;
}


#include "spellcasting.hpp"

#include <cfloat>

#include <boost/format.hpp>

#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"

#include "../mwworld/containerstore.hpp"
#include "../mwworld/actionteleport.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"

#include "../mwrender/animation.hpp"

#include "magiceffects.hpp"
#include "npcstats.hpp"

namespace
{

    /// Get projectile properties (model, sound and speed) for a spell with the given effects
    /// If \a model is empty, the spell has no ranged effects and should not spawn a projectile.
    void getProjectileInfo (const ESM::EffectList& effects, std::string& model, std::string& sound, float& speed)
    {
        for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
            iter!=effects.mList.end(); ++iter)
        {
            if (iter->mRange != ESM::RT_Target)
                continue;

            const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
                iter->mEffectID);

            model = magicEffect->mBolt;
            if (model.empty())
                model = "VFX_DefaultBolt";

            static const std::string schools[] = {
                "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
            };
            if (!magicEffect->mBoltSound.empty())
                sound = magicEffect->mBoltSound;
            else
                sound = schools[magicEffect->mData.mSchool] + " bolt";

            speed = magicEffect->mData.mSpeed;
            break;
        }
    }

}

namespace MWMechanics
{

    ESM::Skill::SkillEnum spellSchoolToSkill(int school)
    {
        std::map<int, ESM::Skill::SkillEnum> schoolSkillMap; // maps spell school to skill id
        schoolSkillMap[0] = ESM::Skill::Alteration;
        schoolSkillMap[1] = ESM::Skill::Conjuration;
        schoolSkillMap[3] = ESM::Skill::Illusion;
        schoolSkillMap[2] = ESM::Skill::Destruction;
        schoolSkillMap[4] = ESM::Skill::Mysticism;
        schoolSkillMap[5] = ESM::Skill::Restoration;
        assert(schoolSkillMap.find(school) != schoolSkillMap.end());
        return schoolSkillMap[school];
    }

    float getSpellSuccessChance (const ESM::Spell* spell, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
    {
        CreatureStats& stats = actor.getClass().getCreatureStats(actor);

        if (stats.getMagicEffects().get(ESM::MagicEffect::Silence).getMagnitude())
            return 0;

        float y = FLT_MAX;
        float lowestSkill = 0;

        for (std::vector<ESM::ENAMstruct>::const_iterator it = spell->mEffects.mList.begin(); it != spell->mEffects.mList.end(); ++it)
        {
            float x = it->mDuration;
            const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(
                        it->mEffectID);
            if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
                x = std::max(1.f, x);
            x *= 0.1 * magicEffect->mData.mBaseCost;
            x *= 0.5 * (it->mMagnMin + it->mMagnMax);
            x *= it->mArea * 0.05 * magicEffect->mData.mBaseCost;
            if (it->mRange == ESM::RT_Target)
                x *= 1.5;
            static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
                        "fEffectCostMult")->getFloat();
            x *= fEffectCostMult;

            float s = 2 * actor.getClass().getSkill(actor, spellSchoolToSkill(magicEffect->mData.mSchool));
            if (s - x < y)
            {
                y = s - x;
                if (effectiveSchool)
                    *effectiveSchool = magicEffect->mData.mSchool;
                lowestSkill = s;
            }
        }

        if (spell->mData.mType != ESM::Spell::ST_Spell)
            return 100;

        if (spell->mData.mFlags & ESM::Spell::F_Always)
            return 100;

        int castBonus = -stats.getMagicEffects().get(ESM::MagicEffect::Sound).getMagnitude();

        int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
        int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified();

        float castChance = (lowestSkill - spell->mData.mCost + castBonus + 0.2 * actorWillpower + 0.1 * actorLuck) * stats.getFatigueTerm();
        if (MWBase::Environment::get().getWorld()->getGodModeState() && actor.getRefData().getHandle() == "player")
            castChance = 100;

        if (!cap)
            return std::max(0.f, castChance);
        else
            return std::max(0.f, std::min(100.f, castChance));
    }

    float getSpellSuccessChance (const std::string& spellId, const MWWorld::Ptr& actor, int* effectiveSchool, bool cap)
    {
        const ESM::Spell* spell =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
        return getSpellSuccessChance(spell, actor, effectiveSchool, cap);
    }

    int getSpellSchool(const std::string& spellId, const MWWorld::Ptr& actor)
    {
        int school = 0;
        getSpellSuccessChance(spellId, actor, &school);
        return school;
    }

    int getSpellSchool(const ESM::Spell* spell, const MWWorld::Ptr& actor)
    {
        int school = 0;
        getSpellSuccessChance(spell, actor, &school);
        return school;
    }

    bool spellIncreasesSkill(const ESM::Spell *spell)
    {
        if (spell->mData.mType == ESM::Spell::ST_Spell && !(spell->mData.mFlags & ESM::Spell::F_Always))
            return true;
        return false;
    }

    bool spellIncreasesSkill(const std::string &spellId)
    {
        const ESM::Spell* spell =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
        return spellIncreasesSkill(spell);
    }

    float getEffectResistanceAttribute (short effectId, const MagicEffects* actorEffects)
    {
        short resistanceEffect = ESM::MagicEffect::getResistanceEffect(effectId);
        short weaknessEffect = ESM::MagicEffect::getWeaknessEffect(effectId);

        float resistance = 0;
        if (resistanceEffect != -1)
            resistance += actorEffects->get(resistanceEffect).getMagnitude();
        if (weaknessEffect != -1)
            resistance -= actorEffects->get(weaknessEffect).getMagnitude();

        if (effectId == ESM::MagicEffect::FireDamage)
            resistance += actorEffects->get(ESM::MagicEffect::FireShield).getMagnitude();
        if (effectId == ESM::MagicEffect::ShockDamage)
            resistance += actorEffects->get(ESM::MagicEffect::LightningShield).getMagnitude();
        if (effectId == ESM::MagicEffect::FrostDamage)
            resistance += actorEffects->get(ESM::MagicEffect::FrostShield).getMagnitude();

        return resistance;
    }

    float getEffectResistance (short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
                               const ESM::Spell* spell, const MagicEffects* effects)
    {
        const ESM::MagicEffect *magicEffect =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
            effectId);

        const MWMechanics::CreatureStats& stats = actor.getClass().getCreatureStats(actor);
        const MWMechanics::MagicEffects* magicEffects = &stats.getMagicEffects();
        if (effects)
            magicEffects = effects;

        float resisted = 0;
        if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful)
        {
            // Effects with no resistance attribute belonging to them can not be resisted
            if (ESM::MagicEffect::getResistanceEffect(effectId) == -1)
                return 0.f;

            float resistance = getEffectResistanceAttribute(effectId, magicEffects);

            float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified();
            float luck = stats.getAttribute(ESM::Attribute::Luck).getModified();
            float x = (willpower + 0.1 * luck) * stats.getFatigueTerm();

            // This makes spells that are easy to cast harder to resist and vice versa
            float castChance = 100.f;
            if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor())
            {
                castChance = getSpellSuccessChance(spell, caster, NULL, false); // Uncapped casting chance
            }
            if (castChance > 0)
                x *= 50 / castChance;

            float roll = static_cast<float>(std::rand()) / RAND_MAX * 100;
            if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
                roll -= resistance;

            if (x <= roll)
                x = 0;
            else
            {
                if (magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)
                    x = 100;
                else
                    x = roll / std::min(x, 100.f);
            }

            x = std::min(x + resistance, 100.f);

            resisted = x;
        }

        return resisted;
    }

    float getEffectMultiplier(short effectId, const MWWorld::Ptr& actor, const MWWorld::Ptr& caster,
                              const ESM::Spell* spell, const MagicEffects* effects)
    {
        float resistance = getEffectResistance(effectId, actor, caster, spell, effects);
        return 1 - resistance / 100.f;
    }

    /// Check if the given affect can be applied to the target. If \a castByPlayer, emits a message box on failure.
    bool checkEffectTarget (int effectId, const MWWorld::Ptr& target, bool castByPlayer)
    {
        switch (effectId)
        {
            case ESM::MagicEffect::Levitate:
                if (!MWBase::Environment::get().getWorld()->isLevitationEnabled())
                {
                    if (castByPlayer)
                        MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}");
                    return false;
                }
                break;
            case ESM::MagicEffect::Soultrap:
                if (!target.getClass().isNpc() // no messagebox for NPCs
                     && (target.getTypeName() == typeid(ESM::Creature).name() && target.get<ESM::Creature>()->mBase->mData.mSoul == 0))
                {
                    if (castByPlayer)
                        MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInvalidTarget}");
                    return true; // must still apply to get visual effect and have target regard it as attack
                }
                break;
            case ESM::MagicEffect::AlmsiviIntervention:
            case ESM::MagicEffect::DivineIntervention:
            case ESM::MagicEffect::Mark:
            case ESM::MagicEffect::Recall:
                if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled())
                {
                    if (castByPlayer)
                        MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}");
                    return false;
                }
                break;
        }

        return true;
    }

    CastSpell::CastSpell(const MWWorld::Ptr &caster, const MWWorld::Ptr &target)
        : mCaster(caster)
        , mTarget(target)
        , mStack(false)
        , mHitPosition(0,0,0)
        , mAlwaysSucceed(false)
    {
    }

    void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster,
                            const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded)
    {
        if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead())
            return;

        // If none of the effects need to apply, we can early-out
        bool found = false;
        for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
            iter!=effects.mList.end(); ++iter)
        {
            if (iter->mRange != range)
                continue;
            found = true;
        }
        if (!found)
            return;

        const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (mId);
        if (spell && (spell->mData.mType == ESM::Spell::ST_Disease || spell->mData.mType == ESM::Spell::ST_Blight))
        {
            float x = (spell->mData.mType == ESM::Spell::ST_Disease) ?
                        target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::ResistCommonDisease).getMagnitude()
                      : target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::ResistBlightDisease).getMagnitude();

            int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
            if (roll <= x)
            {
                // Fully resisted, show message
                if (target.getRefData().getHandle() == "player")
                    MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}");
                return;
            }
        }

        ESM::EffectList reflectedEffects;
        std::vector<ActiveSpells::ActiveEffect> appliedLastingEffects;
        bool firstAppliedEffect = true;
        bool anyHarmfulEffect = false;

        // HACK: cache target's magic effects here, and add any applied effects to it. Use the cached effects for determining resistance.
        // This is required for Weakness effects in a spell to apply to any subsequent effects in the spell.
        // Otherwise, they'd only apply after the whole spell was added.
        MagicEffects targetEffects;
        if (target.getClass().isActor())
            targetEffects += target.getClass().getCreatureStats(target).getMagicEffects();

        bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player");

        // Try absorbing if it's a spell
        // NOTE: Vanilla does this once per effect source instead of adding the % from all sources together, not sure
        // if that is worth replicating.
        bool absorbed = false;
        if (spell && caster != target && target.getClass().isActor())
        {
            int absorb = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude();
            int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
            absorbed = (roll < absorb);
            if (absorbed)
            {
                const ESM::Static* absorbStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_Absorb");
                MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect(
                            "meshes\\" + absorbStatic->mModel, ESM::MagicEffect::SpellAbsorption, false, "");
                // Magicka is increased by cost of spell
                DynamicStat<float> magicka = target.getClass().getCreatureStats(target).getMagicka();
                magicka.setCurrent(magicka.getCurrent() + spell->mData.mCost);
                target.getClass().getCreatureStats(target).setMagicka(magicka);
            }
        }

        for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
            effectIt!=effects.mList.end(); ++effectIt)
        {
            if (effectIt->mRange != range)
                continue;

            const ESM::MagicEffect *magicEffect =
                MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
                effectIt->mEffectID);

            if (!checkEffectTarget(effectIt->mEffectID, target, castByPlayer))
                continue;

            // If player is healing someone, show the target's HP bar
            if (castByPlayer && target != caster
                    && effectIt->mEffectID == ESM::MagicEffect::RestoreHealth
                    && target.getClass().isActor())
                MWBase::Environment::get().getWindowManager()->setEnemy(target);

            float magnitudeMult = 1;
            if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor())
            {
                anyHarmfulEffect = true;

                if (absorbed) // Absorbed, and we know there was a harmful effect (figuring that out is the only reason we are in this loop)
                    break;

                // If player is attempting to cast a harmful spell, show the target's HP bar
                if (castByPlayer && target != caster)
                    MWBase::Environment::get().getWindowManager()->setEnemy(target);

                // Try reflecting
                if (!reflected && magnitudeMult > 0 && !caster.isEmpty() && caster != target && !(magicEffect->mData.mFlags & ESM::MagicEffect::Unreflectable))
                {
                    int reflect = target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Reflect).getMagnitude();
                    int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
                    bool isReflected = (roll < reflect);
                    if (isReflected)
                    {
                        const ESM::Static* reflectStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_Reflect");
                        MWBase::Environment::get().getWorld()->getAnimation(target)->addEffect(
                                    "meshes\\" + reflectStatic->mModel, ESM::MagicEffect::Reflect, false, "");
                        reflectedEffects.mList.push_back(*effectIt);
                        magnitudeMult = 0;
                    }
                }

                // Try resisting
                if (magnitudeMult > 0 && target.getClass().isActor())
                {
                    magnitudeMult = MWMechanics::getEffectMultiplier(effectIt->mEffectID, target, caster, spell, &targetEffects);
                    if (magnitudeMult == 0)
                    {
                        // Fully resisted, show message
                        if (target.getRefData().getHandle() == "player")
                            MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicPCResisted}");
                        else if (castByPlayer)
                            MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicTargetResisted}");
                    }
                }
            }

            if (magnitudeMult > 0 && !absorbed)
            {
                float random = std::rand() / static_cast<float>(RAND_MAX);
                float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random;
                magnitude *= magnitudeMult;

                bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration);
                if (target.getClass().isActor() && hasDuration)
                {
                    ActiveSpells::ActiveEffect effect;
                    effect.mEffectId = effectIt->mEffectID;
                    effect.mArg = MWMechanics::EffectKey(*effectIt).mArg;
                    effect.mDuration = effectIt->mDuration;
                    effect.mMagnitude = magnitude;

                    targetEffects.add(MWMechanics::EffectKey(*effectIt), MWMechanics::EffectParam(effect.mMagnitude));

                    appliedLastingEffects.push_back(effect);

                    // For absorb effects, also apply the effect to the caster - but with a negative
                    // magnitude, since we're transfering stats from the target to the caster
                    if (!caster.isEmpty() && caster.getClass().isActor())
                    {
                        for (int i=0; i<5; ++i)
                        {
                            if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i)
                            {
                                std::vector<ActiveSpells::ActiveEffect> effects;
                                ActiveSpells::ActiveEffect effect_ = effect;
                                effect_.mMagnitude *= -1;
                                effects.push_back(effect_);
                                // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies
                                caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true,
                                            effects, mSourceName, target.getClass().getCreatureStats(target).getActorId());
                            }
                        }
                    }
                }
                else
                    applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude);

                // HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent.
                // This was probably just done to have the effect visible in the magic menu for a while
                // to notify the player they've been damaged?
                if (effectIt->mEffectID == ESM::MagicEffect::DamageAttribute
                        || effectIt->mEffectID == ESM::MagicEffect::DamageSkill
                        || effectIt->mEffectID == ESM::MagicEffect::RestoreAttribute
                        || effectIt->mEffectID == ESM::MagicEffect::RestoreSkill
                        )
                    applyInstantEffect(target, caster, EffectKey(*effectIt), magnitude);

                if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)
                {
                    // Play sound, only for the first effect
                    if (firstAppliedEffect)
                    {
                        static const std::string schools[] = {
                            "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
                        };

                        MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
                        if(!magicEffect->mHitSound.empty())
                            sndMgr->playSound3D(target, magicEffect->mHitSound, 1.0f, 1.0f);
                        else
                            sndMgr->playSound3D(target, schools[magicEffect->mData.mSchool]+" hit", 1.0f, 1.0f);
                        firstAppliedEffect = false;
                    }

                    // Add VFX
                    const ESM::Static* castStatic;
                    if (!magicEffect->mHit.empty())
                        castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
                    else
                        castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find ("VFX_DefaultHit");

                    // TODO: VFX are no longer active after saving/reloading the game
                    bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
                    // Note: in case of non actor, a free effect should be fine as well
                    MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target);
                    if (anim)
                        anim->addEffect("meshes\\" + castStatic->mModel, magicEffect->mIndex, loop, "");
                }
            }
        }

        if (!exploded)
            MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, mId, mSourceName);

        if (!reflectedEffects.mList.empty())
            inflict(caster, target, reflectedEffects, range, true, exploded);

        if (!appliedLastingEffects.empty())
        {
            int casterActorId = -1;
            if (!caster.isEmpty() && caster.getClass().isActor())
                casterActorId = caster.getClass().getCreatureStats(caster).getActorId();
            target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects,
                                                                                mSourceName, casterActorId);
        }

        // Notify the target actor they've been hit
        if (anyHarmfulEffect && target.getClass().isActor() && target != caster && !caster.isEmpty() && caster.getClass().isActor())
            target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true);
    }

    void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude)
    {
        short effectId = effect.mId;
        if (!target.getClass().isActor())
        {
            if (effectId == ESM::MagicEffect::Lock)
            {
                if (target.getCellRef().getLockLevel() < magnitude) //If the door is not already locked to a higher value, lock it to spell magnitude
                {
                    if (caster.getRefData().getHandle() == "player")
                        MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicLockSuccess}");
                    target.getCellRef().setLockLevel(magnitude);
                }
            }
            else if (effectId == ESM::MagicEffect::Open)
            {
                if (target.getCellRef().getLockLevel() <= magnitude)
                {
                    if (target.getCellRef().getLockLevel() > 0)
                    {
                        MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f);
                        if (!caster.isEmpty() && caster.getClass().isActor())
                            MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target);

                        if (caster.getRefData().getHandle() == "player")
                            MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicOpenSuccess}");
                    }
                    target.getCellRef().setLockLevel(-abs(target.getCellRef().getLockLevel()));
                }
                else
                    MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock Fail", 1.f, 1.f);
            }
        }
        else
        {
            if (effectId == ESM::MagicEffect::DamageAttribute || effectId == ESM::MagicEffect::RestoreAttribute)
            {
                int attribute = effect.mArg;
                AttributeValue value = target.getClass().getCreatureStats(target).getAttribute(attribute);
                if (effectId == ESM::MagicEffect::DamageAttribute)
                    value.damage(magnitude);
                else
                    value.restore(magnitude);
                target.getClass().getCreatureStats(target).setAttribute(attribute, value);
            }
            else if (effectId == ESM::MagicEffect::DamageSkill || effectId == ESM::MagicEffect::RestoreSkill)
            {
                if (target.getTypeName() != typeid(ESM::NPC).name())
                    return;
                int skill = effect.mArg;
                SkillValue& value = target.getClass().getNpcStats(target).getSkill(skill);
                if (effectId == ESM::MagicEffect::DamageSkill)
                    value.damage(magnitude);
                else
                    value.restore(magnitude);
            }

            if (effectId == ESM::MagicEffect::CurePoison)
                target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison);
            else if (effectId == ESM::MagicEffect::CureParalyzation)
                target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Paralyze);
            else if (effectId == ESM::MagicEffect::CureCommonDisease)
                target.getClass().getCreatureStats(target).getSpells().purgeCommonDisease();
            else if (effectId == ESM::MagicEffect::CureBlightDisease)
                target.getClass().getCreatureStats(target).getSpells().purgeBlightDisease();
            else if (effectId == ESM::MagicEffect::CureCorprusDisease)
                target.getClass().getCreatureStats(target).getSpells().purgeCorprusDisease();
            else if (effectId == ESM::MagicEffect::Dispel)
                target.getClass().getCreatureStats(target).getActiveSpells().purgeAll(magnitude);
            else if (effectId == ESM::MagicEffect::RemoveCurse)
                target.getClass().getCreatureStats(target).getSpells().purgeCurses();

            if (target.getRefData().getHandle() != "player")
                return;
            if (!MWBase::Environment::get().getWorld()->isTeleportingEnabled())
                return;

            if (effectId == ESM::MagicEffect::DivineIntervention)
            {
                MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "divinemarker");
            }
            else if (effectId == ESM::MagicEffect::AlmsiviIntervention)
            {
                MWBase::Environment::get().getWorld()->teleportToClosestMarker(target, "templemarker");
            }

            else if (effectId == ESM::MagicEffect::Mark)
            {
                MWBase::Environment::get().getWorld()->getPlayer().markPosition(
                            target.getCell(), target.getRefData().getPosition());
            }
            else if (effectId == ESM::MagicEffect::Recall)
            {
                MWWorld::CellStore* markedCell = NULL;
                ESM::Position markedPosition;

                MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition);
                if (markedCell)
                {
                    MWWorld::ActionTeleport action(markedCell->isExterior() ? "" : markedCell->getCell()->mName,
                                            markedPosition);
                    action.execute(target);
                }
            }
        }
    }

    bool CastSpell::cast(const std::string &id)
    {
        if (const ESM::Spell *spell =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search (id))
            return cast(spell);

        if (const ESM::Potion *potion =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::Potion>().search (id))
            return cast(potion);

        if (const ESM::Ingredient *ingredient =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::Ingredient>().search (id))
            return cast(ingredient);

        throw std::runtime_error("ID type cannot be casted");
    }

    bool CastSpell::cast(const MWWorld::Ptr &item)
    {
        std::string enchantmentName = item.getClass().getEnchantment(item);
        if (enchantmentName.empty())
            throw std::runtime_error("can't cast an item without an enchantment");

        mSourceName = item.getClass().getName(item);
        mId = item.getCellRef().getRefId();

        const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(enchantmentName);

        mStack = (enchantment->mData.mType == ESM::Enchantment::CastOnce);

        // Check if there's enough charge left
        if (enchantment->mData.mType == ESM::Enchantment::WhenUsed || enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
        {
            const float enchantCost = enchantment->mData.mCost;
            int eSkill = mCaster.getClass().getSkill(mCaster, ESM::Skill::Enchant);
            const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10));

            if (item.getCellRef().getEnchantmentCharge() == -1)
                item.getCellRef().setEnchantmentCharge(enchantment->mData.mCharge);

            if (item.getCellRef().getEnchantmentCharge() < castCost)
            {
                if (mCaster.getRefData().getHandle() == "player")
                    MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}");

                // Failure sound
                int school = 0;
                for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (enchantment->mEffects.mList.begin());
                    effectIt!=enchantment->mEffects.mList.end(); ++effectIt)
                {
                    const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
                    school = magicEffect->mData.mSchool;
                    break;
                }
                static const std::string schools[] = {
                    "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
                };
                MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
                sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f);
                return false;
            }
            // Reduce charge
            item.getCellRef().setEnchantmentCharge(item.getCellRef().getEnchantmentCharge() - castCost);
        }

        if (enchantment->mData.mType == ESM::Enchantment::WhenUsed)
        {
            if (mCaster.getRefData().getHandle() == "player")
                mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 1);
        }
        if (enchantment->mData.mType == ESM::Enchantment::CastOnce)
            item.getContainerStore()->remove(item, 1, mCaster);
        else if (enchantment->mData.mType != ESM::Enchantment::WhenStrikes)
        {
            if (mCaster.getRefData().getHandle() == "player")
            {
                mCaster.getClass().skillUsageSucceeded (mCaster, ESM::Skill::Enchant, 3);
            }
        }

        inflict(mCaster, mCaster, enchantment->mEffects, ESM::RT_Self);

        if (!mTarget.isEmpty())
        {
            if (!mTarget.getClass().isActor() || !mTarget.getClass().getCreatureStats(mTarget).isDead())
                inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
        }

        std::string projectileModel;
        std::string sound;
        float speed = 0;
        getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
        if (!projectileModel.empty())
            MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
                                                               false, enchantment->mEffects, mCaster, mSourceName,
                                                                   // Not needed, enchantments can only be cast by actors
                                                                   Ogre::Vector3(1,0,0));

        return true;
    }

    bool CastSpell::cast(const ESM::Potion* potion)
    {
        mSourceName = potion->mName;
        mId = potion->mId;
        mStack = true;

        inflict(mCaster, mCaster, potion->mEffects, ESM::RT_Self);

        return true;
    }

    bool CastSpell::cast(const ESM::Spell* spell)
    {
        mSourceName = spell->mName;
        mId = spell->mId;
        mStack = false;

        const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();

        int school = 0;

        if (mCaster.getClass().isActor() && !mAlwaysSucceed)
        {
            school = getSpellSchool(spell, mCaster);

            CreatureStats& stats = mCaster.getClass().getCreatureStats(mCaster);

            // Reduce fatigue (note that in the vanilla game, both GMSTs are 0, and there's no fatigue loss)
            static const float fFatigueSpellBase = store.get<ESM::GameSetting>().find("fFatigueSpellBase")->getFloat();
            static const float fFatigueSpellMult = store.get<ESM::GameSetting>().find("fFatigueSpellMult")->getFloat();
            DynamicStat<float> fatigue = stats.getFatigue();
            const float normalizedEncumbrance = mCaster.getClass().getNormalizedEncumbrance(mCaster);
            float fatigueLoss = spell->mData.mCost * (fFatigueSpellBase + normalizedEncumbrance * fFatigueSpellMult);
            fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); stats.setFatigue(fatigue);

            bool fail = false;

            // Check success
            int successChance = getSpellSuccessChance(spell, mCaster);
            int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
            if (!fail && roll >= successChance)
            {
                if (mCaster.getRefData().getHandle() == "player")
                    MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicSkillFail}");
                fail = true;
            }

            if (fail)
            {
                // Failure sound
                static const std::string schools[] = {
                    "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
                };

                MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
                sndMgr->playSound3D(mCaster, "Spell Failure " + schools[school], 1.0f, 1.0f);
                return false;
            }
        }

        if (mCaster.getRefData().getHandle() == "player" && spellIncreasesSkill(spell))
            mCaster.getClass().skillUsageSucceeded(mCaster,
                spellSchoolToSkill(school), 0);

        inflict(mCaster, mCaster, spell->mEffects, ESM::RT_Self);

        if (!mTarget.isEmpty())
        {
            if (!mTarget.getClass().isActor() || !mTarget.getClass().getCreatureStats(mTarget).isDead())
            {
                inflict(mTarget, mCaster, spell->mEffects, ESM::RT_Touch);
            }
        }

        std::string projectileModel;
        std::string sound;
        float speed = 0;
        getProjectileInfo(spell->mEffects, projectileModel, sound, speed);
        if (!projectileModel.empty())
        {
            Ogre::Vector3 fallbackDirection (0,1,0);
            // Fall back to a "caster to target" direction if we have no other means of determining it
            // (e.g. when cast by a non-actor)
            if (!mTarget.isEmpty())
                fallbackDirection =
                   Ogre::Vector3(mTarget.getRefData().getPosition().pos)-
                   Ogre::Vector3(mCaster.getRefData().getPosition().pos);

            MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
                       false, spell->mEffects, mCaster, mSourceName, fallbackDirection);
        }

        return true;
    }

    bool CastSpell::cast (const ESM::Ingredient* ingredient)
    {
        mId = ingredient->mId;
        mStack = true;
        mSourceName = ingredient->mName;

        ESM::ENAMstruct effect;
        effect.mEffectID = ingredient->mData.mEffectID[0];
        effect.mSkill = ingredient->mData.mSkills[0];
        effect.mAttribute = ingredient->mData.mAttributes[0];
        effect.mRange = ESM::RT_Self;
        effect.mArea = 0;

        const ESM::MagicEffect *magicEffect =
            MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
            effect.mEffectID);

        const MWMechanics::NpcStats& npcStats = mCaster.getClass().getNpcStats(mCaster);
        const MWMechanics::CreatureStats& creatureStats = mCaster.getClass().getCreatureStats(mCaster);

        float x = (npcStats.getSkill (ESM::Skill::Alchemy).getModified() +
                    0.2 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()
                    + 0.1 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())
                    * creatureStats.getFatigueTerm();

        int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
        if (roll > x)
        {
            // "X has no effect on you"
            std::string message = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage50")->getString();
            message = boost::str(boost::format(message) % ingredient->mName);
            MWBase::Environment::get().getWindowManager()->messageBox(message);
            return false;
        }

        float magnitude = 0;
        float y = roll / std::min(x, 100.f);
        y *= 0.25 * x;
        if (magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)
            effect.mDuration = int(y);
        else
            effect.mDuration = 1;
        if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
        {
            if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
                magnitude = int((0.05 * y) / (0.1 * magicEffect->mData.mBaseCost));
            else
                magnitude = int(y / (0.1 * magicEffect->mData.mBaseCost));
            magnitude = std::max(1.f, magnitude);
        }
        else
            magnitude = 1;

        effect.mMagnMax = magnitude;
        effect.mMagnMin = magnitude;

        ESM::EffectList effects;
        effects.mList.push_back(effect);

        inflict(mCaster, mCaster, effects, ESM::RT_Self);

        return true;
    }
}


//---------------------------------------------------------------------------//
// Copyright (c) 2018-2021 Mikhail Komarov <nemo@nil.foundation>
// Copyright (c) 2020-2021 Nikita Kaskov <nbering@nil.foundation>
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//---------------------------------------------------------------------------//

#define BOOST_TEST_MODULE ram_ppzksnark_test

#include <boost/test/unit_test.hpp>

#include <algorithm>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

#include "ram_examples.hpp"
#include "run_ram_ppzksnark.hpp"

using namespace nil::crypto3::zk::snark;

template<typename CurveType>
void test_ram_ppzksnark(const std::size_t w,
                        const std::size_t k,
                        const std::size_t program_size,
                        const std::size_t input_size,
                        const std::size_t time_bound) {
    typedef ram_ppzksnark_machine_pp<CurveType> machine_ppT;
    const std::size_t boot_trace_size_bound = program_size + input_size;
    const bool satisfiable = true;

    const ram_ppzksnark_architecture_params<CurveType> ap(w, k);
    const ram_example<machine_ppT> example =
        gen_ram_example_complex<machine_ppT>(ap, boot_trace_size_bound, time_bound, satisfiable);

    const bool bit = run_ram_ppzksnark<CurveType>(example);
    BOOST_CHECK(bit);
}

BOOST_AUTO_TEST_SUITE(ram_ppzksnark_test_suite)

BOOST_AUTO_TEST_CASE(ram_ppzksnark_test) {
    const std::size_t program_size = 100;
    const std::size_t input_size = 2;
    const std::size_t time_bound = 20;

    // 16-bit TinyRAM with 16 registers
    test_ram_ppzksnark<default_ram_ppzksnark_pp>(16, 16, program_size, input_size, time_bound);

    // 32-bit TinyRAM with 16 registers
    test_ram_ppzksnark<default_ram_ppzksnark_pp>(32, 16, program_size, input_size, time_bound);
}

BOOST_AUTO_TEST_SUITE_END()


#include <boost/geometry/policies/robustness/rescale_policy.hpp>


#include <voxblox/alignment/icp.h>
#include <voxblox/core/layer.h>
#include <voxblox/core/tsdf_map.h>
#include <voxblox/core/voxel.h>
#include <voxblox/integrator/tsdf_integrator.h>
#include <voxblox/io/layer_io.h>
#include <voxblox/io/mesh_ply.h>
#include <voxblox/mesh/mesh_integrator.h>
#include <voxblox/mesh/mesh_layer.h>
#include <voxblox/utils/color_maps.h>
#include <voxblox/utils/evaluation_utils.h>
#include <voxblox/utils/layer_utils.h>

#include <pcl/conversions.h>
#include <pcl/filters/filter.h>
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>

#include <Eigen/Core>
#include <Eigen/Geometry>

DECLARE_bool(alsologtostderr);
DECLARE_bool(logtostderr);
DECLARE_int32(v);

// A tool that uses voxblox to fuse point clouds in camera coordinates
// to a mesh. The input is index file, having a list of camera to world
// transforms and point clouds. The point cloud is in .pcd format,
// as output by pc_filter.

// TODO(oalexan1): Make the interface more friendly.

// TODO(oalexan1): Figure out how to save and load the
// the voxel grid which creates the mesh and how to merge
// such grids. This will make it possible to do most processing
// in parallel.

// TODO(oalexan1): See if loading .pcd files in binary would make
// the processing faster.

// TODO(oalexan1): See if the clouds can be loaded in
// world coordinates rather than camera coordinates.

// TODO(oalexan1): See if there is no need to include inf values when
// creating the pcd with pc_filter, as those are skipped here anyway.

using namespace voxblox;  // NOLINT

/// Check if all coordinates in the PCL point are finite.
template <typename PCLPoint>
inline bool isPointFinite(const PCLPoint& point) {
  return std::isfinite(point.x) && std::isfinite(point.y) &&
         std::isfinite(point.z);
}

// Given a pcl::PointNormal value, use its normal_x to store a grayscale color.
// There does not seem to be a more appropriate structure.
inline Color convertColor(const pcl::PointNormal& point,
                          const std::shared_ptr<ColorMap>& color_map) {
  return color_map->colorLookup(point.normal_x);
}

/// Parse a point cloud having x, y, z, color, and weight
/// from a pcl::PointNormal structure.
/// normal_x is the color and normal_y is the weight.
/// normal_z and the curvature are ignored.
inline void convertPointCloudWithWeight(
    const pcl::PointCloud<pcl::PointNormal >& pointcloud_pcl,
    const std::shared_ptr<ColorMap>         & color_map,
    Pointcloud                              * points_C,
    Colors                                  * colors,
    std::vector<float>                      * weights) {
  points_C->reserve(pointcloud_pcl.size());
  colors->reserve(pointcloud_pcl.size());
  weights->reserve(pointcloud_pcl.size());
  points_C->clear();
  colors->clear();
  weights->clear();

  for (size_t i = 0; i < pointcloud_pcl.points.size(); ++i) {
    if (!isPointFinite(pointcloud_pcl.points[i])) {
      continue;
    }

    // Read xyz
    points_C->push_back(Point(pointcloud_pcl.points[i].x,
                              pointcloud_pcl.points[i].y,
                              pointcloud_pcl.points[i].z));

    // Read the color
    colors->emplace_back(convertColor(pointcloud_pcl.points[i], color_map));

    // read the weight
    weights->push_back(pointcloud_pcl.points[i].normal_y);
  }
}

// Read an affine matrix with double values
void readAffine(Eigen::Affine3d & T, std::string const& filename) {
  Eigen::MatrixXd M(4, 4);

  std::ifstream ifs(filename.c_str(), std::ifstream::in);
  for (int row = 0; row < 4; row++){
    for (int col = 0; col < 4; col++){
      double val;
      if ( !(ifs >> val) ) {
        LOG(FATAL) << "Could not read a 4x4 matrix from: " << filename;
        return;
      }
      M(row, col) = val;
    }
  }

  T.linear()      = M.block<3, 3>(0, 0);
  T.translation() = M.block<3,1>(0,3);
}

class BatchSdfIntegrator {
 public:
  BatchSdfIntegrator(){}

  void Run(std::string const& index_file, std::string const& out_cloud,
           double max_ray_length_m, double voxel_size,
           std::string const& integrator, int beg, int end){

    voxel_size_ = voxel_size;
    voxels_per_side_ = 16;
    block_size_ = voxels_per_side_ * voxel_size_;

    // The grid will exist until this far away from the surface in the normal
    // direction to it.
    truncation_distance_ = 20 * voxel_size_;

    double intensity_max_value = 256.0;

    TsdfIntegratorBase::Config config;
    config.default_truncation_distance = truncation_distance_;
    config.max_ray_length_m = max_ray_length_m;
    config.integrator_threads = 16;
    config.voxel_carving_enabled = false;

    // The default weight of 1.0e+4 seemed too small. But care here,
    // as doing weighted averages with floats can result in loss of precision.
    config.max_weight = 1.0e+5;

    std::cout << "Index file:          " << index_file << std::endl;
    std::cout << "Output cloud:        " << out_cloud << std::endl;
    std::cout << "Voxel size:          " << voxel_size_ << std::endl;
    std::cout << "Voxels per side:     " << voxels_per_side_ << std::endl;
    std::cout << "Block size           " << block_size_ << std::endl;
    std::cout << "Truncation distance: " << truncation_distance_ << std::endl;
    std::cout << "Intensity max value: " << intensity_max_value << std::endl;
    std::cout << "Integrator:          " << integrator << std::endl;

    std::cout << config.print() << std::endl;

    // Simple integrator
    Layer<TsdfVoxel> simple_layer(voxel_size_, voxels_per_side_);
    SimpleTsdfIntegrator simple_integrator(config, &simple_layer);

    // Merged integrator
    Layer<TsdfVoxel> merged_layer(voxel_size_, voxels_per_side_);
    MergedTsdfIntegrator merged_integrator(config, &merged_layer);

    // Fast integrator
    Layer<TsdfVoxel> fast_layer(voxel_size_, voxels_per_side_);
    FastTsdfIntegrator fast_integrator(config, &fast_layer);

    mesh_layer_.reset(new MeshLayer(block_size_));
    color_map_.reset(new GrayscaleColorMap());
    color_map_->setMaxValue(intensity_max_value);

    MeshIntegratorConfig mesh_config;
    if (integrator == "simple")
      mesh_integrator_.reset(new MeshIntegrator<TsdfVoxel>(mesh_config,
                                                           &simple_layer,
                                                           mesh_layer_.get()));
    else if (integrator == "merged")
      mesh_integrator_.reset(new MeshIntegrator<TsdfVoxel>(mesh_config,
                                                           &merged_layer,
                                                           mesh_layer_.get()));
    else if (integrator == "fast")
      mesh_integrator_.reset(new MeshIntegrator<TsdfVoxel>(mesh_config,
                                                           &fast_layer,
                                                           mesh_layer_.get()));
    else {
      std::cout << "Unknown integrator: " << integrator << std::endl;
      exit(1);
    }

    std::vector<std::string> clouds, poses;
    std::cout << "Reading: " << index_file << std::endl;

    std::ifstream ifs(index_file.c_str());
    std::string pose_file, cloud_file;
    while (ifs >> pose_file >> cloud_file){
      poses.push_back(pose_file);
      clouds.push_back(cloud_file);
    }

    for (int i = 0; i < static_cast<int>(clouds.size()); i++) {

      if (i < beg || i >= end)
        continue;

      Pointcloud points_C;
      Colors colors;
      std::vector<float> weights;

      // Load the cloud. x, y, z are the coordinates, normal_x is the color, normal_y is
      // the weight. Invalid coordinates are inf, inf, inf.
      // TODO(oalexan1): This needs to be documented somewhere.
      pcl::PointCloud<pcl::PointNormal> pointcloud_pcl;
      if (pcl::io::loadPCDFile<pcl::PointNormal> (clouds[i], pointcloud_pcl) == -1) {
        std::cout << "Could not read: " << clouds[i] << std::endl;
        return;
      }

      std::cout << "Processing: " << clouds[i] << std::endl;
      //std::cout << "Loaded " << pointcloud_pcl.width * pointcloud_pcl.height
      //          << " data points\n";

      convertPointCloudWithWeight(pointcloud_pcl, color_map_, &points_C, &colors, &weights);

      // Read the transform from camera that acquired the point clouds to world
      Eigen::Affine3d T;
      readAffine(T, poses[i]);

      Point position = T.translation().cast<float>();
      Quaternion rotation(T.linear().cast<float>());
      Transformation Pose(rotation, position);

      if (integrator == "simple") {
        simple_integrator.pointWeights = weights;
        simple_integrator.integratePointCloud(Pose, points_C, colors);
      }
      else if (integrator == "merged") {
        merged_integrator.pointWeights = weights;
        merged_integrator.integratePointCloud(Pose, points_C, colors);
      }
      else if (integrator == "fast")
        fast_integrator.integratePointCloud(Pose, points_C, colors);
    }

    const bool clear_mesh = true;
    if (clear_mesh) {
      constexpr bool only_mesh_updated_blocks = false;
      constexpr bool clear_updated_flag = true;
      mesh_integrator_->generateMesh(only_mesh_updated_blocks,
                                     clear_updated_flag);

      std::cout << "Writing: " << out_cloud << std::endl;
      const bool success = outputMeshLayerAsPly(out_cloud, *mesh_layer_);
      if (!success) {
        std::cout << "Could not write the mesh." << std::endl;
      }
    }

  }

 protected:

  // Map settings
  FloatingPoint voxel_size_;
  int voxels_per_side_;
  double block_size_;
  FloatingPoint truncation_distance_;

  std::shared_ptr<MeshLayer> mesh_layer_;
  std::shared_ptr<MeshIntegrator<TsdfVoxel>> mesh_integrator_;

  std::shared_ptr<ColorMap> color_map_;
};

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

  if (argc < 6) {
    std::cout << "Must specify the data index file, output cloud name, max ray length, "
              << "voxel size, and integrator type.\n";
    return 1;
  }

  int beg = 0;
  int end = std::numeric_limits<int>::max();
  std::string index_file  = argv[1];
  std::string out_cloud   = argv[2];
  double max_ray_length_m = atof(argv[3]);
  double voxel_size       = atof(argv[4]);
  std::string integrator   = argv[5];
  if (argc >= 8) {
    beg = atoi(argv[6]);
    end = atoi(argv[7]);
  }

  BatchSdfIntegrator B;
  B.Run(index_file, out_cloud, max_ray_length_m, voxel_size, integrator, beg, end);

  return 0;
}


#include <dune/multiscale/test/test_common.hxx>

#include <dune/xt/common/fixed_map.hh>
#include <dune/xt/common/string.hh>
#include <dune/xt/common/ranges.hh>
#include <dune/xt/common/configuration.hh>

#include <dune/multiscale/common/main_init.hh>
#include <dune/multiscale/msfem/algorithm.hh>
#include <dune/multiscale/problems/selector.hh>
#include <dune/xt/common/fixed_map.hh>

#include <boost/filesystem.hpp>

TEST(MsFemCompare, All)
{
  using namespace Dune::Multiscale;
  auto errorsMap = msfem_algorithm();

  EXPECT_GT(DXTC_CONFIG.get("expected_errors.msfem_exact_L2", -1.), errorsMap["msfem_exact_L2"]);
  EXPECT_GT(DXTC_CONFIG.get("expected_errors.msfem_exact_H1s", -1.), errorsMap["msfem_exact_H1s"]);

  auto second_run = msfem_algorithm();
  for (auto error_pair : errorsMap) {
    EXPECT_TRUE(Dune::XT::Common::FloatCmp::eq(error_pair.second, second_run[error_pair.first]))
        << "FloatCmp " << error_pair.second << "\n      != " << second_run[error_pair.first];
  }
}