<commit_before>/******************************************************************************\
* File:          file.cpp
* Purpose:       Implementation of wxWidgets file extension classes
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#ifdef __WXMSW__
#include <io.h> // for chmod
#endif
#include <wx/extension/extension.h>
#include <wx/stdpaths.h> // strangely enough, for wxTheFileIconsTable
#include <wx/generic/dirctrlg.h> // for wxTheFileIconsTable

exFile::exFile()
  : m_FileName()
  , m_Stat()
  , m_Message(_("Select File"))
  , m_Wildcard(wxFileSelectorDefaultWildcardStr)
{
}

exFile::exFile(const wxString& filename, wxFile::OpenMode mode)
  : wxFile(filename, mode)
  , m_FileName(filename)
  , m_Stat(filename)
  , m_Message(_("Select File"))
  , m_Wildcard(wxFileSelectorDefaultWildcardStr)
{
}

int exFile::AskFileOpen(wxFileDialog& dlg, bool ask_for_continue)
{
  if (ask_for_continue)
  {
    if (!Continue())
    {
      return wxID_CANCEL;
    }
  }

  // Take care that if current filename exists, we set
  // the directory member and filename, so that is where the dialog will open.
  if (m_FileName.FileExists())
  {
    dlg.SetFilename(m_FileName.GetFullPath());
    dlg.SetDirectory(m_FileName.GetPath());
  }

  m_Message = dlg.GetMessage();
  m_Wildcard = dlg.GetWildcard();

  return dlg.ShowModal();
}

bool exFile::Continue()
{
  if (GetContentsChanged())
  {
    if (!m_FileName.FileExists())
    {
      switch (wxMessageBox(
        _("Save changes") + "?",
        _("Confirm"),
        wxYES_NO | wxCANCEL | wxICON_QUESTION))
      {
        case wxYES:    if (!FileSaveAs()) return false; break;
        case wxNO:     ResetContentsChanged(); break;
        case wxCANCEL: return false; break;
      }
    }
    else
    {
      switch (wxMessageBox(
        _("Save changes to") + ": " + m_FileName.GetFullPath() + "?",
        _("Confirm"),
        wxYES_NO | wxCANCEL | wxICON_QUESTION))
      {
        case wxYES:    FileSave(); break;
        case wxNO:     ResetContentsChanged(); break;
        case wxCANCEL: return false; break;
      }
    }
  }

  return true;
}

bool exFile::FileNew(const exFileName& filename)
{
  if (!Continue()) return false;

  Update(filename);

  return true;
}

bool exFile::FileOpen(const exFileName& filename)
{
  if (
    !Continue() ||
    !filename.FileExists()) return false;

  Update(filename);

  return Open(m_FileName.GetFullPath());
}

bool exFile::FileSave()
{
  wxFile::Close();

  m_FileName.GetStat().Update(m_FileName.GetFullPath());
  m_Stat.Update(m_FileName.GetFullPath());

  return true;
}

bool exFile::FileSaveAs()
{
  wxFileDialog dlg(
    wxTheApp->GetTopWindow(),
    m_Message,
    wxEmptyString,
    wxEmptyString,
    m_Wildcard,
    wxFD_SAVE | wxFD_OVERWRITE_PROMPT);

  if (AskFileOpen(dlg, false) == wxID_CANCEL)
  {
    return false;
  }

  const wxString filename = dlg.GetPath();

  if (!filename.empty())
  {
    m_FileName.Assign(filename);
    m_FileName.SetLexer();
    return FileSave();
  }

  return false;
}

wxString* exFile::Read(wxFileOffset seek_position)
{
  const wxFileOffset bytes_to_read = Length() - seek_position;

  // Always do a seek, so you can do more Reads on the same object.
  Seek(seek_position);

  wxMemoryBuffer buffer(bytes_to_read);
  buffer.SetDataLen(bytes_to_read);
  if (wxFile::Read(buffer.GetData(), bytes_to_read) == bytes_to_read)
  {
    /// \todo If the last char is a NULL, is not put in the string strangely enough.
    ///       However, it is in the buffer as next message shows.
//wxLogMessage("%d", ((char*)buffer.GetData())[bytes_to_read - 1]);
    return new wxString(buffer, *wxConvCurrent, bytes_to_read);
  }
  else
  {
    wxLogError(FILE_INFO("Read error"));
    return NULL;
  }
}

void exFile::Update(const exFileName& filename)
{
  m_FileName.Assign(filename);
  m_FileName.GetStat().Update(filename.GetFullPath());
  m_FileName.SetLexer();
  m_Stat.Update(filename.GetFullPath());
}

int exFileName::GetIcon() const
{
  if (GetStat().IsOk() && !GetExt().empty())
  {
    // README: DirExists from wxFileName is not okay, so use the static one here!
    return
      (wxFileName::DirExists(GetFullPath()) ?
       wxFileIconsTable::folder:
       wxTheFileIconsTable->GetIconID(GetExt()));
  }
  else
  {
    return wxFileIconsTable::computer;
  }
}

void exFileName::SetLexer(
  const wxString& lexer,
  const wxString& text)
{
  // Of course, if the lexers are not yet constructed, skip the rest.
  if (exApp::GetLexers() == NULL) return;

  if (lexer.empty())
  {
    m_Lexer = exApp::GetLexers()->FindByFileName(*this);

    if (m_Lexer.GetScintillaLexer().empty() && !text.empty())
    {
      m_Lexer.SetLexerFromText(text);
    }
  }
  else
  {
    m_Lexer = exApp::GetLexers()->FindByName(lexer);
  }
}

#if wxUSE_STATUSBAR
void exFileName::StatusText(long flags) const
{
  wxString text; // clear status bar for empty or not existing or not initialized file names

  if (IsOk())
  {
    const wxString path = (flags & STAT_FULLPATH
      ? GetFullPath(): GetFullName());

    text += path;

    if (GetStat().IsOk())
    {
      const wxString what = (flags & STAT_SYNC
        ? _("Synchronized"): _("Modified"));
      const wxString time = (flags & STAT_SYNC
        ? wxDateTime::Now().Format(): GetStat().GetModificationTime());
      text += " " + what + " " + time;
    }
  }

  exFrame::StatusText(text);
}
#endif // wxUSE_STATUSBAR

#if wxUSE_GUI
exConfigDialog* exStat::m_ConfigDialog = NULL;
#endif

#if wxUSE_GUI
// This is a static method, cannot use normal members here.
int exStat::ConfigDialog(
  const wxString& title,
  wxWindow* parent,
  wxWindowID id)
{
  std::vector<exConfigItem> items;
  items.push_back(exConfigItem(_("day"), CONFIG_COLOUR));
  items.push_back(exConfigItem(_("week"), CONFIG_COLOUR));
  items.push_back(exConfigItem(_("month"), CONFIG_COLOUR));
  items.push_back(exConfigItem(_("year"), CONFIG_COLOUR));
#ifndef __WXMSW__
  // Links only used on Unix.
  items.push_back(exConfigItem(_("link"), CONFIG_COLOUR));
#endif

  if (m_ConfigDialog == NULL)
  {
    m_ConfigDialog = new exConfigDialog(
      parent,
      items,
      title,
      "Colour/",
      0, 2,
      wxOK | wxCANCEL | wxAPPLY,
      id);
  }

  return m_ConfigDialog->Show();
}
#endif

const wxColour exStat::GetColour() const
{
  if (IsOk())
  {
    const wxString& group = "Colour/";
    const wxDateTime now = wxDateTime::Now();
    const wxDateTime mtime = wxDateTime(st_mtime);

    // within 1 day (12 hours)
    if (mtime > now - 12 * wxTimeSpan::Hour())
    {
      return exApp::GetConfig(group + _("day"), exColourToLong(*wxGREEN));
    }
    // within 1 week
    else if (mtime > now - wxDateSpan::Week())
    {
      return exApp::GetConfig(group + _("week"), exColourToLong(*wxBLUE));
    }
    // within 1 month (default colour is white, so not used)
    else if (mtime > now - wxDateSpan::Month())
    {
      return exApp::GetConfig(group + _("month"), exColourToLong(*wxWHITE));
    }
    // within 1 year (default colour is white, so not used)
    else if (mtime > now - wxDateSpan::Year())
    {
      return exApp::GetConfig(group + _("year"), exColourToLong(*wxWHITE));
    }
  }

  return *wxWHITE;
}

const wxColour exStat::GetLinkColour() const
{
  return exApp::GetConfig("Colour/" + _("link"), exColourToLong(*wxRED));
}

const wxString exStat::GetModificationTime(const wxString& format) const
{
#ifdef __WXMSW__
  return wxDateTime(st_mtime).Format(format);
#else
  // TODO: The current locale %c cannot be sorted in the listview,
  // so override format and use this one.
  return wxDateTime(st_mtime).Format("%Y%m%d %H:%M:%S");
#endif
}

bool exStat::IsLink() const
{
#ifdef __UNIX__
  return m_IsOk && (S_ISLNK(st_mode) != 0);
#else // S_ISLNK not known
  return false;
#endif
}

bool exStat::SetReadOnly(const bool read_only)
{
  if (IsOk())
  {
    if (chmod(m_FullPath.c_str(),
          read_only ?
            st_mode & ~wxS_IWUSR:
            st_mode | wxS_IWUSR) == 0)
    {
      Update(m_FullPath);
      return IsOk();
    }
  }

  return false;
}
<commit_msg>fixed error<commit_after>/******************************************************************************\
* File:          file.cpp
* Purpose:       Implementation of wxWidgets file extension classes
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#ifdef __WXMSW__
#include <io.h> // for chmod
#endif
#include <wx/extension/extension.h>
#include <wx/stdpaths.h> // strangely enough, for wxTheFileIconsTable
#include <wx/generic/dirctrlg.h> // for wxTheFileIconsTable

exFile::exFile()
  : m_FileName()
  , m_Stat()
  , m_Message(_("Select File"))
  , m_Wildcard(wxFileSelectorDefaultWildcardStr)
{
}

exFile::exFile(const wxString& filename, wxFile::OpenMode mode)
  : wxFile(filename, mode)
  , m_FileName(filename)
  , m_Stat(filename)
  , m_Message(_("Select File"))
  , m_Wildcard(wxFileSelectorDefaultWildcardStr)
{
}

int exFile::AskFileOpen(wxFileDialog& dlg, bool ask_for_continue)
{
  if (ask_for_continue)
  {
    if (!Continue())
    {
      return wxID_CANCEL;
    }
  }

  // Take care that if current filename exists, we set
  // the directory member and filename, so that is where the dialog will open.
  if (m_FileName.FileExists())
  {
    dlg.SetFilename(m_FileName.GetFullPath());
    dlg.SetDirectory(m_FileName.GetPath());
  }

  m_Message = dlg.GetMessage();
  m_Wildcard = dlg.GetWildcard();

  return dlg.ShowModal();
}

bool exFile::Continue()
{
  if (GetContentsChanged())
  {
    if (!m_FileName.FileExists())
    {
      switch (wxMessageBox(
        _("Save changes") + "?",
        _("Confirm"),
        wxYES_NO | wxCANCEL | wxICON_QUESTION))
      {
        case wxYES:    if (!FileSaveAs()) return false; break;
        case wxNO:     ResetContentsChanged(); break;
        case wxCANCEL: return false; break;
      }
    }
    else
    {
      switch (wxMessageBox(
        _("Save changes to") + ": " + m_FileName.GetFullPath() + "?",
        _("Confirm"),
        wxYES_NO | wxCANCEL | wxICON_QUESTION))
      {
        case wxYES:    FileSave(); break;
        case wxNO:     ResetContentsChanged(); break;
        case wxCANCEL: return false; break;
      }
    }
  }

  return true;
}

bool exFile::FileNew(const exFileName& filename)
{
  if (!Continue()) return false;

  Update(filename);

  return true;
}

bool exFile::FileOpen(const exFileName& filename)
{
  if (
    !Continue() ||
    !filename.FileExists()) return false;

  Update(filename);

  return Open(m_FileName.GetFullPath());
}

bool exFile::FileSave()
{
  wxFile::Close();

  m_FileName.GetStat().Update(m_FileName.GetFullPath());
  m_Stat.Update(m_FileName.GetFullPath());

  return true;
}

bool exFile::FileSaveAs()
{
  wxFileDialog dlg(
    wxTheApp->GetTopWindow(),
    m_Message,
    wxEmptyString,
    wxEmptyString,
    m_Wildcard,
    wxFD_SAVE | wxFD_OVERWRITE_PROMPT);

  if (AskFileOpen(dlg, false) == wxID_CANCEL)
  {
    return false;
  }

  const wxString filename = dlg.GetPath();

  if (!filename.empty())
  {
    m_FileName.Assign(filename);
    m_FileName.SetLexer();
    return FileSave();
  }

  return false;
}

wxString* exFile::Read(wxFileOffset seek_position)
{
  const wxFileOffset bytes_to_read = Length() - seek_position;

  // Always do a seek, so you can do more Reads on the same object.
  Seek(seek_position);

  wxMemoryBuffer buffer(bytes_to_read);
  buffer.SetDataLen(bytes_to_read);
  if (wxFile::Read(buffer.GetData(), bytes_to_read) == bytes_to_read)
  {
    /// \todo If the last char is a NULL, is not put in the string strangely enough.
    ///       However, it is in the buffer as next message shows.
//wxLogMessage("%d", ((char*)buffer.GetData())[bytes_to_read - 1]);
    return new wxString(buffer, *wxConvCurrent, bytes_to_read);
  }
  else
  {
    wxLogError(FILE_INFO("Read error"));
    return NULL;
  }
}

void exFile::Update(const exFileName& filename)
{
  m_FileName.Assign(filename);
  m_FileName.GetStat().Update(filename.GetFullPath());
  m_FileName.SetLexer();
  m_Stat.Update(filename.GetFullPath());
}

int exFileName::GetIcon() const
{
  if (GetStat().IsOk() && !GetExt().empty())
  {
    // README: DirExists from wxFileName is not okay, so use the static one here!
    return
      (wxFileName::DirExists(GetFullPath()) ?
       wxFileIconsTable::folder:
       wxTheFileIconsTable->GetIconID(GetExt()));
  }
  else
  {
    return wxFileIconsTable::computer;
  }
}

void exFileName::SetLexer(
  const wxString& lexer,
  const wxString& text)
{
  // Of course, if the lexers are not yet constructed, skip the rest.
  if (exApp::GetLexers() == NULL) return;

  if (lexer.empty())
  {
    m_Lexer = exApp::GetLexers()->FindByFileName(*this);

    if (m_Lexer.GetScintillaLexer().empty() && !text.empty())
    {
      m_Lexer.SetLexerFromText(exApp::GetLexers(), text);
    }
  }
  else
  {
    m_Lexer = exApp::GetLexers()->FindByName(lexer);
  }
}

#if wxUSE_STATUSBAR
void exFileName::StatusText(long flags) const
{
  wxString text; // clear status bar for empty or not existing or not initialized file names

  if (IsOk())
  {
    const wxString path = (flags & STAT_FULLPATH
      ? GetFullPath(): GetFullName());

    text += path;

    if (GetStat().IsOk())
    {
      const wxString what = (flags & STAT_SYNC
        ? _("Synchronized"): _("Modified"));
      const wxString time = (flags & STAT_SYNC
        ? wxDateTime::Now().Format(): GetStat().GetModificationTime());
      text += " " + what + " " + time;
    }
  }

  exFrame::StatusText(text);
}
#endif // wxUSE_STATUSBAR

#if wxUSE_GUI
exConfigDialog* exStat::m_ConfigDialog = NULL;
#endif

#if wxUSE_GUI
// This is a static method, cannot use normal members here.
int exStat::ConfigDialog(
  const wxString& title,
  wxWindow* parent,
  wxWindowID id)
{
  std::vector<exConfigItem> items;
  items.push_back(exConfigItem(_("day"), CONFIG_COLOUR));
  items.push_back(exConfigItem(_("week"), CONFIG_COLOUR));
  items.push_back(exConfigItem(_("month"), CONFIG_COLOUR));
  items.push_back(exConfigItem(_("year"), CONFIG_COLOUR));
#ifndef __WXMSW__
  // Links only used on Unix.
  items.push_back(exConfigItem(_("link"), CONFIG_COLOUR));
#endif

  if (m_ConfigDialog == NULL)
  {
    m_ConfigDialog = new exConfigDialog(
      parent,
      items,
      title,
      "Colour/",
      0, 2,
      wxOK | wxCANCEL | wxAPPLY,
      id);
  }

  return m_ConfigDialog->Show();
}
#endif

const wxColour exStat::GetColour() const
{
  if (IsOk())
  {
    const wxString& group = "Colour/";
    const wxDateTime now = wxDateTime::Now();
    const wxDateTime mtime = wxDateTime(st_mtime);

    // within 1 day (12 hours)
    if (mtime > now - 12 * wxTimeSpan::Hour())
    {
      return exApp::GetConfig(group + _("day"), exColourToLong(*wxGREEN));
    }
    // within 1 week
    else if (mtime > now - wxDateSpan::Week())
    {
      return exApp::GetConfig(group + _("week"), exColourToLong(*wxBLUE));
    }
    // within 1 month (default colour is white, so not used)
    else if (mtime > now - wxDateSpan::Month())
    {
      return exApp::GetConfig(group + _("month"), exColourToLong(*wxWHITE));
    }
    // within 1 year (default colour is white, so not used)
    else if (mtime > now - wxDateSpan::Year())
    {
      return exApp::GetConfig(group + _("year"), exColourToLong(*wxWHITE));
    }
  }

  return *wxWHITE;
}

const wxColour exStat::GetLinkColour() const
{
  return exApp::GetConfig("Colour/" + _("link"), exColourToLong(*wxRED));
}

const wxString exStat::GetModificationTime(const wxString& format) const
{
#ifdef __WXMSW__
  return wxDateTime(st_mtime).Format(format);
#else
  // TODO: The current locale %c cannot be sorted in the listview,
  // so override format and use this one.
  return wxDateTime(st_mtime).Format("%Y%m%d %H:%M:%S");
#endif
}

bool exStat::IsLink() const
{
#ifdef __UNIX__
  return m_IsOk && (S_ISLNK(st_mode) != 0);
#else // S_ISLNK not known
  return false;
#endif
}

bool exStat::SetReadOnly(const bool read_only)
{
  if (IsOk())
  {
    if (chmod(m_FullPath.c_str(),
          read_only ?
            st_mode & ~wxS_IWUSR:
            st_mode | wxS_IWUSR) == 0)
    {
      Update(m_FullPath);
      return IsOk();
    }
  }

  return false;
}
<|endoftext|>


<commit_before>/******************************************************************************\
* File:          menu.cpp
* Purpose:       Implementation of wxExMenu class
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <map>
#include <wx/extension/menu.h>
#include <wx/extension/art.h>
#include <wx/extension/tool.h>
#include <wx/extension/util.h> // for wxExEllipsed

using namespace std;

#if wxUSE_GUI

wxExMenu::wxExMenu(long style)
  : m_Style(style)
  , m_ItemsAppended(0)
  , m_IsSeparator(false)
{
}

wxExMenu::wxExMenu(const wxExMenu& menu)
  : m_Style(menu.m_Style)
  , m_ItemsAppended(menu.m_ItemsAppended)
  , m_IsSeparator(menu.m_IsSeparator)
{
}

wxMenuItem* wxExMenu::Append(int id)
{
  m_ItemsAppended++;
  m_IsSeparator = false;

  const wxExStockArt art(id);

  wxMenuItem* item = new wxMenuItem(this, id, wxGetStockLabel(id));

  if (art.GetBitmap().IsOk())
  {
    item->SetBitmap(art.GetBitmap());
  }

  return wxMenu::Append(item);
}

wxMenuItem* wxExMenu::Append(
  int id,
  const wxString& name,
  const wxString& helptext,
  wxArtID artid)
{
  m_ItemsAppended++;
  m_IsSeparator = false;

  wxMenuItem* item = new wxMenuItem(this, id, name, helptext);

  if (!artid.empty())
  {
    const wxBitmap bitmap =
      wxArtProvider::GetBitmap(artid, wxART_MENU, wxSize(16, 15));

    if (bitmap.IsOk())
    {
      item->SetBitmap(bitmap);
    }
  }

  return wxMenu::Append(item);
}

void wxExMenu::AppendEdit(bool add_invert)
{
  if (!(m_Style & MENU_IS_READ_ONLY) &&
       (m_Style & MENU_IS_SELECTED))
  {
    Append(wxID_CUT);
  }

  if (m_Style & MENU_IS_SELECTED)
  {
    Append(wxID_COPY);
  }

  if (!(m_Style & MENU_IS_READ_ONLY) &&
       (m_Style & MENU_CAN_PASTE))
  {
    Append(wxID_PASTE);
  }

  if (!(m_Style & MENU_IS_SELECTED) &&
      !(m_Style & MENU_IS_EMPTY))
  {
    Append(wxID_SELECTALL);
  }
  else
  {
    if (add_invert && !(m_Style & MENU_IS_EMPTY))
    {
      Append(ID_EDIT_SELECT_NONE, _("&Deselect All"));
    }
  }

  if (m_Style & MENU_ALLOW_CLEAR)
  {
    Append(wxID_CLEAR);
  }

  if (add_invert && !(m_Style & MENU_IS_EMPTY))
  {
    Append(ID_EDIT_SELECT_INVERT, _("&Invert"));
  }

  if (!(m_Style & MENU_IS_READ_ONLY) &&
       (m_Style & MENU_IS_SELECTED) &&
      !(m_Style & MENU_IS_EMPTY))
  {
    Append(wxID_DELETE);
  }
}

void wxExMenu::AppendPrint()
{
  Append(wxID_PRINT_SETUP, wxExEllipsed(_("Page &Setup")));
  Append(wxID_PREVIEW);
  Append(wxID_PRINT);
}

void wxExMenu::AppendSeparator()
{
  if (m_ItemsAppended == 0 || m_IsSeparator) return;

  wxMenu::AppendSeparator();

  m_IsSeparator = true;
}

void wxExMenu::AppendSubMenu(
  wxMenu *submenu,
  const wxString& text,
  const wxString& help)
{
  m_ItemsAppended++; // count submenu as one
  m_IsSeparator = false;
  wxMenu::AppendSubMenu(submenu, text, help);
}

void wxExMenu::AppendSVN()
{
  wxMenu* svnmenu = new wxMenu;
  svnmenu->Append(ID_EDIT_SVN_LOG, wxExEllipsed("&Log"));
  svnmenu->Append(ID_EDIT_SVN_STAT, wxExEllipsed("&Stat"));
  svnmenu->Append(ID_EDIT_SVN_DIFF, wxExEllipsed("&Diff"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_COMMIT, wxExEllipsed("&Commit"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_CAT, wxExEllipsed("Ca&t"));
  svnmenu->Append(ID_EDIT_SVN_BLAME, wxExEllipsed("&Blame"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_PROPLIST, wxExEllipsed("&Proplist"));
  svnmenu->Append(ID_EDIT_SVN_PROPSET, wxExEllipsed("&Propset"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_REVERT, wxExEllipsed("&Revert"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_ADD, wxExEllipsed("&Revert"));

  AppendSeparator();
  AppendSubMenu(svnmenu, "&SVN");
}

void wxExMenu::AppendTools()
{
  wxExMenu* menuTool = new wxExMenu(*this);

  for (
    map <int, wxExToolInfo>::const_iterator it = wxExTool::Get()->GetToolInfo().begin();
    it != wxExTool::Get()->GetToolInfo().end();
    ++it)
  {
    if (!it->second.GetText().empty())
    {
      menuTool->Append(it->first, it->second.GetText(), it->second.GetHelpText());
    }
  }

  AppendSubMenu(menuTool, _("&Tools"));
}

#endif // wxUSE_GUI
<commit_msg>fixed error in text for svn add<commit_after>/******************************************************************************\
* File:          menu.cpp
* Purpose:       Implementation of wxExMenu class
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 1998-2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <map>
#include <wx/extension/menu.h>
#include <wx/extension/art.h>
#include <wx/extension/tool.h>
#include <wx/extension/util.h> // for wxExEllipsed

using namespace std;

#if wxUSE_GUI

wxExMenu::wxExMenu(long style)
  : m_Style(style)
  , m_ItemsAppended(0)
  , m_IsSeparator(false)
{
}

wxExMenu::wxExMenu(const wxExMenu& menu)
  : m_Style(menu.m_Style)
  , m_ItemsAppended(menu.m_ItemsAppended)
  , m_IsSeparator(menu.m_IsSeparator)
{
}

wxMenuItem* wxExMenu::Append(int id)
{
  m_ItemsAppended++;
  m_IsSeparator = false;

  const wxExStockArt art(id);

  wxMenuItem* item = new wxMenuItem(this, id, wxGetStockLabel(id));

  if (art.GetBitmap().IsOk())
  {
    item->SetBitmap(art.GetBitmap());
  }

  return wxMenu::Append(item);
}

wxMenuItem* wxExMenu::Append(
  int id,
  const wxString& name,
  const wxString& helptext,
  wxArtID artid)
{
  m_ItemsAppended++;
  m_IsSeparator = false;

  wxMenuItem* item = new wxMenuItem(this, id, name, helptext);

  if (!artid.empty())
  {
    const wxBitmap bitmap =
      wxArtProvider::GetBitmap(artid, wxART_MENU, wxSize(16, 15));

    if (bitmap.IsOk())
    {
      item->SetBitmap(bitmap);
    }
  }

  return wxMenu::Append(item);
}

void wxExMenu::AppendEdit(bool add_invert)
{
  if (!(m_Style & MENU_IS_READ_ONLY) &&
       (m_Style & MENU_IS_SELECTED))
  {
    Append(wxID_CUT);
  }

  if (m_Style & MENU_IS_SELECTED)
  {
    Append(wxID_COPY);
  }

  if (!(m_Style & MENU_IS_READ_ONLY) &&
       (m_Style & MENU_CAN_PASTE))
  {
    Append(wxID_PASTE);
  }

  if (!(m_Style & MENU_IS_SELECTED) &&
      !(m_Style & MENU_IS_EMPTY))
  {
    Append(wxID_SELECTALL);
  }
  else
  {
    if (add_invert && !(m_Style & MENU_IS_EMPTY))
    {
      Append(ID_EDIT_SELECT_NONE, _("&Deselect All"));
    }
  }

  if (m_Style & MENU_ALLOW_CLEAR)
  {
    Append(wxID_CLEAR);
  }

  if (add_invert && !(m_Style & MENU_IS_EMPTY))
  {
    Append(ID_EDIT_SELECT_INVERT, _("&Invert"));
  }

  if (!(m_Style & MENU_IS_READ_ONLY) &&
       (m_Style & MENU_IS_SELECTED) &&
      !(m_Style & MENU_IS_EMPTY))
  {
    Append(wxID_DELETE);
  }
}

void wxExMenu::AppendPrint()
{
  Append(wxID_PRINT_SETUP, wxExEllipsed(_("Page &Setup")));
  Append(wxID_PREVIEW);
  Append(wxID_PRINT);
}

void wxExMenu::AppendSeparator()
{
  if (m_ItemsAppended == 0 || m_IsSeparator) return;

  wxMenu::AppendSeparator();

  m_IsSeparator = true;
}

void wxExMenu::AppendSubMenu(
  wxMenu *submenu,
  const wxString& text,
  const wxString& help)
{
  m_ItemsAppended++; // count submenu as one
  m_IsSeparator = false;
  wxMenu::AppendSubMenu(submenu, text, help);
}

void wxExMenu::AppendSVN()
{
  wxMenu* svnmenu = new wxMenu;
  svnmenu->Append(ID_EDIT_SVN_LOG, wxExEllipsed("&Log"));
  svnmenu->Append(ID_EDIT_SVN_STAT, wxExEllipsed("&Stat"));
  svnmenu->Append(ID_EDIT_SVN_DIFF, wxExEllipsed("&Diff"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_COMMIT, wxExEllipsed("&Commit"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_CAT, wxExEllipsed("Ca&t"));
  svnmenu->Append(ID_EDIT_SVN_BLAME, wxExEllipsed("&Blame"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_PROPLIST, wxExEllipsed("&Proplist"));
  svnmenu->Append(ID_EDIT_SVN_PROPSET, wxExEllipsed("&Propset"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_REVERT, wxExEllipsed("&Revert"));
  svnmenu->AppendSeparator();
  svnmenu->Append(ID_EDIT_SVN_ADD, wxExEllipsed("&Add"));

  AppendSeparator();
  AppendSubMenu(svnmenu, "&SVN");
}

void wxExMenu::AppendTools()
{
  wxExMenu* menuTool = new wxExMenu(*this);

  for (
    map <int, wxExToolInfo>::const_iterator it = wxExTool::Get()->GetToolInfo().begin();
    it != wxExTool::Get()->GetToolInfo().end();
    ++it)
  {
    if (!it->second.GetText().empty())
    {
      menuTool->Append(it->first, it->second.GetText(), it->second.GetHelpText());
    }
  }

  AppendSubMenu(menuTool, _("&Tools"));
}

#endif // wxUSE_GUI
<|endoftext|>


<commit_before>/******************************************************************************\
* File:          util.cpp
* Purpose:       Implementation of wxextension utility methods
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/clipbrd.h>
#include <wx/file.h>
#include <wx/regex.h>
#include <wx/stdpaths.h>
#include <wx/textfile.h> // for wxTextFile::GetEOL()
#include <wx/tokenzr.h>
#include <wx/extension/util.h>

bool wxExClipboardAdd(const wxString& text)
{
  wxClipboardLocker locker;
  if (!locker) return false;
  if (!wxTheClipboard->AddData(new wxTextDataObject(text))) return false;

  // Take care that clipboard data remain after exiting
  // This is a boolean method as well, we don't check it, as
  // clipboard data is copied.
  // At least on Ubuntu 8.10 FLush returns false.
  wxTheClipboard->Flush();

  return true;
}

const wxString wxExClipboardGet()
{
  wxBusyCursor wait;
  wxClipboardLocker locker;

  if (!locker)
  {
    wxLogError("Cannot open clipboard");
    return wxEmptyString;
  }

  if (!wxTheClipboard->IsSupported(wxDF_TEXT))
  {
    wxLogError("Clipboard format not supported");
    return wxEmptyString;
  }

  wxTextDataObject data;
  if (!wxTheClipboard->GetData(data))
  {
    wxLogError("Cannot get clipboard data");
    return wxEmptyString;
  }

  return data.GetText();
}

#if wxUSE_GUI
void wxExComboBoxFromString(
  wxComboBox* cb,
  const wxString& text,
  const wxChar field_separator)
{
  wxStringTokenizer tkz(text, field_separator);
  while (tkz.HasMoreTokens())
  {
    const wxString val = tkz.GetNextToken();
    if (cb->FindString(val) == wxNOT_FOUND)
    {
      cb->Append(val);
    }
  }

  if (cb->GetCount() > 0) cb->SetValue(cb->GetString(0));
}

bool wxExComboBoxToString(
  const wxComboBox* cb,
  wxString& text,
  const wxChar field_separator,
  size_t max_items)
{
  if (cb == NULL)
  {
    return false;
  }

  text = cb->GetValue();
  switch (cb->FindString(cb->GetValue()))
  {
    case wxNOT_FOUND:
    {
      // Add the string, as it is not in the combo box, to the text,
      // simply by appending all combobox items.
      for (size_t i = 0; i < max_items; i++)
        if (i < max_items - 1 && i < cb->GetCount())
          text += field_separator + cb->GetString(i);
    }
    break;
    // No change necessary, the string is already present as the first one.
    case 0: return false; break;
    default:
    {
      // Reorder. The new first element already present, just add all others.
      for (size_t i = 0; i < cb->GetCount(); i++)
      {
        const wxString cb_element = cb->GetString(i);
        if (cb_element != cb->GetValue())
          text += field_separator + cb_element;
      }
    }
  }

  return true;
}

#endif // wxUSE_GUI

long wxExColourToLong(const wxColour& c)
{
  return c.Red() | (c.Green() << 8) | (c.Blue() << 16);
}

const wxString wxExEllipsed(const wxString& text, const wxString& control)
{
  return text + "..." + (!control.empty() ? "\t" + control: wxString(wxEmptyString));
}

const wxString wxExGetEndOfText(
  const wxString& text,
  size_t max_chars)
{
  wxString text_out(text);

  if (text_out.length() > max_chars)
  {
    text_out = "..." + text_out.substr(text_out.length() - max_chars);
  }

  return text_out;
}

int wxExGetNumberOfLines(const wxString& text)
{
  if (text.empty())
  {
    return 0;
  }
  else if (text.Find(wxChar('\r')) != wxNOT_FOUND)
  {
    return text.Freq(wxChar('\r')) + 1;
  }
  else if (text.Find(wxChar('\n')) != wxNOT_FOUND)
  {
    return text.Freq(wxChar('\n')) + 1;
  }
  else
  {
    return 1;
  }
}

int wxExGetLineNumberFromText(const wxString& text)
{
  // Get text after :.
  const size_t pos_char = text.rfind(":");

  if (pos_char == wxString::npos)
  {
    return 0;
  }

  const wxString linenumber = text.substr(pos_char + 1);

  long line;

  if (linenumber.ToLong(&line))
  {
    return line;
  }
  else
  {
    return 0;
  }
}

const wxString wxExGetWord(
  wxString& text,
  bool use_other_field_separators,
  bool use_path_separator)
{
  wxString field_separators = " \t";
  if (use_other_field_separators) field_separators += ":";
  if (use_path_separator) field_separators = wxFILE_SEP_PATH;
  wxString token;
  wxStringTokenizer tkz(text, field_separators);
  if (tkz.HasMoreTokens()) token = tkz.GetNextToken();
  text = tkz.GetString();
  text.Trim(false);
  return token;
}

bool wxExLog(const wxString& text, const wxFileName& filename)
{
  return wxFile(
    filename.GetFullPath(),
    wxFile::write_append).Write(
      wxDateTime::Now().Format() + " " + text + wxTextFile::GetEOL());
}

const wxFileName wxExLogfileName()
{
  if (wxTheApp == NULL)
  {
    return wxFileName("app.log");
  }

#ifdef EX_PORTABLE
  return wxFileName(
    wxPathOnly(wxStandardPaths::Get().GetExecutablePath()),
    wxTheApp->GetAppName().Lower() + ".log");
#else
  return wxFileName(
    wxStandardPaths::Get().GetUserDataDir(),
    wxTheApp->GetAppName().Lower() + ".log");
#endif
}

bool wxExMatchesOneOf(const wxFileName& filename, const wxString& pattern)
{
  if (pattern == "*") return true; // asterix matches always.

  const wxString fullname_uppercase = filename.GetFullName().Upper();

  wxStringTokenizer tokenizer(pattern.Upper(), ";");
  while (tokenizer.HasMoreTokens())
  {
    if (fullname_uppercase.Matches(tokenizer.GetNextToken())) return true;
  }

  return false;
}

const wxString wxExSkipWhiteSpace(const wxString& text, const wxString& replace_with)
{
  wxString output = text;
  wxRegEx("[ \t\n]+").ReplaceAll(&output, replace_with);
  return output;
}

const wxString wxExTranslate(const wxString& text, int pageNum, int numPages)
{
  wxString translation = text;
  wxString num;

  num.Printf("%i", pageNum);
  translation.Replace("@PAGENUM@", num);

  num.Printf("%i", numPages);
  translation.Replace("@PAGESCNT@", num);

  return translation;
}
<commit_msg>added wxASSERT<commit_after>/******************************************************************************\
* File:          util.cpp
* Purpose:       Implementation of wxextension utility methods
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/clipbrd.h>
#include <wx/file.h>
#include <wx/regex.h>
#include <wx/stdpaths.h>
#include <wx/textfile.h> // for wxTextFile::GetEOL()
#include <wx/tokenzr.h>
#include <wx/extension/util.h>

bool wxExClipboardAdd(const wxString& text)
{
  wxClipboardLocker locker;
  if (!locker) return false;
  if (!wxTheClipboard->AddData(new wxTextDataObject(text))) return false;

  // Take care that clipboard data remain after exiting
  // This is a boolean method as well, we don't check it, as
  // clipboard data is copied.
  // At least on Ubuntu 8.10 FLush returns false.
  wxTheClipboard->Flush();

  return true;
}

const wxString wxExClipboardGet()
{
  wxBusyCursor wait;
  wxClipboardLocker locker;

  if (!locker)
  {
    wxLogError("Cannot open clipboard");
    return wxEmptyString;
  }

  if (!wxTheClipboard->IsSupported(wxDF_TEXT))
  {
    wxLogError("Clipboard format not supported");
    return wxEmptyString;
  }

  wxTextDataObject data;
  if (!wxTheClipboard->GetData(data))
  {
    wxLogError("Cannot get clipboard data");
    return wxEmptyString;
  }

  return data.GetText();
}

#if wxUSE_GUI
void wxExComboBoxFromString(
  wxComboBox* cb,
  const wxString& text,
  const wxChar field_separator)
{
  wxASSERT(cb != NULL);

  wxStringTokenizer tkz(text, field_separator);
  while (tkz.HasMoreTokens())
  {
    const wxString val = tkz.GetNextToken();
    if (cb->FindString(val) == wxNOT_FOUND)
    {
      cb->Append(val);
    }
  }

  if (cb->GetCount() > 0) cb->SetValue(cb->GetString(0));
}

bool wxExComboBoxToString(
  const wxComboBox* cb,
  wxString& text,
  const wxChar field_separator,
  size_t max_items)
{
  wxASSERT(cb != NULL);

  text = cb->GetValue();
  switch (cb->FindString(cb->GetValue()))
  {
    case wxNOT_FOUND:
    {
      // Add the string, as it is not in the combo box, to the text,
      // simply by appending all combobox items.
      for (size_t i = 0; i < max_items; i++)
        if (i < max_items - 1 && i < cb->GetCount())
          text += field_separator + cb->GetString(i);
    }
    break;
    // No change necessary, the string is already present as the first one.
    case 0: return false; break;
    default:
    {
      // Reorder. The new first element already present, just add all others.
      for (size_t i = 0; i < cb->GetCount(); i++)
      {
        const wxString cb_element = cb->GetString(i);
        if (cb_element != cb->GetValue())
          text += field_separator + cb_element;
      }
    }
  }

  return true;
}

#endif // wxUSE_GUI

long wxExColourToLong(const wxColour& c)
{
  return c.Red() | (c.Green() << 8) | (c.Blue() << 16);
}

const wxString wxExEllipsed(const wxString& text, const wxString& control)
{
  return text + "..." + (!control.empty() ? "\t" + control: wxString(wxEmptyString));
}

const wxString wxExGetEndOfText(
  const wxString& text,
  size_t max_chars)
{
  wxString text_out(text);

  if (text_out.length() > max_chars)
  {
    text_out = "..." + text_out.substr(text_out.length() - max_chars);
  }

  return text_out;
}

int wxExGetNumberOfLines(const wxString& text)
{
  if (text.empty())
  {
    return 0;
  }
  else if (text.Find(wxChar('\r')) != wxNOT_FOUND)
  {
    return text.Freq(wxChar('\r')) + 1;
  }
  else if (text.Find(wxChar('\n')) != wxNOT_FOUND)
  {
    return text.Freq(wxChar('\n')) + 1;
  }
  else
  {
    return 1;
  }
}

int wxExGetLineNumberFromText(const wxString& text)
{
  // Get text after :.
  const size_t pos_char = text.rfind(":");

  if (pos_char == wxString::npos)
  {
    return 0;
  }

  const wxString linenumber = text.substr(pos_char + 1);

  long line;

  if (linenumber.ToLong(&line))
  {
    return line;
  }
  else
  {
    return 0;
  }
}

const wxString wxExGetWord(
  wxString& text,
  bool use_other_field_separators,
  bool use_path_separator)
{
  wxString field_separators = " \t";
  if (use_other_field_separators) field_separators += ":";
  if (use_path_separator) field_separators = wxFILE_SEP_PATH;
  wxString token;
  wxStringTokenizer tkz(text, field_separators);
  if (tkz.HasMoreTokens()) token = tkz.GetNextToken();
  text = tkz.GetString();
  text.Trim(false);
  return token;
}

bool wxExLog(const wxString& text, const wxFileName& filename)
{
  return wxFile(
    filename.GetFullPath(),
    wxFile::write_append).Write(
      wxDateTime::Now().Format() + " " + text + wxTextFile::GetEOL());
}

const wxFileName wxExLogfileName()
{
  if (wxTheApp == NULL)
  {
    return wxFileName("app.log");
  }

#ifdef EX_PORTABLE
  return wxFileName(
    wxPathOnly(wxStandardPaths::Get().GetExecutablePath()),
    wxTheApp->GetAppName().Lower() + ".log");
#else
  return wxFileName(
    wxStandardPaths::Get().GetUserDataDir(),
    wxTheApp->GetAppName().Lower() + ".log");
#endif
}

bool wxExMatchesOneOf(const wxFileName& filename, const wxString& pattern)
{
  if (pattern == "*") return true; // asterix matches always.

  const wxString fullname_uppercase = filename.GetFullName().Upper();

  wxStringTokenizer tokenizer(pattern.Upper(), ";");
  while (tokenizer.HasMoreTokens())
  {
    if (fullname_uppercase.Matches(tokenizer.GetNextToken())) return true;
  }

  return false;
}

const wxString wxExSkipWhiteSpace(const wxString& text, const wxString& replace_with)
{
  wxString output = text;
  wxRegEx("[ \t\n]+").ReplaceAll(&output, replace_with);
  return output;
}

const wxString wxExTranslate(const wxString& text, int pageNum, int numPages)
{
  wxString translation = text;
  wxString num;

  num.Printf("%i", pageNum);
  translation.Replace("@PAGENUM@", num);

  num.Printf("%i", numPages);
  translation.Replace("@PAGESCNT@", num);

  return translation;
}
<|endoftext|>


<commit_before>//+ Example of a simple script creating 3 threads.
// This script can only be executed via ACliC .x threadsh1.C++.

#include "TCanvas.h"
#include "TFrame.h"
#include "TH1F.h"
#include "TRandom.h"
#include "TThread.h"

TCanvas *c[4];
TH1F    *hpx[4];
TThread *t[5];
Bool_t finished;

void *handle(void *ptr)
{
   long nr = (long) ptr;
   Long64_t nfills = 25000000;
   int upd = 50000;

   char name[32];
   sprintf(name,"hpx%ld",nr);
   TThread::Lock();
   hpx[nr] = new TH1F(name,"This is the px distribution",100,-4,4);
   hpx[nr]->SetFillColor(48);
   TThread::UnLock();
   Float_t px, py, pz;
   gRandom->SetSeed();
   for (Int_t i = 0; i < nfills; i++) {
      gRandom->Rannor(px,py);
      pz = px*px + py*py;
      hpx[nr]->Fill(px);
      if (i && (i%upd) == 0) {
         if (i == upd) {
            TThread::Lock();
            c[nr]->cd();
            hpx[nr]->Draw();
            TThread::UnLock();
         }
         c[nr]->Modified();
         gSystem->Sleep(10);
      }
   }
   return 0;
}

void *joiner(void *)
{
   t[0]->Join();
   t[1]->Join();
   t[2]->Join();
   t[3]->Join();

   finished = kTRUE;

   return 0;
}

void threadsh1()
{
#ifdef __CINT__
   printf("This script can only be executed via ACliC: .x threadsh1.C++\n");
   return;
#endif

   finished = kFALSE;
   //gDebug = 1;

   c[0] = new TCanvas("c0","Dynamic Filling Example",100,20,400,300);
   c[0]->SetFillColor(42);
   c[0]->GetFrame()->SetFillColor(21);
   c[0]->GetFrame()->SetBorderSize(6);
   c[0]->GetFrame()->SetBorderMode(-1);
   c[1] = new TCanvas("c1","Dynamic Filling Example",510,20,400,300);
   c[1]->SetFillColor(42);
   c[1]->GetFrame()->SetFillColor(21);
   c[1]->GetFrame()->SetBorderSize(6);
   c[1]->GetFrame()->SetBorderMode(-1);
   c[2] = new TCanvas("c2","Dynamic Filling Example",100,350,400,300);
   c[2]->SetFillColor(42);
   c[2]->GetFrame()->SetFillColor(21);
   c[2]->GetFrame()->SetBorderSize(6);
   c[2]->GetFrame()->SetBorderMode(-1);
   c[3] = new TCanvas("c3","Dynamic Filling Example",510,350,400,300);
   c[3]->SetFillColor(42);
   c[3]->GetFrame()->SetFillColor(21);
   c[3]->GetFrame()->SetBorderSize(6);
   c[3]->GetFrame()->SetBorderMode(-1);

   printf("Starting Thread 0\n");
   t[0] = new TThread("t0", handle, (void*) 0);
   t[0]->Run();
   printf("Starting Thread 1\n");
   t[1] = new TThread("t1", handle, (void*) 1);
   t[1]->Run();
   printf("Starting Thread 2\n");
   t[2] = new TThread("t2", handle, (void*) 2);
   t[2]->Run();
   printf("Starting Thread 3\n");
   t[3] = new TThread("t3", handle, (void*) 3);
   t[3]->Run();
   printf("Starting Thread 4\n");
   t[4] = new TThread("t4", joiner, (void*) 3);
   t[4]->Run();

   TThread::Ps();

   while (!finished) {
      for (int i = 0; i < 4; i++) {
         if (c[i]->IsModified()) {
            //printf("Update canvas %d\n", i);
            c[i]->Update();
         }
      }
      gSystem->Sleep(100);
      gSystem->ProcessEvents();
   }

   t[4]->Join();
   TThread::Ps();

   delete t[0];
   delete t[1];
   delete t[2];
   delete t[3];
   delete t[4];
}
<commit_msg>Add a protection for the case where a canvas is closed while the threads are running (kill the corresponding thread) This should fix the problem reported on the forum: http://root.cern.ch/phpBB3/viewtopic.php?f=3&t=11835<commit_after>//+ Example of a simple script creating 3 threads.
// This script can only be executed via ACliC .x threadsh1.C++.

#include "TCanvas.h"
#include "TFrame.h"
#include "TH1F.h"
#include "TRandom.h"
#include "TThread.h"

TCanvas *c[4];
TH1F    *hpx[4];
TThread *t[5];
Bool_t finished;

void *handle(void *ptr)
{
   long nr = (long) ptr;
   Long64_t nfills = 25000000;
   int upd = 50000;

   char name[32];
   sprintf(name,"hpx%ld",nr);
   TThread::Lock();
   hpx[nr] = new TH1F(name,"This is the px distribution",100,-4,4);
   hpx[nr]->SetFillColor(48);
   TThread::UnLock();
   Float_t px, py, pz;
   gRandom->SetSeed();
   for (Int_t i = 0; i < nfills; i++) {
      gRandom->Rannor(px,py);
      pz = px*px + py*py;
      hpx[nr]->Fill(px);
      if (i && (i%upd) == 0) {
         if (i == upd) {
            TThread::Lock();
            c[nr]->cd();
            hpx[nr]->Draw();
            TThread::UnLock();
         }
         if (c[nr]) c[nr]->Modified();
         gSystem->Sleep(10);
      }
   }
   return 0;
}

void *joiner(void *)
{
   t[0]->Join();
   t[1]->Join();
   t[2]->Join();
   t[3]->Join();

   finished = kTRUE;

   return 0;
}

void closed(Int_t id)
{
   // kill the thread matching the canvas being closed
   t[id]->Kill();
   // and set the canvas pointer to 0
   c[id] = 0;
}

void threadsh1()
{
#ifdef __CINT__
   printf("This script can only be executed via ACliC: .x threadsh1.C++\n");
   return;
#endif

   finished = kFALSE;
   //gDebug = 1;

   c[0] = new TCanvas("c0","Dynamic Filling Example",100,20,400,300);
   c[0]->SetFillColor(42);
   c[0]->GetFrame()->SetFillColor(21);
   c[0]->GetFrame()->SetBorderSize(6);
   c[0]->GetFrame()->SetBorderMode(-1);
   c[1] = new TCanvas("c1","Dynamic Filling Example",510,20,400,300);
   c[1]->SetFillColor(42);
   c[1]->GetFrame()->SetFillColor(21);
   c[1]->GetFrame()->SetBorderSize(6);
   c[1]->GetFrame()->SetBorderMode(-1);
   c[2] = new TCanvas("c2","Dynamic Filling Example",100,350,400,300);
   c[2]->SetFillColor(42);
   c[2]->GetFrame()->SetFillColor(21);
   c[2]->GetFrame()->SetBorderSize(6);
   c[2]->GetFrame()->SetBorderMode(-1);
   c[3] = new TCanvas("c3","Dynamic Filling Example",510,350,400,300);
   c[3]->SetFillColor(42);
   c[3]->GetFrame()->SetFillColor(21);
   c[3]->GetFrame()->SetBorderSize(6);
   c[3]->GetFrame()->SetBorderMode(-1);

   // connect to the Closed() signal to kill the thread when a canvas is closed
   c[0]->Connect("Closed()", 0, 0, "closed(Int_t=0)");
   c[1]->Connect("Closed()", 0, 0, "closed(Int_t=1)");
   c[2]->Connect("Closed()", 0, 0, "closed(Int_t=2)");
   c[3]->Connect("Closed()", 0, 0, "closed(Int_t=3)");

   printf("Starting Thread 0\n");
   t[0] = new TThread("t0", handle, (void*) 0);
   t[0]->Run();
   printf("Starting Thread 1\n");
   t[1] = new TThread("t1", handle, (void*) 1);
   t[1]->Run();
   printf("Starting Thread 2\n");
   t[2] = new TThread("t2", handle, (void*) 2);
   t[2]->Run();
   printf("Starting Thread 3\n");
   t[3] = new TThread("t3", handle, (void*) 3);
   t[3]->Run();
   printf("Starting Thread 4\n");
   t[4] = new TThread("t4", joiner, (void*) 3);
   t[4]->Run();

   TThread::Ps();

   while (!finished) {
      for (int i = 0; i < 4; i++) {
         if (c[i] && c[i]->IsModified()) {
            //printf("Update canvas %d\n", i);
            c[i]->Update();
         }
      }
      gSystem->Sleep(100);
      gSystem->ProcessEvents();
   }

   t[4]->Join();
   TThread::Ps();

   delete t[0];
   delete t[1];
   delete t[2];
   delete t[3];
   delete t[4];
}
<|endoftext|>


<commit_before>// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * 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.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
// OWNER 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.

// ---
// Author: Ken Ashcraft <opensource@google.com>

#include "static_vars.h"
#include <stddef.h>                     // for NULL
#include <new>                          // for operator new
#include "internal_logging.h"  // for CHECK_CONDITION
#include "common.h"
#include "sampler.h"           // for Sampler
#include "base/googleinit.h"

namespace tcmalloc {

#if defined(HAVE_FORK) && defined(HAVE_PTHREAD)
// These following two functions are registered via pthread_atfork to make
// sure the central_cache locks remain in a consisten state in the forked
// version of the thread.

static
void CentralCacheLockAll()
{
  Static::pageheap_lock()->Lock();
  for (int i = 0; i < kNumClasses; ++i)
    Static::central_cache()[i].Lock();
}

static
void CentralCacheUnlockAll()
{
  for (int i = 0; i < kNumClasses; ++i)
    Static::central_cache()[i].Unlock();
  Static::pageheap_lock()->Unlock();
}
#endif

static inline
void SetupAtForkLocksHandler()
{
#if defined(HAVE_FORK) && defined(HAVE_PTHREAD)
  pthread_atfork(CentralCacheLockAll,    // parent calls before fork
                 CentralCacheUnlockAll,  // parent calls after fork
                 CentralCacheUnlockAll); // child calls after fork
#endif
}

SpinLock Static::pageheap_lock_(SpinLock::LINKER_INITIALIZED);
SizeMap Static::sizemap_;
CentralFreeListPadded Static::central_cache_[kNumClasses];
PageHeapAllocator<Span> Static::span_allocator_;
PageHeapAllocator<StackTrace> Static::stacktrace_allocator_;
Span Static::sampled_objects_;
PageHeapAllocator<StackTraceTable::Bucket> Static::bucket_allocator_;
StackTrace* Static::growth_stacks_ = NULL;
PageHeap* Static::pageheap_ = NULL;

void Static::InitStaticVars() {
  sizemap_.Init();
  span_allocator_.Init();
  span_allocator_.New(); // Reduce cache conflicts
  span_allocator_.New(); // Reduce cache conflicts
  stacktrace_allocator_.Init();
  bucket_allocator_.Init();
  // Do a bit of sanitizing: make sure central_cache is aligned properly
  CHECK_CONDITION((sizeof(central_cache_[0]) % 64) == 0);
  for (int i = 0; i < kNumClasses; ++i) {
    central_cache_[i].Init(i);
  }

  // It's important to have PageHeap allocated, not in static storage,
  // so that HeapLeakChecker does not consider all the byte patterns stored
  // in is caches as pointers that are sources of heap object liveness,
  // which leads to it missing some memory leaks.
  pageheap_ = new (MetaDataAlloc(sizeof(PageHeap))) PageHeap;
  DLL_Init(&sampled_objects_);
  Sampler::InitStatics();
}

REGISTER_MODULE_INITIALIZER(tcmalloc_fork_handler, SetupAtForkLocksHandler());

}  // namespace tcmalloc
<commit_msg>issue-583: include pthread.h into static_var.cc<commit_after>// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * 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.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
// OWNER 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.

// ---
// Author: Ken Ashcraft <opensource@google.com>

#include <config.h>
#include "static_vars.h"
#include <stddef.h>                     // for NULL
#include <new>                          // for operator new
#ifdef HAVE_PTHREAD
#include <pthread.h>                    // for pthread_atfork
#endif
#include "internal_logging.h"  // for CHECK_CONDITION
#include "common.h"
#include "sampler.h"           // for Sampler
#include "base/googleinit.h"

namespace tcmalloc {

#if defined(HAVE_FORK) && defined(HAVE_PTHREAD)
// These following two functions are registered via pthread_atfork to make
// sure the central_cache locks remain in a consisten state in the forked
// version of the thread.

static
void CentralCacheLockAll()
{
  Static::pageheap_lock()->Lock();
  for (int i = 0; i < kNumClasses; ++i)
    Static::central_cache()[i].Lock();
}

static
void CentralCacheUnlockAll()
{
  for (int i = 0; i < kNumClasses; ++i)
    Static::central_cache()[i].Unlock();
  Static::pageheap_lock()->Unlock();
}
#endif

static inline
void SetupAtForkLocksHandler()
{
#if defined(HAVE_FORK) && defined(HAVE_PTHREAD)
  pthread_atfork(CentralCacheLockAll,    // parent calls before fork
                 CentralCacheUnlockAll,  // parent calls after fork
                 CentralCacheUnlockAll); // child calls after fork
#endif
}

SpinLock Static::pageheap_lock_(SpinLock::LINKER_INITIALIZED);
SizeMap Static::sizemap_;
CentralFreeListPadded Static::central_cache_[kNumClasses];
PageHeapAllocator<Span> Static::span_allocator_;
PageHeapAllocator<StackTrace> Static::stacktrace_allocator_;
Span Static::sampled_objects_;
PageHeapAllocator<StackTraceTable::Bucket> Static::bucket_allocator_;
StackTrace* Static::growth_stacks_ = NULL;
PageHeap* Static::pageheap_ = NULL;

void Static::InitStaticVars() {
  sizemap_.Init();
  span_allocator_.Init();
  span_allocator_.New(); // Reduce cache conflicts
  span_allocator_.New(); // Reduce cache conflicts
  stacktrace_allocator_.Init();
  bucket_allocator_.Init();
  // Do a bit of sanitizing: make sure central_cache is aligned properly
  CHECK_CONDITION((sizeof(central_cache_[0]) % 64) == 0);
  for (int i = 0; i < kNumClasses; ++i) {
    central_cache_[i].Init(i);
  }

  // It's important to have PageHeap allocated, not in static storage,
  // so that HeapLeakChecker does not consider all the byte patterns stored
  // in is caches as pointers that are sources of heap object liveness,
  // which leads to it missing some memory leaks.
  pageheap_ = new (MetaDataAlloc(sizeof(PageHeap))) PageHeap;
  DLL_Init(&sampled_objects_);
  Sampler::InitStatics();
}

REGISTER_MODULE_INITIALIZER(tcmalloc_fork_handler, SetupAtForkLocksHandler());

}  // namespace tcmalloc
<|endoftext|>


<commit_before>#ifndef AMGCL_LEVEL_VIENNACL_HPP
#define AMGCL_LEVEL_VIENNACL_HPP

/*
The MIT License

Copyright (c) 2012 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include <array>

#include <amgcl/spmat.hpp>
#include <amgcl/operations_viennacl.hpp>

#include <viennacl/vector.hpp>
#include <viennacl/compressed_matrix.hpp>
#include <viennacl/ell_matrix.hpp>
#include <viennacl/hyb_matrix.hpp>
#include "viennacl/linalg/inner_prod.hpp"
#include <viennacl/linalg/prod.hpp>
#include <viennacl/generator/custom_operation.hpp>

namespace amgcl {
namespace level {

enum gpu_matrix_format {
    GPU_MATRIX_CRS,
    GPU_MATRIX_ELL,
    GPU_MATRIX_HYB
};

template <gpu_matrix_format Format, typename value_type>
struct matrix_format;

template <typename value_type>
struct matrix_format<GPU_MATRIX_CRS, value_type> {
    typedef viennacl::compressed_matrix<value_type> type;
};

template <typename value_type>
struct matrix_format<GPU_MATRIX_ELL, value_type> {
    typedef viennacl::ell_matrix<value_type> type;
};

template <typename value_type>
struct matrix_format<GPU_MATRIX_HYB, value_type> {
    typedef viennacl::hyb_matrix<value_type> type;
};

// ViennaCL-based AMG hierarchy.
template <gpu_matrix_format Format = GPU_MATRIX_HYB>
struct ViennaCL {

template <typename value_t, typename index_t = long long>
class instance {
    public:
        typedef sparse::matrix<value_t, index_t>              cpu_matrix;
        typedef typename matrix_format<Format, value_t>::type matrix;
        typedef viennacl::vector<value_t>                     vector;

        // Construct complete multigrid level from system matrix (a),
        // prolongation (p) and restriction (r) operators.
        // The matrices are moved into the local members.
        instance(cpu_matrix &&a, cpu_matrix &&p, cpu_matrix &&r, const params &prm, unsigned nlevel)
            : d(a.rows), t(a.rows)
        {
            viennacl::copy(sparse::viennacl_map(a), A);
            viennacl::copy(sparse::viennacl_map(p), P);
            viennacl::copy(sparse::viennacl_map(r), R);

            viennacl::fast_copy(diagonal(a), d);

            if (nlevel) {
                u.resize(a.rows);
                f.resize(a.rows);

                if (prm.kcycle && nlevel % prm.kcycle == 0)
                    for(auto v = cg.begin(); v != cg.end(); v++)
                        v->resize(a.rows);
            }

            a.clear();
            p.clear();
            r.clear();
        }

        // Construct the coarsest hierarchy level from system matrix (a) and
        // its inverse (ai).
        instance(cpu_matrix &&a, cpu_matrix &&ai, const params &prm, unsigned nlevel)
            : d(a.rows), u(a.rows), f(a.rows), t(a.rows)
        {
            viennacl::copy(sparse::viennacl_map(a),  A);
            viennacl::copy(sparse::viennacl_map(ai), Ainv);

            viennacl::fast_copy(diagonal(a), d);

            a.clear();
            ai.clear();
        }

        // Perform one relaxation (smoothing) step.
        void relax(const vector &rhs, vector &x) const {
            using namespace viennacl::generator;

            static symbolic_vector<0, value_t> sym_x;
            static symbolic_vector<1, value_t> sym_t;
            static symbolic_vector<2, value_t> sym_d;
            static cpu_symbolic_scalar<3, value_t> sym_w;
            static custom_operation mul_add(
                    sym_x += sym_w * element_div(sym_t, sym_d), "amgcl_relax_mul_add");

            const index_t n = x.size();

            t = rhs;
            t -= viennacl::linalg::prod(A, x);
            value_t w = static_cast<value_t>(0.72);
            viennacl::ocl::enqueue( mul_add(x, t, d, w) );
        }

        // Compute residual value.
        value_t resid(const vector &rhs, vector &x) const {
            t = rhs;
            t -= viennacl::linalg::prod(A, x);

            return sqrt(viennacl::linalg::inner_prod(t, t));
        }

        // Perform one V-cycle. Coarser levels are cycled recursively. The
        // coarsest level is solved directly.
        template <class Iterator>
        static void cycle(Iterator lvl, Iterator end, const params &prm,
                const vector &rhs, vector &x)
        {
            Iterator nxt = lvl; ++nxt;

            if (nxt != end) {
                for(unsigned j = 0; j < prm.ncycle; ++j) {
                    for(unsigned i = 0; i < prm.npre; ++i) lvl->relax(rhs, x);

                    lvl->t = rhs;
                    lvl->t -= viennacl::linalg::prod(lvl->A, x);
                    nxt->f = viennacl::linalg::prod(lvl->R, lvl->t);
                    nxt->u.clear();

                    if (nxt->cg[0].size())
                        kcycle(nxt, end, prm, nxt->f, nxt->u);
                    else
                        cycle(nxt, end, prm, nxt->f, nxt->u);

                    x += viennacl::linalg::prod(lvl->P, nxt->u);

                    for(unsigned i = 0; i < prm.npost; ++i) lvl->relax(rhs, x);
                }
            } else {
                x = viennacl::linalg::prod(lvl->Ainv, rhs);
            }
        }

        template <class Iterator>
        static void kcycle(Iterator lvl, Iterator end, const params &prm,
                const vector &rhs, vector &x)
        {
            Iterator nxt = lvl; ++nxt;

            if (nxt != end) {
                auto &r = lvl->cg[0];
                auto &s = lvl->cg[1];
                auto &p = lvl->cg[2];
                auto &q = lvl->cg[3];

                r = rhs;

                value_t rho1 = 0, rho2 = 0;

                for(int iter = 0; iter < 2; ++iter) {
                    s.clear();
                    cycle(lvl, end, prm, r, s);

                    rho2 = rho1;
                    rho1 = viennacl::linalg::inner_prod(r, s);

                    if (iter)
                        p = s + (rho1 / rho2) * p;
                    else
                        p = s;

                    q = viennacl::linalg::prod(lvl->A, p);

                    value_t alpha = rho1 / viennacl::linalg::inner_prod(q, p);

                    x += alpha * p;
                    r -= alpha * q;
                }
            } else {
                x = viennacl::linalg::prod(lvl->Ainv, rhs);
            }
        }
    private:
        matrix A;
        matrix P;
        matrix R;
        matrix Ainv;

        vector d;

        mutable vector u;
        mutable vector f;
        mutable vector t;

        mutable std::array<vector, 4> cg;
};

};

} // namespace level
} // namespace amgcl

#endif
<commit_msg>ViennaCL optimizations<commit_after>#ifndef AMGCL_LEVEL_VIENNACL_HPP
#define AMGCL_LEVEL_VIENNACL_HPP

/*
The MIT License

Copyright (c) 2012 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include <array>

#include <amgcl/spmat.hpp>
#include <amgcl/operations_viennacl.hpp>

#include <viennacl/vector.hpp>
#include <viennacl/compressed_matrix.hpp>
#include <viennacl/ell_matrix.hpp>
#include <viennacl/hyb_matrix.hpp>
#include "viennacl/linalg/inner_prod.hpp"
#include <viennacl/linalg/prod.hpp>
#include <viennacl/generator/custom_operation.hpp>

namespace amgcl {
namespace level {

enum gpu_matrix_format {
    GPU_MATRIX_CRS,
    GPU_MATRIX_ELL,
    GPU_MATRIX_HYB
};

template <gpu_matrix_format Format, typename value_type>
struct matrix_format;

template <typename value_type>
struct matrix_format<GPU_MATRIX_CRS, value_type> {
    typedef viennacl::compressed_matrix<value_type> type;
};

template <typename value_type>
struct matrix_format<GPU_MATRIX_ELL, value_type> {
    typedef viennacl::ell_matrix<value_type> type;
};

template <typename value_type>
struct matrix_format<GPU_MATRIX_HYB, value_type> {
    typedef viennacl::hyb_matrix<value_type> type;
};

// ViennaCL-based AMG hierarchy.
template <gpu_matrix_format Format = GPU_MATRIX_HYB>
struct ViennaCL {

template <typename value_t, typename index_t = long long>
class instance {
    public:
        typedef sparse::matrix<value_t, index_t>              cpu_matrix;
        typedef typename matrix_format<Format, value_t>::type matrix;
        typedef viennacl::vector<value_t>                     vector;

        // Construct complete multigrid level from system matrix (a),
        // prolongation (p) and restriction (r) operators.
        // The matrices are moved into the local members.
        instance(cpu_matrix &&a, cpu_matrix &&p, cpu_matrix &&r, const params &prm, unsigned nlevel)
            : d(a.rows), t(a.rows)
        {
            viennacl::copy(sparse::viennacl_map(a), A);
            viennacl::copy(sparse::viennacl_map(p), P);
            viennacl::copy(sparse::viennacl_map(r), R);

            viennacl::fast_copy(diagonal(a), d);

            if (nlevel) {
                u.resize(a.rows);
                f.resize(a.rows);

                if (prm.kcycle && nlevel % prm.kcycle == 0)
                    for(auto v = cg.begin(); v != cg.end(); v++)
                        v->resize(a.rows);
            }

            a.clear();
            p.clear();
            r.clear();
        }

        // Construct the coarsest hierarchy level from system matrix (a) and
        // its inverse (ai).
        instance(cpu_matrix &&a, cpu_matrix &&ai, const params &prm, unsigned nlevel)
            : d(a.rows), u(a.rows), f(a.rows), t(a.rows)
        {
            viennacl::copy(sparse::viennacl_map(a),  A);
            viennacl::copy(sparse::viennacl_map(ai), Ainv);

            viennacl::fast_copy(diagonal(a), d);

            a.clear();
            ai.clear();
        }

        // Perform one relaxation (smoothing) step.
        void relax(const vector &rhs, vector &x) const {
            using namespace viennacl::generator;

            static symbolic_vector<0, value_t> sym_x;
            static symbolic_vector<1, value_t> sym_t;
            static symbolic_vector<2, value_t> sym_d;
            static cpu_symbolic_scalar<3, value_t> sym_w;
            static custom_operation mul_add(
                    sym_x += sym_w * element_div(sym_t, sym_d), "amgcl_relax_mul_add");

            const index_t n = x.size();

            t = viennacl::linalg::prod(A, x);
            t = rhs - t;
            value_t w = static_cast<value_t>(0.72);
            viennacl::ocl::enqueue( mul_add(x, t, d, w) );
        }

        // Compute residual value.
        value_t resid(const vector &rhs, vector &x) const {
            t = viennacl::linalg::prod(A, x);
            t = rhs - t;

            return sqrt(viennacl::linalg::inner_prod(t, t));
        }

        // Perform one V-cycle. Coarser levels are cycled recursively. The
        // coarsest level is solved directly.
        template <class Iterator>
        static void cycle(Iterator lvl, Iterator end, const params &prm,
                const vector &rhs, vector &x)
        {
            Iterator nxt = lvl; ++nxt;

            if (nxt != end) {
                for(unsigned j = 0; j < prm.ncycle; ++j) {
                    for(unsigned i = 0; i < prm.npre; ++i) lvl->relax(rhs, x);

                    lvl->t = viennacl::linalg::prod(lvl->A, x);
                    lvl->t = rhs - lvl->t;
                    nxt->f = viennacl::linalg::prod(lvl->R, lvl->t);
                    nxt->u.clear();

                    if (nxt->cg[0].size())
                        kcycle(nxt, end, prm, nxt->f, nxt->u);
                    else
                        cycle(nxt, end, prm, nxt->f, nxt->u);

                    lvl->t = viennacl::linalg::prod(lvl->P, nxt->u);
                    x += lvl->t;

                    for(unsigned i = 0; i < prm.npost; ++i) lvl->relax(rhs, x);
                }
            } else {
                x = viennacl::linalg::prod(lvl->Ainv, rhs);
            }
        }

        template <class Iterator>
        static void kcycle(Iterator lvl, Iterator end, const params &prm,
                const vector &rhs, vector &x)
        {
            Iterator nxt = lvl; ++nxt;

            if (nxt != end) {
                auto &r = lvl->cg[0];
                auto &s = lvl->cg[1];
                auto &p = lvl->cg[2];
                auto &q = lvl->cg[3];

                r = rhs;

                value_t rho1 = 0, rho2 = 0;

                for(int iter = 0; iter < 2; ++iter) {
                    s.clear();
                    cycle(lvl, end, prm, r, s);

                    rho2 = rho1;
                    rho1 = viennacl::linalg::inner_prod(r, s);

                    if (iter)
                        p = s + (rho1 / rho2) * p;
                    else
                        p = s;

                    q = viennacl::linalg::prod(lvl->A, p);

                    value_t alpha = rho1 / viennacl::linalg::inner_prod(q, p);

                    x += alpha * p;
                    r -= alpha * q;
                }
            } else {
                x = viennacl::linalg::prod(lvl->Ainv, rhs);
            }
        }
    private:
        matrix A;
        matrix P;
        matrix R;
        matrix Ainv;

        vector d;

        mutable vector u;
        mutable vector f;
        mutable vector t;

        mutable std::array<vector, 4> cg;
};

};

} // namespace level
} // namespace amgcl

#endif
<|endoftext|>


<commit_before>/*Copyright (c) 2011, Edgar Solomonik, all rights reserved.*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <math.h>
#include <assert.h>
#include <algorithm>
#include <ctf.hpp>
#include "../src/shared/util.h"

void ccsdt_t3_to_t2(int const  n,
                    CTF_World  &dw,
                    char const *dir){
  int rank, i, num_pes;
  int64_t np;
  double * pairs;
  int64_t * indices;

  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &num_pes);

  if (rank == 0)
    printf("n = %d\n", n);

  int shapeAS4[] = {AS,NS,AS,NS};
  int shapeAS6[] = {AS,AS,NS,AS,AS,NS};
  int shapeNS4[] = {NS,NS,NS,NS};
  int shapeNS6[] = {NS,NS,NS,NS,NS,NS};
  int sizeN4[] = {n,n,n,n};
  int sizeN6[] = {n,n,n,n,n,n};

  //* Creates distributed tensors initialized with zeros
  CTF_Tensor AS_A(4, sizeN4, shapeAS4, dw);
  CTF_Tensor AS_B(6, sizeN6, shapeAS6, dw);
  CTF_Tensor AS_C(4, sizeN4, shapeAS4, dw);
  CTF_Tensor NS_A(4, sizeN4, shapeNS4, dw);
  CTF_Tensor NS_B(6, sizeN6, shapeNS6, dw);
  CTF_Tensor NS_C(4, sizeN4, shapeNS4, dw);

  if (rank == 0)
    printf("tensor creation succeed\n");

  //* Writes noise to local data based on global index
  AS_A.get_local_data(&np, &indices, &pairs);
  for (i=0; i<np; i++ ) pairs[i] = (1.E-3)*sin(indices[i]);
  AS_A.write_remote_data(np, indices, pairs);
  NS_A.write_remote_data(np, indices, pairs);
  free(pairs);
  free(indices);
  AS_B.get_local_data(&np, &indices, &pairs);
  for (i=0; i<np; i++ ) pairs[i] = (1.E-3)*sin(.33+indices[i]);
  AS_B.write_remote_data(np, indices, pairs);
  AS_B.write_remote_data(np, indices, pairs);
  free(pairs);
  free(indices);
  AS_C.get_local_data(&np, &indices, &pairs);
  for (i=0; i<np; i++ ) pairs[i] = (1.E-3)*sin(.66+indices[i]);
  AS_C.write_remote_data(np, indices, pairs);
  NS_C.write_remote_data(np, indices, pairs);

  AS_C["ijmn"] += .5*AS_A["mnje"]*AS_B["abeimn"];

  NS_C["ijmn"] += NS_A["mnje"]*NS_B["abeimn"];
  NS_C["ijmn"] -= NS_A["mnje"]*NS_B["abemin"];
  NS_C["ijmn"] -= NS_A["mnje"]*NS_B["abenmi"];
  NS_C["ijmn"] -= NS_A["mnje"]*NS_B["aebimn"];
  NS_C["ijmn"] += NS_A["mnje"]*NS_B["aebmin"];
  NS_C["ijmn"] += NS_A["mnje"]*NS_B["aebnmi"];
  NS_C["ijmn"] -= NS_A["mnje"]*NS_B["eabimn"];
  NS_C["ijmn"] += NS_A["mnje"]*NS_B["eabmin"];
  NS_C["ijmn"] += NS_A["mnje"]*NS_B["eabnmi"];
  NS_C["ijmn"] -= NS_A["mnej"]*NS_B["abeimn"];
  NS_C["ijmn"] += NS_A["mnej"]*NS_B["abemin"];
  NS_C["ijmn"] += NS_A["mnej"]*NS_B["abenmi"];
  NS_C["ijmn"] += NS_A["mnej"]*NS_B["aebimn"];
  NS_C["ijmn"] -= NS_A["mnej"]*NS_B["aebmin"];
  NS_C["ijmn"] -= NS_A["mnej"]*NS_B["aebnmi"];
  NS_C["ijmn"] += NS_A["mnej"]*NS_B["eabimn"];
  NS_C["ijmn"] -= NS_A["mnej"]*NS_B["eabmin"];
  NS_C["ijmn"] -= NS_A["mnej"]*NS_B["eabnmi"];
  double nrm_AS = AS_C.reduce(CTF_OP_SQNRM2);
  double nrm_NS = NS_C.reduce(CTF_OP_SQNRM2);
  if (rank == 0) printf("norm of AS_C = %lf NS_C = %lf\n", nrm_AS, nrm_NS);
  AS_C["ijmn"] -= NS_C["ijmn"];

  double nrm = AS_C.reduce(CTF_OP_SQNRM2);
  if (rank == 0) printf("norm of AS_C after contraction should be zero, is = %lf\n", nrm);

  free(pairs);
  free(indices);
}

char* getCmdOption(char ** begin,
                   char ** end,
                   const   std::string & option){
  char ** itr = std::find(begin, end, option);
  if (itr != end && ++itr != end){
    return *itr;
  }
  return 0;
}

int main(int argc, char ** argv){
  int rank, np, niter, n;
  int const in_num = argc;
  char dir[120];
  char ** input_str = argv;

  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &np);

  if (getCmdOption(input_str, input_str+in_num, "-n")){
    n = atoi(getCmdOption(input_str, input_str+in_num, "-n"));
    if (n < 0) n = 7;
  } else n = 7;

  if (getCmdOption(input_str, input_str+in_num, "-niter")){
    niter = atoi(getCmdOption(input_str, input_str+in_num, "-niter"));
    if (niter < 0) niter = 3;
  } else niter = 3;

  CTF_World dw;

  ccsdt_t3_to_t2(n, dw, dir);

  MPI_Finalize();
  return 0;
}

<commit_msg>Updated CCSDT test<commit_after>/*Copyright (c) 2011, Edgar Solomonik, all rights reserved.*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <math.h>
#include <assert.h>
#include <algorithm>
#include <ctf.hpp>
#include "../src/shared/util.h"

void ccsdt_t3_to_t2(int const  n,
                    int const  m,
                    CTF_World  &dw,
                    char const *dir){
  int rank, i, num_pes;
  int64_t np;
  double * pairs;
  int64_t * indices;

  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &num_pes);

  if (rank == 0)
    printf("n = %d\n", n);

  int shapeAS4[] = {AS,NS,AS,NS};
  int shapeAS6[] = {AS,NS,NS,AS,NS,NS};
  int shapeNS4[] = {NS,NS,NS,NS};
  int shapeNS6[] = {NS,NS,NS,NS,NS,NS};
  int nnnm[] = {n,n,n,m};
  int mmnn[] = {m,m,n,n};
  int mmmnnn[] = {m,m,m,n,n,n};

  //* Creates distributed tensors initialized with zeros
  CTF_Tensor AS_A(4, nnnm, shapeNS4, dw);
  CTF_Tensor AS_B(6, mmmnnn, shapeAS6, dw);
  CTF_Tensor AS_C(4, mmnn, shapeAS4, dw);
  CTF_Tensor NS_A(4, nnnm, shapeNS4, dw);
  CTF_Tensor NS_B(6, mmmnnn, shapeNS6, dw);
  CTF_Tensor NS_C(4, mmnn, shapeNS4, dw);

  if (rank == 0)
    printf("tensor creation succeed\n");

  //* Writes noise to local data based on global index
  srand48(2013);
  AS_A.get_local_data(&np, &indices, &pairs);
  for (i=0; i<np; i++ ) pairs[i] = drand48()-.5; //(1.E-3)*sin(indices[i]);
  AS_A.write_remote_data(np, indices, pairs);
//  NS_A.write_remote_data(np, indices, pairs);
  free(pairs);
  free(indices);
  AS_B.get_local_data(&np, &indices, &pairs);
  for (i=0; i<np; i++ ) pairs[i] = drand48()-.5; //(1.E-3)*sin(.33+indices[i]);
  AS_B.write_remote_data(np, indices, pairs);
//  NS_B.write_remote_data(np, indices, pairs);
  free(pairs);
  free(indices);
  AS_C.get_local_data(&np, &indices, &pairs);
  for (i=0; i<np; i++ ) pairs[i] = drand48()-.5; //(1.E-3)*sin(.66+indices[i]);
  AS_C.write_remote_data(np, indices, pairs);
//  NS_C.write_remote_data(np, indices, pairs);
  NS_C["abij"] = AS_C["abij"];
  NS_A["abij"] = AS_A["abij"];
  NS_B["abcijk"] = AS_B["abcijk"];
  NS_B["abcijk"] -= AS_B["bacijk"];
  NS_B["abcijk"] -= AS_B["abcjik"];
  NS_B["abcijk"] += AS_B["bacjik"];

  AS_C["abij"] += 0.5*AS_A["mnje"]*AS_B["abeimn"];

  NS_C["abij"] += 0.5*NS_A["mnje"]*NS_B["abeimn"];
//  NS_C["abij"] -= NS_A["mnje"]*NS_B["abemin"];
  NS_C["abij"] -= 0.5*NS_A["mnie"]*NS_B["abejmn"];
//  NS_C["abij"] += NS_A["mnie"]*NS_B["abemjn"];
//  NS_C["abij"] += NS_A["mnje"]*NS_B["abemni"];
//  NS_C["abij"] += NS_A["nmje"]*NS_B["abenim"];

  double nrm_AS = AS_C.reduce(CTF_OP_SQNRM2);
  double nrm_NS = NS_C.reduce(CTF_OP_SQNRM2);
  if (rank == 0) printf("norm of AS_C = %lf NS_C = %lf\n", nrm_AS, nrm_NS);
  AS_C["abij"] -= NS_C["abij"];

  double nrm = AS_C.reduce(CTF_OP_SQNRM2);
  if (rank == 0) printf("norm of AS_C after contraction should be zero, is = %lf\n", nrm);

  free(pairs);
  free(indices);
}

char* getCmdOption(char ** begin,
                   char ** end,
                   const   std::string & option){
  char ** itr = std::find(begin, end, option);
  if (itr != end && ++itr != end){
    return *itr;
  }
  return 0;
}

int main(int argc, char ** argv){
  int rank, np, niter, n, m;
  int const in_num = argc;
  char dir[120];
  char ** input_str = argv;

  MPI_Init(&argc, &argv);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &np);

  if (getCmdOption(input_str, input_str+in_num, "-n")){
    n = atoi(getCmdOption(input_str, input_str+in_num, "-n"));
    if (n < 0) n = 4;
  } else n = 4;
  if (getCmdOption(input_str, input_str+in_num, "-m")){
    m = atoi(getCmdOption(input_str, input_str+in_num, "-m"));
    if (m < 0) m = 6;
  } else m = 6;

  if (getCmdOption(input_str, input_str+in_num, "-niter")){
    niter = atoi(getCmdOption(input_str, input_str+in_num, "-niter"));
    if (niter < 0) niter = 3;
  } else niter = 3;

  CTF_World dw;

  ccsdt_t3_to_t2(n, m, dw, dir);

  MPI_Finalize();
  return 0;
}

<|endoftext|>


<commit_before>#pragma once
#include <memory>
#include <iostream>
#include "TimingGraph.hpp"
#include "timing_constraints_fwd.hpp"
#include "TimingAnalyzer.hpp"

void write_timing_graph(std::ostream& os, const tatum::TimingGraph& tg);
void write_timing_constraints(std::ostream& os, const tatum::TimingConstraints& tc);
void write_analysis_result(std::ostream& os, const tatum::TimingGraph& tg, const std::shared_ptr<tatum::TimingAnalyzer> analyzer);

template<class DelayCalc>
void write_delay_model(std::ostream& os, const tatum::TimingGraph& tg, const DelayCalc& dc) {
    os << "delay_model:\n";
    for(auto edge_id : tg.edges()) {
        os << " edge: " << size_t(edge_id);
        os << " min_delay: " << dc.min_edge_delay(tg, edge_id).value();
        os << " max_delay: " << dc.max_edge_delay(tg, edge_id).value();
        os << "\n";
    }
    os << "\n";
}
<commit_msg>Separate-out setup/hold edges in delay model echo<commit_after>#pragma once
#include <memory>
#include <iostream>
#include "TimingGraph.hpp"
#include "timing_constraints_fwd.hpp"
#include "TimingAnalyzer.hpp"

void write_timing_graph(std::ostream& os, const tatum::TimingGraph& tg);
void write_timing_constraints(std::ostream& os, const tatum::TimingConstraints& tc);
void write_analysis_result(std::ostream& os, const tatum::TimingGraph& tg, const std::shared_ptr<tatum::TimingAnalyzer> analyzer);

template<class DelayCalc>
void write_delay_model(std::ostream& os, const tatum::TimingGraph& tg, const DelayCalc& dc) {
    os << "delay_model:\n";
    for(auto edge_id : tg.edges()) {
        tatum::NodeId src_node = tg.edge_src_node(edge_id);
        tatum::NodeId sink_node = tg.edge_sink_node(edge_id);

        os << " edge: " << size_t(edge_id);
        if(tg.node_type(src_node) == tatum::NodeType::CPIN && tg.node_type(sink_node) == tatum::NodeType::SINK) {
            os << " setup_time: " << dc.setup_time(tg, edge_id).value();
            os << " hold_time: " << dc.hold_time(tg, edge_id).value();
        } else {
            os << " min_delay: " << dc.min_edge_delay(tg, edge_id).value();
            os << " max_delay: " << dc.max_edge_delay(tg, edge_id).value();
        }
        os << "\n";
    }
    os << "\n";
}
<|endoftext|>


<commit_before>#include "meshoptimizer.h"

#include <cassert>

#include <algorithm>
#include <vector>

static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned int* valence)
{
	unsigned int index = 0;
	unsigned int iv = ~0u;

	for (unsigned int i = 0; i < buffer_size; ++i)
	{
		unsigned int v = std::min(valence[buffer[i][0]], std::min(valence[buffer[i][1]], valence[buffer[i][2]]));

		if (v < iv)
		{
			index = i;
			iv = v;
		}
	}

	return index;
}

static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1)
{
	for (unsigned int i = 0; i < buffer_size; ++i)
	{
		unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];

		if (e0 == a && e1 == b)
			return (i << 2) | 2;
		else if (e0 == b && e1 == c)
			return (i << 2) | 0;
		else if (e0 == c && e1 == a)
			return (i << 2) | 1;
	}

	return -1;
}

size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count)
{
	const size_t buffer_capacity = 16;

	unsigned int buffer[buffer_capacity][3] = {};
	unsigned int buffer_size = 0;

	size_t index_offset = 0;

	unsigned int strip[3] = {};
	unsigned int stripx = 0;

	size_t strip_size = 0;

	std::vector<unsigned int> valence(vertex_count, 0);

	for (size_t i = 0; i < index_count; ++i)
	{
		unsigned int index = indices[i];
		assert(index < vertex_count);

		valence[index]++;
	}

	while (buffer_size > 0 || index_offset < index_count)
	{
		// fill triangle buffer
		while (buffer_size < buffer_capacity && index_offset < index_count)
		{
			buffer[buffer_size][0] = indices[index_offset + 0];
			buffer[buffer_size][1] = indices[index_offset + 1];
			buffer[buffer_size][2] = indices[index_offset + 2];

			buffer_size++;
			index_offset += 3;
		}

		assert(buffer_size > 0);

		// find next triangle
		// note that the order of last edge flips on every iteration
		int next = findStripNext(buffer, buffer_size, strip[1 + stripx], strip[2 - stripx]);

		if (next >= 0)
		{
			assert((next & 3) < 3);

			unsigned int i = next >> 2;
			unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
			unsigned int v = buffer[i][next & 3];

			// emit the next vertex in the strip
			destination[strip_size++] = v;

			// next strip has flipped winding
			strip[0] = strip[1];
			strip[1] = strip[2];
			strip[2] = v;
			stripx ^= 1;

			// unordered removal from the buffer
			buffer[i][0] = buffer[buffer_size - 1][0];
			buffer[i][1] = buffer[buffer_size - 1][1];
			buffer[i][2] = buffer[buffer_size - 1][2];
			buffer_size--;

			// update vertex valences for strip start heuristic
			valence[a]--;
			valence[b]--;
			valence[c]--;
		}
		else
		{
			// in some cases we need to perform a swap to pick a different outgoing triangle edge
			// for [a b c], the default strip edge is [b c], but we might want to use [a c]
			int swap = findStripNext(buffer, buffer_size, strip[stripx ? 0 : 2], strip[stripx ? 2 : 0]);

			if (swap >= 0)
			{
				// reorganize strip to insert a degenerate triangle
				assert(destination[strip_size - 3] == strip[0]);
				assert(destination[strip_size - 2] == strip[1]);
				assert(destination[strip_size - 1] == strip[2]);

				// [a b c] => [a b a c]
				destination[strip_size - 1] = strip[0];
				destination[strip_size++] = strip[2];

				// next
				assert((swap & 3) < 3);

				unsigned int i = swap >> 2;
				unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
				unsigned int v = buffer[i][swap & 3];

				// emit the next vertex in the strip
				destination[strip_size++] = v;

				// next strip has same winding
				strip[1] = strip[2];
				strip[2] = v;

				// unordered removal from the buffer
				buffer[i][0] = buffer[buffer_size - 1][0];
				buffer[i][1] = buffer[buffer_size - 1][1];
				buffer[i][2] = buffer[buffer_size - 1][2];
				buffer_size--;

				// update vertex valences for strip start heuristic
				valence[a]--;
				valence[b]--;
				valence[c]--;
			}
			else
			{
				// if we didn't find anything, we need to find the next new triangle
				// we use a heuristic to maximize the strip length
				unsigned int i = findStripFirst(buffer, buffer_size, &valence[0]);
				unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];

				// unordered removal from the buffer
				buffer[i][0] = buffer[buffer_size - 1][0];
				buffer[i][1] = buffer[buffer_size - 1][1];
				buffer[i][2] = buffer[buffer_size - 1][2];
				buffer_size--;

				// we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration
				int ea = findStripNext(buffer, buffer_size, c, b);
				int eb = findStripNext(buffer, buffer_size, a, c);
				int ec = findStripNext(buffer, buffer_size, b, a);

				if (ea >= 0)
				{
					// keep abc
				}
				else if (eb >= 0)
				{
					// abc -> bca
					unsigned int t = a;
					a = b, b = c, c = t;
				}
				else if (ec >= 0)
				{
					// abc -> cab
					unsigned int t = c;
					c = b, b = a, a = t;
				}

				// emit the new strip; we use restart indices
				if (strip_size)
					destination[strip_size++] = ~0u;

				destination[strip_size++] = a;
				destination[strip_size++] = b;
				destination[strip_size++] = c;

				// new strip always starts with the same edge winding
				strip[0] = a;
				strip[1] = b;
				strip[2] = c;
				stripx = 1;

				// update vertex valences for strip start heuristic
				valence[a]--;
				valence[b]--;
				valence[c]--;
			}
		}
	}

	return strip_size;
}

size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count)
{
	size_t offset = 0;
	size_t start = 0;

	for (size_t i = 0; i < index_count; ++i)
	{
		if (indices[i] == ~0u)
		{
			start = i + 1;
		}
		else if (i - start >= 2)
		{
			unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i];

			if ((i - start) % 2 == 1)
				std::swap(a, b);

			if (a != b && a != c && b != c)
			{
				destination[offset + 0] = a;
				destination[offset + 1] = b;
				destination[offset + 2] = c;
				offset += 3;
			}
		}
	}

	return offset;
}
<commit_msg>stripify: Perform strip swaps without rewrites<commit_after>#include "meshoptimizer.h"

#include <cassert>

#include <algorithm>
#include <vector>

static unsigned int findStripFirst(const unsigned int buffer[][3], unsigned int buffer_size, const unsigned int* valence)
{
	unsigned int index = 0;
	unsigned int iv = ~0u;

	for (unsigned int i = 0; i < buffer_size; ++i)
	{
		unsigned int v = std::min(valence[buffer[i][0]], std::min(valence[buffer[i][1]], valence[buffer[i][2]]));

		if (v < iv)
		{
			index = i;
			iv = v;
		}
	}

	return index;
}

static int findStripNext(const unsigned int buffer[][3], unsigned int buffer_size, unsigned int e0, unsigned int e1)
{
	for (unsigned int i = 0; i < buffer_size; ++i)
	{
		unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];

		if (e0 == a && e1 == b)
			return (i << 2) | 2;
		else if (e0 == b && e1 == c)
			return (i << 2) | 0;
		else if (e0 == c && e1 == a)
			return (i << 2) | 1;
	}

	return -1;
}

size_t meshopt_stripify(unsigned int* destination, const unsigned int* indices, size_t index_count, size_t vertex_count)
{
	const size_t buffer_capacity = 16;

	unsigned int buffer[buffer_capacity][3] = {};
	unsigned int buffer_size = 0;

	size_t index_offset = 0;

	unsigned int strip[3] = {};
	unsigned int stripx = 0;

	size_t strip_size = 0;

	std::vector<unsigned int> valence(vertex_count, 0);

	for (size_t i = 0; i < index_count; ++i)
	{
		unsigned int index = indices[i];
		assert(index < vertex_count);

		valence[index]++;
	}

	while (buffer_size > 0 || index_offset < index_count)
	{
		// fill triangle buffer
		while (buffer_size < buffer_capacity && index_offset < index_count)
		{
			buffer[buffer_size][0] = indices[index_offset + 0];
			buffer[buffer_size][1] = indices[index_offset + 1];
			buffer[buffer_size][2] = indices[index_offset + 2];

			buffer_size++;
			index_offset += 3;
		}

		assert(buffer_size > 0);

		// find next triangle
		// note that the order of last edge flips on every iteration
		int next = findStripNext(buffer, buffer_size, strip[1 + stripx], strip[2 - stripx]);

		if (next >= 0)
		{
			assert((next & 3) < 3);

			unsigned int i = next >> 2;
			unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];
			unsigned int v = buffer[i][next & 3];

			// unordered removal from the buffer
			buffer[i][0] = buffer[buffer_size - 1][0];
			buffer[i][1] = buffer[buffer_size - 1][1];
			buffer[i][2] = buffer[buffer_size - 1][2];
			buffer_size--;

			// update vertex valences for strip start heuristic
			valence[a]--;
			valence[b]--;
			valence[c]--;

			// in some cases we need to perform a swap to pick a different outgoing triangle edge
			// for [a b c], the default strip edge is [b c], but we might want to use [a c]
			int cont = findStripNext(buffer, buffer_size, stripx ? strip[2] : v, stripx ? v : strip[2]);
			int swap = findStripNext(buffer, buffer_size, stripx ? v : strip[1], stripx ? strip[1] : v);

			if (cont < 0 && swap >= 0)
			{
				// [a b c] => [a b a c]
				unsigned int sa = strip[1];

				destination[strip_size++] = sa;
				destination[strip_size++] = v;

				// next strip has same winding
				// ? a b => b a v
				strip[0] = strip[2];
				strip[1] = sa;
				strip[2] = v;
			}
			else
			{
				// emit the next vertex in the strip
				destination[strip_size++] = v;

				// next strip has flipped winding
				strip[0] = strip[1];
				strip[1] = strip[2];
				strip[2] = v;
				stripx ^= 1;
			}
		}
		else
		{
			// if we didn't find anything, we need to find the next new triangle
			// we use a heuristic to maximize the strip length
			unsigned int i = findStripFirst(buffer, buffer_size, &valence[0]);
			unsigned int a = buffer[i][0], b = buffer[i][1], c = buffer[i][2];

			// unordered removal from the buffer
			buffer[i][0] = buffer[buffer_size - 1][0];
			buffer[i][1] = buffer[buffer_size - 1][1];
			buffer[i][2] = buffer[buffer_size - 1][2];
			buffer_size--;

			// update vertex valences for strip start heuristic
			valence[a]--;
			valence[b]--;
			valence[c]--;

			// we need to pre-rotate the triangle so that we will find a match in the existing buffer on the next iteration
			int ea = findStripNext(buffer, buffer_size, c, b);
			int eb = findStripNext(buffer, buffer_size, a, c);
			int ec = findStripNext(buffer, buffer_size, b, a);

			if (ea >= 0)
			{
				// keep abc
			}
			else if (eb >= 0)
			{
				// abc -> bca
				unsigned int t = a;
				a = b, b = c, c = t;
			}
			else if (ec >= 0)
			{
				// abc -> cab
				unsigned int t = c;
				c = b, b = a, a = t;
			}

			// emit the new strip; we use restart indices
			if (strip_size)
				destination[strip_size++] = ~0u;

			destination[strip_size++] = a;
			destination[strip_size++] = b;
			destination[strip_size++] = c;

			// new strip always starts with the same edge winding
			strip[0] = a;
			strip[1] = b;
			strip[2] = c;
			stripx = 1;
		}
	}

	return strip_size;
}

size_t meshopt_unstripify(unsigned int* destination, const unsigned int* indices, size_t index_count)
{
	size_t offset = 0;
	size_t start = 0;

	for (size_t i = 0; i < index_count; ++i)
	{
		if (indices[i] == ~0u)
		{
			start = i + 1;
		}
		else if (i - start >= 2)
		{
			unsigned int a = indices[i - 2], b = indices[i - 1], c = indices[i];

			if ((i - start) % 2 == 1)
				std::swap(a, b);

			if (a != b && a != c && b != c)
			{
				destination[offset + 0] = a;
				destination[offset + 1] = b;
				destination[offset + 2] = c;
				offset += 3;
			}
		}
	}

	return offset;
}
<|endoftext|>