<commit_before>//===--- CSPropagate.cpp - Constraint Propagation -------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements the constraint propagation algorithm in the solver.
//
//===----------------------------------------------------------------------===//
#include "ConstraintGraph.h"
#include "ConstraintSystem.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/raw_ostream.h"

using namespace swift;
using namespace constraints;

bool isBindOverloadDisjunction(ConstraintSystem &CS, Constraint *disjunction) {
  assert(disjunction->getKind() == ConstraintKind::Disjunction &&
         "Expected disjunction constraint!");

  assert(!disjunction->getNestedConstraints().empty() &&
         "Unexpected empty disjunction!");

  auto *nested = disjunction->getNestedConstraints().front();
  return nested->getKind() == ConstraintKind::BindOverload;
}

// Find the disjunction of bind overload constraints related to this
// applicable function constraint, if it exists.
Constraint *
getBindOverloadDisjunction(ConstraintSystem &CS, Constraint *applicableFn) {
  assert(applicableFn->getKind() == ConstraintKind::ApplicableFunction
         && "Expected ApplicableFunction disjunction!");
  auto *tyvar = applicableFn->getSecondType()->getAs<TypeVariableType>();
  assert(tyvar && "Expected type variable!");

  Constraint *found = nullptr;
  for (auto *constraint : CS.getConstraintGraph()[tyvar].getConstraints()) {
    if (constraint->getKind() == ConstraintKind::Disjunction) {
      found = constraint;
      break;
    }
  }

  if (!found)
    return nullptr;

#if !defined(NDEBUG)
  for (auto *constraint : CS.getConstraintGraph()[tyvar].getConstraints()) {
    if (constraint == found)
      continue;

    assert(constraint->getKind() != ConstraintKind::Disjunction
           && "Type variable is involved in more than one disjunction!");
  }
#endif

  // Verify the disjunction consists of BindOverload constraints.
  assert(isBindOverloadDisjunction(CS, found));

  return found;
}

void ConstraintSystem::collectNeighboringBindOverloadDisjunctions(
    llvm::SetVector<Constraint *> &neighbors) {

  while (!ActiveConstraints.empty()) {
    auto *constraint = &ActiveConstraints.front();
    ActiveConstraints.pop_front();

    assert(constraint->isActive() && "Expected constraints to be active?");
    assert(!constraint->isDisabled() && "Unexpected disabled constraint!");

    if (constraint->getKind() == ConstraintKind::Disjunction) {
      if (isBindOverloadDisjunction(*this, constraint)) {
        neighbors.insert(constraint);
      }
    } else if (constraint->getKind() == ConstraintKind::ApplicableFunction) {
      if (auto *bindDisjunction =
              getBindOverloadDisjunction(*this, constraint)) {
        neighbors.insert(bindDisjunction);
      }
    }

    solverState->retireConstraint(constraint);
    CG.removeConstraint(constraint);
    constraint->setActive(false);
  }
}

// Simplify any active constraints, returning true on success, false
// on failure.
bool ConstraintSystem::simplifyForConstraintPropagation() {
  while (!ActiveConstraints.empty()) {
    auto *constraint = &ActiveConstraints.front();
    ActiveConstraints.pop_front();

    assert(constraint->isActive()
           && "Expected constraints to be active?");
    assert(!constraint->isDisabled() && "Unexpected disabled constraint!");

    bool failed = false;

    // Simplify this constraint.
    switch (simplifyConstraint(*constraint)) {
    case SolutionKind::Error:
      failed = true;
      LLVM_FALLTHROUGH;

    case SolutionKind::Solved:
      solverState->retireConstraint(constraint);
      CG.removeConstraint(constraint);
      break;

    case SolutionKind::Unsolved:
      InactiveConstraints.push_back(constraint);
      break;
    }

    constraint->setActive(false);

    if (failed)
      return false;
  }

  return true;
}

bool ConstraintSystem::areBindPairConsistent(Constraint *first,
                                             Constraint *second) {
  // Set up a scope that will be torn down when we're done testing
  // this constraint.
  ConstraintSystem::SolverScope scope(*this);

  if (TC.getLangOpts().DebugConstraintSolver) {
    auto &log = getASTContext().TypeCheckerDebug->getStream();
    log << "Testing constraints for consistency: ";
    first->print(log, &TC.Context.SourceMgr);
    log << "\nversus: ";
    second->print(log, &TC.Context.SourceMgr);
  }

  auto result = simplifyConstraint(*first);
  assert(result == ConstraintSystem::SolutionKind::Solved &&
         "Expected the first bind constraint to work!");

  solverState->retireConstraint(first);
  solverState->addGeneratedConstraint(first);

  result = simplifyConstraint(*second);
  assert(result == ConstraintSystem::SolutionKind::Solved &&
         "Expected the second bind constraint to work!");

  solverState->retireConstraint(second);
  solverState->addGeneratedConstraint(second);

  auto success = simplifyForConstraintPropagation();
  if (TC.getLangOpts().DebugConstraintSolver) {
    auto &log = getASTContext().TypeCheckerDebug->getStream();
    if (success)
      log << "Consistent!\n";
    else
      log << "Not consistent!\n";
  }

  return success;
}

// Test a bind overload constraint to see if it is consistent with the
// rest of the constraint system.
bool ConstraintSystem::isBindOverloadConsistent(
    Constraint *bindConstraint, llvm::SetVector<Constraint *> &workList) {

  llvm::SetVector<Constraint *> otherDisjunctions;

  {
    // Set up a scope that will be torn down when we're done testing
    // this constraint.
    ConstraintSystem::SolverScope scope(*this);

    assert(bindConstraint->getKind() == ConstraintKind::BindOverload &&
           "Expected a BindOverload constraint!");

    // Test this bind overload constraint, activating neighboring
    // constraints.
    auto result = simplifyConstraint(*bindConstraint);
    assert(result == ConstraintSystem::SolutionKind::Solved &&
           "Expected the bind constraint to work!");
    (void)result;

    solverState->retireConstraint(bindConstraint);
    solverState->addGeneratedConstraint(bindConstraint);

    collectNeighboringBindOverloadDisjunctions(otherDisjunctions);
  }

  // Test the our primary constraint against all of the members of
  // neighboring disjunctions. If this constraint fails with all
  // members of a neighboring disjunction, we'll disable it and queue
  // up these neighbors for further processing. If this constraint
  // works with any member of each of the disjunctions, we do not need
  // to test the remaining members.
  for (auto *disjunction : otherDisjunctions) {
    auto insertPt = InactiveConstraints.erase(disjunction);
    CG.removeConstraint(disjunction);

    bool foundConsistent = false;
    for (auto *nested : disjunction->getNestedConstraints()) {
      assert(nested->getKind() == ConstraintKind::BindOverload &&
             "Expected a BindOverload constraint");

      if (nested->isDisabled())
        continue;

      if (areBindPairConsistent(bindConstraint, nested)) {
        foundConsistent = true;
        break;
      }
    }

    CG.addConstraint(disjunction);
    InactiveConstraints.insert(insertPt, disjunction);

    // We failed to find a working pair between the bind overload
    // constraint we started with and the members of this
    // disjunction. We'll mark this bind overload as disabled and
    // queue up the neighboring disjunctions for re-processing.
    if (!foundConsistent) {
      if (TC.getLangOpts().DebugConstraintSolver) {
        auto &log = getASTContext().TypeCheckerDebug->getStream();
        log << "Disabling bind constraint: ";
        bindConstraint->print(log, &TC.Context.SourceMgr);
        log << "\n";
      }

      bindConstraint->setDisabled();
      for (auto *disjunction : otherDisjunctions)
        workList.insert(disjunction);

      return false;
    }
  }

  return true;
}

void ConstraintSystem::reviseBindOverloadDisjunction(
    Constraint *disjunction, llvm::SetVector<Constraint *> &workList,
    bool *foundConsistent) {
  assert(disjunction->getKind() == ConstraintKind::Disjunction &&
         "Expected disjunction constraint on work list!");

  // Set up a scope that will be torn down when we're done testing
  // this constraint.
  ConstraintSystem::SolverScope scope(*this);

  // Temporarily remove the disjunction from the constraint system and
  // constraint graph.
  auto insertPt = InactiveConstraints.erase(disjunction);
  CG.removeConstraint(disjunction);

  *foundConsistent = false;
  for (auto *bindConstraint : disjunction->getNestedConstraints()) {
    assert(bindConstraint->getKind() == ConstraintKind::BindOverload
           && "Expected a BindOverload constraint!");

    if (bindConstraint->isDisabled())
      continue;

    if (isBindOverloadConsistent(bindConstraint, workList))
      *foundConsistent = true;
  }

  CG.addConstraint(disjunction);
  InactiveConstraints.insert(insertPt, disjunction);
}

// Do a form of constraint propagation consisting of examining
// applicable function constraints and their associated disjunction of
// bind overload constraints. Disable bind overload constraints in the
// disjunction if they are inconsistent with the rest of the
// constraint system. By doing this we can eliminate a lot of the work
// that we'll perform in the constraint solver.
bool ConstraintSystem::propagateConstraints() {
  assert(!failedConstraint && "Unexpected failed constraint!");
  assert(getActiveConstraints().empty() && "Expected no active constraints!");

  if (TC.getLangOpts().DebugConstraintSolver) {
    auto &log = getASTContext().TypeCheckerDebug->getStream();
    log << "---Propagating constraints---\n";
  }

  // Queue an initial list of bind overload disjunction constraints to
  // process.
  llvm::SetVector<Constraint *> workList;
  for (auto &constraint : getConstraints())
    if (constraint.getKind() == ConstraintKind::Disjunction)
      if (isBindOverloadDisjunction(*this, &constraint))
        workList.insert(&constraint);

  // Process each disjunction in the work list. If we modify the
  // active constraints in the disjunction as a result of processing
  // it, we'll add it's neighbors back to the worklist for
  // reprocessing.
  while (!workList.empty()) {
    auto *disjunction = workList.pop_back_val();

    bool foundConsistent;
    reviseBindOverloadDisjunction(disjunction, workList, &foundConsistent);
    if (!foundConsistent)
      return true;
  }

  return false;
}
<commit_msg>[Constraint solver] Remove unused function argument.<commit_after>//===--- CSPropagate.cpp - Constraint Propagation -------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file implements the constraint propagation algorithm in the solver.
//
//===----------------------------------------------------------------------===//
#include "ConstraintGraph.h"
#include "ConstraintSystem.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/raw_ostream.h"

using namespace swift;
using namespace constraints;

bool isBindOverloadDisjunction(Constraint *disjunction) {
  assert(disjunction->getKind() == ConstraintKind::Disjunction &&
         "Expected disjunction constraint!");

  assert(!disjunction->getNestedConstraints().empty() &&
         "Unexpected empty disjunction!");

  auto *nested = disjunction->getNestedConstraints().front();
  return nested->getKind() == ConstraintKind::BindOverload;
}

// Find the disjunction of bind overload constraints related to this
// applicable function constraint, if it exists.
Constraint *
getBindOverloadDisjunction(ConstraintSystem &CS, Constraint *applicableFn) {
  assert(applicableFn->getKind() == ConstraintKind::ApplicableFunction
         && "Expected ApplicableFunction disjunction!");
  auto *tyvar = applicableFn->getSecondType()->getAs<TypeVariableType>();
  assert(tyvar && "Expected type variable!");

  Constraint *found = nullptr;
  for (auto *constraint : CS.getConstraintGraph()[tyvar].getConstraints()) {
    if (constraint->getKind() == ConstraintKind::Disjunction) {
      found = constraint;
      break;
    }
  }

  if (!found)
    return nullptr;

#if !defined(NDEBUG)
  for (auto *constraint : CS.getConstraintGraph()[tyvar].getConstraints()) {
    if (constraint == found)
      continue;

    assert(constraint->getKind() != ConstraintKind::Disjunction
           && "Type variable is involved in more than one disjunction!");
  }
#endif

  // Verify the disjunction consists of BindOverload constraints.
  assert(isBindOverloadDisjunction(found));

  return found;
}

void ConstraintSystem::collectNeighboringBindOverloadDisjunctions(
    llvm::SetVector<Constraint *> &neighbors) {

  while (!ActiveConstraints.empty()) {
    auto *constraint = &ActiveConstraints.front();
    ActiveConstraints.pop_front();

    assert(constraint->isActive() && "Expected constraints to be active?");
    assert(!constraint->isDisabled() && "Unexpected disabled constraint!");

    if (constraint->getKind() == ConstraintKind::Disjunction) {
      if (isBindOverloadDisjunction(constraint)) {
        neighbors.insert(constraint);
      }
    } else if (constraint->getKind() == ConstraintKind::ApplicableFunction) {
      if (auto *bindDisjunction =
              getBindOverloadDisjunction(*this, constraint)) {
        neighbors.insert(bindDisjunction);
      }
    }

    solverState->retireConstraint(constraint);
    CG.removeConstraint(constraint);
    constraint->setActive(false);
  }
}

// Simplify any active constraints, returning true on success, false
// on failure.
bool ConstraintSystem::simplifyForConstraintPropagation() {
  while (!ActiveConstraints.empty()) {
    auto *constraint = &ActiveConstraints.front();
    ActiveConstraints.pop_front();

    assert(constraint->isActive()
           && "Expected constraints to be active?");
    assert(!constraint->isDisabled() && "Unexpected disabled constraint!");

    bool failed = false;

    // Simplify this constraint.
    switch (simplifyConstraint(*constraint)) {
    case SolutionKind::Error:
      failed = true;
      LLVM_FALLTHROUGH;

    case SolutionKind::Solved:
      solverState->retireConstraint(constraint);
      CG.removeConstraint(constraint);
      break;

    case SolutionKind::Unsolved:
      InactiveConstraints.push_back(constraint);
      break;
    }

    constraint->setActive(false);

    if (failed)
      return false;
  }

  return true;
}

bool ConstraintSystem::areBindPairConsistent(Constraint *first,
                                             Constraint *second) {
  // Set up a scope that will be torn down when we're done testing
  // this constraint.
  ConstraintSystem::SolverScope scope(*this);

  if (TC.getLangOpts().DebugConstraintSolver) {
    auto &log = getASTContext().TypeCheckerDebug->getStream();
    log << "Testing constraints for consistency: ";
    first->print(log, &TC.Context.SourceMgr);
    log << "\nversus: ";
    second->print(log, &TC.Context.SourceMgr);
  }

  auto result = simplifyConstraint(*first);
  assert(result == ConstraintSystem::SolutionKind::Solved &&
         "Expected the first bind constraint to work!");

  solverState->retireConstraint(first);
  solverState->addGeneratedConstraint(first);

  result = simplifyConstraint(*second);
  assert(result == ConstraintSystem::SolutionKind::Solved &&
         "Expected the second bind constraint to work!");

  solverState->retireConstraint(second);
  solverState->addGeneratedConstraint(second);

  auto success = simplifyForConstraintPropagation();
  if (TC.getLangOpts().DebugConstraintSolver) {
    auto &log = getASTContext().TypeCheckerDebug->getStream();
    if (success)
      log << "Consistent!\n";
    else
      log << "Not consistent!\n";
  }

  return success;
}

// Test a bind overload constraint to see if it is consistent with the
// rest of the constraint system.
bool ConstraintSystem::isBindOverloadConsistent(
    Constraint *bindConstraint, llvm::SetVector<Constraint *> &workList) {

  llvm::SetVector<Constraint *> otherDisjunctions;

  {
    // Set up a scope that will be torn down when we're done testing
    // this constraint.
    ConstraintSystem::SolverScope scope(*this);

    assert(bindConstraint->getKind() == ConstraintKind::BindOverload &&
           "Expected a BindOverload constraint!");

    // Test this bind overload constraint, activating neighboring
    // constraints.
    auto result = simplifyConstraint(*bindConstraint);
    assert(result == ConstraintSystem::SolutionKind::Solved &&
           "Expected the bind constraint to work!");
    (void)result;

    solverState->retireConstraint(bindConstraint);
    solverState->addGeneratedConstraint(bindConstraint);

    collectNeighboringBindOverloadDisjunctions(otherDisjunctions);
  }

  // Test the our primary constraint against all of the members of
  // neighboring disjunctions. If this constraint fails with all
  // members of a neighboring disjunction, we'll disable it and queue
  // up these neighbors for further processing. If this constraint
  // works with any member of each of the disjunctions, we do not need
  // to test the remaining members.
  for (auto *disjunction : otherDisjunctions) {
    auto insertPt = InactiveConstraints.erase(disjunction);
    CG.removeConstraint(disjunction);

    bool foundConsistent = false;
    for (auto *nested : disjunction->getNestedConstraints()) {
      assert(nested->getKind() == ConstraintKind::BindOverload &&
             "Expected a BindOverload constraint");

      if (nested->isDisabled())
        continue;

      if (areBindPairConsistent(bindConstraint, nested)) {
        foundConsistent = true;
        break;
      }
    }

    CG.addConstraint(disjunction);
    InactiveConstraints.insert(insertPt, disjunction);

    // We failed to find a working pair between the bind overload
    // constraint we started with and the members of this
    // disjunction. We'll mark this bind overload as disabled and
    // queue up the neighboring disjunctions for re-processing.
    if (!foundConsistent) {
      if (TC.getLangOpts().DebugConstraintSolver) {
        auto &log = getASTContext().TypeCheckerDebug->getStream();
        log << "Disabling bind constraint: ";
        bindConstraint->print(log, &TC.Context.SourceMgr);
        log << "\n";
      }

      bindConstraint->setDisabled();
      for (auto *disjunction : otherDisjunctions)
        workList.insert(disjunction);

      return false;
    }
  }

  return true;
}

void ConstraintSystem::reviseBindOverloadDisjunction(
    Constraint *disjunction, llvm::SetVector<Constraint *> &workList,
    bool *foundConsistent) {
  assert(disjunction->getKind() == ConstraintKind::Disjunction &&
         "Expected disjunction constraint on work list!");

  // Set up a scope that will be torn down when we're done testing
  // this constraint.
  ConstraintSystem::SolverScope scope(*this);

  // Temporarily remove the disjunction from the constraint system and
  // constraint graph.
  auto insertPt = InactiveConstraints.erase(disjunction);
  CG.removeConstraint(disjunction);

  *foundConsistent = false;
  for (auto *bindConstraint : disjunction->getNestedConstraints()) {
    assert(bindConstraint->getKind() == ConstraintKind::BindOverload
           && "Expected a BindOverload constraint!");

    if (bindConstraint->isDisabled())
      continue;

    if (isBindOverloadConsistent(bindConstraint, workList))
      *foundConsistent = true;
  }

  CG.addConstraint(disjunction);
  InactiveConstraints.insert(insertPt, disjunction);
}

// Do a form of constraint propagation consisting of examining
// applicable function constraints and their associated disjunction of
// bind overload constraints. Disable bind overload constraints in the
// disjunction if they are inconsistent with the rest of the
// constraint system. By doing this we can eliminate a lot of the work
// that we'll perform in the constraint solver.
bool ConstraintSystem::propagateConstraints() {
  assert(!failedConstraint && "Unexpected failed constraint!");
  assert(getActiveConstraints().empty() && "Expected no active constraints!");

  if (TC.getLangOpts().DebugConstraintSolver) {
    auto &log = getASTContext().TypeCheckerDebug->getStream();
    log << "---Propagating constraints---\n";
  }

  // Queue an initial list of bind overload disjunction constraints to
  // process.
  llvm::SetVector<Constraint *> workList;
  for (auto &constraint : getConstraints())
    if (constraint.getKind() == ConstraintKind::Disjunction)
      if (isBindOverloadDisjunction(&constraint))
        workList.insert(&constraint);

  // Process each disjunction in the work list. If we modify the
  // active constraints in the disjunction as a result of processing
  // it, we'll add it's neighbors back to the worklist for
  // reprocessing.
  while (!workList.empty()) {
    auto *disjunction = workList.pop_back_val();

    bool foundConsistent;
    reviseBindOverloadDisjunction(disjunction, workList, &foundConsistent);
    if (!foundConsistent)
      return true;
  }

  return false;
}
<|endoftext|>


<commit_before>/****************************************************************************
 *
 * Copyright 2008 Tungsten Graphics, Inc.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 ****************************************************************************/

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <zlib.h>

#include "log.hpp"

#ifdef WIN32
#ifndef PATH_MAX
#define PATH_MAX _MAX_PATH
#endif
#ifndef snprintf
#define snprintf _snprintf
#endif
#ifndef vsnprintf
#define vsnprintf _vsnprintf
#endif
#endif

namespace Log {

static gzFile g_gzFile = NULL;
static char g_szFileName[PATH_MAX];

static void _Close(void) {
    if(g_gzFile != NULL) {
        gzclose(g_gzFile);
        g_gzFile = NULL;
    }
}

static void _Open(const char *szName, const char *szExtension) {
    _Close();

    static unsigned dwCounter = 0;

    for(;;) {
        FILE *file;

        if(dwCounter)
            snprintf(g_szFileName, PATH_MAX, "%s.%u.%s.gz", szName, dwCounter, szExtension);
        else
            snprintf(g_szFileName, PATH_MAX, "%s.%s.gz", szName, szExtension);

        file = fopen(g_szFileName, "rb");
        if(file == NULL)
            break;

        fclose(file);

        ++dwCounter;
    }

    g_gzFile = gzopen(g_szFileName, "wb");
}

static inline void _ReOpen(void) {
    /* XXX */
}

static inline void Write(const char *sBuffer, size_t dwBytesToWrite) {
    if(g_gzFile == NULL)
        return;

    gzwrite(g_gzFile, sBuffer, dwBytesToWrite);
}

static inline void Write(const char *szText) {
    Write(szText, strlen(szText));
}

static inline void
Escape(const char *s) {
    /* FIXME */
    Write(s);
}

static inline void
Indent(unsigned level) {
    for(unsigned i = 0; i < level; ++i)
        Write("\t");
}

static inline void
NewLine(void) {
    Write("\r\n");
}

static inline void
Tag(const char *name) {
    Write("<");
    Write(name);
    Write("/>");
}

static inline void
BeginTag(const char *name) {
    Write("<");
    Write(name);
    Write(">");
}

static inline void
BeginTag(const char *name,
         const char *attr1, const char *value1) {
    Write("<");
    Write(name);
    Write(" ");
    Write(attr1);
    Write("=\"");
    Escape(value1);
    Write("\">");
}

static inline void
BeginTag(const char *name,
         const char *attr1, const char *value1,
         const char *attr2, const char *value2) {
    Write("<");
    Write(name);
    Write(" ");
    Write(attr1);
    Write("=\"");
    Escape(value1);
    Write("\" ");
    Write(attr2);
    Write("=\"");
    Escape(value2);
    Write("\">");
}

static inline void
BeginTag(const char *name,
              const char *attr1, const char *value1,
              const char *attr2, const char *value2,
              const char *attr3, const char *value3) {
    Write("<");
    Write(name);
    Write(" ");
    Write(attr1);
    Write("=\"");
    Escape(value1);
    Write("\" ");
    Write(attr2);
    Write("=\"");
    Escape(value2);
    Write("\" ");
    Write(attr3);
    Write("=\"");
    Escape(value3);
    Write("\">");
}

static inline void
EndTag(const char *name) {
    Write("</");
    Write(name);
    Write(">");
}

void Open(const char *name) {
    _Open(name, "xml");
    Write("<?xml version='1.0' encoding='UTF-8'?>");
    NewLine();
    Write("<?xml-stylesheet type='text/xsl' href='apitrace.xsl'?>");
    NewLine();
    BeginTag("trace");
    NewLine();
}

void ReOpen(void) {
    _ReOpen();
}

void Close(void) {
    EndTag("trace");
    NewLine();
    _Close();
}

void Text(const char *text) {
    Escape(text);
}

static void TextChar(char c) {
    char szText[2];
    szText[0] = c;
    szText[1] = 0;
    Text(szText);
}

void TextF(const char *format, ...) {
    char szBuffer[4196];
    va_list ap;
    va_start(ap, format);
    vsnprintf(szBuffer, sizeof(szBuffer), format, ap);
    va_end(ap);
    Text(szBuffer);
}

void BeginCall(const char *function) {
    Indent(1);
    BeginTag("call", "name", function);
    NewLine();
}

void EndCall(void) {
    Indent(1);
    EndTag("call");
    NewLine();
    gzflush(g_gzFile, Z_SYNC_FLUSH);
}

void BeginArg(const char *type, const char *name) {
    Indent(2);
    BeginTag("arg", "type", type, "name", name);
}

void EndArg(void) {
    EndTag("arg");
    NewLine();
}

void BeginReturn(const char *type) {
    Indent(2);
    BeginTag("ret", "type", type);
}

void EndReturn(void) {
    EndTag("ret");
    NewLine();
}

void BeginElement(const char *type, const char *name) {
    BeginTag("elem", "type", type, "name", name);
}

void BeginElement(const char *type) {
    BeginTag("elem", "type", type);
}

void EndElement(void) {
    EndTag("elem");
}

void BeginReference(const char *type, const void *addr) {
    char saddr[256];
    snprintf(saddr, sizeof(saddr), "%p", addr);
    BeginTag("ref", "type", type, "addr", saddr);
}

void EndReference(void) {
    EndTag("ref");
}

void DumpString(const char *str) {
    const unsigned char *p = (const unsigned char *)str;
    Log::Text("\"");
    unsigned char c;
    while((c = *p++) != 0) {
        if(c == '\"')
            Text("\\\"");
        else if(c == '\\')
            Text("\\\\");
        else if(c >= 0x20 && c <= 0x7e)
            TextChar(c);
        else if(c == '\t')
            Text("\\t");
        else if(c == '\r')
            Text("\\r");
        else if(c == '\n')
            Text("\\n");
        else {
            unsigned char octal0 = c & 0x7;
            unsigned char octal1 = (c >> 3) & 0x7;
            unsigned char octal2 = (c >> 3) & 0x7;
            if(octal2)
                TextF("\\%u%u%u", octal2, octal1, octal0);
            else if(octal1)
                TextF("\\%u%u", octal1, octal0);
            else
                TextF("\\%u", octal0);
        }
    }
    Log::Text("\"");
}

} /* namespace Log */
<commit_msg>Use critical sections.<commit_after>/****************************************************************************
 *
 * Copyright 2008-2009 VMware, Inc.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 ****************************************************************************/

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <windows.h>

#include <zlib.h>

#include "log.hpp"

#ifdef WIN32
#ifndef PATH_MAX
#define PATH_MAX _MAX_PATH
#endif
#ifndef snprintf
#define snprintf _snprintf
#endif
#ifndef vsnprintf
#define vsnprintf _vsnprintf
#endif
#endif

namespace Log {

static gzFile g_gzFile = NULL;
static char g_szFileName[PATH_MAX];
static CRITICAL_SECTION CriticalSection;

static void _Close(void) {
    if(g_gzFile != NULL) {
        gzclose(g_gzFile);
        g_gzFile = NULL;
        DeleteCriticalSection(&CriticalSection);
    }
}

static void _Open(const char *szName, const char *szExtension) {
    _Close();

    static unsigned dwCounter = 0;

    for(;;) {
        FILE *file;

        if(dwCounter)
            snprintf(g_szFileName, PATH_MAX, "%s.%u.%s.gz", szName, dwCounter, szExtension);
        else
            snprintf(g_szFileName, PATH_MAX, "%s.%s.gz", szName, szExtension);

        file = fopen(g_szFileName, "rb");
        if(file == NULL)
            break;

        fclose(file);

        ++dwCounter;
    }

    g_gzFile = gzopen(g_szFileName, "wb");
    InitializeCriticalSection(&CriticalSection);
}

static inline void _ReOpen(void) {
    /* XXX */
}

static inline void Write(const char *sBuffer, size_t dwBytesToWrite) {
    if(g_gzFile == NULL)
        return;

    gzwrite(g_gzFile, sBuffer, dwBytesToWrite);
}

static inline void Write(const char *szText) {
    Write(szText, strlen(szText));
}

static inline void
Escape(const char *s) {
    /* FIXME */
    Write(s);
}

static inline void
Indent(unsigned level) {
    for(unsigned i = 0; i < level; ++i)
        Write("\t");
}

static inline void
NewLine(void) {
    Write("\r\n");
}

static inline void
Tag(const char *name) {
    Write("<");
    Write(name);
    Write("/>");
}

static inline void
BeginTag(const char *name) {
    Write("<");
    Write(name);
    Write(">");
}

static inline void
BeginTag(const char *name,
         const char *attr1, const char *value1) {
    Write("<");
    Write(name);
    Write(" ");
    Write(attr1);
    Write("=\"");
    Escape(value1);
    Write("\">");
}

static inline void
BeginTag(const char *name,
         const char *attr1, const char *value1,
         const char *attr2, const char *value2) {
    Write("<");
    Write(name);
    Write(" ");
    Write(attr1);
    Write("=\"");
    Escape(value1);
    Write("\" ");
    Write(attr2);
    Write("=\"");
    Escape(value2);
    Write("\">");
}

static inline void
BeginTag(const char *name,
              const char *attr1, const char *value1,
              const char *attr2, const char *value2,
              const char *attr3, const char *value3) {
    Write("<");
    Write(name);
    Write(" ");
    Write(attr1);
    Write("=\"");
    Escape(value1);
    Write("\" ");
    Write(attr2);
    Write("=\"");
    Escape(value2);
    Write("\" ");
    Write(attr3);
    Write("=\"");
    Escape(value3);
    Write("\">");
}

static inline void
EndTag(const char *name) {
    Write("</");
    Write(name);
    Write(">");
}

void Open(const char *name) {
    _Open(name, "xml");
    Write("<?xml version='1.0' encoding='UTF-8'?>");
    NewLine();
    Write("<?xml-stylesheet type='text/xsl' href='apitrace.xsl'?>");
    NewLine();
    BeginTag("trace");
    NewLine();
}

void ReOpen(void) {
    _ReOpen();
}

void Close(void) {
    EndTag("trace");
    NewLine();
    _Close();
}

void Text(const char *text) {
    Escape(text);
}

static void TextChar(char c) {
    char szText[2];
    szText[0] = c;
    szText[1] = 0;
    Text(szText);
}

void TextF(const char *format, ...) {
    char szBuffer[4196];
    va_list ap;
    va_start(ap, format);
    vsnprintf(szBuffer, sizeof(szBuffer), format, ap);
    va_end(ap);
    Text(szBuffer);
}

void BeginCall(const char *function) {
    EnterCriticalSection(&CriticalSection);
    Indent(1);
    BeginTag("call", "name", function);
    NewLine();
}

void EndCall(void) {
    Indent(1);
    EndTag("call");
    NewLine();
    gzflush(g_gzFile, Z_SYNC_FLUSH);
    LeaveCriticalSection(&CriticalSection);
}

void BeginArg(const char *type, const char *name) {
    Indent(2);
    BeginTag("arg", "type", type, "name", name);
}

void EndArg(void) {
    EndTag("arg");
    NewLine();
}

void BeginReturn(const char *type) {
    Indent(2);
    BeginTag("ret", "type", type);
}

void EndReturn(void) {
    EndTag("ret");
    NewLine();
}

void BeginElement(const char *type, const char *name) {
    BeginTag("elem", "type", type, "name", name);
}

void BeginElement(const char *type) {
    BeginTag("elem", "type", type);
}

void EndElement(void) {
    EndTag("elem");
}

void BeginReference(const char *type, const void *addr) {
    char saddr[256];
    snprintf(saddr, sizeof(saddr), "%p", addr);
    BeginTag("ref", "type", type, "addr", saddr);
}

void EndReference(void) {
    EndTag("ref");
}

void DumpString(const char *str) {
    const unsigned char *p = (const unsigned char *)str;
    Log::Text("\"");
    unsigned char c;
    while((c = *p++) != 0) {
        if(c == '\"')
            Text("\\\"");
        else if(c == '\\')
            Text("\\\\");
        else if(c >= 0x20 && c <= 0x7e)
            TextChar(c);
        else if(c == '\t')
            Text("\\t");
        else if(c == '\r')
            Text("\\r");
        else if(c == '\n')
            Text("\\n");
        else {
            unsigned char octal0 = c & 0x7;
            unsigned char octal1 = (c >> 3) & 0x7;
            unsigned char octal2 = (c >> 3) & 0x7;
            if(octal2)
                TextF("\\%u%u%u", octal2, octal1, octal0);
            else if(octal1)
                TextF("\\%u%u", octal1, octal0);
            else
                TextF("\\%u", octal0);
        }
    }
    Log::Text("\"");
}

} /* namespace Log */
<|endoftext|>


<commit_before><commit_msg>Fix display of Unicode strings.<commit_after><|endoftext|>


<commit_before>#pragma once
//=====================================================================//
/*!	@file
	@brief	１０キーボード @n
			タッチ操作で数字を入力する、ソフトキーボード
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2022 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=====================================================================//
#include "gui/widget.hpp"

namespace gui {

	namespace key_10_base {  // キーの位置、キーコードを保持する構造体

		struct location_t {
			vtx::srect	rect;
			char		code;  // normal-code
			constexpr location_t(const vtx::srect& r, char c) noexcept :
				rect(r), code(c)
			{ }
		};

	}

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	10 ソフトキー・クラス
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	struct key_10 : public widget {

		typedef key_10 value_type;

		typedef std::function<void(char)> SELECT_FUNC_TYPE;

	private:

	public:
		static constexpr int16_t BOARD_WIDTH  = 100;  ///< ボード幅
		static constexpr int16_t BOARD_HEIGHT = 100;  ///< ボード高さ

	private:

		static constexpr key_10_base::location_t key_locations_[] = {
			{ { 0, 0, 0, 0 }, '9' }
		};

		struct key_t {
			bool	level;
			bool	positive;
			bool	negative;
			bool	draw;
			key_t() noexcept : level(false), positive(false), negative(true), draw(true) { }
		};
		key_t	key_[10];

		SELECT_FUNC_TYPE	select_func_;
		char	code_;

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
			@param[in]	loc		ロケーション
		*/
		//-----------------------------------------------------------------//
		key_10(const vtx::srect& loc = vtx::srect(0), const char* str = nullptr) noexcept :
			widget(loc, str), key_{ },
			select_func_(), code_(0xff)
		{
			if(at_location().size.x <= 0) {
				at_location().size.x = BOARD_WIDTH;
			}
			if(at_location().size.y <= 0) {
				at_location().size.y = BOARD_HEIGHT;
			}
			insert_widget(this);
		}

		key_10(const key_10& th) = delete;
		key_10& operator = (const key_10& th) = delete;

		//-----------------------------------------------------------------//
		/*!
			@brief	デストラクタ
		*/
		//-----------------------------------------------------------------//
		virtual ~key_10() noexcept { remove_widget(this); }

		//-----------------------------------------------------------------//
		/*!
			@brief	型整数を取得
			@return 型整数
		*/
		//-----------------------------------------------------------------//
		const char* get_name() const noexcept override { return "Key10"; }

		//-----------------------------------------------------------------//
		/*!
			@brief	ID を取得
			@return ID
		*/
		//-----------------------------------------------------------------//
		ID get_id() const noexcept override { return ID::KEY_10; }

		//-----------------------------------------------------------------//
		/*!
			@brief	初期化
		*/
		//-----------------------------------------------------------------//
		void init() noexcept override { }

		//-----------------------------------------------------------------//
		/*!
			@brief	タッチ判定を更新
			@param[in]	pos		判定位置
			@param[in]	num		タッチ数
		*/
		//-----------------------------------------------------------------//
		void update_touch(const vtx::spos& pos, uint16_t num) noexcept override
		{
			update_touch_def(pos, num);

			auto loc = vtx::srect(get_final_position(), widget::get_location().size);
			uint32_t i = 0;
			for(auto& k : key_) {
				vtx::srect r(loc.org + key_locations_[i].rect.org, key_locations_[i].rect.size);
				auto level = false;
				if(r.is_focus(pos)) {
					level = num > 0;
				}
				k.positive = ( level && !k.level);
				k.negative = (!level &&  k.level);
				k.level = level;
				if(k.positive) {
#if 0
					auto code = key_locations_[i].code;
						if(ctrl_) {
							if(code >= 'a') {
								code_ = code - 0x60;
							} else {
								code_ = code;
							}
						} else if(shift_) {
							code_ = key_locations_[i].shift;
						} else {
							code_ = key_locations_[i].code;
						}
#endif
					k.draw = true;
				}
				if(k.negative) {
					k.draw = true;
				}
				++i;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	選択推移
		*/
		//-----------------------------------------------------------------//
		void exec_select() noexcept override
		{
			if(code_ != 0xff) {
				if(select_func_) {
					select_func_(code_);
				}
				code_ = 0xff;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	許可・不許可
			@param[in]	ena		不許可の場合「false」
		*/
		//-----------------------------------------------------------------//
		void enable(bool ena = true) override
		{
			if(ena) {
				set_state(STATE::ENABLE);
			} else {
				set_state(STATE::DISABLE);
				reset_touch_state();
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	セレクト関数への参照
			@return	セレクト関数
		*/
		//-----------------------------------------------------------------//
		SELECT_FUNC_TYPE& at_select_func() noexcept { return select_func_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	描画
			@param[in] rdr	描画インスタンス
		*/
		//-----------------------------------------------------------------//
		template<class RDR>
		void draw(RDR& rdr) noexcept
		{
#if 0
			auto r = vtx::srect(get_final_position(), get_location().size);

			uint32_t i = 0;
			for(auto& k : key_) {
				uint8_t inten = 64;
				if(k.level) {
					inten = 192;
				}
				if(k.draw) {
					k.draw = false;
					graphics::share_color sc;
					sc.set_color(get_base_color().rgba8, inten);
					rdr.set_fore_color(sc);
					auto rr = key_locations_[i].rect;
					rr.org += r.org;
					if(k.level) {
						rr.org += 1;
						rr.size -= 2;
					}
					rdr.round_box(rr, 3);

					rdr.set_fore_color(get_font_color());
					char cha;
					if(shift_) {
						cha = key_locations_[i].shift;
					} else {
						cha = key_locations_[i].code;
					}

					vtx::spos sz(8, 16);
					rdr.draw_font(rr.org + (rr.size - sz) / 2, cha);
				}
				++i;
			}
#endif
		}
	};
}
<commit_msg>Update: key layout<commit_after>#pragma once
//=========================================================================//
/*!	@file
	@brief	１０キーボード @n
			タッチ操作で数字を入力する、ソフトキーボード
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2022 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=========================================================================//
#include "gui/widget.hpp"

namespace gui {

	namespace key_10_base {  // キーの位置、キーコードを保持する構造体

		struct location_t {
			vtx::srect	rect;
			char		code;  // normal-code
			constexpr location_t(const vtx::srect& r, char c) noexcept :
				rect(r), code(c)
			{ }
		};

	}

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief	10 ソフトキー・クラス
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	struct key_10 : public widget {

		typedef key_10 value_type;

		//=================================================================//
		/*!
			@brief	ボタンの配置スタイル型
		*/
		//=================================================================//
		enum class STYLE : uint8_t {
			W3_H4,	///< 横に３個、縦に４個の並び
			W5_H2,	///< 横に５個、縦に２個の並び
		};

		//=================================================================//
		/*!
			@brief	キーマップ型
		*/
		//=================================================================//
		enum class KEY_MAP : uint8_t {
			_9,
			_8,
			_7,
			_6,
			_5,
			_4,
			_3,
			_2,
			_1,
			_0,
			NONE
		};

		typedef std::function<void(char, KEY_MAP)> SELECT_FUNC_TYPE;

	private:
		static constexpr int16_t space = 10;  // 隙間

		static constexpr int16_t sz_x = 40;  // 通常幅

		static constexpr int16_t sz_y = 40;  // 通常高

		static constexpr int16_t g0_x = 0;
		static constexpr int16_t g1_x = g0_x + space * 1 + sz_x * 1;
		static constexpr int16_t g2_x = g0_x + space * 2 + sz_x * 2;
		static constexpr int16_t g3_x = g0_x + space * 3 + sz_x * 3;
		static constexpr int16_t g4_x = g0_x + space * 4 + sz_x * 4;

		static constexpr int16_t g0_y = 0;
		static constexpr int16_t g1_y = g0_y + space * 1 + sz_y * 1;
		static constexpr int16_t g2_y = g0_y + space * 2 + sz_y * 2;
		static constexpr int16_t g3_y = g0_y + space * 3 + sz_y * 3;

	public:

		//-----------------------------------------------------------------//
		/*!
			@brief	ボードサイズを取得
			@param[in]	style	ボード・スタイル
			@return ボードサイズ
		*/
		//-----------------------------------------------------------------//
		static constexpr vtx::spos get_board_size(STYLE style)
		{
			switch(style) {
			case STYLE::W3_H4:
				return vtx::spos(sz_x * 3 + space * 2, sz_y * 4 + space * 3);
			case STYLE::W5_H2:
				return vtx::spos(sz_x * 5 + space * 4, sz_x * 2 + space * 1);
			}
			return vtx::spos(0);
		}

	private:

		static constexpr key_10_base::location_t key_locations_[2][10] = {
			{  // W3_H4
				{ { g0_x, g0_y, sz_x, sz_y }, '9' },
				{ { g1_x, g0_y, sz_x, sz_y }, '8' },
				{ { g2_x, g0_y, sz_x, sz_y }, '7' },
				{ { g0_x, g1_y, sz_x, sz_y }, '6' },
				{ { g1_x, g1_y, sz_x, sz_y }, '5' },
				{ { g2_x, g1_y, sz_x, sz_y }, '4' },
				{ { g0_x, g2_y, sz_x, sz_y }, '3' },
				{ { g1_x, g2_y, sz_x, sz_y }, '2' },
				{ { g2_x, g2_y, sz_x, sz_y }, '1' },
				{ { g1_x, g3_y, sz_x, sz_y }, '0' }
			}, {  // W5_H2
				{ { g0_x, g0_y, sz_x, sz_y }, '9' },
				{ { g1_x, g0_y, sz_x, sz_y }, '8' },
				{ { g2_x, g0_y, sz_x, sz_y }, '7' },
				{ { g3_x, g0_y, sz_x, sz_y }, '6' },
				{ { g4_x, g0_y, sz_x, sz_y }, '5' },
				{ { g0_x, g1_y, sz_x, sz_y }, '4' },
				{ { g1_x, g1_y, sz_x, sz_y }, '3' },
				{ { g2_x, g1_y, sz_x, sz_y }, '2' },
				{ { g3_x, g1_y, sz_x, sz_y }, '1' },
				{ { g4_x, g1_y, sz_x, sz_y }, '0' }
			}
		};

		static const auto& get_key_locations_(STYLE style, uint32_t item)
		{
			return key_locations_[static_cast<uint32_t>(style)][item];
		}

		struct key_t {
			bool	level;
			bool	positive;
			bool	negative;
			bool	draw;
			key_t() noexcept : level(false), positive(false), negative(true), draw(true) { }
		};
		key_t	key_[10];

		SELECT_FUNC_TYPE	select_func_;
		char	code_;
		KEY_MAP	key_map_;
		STYLE	style_;

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
			@param[in]	loc		ロケーション
			@param[in]	style	ボタンの配置スタイル（標準は「縦型」）
		*/
		//-----------------------------------------------------------------//
		key_10(const vtx::srect& loc = vtx::srect(0), STYLE style = STYLE::W3_H4) noexcept :
			widget(loc, nullptr), key_{ },
			select_func_(), code_(0xff), key_map_(KEY_MAP::NONE), style_(style)
		{
			if(at_location().size.x <= 0) {
				at_location().size.x = get_board_size(style).x;
			}
			if(at_location().size.y <= 0) {
				at_location().size.y = get_board_size(style).y;
			}
			insert_widget(this);
		}

		key_10(const key_10& th) = delete;
		key_10& operator = (const key_10& th) = delete;

		//-----------------------------------------------------------------//
		/*!
			@brief	デストラクタ
		*/
		//-----------------------------------------------------------------//
		virtual ~key_10() noexcept { remove_widget(this); }

		//-----------------------------------------------------------------//
		/*!
			@brief	型整数を取得
			@return 型整数
		*/
		//-----------------------------------------------------------------//
		const char* get_name() const noexcept override { return "Key10"; }

		//-----------------------------------------------------------------//
		/*!
			@brief	ID を取得
			@return ID
		*/
		//-----------------------------------------------------------------//
		ID get_id() const noexcept override { return ID::KEY_10; }

		//-----------------------------------------------------------------//
		/*!
			@brief	ハイブリッド・タイプか検査
			@return ハイブリッドの場合「true」
		*/
		//-----------------------------------------------------------------//
		bool hybrid() const noexcept override { return true; }

		//-----------------------------------------------------------------//
		/*!
			@brief	初期化
		*/
		//-----------------------------------------------------------------//
		void init() noexcept override { }

		//-----------------------------------------------------------------//
		/*!
			@brief	タッチ判定を更新
			@param[in]	pos		判定位置
			@param[in]	num		タッチ数
		*/
		//-----------------------------------------------------------------//
		void update_touch(const vtx::spos& pos, uint16_t num) noexcept override
		{
			update_touch_def(pos, num);

			auto loc = vtx::srect(get_final_position(), widget::get_location().size);
			uint32_t i = 0;
			for(auto& k : key_) {
				auto key_loc = get_key_locations_(style_, i);
				vtx::srect r(loc.org + key_loc.rect.org, key_loc.rect.size);
				auto level = false;
				if(r.is_focus(pos)) {
					level = num > 0;
				}
				k.positive = ( level && !k.level);
				k.negative = (!level &&  k.level);
				k.level = level;
				if(k.positive) {
					code_ = key_loc.code;
					key_map_ = static_cast<KEY_MAP>(i);
					k.draw = true;
				}
				if(k.negative) {
					k.draw = true;
				}
				++i;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	選択推移
		*/
		//-----------------------------------------------------------------//
		void exec_select() noexcept override
		{
			if(code_ != 0xff) {
				if(select_func_) {
					select_func_(code_, key_map_);
				}
				code_ = 0xff;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	許可・不許可
			@param[in]	ena		不許可の場合「false」
		*/
		//-----------------------------------------------------------------//
		void enable(bool ena = true) override
		{
			if(ena) {
				set_state(STATE::ENABLE);
				request_redraw();
			} else {
				set_state(STATE::DISABLE);
				reset_touch_state();
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	セレクト関数への参照
			@return	セレクト関数
		*/
		//-----------------------------------------------------------------//
		SELECT_FUNC_TYPE& at_select_func() noexcept { return select_func_; }

		//-----------------------------------------------------------------//
		/*!
			@brief	全体再描画をリクエスト
		*/
		//-----------------------------------------------------------------//
		void request_redraw() noexcept
		{
			for(auto& k : key_) {
				k.draw = true;
			}
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	描画
			@param[in] rdr	描画インスタンス
		*/
		//-----------------------------------------------------------------//
		template<class RDR>
		void draw(RDR& rdr) noexcept
		{
			auto r = vtx::srect(get_final_position(), get_location().size);

			uint32_t i = 0;
			for(auto& k : key_) {
				auto key_loc = get_key_locations_(style_, i);
				uint8_t inten = 64;
				if(k.level) {
					inten = 192;
				}
				if(k.draw) {
					k.draw = false;
					graphics::share_color sc;
					sc.set_color(get_base_color().rgba8, inten);
					rdr.set_fore_color(sc);
					auto rr = key_loc.rect;
					rr.org += r.org;
					if(k.level) {
						rr.org += 1;
						rr.size -= 2;
					}
					auto rad = rr.size.x / 2;
					rdr.fill_circle(rr.org + rr.size / 2, rad);

					rdr.set_fore_color(get_font_color());
					auto cha = key_loc.code;
					vtx::spos sz(8, 16);
					rdr.draw_font(rr.org + (rr.size - sz) / 2, cha);
				}
				++i;
			}
		}
	};
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014-2015 Patrick Wieschollek
 *
 * 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 "LbfgsbSolver.h"
#include "linesearch/WolfeRule.h"
#include <iostream>
#include <vector>
#include <list>

namespace pwie
{

    std::vector<int> sort_indexes(const std::vector< std::pair<int, double> > &v)
    {
        std::vector<int> idx(v.size());
        for (size_t i = 0; i != idx.size(); ++i)
            idx[i] = v[i].first;
        sort(idx.begin(), idx.end(), [&v](size_t i1, size_t i2)
        {
            return v[i1].second < v[i2].second;
        });
        return idx;
    }

    LbfgsbSolver::LbfgsbSolver() : ISolver()
    {
        // TODO Auto-generated constructor stub
        hasbounds = false;
        hasbound_lower = false;
        hasbound_upper = false;

    }

    void LbfgsbSolver::setLowerBound(const Vector &lower)
    {
        lb = lower;
        hasbound_lower = true;
    }
    void LbfgsbSolver::setUpperBound(const Vector &upper)
    {
        ub = upper;
        hasbound_upper = true;
    }

    void LbfgsbSolver::GetGeneralizedCauchyPoint(Vector &x, Vector &g, Vector &x_cauchy,
                                                 Vector &c)
    {
        const int DIM = x.rows();
        // PAGE 8
        // Algorithm CP: Computation of the generalized Cauchy point
        // Given x,l,u,g, and B = \theta I-WMW

        // {all t_i} = { (idx,value), ... }
        // TODO: use "std::set" ?
        std::vector<std::pair<int, double> > SetOfT;
        // the feasible set is implicitly given by "SetOfT - {t_i==0}"
        Vector d = Vector::Zero(DIM, 1);

        // n operations
        for (int j = 0; j < DIM; j++)
        {
            if (g(j) == 0)
            {
                SetOfT.push_back(std::make_pair(j, INF));
            }
            else
            {
                double tmp = 0;
                if (g(j) < 0)
                {
                    tmp = (x(j) - ub(j)) / g(j);
                }
                else
                {
                    tmp = (x(j) - lb(j)) / g(j);
                }
                d(j) = -g(j);
                SetOfT.push_back(std::make_pair(j, tmp));
            }

        }
        Debug(d.transpose());

        // paper: using heapsort
        // sortedindices [1,0,2] means the minimal element is on the 1th entry
        std::vector<int> SortedIndices = sort_indexes(SetOfT);

        x_cauchy = x;
        // Initialize
        // p :=     W^T*p
        Vector p = (W.transpose() * d);                     // (2mn operations)
        // c :=     0
        c = Eigen::MatrixXd::Zero(M.rows(), 1);
        // f' :=    g^T*d = -d^Td
        double f_prime = -d.dot(d);                         // (n operations)
        // f'' :=   \theta*d^T*d-d^T*W*M*W^T*d = -\theta*f' - p^T*M*p
        double f_doubleprime = (double)(-1.0 * theta) * f_prime - p.dot(M * p); // (O(m^2) operations)
        // \delta t_min :=  -f'/f''
        double dt_min = -f_prime / f_doubleprime;
        // t_old :=     0
        double t_old = 0;
        // b :=     argmin {t_i , t_i >0}
        int i = 0;
        for (int j = 0; j < DIM; j++)
        {
            i = j;
            if (SetOfT[SortedIndices[j]].second != 0)
                break;
        }
        int b = SortedIndices[i];
        // see below
        // t                    :=  min{t_i : i in F}
        double t = SetOfT[b].second;
        // \delta t             :=  t - 0
        double dt = t - t_old;

        // examination of subsequent segments
        while ((dt_min >= dt) && (i < DIM))
        {
            if (d(b) > 0)
                x_cauchy(b) = ub(b);
            else if (d(b) < 0)
                x_cauchy(b) = lb(b);

            // z_b = x_p^{cp} - x_b
            double zb = x_cauchy(b) - x(b);
            // c   :=  c +\delta t*p
            c += dt * p;
            // cache
            Vector wbt = W.row(b);

            f_prime += dt * f_doubleprime + (double) g(b) * g(b)
                       + (double) theta * g(b) * zb
                       - (double) g(b) * wbt.transpose() * (M * c);
            f_doubleprime += (double) - 1.0 * theta * g(b) * g(b)
                             - (double) 2.0 * (g(b) * (wbt.dot(M * p)))
                             - (double) g(b) * g(b) * wbt.transpose() * (M * wbt);
            p += g(b) * wbt.transpose();
            d(b) = 0;
            dt_min = -f_prime / f_doubleprime;
            t_old = t;
            ++i;
            if (i < DIM)
            {
                b = SortedIndices[i];
                t = SetOfT[b].second;
                dt = t - t_old;
            }

        }

        dt_min = max(dt_min, 0);
        t_old += dt_min;

        Debug(SortedIndices[0] << " " << SortedIndices[1]);

#pragma omp parallel for
        for (int ii = i; ii < x_cauchy.rows(); ii++)
        {
            x_cauchy(SortedIndices[ii]) = x(SortedIndices[ii])
                                          + t_old * d(SortedIndices[ii]);
        }
        Debug(x_cauchy.transpose());

        c += dt_min * p;
        Debug(c.transpose());

    }
    double LbfgsbSolver::FindAlpha(Vector &x_cp, Vector &du, std::vector<int> &FreeVariables)
    {
        /* this returns
   * a* = max {a : a <= 1 and  l_i-xc_i <= a*d_i <= u_i-xc_i}
   */
        double alphastar = 1;
        const unsigned int n = FreeVariables.size();
        for (unsigned int i = 0; i < n; i++)
        {
            if (du(i) > 0)
            {
                alphastar = min(alphastar,
                                (ub(FreeVariables[i]) - x_cp(FreeVariables[i]))
                                / du(i));
            }
            else
            {
                alphastar = min(alphastar,
                                (lb(FreeVariables[i]) - x_cp(FreeVariables[i]))
                                / du(i));
            }
        }
        return alphastar;
    }

    void LbfgsbSolver::SubspaceMinimization(Vector &x_cauchy, Vector &x, Vector &c, Vector &g,
                                            Vector &SubspaceMin)
    {

        // cached value: ThetaInverse=1/theta;
        double theta_inverse = 1 / theta;

        // size of "t"
        std::vector<int> FreeVariablesIndex;
        Debug(x_cauchy.transpose());

        //std::cout << "free vars " << FreeVariables.rows() << std::endl;
        for (int i = 0; i < x_cauchy.rows(); i++)
        {
            Debug(x_cauchy(i) << " " << ub(i) << " " << lb(i));
            if ((x_cauchy(i) != ub(i)) && (x_cauchy(i) != lb(i)))
            {
                FreeVariablesIndex.push_back(i);
            }
        }
        const int FreeVarCount = FreeVariablesIndex.size();

        Matrix WZ = Matrix::Zero(W.cols(), FreeVarCount);

        for (int i = 0; i < FreeVarCount; i++)
            WZ.col(i) = W.row(FreeVariablesIndex[i]);

        Debug(WZ);

        // r=(g+theta*(x_cauchy-x)-W*(M*c));
        Debug(g);
        Debug(x_cauchy);
        Debug(x);
        Vector rr = (g + theta * (x_cauchy - x) - W * (M * c));
        // r=r(FreeVariables);
        Vector r = Matrix::Zero(FreeVarCount, 1);
        for (int i = 0; i < FreeVarCount; i++)
            r.row(i) = rr.row(FreeVariablesIndex[i]);

        Debug(r.transpose());

        // STEP 2: "v = w^T*Z*r" and STEP 3: "v = M*v"
        Vector v = M * (WZ * r);
        // STEP 4: N = 1/theta*W^T*Z*(W^T*Z)^T
        Matrix N = theta_inverse * WZ * WZ.transpose();
        // N = I - MN
        N = Matrix::Identity(N.rows(), N.rows()) - M * N;
        // STEP: 5
        // v = N^{-1}*v
        v = N.lu().solve(v);
        // STEP: 6
        // HERE IS A MISTAKE IN THE ORIGINAL PAPER!
        Vector du = -theta_inverse * r
                    - theta_inverse * theta_inverse * WZ.transpose() * v;
        Debug(du.transpose());
        // STEP: 7
        double alpha_star = FindAlpha(x_cauchy, du, FreeVariablesIndex);

        // STEP: 8
        Vector dStar = alpha_star * du;

        SubspaceMin = x_cauchy;
        for (int i = 0; i < FreeVarCount; i++)
        {
            SubspaceMin(FreeVariablesIndex[i]) = SubspaceMin(
                                                     FreeVariablesIndex[i]) + dStar(i);
        }
    }

    void LbfgsbSolver::internalSolve(Vector &x0,
                                     function_t const &FunctionValue,
                                     gradient_t const &FunctionGradient,
                                     hessian_t  const &FunctionHessian)
    {
        UNUSED(FunctionHessian);
        DIM = x0.rows();

        if (!hasbound_lower)
        {
            lb = (-1 * Vector::Ones(DIM)) * INF;
            hasbound_lower = true;
        }

        if (!hasbound_upper)
        {
            ub = Vector::Ones(DIM) * INF;
            hasbound_upper = true;
        }
        theta = 1.0;

        W = Matrix::Zero(DIM, 0);
        M = Matrix::Zero(0, 0);
        Matrix H = Matrix::Identity(DIM, DIM);

        FunctionObjectiveOracle_ = FunctionValue;
        FunctionGradientOracle_ = FunctionGradient;

        Assert(x0.rows() == lb.rows(), "lower bound size incorrect");
        Assert(x0.rows() == ub.rows(), "upper bound size incorrect");

        Debug(x0.transpose());
        Debug(lb.transpose());
        Debug(ub.transpose());

        Assert((x0.array() >= lb.array()).all(),
               "seed is not feasible (violates lower bound)");
        Assert((x0.array() <= ub.array()).all(),
               "seed is not feasible (violates upper bound)");

        xHistory.push_back(x0);

        Matrix yHistory = Matrix::Zero(DIM, 0);
        Matrix sHistory = Matrix::Zero(DIM, 0);

        Vector x = x0, g;
        size_t k = 0;

        double f = FunctionObjectiveOracle_(x);

        FunctionGradientOracle_(x, g);

        Debug(f);
        Debug(g.transpose());

        auto noConvergence =
                [&](Vector & x, Vector & g)->bool
        {
            return (((x - g).cwiseMax(lb).cwiseMin(ub) - x).lpNorm<Eigen::Infinity>() >= 1e-4);
        };

        while (noConvergence(x, g) && (k < settings.maxIter))
        {

            //std::cout << FunctionObjectiveOracle_(x) << std::endl;
            Debug("iteration " << k)
                    double f_old = f;
            Vector x_old = x;
            Vector g_old = g;

            // STEP 2: compute the cauchy point by algorithm CP
            Vector CauchyPoint = Matrix::Zero(DIM, 1), c = Matrix::Zero(DIM, 1);
            GetGeneralizedCauchyPoint(x, g, CauchyPoint, c);
            // STEP 3: compute a search direction d_k by the primal method
            Vector SubspaceMin;
            SubspaceMinimization(CauchyPoint, x, c, g, SubspaceMin);

            double Length = 0;
            double initial_alpha = (k==0) ? 1.0 : 1.0/g.norm();
            // STEP 4: perform linesearch and STEP 5: compute gradient
            WolfeRule::linesearch(x, SubspaceMin - x,FunctionValue, FunctionGradient, initial_alpha);

            xHistory.push_back(x);

            // prepare for next iteration
            Vector newY = g - g_old;
            Vector newS = x - x_old;

            // STEP 6:
            double test = newS.dot(newY);
            test = (test < 0) ? -1.0 * test : test;

            if (test > EPS * newY.squaredNorm())
            {
                if (k < settings.m)
                {
                    yHistory.conservativeResize(DIM, k + 1);
                    sHistory.conservativeResize(DIM, k + 1);
                }
                else
                {

                    yHistory.leftCols(settings.m - 1) = yHistory.rightCols(
                                                            settings.m - 1).eval();
                    sHistory.leftCols(settings.m - 1) = sHistory.rightCols(
                                                            settings.m - 1).eval();
                }
                yHistory.rightCols(1) = newY;
                sHistory.rightCols(1) = newS;

                // STEP 7:
                theta = (double)(newY.transpose() * newY)
                        / (newY.transpose() * newS);

                W = Matrix::Zero(yHistory.rows(),
                                 yHistory.cols() + sHistory.cols());

                W << yHistory, (theta * sHistory);

                Matrix A = sHistory.transpose() * yHistory;
                Matrix L = A.triangularView<Eigen::StrictlyLower>();
                Matrix MM(A.rows() + L.rows(), A.rows() + L.cols());
                Matrix D = -1 * A.diagonal().asDiagonal();
                MM << D, L.transpose(), L, ((sHistory.transpose() * sHistory)
                                            * theta);

                M = MM.inverse();

            }

            Vector ttt = Matrix::Zero(1, 1);
            ttt(0) = f_old - f;
            Debug("--> " << ttt.norm());
            if (ttt.norm() < 1e-8)
            {
                // successive function values too similar
                break;
            }
            k++;

        }

        x0 = x;

    }
}

/* namespace pwie */
<commit_msg>Minor coding style changes<commit_after>/**
 * Copyright (c) 2014-2015 Patrick Wieschollek
 *
 * 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 "LbfgsbSolver.h"
#include "linesearch/WolfeRule.h"
#include <iostream>
#include <vector>
#include <list>

namespace pwie
{

    std::vector<int> sort_indexes(const std::vector< std::pair<int, double> > &v)
    {
        std::vector<int> idx(v.size());
        for (size_t i = 0; i != idx.size(); ++i)
            idx[i] = v[i].first;
        sort(idx.begin(), idx.end(), [&v](size_t i1, size_t i2)
        {
            return v[i1].second < v[i2].second;
        });
        return idx;
    }

    LbfgsbSolver::LbfgsbSolver() : ISolver()
    {
        // TODO Auto-generated constructor stub
        hasbounds = false;
        hasbound_lower = false;
        hasbound_upper = false;

    }

    void LbfgsbSolver::setLowerBound(const Vector &lower)
    {
        lb = lower;
        hasbound_lower = true;
    }
    void LbfgsbSolver::setUpperBound(const Vector &upper)
    {
        ub = upper;
        hasbound_upper = true;
    }

    void LbfgsbSolver::GetGeneralizedCauchyPoint(Vector &x, Vector &g, Vector &x_cauchy,
                                                 Vector &c)
    {
        const int DIM = x.rows();
        // PAGE 8
        // Algorithm CP: Computation of the generalized Cauchy point
        // Given x,l,u,g, and B = \theta I-WMW

        // {all t_i} = { (idx,value), ... }
        // TODO: use "std::set" ?
        std::vector<std::pair<int, double> > SetOfT;
        // the feasible set is implicitly given by "SetOfT - {t_i==0}"
        Vector d = Vector::Zero(DIM, 1);

        // n operations
        for (int j = 0; j < DIM; j++)
        {
            if (g(j) == 0)
            {
                SetOfT.push_back(std::make_pair(j, INF));
            }
            else
            {
                double tmp = 0;
                if (g(j) < 0)
                {
                    tmp = (x(j) - ub(j)) / g(j);
                }
                else
                {
                    tmp = (x(j) - lb(j)) / g(j);
                }
                d(j) = -g(j);
                SetOfT.push_back(std::make_pair(j, tmp));
            }

        }
        Debug(d.transpose());

        // paper: using heapsort
        // sortedindices [1,0,2] means the minimal element is on the 1th entry
        std::vector<int> SortedIndices = sort_indexes(SetOfT);

        x_cauchy = x;
        // Initialize
        // p :=     W^T*p
        Vector p = (W.transpose() * d);                     // (2mn operations)
        // c :=     0
        c = Eigen::MatrixXd::Zero(M.rows(), 1);
        // f' :=    g^T*d = -d^Td
        double f_prime = -d.dot(d);                         // (n operations)
        // f'' :=   \theta*d^T*d-d^T*W*M*W^T*d = -\theta*f' - p^T*M*p
        double f_doubleprime = (double)(-1.0 * theta) * f_prime - p.dot(M * p); // (O(m^2) operations)
        // \delta t_min :=  -f'/f''
        double dt_min = -f_prime / f_doubleprime;
        // t_old :=     0
        double t_old = 0;
        // b :=     argmin {t_i , t_i >0}
        int i = 0;
        for (int j = 0; j < DIM; j++)
        {
            i = j;
            if (SetOfT[SortedIndices[j]].second != 0)
                break;
        }
        int b = SortedIndices[i];
        // see below
        // t                    :=  min{t_i : i in F}
        double t = SetOfT[b].second;
        // \delta t             :=  t - 0
        double dt = t - t_old;

        // examination of subsequent segments
        while ((dt_min >= dt) && (i < DIM))
        {
            if (d(b) > 0)
                x_cauchy(b) = ub(b);
            else if (d(b) < 0)
                x_cauchy(b) = lb(b);

            // z_b = x_p^{cp} - x_b
            double zb = x_cauchy(b) - x(b);
            // c   :=  c +\delta t*p
            c += dt * p;
            // cache
            Vector wbt = W.row(b);

            f_prime += dt * f_doubleprime + (double) g(b) * g(b)
                       + (double) theta * g(b) * zb
                       - (double) g(b) * wbt.transpose() * (M * c);
            f_doubleprime += (double) - 1.0 * theta * g(b) * g(b)
                             - (double) 2.0 * (g(b) * (wbt.dot(M * p)))
                             - (double) g(b) * g(b) * wbt.transpose() * (M * wbt);
            p += g(b) * wbt.transpose();
            d(b) = 0;
            dt_min = -f_prime / f_doubleprime;
            t_old = t;
            ++i;
            if (i < DIM)
            {
                b = SortedIndices[i];
                t = SetOfT[b].second;
                dt = t - t_old;
            }

        }

        dt_min = max(dt_min, 0);
        t_old += dt_min;

        Debug(SortedIndices[0] << " " << SortedIndices[1]);

        #pragma omp parallel for
        for (int ii = i; ii < x_cauchy.rows(); ii++)
        {
            x_cauchy(SortedIndices[ii]) = x(SortedIndices[ii])
                                          + t_old * d(SortedIndices[ii]);
        }
        Debug(x_cauchy.transpose());

        c += dt_min * p;
        Debug(c.transpose());
    }

    double LbfgsbSolver::FindAlpha(Vector &x_cp, Vector &du, std::vector<int> &FreeVariables)
    {
        /* this returns
   * a* = max {a : a <= 1 and  l_i-xc_i <= a*d_i <= u_i-xc_i}
   */
        double alphastar = 1;
        const unsigned int n = FreeVariables.size();
        for (unsigned int i = 0; i < n; i++)
        {
            if (du(i) > 0)
            {
                alphastar = min(alphastar, (ub(FreeVariables[i]) - x_cp(FreeVariables[i])) / du(i));
            }
            else
            {
                alphastar = min(alphastar, (lb(FreeVariables[i]) - x_cp(FreeVariables[i])) / du(i));
            }
        }

        return alphastar;
    }

    void LbfgsbSolver::SubspaceMinimization(Vector &x_cauchy, Vector &x, Vector &c, Vector &g,
                                            Vector &SubspaceMin)
    {

        // cached value: ThetaInverse=1/theta;
        double theta_inverse = 1 / theta;

        // size of "t"
        std::vector<int> FreeVariablesIndex;
        Debug(x_cauchy.transpose());

        //std::cout << "free vars " << FreeVariables.rows() << std::endl;
        for (int i = 0; i < x_cauchy.rows(); i++)
        {
            Debug(x_cauchy(i) << " " << ub(i) << " " << lb(i));
            if ((x_cauchy(i) != ub(i)) && (x_cauchy(i) != lb(i)))
            {
                FreeVariablesIndex.push_back(i);
            }
        }

        int const FreeVarCount = FreeVariablesIndex.size();

        Matrix WZ = Matrix::Zero(W.cols(), FreeVarCount);

        for (int i = 0; i < FreeVarCount; i++)
        {
            WZ.col(i) = W.row(FreeVariablesIndex[i]);
        }

        Debug(WZ);

        // r=(g+theta*(x_cauchy-x)-W*(M*c));
        Debug(g);
        Debug(x_cauchy);
        Debug(x);
        Vector rr = (g + theta * (x_cauchy - x) - W * (M * c));
        // r=r(FreeVariables);
        Vector r = Matrix::Zero(FreeVarCount, 1);
        for (int i = 0; i < FreeVarCount; i++)
            r.row(i) = rr.row(FreeVariablesIndex[i]);

        Debug(r.transpose());

        // STEP 2: "v = w^T*Z*r" and STEP 3: "v = M*v"
        Vector v = M * (WZ * r);

        // STEP 4: N = 1/theta*W^T*Z*(W^T*Z)^T
        Matrix N = theta_inverse * WZ * WZ.transpose();
        // N = I - MN
        N = Matrix::Identity(N.rows(), N.rows()) - M * N;

        // STEP: 5
        // v = N^{-1}*v
        v = N.lu().solve(v);

        // STEP: 6
        // HERE IS A MISTAKE IN THE ORIGINAL PAPER!
        Vector du = -theta_inverse * r
                    - theta_inverse * theta_inverse * WZ.transpose() * v;
        Debug(du.transpose());

        // STEP: 7
        double alpha_star = FindAlpha(x_cauchy, du, FreeVariablesIndex);

        // STEP: 8
        Vector dStar = alpha_star * du;

        SubspaceMin = x_cauchy;
        for (int i = 0; i < FreeVarCount; i++)
        {
            SubspaceMin(FreeVariablesIndex[i]) = SubspaceMin( FreeVariablesIndex[i]) + dStar(i);
        }
    }

    void LbfgsbSolver::internalSolve( Vector &x0,
                                      function_t const &FunctionValue,
                                      gradient_t const &FunctionGradient,
                                      hessian_t  const &FunctionHessian )
    {
        UNUSED(FunctionHessian);
        DIM = x0.rows();

        if (!hasbound_lower)
        {
            lb = (-1 * Vector::Ones(DIM)) * INF;
            hasbound_lower = true;
        }

        if (!hasbound_upper)
        {
            ub = Vector::Ones(DIM) * INF;
            hasbound_upper = true;
        }
        theta = 1.0;

        W = Matrix::Zero(DIM, 0);
        M = Matrix::Zero(0, 0);

        FunctionObjectiveOracle_ = FunctionValue;
        FunctionGradientOracle_ = FunctionGradient;

        Assert(x0.rows() == lb.rows(), "lower bound size incorrect");
        Assert(x0.rows() == ub.rows(), "upper bound size incorrect");

        Debug(x0.transpose());
        Debug(lb.transpose());
        Debug(ub.transpose());

        Assert((x0.array() >= lb.array()).all(), "seed is not feasible (violates lower bound)");
        Assert((x0.array() <= ub.array()).all(), "seed is not feasible (violates upper bound)");

        xHistory.push_back(x0);

        Matrix yHistory = Matrix::Zero(DIM, 0);
        Matrix sHistory = Matrix::Zero(DIM, 0);

        Vector x = x0, g;
        size_t k = 0;

        double f = FunctionObjectiveOracle_(x);

        FunctionGradientOracle_(x, g);

        Debug(f);
        Debug(g.transpose());

        auto noConvergence = [&](Vector & x, Vector & g)->bool
        {
            return (((x - g).cwiseMax(lb).cwiseMin(ub) - x).lpNorm<Eigen::Infinity>() >= 1e-4);
        };

        while (noConvergence(x, g) && (k < settings.maxIter))
        {

            //std::cout << FunctionObjectiveOracle_(x) << std::endl;
            Debug("iteration " << k)
                    double f_old = f;
            Vector x_old = x;
            Vector g_old = g;

            // STEP 2: compute the cauchy point by algorithm CP
            Vector CauchyPoint = Matrix::Zero(DIM, 1), c = Matrix::Zero(DIM, 1);
            GetGeneralizedCauchyPoint(x, g, CauchyPoint, c);
            // STEP 3: compute a search direction d_k by the primal method
            Vector SubspaceMin;
            SubspaceMinimization(CauchyPoint, x, c, g, SubspaceMin);
            double initial_alpha = (k==0) ? 1.0 : 1.0/g.norm();

            // STEP 4: perform linesearch and STEP 5: compute gradient
            WolfeRule::linesearch(x, SubspaceMin - x,FunctionValue, FunctionGradient, initial_alpha);

            xHistory.push_back(x);

            // prepare for next iteration
            Vector newY = g - g_old;
            Vector newS = x - x_old;

            // STEP 6:
            double test = newS.dot(newY);
            test = (test < 0) ? -1.0 * test : test;

            if (test > EPS * newY.squaredNorm())
            {
                if (k < settings.m)
                {
                    yHistory.conservativeResize(DIM, k + 1);
                    sHistory.conservativeResize(DIM, k + 1);
                }
                else
                {

                    yHistory.leftCols(settings.m - 1) = yHistory.rightCols(
                                                            settings.m - 1).eval();
                    sHistory.leftCols(settings.m - 1) = sHistory.rightCols(
                                                            settings.m - 1).eval();
                }
                yHistory.rightCols(1) = newY;
                sHistory.rightCols(1) = newS;

                // STEP 7:
                theta = (double)(newY.transpose() * newY) / (newY.transpose() * newS);

                W = Matrix::Zero(yHistory.rows(),
                                 yHistory.cols() + sHistory.cols());

                W << yHistory, (theta * sHistory);

                Matrix A = sHistory.transpose() * yHistory;
                Matrix L = A.triangularView<Eigen::StrictlyLower>();
                Matrix MM(A.rows() + L.rows(), A.rows() + L.cols());
                Matrix D = -1 * A.diagonal().asDiagonal();
                MM << D, L.transpose(), L, ((sHistory.transpose() * sHistory)
                                            * theta);

                M = MM.inverse();

            }

            Vector ttt = Matrix::Zero(1, 1);
            ttt(0) = f_old - f;
            Debug("--> " << ttt.norm());
            if (ttt.norm() < 1e-8)
            {
                // successive function values too similar
                break;
            }
            ++k;

        }

        x0 = x;
    }
}

/* namespace pwie */
<|endoftext|>