<commit_before>// Author: Enrico Guiraud, Danilo Piparo CERN  03/2017

/*************************************************************************
 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "RConfigure.h" // R__USE_IMT
#include "ROOT/RDataSource.hxx"
#include "ROOT/RDF/RCustomColumnBase.hxx"
#include "ROOT/RDF/RLoopManager.hxx"
#include "RtypesCore.h"
#include "TBranch.h"
#include "TBranchElement.h"
#include "TClass.h"
#include "TClassEdit.h"
#include "TClassRef.h"
#include "TInterpreter.h"
#include "TLeaf.h"
#include "TROOT.h" // IsImplicitMTEnabled, GetThreadPoolSize
#include "TTree.h"

#include <stdexcept>
#include <string>
#include <cstring>
#include <typeinfo>

using namespace ROOT::Detail::RDF;
using namespace ROOT::RDF;

namespace ROOT {
namespace Internal {
namespace RDF {

/// Return the type_info associated to a name. If the association fails, an
/// exception is thrown.
/// References and pointers are not supported since those cannot be stored in
/// columns.
const std::type_info &TypeName2TypeID(const std::string &name)
{
   if (auto c = TClass::GetClass(name.c_str())) {
      return *c->GetTypeInfo();
   } else if (name == "char" || name == "Char_t")
      return typeid(char);
   else if (name == "unsigned char" || name == "UChar_t")
      return typeid(unsigned char);
   else if (name == "int" || name == "Int_t")
      return typeid(int);
   else if (name == "unsigned int" || name == "UInt_t")
      return typeid(unsigned int);
   else if (name == "short" || name == "Short_t")
      return typeid(short);
   else if (name == "unsigned short" || name == "UShort_t")
      return typeid(unsigned short);
   else if (name == "long" || name == "Long_t")
      return typeid(long);
   else if (name == "unsigned long" || name == "ULong_t")
      return typeid(unsigned long);
   else if (name == "double" || name == "Double_t")
      return typeid(double);
   else if (name == "float" || name == "Float_t")
      return typeid(float);
   else if (name == "long long" || name == "long long int" || name == "Long64_t")
      return typeid(Long64_t);
   else if (name == "unsigned long long" || name == "unsigned long long int" || name == "ULong64_t")
      return typeid(ULong64_t);
   else if (name == "bool" || name == "Bool_t")
      return typeid(bool);
   else {
      std::string msg("Cannot extract type_info of type ");
      msg += name.c_str();
      msg += ".";
      throw std::runtime_error(msg);
   }
}

/// Returns the name of a type starting from its type_info
/// An empty string is returned in case of failure
/// References and pointers are not supported since those cannot be stored in
/// columns.
std::string TypeID2TypeName(const std::type_info &id)
{
   if (auto c = TClass::GetClass(id)) {
      return c->GetName();
   } else if (id == typeid(char))
      return "char";
   else if (id == typeid(unsigned char))
      return "unsigned char";
   else if (id == typeid(int))
      return "int";
   else if (id == typeid(unsigned int))
      return "unsigned int";
   else if (id == typeid(short))
      return "short";
   else if (id == typeid(unsigned short))
      return "unsigned short";
   else if (id == typeid(long))
      return "long";
   else if (id == typeid(unsigned long))
      return "unsigned long";
   else if (id == typeid(double))
      return "double";
   else if (id == typeid(float))
      return "float";
   else if (id == typeid(Long64_t))
      return "Long64_t";
   else if (id == typeid(ULong64_t))
      return "ULong64_t";
   else if (id == typeid(bool))
      return "bool";
   else
      return "";
}

std::string ComposeRVecTypeName(const std::string &valueType)
{
   return "ROOT::VecOps::RVec<" + valueType + ">";
}

std::string GetLeafTypeName(TLeaf *leaf, const std::string &colName)
{
   std::string colType = leaf->GetTypeName();
   if (colType.empty())
      throw std::runtime_error("Could not deduce type of leaf " + colName);
   if (leaf->GetLeafCount() != nullptr && leaf->GetLenStatic() == 1) {
      // this is a variable-sized array
      colType = ComposeRVecTypeName(colType);
   } else if (leaf->GetLeafCount() == nullptr && leaf->GetLenStatic() > 1) {
      // this is a fixed-sized array (we do not differentiate between variable- and fixed-sized arrays)
      colType = ComposeRVecTypeName(colType);
   } else if (leaf->GetLeafCount() != nullptr && leaf->GetLenStatic() > 1) {
      // we do not know how to deal with this branch
      throw std::runtime_error("TTree leaf " + colName +
                               " has both a leaf count and a static length. This is not supported.");
   }

   return colType;
}

/// Return the typename of object colName stored in t, if any. Return an empty string if colName is not in t.
/// Supported cases:
/// - leaves corresponding to single values, variable- and fixed-length arrays, with following syntax:
///   - "leafname", as long as TTree::GetLeaf resolves it
///   - "b1.b2...leafname", as long as TTree::GetLeaf("b1.b2....", "leafname") resolves it
/// - TBranchElements, as long as TTree::GetBranch resolves their names
std::string GetBranchOrLeafTypeName(TTree &t, const std::string &colName)
{
   // look for TLeaf either with GetLeaf(colName) or with GetLeaf(branchName, leafName) (splitting on last dot)
   auto leaf = t.GetLeaf(colName.c_str());
   if (!leaf) {
      const auto dotPos = colName.find_last_of('.');
      const auto hasDot = dotPos != std::string::npos;
      if (hasDot) {
         const auto branchName = colName.substr(0, dotPos);
         const auto leafName = colName.substr(dotPos + 1);
         leaf = t.GetLeaf(branchName.c_str(), leafName.c_str());
      }
   }
   if (leaf)
      return GetLeafTypeName(leaf, colName);

   // we could not find a leaf named colName, so we look for a TBranchElement
   auto branch = t.GetBranch(colName.c_str());
   if (branch) {
      static const TClassRef tbranchelement("TBranchElement");
      if (branch->InheritsFrom(tbranchelement)) {
         auto be = static_cast<TBranchElement *>(branch);
         if (auto currentClass = be->GetCurrentClass())
            return currentClass->GetName();
         else {
            // Here we have a special case for getting right the type of data members
            // of classes sorted in TClonesArrays: ROOT-9674
            auto mother = be->GetMother();
            if (mother && mother->InheritsFrom(tbranchelement)) {
               auto beMom = static_cast<TBranchElement *>(mother);
               auto beMomClass = beMom->GetClass();
               if (beMomClass && 0 == std::strcmp("TClonesArray", beMomClass->GetName()))
                  return be->GetTypeName();
            }
            return be->GetClassName();
         }
      }
   }

   // colName is not a leaf nor a TBranchElement
   return std::string();
}

/// Return a string containing the type of the given branch. Works both with real TTree branches and with temporary
/// column created by Define. Throws if type name deduction fails.
/// Note that for fixed- or variable-sized c-style arrays the returned type name will be RVec<T>.
/// vector2rvec specifies whether typename 'std::vector<T>' should be converted to 'RVec<T>' or returned as is
/// customColID is only used if isCustomColumn is true, and must correspond to the custom column's unique identifier
/// returned by its `GetID()` method.
std::string ColumnName2ColumnTypeName(const std::string &colName, TTree *tree, RDataSource *ds,
                                      RCustomColumnBase *customColumn, bool vector2rvec)
{
   std::string colType;

   if (ds && ds->HasColumn(colName))
      colType = ds->GetTypeName(colName);

   if (colType.empty() && tree) {
      colType = GetBranchOrLeafTypeName(*tree, colName);
      if (vector2rvec && TClassEdit::IsSTLCont(colType) == ROOT::ESTLType::kSTLvector) {
         std::vector<std::string> split;
         int dummy;
         TClassEdit::GetSplit(colType.c_str(), split, dummy);
         auto &valueType = split[1];
         colType = ComposeRVecTypeName(valueType);
      }
   }

   if (colType.empty() && customColumn) {
      colType = customColumn->GetTypeName();
   }

   if (colType.empty())
      throw std::runtime_error("Column \"" + colName +
                               "\" is not in a dataset and is not a custom column been defined.");

   return colType;
}

/// Convert type name (e.g. "Float_t") to ROOT type code (e.g. 'F') -- see TBranch documentation.
/// Return a space ' ' in case no match was found.
char TypeName2ROOTTypeName(const std::string &b)
{
   if (b == "Char_t" || b == "char")
      return 'B';
   if (b == "UChar_t" || b == "unsigned char")
      return 'b';
   if (b == "Short_t" || b == "short" || b == "short int")
      return 'S';
   if (b == "UShort_t" || b == "unsigned short" || b == "unsigned short int")
      return 's';
   if (b == "Int_t" || b == "int")
      return 'I';
   if (b == "UInt_t" || b == "unsigned" || b == "unsigned int")
      return 'i';
   if (b == "Float_t" || b == "float")
      return 'F';
   if (b == "Double_t" || b == "double")
      return 'D';
   if (b == "Long64_t" || b == "long" || b == "long int")
      return 'L';
   if (b == "ULong64_t" || b == "unsigned long" || b == "unsigned long int")
      return 'l';
   if (b == "Bool_t" || b == "bool")
      return 'O';
   return ' ';
}

unsigned int GetNSlots()
{
   unsigned int nSlots = 1;
#ifdef R__USE_IMT
   if (ROOT::IsImplicitMTEnabled())
      nSlots = ROOT::GetThreadPoolSize();
#endif // R__USE_IMT
   return nSlots;
}

/// Replace occurrences of '.' with '_' in each string passed as argument.
/// An Info message is printed when this happens. Dots at the end of the string are not replaced.
/// An exception is thrown in case the resulting set of strings would contain duplicates.
std::vector<std::string> ReplaceDotWithUnderscore(const std::vector<std::string> &columnNames)
{
   auto newColNames = columnNames;
   for (auto &col : newColNames) {
      const auto dotPos = col.find('.');
      if (dotPos != std::string::npos && dotPos != col.size() - 1 && dotPos != 0u) {
         auto oldName = col;
         std::replace(col.begin(), col.end(), '.', '_');
         if (std::find(columnNames.begin(), columnNames.end(), col) != columnNames.end())
            throw std::runtime_error("Column " + oldName + " would be written as " + col +
                                     " but this column already exists. Please use Alias to select a new name for " +
                                     oldName);
         Info("Snapshot", "Column %s will be saved as %s", oldName.c_str(), col.c_str());
      }
   }

   return newColNames;
}

void InterpreterDeclare(const std::string &code)
{
   if (!gInterpreter->Declare(code.c_str())) {
      const auto msg =
         "\nRDataFrame: An error occurred during just-in-time compilation. The lines above might indicate the cause of "
         "the crash\n All RDF objects that have not run an event loop yet should be considered in an invalid state.\n";
      throw std::runtime_error(msg);
   }
}

Long64_t InterpreterCalc(const std::string &code, const std::string &context)
{
   TInterpreter::EErrorCode errorCode(TInterpreter::kNoError);
   auto res = gInterpreter->Calc(code.c_str(), &errorCode);
   if (errorCode != TInterpreter::EErrorCode::kNoError) {
      std::string msg = "\nAn error occurred during just-in-time compilation";
      if (!context.empty())
         msg += " in " + context;
      msg += ". The lines above might indicate the cause of the crash\nAll RDF objects that have not run their event "
             "loop yet should be considered in an invalid state.\n";
      throw std::runtime_error(msg);
   }
   return res;
}

} // end NS RDF
} // end NS Internal
} // end NS ROOT
<commit_msg>[DF] Fix type inference of TClonesArrays branches<commit_after>// Author: Enrico Guiraud, Danilo Piparo CERN  03/2017

/*************************************************************************
 * Copyright (C) 1995-2018, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "RConfigure.h" // R__USE_IMT
#include "ROOT/RDataSource.hxx"
#include "ROOT/RDF/RCustomColumnBase.hxx"
#include "ROOT/RDF/RLoopManager.hxx"
#include "RtypesCore.h"
#include "TBranch.h"
#include "TBranchElement.h"
#include "TClass.h"
#include "TClassEdit.h"
#include "TClassRef.h"
#include "TInterpreter.h"
#include "TLeaf.h"
#include "TROOT.h" // IsImplicitMTEnabled, GetThreadPoolSize
#include "TTree.h"

#include <stdexcept>
#include <string>
#include <cstring>
#include <typeinfo>

using namespace ROOT::Detail::RDF;
using namespace ROOT::RDF;

namespace ROOT {
namespace Internal {
namespace RDF {

/// Return the type_info associated to a name. If the association fails, an
/// exception is thrown.
/// References and pointers are not supported since those cannot be stored in
/// columns.
const std::type_info &TypeName2TypeID(const std::string &name)
{
   if (auto c = TClass::GetClass(name.c_str())) {
      return *c->GetTypeInfo();
   } else if (name == "char" || name == "Char_t")
      return typeid(char);
   else if (name == "unsigned char" || name == "UChar_t")
      return typeid(unsigned char);
   else if (name == "int" || name == "Int_t")
      return typeid(int);
   else if (name == "unsigned int" || name == "UInt_t")
      return typeid(unsigned int);
   else if (name == "short" || name == "Short_t")
      return typeid(short);
   else if (name == "unsigned short" || name == "UShort_t")
      return typeid(unsigned short);
   else if (name == "long" || name == "Long_t")
      return typeid(long);
   else if (name == "unsigned long" || name == "ULong_t")
      return typeid(unsigned long);
   else if (name == "double" || name == "Double_t")
      return typeid(double);
   else if (name == "float" || name == "Float_t")
      return typeid(float);
   else if (name == "long long" || name == "long long int" || name == "Long64_t")
      return typeid(Long64_t);
   else if (name == "unsigned long long" || name == "unsigned long long int" || name == "ULong64_t")
      return typeid(ULong64_t);
   else if (name == "bool" || name == "Bool_t")
      return typeid(bool);
   else {
      std::string msg("Cannot extract type_info of type ");
      msg += name.c_str();
      msg += ".";
      throw std::runtime_error(msg);
   }
}

/// Returns the name of a type starting from its type_info
/// An empty string is returned in case of failure
/// References and pointers are not supported since those cannot be stored in
/// columns.
std::string TypeID2TypeName(const std::type_info &id)
{
   if (auto c = TClass::GetClass(id)) {
      return c->GetName();
   } else if (id == typeid(char))
      return "char";
   else if (id == typeid(unsigned char))
      return "unsigned char";
   else if (id == typeid(int))
      return "int";
   else if (id == typeid(unsigned int))
      return "unsigned int";
   else if (id == typeid(short))
      return "short";
   else if (id == typeid(unsigned short))
      return "unsigned short";
   else if (id == typeid(long))
      return "long";
   else if (id == typeid(unsigned long))
      return "unsigned long";
   else if (id == typeid(double))
      return "double";
   else if (id == typeid(float))
      return "float";
   else if (id == typeid(Long64_t))
      return "Long64_t";
   else if (id == typeid(ULong64_t))
      return "ULong64_t";
   else if (id == typeid(bool))
      return "bool";
   else
      return "";
}

std::string ComposeRVecTypeName(const std::string &valueType)
{
   return "ROOT::VecOps::RVec<" + valueType + ">";
}

std::string GetLeafTypeName(TLeaf *leaf, const std::string &colName)
{
   std::string colType = leaf->GetTypeName();
   if (colType.empty())
      throw std::runtime_error("Could not deduce type of leaf " + colName);
   if (leaf->GetLeafCount() != nullptr && leaf->GetLenStatic() == 1) {
      // this is a variable-sized array
      colType = ComposeRVecTypeName(colType);
   } else if (leaf->GetLeafCount() == nullptr && leaf->GetLenStatic() > 1) {
      // this is a fixed-sized array (we do not differentiate between variable- and fixed-sized arrays)
      colType = ComposeRVecTypeName(colType);
   } else if (leaf->GetLeafCount() != nullptr && leaf->GetLenStatic() > 1) {
      // we do not know how to deal with this branch
      throw std::runtime_error("TTree leaf " + colName +
                               " has both a leaf count and a static length. This is not supported.");
   }

   return colType;
}

/// Return the typename of object colName stored in t, if any. Return an empty string if colName is not in t.
/// Supported cases:
/// - leaves corresponding to single values, variable- and fixed-length arrays, with following syntax:
///   - "leafname", as long as TTree::GetLeaf resolves it
///   - "b1.b2...leafname", as long as TTree::GetLeaf("b1.b2....", "leafname") resolves it
/// - TBranchElements, as long as TTree::GetBranch resolves their names
std::string GetBranchOrLeafTypeName(TTree &t, const std::string &colName)
{
   // look for TLeaf either with GetLeaf(colName) or with GetLeaf(branchName, leafName) (splitting on last dot)
   auto leaf = t.GetLeaf(colName.c_str());
   if (!leaf) {
      const auto dotPos = colName.find_last_of('.');
      const auto hasDot = dotPos != std::string::npos;
      if (hasDot) {
         const auto branchName = colName.substr(0, dotPos);
         const auto leafName = colName.substr(dotPos + 1);
         leaf = t.GetLeaf(branchName.c_str(), leafName.c_str());
      }
   }
   if (leaf)
      return GetLeafTypeName(leaf, colName);

   // we could not find a leaf named colName, so we look for a TBranchElement
   auto branch = t.GetBranch(colName.c_str());
   if (branch) {
      static const TClassRef tbranchelement("TBranchElement");
      if (branch->InheritsFrom(tbranchelement)) {
         auto be = static_cast<TBranchElement *>(branch);
         if (auto currentClass = be->GetCurrentClass())
            return currentClass->GetName();
         else {
            // Here we have a special case for getting right the type of data members
            // of classes sorted in TClonesArrays: ROOT-9674
            auto mother = be->GetMother();
            if (mother && mother->InheritsFrom(tbranchelement) && mother != be) {
               auto beMom = static_cast<TBranchElement *>(mother);
               auto beMomClass = beMom->GetClass();
               if (beMomClass && 0 == std::strcmp("TClonesArray", beMomClass->GetName()))
                  return be->GetTypeName();
            }
            return be->GetClassName();
         }
      }
   }

   // colName is not a leaf nor a TBranchElement
   return std::string();
}

/// Return a string containing the type of the given branch. Works both with real TTree branches and with temporary
/// column created by Define. Throws if type name deduction fails.
/// Note that for fixed- or variable-sized c-style arrays the returned type name will be RVec<T>.
/// vector2rvec specifies whether typename 'std::vector<T>' should be converted to 'RVec<T>' or returned as is
/// customColID is only used if isCustomColumn is true, and must correspond to the custom column's unique identifier
/// returned by its `GetID()` method.
std::string ColumnName2ColumnTypeName(const std::string &colName, TTree *tree, RDataSource *ds,
                                      RCustomColumnBase *customColumn, bool vector2rvec)
{
   std::string colType;

   if (ds && ds->HasColumn(colName))
      colType = ds->GetTypeName(colName);

   if (colType.empty() && tree) {
      colType = GetBranchOrLeafTypeName(*tree, colName);
      if (vector2rvec && TClassEdit::IsSTLCont(colType) == ROOT::ESTLType::kSTLvector) {
         std::vector<std::string> split;
         int dummy;
         TClassEdit::GetSplit(colType.c_str(), split, dummy);
         auto &valueType = split[1];
         colType = ComposeRVecTypeName(valueType);
      }
   }

   if (colType.empty() && customColumn) {
      colType = customColumn->GetTypeName();
   }

   if (colType.empty())
      throw std::runtime_error("Column \"" + colName +
                               "\" is not in a dataset and is not a custom column been defined.");

   return colType;
}

/// Convert type name (e.g. "Float_t") to ROOT type code (e.g. 'F') -- see TBranch documentation.
/// Return a space ' ' in case no match was found.
char TypeName2ROOTTypeName(const std::string &b)
{
   if (b == "Char_t" || b == "char")
      return 'B';
   if (b == "UChar_t" || b == "unsigned char")
      return 'b';
   if (b == "Short_t" || b == "short" || b == "short int")
      return 'S';
   if (b == "UShort_t" || b == "unsigned short" || b == "unsigned short int")
      return 's';
   if (b == "Int_t" || b == "int")
      return 'I';
   if (b == "UInt_t" || b == "unsigned" || b == "unsigned int")
      return 'i';
   if (b == "Float_t" || b == "float")
      return 'F';
   if (b == "Double_t" || b == "double")
      return 'D';
   if (b == "Long64_t" || b == "long" || b == "long int")
      return 'L';
   if (b == "ULong64_t" || b == "unsigned long" || b == "unsigned long int")
      return 'l';
   if (b == "Bool_t" || b == "bool")
      return 'O';
   return ' ';
}

unsigned int GetNSlots()
{
   unsigned int nSlots = 1;
#ifdef R__USE_IMT
   if (ROOT::IsImplicitMTEnabled())
      nSlots = ROOT::GetThreadPoolSize();
#endif // R__USE_IMT
   return nSlots;
}

/// Replace occurrences of '.' with '_' in each string passed as argument.
/// An Info message is printed when this happens. Dots at the end of the string are not replaced.
/// An exception is thrown in case the resulting set of strings would contain duplicates.
std::vector<std::string> ReplaceDotWithUnderscore(const std::vector<std::string> &columnNames)
{
   auto newColNames = columnNames;
   for (auto &col : newColNames) {
      const auto dotPos = col.find('.');
      if (dotPos != std::string::npos && dotPos != col.size() - 1 && dotPos != 0u) {
         auto oldName = col;
         std::replace(col.begin(), col.end(), '.', '_');
         if (std::find(columnNames.begin(), columnNames.end(), col) != columnNames.end())
            throw std::runtime_error("Column " + oldName + " would be written as " + col +
                                     " but this column already exists. Please use Alias to select a new name for " +
                                     oldName);
         Info("Snapshot", "Column %s will be saved as %s", oldName.c_str(), col.c_str());
      }
   }

   return newColNames;
}

void InterpreterDeclare(const std::string &code)
{
   if (!gInterpreter->Declare(code.c_str())) {
      const auto msg =
         "\nRDataFrame: An error occurred during just-in-time compilation. The lines above might indicate the cause of "
         "the crash\n All RDF objects that have not run an event loop yet should be considered in an invalid state.\n";
      throw std::runtime_error(msg);
   }
}

Long64_t InterpreterCalc(const std::string &code, const std::string &context)
{
   TInterpreter::EErrorCode errorCode(TInterpreter::kNoError);
   auto res = gInterpreter->Calc(code.c_str(), &errorCode);
   if (errorCode != TInterpreter::EErrorCode::kNoError) {
      std::string msg = "\nAn error occurred during just-in-time compilation";
      if (!context.empty())
         msg += " in " + context;
      msg += ". The lines above might indicate the cause of the crash\nAll RDF objects that have not run their event "
             "loop yet should be considered in an invalid state.\n";
      throw std::runtime_error(msg);
   }
   return res;
}

} // end NS RDF
} // end NS Internal
} // end NS ROOT
<|endoftext|>


<commit_before>//
// Copyright 2017 Shivansh Rai
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// $FreeBSD$

#include <dirent.h>
#include <fstream>
#include <iostream>
#include <string>
#include "read_annotations.h"

void
annotations::read_annotations(std::string utility,
                              std::unordered_set<char>& annot)
{
  std::string line;
  // TODO do this for all the annotation files
  std::ifstream annot_fstream;
  annot_fstream.open("annotations/" + utility + "_test.annot");

  while (getline(annot_fstream, line)) {
    // Add a unique identifier for no_arguments testcase
    if (line.compare(2, 4, "flag")) {
      annot.insert('*');
      continue;
    }
    annot.insert(line[0]);
  }

  annot_fstream.close();
}
<commit_msg>Fix condition for annotating "no_arguments" testcase<commit_after>//
// Copyright 2017 Shivansh Rai
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.
//
// $FreeBSD$

#include <dirent.h>
#include <fstream>
#include <iostream>
#include <string>
#include "read_annotations.h"

void
annotations::read_annotations(std::string utility,
                              std::unordered_set<char>& annot)
{
  std::string line;
  // TODO do this for all the annotation files
  std::ifstream annot_fstream;
  annot_fstream.open("annotations/" + utility + "_test.annot");

  while (getline(annot_fstream, line)) {
    // Add a unique identifier for no_arguments testcase
    if (!line.compare(0, 12, "no_arguments"))
      annot.insert('*');
    // Add flag value for supported argument testcases
    // Doing so we ignore the "invalid_usage" testcase
    // as it is guaranteed to always succeed.
    else if (!line.compare(2, 4, "flag"))
      annot.insert(line[0]);
  }

  annot_fstream.close();
}
<|endoftext|>


<commit_before>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/** @file
* Tests for the Gauss-Seidel implementation of the MLCP solver.
*/

#include <math.h>
#include <memory>
#include <string>

#include "gtest/gtest.h"
#include <boost/chrono.hpp>

#include <SurgSim/Math/Valid.h>
#include <SurgSim/Math/MlcpGaussSeidelSolver.h>
#include <SurgSim/Math/MlcpSolution.h>
#include "MlcpTestData.h"
#include "ReadText.h"

using SurgSim::Math::isValid;
using SurgSim::Math::MlcpGaussSeidelSolver;

static std::shared_ptr<MlcpTestData> loadTestData(const std::string& fileName)
{
	std::shared_ptr<MlcpTestData> data = std::make_shared<MlcpTestData>();
	if (! readMlcpTestDataAsText("MlcpTestData/" + fileName, data.get()))
	{
		data.reset();
	}
	return data;
}

static inline std::string getTestFileName(const std::string& prefix, int index, const std::string& suffix)
{
	std::ostringstream stream;
	stream << prefix << std::setfill('0') << std::setw(3) << index << suffix;
	return stream.str();
}

TEST(MlcpGaussSeidelSolverTests, CanConstruct)
{
	//ASSERT_NO_THROW({
	MlcpGaussSeidelSolver mlcpSolver(1.0, 1.0, 100);
}

static void solveAndCompareResult(const std::string& fileName, bool compare = false,
						  double gsSolverPrecision = 1e-9, double gsContactTolerance = 1e-9, int gsMaxIterations = 100)
{
	SCOPED_TRACE("while running test " + fileName);
	printf("-- TEST %s --\n", fileName.c_str());

	const std::shared_ptr<MlcpTestData> data = loadTestData(fileName);
	ASSERT_TRUE(data) << "Failed to load " << fileName;

	// NB: need to make the solver calls const-correct.
	Eigen::MatrixXd A = data->problem.A;
	Eigen::VectorXd b = data->problem.b;
	Eigen::VectorXd mu = data->problem.mu;
	std::vector<SurgSim::Math::MlcpConstraintType> constraintTypes = data->problem.constraintTypes;

	const int size = data->getSize();
	SurgSim::Math::MlcpSolution solution;
	solution.x.resize(size);

	//################################
	// Gauss-Seidel solver
	MlcpGaussSeidelSolver mlcpSolver(gsSolverPrecision, gsContactTolerance,
									 gsMaxIterations);

	printf("  ### Gauss Seidel solver:\n");
	solution.x.setZero();

	// XXX set ratio to 1
	bool res = mlcpSolver.solve(data->problem, &solution);

	printf("\tsolver did %d iterations convergence=%d Signorini=%d\n",
		   solution.numIterations, solution.validConvergence ? 1 : 0, solution.validSignorini ? 1 : 0);

	ASSERT_EQ(size, solution.x.rows());
	ASSERT_EQ(size, data->expectedLambda.rows());
	if (size > 0)
	{
		ASSERT_TRUE(isValid(solution.x)) << solution.x;
	}

	// TODO(advornik): Because this is a mixed LCP problem *with friction*, we can't just easily check that
	//   all x are positive (the frictional entries may not be), or that all Ax+b are positive(ditto).
	//   We need to either (a) make the test aware of the meaning of the constraint types, or (b) get rid
	//   of the constraint types and flag the frictional DOFs more directly.
	//
	//   For now, we check if the MLCP is really a pure LCP (only unilaterals without friction), and we
	//   perform some simple checks that apply to that case and that case only.
	bool isSimpleLcp = true;
	for (auto it = constraintTypes.cbegin();  it != constraintTypes.cend();  ++it)
	{
		if ((*it) != SurgSim::Math::MLCP_UNILATERAL_3D_FRICTIONLESS_CONSTRAINT)
		{
			isSimpleLcp = false;
		}
	}

	if (isSimpleLcp && (size > 0))
	{
		EXPECT_GE(solution.x.minCoeff(), 0.0) << "x contains negative coefficients:" << std::endl << solution.x;

		Eigen::VectorXd c = A * solution.x + b;
		EXPECT_GE(c.minCoeff(), -gsSolverPrecision) << "Ax+b contains negative coefficients:" << std::endl << c;

		// Orthogonality test should be taking into account the scaling factor
		double maxAbsForce = abs(solution.x.maxCoeff());
		if (abs(solution.x.minCoeff()) > maxAbsForce)
		{
			maxAbsForce = abs(solution.x.minCoeff());
		}
		const double epsilon = 1e-9 * maxAbsForce;
		EXPECT_NEAR(0.0, c.dot(solution.x), epsilon) << "Ax+b is not orthogonal to x!" << std::endl <<
			"x:" << std::endl << solution.x << std::endl << "Ax+b:" << std::endl << c;
	}

	if (compare)
	{
		EXPECT_TRUE(solution.x.isApprox(data->expectedLambda)) << "lambda:" << std::endl << solution.x << std::endl <<
			"expected:" << std::endl << data->expectedLambda;
	}

//	double convergenceCriteria=0.0;
//	bool validSignorini=false;
//	int nbContactToSkip=0;
//	int currentAtomicIndex = calculateConvergenceCriteria(lambda, nbContactToSkip, convergenceCriteria,
//		validSignorini, 1.0);
//	printf("\tStatus method final [convergence criteria=%g, Signorini=%d]\n",convergenceCriteria,validSignorini?1:0);
	printf("############\n");
}

TEST(MlcpGaussSeidelSolverTests, SolveOriginal)
{
	const double gsSolverPrecision = 1e-4;
	const double gsContactTolerance = 2e-4;
	int gsMaxIterations = 30;
	solveAndCompareResult("mlcpOriginalTest.txt", false, gsSolverPrecision, gsContactTolerance, gsMaxIterations);
}

TEST(MlcpGaussSeidelSolverTests, CompareResultOriginal)
{
	const double gsSolverPrecision = 1e-4;
	const double gsContactTolerance = 2e-4;
	int gsMaxIterations = 30;
	solveAndCompareResult("mlcpOriginalTest.txt", true, gsSolverPrecision, gsContactTolerance, gsMaxIterations);
}

TEST(MlcpGaussSeidelSolverTests, solveSequence)
{
	for (int i = 0;  i <= 9;  ++i)
	{
		solveAndCompareResult(getTestFileName("mlcpTest", i, ".txt"), false);
	}
}

TEST(MlcpGaussSeidelSolverTests, CompareResultsSequence)
{
	for (int i = 0;  i <= 9;  ++i)
	{
		solveAndCompareResult(getTestFileName("mlcpTest", i, ".txt"), true);
	}
}

static void solveRepeatedly(const MlcpTestData& data,
							/*XXX const */ MlcpGaussSeidelSolver* mlcpSolver,
							const int repetitions)
{
	// NB: need to make the solver calls const-correct.
	SurgSim::Math::MlcpProblem problem;
	SurgSim::Math::MlcpSolution solution;
	std::vector<SurgSim::Math::MlcpConstraintType> constraintTypes;

	const int size = data.getSize();
	solution.x.resize(size);

	for (int i = repetitions;  i > 0;  --i)
	{
		problem = data.problem;

		if (mlcpSolver)
		{
			solution.x.setZero();
			bool res = mlcpSolver->solve(problem, &solution);
		}
	}
}

static double measureExecutionTimeUsec(const std::string& fileName,
									   double gsSolverPrecision = 1e-8, double gsContactTolerance = 1e-8,
									   int gsMaxIterations = 20)
{
	SCOPED_TRACE("while running test " + fileName);
	printf("-- TEST %s --\n", fileName.c_str());

	const std::shared_ptr<MlcpTestData> data = loadTestData(fileName);
	EXPECT_TRUE(data) << "Failed to load " << fileName;

	MlcpGaussSeidelSolver mlcpSolver(gsSolverPrecision, gsContactTolerance, gsMaxIterations);

	typedef boost::chrono::high_resolution_clock clock;
	clock::time_point calibrationStart = clock::now();

	const int calibrationRepetitions = 1000;
	solveRepeatedly(*data, &mlcpSolver, calibrationRepetitions);

	boost::chrono::duration<double> calibrationTime = clock::now() - calibrationStart;
	double desiredTestTimeSec = 1.0;
	double desiredRepetitions = desiredTestTimeSec * calibrationRepetitions / calibrationTime.count();
	const int repetitions = std::max(10, std::min(1000000, static_cast<int>(desiredRepetitions)));

	clock::time_point time0 = clock::now();

	// Do not actually solve the problem; just copy the input data.
	solveRepeatedly(*data, nullptr, repetitions);

	clock::time_point time1 = clock::now();

	// Actually solve the problem.
	solveRepeatedly(*data, &mlcpSolver, repetitions);

	clock::time_point time2 = clock::now();

	boost::chrono::duration<double> elapsedSolveTime = time2 - time1;
	//std::cout << "Elapsed:               " << (elapsedSolveTime.count() * 1000.) << " ms" << std::endl;
	boost::chrono::duration<double> elapsedBaseline = time1 - time0;
	//std::cout << "Baseline:              " << (elapsedBaseline.count() * 1000.) << " ms" << std::endl;
	double solveTimeUsec = (elapsedSolveTime - elapsedBaseline).count() * 1e6 / repetitions;
	double copyTimeUsec = elapsedBaseline.count() * 1e6 / repetitions;
	std::cout << "Average solution time: " << solveTimeUsec << " microseconds (over " <<
		repetitions << " loops)" << std::endl;
	return solveTimeUsec;
}

TEST(MlcpGaussSeidelSolverTests, MeasureExecutionTimeOriginal)
{
	const double gsSolverPrecision = 1e-4;
	const double gsContactTolerance = 2e-5;
	int gsMaxIterations = 30;
	double solveTimeUsec = measureExecutionTimeUsec("mlcpOriginalTest.txt", gsSolverPrecision, gsContactTolerance,
													gsMaxIterations);

	// When refactoring the MLCP code, it can be very useful to compare the execution time before and after making
	// changes.  But you can't usefully compare execution times on different machines, or with different build
	// settings, so you'll need to manually uncomment the following line and adjust the time accordingly.
//	EXPECT_LE(solveTimeUsec, 14.0);
}
<commit_msg>Get rid of the split between solve/compare tests in MLCPGauss-SeidelSolverTests.<commit_after>// This file is a part of the OpenSurgSim project.
// Copyright 2013, SimQuest Solutions Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/** @file
* Tests for the Gauss-Seidel implementation of the MLCP solver.
*/

#include <math.h>
#include <memory>
#include <string>

#include "gtest/gtest.h"
#include <boost/chrono.hpp>

#include <SurgSim/Math/Valid.h>
#include <SurgSim/Math/MlcpGaussSeidelSolver.h>
#include <SurgSim/Math/MlcpSolution.h>
#include "MlcpTestData.h"
#include "ReadText.h"

using SurgSim::Math::isValid;
using SurgSim::Math::MlcpGaussSeidelSolver;

static std::shared_ptr<MlcpTestData> loadTestData(const std::string& fileName)
{
	std::shared_ptr<MlcpTestData> data = std::make_shared<MlcpTestData>();
	if (! readMlcpTestDataAsText("MlcpTestData/" + fileName, data.get()))
	{
		data.reset();
	}
	return data;
}

static inline std::string getTestFileName(const std::string& prefix, int index, const std::string& suffix)
{
	std::ostringstream stream;
	stream << prefix << std::setfill('0') << std::setw(3) << index << suffix;
	return stream.str();
}

TEST(MlcpGaussSeidelSolverTests, CanConstruct)
{
	//ASSERT_NO_THROW({
	MlcpGaussSeidelSolver mlcpSolver(1.0, 1.0, 100);
}

static void solveAndCompareResult(const std::string& fileName,
						  double gsSolverPrecision = 1e-9, double gsContactTolerance = 1e-9, int gsMaxIterations = 100)
{
	SCOPED_TRACE("while running test " + fileName);
	printf("-- TEST %s --\n", fileName.c_str());

	const std::shared_ptr<MlcpTestData> data = loadTestData(fileName);
	ASSERT_TRUE(data) << "Failed to load " << fileName;

	// NB: need to make the solver calls const-correct.
	Eigen::MatrixXd A = data->problem.A;
	Eigen::VectorXd b = data->problem.b;
	Eigen::VectorXd mu = data->problem.mu;
	std::vector<SurgSim::Math::MlcpConstraintType> constraintTypes = data->problem.constraintTypes;

	const int size = data->getSize();
	SurgSim::Math::MlcpSolution solution;
	solution.x.resize(size);

	//################################
	// Gauss-Seidel solver
	MlcpGaussSeidelSolver mlcpSolver(gsSolverPrecision, gsContactTolerance,
									 gsMaxIterations);

	printf("  ### Gauss Seidel solver:\n");
	solution.x.setZero();

	// XXX set ratio to 1
	bool res = mlcpSolver.solve(data->problem, &solution);

	printf("\tsolver did %d iterations convergence=%d Signorini=%d\n",
		   solution.numIterations, solution.validConvergence ? 1 : 0, solution.validSignorini ? 1 : 0);

	ASSERT_EQ(size, solution.x.rows());
	ASSERT_EQ(size, data->expectedLambda.rows());
	if (size > 0)
	{
		ASSERT_TRUE(isValid(solution.x)) << solution.x;
	}

	// TODO(advornik): Because this is a mixed LCP problem *with friction*, we can't just easily check that
	//   all x are positive (the frictional entries may not be), or that all Ax+b are positive(ditto).
	//   We need to either (a) make the test aware of the meaning of the constraint types, or (b) get rid
	//   of the constraint types and flag the frictional DOFs more directly.
	//
	//   For now, we check if the MLCP is really a pure LCP (only unilaterals without friction), and we
	//   perform some simple checks that apply to that case and that case only.
	bool isSimpleLcp = true;
	for (auto it = constraintTypes.cbegin();  it != constraintTypes.cend();  ++it)
	{
		if ((*it) != SurgSim::Math::MLCP_UNILATERAL_3D_FRICTIONLESS_CONSTRAINT)
		{
			isSimpleLcp = false;
		}
	}

	if (isSimpleLcp && (size > 0))
	{
		EXPECT_GE(solution.x.minCoeff(), 0.0) << "x contains negative coefficients:" << std::endl << solution.x;

		Eigen::VectorXd c = A * solution.x + b;
		EXPECT_GE(c.minCoeff(), -gsSolverPrecision) << "Ax+b contains negative coefficients:" << std::endl << c;

		// Orthogonality test should be taking into account the scaling factor
		double maxAbsForce = abs(solution.x.maxCoeff());
		if (abs(solution.x.minCoeff()) > maxAbsForce)
		{
			maxAbsForce = abs(solution.x.minCoeff());
		}
		const double epsilon = 1e-9 * maxAbsForce;
		EXPECT_NEAR(0.0, c.dot(solution.x), epsilon) << "Ax+b is not orthogonal to x!" << std::endl <<
			"x:" << std::endl << solution.x << std::endl << "Ax+b:" << std::endl << c;
	}

	EXPECT_TRUE(solution.x.isApprox(data->expectedLambda)) << "lambda:" << std::endl << solution.x << std::endl <<
		"expected:" << std::endl << data->expectedLambda;

//	double convergenceCriteria=0.0;
//	bool validSignorini=false;
//	int nbContactToSkip=0;
//	int currentAtomicIndex = calculateConvergenceCriteria(lambda, nbContactToSkip, convergenceCriteria,
//		validSignorini, 1.0);
//	printf("\tStatus method final [convergence criteria=%g, Signorini=%d]\n",convergenceCriteria,validSignorini?1:0);
	printf("############\n");
}

TEST(MlcpGaussSeidelSolverTests, SolveOriginal)
{
	const double gsSolverPrecision = 1e-4;
	const double gsContactTolerance = 2e-4;
	int gsMaxIterations = 30;
	solveAndCompareResult("mlcpOriginalTest.txt", gsSolverPrecision, gsContactTolerance, gsMaxIterations);
}

TEST(MlcpGaussSeidelSolverTests, SolveSequence)
{
	for (int i = 0;  i <= 9;  ++i)
	{
		solveAndCompareResult(getTestFileName("mlcpTest", i, ".txt"));
	}
}

static void solveRepeatedly(const MlcpTestData& data,
							/*XXX const */ MlcpGaussSeidelSolver* mlcpSolver,
							const int repetitions)
{
	// NB: need to make the solver calls const-correct.
	SurgSim::Math::MlcpProblem problem;
	SurgSim::Math::MlcpSolution solution;
	std::vector<SurgSim::Math::MlcpConstraintType> constraintTypes;

	const int size = data.getSize();
	solution.x.resize(size);

	for (int i = repetitions;  i > 0;  --i)
	{
		problem = data.problem;

		if (mlcpSolver)
		{
			solution.x.setZero();
			bool res = mlcpSolver->solve(problem, &solution);
		}
	}
}

static double measureExecutionTimeUsec(const std::string& fileName,
									   double gsSolverPrecision = 1e-8, double gsContactTolerance = 1e-8,
									   int gsMaxIterations = 20)
{
	SCOPED_TRACE("while running test " + fileName);
	printf("-- TEST %s --\n", fileName.c_str());

	const std::shared_ptr<MlcpTestData> data = loadTestData(fileName);
	EXPECT_TRUE(data) << "Failed to load " << fileName;

	MlcpGaussSeidelSolver mlcpSolver(gsSolverPrecision, gsContactTolerance, gsMaxIterations);

	typedef boost::chrono::high_resolution_clock clock;
	clock::time_point calibrationStart = clock::now();

	const int calibrationRepetitions = 1000;
	solveRepeatedly(*data, &mlcpSolver, calibrationRepetitions);

	boost::chrono::duration<double> calibrationTime = clock::now() - calibrationStart;
	double desiredTestTimeSec = 1.0;
	double desiredRepetitions = desiredTestTimeSec * calibrationRepetitions / calibrationTime.count();
	const int repetitions = std::max(10, std::min(1000000, static_cast<int>(desiredRepetitions)));

	clock::time_point time0 = clock::now();

	// Do not actually solve the problem; just copy the input data.
	solveRepeatedly(*data, nullptr, repetitions);

	clock::time_point time1 = clock::now();

	// Actually solve the problem.
	solveRepeatedly(*data, &mlcpSolver, repetitions);

	clock::time_point time2 = clock::now();

	boost::chrono::duration<double> elapsedSolveTime = time2 - time1;
	//std::cout << "Elapsed:               " << (elapsedSolveTime.count() * 1000.) << " ms" << std::endl;
	boost::chrono::duration<double> elapsedBaseline = time1 - time0;
	//std::cout << "Baseline:              " << (elapsedBaseline.count() * 1000.) << " ms" << std::endl;
	double solveTimeUsec = (elapsedSolveTime - elapsedBaseline).count() * 1e6 / repetitions;
	double copyTimeUsec = elapsedBaseline.count() * 1e6 / repetitions;
	std::cout << "Average solution time: " << solveTimeUsec << " microseconds (over " <<
		repetitions << " loops)" << std::endl;
	return solveTimeUsec;
}

TEST(MlcpGaussSeidelSolverTests, MeasureExecutionTimeOriginal)
{
	const double gsSolverPrecision = 1e-4;
	const double gsContactTolerance = 2e-5;
	int gsMaxIterations = 30;
	double solveTimeUsec = measureExecutionTimeUsec("mlcpOriginalTest.txt", gsSolverPrecision, gsContactTolerance,
													gsMaxIterations);

	// When refactoring the MLCP code, it can be very useful to compare the execution time before and after making
	// changes.  But you can't usefully compare execution times on different machines, or with different build
	// settings, so you'll need to manually uncomment the following line and adjust the time accordingly.
//	EXPECT_LE(solveTimeUsec, 14.0);
}
<|endoftext|>


<commit_before>/**
 * Zillians MMO
 * Copyright (C) 2007-2011 Zillians.com, Inc.
 * For more information see http://www.zillians.com
 *
 * Zillians MMO is the library and runtime for massive multiplayer online game
 * development in utility computing model, which runs as a service for every
 * developer to build their virtual world running on our GPU-assisted machines.
 *
 * This is a close source library intended to be used solely within Zillians.com
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "language/stage/generator/LLVMGeneratorStage.h"
#include "language/stage/generator/detail/LLVMForeach.h"
#include "language/context/ParserContext.h"
#include "language/context/GeneratorContext.h"

#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#include <llvm/Function.h>
#include <llvm/PassManager.h>
#include <llvm/CallingConv.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Assembly/PrintModulePass.h>
#include <llvm/Support/IRBuilder.h>

namespace zillians { namespace language { namespace stage {

LLVMGeneratorStage::LLVMGeneratorStage()
{ }

LLVMGeneratorStage::~LLVMGeneratorStage()
{ }

const char* LLVMGeneratorStage::name()
{
	return "llvm_generator";
}

void LLVMGeneratorStage::initializeOptions(po::options_description& option_desc, po::positional_options_description& positional_desc)
{
    option_desc.add_options()
    ("llvm-module-name", po::value<std::string>(),	"llvm module name");
}

bool LLVMGeneratorStage::parseOptions(po::variables_map& vm)
{
	if(vm.count("llvm-module-name") == 0)
	{
		llvm_module_name = "default_module";
	}
	else if(vm.count("llvm-module-name") == 1)
	{
		llvm_module_name = vm["llvm-module-name"].as<std::string>();
	}
	else
	{
		return false;
	}

	return true;
}

bool LLVMGeneratorStage::execute()
{
	llvm::Module* module = new llvm::Module(llvm_module_name, llvm::getGlobalContext());
	// TODO make a visitor to walk through the entire tree and generate instructions accordingly
	getGeneratorContext().modules.push_back(module);
	return true;
}

} } }
<commit_msg>initialize GeneratorContext<commit_after>/**
 * Zillians MMO
 * Copyright (C) 2007-2011 Zillians.com, Inc.
 * For more information see http://www.zillians.com
 *
 * Zillians MMO is the library and runtime for massive multiplayer online game
 * development in utility computing model, which runs as a service for every
 * developer to build their virtual world running on our GPU-assisted machines.
 *
 * This is a close source library intended to be used solely within Zillians.com
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include "language/stage/generator/LLVMGeneratorStage.h"
#include "language/stage/generator/detail/LLVMForeach.h"
#include "language/context/ParserContext.h"
#include "language/context/GeneratorContext.h"

#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#include <llvm/Function.h>
#include <llvm/PassManager.h>
#include <llvm/CallingConv.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Assembly/PrintModulePass.h>
#include <llvm/Support/IRBuilder.h>

namespace zillians { namespace language { namespace stage {

LLVMGeneratorStage::LLVMGeneratorStage()
{ }

LLVMGeneratorStage::~LLVMGeneratorStage()
{ }

const char* LLVMGeneratorStage::name()
{
	return "llvm_generator";
}

void LLVMGeneratorStage::initializeOptions(po::options_description& option_desc, po::positional_options_description& positional_desc)
{
    option_desc.add_options()
    ("llvm-module-name", po::value<std::string>(),	"llvm module name");
}

bool LLVMGeneratorStage::parseOptions(po::variables_map& vm)
{
	if(vm.count("llvm-module-name") == 0)
	{
		llvm_module_name = "default_module";
	}
	else if(vm.count("llvm-module-name") == 1)
	{
		llvm_module_name = vm["llvm-module-name"].as<std::string>();
	}
	else
	{
		return false;
	}

	return true;
}

bool LLVMGeneratorStage::execute()
{
	setGeneratorContext(new GeneratorContext());

	llvm::Module* module = new llvm::Module(llvm_module_name, llvm::getGlobalContext());
	// TODO make a visitor to walk through the entire tree and generate instructions accordingly
	getGeneratorContext().modules.push_back(module);
	return true;
}

} } }
<|endoftext|>


<commit_before>/**
 *  @copyright Copyright 2016 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *  @file TaskC.cpp
 */

#include <iostream>
#include <JPetWriter/JPetWriter.h>
#include <JPetAnalysisTools/JPetAnalysisTools.h>
#include "TaskC.h"
#include <algorithm>

using namespace std;

TaskC::TaskC(const char* name, const char* description): JPetTask(name, description) {}
TaskC::~TaskC() {}

void TaskC::init(const JPetTaskInterface::Options& opts)
{
}

void TaskC::exec()
{
  //getting the data from event in propriate format
  if (auto currSignal = dynamic_cast<const JPetRawSignal* const>(getEvent())) {
    getStatistics().getCounter("No. initial signals")++;
    if (fSignals.empty()) {
      fSignals.push_back(*currSignal);
    } else {
      if (fSignals[0].getTimeWindowIndex() == currSignal->getTimeWindowIndex()) {
        fSignals.push_back(*currSignal);
      } else {

        vector<JPetHit> hits = createHits(fSignals);
        hits = JPetAnalysisTools::getHitsOrderedByTime(hits);
        // uncomment this in order to fill histograms
        // of time differences for subsequent hist
        studyTimeWindow(hits);
        saveHits(hits);

        fSignals.clear();
        fSignals.push_back(*currSignal);
      }
    }
  }
}

vector<JPetHit> TaskC::createHits(const vector<JPetRawSignal>& signals)
{
  vector<JPetHit> hits;
  for (auto i = signals.begin(); i != signals.end(); ++i) {
    for (auto j = i; ++j != signals.end();) {
      if (i->getPM().getScin() == j->getPM().getScin()) {
        // found 2 signals from the same scintillator
        // wrap the RawSignal objects into RecoSignal and PhysSignal
        // for now this is just wrapping opne object into another
        // in the future analyses it will involve more logic like
        // reconstructing the signal's shape, charge, amplitude etc.
        JPetRecoSignal recoSignalA;
        JPetRecoSignal recoSignalB;
        JPetPhysSignal physSignalA;
        JPetPhysSignal physSignalB;
        // assign sides A and B properly
        if (
          (i->getPM().getSide() == JPetPM::SideA)
          && (j->getPM().getSide() == JPetPM::SideB)
        ) {
          recoSignalA.setRawSignal(*i);
          recoSignalB.setRawSignal(*j);
        } else if (
          (j->getPM().getSide() == JPetPM::SideA)
          && (i->getPM().getSide() == JPetPM::SideB)
        ) {
          recoSignalA.setRawSignal(*j);
          recoSignalB.setRawSignal(*i);
        } else {
          // if two hits on the same side, ignore
          WARNING("TWO hits on the same scintillator side we ignore it");
          continue;
        }

        if ( recoSignalA.getRawSignal().getNumberOfPoints(JPetSigCh::Leading) < 4 ) continue;
        if ( recoSignalB.getRawSignal().getNumberOfPoints(JPetSigCh::Leading) < 4 ) continue;

        bool thresholds_ok = true;
        for (int i = 1; i <= 4; ++i) {
          if ( recoSignalA.getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).count(i) < 1 ) {
            thresholds_ok = false;
          }
          if ( recoSignalB.getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).count(i) < 1 ) {
            thresholds_ok = false;
          }
        }
        if (thresholds_ok == false) {
          continue;
        }

        physSignalA.setRecoSignal(recoSignalA);
        physSignalB.setRecoSignal(recoSignalB);
        auto leading_points_a = physSignalA.getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading);
        auto leading_points_b = physSignalB.getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading);

        //skip signals with no information on 1st threshold
        if (leading_points_a.count(1) == 0) continue;
        if (leading_points_b.count(1) == 0) continue;

        physSignalA.setTime(leading_points_a.at(1));
        physSignalB.setTime(leading_points_b.at(1));

        JPetHit hit;
        hit.setSignalA(physSignalA);
        hit.setSignalB(physSignalB);
        hit.setScintillator(i->getPM().getScin());
        hit.setBarrelSlot(i->getPM().getScin().getBarrelSlot());

        physSignalA.setTime(physSignalA.getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(1));
        physSignalB.setTime(physSignalB.getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(1));

        hit.setTime( 0.5 * ( hit.getSignalA().getTime() + hit.getSignalB().getTime()) );
        hits.push_back(hit);
        getStatistics().getCounter("No. found hits")++;
      }
    }
  }
  return hits;
}

void TaskC::terminate()
{
  //	saveHits(createHits(fSignals)); //if there is something left
  INFO( Form("From %d initial signals %d hits were paired.",
             static_cast<int>(getStatistics().getCounter("No. initial signals")),
             static_cast<int>(getStatistics().getCounter("No. found hits")) )
      );
}

void TaskC::studyTimeWindow(const vector<JPetHit>& hits)
{
    // make sure the hits are really sorted
    // assert(hits.at(i-1).getTime() <= hits.at(i).getTime());

    // double dt = hits.at(i).getTime() - hits.at(i-1).getTime();

    // getStatistics().getHisto1D("timeSepSmall").Fill(dt / 1000.); // we fill the histo in [ns]
    // getStatistics().getHisto1D("timeSepLarge").Fill(dt / 1000.); // we fill the histo in [ns]

    for(int k=1;k<=4;++k){

      double t2 = 0.5*(hits.at(i).getSignalA().getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(k) + hits.at(i).getSignalB().getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(k));

      double t1 = 0.5*(hits.at(i-1).getSignalA().getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(k) + hits.at(i-1).getSignalB().getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(k));

      double dt = t2 - t1;

      getStatistics().getHisto1D(Form("timeSepSmall_thr_%d", k)).Fill(dt / 1000.); // we fill the histo in [ns]
      getStatistics().getHisto1D(Form("timeSepLarge_thr_%d", k)).Fill(dt / 1000.); // we fill the histo in [ns]

    }

  }

  getStatistics().getHisto1D("timeSepSmall").Fill(dt / 1000.); // we fill the histo in [ns]
  getStatistics().getHisto1D("timeSepLarge").Fill(dt / 1000.); // we fill the histo in [ns]
}

void TaskC::saveHits(const vector<JPetHit>& hits)
{
  assert(fWriter);
  for (auto hit : hits) {
    // here one can impose any conditions on hits that should be
    // saved or skipped
    // for now, all hits are written to the output file
    // without checking anything
    fWriter->write(hit);
  }
}
<commit_msg>Use hit sorting from framework library<commit_after>/**
 *  @copyright Copyright 2016 The J-PET Framework Authors. All rights reserved.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may find a copy of the License in the LICENCE file.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *  @file TaskC.cpp
 */

#include <iostream>
#include <JPetWriter/JPetWriter.h>
#include <JPetAnalysisTools/JPetAnalysisTools.h>
#include "TaskC.h"
#include <algorithm>

using namespace std;

TaskC::TaskC(const char* name, const char* description): JPetTask(name, description) {}
TaskC::~TaskC() {}

void TaskC::init(const JPetTaskInterface::Options& opts)
{
}

void TaskC::exec()
{
  //getting the data from event in propriate format
  if (auto currSignal = dynamic_cast<const JPetRawSignal* const>(getEvent())) {
    getStatistics().getCounter("No. initial signals")++;
    if (fSignals.empty()) {
      fSignals.push_back(*currSignal);
    } else {
      if (fSignals[0].getTimeWindowIndex() == currSignal->getTimeWindowIndex()) {
        fSignals.push_back(*currSignal);
      } else {

        vector<JPetHit> hits = createHits(fSignals);
        hits = JPetAnalysisTools::getHitsOrderedByTime(hits);
        // uncomment this in order to fill histograms
        // of time differences for subsequent hist
        studyTimeWindow(hits);
        saveHits(hits);

        fSignals.clear();
        fSignals.push_back(*currSignal);
      }
    }
  }
}

void TaskC::exec()
{
  //getting the data from event in propriate format
  if (auto currSignal = dynamic_cast<const JPetRawSignal* const>(getEvent())) {
    getStatistics().getCounter("No. initial signals")++;
    if (fSignals.empty()) {
      fSignals.push_back(*currSignal);
    } else {
      if (fSignals[0].getTimeWindowIndex() == currSignal->getTimeWindowIndex()) {
        fSignals.push_back(*currSignal);
      } else {

        vector<JPetHit> hits = createHits(fSignals);
        hits = JPetAnalysisTools::getHitsOrderedByTime(hits);
        // uncomment this in order to fill histograms
        // of time differences for subsequent hist
        studyTimeWindow(hits);

        saveHits(hits);

        fSignals.clear();
        fSignals.push_back(*currSignal);
      }
    }
  }
}
vector<JPetHit> TaskC::createHits(const vector<JPetRawSignal>& signals)
{
  vector<JPetHit> hits;
  for (auto i = signals.begin(); i != signals.end(); ++i) {
    for (auto j = i; ++j != signals.end();) {
      if (i->getPM().getScin() == j->getPM().getScin()) {
        // found 2 signals from the same scintillator
        // wrap the RawSignal objects into RecoSignal and PhysSignal
        // for now this is just wrapping opne object into another
        // in the future analyses it will involve more logic like
        // reconstructing the signal's shape, charge, amplitude etc.
        JPetRecoSignal recoSignalA;
        JPetRecoSignal recoSignalB;
        JPetPhysSignal physSignalA;
        JPetPhysSignal physSignalB;
        // assign sides A and B properly
        if (
          (i->getPM().getSide() == JPetPM::SideA)
          && (j->getPM().getSide() == JPetPM::SideB)
        ) {
          recoSignalA.setRawSignal(*i);
          recoSignalB.setRawSignal(*j);
        } else if (
          (j->getPM().getSide() == JPetPM::SideA)
          && (i->getPM().getSide() == JPetPM::SideB)
        ) {
          recoSignalA.setRawSignal(*j);
          recoSignalB.setRawSignal(*i);
        } else {
          // if two hits on the same side, ignore
          WARNING("TWO hits on the same scintillator side we ignore it");
          continue;
        }

        if ( recoSignalA.getRawSignal().getNumberOfPoints(JPetSigCh::Leading) < 4 ) continue;
        if ( recoSignalB.getRawSignal().getNumberOfPoints(JPetSigCh::Leading) < 4 ) continue;

        bool thresholds_ok = true;
        for (int i = 1; i <= 4; ++i) {
          if ( recoSignalA.getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).count(i) < 1 ) {
            thresholds_ok = false;
          }
          if ( recoSignalB.getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).count(i) < 1 ) {
            thresholds_ok = false;
          }
        }
        if (thresholds_ok == false) {
          continue;
        }

        physSignalA.setRecoSignal(recoSignalA);
        physSignalB.setRecoSignal(recoSignalB);
        auto leading_points_a = physSignalA.getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading);
        auto leading_points_b = physSignalB.getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading);

        //skip signals with no information on 1st threshold
        // if(leading_points_a.count(1) == 0) continue;
        // if(leading_points_b.count(1) == 0) continue;

        physSignalA.setTime(leading_points_a.at(1));
        physSignalB.setTime(leading_points_b.at(1));

        JPetHit hit;
        hit.setSignalA(physSignalA);
        hit.setSignalB(physSignalB);
        hit.setScintillator(i->getPM().getScin());
        hit.setBarrelSlot(i->getPM().getScin().getBarrelSlot());

        physSignalA.setTime(physSignalA.getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(1));
        physSignalB.setTime(physSignalB.getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(1));

        hit.setTime( 0.5 * ( hit.getSignalA().getTime() + hit.getSignalB().getTime()) );

        hits.push_back(hit);
        getStatistics().getCounter("No. found hits")++;
      }
    }
  }
  return hits;
}

void TaskC::terminate()
{
  //	saveHits(createHits(fSignals)); //if there is something left
  INFO( Form("From %d initial signals %d hits were paired.",
             static_cast<int>(getStatistics().getCounter("No. initial signals")),
             static_cast<int>(getStatistics().getCounter("No. found hits")) )
      );
}

void TaskC::studyTimeWindow(const vector<JPetHit>& hits)
{

  // plot time differences for subsequent hits at each threshold separately
  for (int i = 1; i < hits.size(); ++i) {
    >>> >>> > Use hit sorting from framework library
    for (int k = 1; k <= 4; ++k) {
      double t2 = 0.5 * (hits.at(i).getSignalA().getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(k) + hits.at(i).getSignalB().getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(k));
      double t1 = 0.5 * (hits.at(i - 1).getSignalA().getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(k) + hits.at(i - 1).getSignalB().getRecoSignal().getRawSignal().getTimesVsThresholdNumber(JPetSigCh::Leading).at(k));
      double dt = t2 - t1;
      getStatistics().getHisto1D(Form("timeSepSmall_thr_%d", k)).Fill(dt / 1000.); // we fill the histo in [ns]
      getStatistics().getHisto1D(Form("timeSepLarge_thr_%d", k)).Fill(dt / 1000.); // we fill the histo in [ns]
    }
  }
}

void TaskC::saveHits(const vector<JPetHit>& hits)
{
  assert(fWriter);
  for (auto hit : hits) {
    // here one can impose any conditions on hits that should be
    // saved or skipped
    // for now, all hits are written to the output file
    // without checking anything
    fWriter->write(hit);
  }
}
<|endoftext|>


<commit_before>/*ckwg +5
 * Copyright 2011 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "thread_per_process_schedule.h"

#include <vistk/pipeline/pipeline.h>
#include <vistk/pipeline/utils.h>

#include <boost/foreach.hpp>
#include <boost/thread/thread.hpp>

namespace vistk
{

static void run_process(process_t process);

class thread_per_process_schedule::priv
{
  public:
    priv();
    ~priv();

    boost::thread_group process_threads;
};

thread_per_process_schedule
::thread_per_process_schedule(config_t const& config, pipeline_t const& pipe)
  : schedule(config, pipe)
{
}

thread_per_process_schedule
::~thread_per_process_schedule()
{
}

void
thread_per_process_schedule
::start()
{
  BOOST_FOREACH (process::name_t const& name, pipeline()->process_names())
  {
    process_t process = pipeline()->process_by_name(name);

    d->process_threads.create_thread(boost::bind(run_process, process));
  }
}

void
thread_per_process_schedule
::stop()
{
  /// \todo Shut the schedule down.
}

thread_per_process_schedule::priv
::priv()
{
}

thread_per_process_schedule::priv
::~priv()
{
}

void
run_process(process_t process)
{
  name_thread(process->name());

  /// \todo Run the process until it is complete.
}

}
<commit_msg>Initialize the private pointer<commit_after>/*ckwg +5
 * Copyright 2011 by Kitware, Inc. All Rights Reserved. Please refer to
 * KITWARE_LICENSE.TXT for licensing information, or contact General Counsel,
 * Kitware, Inc., 28 Corporate Drive, Clifton Park, NY 12065.
 */

#include "thread_per_process_schedule.h"

#include <vistk/pipeline/pipeline.h>
#include <vistk/pipeline/utils.h>

#include <boost/foreach.hpp>
#include <boost/thread/thread.hpp>

namespace vistk
{

static void run_process(process_t process);

class thread_per_process_schedule::priv
{
  public:
    priv();
    ~priv();

    boost::thread_group process_threads;
};

thread_per_process_schedule
::thread_per_process_schedule(config_t const& config, pipeline_t const& pipe)
  : schedule(config, pipe)
{
  d = boost::shared_ptr<priv>(new priv);
}

thread_per_process_schedule
::~thread_per_process_schedule()
{
}

void
thread_per_process_schedule
::start()
{
  BOOST_FOREACH (process::name_t const& name, pipeline()->process_names())
  {
    process_t process = pipeline()->process_by_name(name);

    d->process_threads.create_thread(boost::bind(run_process, process));
  }
}

void
thread_per_process_schedule
::stop()
{
  /// \todo Shut the schedule down.
}

thread_per_process_schedule::priv
::priv()
{
}

thread_per_process_schedule::priv
::~priv()
{
}

void
run_process(process_t process)
{
  name_thread(process->name());

  /// \todo Run the process until it is complete.
}

}
<|endoftext|>


<commit_before>/*   This file is part of the KDE project
 *
 *   Copyright (C) 2014 Dominik Haumann <dhauumann@kde.org>
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 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
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public License
 *   along with this library; see the file COPYING.LIB.  If not, write to
 *   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *   Boston, MA 02110-1301, USA.
 */

#include "katetabbutton.h"

#include <klocalizedstring.h>

#include <QApplication>
#include <QColorDialog>
#include <QContextMenuEvent>
#include <QFontDatabase>
#include <QIcon>
#include <QMenu>
#include <QPainter>
#include <QStyle>

QColor KateTabButton::s_predefinedColors[] = { Qt::red, Qt::yellow, Qt::green, Qt::cyan, Qt::blue, Qt::magenta };
const int KateTabButton::s_colorCount = 6;
int KateTabButton::s_currentColor = 0;

KateTabButton::KateTabButton(const QString &docurl, const QString &caption,
                             int button_id, QWidget *parent)
    : QPushButton(parent)
{
    setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
    setCheckable(true);
    setFocusPolicy(Qt::NoFocus);
    setMinimumWidth(1);
    setFlat(true);
//     setAutoFillBackground(true);

    m_buttonId = button_id;
    m_modified = false;

    setIcon(QIcon());
    setText(caption);
    setURL(docurl);

    connect(this, SIGNAL(clicked()), this, SLOT(buttonClicked()));
}

KateTabButton::~KateTabButton()
{
}

void KateTabButton::setURL(const QString &docurl)
{
    m_url = docurl;
    if (!m_url.isEmpty()) {
        setToolTip(m_url);
    } else {
        setToolTip(text());
    }
}

QString KateTabButton::url() const
{
    return m_url;
}

void KateTabButton::buttonClicked()
{
    // once down, stay down until another tab is activated
    if (isChecked()) {
        emit activated(this);
    } else {
        setChecked(true);
    }
}

void KateTabButton::setActivated(bool active)
{
    if (isChecked() == active) {
        return;
    }
    setChecked(active);
    update();
}

bool KateTabButton::isActivated() const
{
    return isChecked();
}

void KateTabButton::paintEvent(QPaintEvent *ev)
{
    Q_UNUSED(ev)

    QPalette pal = QApplication::palette();

    QPainter p(this);
    if (underMouse()) {
        QColor c = pal.color(QPalette::Background);
        p.fillRect(rect(), c.lighter(110));
    }

    // draw text
    style()->drawItemText(&p, rect(), Qt::AlignHCenter, pal, true, text());

    if (m_highlightColor.isValid()) {
        p.fillRect(QRect(0, height() - 3, width(), 10), m_highlightColor);
    }

    if (isActivated()) {
        p.fillRect(QRect(0, height() - 3, width(), 10), QColor(0, 0, 255, 128));
    }
}

void KateTabButton::contextMenuEvent(QContextMenuEvent *ev)
{
    QPixmap colorIcon(22, 22);
    QMenu menu(/*text(),*/ this);
    QMenu *colorMenu = menu.addMenu(i18n("&Highlight Tab"));
    QAction *aNone = colorMenu->addAction(i18n("&None"));
    colorMenu->addSeparator();
    colorIcon.fill(Qt::red);
    QAction *aRed = colorMenu->addAction(colorIcon, i18n("&Red"));
    colorIcon.fill(Qt::yellow);
    QAction *aYellow = colorMenu->addAction(colorIcon, i18n("&Yellow"));
    colorIcon.fill(Qt::green);
    QAction *aGreen = colorMenu->addAction(colorIcon, i18n("&Green"));
    colorIcon.fill(Qt::cyan);
    QAction *aCyan = colorMenu->addAction(colorIcon, i18n("&Cyan"));
    colorIcon.fill(Qt::blue);
    QAction *aBlue = colorMenu->addAction(colorIcon, i18n("&Blue"));
    colorIcon.fill(Qt::magenta);
    QAction *aMagenta = colorMenu->addAction(colorIcon, i18n("&Magenta"));
    colorMenu->addSeparator();
    QAction *aCustomColor = colorMenu->addAction(
                                QIcon::fromTheme(QStringLiteral("colors")), i18n("C&ustom Color..."));
    menu.addSeparator();

    QAction *aCloseTab = menu.addAction(i18n("&Close Tab"));
    QAction *aCloseOtherTabs = menu.addAction(i18n("Close &Other Tabs"));
    QAction *aCloseAllTabs = menu.addAction(i18n("Close &All Tabs"));

    QAction *choice = menu.exec(ev->globalPos());

    // process the result
    if (choice == aNone) {
        if (m_highlightColor.isValid()) {
            setHighlightColor(QColor());
            emit highlightChanged(this);
        }
    } else if (choice == aRed) {
        setHighlightColor(Qt::red);
        emit highlightChanged(this);
    } else if (choice == aYellow) {
        setHighlightColor(Qt::yellow);
        emit highlightChanged(this);
    } else if (choice == aGreen) {
        setHighlightColor(Qt::green);
        emit highlightChanged(this);
    } else if (choice == aCyan) {
        setHighlightColor(Qt::cyan);
        emit highlightChanged(this);
    } else if (choice == aBlue) {
        setHighlightColor(Qt::blue);
        emit highlightChanged(this);
    } else if (choice == aMagenta) {
        setHighlightColor(Qt::magenta);
        emit highlightChanged(this);
    } else if (choice == aCustomColor) {
        QColor newColor = QColorDialog::getColor(m_highlightColor, this);
        if (newColor.isValid()) {
            setHighlightColor(newColor);
            emit highlightChanged(this);
        }
    } else if (choice == aCloseTab) {
        emit closeRequest(this);
    } else if (choice == aCloseOtherTabs) {
        emit closeOtherTabsRequest(this);
    } else if (choice == aCloseAllTabs) {
        emit closeAllTabsRequest();
    }
}

void KateTabButton::mousePressEvent(QMouseEvent *ev)
{
    if (ev->button() == Qt::MidButton) {
        if (ev->modifiers() & Qt::ControlModifier) {
            // clear tab highlight
            setHighlightColor(QColor());
        } else {
            setHighlightColor(s_predefinedColors[s_currentColor]);
            if (++s_currentColor >= s_colorCount) {
                s_currentColor = 0;
            }
        }
        ev->accept();
    } else {
        QPushButton::mousePressEvent(ev);
    }
}

void KateTabButton::setButtonID(int button_id)
{
    m_buttonId = button_id;
}

int KateTabButton::buttonID() const
{
    return m_buttonId;
}

void KateTabButton::setHighlightColor(const QColor &color)
{
    if (color.isValid()) {
        m_highlightColor = color;
        update();
    } else if (m_highlightColor.isValid()) {
        m_highlightColor = QColor();
        update();
    }
}

QColor KateTabButton::highlightColor() const
{
    return m_highlightColor;
}

void KateTabButton::setModified(bool modified)
{
    m_modified = modified;
    update();
}

bool KateTabButton::isModified() const
{
    return m_modified;
}

// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix;
<commit_msg>center vertically too<commit_after>/*   This file is part of the KDE project
 *
 *   Copyright (C) 2014 Dominik Haumann <dhauumann@kde.org>
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 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
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public License
 *   along with this library; see the file COPYING.LIB.  If not, write to
 *   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 *   Boston, MA 02110-1301, USA.
 */

#include "katetabbutton.h"

#include <klocalizedstring.h>

#include <QApplication>
#include <QColorDialog>
#include <QContextMenuEvent>
#include <QFontDatabase>
#include <QIcon>
#include <QMenu>
#include <QPainter>
#include <QStyle>

QColor KateTabButton::s_predefinedColors[] = { Qt::red, Qt::yellow, Qt::green, Qt::cyan, Qt::blue, Qt::magenta };
const int KateTabButton::s_colorCount = 6;
int KateTabButton::s_currentColor = 0;

KateTabButton::KateTabButton(const QString &docurl, const QString &caption,
                             int button_id, QWidget *parent)
    : QPushButton(parent)
{
    setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont));
    setCheckable(true);
    setFocusPolicy(Qt::NoFocus);
    setMinimumWidth(1);
    setFlat(true);
//     setAutoFillBackground(true);

    m_buttonId = button_id;
    m_modified = false;

    setIcon(QIcon());
    setText(caption);
    setURL(docurl);

    connect(this, SIGNAL(clicked()), this, SLOT(buttonClicked()));
}

KateTabButton::~KateTabButton()
{
}

void KateTabButton::setURL(const QString &docurl)
{
    m_url = docurl;
    if (!m_url.isEmpty()) {
        setToolTip(m_url);
    } else {
        setToolTip(text());
    }
}

QString KateTabButton::url() const
{
    return m_url;
}

void KateTabButton::buttonClicked()
{
    // once down, stay down until another tab is activated
    if (isChecked()) {
        emit activated(this);
    } else {
        setChecked(true);
    }
}

void KateTabButton::setActivated(bool active)
{
    if (isChecked() == active) {
        return;
    }
    setChecked(active);
    update();
}

bool KateTabButton::isActivated() const
{
    return isChecked();
}

void KateTabButton::paintEvent(QPaintEvent *ev)
{
    Q_UNUSED(ev)

    QPalette pal = QApplication::palette();

    QPainter p(this);
    if (underMouse()) {
        QColor c = pal.color(QPalette::Background);
        p.fillRect(rect(), c.lighter(110));
    }

    // draw text
    style()->drawItemText(&p, rect(), Qt::AlignHCenter | Qt::AlignVCenter, pal, true, text());

    if (m_highlightColor.isValid()) {
        p.fillRect(QRect(0, height() - 3, width(), 10), m_highlightColor);
    }

    if (isActivated()) {
        p.fillRect(QRect(0, height() - 3, width(), 10), QColor(0, 0, 255, 128));
    }
}

void KateTabButton::contextMenuEvent(QContextMenuEvent *ev)
{
    QPixmap colorIcon(22, 22);
    QMenu menu(/*text(),*/ this);
    QMenu *colorMenu = menu.addMenu(i18n("&Highlight Tab"));
    QAction *aNone = colorMenu->addAction(i18n("&None"));
    colorMenu->addSeparator();
    colorIcon.fill(Qt::red);
    QAction *aRed = colorMenu->addAction(colorIcon, i18n("&Red"));
    colorIcon.fill(Qt::yellow);
    QAction *aYellow = colorMenu->addAction(colorIcon, i18n("&Yellow"));
    colorIcon.fill(Qt::green);
    QAction *aGreen = colorMenu->addAction(colorIcon, i18n("&Green"));
    colorIcon.fill(Qt::cyan);
    QAction *aCyan = colorMenu->addAction(colorIcon, i18n("&Cyan"));
    colorIcon.fill(Qt::blue);
    QAction *aBlue = colorMenu->addAction(colorIcon, i18n("&Blue"));
    colorIcon.fill(Qt::magenta);
    QAction *aMagenta = colorMenu->addAction(colorIcon, i18n("&Magenta"));
    colorMenu->addSeparator();
    QAction *aCustomColor = colorMenu->addAction(
                                QIcon::fromTheme(QStringLiteral("colors")), i18n("C&ustom Color..."));
    menu.addSeparator();

    QAction *aCloseTab = menu.addAction(i18n("&Close Tab"));
    QAction *aCloseOtherTabs = menu.addAction(i18n("Close &Other Tabs"));
    QAction *aCloseAllTabs = menu.addAction(i18n("Close &All Tabs"));

    QAction *choice = menu.exec(ev->globalPos());

    // process the result
    if (choice == aNone) {
        if (m_highlightColor.isValid()) {
            setHighlightColor(QColor());
            emit highlightChanged(this);
        }
    } else if (choice == aRed) {
        setHighlightColor(Qt::red);
        emit highlightChanged(this);
    } else if (choice == aYellow) {
        setHighlightColor(Qt::yellow);
        emit highlightChanged(this);
    } else if (choice == aGreen) {
        setHighlightColor(Qt::green);
        emit highlightChanged(this);
    } else if (choice == aCyan) {
        setHighlightColor(Qt::cyan);
        emit highlightChanged(this);
    } else if (choice == aBlue) {
        setHighlightColor(Qt::blue);
        emit highlightChanged(this);
    } else if (choice == aMagenta) {
        setHighlightColor(Qt::magenta);
        emit highlightChanged(this);
    } else if (choice == aCustomColor) {
        QColor newColor = QColorDialog::getColor(m_highlightColor, this);
        if (newColor.isValid()) {
            setHighlightColor(newColor);
            emit highlightChanged(this);
        }
    } else if (choice == aCloseTab) {
        emit closeRequest(this);
    } else if (choice == aCloseOtherTabs) {
        emit closeOtherTabsRequest(this);
    } else if (choice == aCloseAllTabs) {
        emit closeAllTabsRequest();
    }
}

void KateTabButton::mousePressEvent(QMouseEvent *ev)
{
    if (ev->button() == Qt::MidButton) {
        if (ev->modifiers() & Qt::ControlModifier) {
            // clear tab highlight
            setHighlightColor(QColor());
        } else {
            setHighlightColor(s_predefinedColors[s_currentColor]);
            if (++s_currentColor >= s_colorCount) {
                s_currentColor = 0;
            }
        }
        ev->accept();
    } else {
        QPushButton::mousePressEvent(ev);
    }
}

void KateTabButton::setButtonID(int button_id)
{
    m_buttonId = button_id;
}

int KateTabButton::buttonID() const
{
    return m_buttonId;
}

void KateTabButton::setHighlightColor(const QColor &color)
{
    if (color.isValid()) {
        m_highlightColor = color;
        update();
    } else if (m_highlightColor.isValid()) {
        m_highlightColor = QColor();
        update();
    }
}

QColor KateTabButton::highlightColor() const
{
    return m_highlightColor;
}

void KateTabButton::setModified(bool modified)
{
    m_modified = modified;
    update();
}

bool KateTabButton::isModified() const
{
    return m_modified;
}

// kate: space-indent on; indent-width 4; tab-width 4; replace-tabs on; eol unix;
<|endoftext|>