<commit_before>//===-- examples/clang-interpreter/main.cpp - Clang C Interpreter Example -===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "clang/CodeGen/CodeGenAction.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"

#include "llvm/Module.h"
#include "llvm/Config/config.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Config/config.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TargetSelect.h"
using namespace clang;
using namespace clang::driver;

// This function isn't referenced outside its translation unit, but it
// can't use the "static" keyword because its address is used for
// GetMainExecutable (since some platforms don't support taking the
// address of main, and some platforms can't implement GetMainExecutable
// without being given the address of a function in the main executable).
llvm::sys::Path GetExecutablePath(const char *Argv0) {
  // This just needs to be some symbol in the binary; C++ doesn't
  // allow taking the address of ::main however.
  void *MainAddr = (void*) (intptr_t) GetExecutablePath;
  return llvm::sys::Path::GetMainExecutable(Argv0, MainAddr);
}

static int Execute(llvm::Module *Mod, char * const *envp) {
  llvm::InitializeNativeTarget();

  std::string Error;
  llvm::OwningPtr<llvm::ExecutionEngine> EE(
    llvm::ExecutionEngine::createJIT(Mod, &Error));
  if (!EE) {
    llvm::errs() << "unable to make execution engine: " << Error << "\n";
    return 255;
  }

  llvm::Function *EntryFn = Mod->getFunction("main");
  if (!EntryFn) {
    llvm::errs() << "'main' function not found in module.\n";
    return 255;
  }

  // FIXME: Support passing arguments.
  std::vector<std::string> Args;
  Args.push_back(Mod->getModuleIdentifier());

  return EE->runFunctionAsMain(EntryFn, Args, envp);
}

int main(int argc, const char **argv, char * const *envp) {
  void *MainAddr = (void*) (intptr_t) GetExecutablePath;
  llvm::sys::Path Path = GetExecutablePath(argv[0]);
  TextDiagnosticPrinter *DiagClient =
    new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions());

  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
  DiagnosticsEngine Diags(DiagID, DiagClient);
  Driver TheDriver(Path.str(), llvm::sys::getHostTriple(),
                   "a.out", /*IsProduction=*/false, Diags);
  TheDriver.setTitle("clang interpreter");

  // FIXME: This is a hack to try to force the driver to do something we can
  // recognize. We need to extend the driver library to support this use model
  // (basically, exactly one input, and the operation mode is hard wired).
  llvm::SmallVector<const char *, 16> Args(argv, argv + argc);
  Args.push_back("-fsyntax-only");
  llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args));
  if (!C)
    return 0;

  // FIXME: This is copied from ASTUnit.cpp; simplify and eliminate.

  // We expect to get back exactly one command job, if we didn't something
  // failed. Extract that job from the compilation.
  const driver::JobList &Jobs = C->getJobs();
  if (Jobs.size() != 1 || !isa<driver::Command>(*Jobs.begin())) {
    llvm::SmallString<256> Msg;
    llvm::raw_svector_ostream OS(Msg);
    C->PrintJob(OS, C->getJobs(), "; ", true);
    Diags.Report(diag::err_fe_expected_compiler_job) << OS.str();
    return 1;
  }

  const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
  if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") {
    Diags.Report(diag::err_fe_expected_clang_command);
    return 1;
  }

  // Initialize a compiler invocation object from the clang (-cc1) arguments.
  const driver::ArgStringList &CCArgs = Cmd->getArguments();
  llvm::OwningPtr<CompilerInvocation> CI(new CompilerInvocation);
  CompilerInvocation::CreateFromArgs(*CI,
                                     const_cast<const char **>(CCArgs.data()),
                                     const_cast<const char **>(CCArgs.data()) +
                                       CCArgs.size(),
                                     Diags);

  // Show the invocation, with -v.
  if (CI->getHeaderSearchOpts().Verbose) {
    llvm::errs() << "clang invocation:\n";
    C->PrintJob(llvm::errs(), C->getJobs(), "\n", true);
    llvm::errs() << "\n";
  }

  // FIXME: This is copied from cc1_main.cpp; simplify and eliminate.

  // Create a compiler instance to handle the actual work.
  CompilerInstance Clang;
  Clang.setInvocation(CI.take());

  // Create the compilers actual diagnostics engine.
  Clang.createDiagnostics(int(CCArgs.size()),const_cast<char**>(CCArgs.data()));
  if (!Clang.hasDiagnostics())
    return 1;

  // Infer the builtin include path if unspecified.
  if (Clang.getHeaderSearchOpts().UseBuiltinIncludes &&
      Clang.getHeaderSearchOpts().ResourceDir.empty())
    Clang.getHeaderSearchOpts().ResourceDir =
      CompilerInvocation::GetResourcesPath(argv[0], MainAddr);

  // Create and execute the frontend to generate an LLVM bitcode module.
  llvm::OwningPtr<CodeGenAction> Act(new EmitLLVMOnlyAction());
  if (!Clang.ExecuteAction(*Act))
    return 1;

  int Res = 255;
  if (llvm::Module *Module = Act->takeModule())
    Res = Execute(Module, envp);

  // Shutdown.

  llvm::llvm_shutdown();

  return Res;
}
<commit_msg>Add missing include to clang-interpreter example, to make it work on Windows.  Patch by Dean Pavlekovic.<commit_after>//===-- examples/clang-interpreter/main.cpp - Clang C Interpreter Example -===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "clang/CodeGen/CodeGenAction.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/DiagnosticOptions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"

#include "llvm/Module.h"
#include "llvm/Config/config.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Config/config.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/TargetSelect.h"
using namespace clang;
using namespace clang::driver;

// This function isn't referenced outside its translation unit, but it
// can't use the "static" keyword because its address is used for
// GetMainExecutable (since some platforms don't support taking the
// address of main, and some platforms can't implement GetMainExecutable
// without being given the address of a function in the main executable).
llvm::sys::Path GetExecutablePath(const char *Argv0) {
  // This just needs to be some symbol in the binary; C++ doesn't
  // allow taking the address of ::main however.
  void *MainAddr = (void*) (intptr_t) GetExecutablePath;
  return llvm::sys::Path::GetMainExecutable(Argv0, MainAddr);
}

static int Execute(llvm::Module *Mod, char * const *envp) {
  llvm::InitializeNativeTarget();

  std::string Error;
  llvm::OwningPtr<llvm::ExecutionEngine> EE(
    llvm::ExecutionEngine::createJIT(Mod, &Error));
  if (!EE) {
    llvm::errs() << "unable to make execution engine: " << Error << "\n";
    return 255;
  }

  llvm::Function *EntryFn = Mod->getFunction("main");
  if (!EntryFn) {
    llvm::errs() << "'main' function not found in module.\n";
    return 255;
  }

  // FIXME: Support passing arguments.
  std::vector<std::string> Args;
  Args.push_back(Mod->getModuleIdentifier());

  return EE->runFunctionAsMain(EntryFn, Args, envp);
}

int main(int argc, const char **argv, char * const *envp) {
  void *MainAddr = (void*) (intptr_t) GetExecutablePath;
  llvm::sys::Path Path = GetExecutablePath(argv[0]);
  TextDiagnosticPrinter *DiagClient =
    new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions());

  llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs());
  DiagnosticsEngine Diags(DiagID, DiagClient);
  Driver TheDriver(Path.str(), llvm::sys::getHostTriple(),
                   "a.out", /*IsProduction=*/false, Diags);
  TheDriver.setTitle("clang interpreter");

  // FIXME: This is a hack to try to force the driver to do something we can
  // recognize. We need to extend the driver library to support this use model
  // (basically, exactly one input, and the operation mode is hard wired).
  llvm::SmallVector<const char *, 16> Args(argv, argv + argc);
  Args.push_back("-fsyntax-only");
  llvm::OwningPtr<Compilation> C(TheDriver.BuildCompilation(Args));
  if (!C)
    return 0;

  // FIXME: This is copied from ASTUnit.cpp; simplify and eliminate.

  // We expect to get back exactly one command job, if we didn't something
  // failed. Extract that job from the compilation.
  const driver::JobList &Jobs = C->getJobs();
  if (Jobs.size() != 1 || !isa<driver::Command>(*Jobs.begin())) {
    llvm::SmallString<256> Msg;
    llvm::raw_svector_ostream OS(Msg);
    C->PrintJob(OS, C->getJobs(), "; ", true);
    Diags.Report(diag::err_fe_expected_compiler_job) << OS.str();
    return 1;
  }

  const driver::Command *Cmd = cast<driver::Command>(*Jobs.begin());
  if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") {
    Diags.Report(diag::err_fe_expected_clang_command);
    return 1;
  }

  // Initialize a compiler invocation object from the clang (-cc1) arguments.
  const driver::ArgStringList &CCArgs = Cmd->getArguments();
  llvm::OwningPtr<CompilerInvocation> CI(new CompilerInvocation);
  CompilerInvocation::CreateFromArgs(*CI,
                                     const_cast<const char **>(CCArgs.data()),
                                     const_cast<const char **>(CCArgs.data()) +
                                       CCArgs.size(),
                                     Diags);

  // Show the invocation, with -v.
  if (CI->getHeaderSearchOpts().Verbose) {
    llvm::errs() << "clang invocation:\n";
    C->PrintJob(llvm::errs(), C->getJobs(), "\n", true);
    llvm::errs() << "\n";
  }

  // FIXME: This is copied from cc1_main.cpp; simplify and eliminate.

  // Create a compiler instance to handle the actual work.
  CompilerInstance Clang;
  Clang.setInvocation(CI.take());

  // Create the compilers actual diagnostics engine.
  Clang.createDiagnostics(int(CCArgs.size()),const_cast<char**>(CCArgs.data()));
  if (!Clang.hasDiagnostics())
    return 1;

  // Infer the builtin include path if unspecified.
  if (Clang.getHeaderSearchOpts().UseBuiltinIncludes &&
      Clang.getHeaderSearchOpts().ResourceDir.empty())
    Clang.getHeaderSearchOpts().ResourceDir =
      CompilerInvocation::GetResourcesPath(argv[0], MainAddr);

  // Create and execute the frontend to generate an LLVM bitcode module.
  llvm::OwningPtr<CodeGenAction> Act(new EmitLLVMOnlyAction());
  if (!Clang.ExecuteAction(*Act))
    return 1;

  int Res = 255;
  if (llvm::Module *Module = Act->takeModule())
    Res = Execute(Module, envp);

  // Shutdown.

  llvm::llvm_shutdown();

  return Res;
}
<|endoftext|>


<commit_before>// basic file operations
#include <iostream>
#include <fstream>
#include <algorithm>    // std::max, min
#include <dai/alldai.h>  // Include main libDAI header file
#include <CImg.h>        // This example needs CImg to be installed
#include <map>
#include <dai/utils/timer.h>

using namespace dai;
using namespace std;

Factor createFactorRecommendation(const Var &n1, const Var &n2, Real alpha) {
    VarSet var_set = VarSet(n1, n2);
    Factor fac(var_set);
    map<Var, size_t> state;
    for (size_t i = 0; i < n1.states(); ++i) {
        state[n1] = i;
        for (size_t j = 0; j < n2.states(); ++j) {
            state[n2] = j;
            size_t index = calcLinearState(var_set, state);

            if (i == j) {
                fac.set(index, 0.5 + alpha);
            } else {
                fac.set(index, 0.5 - alpha);
            }
        }
    }
    return fac;
}

FactorGraph example2fg() {
    vector<Var> vars;
    vector<Factor> factors;

    size_t N = 5;
    size_t M = 5;
    Real alpha = 0.1;
    // Reserve memory for the variables
    vars.reserve(N + M);

    // Create a binary variable for each movie/person
    for (size_t i = 0; i < N + M; i++)
        vars.push_back(Var(i, 2));

    factors.push_back(createFactorRecommendation(vars[0], vars[N], alpha));
    factors.push_back(createFactorRecommendation(vars[0], vars[N + 1], alpha));
    factors.push_back(createFactorRecommendation(vars[0], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[0], vars[N + 3], alpha));
    factors.push_back(createFactorRecommendation(vars[1], vars[N + 1], alpha));
    factors.push_back(createFactorRecommendation(vars[1], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[1], vars[N + 3], alpha));
    factors.push_back(createFactorRecommendation(vars[2], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[2], vars[N + 3], alpha));
    factors.push_back(createFactorRecommendation(vars[3], vars[N + 1], alpha));
    factors.push_back(createFactorRecommendation(vars[3], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[3], vars[N + 3], alpha));
    factors.push_back(createFactorRecommendation(vars[4], vars[N + 1], alpha));
    factors.push_back(createFactorRecommendation(vars[4], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[4], vars[N + 3], alpha));
    Factor fac1(vars[2]);
    fac1.set(0, 0.9);
    Factor fac2(vars[3]);
    fac2.set(0, 0.9);
    factors.push_back(fac1);
    factors.push_back(fac2);

    // Create the factor graph out of the variables and factors
    cout << "Creating the factor graph..." << endl;
    return FactorGraph(factors.begin(), factors.end(), vars.begin(), vars.end(), factors.size(), vars.size());
}

double doInference(FactorGraph &fg, string algOpts, size_t maxIter, double tol, vector<double> &m) {
    // Construct inference algorithm
    cout << "Inference algorithm: " << algOpts << endl;
    cout << "Constructing inference algorithm object..." << endl;
    InfAlg *ia = newInfAlgFromString(algOpts, fg);

    // Initialize inference algorithm
    cout << "Initializing inference algorithm..." << endl;
    ia->init();

    // Initialize vector for storing the recommendations
    m = vector<double>(fg.nrVars(), 0.0);

    // maxDiff stores the current convergence level
    double maxDiff = 1.0;

    // Iterate while maximum number of iterations has not been
    // reached and requested convergence level has not been reached
    cout << "Starting inference algorithm..." << endl;
    for (size_t iter = 0; iter < maxIter && maxDiff > tol; iter++) {
        // Set recommendations to beliefs
        for (size_t i = 0; i < fg.nrVars(); i++)
            m[i] = ia->beliefV(i)[0];

        // Perform the requested inference algorithm for only one step
        ia->setMaxIter(iter + 1);
        maxDiff = ia->run();

        // Output progress
        cout << "  Iterations = " << iter << ", maxDiff = " << maxDiff << endl;
    }
    cout << "Finished inference algorithm" << endl;

    // Clean up inference algorithm
    delete ia;

    // Return reached convergence level
    return maxDiff;
}

// vector of users, containing a vector of ratings. Each rating consists of a movie id (first) and the rating (second)
vector<vector<pair<int, int> > > extract_ratings(string file_name) {
    ifstream fin;
    fin.open(file_name.c_str(), ifstream::in);
    vector<vector<pair<int, int> > > ratings;
    int num_entries;
    fin >> num_entries;

    for (int i = 0; i < num_entries; ++i) {
        size_t user;
        int movie, rating;
        long long time;
        fin >> user >> movie >> rating >> time;
        user--;
        //cout << user << " " << movie << " " << rating << endl;
        while (user >= ratings.size()) {
            ratings.push_back(vector<pair<int, int> >());
        }
        ratings[user].push_back(make_pair(movie, rating));
    }
    return ratings;
}

FactorGraph data2fg(const vector<vector<pair<int, int> > > &votings, int user) {
    // We will create a variable for every potential user/movie. We know the number of users, let us estimate the number of movies.
    int num_users = votings.size();
    int num_movies = 0;
    for (size_t i = 0; i < votings.size(); ++i) {
        if (votings[i].size() > 0) {
            num_movies = max(num_movies, votings[i][votings[i].size() - 1].first);
        }
    }
    // We add one to avoid the case where movies start counting at 1, leading to one additional movie.
    num_movies++;

    Real alpha = 0.0001;
    int threshold = 4;
    vector<Var> vars;
    vector<Factor> factors;

    // Reserve memory for the variables
    cout << "Estimated num_users/num_movies: " << num_users << "/" << num_movies << endl;
    vars.reserve(num_users + num_movies);
    // Create a binary variable for each movie/person
    for (size_t i = 0; i < (size_t) (num_users + num_movies); i++)
        vars.push_back(Var(i, 2));

    for (size_t i = 0; i < votings.size(); ++i) {
        for (size_t j = 0; j < votings[i].size(); ++j) {
            if (votings[i][j].second >= threshold) {
                factors.push_back(createFactorRecommendation(vars[i], vars[num_users + votings[i][j].first], alpha));
            }
        }
    }
    cout << "Factors created. Dealing with the user now..." << endl;
    // calculate some metrics for the user.
    int normalization_factor_p = 4;
    double sum = 0;
    double sq_sum = 0;
    for (size_t i = 0; i < votings[user].size(); ++i) {
        sum += votings[user][i].second;
    }
    double mean = sum / votings[user].size();
    for (size_t i = 0; i < votings[user].size(); ++i) {
        sq_sum += pow(votings[user][i].second - mean, 2);
    }
    double stdev = std::sqrt(sq_sum / votings[user].size());

    for (size_t i = 0; i < votings[user].size(); ++i) {
        Factor fac(vars[num_users + votings[user][i].first]);
        double like = max(0.1, min(0.9, 0.5 + (votings[user][i].second - mean) / (stdev * normalization_factor_p)));
        //cout << votings[user][i].second << " to " << like << endl;
        fac.set(0, like);
        factors.push_back(fac);
    }

    // Create the factor graph out of the variables and factors
    cout << "Creating the factor graph..." << endl;
    return FactorGraph(factors.begin(), factors.end(), vars.begin(), vars.end(), factors.size(), vars.size());
}

pair<double, double> getPrecisionAndRecall(const vector<vector<pair<int, int> > > &test_data,
                                           const vector<pair<double, int> > &ratings, int user, size_t N) {
    // get the top predicted elements.
    vector<int> predicted;
    for (size_t i = 0; i < std::min(N, ratings.size()); ++i) {
        predicted.push_back(ratings[i].second);
    }

    vector<int> wanted;
    for (size_t i = 0; i < test_data[user].size(); ++i) {
        if (test_data[user][i].second >= 5) {
            wanted.push_back(test_data[user][i].first);
        }
    }

    // compute the intersection.
    vector<int> v(N);
    sort(predicted.begin(), predicted.end());
    sort(wanted.begin(), wanted.end());
    vector<int>::iterator it = set_intersection(predicted.begin(), predicted.end(), wanted.begin(), wanted.end(),
                                                v.begin());
    v.resize(it - v.begin());

    //std::cout << "The intersection has " << (v.size()) << " elements:\n";
    if (wanted.size() == 0) {
        // dummy element to prevent division by zero.
        wanted.push_back(0);
    }
    return make_pair<double, double>(v.size() / static_cast<double>(N), v.size() / static_cast<double>(wanted.size()));
}

/// Main program
int main(int argc, char **argv) {

    cimg_usage("This example shows how libDAI can be used for a simple recommendation task");
    const char *infname = cimg_option("-method", "BP[updates=SEQMAX,maxiter=100,tol=1e-15,logdomain=0]",
                                      "Inference method in format name[key1=val1,...,keyn=valn]");
    const size_t maxiter = cimg_option("-maxiter", 100, "Maximum number of iterations for inference method");
    const double tol = cimg_option("-tol", 1e-15, "Desired tolerance level for inference method");

    cout << "reading data now..." << endl;
    vector<vector<pair<int, int> > > input_data = extract_ratings("uV2New1.base");
    vector<vector<pair<int, int> > > test_data = extract_ratings("uV2New1.test");
    const int N = 1;
    double p10 = 0;
    double p20 = 0;
    double r10 = 0;
    double r20 = 0;
    Timer timer;
    timer.tic();
    for (int user=0; user<N; ++user) {
        cout << "building factor graph for user " << user << " out of " << N << endl;
        FactorGraph fg = data2fg(input_data, user);

        vector<double> m; // Stores the final recommendations
        cout << "Solving the inference problem...please be patient!" << endl;
        doInference(fg, infname, maxiter, tol, m);
        vector<pair<double, int> > ratings;
        for (size_t i = input_data.size(); i < m.size(); ++i) {
            // push back the negative so we can use the standard sorting.
            ratings.push_back(make_pair<double, int>(-m[i], i - input_data.size() + 1));
        }
        sort(ratings.begin(), ratings.end());
        pair<double, double> pr10 = getPrecisionAndRecall(test_data, ratings, user, 10);
        pair<double, double> pr20 = getPrecisionAndRecall(test_data, ratings, user, 20);

        p10 +=  pr10.first;
        p20 +=  pr20.first;
        r10 +=  pr10.second;
        r20 +=  pr20.second;
        cout << "Precision (N=10): " << pr10.first << endl;
        cout << "Precision (N=20): " << pr20.first << endl;
        cout << "Recall (N=10): " << pr10.second << endl;
        cout << "Recall (N=20): " << pr20.second << endl;
    }
    double elapsed_secs = timer.toc();
    p10 = p10 / static_cast<double>(N);
    p20 = p20 / static_cast<double>(N);
    r10 = r20 / static_cast<double>(N);
    r20 = r20 / static_cast<double>(N);
    cout << "Final estimated:" << endl;
    cout << "Precision (N=10): " << p10 << endl;
    cout << "Precision (N=20): " << p20 << endl;
    cout << "Recall (N=10): " << r10 << endl;
    cout << "Recall (N=20): " << r20 << endl;
    cout << "Time in seconds: " << elapsed_secs << endl;
    return 0;
}

<commit_msg>measure cycles in doInference() AND remove cout messages in inference loop<commit_after>// basic file operations
#include <iostream>
#include <fstream>
#include <algorithm>    // std::max, min
#include <dai/alldai.h>  // Include main libDAI header file
#include <CImg.h>        // This example needs CImg to be installed
#include <map>
#include <dai/utils/timer.h>

using namespace dai;
using namespace std;

Factor createFactorRecommendation(const Var &n1, const Var &n2, Real alpha) {
    VarSet var_set = VarSet(n1, n2);
    Factor fac(var_set);
    map<Var, size_t> state;
    for (size_t i = 0; i < n1.states(); ++i) {
        state[n1] = i;
        for (size_t j = 0; j < n2.states(); ++j) {
            state[n2] = j;
            size_t index = calcLinearState(var_set, state);

            if (i == j) {
                fac.set(index, 0.5 + alpha);
            } else {
                fac.set(index, 0.5 - alpha);
            }
        }
    }
    return fac;
}

FactorGraph example2fg() {
    vector<Var> vars;
    vector<Factor> factors;

    size_t N = 5;
    size_t M = 5;
    Real alpha = 0.1;
    // Reserve memory for the variables
    vars.reserve(N + M);

    // Create a binary variable for each movie/person
    for (size_t i = 0; i < N + M; i++)
        vars.push_back(Var(i, 2));

    factors.push_back(createFactorRecommendation(vars[0], vars[N], alpha));
    factors.push_back(createFactorRecommendation(vars[0], vars[N + 1], alpha));
    factors.push_back(createFactorRecommendation(vars[0], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[0], vars[N + 3], alpha));
    factors.push_back(createFactorRecommendation(vars[1], vars[N + 1], alpha));
    factors.push_back(createFactorRecommendation(vars[1], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[1], vars[N + 3], alpha));
    factors.push_back(createFactorRecommendation(vars[2], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[2], vars[N + 3], alpha));
    factors.push_back(createFactorRecommendation(vars[3], vars[N + 1], alpha));
    factors.push_back(createFactorRecommendation(vars[3], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[3], vars[N + 3], alpha));
    factors.push_back(createFactorRecommendation(vars[4], vars[N + 1], alpha));
    factors.push_back(createFactorRecommendation(vars[4], vars[N + 2], alpha));
    factors.push_back(createFactorRecommendation(vars[4], vars[N + 3], alpha));
    Factor fac1(vars[2]);
    fac1.set(0, 0.9);
    Factor fac2(vars[3]);
    fac2.set(0, 0.9);
    factors.push_back(fac1);
    factors.push_back(fac2);

    // Create the factor graph out of the variables and factors
    cout << "Creating the factor graph..." << endl;
    return FactorGraph(factors.begin(), factors.end(), vars.begin(), vars.end(), factors.size(), vars.size());
}

pair<size_t, double> doInference(FactorGraph &fg, string algOpts, size_t maxIter, double tol, vector<double> &m) {
    InfAlg *ia = newInfAlgFromString(algOpts, fg);

    // Initialize inference algorithm
    //cout << "Initializing inference algorithm..." << endl;
    ia->init();

    // Initialize vector for storing the recommendations
    m = vector<double>(fg.nrVars(), 0.0);

    // maxDiff stores the current convergence level
    double maxDiff = 1.0;

    // Iterate while maximum number of iterations has not been
    // reached and requested convergence level has not been reached
    //cout << "Starting inference algorithm..." << endl;
    size_t iter;
    for (iter = 0; iter < maxIter && maxDiff > tol; iter++) {
        // Set recommendations to beliefs
        for (size_t i = 0; i < fg.nrVars(); i++)
            m[i] = ia->beliefV(i)[0];

        // Perform the requested inference algorithm for only one step
        ia->setMaxIter(iter + 1);
        maxDiff = ia->run();

        // Output progress
        //cout << "  Iterations = " << iter << ", maxDiff = " << maxDiff << endl;
    }
    //cout << "Finished inference algorithm" << endl;

    // Clean up inference algorithm
    delete ia;

    // Return num of iterations and reached convergence level
    return make_pair(++iter, maxDiff);
}

// vector of users, containing a vector of ratings. Each rating consists of a movie id (first) and the rating (second)
vector<vector<pair<int, int> > > extract_ratings(string file_name) {
    ifstream fin;
    fin.open(file_name.c_str(), ifstream::in);
    vector<vector<pair<int, int> > > ratings;
    int num_entries;
    fin >> num_entries;

    for (int i = 0; i < num_entries; ++i) {
        size_t user;
        int movie, rating;
        long long time;
        fin >> user >> movie >> rating >> time;
        user--;
        //cout << user << " " << movie << " " << rating << endl;
        while (user >= ratings.size()) {
            ratings.push_back(vector<pair<int, int> >());
        }
        ratings[user].push_back(make_pair(movie, rating));
    }
    return ratings;
}

FactorGraph data2fg(const vector<vector<pair<int, int> > > &votings, int user) {
    // We will create a variable for every potential user/movie. We know the number of users, let us estimate the number of movies.
    int num_users = votings.size();
    int num_movies = 0;
    for (size_t i = 0; i < votings.size(); ++i) {
        if (votings[i].size() > 0) {
            num_movies = max(num_movies, votings[i][votings[i].size() - 1].first);
        }
    }
    // We add one to avoid the case where movies start counting at 1, leading to one additional movie.
    num_movies++;

    Real alpha = 0.0001;
    int threshold = 4;
    vector<Var> vars;
    vector<Factor> factors;

    // Reserve memory for the variables
    cout << "Estimated num_users/num_movies: " << num_users << "/" << num_movies << endl;
    vars.reserve(num_users + num_movies);
    // Create a binary variable for each movie/person
    for (size_t i = 0; i < (size_t) (num_users + num_movies); i++)
        vars.push_back(Var(i, 2));

    for (size_t i = 0; i < votings.size(); ++i) {
        for (size_t j = 0; j < votings[i].size(); ++j) {
            if (votings[i][j].second >= threshold) {
                factors.push_back(createFactorRecommendation(vars[i], vars[num_users + votings[i][j].first], alpha));
            }
        }
    }
    cout << "Factors created. Dealing with the user now..." << endl;
    // calculate some metrics for the user.
    int normalization_factor_p = 4;
    double sum = 0;
    double sq_sum = 0;
    for (size_t i = 0; i < votings[user].size(); ++i) {
        sum += votings[user][i].second;
    }
    double mean = sum / votings[user].size();
    for (size_t i = 0; i < votings[user].size(); ++i) {
        sq_sum += pow(votings[user][i].second - mean, 2);
    }
    double stdev = std::sqrt(sq_sum / votings[user].size());

    for (size_t i = 0; i < votings[user].size(); ++i) {
        Factor fac(vars[num_users + votings[user][i].first]);
        double like = max(0.1, min(0.9, 0.5 + (votings[user][i].second - mean) / (stdev * normalization_factor_p)));
        //cout << votings[user][i].second << " to " << like << endl;
        fac.set(0, like);
        factors.push_back(fac);
    }

    // Create the factor graph out of the variables and factors
    cout << "Creating the factor graph..." << endl;
    return FactorGraph(factors.begin(), factors.end(), vars.begin(), vars.end(), factors.size(), vars.size());
}

pair<double, double> getPrecisionAndRecall(const vector<vector<pair<int, int> > > &test_data,
                                           const vector<pair<double, int> > &ratings, int user, size_t N) {
    // get the top predicted elements.
    vector<int> predicted;
    for (size_t i = 0; i < std::min(N, ratings.size()); ++i) {
        predicted.push_back(ratings[i].second);
    }

    vector<int> wanted;
    for (size_t i = 0; i < test_data[user].size(); ++i) {
        if (test_data[user][i].second >= 5) {
            wanted.push_back(test_data[user][i].first);
        }
    }

    // compute the intersection.
    vector<int> v(N);
    sort(predicted.begin(), predicted.end());
    sort(wanted.begin(), wanted.end());
    vector<int>::iterator it = set_intersection(predicted.begin(), predicted.end(), wanted.begin(), wanted.end(),
                                                v.begin());
    v.resize(it - v.begin());

    //std::cout << "The intersection has " << (v.size()) << " elements:\n";
    if (wanted.size() == 0) {
        // dummy element to prevent division by zero.
        wanted.push_back(0);
    }
    return make_pair<double, double>(v.size() / static_cast<double>(N), v.size() / static_cast<double>(wanted.size()));
}

/// Main program
int main(int argc, char **argv) {

    cimg_usage("This example shows how libDAI can be used for a simple recommendation task");
    const char *infname = cimg_option("-method", "BP[updates=SEQMAX,maxiter=100,tol=1e-15,logdomain=0]",
                                      "Inference method in format name[key1=val1,...,keyn=valn]");
    const size_t maxiter = cimg_option("-maxiter", 100, "Maximum number of iterations for inference method");
    const double tol = cimg_option("-tol", 1e-15, "Desired tolerance level for inference method");

    cout << "reading data now..." << endl;
    vector<vector<pair<int, int> > > input_data = extract_ratings("uV2New1.base");
    vector<vector<pair<int, int> > > test_data = extract_ratings("uV2New1.test");
    const int N = 1;
    double p10 = 0;
    double p20 = 0;
    double r10 = 0;
    double r20 = 0;
    long measured_cycles = 0;
    Timer timer;
    for (int user=0; user<N; ++user) {
        cout << "building factor graph for user " << user << " out of " << N << endl;
        FactorGraph fg = data2fg(input_data, user);

        vector<double> m; // Stores the final recommendations
        cout << "Inference algorithm: " << infname << endl;
        cout << "Solving the inference problem...please be patient!" << endl;
        cout << "Note: There's no output during the inference. You may have to wait a bit..." << endl;

        timer.tic();
        pair<size_t, double> result = doInference(fg, infname, maxiter, tol, m);
        measured_cycles += timer.toc();

        cout << "Iterations = " << result.first << ", maxDiff = " << result.second << endl;

        vector<pair<double, int> > ratings;
        for (size_t i = input_data.size(); i < m.size(); ++i) {
            // push back the negative so we can use the standard sorting.
            ratings.push_back(make_pair<double, int>(-m[i], i - input_data.size() + 1));
        }
        sort(ratings.begin(), ratings.end());
        pair<double, double> pr10 = getPrecisionAndRecall(test_data, ratings, user, 10);
        pair<double, double> pr20 = getPrecisionAndRecall(test_data, ratings, user, 20);

        p10 +=  pr10.first;
        p20 +=  pr20.first;
        r10 +=  pr10.second;
        r20 +=  pr20.second;
        cout << "Precision (N=10): " << pr10.first << endl;
        cout << "Precision (N=20): " << pr20.first << endl;
        cout << "Recall (N=10): " << pr10.second << endl;
        cout << "Recall (N=20): " << pr20.second << endl;
    }
     timer.toc();
    p10 = p10 / static_cast<double>(N);
    p20 = p20 / static_cast<double>(N);
    r10 = r20 / static_cast<double>(N);
    r20 = r20 / static_cast<double>(N);
    cout << "Final estimated:" << endl;
    cout << "Precision (N=10): " << p10 << endl;
    cout << "Precision (N=20): " << p20 << endl;
    cout << "Recall (N=10): " << r10 << endl;
    cout << "Recall (N=20): " << r20 << endl;
    cout << "Measured cycles: " << measured_cycles << endl;
    return 0;
}

<|endoftext|>


<commit_before>// The libMesh Finite Element Library.
// Copyright (C) 2002-2019 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

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

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

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

//
Introduction Example 2 - Defining a Simple System

// \author Benjamin S. Kirk
// \date 2003
//
// This is the second example program.  It demonstrates how to
// create an equation system for a simple scalar system.  This
// example will also introduce some of the issues involved with using PETSc
// in your application.
//
// This is the first example program that indirectly
// uses the PETSc library.  By default equation data is stored
// in PETSc vectors, which may span multiple processors.  Before
// PETSc is used it must be initialized via libMesh::init().  Note that
// by passing argc and argv to PETSc you may specify
// command line arguments to PETSc.  For example, you might
// try running this example as:
//
// ./introduction_ex2 -log_info
//
// to see what PETSc is doing behind the scenes or
//
// ./introduction_ex2 -log_summary
//
// to get a summary of what PETSc did.
// Among other things, libMesh::init() initializes the MPI
// communications library and PETSc numeric library on your system if
// you haven't already done so.

// C++ include files that we need
#include <iostream>
//Basic include file needed for the mesh functionality.
#include "libmesh/libmesh.h"
#include "libmesh/mesh.h"
#include "libmesh/enum_xdr_mode.h"
// Include file that defines various mesh generation utilities
#include "libmesh/mesh_generation.h"
// Include file that defines (possibly multiple) systems of equations.
#include "libmesh/equation_systems.h"
// Include files that define a simple steady system
#include "libmesh/linear_implicit_system.h"
#include "libmesh/transient_system.h"
#include "libmesh/explicit_system.h"
#include "libmesh/enum_solver_package.h"

// Bring in everything from the libMesh namespace
using namespace libMesh;

int main (int argc, char ** argv)
{
  LibMeshInit init (argc, argv);

  // Skip this 2D example if libMesh was compiled as 1D-only.
  libmesh_example_requires(2 <= LIBMESH_DIM, "2D support");

  // This example requires a linear solver package.
  libmesh_example_requires(libMesh::default_solver_package() != INVALID_SOLVER_PACKAGE,
                           "--enable-petsc, --enable-trilinos, or --enable-eigen");

  // A brief message to the user to inform her of the
  // exact name of the program being run, and its command line.
  libMesh::out << "Running " << argv[0];
  for (int i=1; i<argc; i++)
    libMesh::out << " " << argv[i];
  libMesh::out << std::endl << std::endl;

  // Create a mesh, with dimension to be overridden later, distributed
  // across the default MPI communicator.
  Mesh mesh(init.comm());

  // Use the MeshTools::Generation mesh generator to create a uniform
  // 2D grid on the unit square.  By default a mesh of QUAD4
  // elements will be created.  We instruct the mesh generator
  // to build a mesh of 5x5 elements.
  MeshTools::Generation::build_square (mesh, 5, 5);

  // Create an equation systems object. This object can
  // contain multiple systems of different
  // flavors for solving loosely coupled physics.  Each system can
  // contain multiple variables of different approximation orders.
  // Here we will simply create a single system with one variable.
  // Later on, other flavors of systems will be introduced.  For the
  // moment, we use the general system.
  // The EquationSystems object needs a reference to the mesh
  // object, so the order of construction here is important.
  EquationSystems equation_systems (mesh);

  // Add a flag "test" that is visible for all systems.  This
  // helps in inter-system communication.
  equation_systems.parameters.set<bool> ("test") = true;

  // Set a simulation-specific parameter visible for all systems.
  // This helps in inter-system-communication.
  equation_systems.parameters.set<Real> ("dummy") = 42.;

  // Set another simulation-specific parameter
  equation_systems.parameters.set<Real> ("nobody") = 0.;

  // Now we declare the system and its variables.
  // We begin by adding a "TransientLinearImplicitSystem" to the
  // EquationSystems object, and we give it the name
  // "Simple System".
  equation_systems.add_system<TransientLinearImplicitSystem> ("Simple System");

  // Adds the variable "u" to "Simple System".  "u"
  // will be approximated using first-order approximation.
  equation_systems.get_system("Simple System").add_variable("u", FIRST);

  // Next we'll by add an "ExplicitSystem" to the
  // EquationSystems object, and we give it the name
  // "Complex System".
  equation_systems.add_system<ExplicitSystem> ("Complex System");

  // Give "Complex System" three variables -- each with a different approximation
  // order.  Variables "c" and "T" will use first-order Lagrange approximation,
  // while variable "dv" will use a second-order discontinuous
  // approximation space.
  equation_systems.get_system("Complex System").add_variable("c", FIRST);
  equation_systems.get_system("Complex System").add_variable("T", FIRST);
  equation_systems.get_system("Complex System").add_variable("dv", SECOND, MONOMIAL);

  // Initialize the data structures for the equation system.
  equation_systems.init();

  // Print information about the mesh to the screen.
  mesh.print_info();
  // Prints information about the system to the screen.
  equation_systems.print_info();

  // Write the equation system if the user specified an
  // output file name.  Note that there are two possible
  // formats to write to.  Specifying WRITE will
  // create a formatted ASCII file.  Optionally, you can specify
  // ENCODE and get an XDR-encoded binary file.
  //
  // We will write the data, clear the object, and read the file
  // we just wrote.  This is simply to demonstrate capability.
  // Note that you might use this in an application to periodically
  // dump the state of your simulation.  You can then restart from
  // this data later.
  if (argc > 1)
    if (argv[1][0] != '-')
      {
        libMesh::out << "<<< Writing system to file " << argv[1]
                     << std::endl;

        // Write the system.
        equation_systems.write (argv[1], WRITE);

        // Clear the equation systems data structure.
        equation_systems.clear ();

        libMesh::out << ">>> Reading system from file " << argv[1]
                     << std::endl << std::endl;

        // Read the file we just wrote.  This better
        // work!
        equation_systems.read (argv[1], READ);

        // Print the information again.
        equation_systems.print_info();
      }

  // All done.  libMesh objects are destroyed here.  Because the
  // LibMeshInit object was created first, its destruction occurs
  // last, and it's destructor finalizes any external libraries and
  // checks for leaked memory.
  return 0;
}
<commit_msg>Recommend non-deprecated options in example.<commit_after>// The libMesh Finite Element Library.
// Copyright (C) 2002-2019 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

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

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

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

//
Introduction Example 2 - Defining a Simple System

// \author Benjamin S. Kirk
// \date 2003
//
// This is the second example program.  It demonstrates how to
// create an equation system for a simple scalar system.  This
// example will also introduce some of the issues involved with using PETSc
// in your application.
//
// This is the first example program that indirectly
// uses the PETSc library.  By default equation data is stored
// in PETSc vectors, which may span multiple processors.  Before
// PETSc is used it must be initialized via libMesh::init().  Note that
// by passing argc and argv to PETSc you may specify
// command line arguments to PETSc.  For example, you might
// try running this example as:
// ./introduction_ex2 -info
//
// to see what PETSc is doing behind the scenes or
//
// ./introduction_ex2 -log_view
//
// to get a summary of what PETSc did.
// Among other things, libMesh::init() initializes the MPI
// communications library and PETSc numeric library on your system if
// you haven't already done so.

// C++ include files that we need
#include <iostream>
//Basic include file needed for the mesh functionality.
#include "libmesh/libmesh.h"
#include "libmesh/mesh.h"
#include "libmesh/enum_xdr_mode.h"
// Include file that defines various mesh generation utilities
#include "libmesh/mesh_generation.h"
// Include file that defines (possibly multiple) systems of equations.
#include "libmesh/equation_systems.h"
// Include files that define a simple steady system
#include "libmesh/linear_implicit_system.h"
#include "libmesh/transient_system.h"
#include "libmesh/explicit_system.h"
#include "libmesh/enum_solver_package.h"

// Bring in everything from the libMesh namespace
using namespace libMesh;

int main (int argc, char ** argv)
{
  LibMeshInit init (argc, argv);

  // Skip this 2D example if libMesh was compiled as 1D-only.
  libmesh_example_requires(2 <= LIBMESH_DIM, "2D support");

  // This example requires a linear solver package.
  libmesh_example_requires(libMesh::default_solver_package() != INVALID_SOLVER_PACKAGE,
                           "--enable-petsc, --enable-trilinos, or --enable-eigen");

  // A brief message to the user to inform her of the
  // exact name of the program being run, and its command line.
  libMesh::out << "Running " << argv[0];
  for (int i=1; i<argc; i++)
    libMesh::out << " " << argv[i];
  libMesh::out << std::endl << std::endl;

  // Create a mesh, with dimension to be overridden later, distributed
  // across the default MPI communicator.
  Mesh mesh(init.comm());

  // Use the MeshTools::Generation mesh generator to create a uniform
  // 2D grid on the unit square.  By default a mesh of QUAD4
  // elements will be created.  We instruct the mesh generator
  // to build a mesh of 5x5 elements.
  MeshTools::Generation::build_square (mesh, 5, 5);

  // Create an equation systems object. This object can
  // contain multiple systems of different
  // flavors for solving loosely coupled physics.  Each system can
  // contain multiple variables of different approximation orders.
  // Here we will simply create a single system with one variable.
  // Later on, other flavors of systems will be introduced.  For the
  // moment, we use the general system.
  // The EquationSystems object needs a reference to the mesh
  // object, so the order of construction here is important.
  EquationSystems equation_systems (mesh);

  // Add a flag "test" that is visible for all systems.  This
  // helps in inter-system communication.
  equation_systems.parameters.set<bool> ("test") = true;

  // Set a simulation-specific parameter visible for all systems.
  // This helps in inter-system-communication.
  equation_systems.parameters.set<Real> ("dummy") = 42.;

  // Set another simulation-specific parameter
  equation_systems.parameters.set<Real> ("nobody") = 0.;

  // Now we declare the system and its variables.
  // We begin by adding a "TransientLinearImplicitSystem" to the
  // EquationSystems object, and we give it the name
  // "Simple System".
  equation_systems.add_system<TransientLinearImplicitSystem> ("Simple System");

  // Adds the variable "u" to "Simple System".  "u"
  // will be approximated using first-order approximation.
  equation_systems.get_system("Simple System").add_variable("u", FIRST);

  // Next we'll by add an "ExplicitSystem" to the
  // EquationSystems object, and we give it the name
  // "Complex System".
  equation_systems.add_system<ExplicitSystem> ("Complex System");

  // Give "Complex System" three variables -- each with a different approximation
  // order.  Variables "c" and "T" will use first-order Lagrange approximation,
  // while variable "dv" will use a second-order discontinuous
  // approximation space.
  equation_systems.get_system("Complex System").add_variable("c", FIRST);
  equation_systems.get_system("Complex System").add_variable("T", FIRST);
  equation_systems.get_system("Complex System").add_variable("dv", SECOND, MONOMIAL);

  // Initialize the data structures for the equation system.
  equation_systems.init();

  // Print information about the mesh to the screen.
  mesh.print_info();
  // Prints information about the system to the screen.
  equation_systems.print_info();

  // Write the equation system if the user specified an
  // output file name.  Note that there are two possible
  // formats to write to.  Specifying WRITE will
  // create a formatted ASCII file.  Optionally, you can specify
  // ENCODE and get an XDR-encoded binary file.
  //
  // We will write the data, clear the object, and read the file
  // we just wrote.  This is simply to demonstrate capability.
  // Note that you might use this in an application to periodically
  // dump the state of your simulation.  You can then restart from
  // this data later.
  if (argc > 1)
    if (argv[1][0] != '-')
      {
        libMesh::out << "<<< Writing system to file " << argv[1]
                     << std::endl;

        // Write the system.
        equation_systems.write (argv[1], WRITE);

        // Clear the equation systems data structure.
        equation_systems.clear ();

        libMesh::out << ">>> Reading system from file " << argv[1]
                     << std::endl << std::endl;

        // Read the file we just wrote.  This better
        // work!
        equation_systems.read (argv[1], READ);

        // Print the information again.
        equation_systems.print_info();
      }

  // All done.  libMesh objects are destroyed here.  Because the
  // LibMeshInit object was created first, its destruction occurs
  // last, and it's destructor finalizes any external libraries and
  // checks for leaked memory.
  return 0;
}
<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
 * Copyright © 2006-2012 Quality & Usability Lab,                             *
 *                       Telekom Innovation Laboratories, TU Berlin           *
 *                                                                            *
 * This file is part of the Audio Processing Framework (APF).                 *
 *                                                                            *
 * The APF is free software:  you can redistribute it and/or modify it  under *
 * the terms of the  GNU  General  Public  License  as published by the  Free *
 * Software Foundation, either version 3 of the License,  or (at your option) *
 * any later version.                                                         *
 *                                                                            *
 * The APF is distributed in the hope that it will be useful, but WITHOUT ANY *
 * WARRANTY;  without even the implied warranty of MERCHANTABILITY or FITNESS *
 * FOR A PARTICULAR PURPOSE.                                                  *
 * See the GNU General Public License for more details.                       *
 *                                                                            *
 * You should  have received a copy  of the GNU General Public License  along *
 * with this program.  If not, see <http://www.gnu.org/licenses/>.            *
 *                                                                            *
 *                                 http://AudioProcessingFramework.github.com *
 ******************************************************************************/

// Tests for math functions.

#include "apf/math.h"

#include "catch/catch.hpp"

using namespace apf::math;

TEST_CASE("math", "Test all functions of math namespace")
{

SECTION("pi", "")
{
  CHECK(pi<float>() == 4.0f * std::atan(1.0f));
  CHECK(pi<double>() == 4.0 * std::atan(1.0));
  CHECK(pi<long double>() == 4.0l * std::atan(1.0l));

  // pi divided by 180

  CHECK((pi_div_180<float>() * 180.0f / pi<float>()) == 1.0f);
  CHECK((pi_div_180<double>() * 180.0 / pi<double>()) == 1.0);
  CHECK((pi_div_180<long double>() * 180.0l / pi<long double>()) == 1.0l);
}

SECTION("square", "a*a")
{
  CHECK(square(2.0f) == 4.0f);
  CHECK(square(2.0) == 4.0f);
  CHECK(square(2.0l) == 4.0l);
}

SECTION("dB2linear", "")
{
  CHECK(dB2linear(6.0f) == Approx(1.99526231));
  CHECK(dB2linear(6.0) == Approx(1.99526231));
  // Approx doesn't exist for long double
  CHECK(static_cast<double>(dB2linear(6.0l)) == Approx(1.99526231));

  // now with the "power" option

  CHECK(dB2linear(3.0f, true) == Approx(1.99526231));
  CHECK(dB2linear(3.0, true) == Approx(1.99526231));
  CHECK(static_cast<double>(dB2linear(3.0l, true)) == Approx(1.99526231));
}

SECTION("linear2dB", "")
{
  CHECK(linear2dB(1.99526231f) == Approx(6.0));
  CHECK(linear2dB(1.99526231) == Approx(6.0));
  CHECK(static_cast<double>(linear2dB(1.99526231l)) == Approx(6.0));

  CHECK(linear2dB(1.99526231f, true) == Approx(3.0));
  CHECK(linear2dB(1.99526231, true) == Approx(3.0));
  CHECK(static_cast<double>(linear2dB(1.99526231l, true)) == Approx(3.0));

  CHECK(linear2dB(0.0f) == -std::numeric_limits<float>::infinity());
  CHECK(linear2dB(0.0) == -std::numeric_limits<double>::infinity());
  CHECK(linear2dB(0.0l) == -std::numeric_limits<long double>::infinity());

  // TODO: how to check NaN results?
  // linear2dB(-0.1f)
  // linear2dB(-0.1)
  // linear2dB(-0.1l)
}

SECTION("deg2rad", "")
{
  CHECK(deg2rad(180.0f) == pi<float>());
  CHECK(deg2rad(180.0) == pi<double>());
  CHECK(deg2rad(180.0l) == pi<long double>());
}

SECTION("rad2deg", "")
{
  CHECK(rad2deg(pi<float>()) == 180.0f);
  CHECK(rad2deg(pi<double>()) == 180.0);
  CHECK(rad2deg(pi<long double>()) == 180.0l);
}

SECTION("next_power_of_2", "")
{
  CHECK(next_power_of_2(-3) == 1);
  CHECK(next_power_of_2(-2) == 1);
  CHECK(next_power_of_2(-1) == 1);
  CHECK(next_power_of_2(0) == 1);
  CHECK(next_power_of_2(1) == 1);
  CHECK(next_power_of_2(2) == 2);
  CHECK(next_power_of_2(3) == 4);

  CHECK(next_power_of_2(1.0f) == 1.0f);
  CHECK(next_power_of_2(2.0f) == 2.0f);
  CHECK(next_power_of_2(3.0f) == 4.0f);
  CHECK(next_power_of_2(1.0) == 1.0);
  CHECK(next_power_of_2(2.0) == 2.0);
  CHECK(next_power_of_2(2.5) == 4.0);
  CHECK(next_power_of_2(3.0) == 4.0);
  CHECK(next_power_of_2(1.0l) == 1.0l);
  CHECK(next_power_of_2(2.0l) == 2.0l);
  CHECK(next_power_of_2(3.0l) == 4.0l);
}

SECTION("max_amplitude", "")
{
  std::vector<double> sig(5);

  CHECK(max_amplitude(sig.begin(), sig.end()) == 0.0);

  sig[2] = -2.0;
  CHECK(max_amplitude(sig.begin(), sig.end()) == 2.0);

  sig[3] = 4.0;
  CHECK(max_amplitude(sig.begin(), sig.end()) == 4.0);
}

SECTION("rms", "")
{
  std::vector<double> sig(5);

  CHECK(rms(sig.begin(), sig.end()) == 0.0);

  sig[0] = -1.0;
  sig[1] = -1.0;
  sig[2] = -1.0;
  sig[3] =  1.0;
  sig[4] =  1.0;

  CHECK(rms(sig.begin(), sig.end()) == 1.0);
}

SECTION("raised_cosine", "")
{
  raised_cosine<float> rc1(1.5f);
  CHECK(rc1(0.75f) == 0.0f);
  CHECK(rc1(1.5f) == 1.0f);

  raised_cosine<double> rc2(1.5);
  CHECK(rc2(0.75) == 0.0);
  CHECK(rc2(1.5) == 1.0);

  raised_cosine<double> rc3(360);
  CHECK(rc3(60) == 0.75);
}

SECTION("linear_interpolator", "")
{
  linear_interpolator<double> in(1.5, 3.0, 3.0);
  CHECK(in(0.0) == 1.5);
  CHECK(in(1.0) == 2.0);
  CHECK(in(2.0) == 2.5);

  // using default length 1:
  in = make_linear_interpolator(5.0, 6.0);
  CHECK(in(0.0) == 5.0);
  CHECK(in(0.5) == 5.5);
  CHECK(in(1.0) == 6.0);
}

SECTION("linear_interpolator, integer index", "")
{
  linear_interpolator<double, int> in(5.0, 6.0, 2);
  CHECK(in(0) == 5.0);
  CHECK(in(1) == 5.5);
}

SECTION("linear_interpolator, integer index converted to double", "")
{
  linear_interpolator<double> in(1.0, 2.0, 4);
  CHECK(in(0) == 1.0);
  CHECK(in(1) == 1.25);
}

SECTION("identity", "")
{
  identity<float> id;
  CHECK(id(0.5f) == 0.5f);
}

} // TEST_CASE

// Settings for Vim (http://www.vim.org/), please do not remove:
// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
<commit_msg>Unit tests for math::wrap()<commit_after>/******************************************************************************
 * Copyright © 2012-2013 Institut für Nachrichtentechnik, Universität Rostock *
 * Copyright © 2006-2012 Quality & Usability Lab,                             *
 *                       Telekom Innovation Laboratories, TU Berlin           *
 *                                                                            *
 * This file is part of the Audio Processing Framework (APF).                 *
 *                                                                            *
 * The APF is free software:  you can redistribute it and/or modify it  under *
 * the terms of the  GNU  General  Public  License  as published by the  Free *
 * Software Foundation, either version 3 of the License,  or (at your option) *
 * any later version.                                                         *
 *                                                                            *
 * The APF is distributed in the hope that it will be useful, but WITHOUT ANY *
 * WARRANTY;  without even the implied warranty of MERCHANTABILITY or FITNESS *
 * FOR A PARTICULAR PURPOSE.                                                  *
 * See the GNU General Public License for more details.                       *
 *                                                                            *
 * You should  have received a copy  of the GNU General Public License  along *
 * with this program.  If not, see <http://www.gnu.org/licenses/>.            *
 *                                                                            *
 *                                 http://AudioProcessingFramework.github.com *
 ******************************************************************************/

// Tests for math functions.

#include "apf/math.h"

#include "catch/catch.hpp"

using namespace apf::math;

TEST_CASE("math", "Test all functions of math namespace")
{

SECTION("pi", "")
{
  CHECK(pi<float>() == 4.0f * std::atan(1.0f));
  CHECK(pi<double>() == 4.0 * std::atan(1.0));
  CHECK(pi<long double>() == 4.0l * std::atan(1.0l));

  // pi divided by 180

  CHECK((pi_div_180<float>() * 180.0f / pi<float>()) == 1.0f);
  CHECK((pi_div_180<double>() * 180.0 / pi<double>()) == 1.0);
  CHECK((pi_div_180<long double>() * 180.0l / pi<long double>()) == 1.0l);
}

SECTION("square", "a*a")
{
  CHECK(square(2.0f) == 4.0f);
  CHECK(square(2.0) == 4.0f);
  CHECK(square(2.0l) == 4.0l);
}

SECTION("dB2linear", "")
{
  CHECK(dB2linear(6.0f) == Approx(1.99526231));
  CHECK(dB2linear(6.0) == Approx(1.99526231));
  // Approx doesn't exist for long double
  CHECK(static_cast<double>(dB2linear(6.0l)) == Approx(1.99526231));

  // now with the "power" option

  CHECK(dB2linear(3.0f, true) == Approx(1.99526231));
  CHECK(dB2linear(3.0, true) == Approx(1.99526231));
  CHECK(static_cast<double>(dB2linear(3.0l, true)) == Approx(1.99526231));
}

SECTION("linear2dB", "")
{
  CHECK(linear2dB(1.99526231f) == Approx(6.0));
  CHECK(linear2dB(1.99526231) == Approx(6.0));
  CHECK(static_cast<double>(linear2dB(1.99526231l)) == Approx(6.0));

  CHECK(linear2dB(1.99526231f, true) == Approx(3.0));
  CHECK(linear2dB(1.99526231, true) == Approx(3.0));
  CHECK(static_cast<double>(linear2dB(1.99526231l, true)) == Approx(3.0));

  CHECK(linear2dB(0.0f) == -std::numeric_limits<float>::infinity());
  CHECK(linear2dB(0.0) == -std::numeric_limits<double>::infinity());
  CHECK(linear2dB(0.0l) == -std::numeric_limits<long double>::infinity());

  // TODO: how to check NaN results?
  // linear2dB(-0.1f)
  // linear2dB(-0.1)
  // linear2dB(-0.1l)
}

SECTION("deg2rad", "")
{
  CHECK(deg2rad(180.0f) == pi<float>());
  CHECK(deg2rad(180.0) == pi<double>());
  CHECK(deg2rad(180.0l) == pi<long double>());
}

SECTION("rad2deg", "")
{
  CHECK(rad2deg(pi<float>()) == 180.0f);
  CHECK(rad2deg(pi<double>()) == 180.0);
  CHECK(rad2deg(pi<long double>()) == 180.0l);
}

SECTION("wrap int", "")
{
  CHECK(wrap(-1, 7) == 6);
  CHECK(wrap(0, 7) == 0);
  CHECK(wrap(6, 7) == 6);
  CHECK(wrap(7, 7) == 0);
  CHECK(wrap(8, 7) == 1);
}

SECTION("wrap double", "")
{
  CHECK(wrap(-0.5, 360.0) == 359.5);
  CHECK(wrap(0.0, 360.0) == 0.0);
  CHECK(wrap(359.5, 360.0) == 359.5);
  CHECK(wrap(360.0, 360.0) == 0.0);
  CHECK(wrap(360.5, 360.0) == 0.5);
}

SECTION("next_power_of_2", "")
{
  CHECK(next_power_of_2(-3) == 1);
  CHECK(next_power_of_2(-2) == 1);
  CHECK(next_power_of_2(-1) == 1);
  CHECK(next_power_of_2(0) == 1);
  CHECK(next_power_of_2(1) == 1);
  CHECK(next_power_of_2(2) == 2);
  CHECK(next_power_of_2(3) == 4);

  CHECK(next_power_of_2(1.0f) == 1.0f);
  CHECK(next_power_of_2(2.0f) == 2.0f);
  CHECK(next_power_of_2(3.0f) == 4.0f);
  CHECK(next_power_of_2(1.0) == 1.0);
  CHECK(next_power_of_2(2.0) == 2.0);
  CHECK(next_power_of_2(2.5) == 4.0);
  CHECK(next_power_of_2(3.0) == 4.0);
  CHECK(next_power_of_2(1.0l) == 1.0l);
  CHECK(next_power_of_2(2.0l) == 2.0l);
  CHECK(next_power_of_2(3.0l) == 4.0l);
}

SECTION("max_amplitude", "")
{
  std::vector<double> sig(5);

  CHECK(max_amplitude(sig.begin(), sig.end()) == 0.0);

  sig[2] = -2.0;
  CHECK(max_amplitude(sig.begin(), sig.end()) == 2.0);

  sig[3] = 4.0;
  CHECK(max_amplitude(sig.begin(), sig.end()) == 4.0);
}

SECTION("rms", "")
{
  std::vector<double> sig(5);

  CHECK(rms(sig.begin(), sig.end()) == 0.0);

  sig[0] = -1.0;
  sig[1] = -1.0;
  sig[2] = -1.0;
  sig[3] =  1.0;
  sig[4] =  1.0;

  CHECK(rms(sig.begin(), sig.end()) == 1.0);
}

SECTION("raised_cosine", "")
{
  raised_cosine<float> rc1(1.5f);
  CHECK(rc1(0.75f) == 0.0f);
  CHECK(rc1(1.5f) == 1.0f);

  raised_cosine<double> rc2(1.5);
  CHECK(rc2(0.75) == 0.0);
  CHECK(rc2(1.5) == 1.0);

  raised_cosine<double> rc3(360);
  CHECK(rc3(60) == 0.75);
}

SECTION("linear_interpolator", "")
{
  linear_interpolator<double> in(1.5, 3.0, 3.0);
  CHECK(in(0.0) == 1.5);
  CHECK(in(1.0) == 2.0);
  CHECK(in(2.0) == 2.5);

  // using default length 1:
  in = make_linear_interpolator(5.0, 6.0);
  CHECK(in(0.0) == 5.0);
  CHECK(in(0.5) == 5.5);
  CHECK(in(1.0) == 6.0);
}

SECTION("linear_interpolator, integer index", "")
{
  linear_interpolator<double, int> in(5.0, 6.0, 2);
  CHECK(in(0) == 5.0);
  CHECK(in(1) == 5.5);
}

SECTION("linear_interpolator, integer index converted to double", "")
{
  linear_interpolator<double> in(1.0, 2.0, 4);
  CHECK(in(0) == 1.0);
  CHECK(in(1) == 1.25);
}

SECTION("identity", "")
{
  identity<float> id;
  CHECK(id(0.5f) == 0.5f);
}

} // TEST_CASE

// Settings for Vim (http://www.vim.org/), please do not remove:
// vim:softtabstop=2:shiftwidth=2:expandtab:textwidth=80:cindent
<|endoftext|>


<commit_before>/*
 * galleryenlargeshrinkplugin.cpp
 *
 * Copyright (C) 2012 Igalia, S.L.
 * Author: Antia Puentes <apuentes@igalia.com>
 *
 * This file is part of the Gallery Enlarge/Shrink Plugin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see http://www.gnu.org/licenses/ *
 */

#include "galleryenlargeshrinkplugin.h"
#include "galleryenlargeshrinkplugin_p.h"
#include "galleryenlargeshrinkwidget.h"

#include <galleryedituiprovider.h>
#include <MLibrary>
#include <MApplication>
#include <MBanner>
#include <MMessageBox>
#include <MLabel>
#include <QGraphicsSceneMouseEvent>
#include <QDesktopServices>
#include <QUrl>
#include <QTextOption>

#include <QuillImageFilter>

static const float   EFFECT_FORCE          = 1.0;
static const int     TAP_DISTANCE          = 20;
static const int     PORTRAIT_HEIGHT       = 224;
static const int     LANDSCAPE_HEIGHT      = 112;
static const int     INFOBANNER_TIMEOUT    = 2 * 1000;
static const int     IMAGE_MAX_HEIGHT      = 512;
static const int     IMAGE_MAX_WIDTH       = 512;

M_LIBRARY

GalleryEnlargeShrinkPluginPrivate::GalleryEnlargeShrinkPluginPrivate() :
    m_focusPosition(),
    m_validImage(true)
{
}

GalleryEnlargeShrinkPluginPrivate::~GalleryEnlargeShrinkPluginPrivate()
{
}

GalleryEnlargeShrinkPlugin::GalleryEnlargeShrinkPlugin(QObject* parent) :
    GalleryEditPlugin(parent),
    d_ptr(new GalleryEnlargeShrinkPluginPrivate())
{
}

GalleryEnlargeShrinkPlugin::~GalleryEnlargeShrinkPlugin()
{
    delete d_ptr;
}

QString GalleryEnlargeShrinkPlugin::name() const
{
    return QString("Enlarge - Shrink");
}

QString GalleryEnlargeShrinkPlugin::iconID() const
{
    return QString("icon-m-image-edit-enlarge-shrink");
}

bool GalleryEnlargeShrinkPlugin::containsUi() const
{
    return true;
}

bool GalleryEnlargeShrinkPlugin::zoomingAllowed() const
{
    return true;
}

QGraphicsWidget* GalleryEnlargeShrinkPlugin::createToolBarWidget(QGraphicsItem* parent)
{
    GalleryEnlargeShrinkWidget* widget = new GalleryEnlargeShrinkWidget(parent);
    connect(widget, SIGNAL(aboutLinkActivated(QString)),
            this,   SLOT(onAboutLinkActivated(QString)));
    return widget;
}

const QSize GalleryEnlargeShrinkPlugin::toolBarWidgetSize(const M::Orientation& orientation) const
{
    QSize size = GalleryEditPlugin::toolBarWidgetSize(orientation);

    if (M::Portrait == orientation) {
        size.setHeight(PORTRAIT_HEIGHT);
    } else {
        size.setHeight(LANDSCAPE_HEIGHT);
    }

    return size;
}

bool GalleryEnlargeShrinkPlugin::receiveMouseEvent(QGraphicsSceneMouseEvent *event)
{
    if (event &&
        event->type() == QEvent::GraphicsSceneMouseRelease &&
        event->button() == Qt::LeftButton &&
        (event->scenePos() - event->buttonDownScenePos(Qt::LeftButton)).manhattanLength() < TAP_DISTANCE) {
        Q_D(GalleryEnlargeShrinkPlugin);
        if (d->m_validImage) {
            d->m_focusPosition = event->pos().toPoint();
            performEditOperation();
            return true;
        } else {
            showInfoBanner("Plugin disabled for this image size");
        }
    }
    return false;
}

void GalleryEnlargeShrinkPlugin::activate()
{
    if (editUiProvider()) {
        Q_D(GalleryEnlargeShrinkPlugin);
        d->m_validImage = editUiProvider()->fullImageSize().height() <= IMAGE_MAX_HEIGHT &&
                          editUiProvider()->fullImageSize().width()  <= IMAGE_MAX_WIDTH;
        if (d->m_validImage) {
            showInfoBanner("Tap on an area to keep it focused");
        } else {
            showMessageBox("Enlarge Shrink plugin limitations",
                           "Gallery Enlarge Shrink plugin is currently limited to "
                           "small images (512x512)
"
                           "For a given image:"
                           "
"
                           "
Scale it or crop it
"
                           "
Save it with a different name
"
                           "
Apply the filter to the new one
"
                           "
");
            GalleryEnlargeShrinkWidget* widget = static_cast<GalleryEnlargeShrinkWidget*>(toolBarWidget());
            widget->enableInput(d->m_validImage);
        }
    }
}

void GalleryEnlargeShrinkPlugin::performEditOperation()
{
    if (editUiProvider()) {
        Q_D(GalleryEnlargeShrinkPlugin);
        QHash<QuillImageFilter::QuillFilterOption, QVariant> optionHash;
        const QPoint imagePosition = editUiProvider()->convertScreenCoordToImageCoord(d->m_focusPosition);
        if (imagePosition != QPoint(-1, -1)) {
            GalleryEnlargeShrinkWidget* widget = static_cast<GalleryEnlargeShrinkWidget*>(toolBarWidget());
            optionHash.insert(QuillImageFilter::Center, QVariant(imagePosition));
            double radius = widget->radius() / 100.0 * qMin(editUiProvider()->fullImageSize().width(),
                                                            editUiProvider()->fullImageSize().height());
            optionHash.insert(QuillImageFilter::Radius, radius);
            // To enlarge (punch effect) "force" must be positive
            // To shrink  (pinch effect) "force" must be negative
            optionHash.insert("force", EFFECT_FORCE * (widget->shrink()? -1 : 1));
            editUiProvider()->runEditFilter("com.igalia.enlargeshrink", optionHash);
            emit editOperationPerformed();
        }
    }
}

MMessageBox* GalleryEnlargeShrinkPlugin::showMessageBox(const QString& title, const QString& text) const
{
    MMessageBox* messageBox = new MMessageBox(title, "");
    MLabel* innerLabel = new MLabel(messageBox);
    innerLabel->setWordWrap(true);
    innerLabel->setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    innerLabel->setStyleName("CommonQueryText");
    innerLabel->setText(text);
    innerLabel->setAlignment(Qt::AlignHCenter);
    messageBox->setCentralWidget(innerLabel);
    connect(this, SIGNAL(deactivated()),
            messageBox, SLOT(disappear()));
    messageBox->appear(MSceneWindow::DestroyWhenDone);

    return messageBox;
}

MBanner* GalleryEnlargeShrinkPlugin::showInfoBanner(const QString& title) const
{
    MBanner *infoBanner = new MBanner;
    infoBanner->setStyleName(MBannerType::InformationBanner);
    infoBanner->setTitle(title);
    infoBanner->model()->setDisappearTimeout(INFOBANNER_TIMEOUT);
    connect(this, SIGNAL(deactivated()),
            infoBanner, SLOT(disappear()));
    infoBanner->appear(MApplication::activeWindow(), MSceneWindow::DestroyWhenDone);

    return infoBanner;
}

void GalleryEnlargeShrinkPlugin::onAboutLinkActivated(const QString &link)
{
    if (link.toLower().startsWith("http") || link.toLower().startsWith("mailto")) {
        QDesktopServices::openUrl(QUrl(link));
    } else {
        showMessageBox("About Enlarge Shrink plugin",
                       "Copyright (c) 2012 Igalia S.L."
                       "

"
                       "apuentes@igalia.com | "
                       "www.igalia.com"
                       "

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

Q_EXPORT_PLUGIN2(galleryenlargeshrinkplugin, GalleryEnlargeShrinkPlugin)
<commit_msg>GalleryEnlargeShrinkPlugin: use the PACKAGEVERSION define in the disclaimer<commit_after>/*
 * galleryenlargeshrinkplugin.cpp
 *
 * Copyright (C) 2012 Igalia, S.L.
 * Author: Antia Puentes <apuentes@igalia.com>
 *
 * This file is part of the Gallery Enlarge/Shrink Plugin.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library. If not, see http://www.gnu.org/licenses/ *
 */

#include "galleryenlargeshrinkplugin.h"
#include "galleryenlargeshrinkplugin_p.h"
#include "galleryenlargeshrinkwidget.h"

#include <galleryedituiprovider.h>
#include <MLibrary>
#include <MApplication>
#include <MBanner>
#include <MMessageBox>
#include <MLabel>
#include <QGraphicsSceneMouseEvent>
#include <QDesktopServices>
#include <QUrl>
#include <QTextOption>

#include <QuillImageFilter>

static const float   EFFECT_FORCE          = 1.0;
static const int     TAP_DISTANCE          = 20;
static const int     PORTRAIT_HEIGHT       = 224;
static const int     LANDSCAPE_HEIGHT      = 112;
static const int     INFOBANNER_TIMEOUT    = 2 * 1000;
static const int     IMAGE_MAX_HEIGHT      = 512;
static const int     IMAGE_MAX_WIDTH       = 512;

M_LIBRARY

GalleryEnlargeShrinkPluginPrivate::GalleryEnlargeShrinkPluginPrivate() :
    m_focusPosition(),
    m_validImage(true)
{
}

GalleryEnlargeShrinkPluginPrivate::~GalleryEnlargeShrinkPluginPrivate()
{
}

GalleryEnlargeShrinkPlugin::GalleryEnlargeShrinkPlugin(QObject* parent) :
    GalleryEditPlugin(parent),
    d_ptr(new GalleryEnlargeShrinkPluginPrivate())
{
}

GalleryEnlargeShrinkPlugin::~GalleryEnlargeShrinkPlugin()
{
    delete d_ptr;
}

QString GalleryEnlargeShrinkPlugin::name() const
{
    return QString("Enlarge - Shrink");
}

QString GalleryEnlargeShrinkPlugin::iconID() const
{
    return QString("icon-m-image-edit-enlarge-shrink");
}

bool GalleryEnlargeShrinkPlugin::containsUi() const
{
    return true;
}

bool GalleryEnlargeShrinkPlugin::zoomingAllowed() const
{
    return true;
}

QGraphicsWidget* GalleryEnlargeShrinkPlugin::createToolBarWidget(QGraphicsItem* parent)
{
    GalleryEnlargeShrinkWidget* widget = new GalleryEnlargeShrinkWidget(parent);
    connect(widget, SIGNAL(aboutLinkActivated(QString)),
            this,   SLOT(onAboutLinkActivated(QString)));
    return widget;
}

const QSize GalleryEnlargeShrinkPlugin::toolBarWidgetSize(const M::Orientation& orientation) const
{
    QSize size = GalleryEditPlugin::toolBarWidgetSize(orientation);

    if (M::Portrait == orientation) {
        size.setHeight(PORTRAIT_HEIGHT);
    } else {
        size.setHeight(LANDSCAPE_HEIGHT);
    }

    return size;
}

bool GalleryEnlargeShrinkPlugin::receiveMouseEvent(QGraphicsSceneMouseEvent *event)
{
    if (event &&
        event->type() == QEvent::GraphicsSceneMouseRelease &&
        event->button() == Qt::LeftButton &&
        (event->scenePos() - event->buttonDownScenePos(Qt::LeftButton)).manhattanLength() < TAP_DISTANCE) {
        Q_D(GalleryEnlargeShrinkPlugin);
        if (d->m_validImage) {
            d->m_focusPosition = event->pos().toPoint();
            performEditOperation();
            return true;
        } else {
            showInfoBanner("Plugin disabled for this image size");
        }
    }
    return false;
}

void GalleryEnlargeShrinkPlugin::activate()
{
    if (editUiProvider()) {
        Q_D(GalleryEnlargeShrinkPlugin);
        d->m_validImage = editUiProvider()->fullImageSize().height() <= IMAGE_MAX_HEIGHT &&
                          editUiProvider()->fullImageSize().width()  <= IMAGE_MAX_WIDTH;
        if (d->m_validImage) {
            showInfoBanner("Tap on an area to keep it focused");
        } else {
            showMessageBox("Enlarge Shrink plugin limitations",
                           "Gallery Enlarge Shrink plugin is currently limited to "
                           "small images (512x512)
"
                           "For a given image:"
                           "
"
                           "
Scale it or crop it
"
                           "
Save it with a different name
"
                           "
Apply the filter to the new one
"
                           "
");
            GalleryEnlargeShrinkWidget* widget = static_cast<GalleryEnlargeShrinkWidget*>(toolBarWidget());
            widget->enableInput(d->m_validImage);
        }
    }
}

void GalleryEnlargeShrinkPlugin::performEditOperation()
{
    if (editUiProvider()) {
        Q_D(GalleryEnlargeShrinkPlugin);
        QHash<QuillImageFilter::QuillFilterOption, QVariant> optionHash;
        const QPoint imagePosition = editUiProvider()->convertScreenCoordToImageCoord(d->m_focusPosition);
        if (imagePosition != QPoint(-1, -1)) {
            GalleryEnlargeShrinkWidget* widget = static_cast<GalleryEnlargeShrinkWidget*>(toolBarWidget());
            optionHash.insert(QuillImageFilter::Center, QVariant(imagePosition));
            double radius = widget->radius() / 100.0 * qMin(editUiProvider()->fullImageSize().width(),
                                                            editUiProvider()->fullImageSize().height());
            optionHash.insert(QuillImageFilter::Radius, radius);
            // To enlarge (punch effect) "force" must be positive
            // To shrink  (pinch effect) "force" must be negative
            optionHash.insert("force", EFFECT_FORCE * (widget->shrink()? -1 : 1));
            editUiProvider()->runEditFilter("com.igalia.enlargeshrink", optionHash);
            emit editOperationPerformed();
        }
    }
}

MMessageBox* GalleryEnlargeShrinkPlugin::showMessageBox(const QString& title, const QString& text) const
{
    MMessageBox* messageBox = new MMessageBox(title, "");
    MLabel* innerLabel = new MLabel(messageBox);
    innerLabel->setWordWrap(true);
    innerLabel->setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
    innerLabel->setStyleName("CommonQueryText");
    innerLabel->setText(text);
    innerLabel->setAlignment(Qt::AlignHCenter);
    messageBox->setCentralWidget(innerLabel);
    connect(this, SIGNAL(deactivated()),
            messageBox, SLOT(disappear()));
    messageBox->appear(MSceneWindow::DestroyWhenDone);

    return messageBox;
}

MBanner* GalleryEnlargeShrinkPlugin::showInfoBanner(const QString& title) const
{
    MBanner *infoBanner = new MBanner;
    infoBanner->setStyleName(MBannerType::InformationBanner);
    infoBanner->setTitle(title);
    infoBanner->model()->setDisappearTimeout(INFOBANNER_TIMEOUT);
    connect(this, SIGNAL(deactivated()),
            infoBanner, SLOT(disappear()));
    infoBanner->appear(MApplication::activeWindow(), MSceneWindow::DestroyWhenDone);

    return infoBanner;
}

void GalleryEnlargeShrinkPlugin::onAboutLinkActivated(const QString &link)
{
    if (link.toLower().startsWith("http") || link.toLower().startsWith("mailto")) {
        QDesktopServices::openUrl(QUrl(link));
    } else {
        showMessageBox(QString("About Enlarge Shrink plugin - %1").arg(PACKAGEVERSION),
                       "Copyright (c) 2012 Igalia S.L."
                       "

"
                       "apuentes@igalia.com | "
                       "www.igalia.com"
                       "

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

Q_EXPORT_PLUGIN2(galleryenlargeshrinkplugin, GalleryEnlargeShrinkPlugin)
<|endoftext|>


<commit_before>//============================================================================
// Name        : ImageProcessingController.cpp
// Author      : ITM13
// Version     : 1.0
// Copyright   : Copyright (c) 2014 Swank Rat, MIT License (MIT)
// Description :
//============================================================================
#include "ImageProcessingController.h"
#include "..\shared\Logger.h"

ImageProcessingController::ImageProcessingController()
{
	webcamService = new WebcamService();
}

ImageProcessingController::~ImageProcessingController()
{
	StopImageProcessing();
	webcamService = NULL;
}

void ImageProcessingController::StartImageProcessing() {
	webcamService->StartRecording();
}

bool ImageProcessingController::StopImageProcessing() {
	webcamService->StopRecording();

	return true;
}

void ImageProcessingController::Update(WebcamService* observable) {
	Logger::addMessage("New image available");
	observable->GetLastImage();
}
<commit_msg>Modified image proc controller<commit_after>//============================================================================
// Name        : ImageProcessingController.cpp
// Author      : ITM13
// Version     : 1.0
// Copyright   : Copyright (c) 2014 Swank Rat, MIT License (MIT)
// Description :
//============================================================================
#include "ImageProcessingController.h"
#include "..\shared\Logger.h"

ImageProcessingController::ImageProcessingController()
{
	webcamService = new WebcamService();
}

ImageProcessingController::~ImageProcessingController()
{
	StopImageProcessing();
	webcamService = NULL;
}

void ImageProcessingController::StartImageProcessing() {
	webcamService->StartRecording();
}

bool ImageProcessingController::StopImageProcessing() {
	webcamService->StopRecording();

	return true;
}

void ImageProcessingController::Update(WebcamService* observable) {
	Logger::addMessage("New image available");
	observable->GetLastImage();

}
<|endoftext|>


<commit_before>/**
 * \file
 * \brief BufferingBlockDevice class header
 *
 * \author Copyright (C) 2019 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef INCLUDE_DISTORTOS_DEVICES_MEMORY_BUFFERINGBLOCKDEVICE_HPP_
#define INCLUDE_DISTORTOS_DEVICES_MEMORY_BUFFERINGBLOCKDEVICE_HPP_

#include "distortos/devices/memory/BlockDevice.hpp"

namespace distortos
{

namespace devices
{

class BlockDevice;

/**
 * BufferingBlockDevice class is a buffering wrapper for BlockDevice.
 *
 * This class tries to minimize amount of BlockDevice operations by buffering both reads and writes. This can give
 * signigicant gain in case of devices like SD cards, where each operation may cause the device to become "busy" for a
 * noticeable amount of time. With this class several adjacent reads or writes can be combined into a single larger
 * operation, which is much faster overall when you also include the waits for the SD card to become "idle".
 *
 * Another use for this class is as a proxy between a file system and a block device which requires specific alignment.
 *
 * \ingroup devices
 */

class BufferingBlockDevice : public BlockDevice
{
public:

	/**
	 * \brief BufferingBlockDevice's constructor
	 *
	 * \param [in] blockDevice is a reference to associated block device
	 * \param [in] readBuffer is a pointer to buffer for reads, its address must be aligned to
	 * `DISTORTOS_BLOCKDEVICE_BUFFER_ALIGNMENT` bytes
	 * \param [in] readBufferSize is the size of \a readBuffer, bytes, must be a multiple of \a blockDevice block size
	 * \param [in] writeBuffer is a pointer to buffer for writes, its address must be aligned to
	 * `DISTORTOS_BLOCKDEVICE_BUFFER_ALIGNMENT` bytes
	 * \param [in] writeBufferSize is the size of \a writeBuffer, bytes, must be a multiple of \a blockDevice block size
	 */

	constexpr explicit BufferingBlockDevice(BlockDevice& blockDevice, void* const readBuffer,
			const size_t readBufferSize, void* const writeBuffer, const size_t writeBufferSize) :
					readBufferAddress_{},
					writeBufferAddress_{},
					blockDevice_{blockDevice},
					readBuffer_{readBuffer},
					readBufferSize_{readBufferSize},
					writeBuffer_{writeBuffer},
					writeBufferSize_{writeBufferSize},
					writeBufferValidSize_{},
					openCount_{},
					readBufferValid_{}
	{

	}

	/**
	 * \brief BufferingBlockDevice's destructor
	 *
	 * \pre Device is closed.
	 */

	~BufferingBlockDevice() override;

	/**
	 * \brief Closes device.
	 *
	 * \note Even if error code is returned, the device must not be used from the context which opened it (until it is
	 * successfully opened again).
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer();
	 * - error codes returned by BlockDevice::close();
	 */

	int close() override;

	/**
	 * \brief Erases blocks on a device.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 * \pre \a address and \a size are valid.
	 * \pre Selected range is within address space of device.
	 *
	 * \param [in] address is the address of range that will be erased, must be a multiple of block size
	 * \param [in] size is the size of erased range, bytes, must be a multiple of block size
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer(size_t);
	 * - error codes returned by BlockDevice::erase();
	 */

	int erase(uint64_t address, uint64_t size) override;

	/**
	 * \return block size, bytes
	 */

	size_t getBlockSize() const override;

	/**
	 * \return size of block device, bytes
	 */

	uint64_t getSize() const override;

	/**
	 * \brief Locks the device for exclusive use by current thread.
	 *
	 * When the object is locked, any call to any member function from other thread will be blocked until the object is
	 * unlocked. Locking is optional, but may be useful when more than one transaction must be done atomically.
	 *
	 * \note Locks are recursive.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre The number of recursive locks of device is less than 65535.
	 *
	 * \post Device is locked.
	 */

	void lock() override;

	/**
	 * \brief Opens device.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre The number of times the device is opened is less than 255.
	 * \pre Addresses of associated read and write buffers are aligned to `DISTORTOS_BLOCKDEVICE_BUFFER_ALIGNMENT`
	 * bytes.
	 * \pre Sizes of associated read and write buffers are non-zero multiples of associated block device's block size.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by BlockDevice::open();
	 */

	int open() override;

	/**
	 * \brief Reads data from a device.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 * \pre \a address and \a buffer and \a size are valid.
	 * \pre Selected range is within address space of device.
	 *
	 * \param [in] address is the address of data that will be read, must be a multiple of block size
	 * \param [out] buffer is the buffer into which the data will be read, must be valid
	 * \param [in] size is the size of \a buffer, bytes, must be a multiple of block size
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by readImplementation();
	 */

	int read(uint64_t address, void* buffer, size_t size) override;

	/**
	 * \brief Synchronizes state of a device, ensuring all cached writes are finished.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer();
	 * - error codes returned by BlockDevice::synchronize();
	 */

	int synchronize() override;

	/**
	 * \brief Unlocks the device which was previously locked by current thread.
	 *
	 * \note Locks are recursive.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre This function is called by the thread that locked the device.
	 */

	void unlock() override;

	/**
	 * \brief Writes data to a device.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 * \pre \a address and \a buffer and \a size are valid.
	 * \pre Selected range is within address space of device.
	 *
	 * \param [in] address is the address of data that will be written, must be a multiple of block size
	 * \param [in] buffer is the buffer with data that will be written, must be valid
	 * \param [in] size is the size of \a buffer, bytes, must be a multiple of block size
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer();
	 */

	int write(uint64_t address, const void* buffer, size_t size) override;

private:

	/**
	 * \brief Flushes whole write buffer to the associated block device.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer(size_t);
	 */

	int flushWriteBuffer()
	{
		return flushWriteBuffer(writeBufferValidSize_);
	}

	/**
	 * \brief Flushes write buffer to the associated block device.
	 *
	 * \param [in] size is the max amount of data that will be flushed, bytes, must be a multiple of block size
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by BlockDevice::write();
	 */

	int flushWriteBuffer(size_t size);

	/**
	 * \brief Implementation of read()
	 *
	 * \pre Selected range must either not intersect with read buffer or the address of intersection must be equal to
	 * the address of read buffer.
	 *
	 * \param [in] address is the address of data that will be read, must be a multiple of block size
	 * \param [out] buffer is the buffer into which the data will be read, must be valid
	 * \param [in] size is the size of \a buffer, bytes, must be a multiple of block size
	 * \param [in] deviceSize is the size of block device, bytes
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by BlockDevice::read();
	 */

	int readImplementation(uint64_t address, void* buffer, size_t size, uint64_t deviceSize);

	/// address of data in read buffer
	uint64_t readBufferAddress_;

	/// address of data in write buffer
	uint64_t writeBufferAddress_;

	/// reference to associated block device
	BlockDevice& blockDevice_;

	/// pointer to buffer for reads
	void* readBuffer_;

	/// size of \a readBuffer_, bytes
	size_t readBufferSize_;

	/// pointer to buffer for writes
	void* writeBuffer_;

	/// size of \a writeBuffer_, bytes
	size_t writeBufferSize_;

	/// amount of data pending to be written in write buffer, bytes
	size_t writeBufferValidSize_;

	/// number of times this device was opened but not yet closed
	uint8_t openCount_;

	/// true if read buffer holds valid data, false otherwise
	bool readBufferValid_;
};

}	// namespace devices

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_DEVICES_MEMORY_BUFFERINGBLOCKDEVICE_HPP_
<commit_msg>fixup BufferingBlockDevice<commit_after>/**
 * \file
 * \brief BufferingBlockDevice class header
 *
 * \author Copyright (C) 2019 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info
 *
 * \par License
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not
 * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#ifndef INCLUDE_DISTORTOS_DEVICES_MEMORY_BUFFERINGBLOCKDEVICE_HPP_
#define INCLUDE_DISTORTOS_DEVICES_MEMORY_BUFFERINGBLOCKDEVICE_HPP_

#include "distortos/devices/memory/BlockDevice.hpp"

namespace distortos
{

namespace devices
{

class BlockDevice;

/**
 * BufferingBlockDevice class is a buffering wrapper for BlockDevice.
 *
 * This class tries to minimize amount of BlockDevice operations by buffering both reads and writes. Such buffering can
 * give signigicant gain in case of devices like SD cards, where each operation may cause the device to become "busy"
 * for a noticeable amount of time. With this class several adjacent reads or writes can be combined into a single
 * larger operation, which is much faster overall when you also include the waits for the SD card to become "idle".
 *
 * Another use for this class is as a proxy between a file system and a block device which requires specific alignment.
 *
 * \ingroup devices
 */

class BufferingBlockDevice : public BlockDevice
{
public:

	/**
	 * \brief BufferingBlockDevice's constructor
	 *
	 * \param [in] blockDevice is a reference to associated block device
	 * \param [in] readBuffer is a pointer to buffer for reads, its address must be aligned to
	 * `DISTORTOS_BLOCKDEVICE_BUFFER_ALIGNMENT` bytes
	 * \param [in] readBufferSize is the size of \a readBuffer, bytes, must be a multiple of \a blockDevice block size
	 * \param [in] writeBuffer is a pointer to buffer for writes, its address must be aligned to
	 * `DISTORTOS_BLOCKDEVICE_BUFFER_ALIGNMENT` bytes
	 * \param [in] writeBufferSize is the size of \a writeBuffer, bytes, must be a multiple of \a blockDevice block size
	 */

	constexpr explicit BufferingBlockDevice(BlockDevice& blockDevice, void* const readBuffer,
			const size_t readBufferSize, void* const writeBuffer, const size_t writeBufferSize) :
					readBufferAddress_{},
					writeBufferAddress_{},
					blockDevice_{blockDevice},
					readBuffer_{readBuffer},
					readBufferSize_{readBufferSize},
					writeBuffer_{writeBuffer},
					writeBufferSize_{writeBufferSize},
					writeBufferValidSize_{},
					openCount_{},
					readBufferValid_{}
	{

	}

	/**
	 * \brief BufferingBlockDevice's destructor
	 *
	 * \pre Device is closed.
	 */

	~BufferingBlockDevice() override;

	/**
	 * \brief Closes device.
	 *
	 * \note Even if error code is returned, the device must not be used from the context which opened it (until it is
	 * successfully opened again).
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer();
	 * - error codes returned by BlockDevice::close();
	 */

	int close() override;

	/**
	 * \brief Erases blocks on a device.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 * \pre \a address and \a size are valid.
	 * \pre Selected range is within address space of device.
	 *
	 * \param [in] address is the address of range that will be erased, must be a multiple of block size
	 * \param [in] size is the size of erased range, bytes, must be a multiple of block size
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer(size_t);
	 * - error codes returned by BlockDevice::erase();
	 */

	int erase(uint64_t address, uint64_t size) override;

	/**
	 * \return block size, bytes
	 */

	size_t getBlockSize() const override;

	/**
	 * \return size of block device, bytes
	 */

	uint64_t getSize() const override;

	/**
	 * \brief Locks the device for exclusive use by current thread.
	 *
	 * When the object is locked, any call to any member function from other thread will be blocked until the object is
	 * unlocked. Locking is optional, but may be useful when more than one transaction must be done atomically.
	 *
	 * \note Locks are recursive.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre The number of recursive locks of device is less than 65535.
	 *
	 * \post Device is locked.
	 */

	void lock() override;

	/**
	 * \brief Opens device.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre The number of times the device is opened is less than 255.
	 * \pre Addresses of associated read and write buffers are aligned to `DISTORTOS_BLOCKDEVICE_BUFFER_ALIGNMENT`
	 * bytes.
	 * \pre Sizes of associated read and write buffers are non-zero multiples of associated block device's block size.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by BlockDevice::open();
	 */

	int open() override;

	/**
	 * \brief Reads data from a device.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 * \pre \a address and \a buffer and \a size are valid.
	 * \pre Selected range is within address space of device.
	 *
	 * \param [in] address is the address of data that will be read, must be a multiple of block size
	 * \param [out] buffer is the buffer into which the data will be read, must be valid
	 * \param [in] size is the size of \a buffer, bytes, must be a multiple of block size
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by readImplementation();
	 */

	int read(uint64_t address, void* buffer, size_t size) override;

	/**
	 * \brief Synchronizes state of a device, ensuring all cached writes are finished.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer();
	 * - error codes returned by BlockDevice::synchronize();
	 */

	int synchronize() override;

	/**
	 * \brief Unlocks the device which was previously locked by current thread.
	 *
	 * \note Locks are recursive.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre This function is called by the thread that locked the device.
	 */

	void unlock() override;

	/**
	 * \brief Writes data to a device.
	 *
	 * \warning This function must not be called from interrupt context!
	 *
	 * \pre Device is opened.
	 * \pre \a address and \a buffer and \a size are valid.
	 * \pre Selected range is within address space of device.
	 *
	 * \param [in] address is the address of data that will be written, must be a multiple of block size
	 * \param [in] buffer is the buffer with data that will be written, must be valid
	 * \param [in] size is the size of \a buffer, bytes, must be a multiple of block size
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer();
	 */

	int write(uint64_t address, const void* buffer, size_t size) override;

private:

	/**
	 * \brief Flushes whole write buffer to the associated block device.
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by flushWriteBuffer(size_t);
	 */

	int flushWriteBuffer()
	{
		return flushWriteBuffer(writeBufferValidSize_);
	}

	/**
	 * \brief Flushes write buffer to the associated block device.
	 *
	 * \param [in] size is the max amount of data that will be flushed, bytes, must be a multiple of block size
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by BlockDevice::write();
	 */

	int flushWriteBuffer(size_t size);

	/**
	 * \brief Implementation of read()
	 *
	 * \pre Selected range must either not intersect with read buffer or the address of intersection must be equal to
	 * the address of read buffer.
	 *
	 * \param [in] address is the address of data that will be read, must be a multiple of block size
	 * \param [out] buffer is the buffer into which the data will be read, must be valid
	 * \param [in] size is the size of \a buffer, bytes, must be a multiple of block size
	 * \param [in] deviceSize is the size of block device, bytes
	 *
	 * \return 0 on success, error code otherwise:
	 * - error codes returned by BlockDevice::read();
	 */

	int readImplementation(uint64_t address, void* buffer, size_t size, uint64_t deviceSize);

	/// address of data in read buffer
	uint64_t readBufferAddress_;

	/// address of data in write buffer
	uint64_t writeBufferAddress_;

	/// reference to associated block device
	BlockDevice& blockDevice_;

	/// pointer to buffer for reads
	void* readBuffer_;

	/// size of \a readBuffer_, bytes
	size_t readBufferSize_;

	/// pointer to buffer for writes
	void* writeBuffer_;

	/// size of \a writeBuffer_, bytes
	size_t writeBufferSize_;

	/// amount of data pending to be written in write buffer, bytes
	size_t writeBufferValidSize_;

	/// number of times this device was opened but not yet closed
	uint8_t openCount_;

	/// true if read buffer holds valid data, false otherwise
	bool readBufferValid_;
};

}	// namespace devices

}	// namespace distortos

#endif	// INCLUDE_DISTORTOS_DEVICES_MEMORY_BUFFERINGBLOCKDEVICE_HPP_
<|endoftext|>