<commit_before>// @(#)root/roostats:$Id$
// Authors: Kevin Belasco        17/06/2009
// Authors: Kyle Cranmer         17/06/2009
/*************************************************************************
 * Copyright (C) 1995-2008, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//_________________________________________________
/*
BEGIN_HTML

This class uses the Metropolis-Hastings algorithm to construct a Markov Chain
of data points using Monte Carlo. In the main algorithm, new points in the
parameter space are proposed and then visited based on their relative
likelihoods.  This class can use any implementation of the ProposalFunction,
including non-symmetric proposal functions, to propose parameter points and
still maintain detailed balance when constructing the chain.

The "Likelihood" function that is sampled when deciding what steps to take in
the chain has been given a very generic implementation.  The user can create
any RooAbsReal based on the parameters and pass it to a MetropolisHastings
object with the method SetFunction(RooAbsReal&).  Be sure to tell
MetropolisHastings whether your RooAbsReal is on a (+/-) regular or log scale,
so that it knows what logic to use when sampling your RooAbsReal.  For example,
a common use is to sample from a -log(Likelihood) distribution (NLL), for which
the appropriate configuration calls are SetType(MetropolisHastings::kLog);
SetSign(MetropolisHastings::kNegative);
If you're using a traditional likelihood function:
SetType(MetropolisHastings::kRegular);  SetSign(MetropolisHastings::kPositive);
You must set these type and sign flags or MetropolisHastings will not construct
a MarkovChain.

Also note that in ConstructChain(), the values of the variables are randomized
uniformly over their intervals before construction of the MarkovChain begins.

END_HTML
*/
//_________________________________________________

#ifndef RooStats_RooStatsUtils
#include "RooStats/RooStatsUtils.h"
#endif
#ifndef ROOT_Rtypes
#include "Rtypes.h"
#endif
#ifndef ROO_REAL_VAR
#include "RooRealVar.h"
#endif
#ifndef ROO_NLL_VAR
#include "RooNLLVar.h"
#endif
#ifndef ROO_GLOBAL_FUNC
#include "RooGlobalFunc.h"
#endif
#ifndef ROO_DATA_SET
#include "RooDataSet.h"
#endif
#ifndef ROO_ARG_SET
#include "RooArgSet.h"
#endif
#ifndef ROO_ARG_LIST
#include "RooArgList.h"
#endif
#ifndef ROO_MSG_SERVICE
#include "RooMsgService.h"
#endif
#ifndef ROO_RANDOM
#include "RooRandom.h"
#endif
#ifndef ROOT_TH1
#include "TH1.h"
#endif
#ifndef ROOT_TMath
#include "TMath.h"
#endif
#ifndef ROOT_TFile
#include "TFile.h"
#endif
#ifndef ROOSTATS_MetropolisHastings
#include "RooStats/MetropolisHastings.h"
#endif
#ifndef ROOSTATS_MarkovChain
#include "RooStats/MarkovChain.h"
#endif
#ifndef RooStats_MCMCInterval
#include "RooStats/MCMCInterval.h"
#endif

ClassImp(RooStats::MetropolisHastings);

using namespace RooFit;
using namespace RooStats;

MetropolisHastings::MetropolisHastings()
{
   // default constructor
   fFunction = NULL;
   fPropFunc = NULL;
   fNumIters = 0;
   fNumBurnInSteps = 0;
   fSign = kSignUnset;
   fType = kTypeUnset;
}

MetropolisHastings::MetropolisHastings(RooAbsReal& function, const RooArgSet& paramsOfInterest,
      ProposalFunction& proposalFunction, Int_t numIters)
{
   fFunction = &function;
   SetParameters(paramsOfInterest);
   SetProposalFunction(proposalFunction);
   fNumIters = numIters;
   fNumBurnInSteps = 0;
   fSign = kSignUnset;
   fType = kTypeUnset;
}

MarkovChain* MetropolisHastings::ConstructChain()
{
   if (fParameters.getSize() == 0 || !fPropFunc || !fFunction) {
      coutE(Eval) << "Critical members unintialized: parameters, proposal " <<
                     " function, or (log) likelihood function" << endl;
         return NULL;
   }
   if (fSign == kSignUnset || fType == kTypeUnset) {
      coutE(Eval) << "Please set type and sign of your function using "
         << "MetropolisHastings::SetType() and MetropolisHastings::SetSign()" <<
         endl;
      return NULL;
   }

   if (fChainParams.getSize() == 0) fChainParams.add(fParameters);

   RooArgSet x;
   RooArgSet xPrime;
   x.addClone(fParameters);
   RandomizeCollection(x);
   xPrime.addClone(fParameters);
   RandomizeCollection(xPrime);

   MarkovChain* chain = new MarkovChain();
   // only the POI will be added to the chain
   chain->SetParameters(fChainParams);

   Int_t weight = 0;
   Double_t xL = 0.0, xPrimeL = 0.0, a = 0.0;

   RooFit::MsgLevel oldMsgLevel = RooMsgService::instance().globalKillBelow();
   RooMsgService::instance().setGlobalKillBelow(RooFit::ERROR);

   // We will need to check if log-likelihood evaluation left an error status.
   // Now using faster eval error logging with CountErrors.
   if (fType == kLog)
     fFunction->setEvalErrorLoggingMode(RooAbsReal::CountErrors);

   bool hadEvalError = true;

   Int_t i = 0;
   // get a good starting point for x
   // for fType == kLog, this means that fFunction->getVal() did not cause
   // an eval error
   // for fType == kRegular this means fFunction->getVal() != 0
   //
   // kbelasco: i < 1000 is sort of arbitary, but way higher than the number of
   // steps we should have to take for any reasonable (log) likelihood function
   while (i < 1000 && hadEvalError) {
      RandomizeCollection(x);
      RooStats::SetParameters(&x, &fParameters);
      xL = fFunction->getVal();

      if (fType == kLog) {
         if (fFunction->numEvalErrors() > 0) {
            fFunction->clearEvalErrorLog();
            hadEvalError = true;
         } else
            hadEvalError = false;
      } else if (fType == kRegular) {
         if (xL == 0.0)
            hadEvalError = true;
         else
            hadEvalError = false;
      } else
         // for now the only 2 types are kLog and kRegular (won't get here)
         hadEvalError = false;
   }

   if(hadEvalError) {
      coutE(Eval) << "Problem finding a good starting point in " <<
                     "MetropolisHastings::ConstructChain() " << endl;
   }

   // do main loop
   for (i = 0; i < fNumIters; i++) {
      // reset error handling flag
      hadEvalError = false;

      if (i % (fNumIters / 100) == 0) {
         // print a dot every 1% of the chain construction
         fprintf(stdout, ".");
         fflush(NULL);
      }

      fPropFunc->Propose(xPrime, x);

      RooStats::SetParameters(&xPrime, &fParameters);
      xPrimeL = fFunction->getVal();

      // check if log-likelihood for xprime had an error status
      if (fFunction->numEvalErrors() > 0 && fType == kLog) {
         xPrimeL = RooNumber::infinity();
         fFunction->clearEvalErrorLog();
         hadEvalError = true;
      }

      // why evaluate the last point again, can't we cache it?
      // kbelasco: commenting out lines below to add/test caching support
      //RooStats::SetParameters(&x, &fParameters);
      //xL = fFunction->getVal();

      if (fType == kLog) {
         if (fSign == kPositive)
            a = xL - xPrimeL;
         else
            a = xPrimeL - xL;
      }
      else
         a = xPrimeL / xL;
      //a = xL / xPrimeL;

      if (!hadEvalError && !fPropFunc->IsSymmetric(xPrime, x)) {
         Double_t xPrimePD = fPropFunc->GetProposalDensity(xPrime, x);
         Double_t xPD      = fPropFunc->GetProposalDensity(x, xPrime);
         if (fType == kRegular)
            a *= xPD / xPrimePD;
         else
            a += TMath::Log(xPrimePD) - TMath::Log(xPD);
      }

      if (!hadEvalError && ShouldTakeStep(a)) {
         // go to the proposed point xPrime

         // add the current point with the current weight
         if (weight != 0.0)
            chain->Add(x, CalcNLL(xL), (Double_t)weight);

         // reset the weight and go to xPrime
         weight = 1;
         RooStats::SetParameters(&xPrime, &x);
         xL = xPrimeL;
      } else {
         // stay at the current point
         weight++;
      }
   }

   // make sure to add the last point
   if (weight != 0.0)
      chain->Add(x, CalcNLL(xL), (Double_t)weight);
   printf("\n");

   RooMsgService::instance().setGlobalKillBelow(oldMsgLevel);

   Int_t numAccepted = chain->Size();
   coutI(Eval) << "Proposal acceptance rate: " <<
                   numAccepted/(Float_t)fNumIters * 100 << "%" << endl;
   coutI(Eval) << "Number of steps in chain: " << numAccepted << endl;

   //TFile chainDataFile("chainData.root", "recreate");
   //chain->GetDataSet()->Write();
   //chainDataFile.Close();

   return chain;
}

Bool_t MetropolisHastings::ShouldTakeStep(Double_t a)
{
   if ((fType == kLog && a <= 0.0) || (fType == kRegular && a >= 1.0)) {
      // The proposed point has a higher likelihood than the
      // current point, so we should go there
      return kTRUE;
   }
   else {
      // generate numbers on a log distribution to decide
      // whether to go to xPrime or stay at x
      //Double_t rand = fGen.Uniform(1.0);
      Double_t rand = RooRandom::uniform();
      if (fType == kLog) {
         rand = TMath::Log(rand);
         // kbelasco: should this be changed to just (-rand > a) for logical
         // consistency with below test when fType == kRegular?
         if (-1.0 * rand >= a)
            // we chose to go to the new proposed point
            // even though it has a lower likelihood than the current one
            return kTRUE;
      } else {
         // fType must be kRegular
         // kbelasco: ensure that we never visit a point where PDF == 0
         //if (rand <= a)
         if (rand < a)
            // we chose to go to the new proposed point
            // even though it has a lower likelihood than the current one
            return kTRUE;
      }
      return kFALSE;
   }
}

Double_t MetropolisHastings::CalcNLL(Double_t xL)
{
   if (fType == kLog) {
      if (fSign == kNegative)
         return xL;
      else
         return -xL;
   } else {
      if (fSign == kPositive)
         return -1.0 * TMath::Log(xL);
      else
         return -1.0 * TMath::Log(-xL);
   }
}
<commit_msg>from Grabriel: mke printing of the "." as a Progress message in RooFit. Remove suppression of messages below Error<commit_after>// @(#)root/roostats:$Id$
// Authors: Kevin Belasco        17/06/2009
// Authors: Kyle Cranmer         17/06/2009
/*************************************************************************
 * Copyright (C) 1995-2008, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//_________________________________________________
/*
BEGIN_HTML

This class uses the Metropolis-Hastings algorithm to construct a Markov Chain
of data points using Monte Carlo. In the main algorithm, new points in the
parameter space are proposed and then visited based on their relative
likelihoods.  This class can use any implementation of the ProposalFunction,
including non-symmetric proposal functions, to propose parameter points and
still maintain detailed balance when constructing the chain.

The "Likelihood" function that is sampled when deciding what steps to take in
the chain has been given a very generic implementation.  The user can create
any RooAbsReal based on the parameters and pass it to a MetropolisHastings
object with the method SetFunction(RooAbsReal&).  Be sure to tell
MetropolisHastings whether your RooAbsReal is on a (+/-) regular or log scale,
so that it knows what logic to use when sampling your RooAbsReal.  For example,
a common use is to sample from a -log(Likelihood) distribution (NLL), for which
the appropriate configuration calls are SetType(MetropolisHastings::kLog);
SetSign(MetropolisHastings::kNegative);
If you're using a traditional likelihood function:
SetType(MetropolisHastings::kRegular);  SetSign(MetropolisHastings::kPositive);
You must set these type and sign flags or MetropolisHastings will not construct
a MarkovChain.

Also note that in ConstructChain(), the values of the variables are randomized
uniformly over their intervals before construction of the MarkovChain begins.

END_HTML
*/
//_________________________________________________

#ifndef RooStats_RooStatsUtils
#include "RooStats/RooStatsUtils.h"
#endif
#ifndef ROOT_Rtypes
#include "Rtypes.h"
#endif
#ifndef ROO_REAL_VAR
#include "RooRealVar.h"
#endif
#ifndef ROO_NLL_VAR
#include "RooNLLVar.h"
#endif
#ifndef ROO_GLOBAL_FUNC
#include "RooGlobalFunc.h"
#endif
#ifndef ROO_DATA_SET
#include "RooDataSet.h"
#endif
#ifndef ROO_ARG_SET
#include "RooArgSet.h"
#endif
#ifndef ROO_ARG_LIST
#include "RooArgList.h"
#endif
#ifndef ROO_MSG_SERVICE
#include "RooMsgService.h"
#endif
#ifndef ROO_RANDOM
#include "RooRandom.h"
#endif
#ifndef ROOT_TH1
#include "TH1.h"
#endif
#ifndef ROOT_TMath
#include "TMath.h"
#endif
#ifndef ROOT_TFile
#include "TFile.h"
#endif
#ifndef ROOSTATS_MetropolisHastings
#include "RooStats/MetropolisHastings.h"
#endif
#ifndef ROOSTATS_MarkovChain
#include "RooStats/MarkovChain.h"
#endif
#ifndef RooStats_MCMCInterval
#include "RooStats/MCMCInterval.h"
#endif

ClassImp(RooStats::MetropolisHastings);

using namespace RooFit;
using namespace RooStats;

MetropolisHastings::MetropolisHastings()
{
   // default constructor
   fFunction = NULL;
   fPropFunc = NULL;
   fNumIters = 0;
   fNumBurnInSteps = 0;
   fSign = kSignUnset;
   fType = kTypeUnset;
}

MetropolisHastings::MetropolisHastings(RooAbsReal& function, const RooArgSet& paramsOfInterest,
      ProposalFunction& proposalFunction, Int_t numIters)
{
   fFunction = &function;
   SetParameters(paramsOfInterest);
   SetProposalFunction(proposalFunction);
   fNumIters = numIters;
   fNumBurnInSteps = 0;
   fSign = kSignUnset;
   fType = kTypeUnset;
}

MarkovChain* MetropolisHastings::ConstructChain()
{
   if (fParameters.getSize() == 0 || !fPropFunc || !fFunction) {
      coutE(Eval) << "Critical members unintialized: parameters, proposal " <<
                     " function, or (log) likelihood function" << endl;
         return NULL;
   }
   if (fSign == kSignUnset || fType == kTypeUnset) {
      coutE(Eval) << "Please set type and sign of your function using "
         << "MetropolisHastings::SetType() and MetropolisHastings::SetSign()" <<
         endl;
      return NULL;
   }

   if (fChainParams.getSize() == 0) fChainParams.add(fParameters);

   RooArgSet x;
   RooArgSet xPrime;
   x.addClone(fParameters);
   RandomizeCollection(x);
   xPrime.addClone(fParameters);
   RandomizeCollection(xPrime);

   MarkovChain* chain = new MarkovChain();
   // only the POI will be added to the chain
   chain->SetParameters(fChainParams);

   Int_t weight = 0;
   Double_t xL = 0.0, xPrimeL = 0.0, a = 0.0;

   // ibucur: i think the user should have the possiblity to display all the message
   //    levels should he/she want to; maybe a setPrintLevel would be appropriate
   //    (maybe for the other classes that use this approach as well)?
   RooFit::MsgLevel oldMsgLevel = RooMsgService::instance().globalKillBelow();
   RooMsgService::instance().setGlobalKillBelow(RooFit::PROGRESS);

   // We will need to check if log-likelihood evaluation left an error status.
   // Now using faster eval error logging with CountErrors.
   if (fType == kLog)
     fFunction->setEvalErrorLoggingMode(RooAbsReal::CountErrors);

   bool hadEvalError = true;

   Int_t i = 0;
   // get a good starting point for x
   // for fType == kLog, this means that fFunction->getVal() did not cause
   // an eval error
   // for fType == kRegular this means fFunction->getVal() != 0
   //
   // kbelasco: i < 1000 is sort of arbitary, but way higher than the number of
   // steps we should have to take for any reasonable (log) likelihood function
   while (i < 1000 && hadEvalError) {
      RandomizeCollection(x);
      RooStats::SetParameters(&x, &fParameters);
      xL = fFunction->getVal();

      if (fType == kLog) {
         if (fFunction->numEvalErrors() > 0) {
            fFunction->clearEvalErrorLog();
            hadEvalError = true;
         } else
            hadEvalError = false;
      } else if (fType == kRegular) {
         if (xL == 0.0)
            hadEvalError = true;
         else
            hadEvalError = false;
      } else
         // for now the only 2 types are kLog and kRegular (won't get here)
         hadEvalError = false;
   }

   if(hadEvalError) {
      coutE(Eval) << "Problem finding a good starting point in " <<
                     "MetropolisHastings::ConstructChain() " << endl;
   }

   ooccoutP((TObject *)0, Generation) << "Metropolis-Hastings progress: ";

   // do main loop
   for (i = 0; i < fNumIters; i++) {
      // reset error handling flag
      hadEvalError = false;

      // print a dot every 1% of the chain construction
      if (i % (fNumIters / 100) == 0) ooccoutP((TObject*)0, Generation) << ".";

      fPropFunc->Propose(xPrime, x);

      RooStats::SetParameters(&xPrime, &fParameters);
      xPrimeL = fFunction->getVal();

      // check if log-likelihood for xprime had an error status
      if (fFunction->numEvalErrors() > 0 && fType == kLog) {
         xPrimeL = RooNumber::infinity();
         fFunction->clearEvalErrorLog();
         hadEvalError = true;
      }

      // why evaluate the last point again, can't we cache it?
      // kbelasco: commenting out lines below to add/test caching support
      //RooStats::SetParameters(&x, &fParameters);
      //xL = fFunction->getVal();

      if (fType == kLog) {
         if (fSign == kPositive)
            a = xL - xPrimeL;
         else
            a = xPrimeL - xL;
      }
      else
         a = xPrimeL / xL;
      //a = xL / xPrimeL;

      if (!hadEvalError && !fPropFunc->IsSymmetric(xPrime, x)) {
         Double_t xPrimePD = fPropFunc->GetProposalDensity(xPrime, x);
         Double_t xPD      = fPropFunc->GetProposalDensity(x, xPrime);
         if (fType == kRegular)
            a *= xPD / xPrimePD;
         else
            a += TMath::Log(xPrimePD) - TMath::Log(xPD);
      }

      if (!hadEvalError && ShouldTakeStep(a)) {
         // go to the proposed point xPrime

         // add the current point with the current weight
         if (weight != 0.0)
            chain->Add(x, CalcNLL(xL), (Double_t)weight);

         // reset the weight and go to xPrime
         weight = 1;
         RooStats::SetParameters(&xPrime, &x);
         xL = xPrimeL;
      } else {
         // stay at the current point
         weight++;
      }
   }

   // make sure to add the last point
   if (weight != 0.0)
      chain->Add(x, CalcNLL(xL), (Double_t)weight);
   ooccoutP((TObject *)0, Generation) << endl;

   RooMsgService::instance().setGlobalKillBelow(oldMsgLevel);

   Int_t numAccepted = chain->Size();
   coutI(Eval) << "Proposal acceptance rate: " <<
                   numAccepted/(Float_t)fNumIters * 100 << "%" << endl;
   coutI(Eval) << "Number of steps in chain: " << numAccepted << endl;

   //TFile chainDataFile("chainData.root", "recreate");
   //chain->GetDataSet()->Write();
   //chainDataFile.Close();

   return chain;
}

Bool_t MetropolisHastings::ShouldTakeStep(Double_t a)
{
   if ((fType == kLog && a <= 0.0) || (fType == kRegular && a >= 1.0)) {
      // The proposed point has a higher likelihood than the
      // current point, so we should go there
      return kTRUE;
   }
   else {
      // generate numbers on a log distribution to decide
      // whether to go to xPrime or stay at x
      //Double_t rand = fGen.Uniform(1.0);
      Double_t rand = RooRandom::uniform();
      if (fType == kLog) {
         rand = TMath::Log(rand);
         // kbelasco: should this be changed to just (-rand > a) for logical
         // consistency with below test when fType == kRegular?
         if (-1.0 * rand >= a)
            // we chose to go to the new proposed point
            // even though it has a lower likelihood than the current one
            return kTRUE;
      } else {
         // fType must be kRegular
         // kbelasco: ensure that we never visit a point where PDF == 0
         //if (rand <= a)
         if (rand < a)
            // we chose to go to the new proposed point
            // even though it has a lower likelihood than the current one
            return kTRUE;
      }
      return kFALSE;
   }
}

Double_t MetropolisHastings::CalcNLL(Double_t xL)
{
   if (fType == kLog) {
      if (fSign == kNegative)
         return xL;
      else
         return -xL;
   } else {
      if (fSign == kPositive)
         return -1.0 * TMath::Log(xL);
      else
         return -1.0 * TMath::Log(-xL);
   }
}
<|endoftext|>


<commit_before>/*
 * Keyman is copyright (C) SIL International. MIT License.
 *
 * Keyman Core - Debugger API unit tests
 */

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include "../../../src/kmx/kmx_xstring.h"
#include "../test_assert.h"

using namespace km::kbp::kmx;
using namespace std;

void test_incxstr() {

  PKMX_WCHAR p;   // pointer input to incxstr()
  PKMX_WCHAR q;   // pointer output to incxstr()

  // --------------------------------------------------------------------------------------------------------------------------------------------------
  // ---- NULL, SURROGATE PAIRS, NON_UC_SENTINEL, ONE CHARACTER ---------------------------------------------------------------------------------------
  // --------------------------------------------------------------------------------------------------------------------------------------------------

  // --- Test for empty string ------------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR) u"\0";
  q = incxstr(p);
  assert(q == p);

  // --- Test for character ---------------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR) u"\U00001234";
  q = incxstr(p);
  assert(q == p+1);

  // --- Test for surrogate pair ----------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR) u"\U0001F609";
  q = incxstr(p);
  assert(q == p+2);

    // --- Test for one <control> -----------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U00000012";
  q = incxstr(p);
  assert(q == p + 1);

  // --- Test for FFFF only -------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF";
  q = incxstr(p);
  assert(q == p + 1);

  // --------------------------------------------------------------------------------------------------------------------------------------------------
  // ---- UC_SENTINEL WITHOUT \0 ----------------------------------------------------------------------------------------------------------------------
  // --------------------------------------------------------------------------------------------------------------------------------------------------

  // --- Test for FFFF + CODE_ANY ----------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000001\U00000001";
  q = incxstr(p);
  assert(q == p + 3);
  //
  // --- Test for FFFF +CODE_NOTANY ----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000012\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_INDEX --------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000002\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 4);

  // --- Test for FFFF +CODE_USE ----------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000005\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_DEADKEY ------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000008\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF  CODE_EXTENDED --------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\U00000006\U00000010";
  q = incxstr(p);
  assert(q == p + 9);

  // --- Test for FFFF +CODE_CLEARCONTEXT ------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000E\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_CALL ----------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000F\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_CONTEXTEX ---------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000011\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_IFOPT -------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000014\U00000002\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 5);

  // --- Test for FFFF +CODE_IFSYSTEMSTORE ------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000017\U00000002\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 5);

  // --- Test for FFFF +CODE_SETOPT ----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000013\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 4);

  // --- Test for FFFF +CODE_SETSYSTEMRESTORE ---------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000018\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 4);

  // --- Test for FFFF +CODE_RESETOPT -----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000016\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_SAVEOPT -----------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000015\U00000001";
  q = incxstr(p);
  assert(q == p + 3 );

  // --- Test for FFFF +default ----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000004";
  q = incxstr(p);
  assert(q == p + 2);

  // --------------------------------------------------------------------------------------------------------------------------------------------------
  // ---- UC_SENTINEL WITH \0 AT DIFFERENT POSITIONS --------------------------------------------------------------------------------------------------
  // --------------------------------------------------------------------------------------------------------------------------------------------------

  // --- Test for FFFF + control (earlier p+1) with \0 after first position --------------- unit test failed with old version of incxstr() -----
  p = (PKMX_WCHAR)u"\U0000FFFF\0\U00000008\U00000001";
  q = incxstr(p);
  assert(q == p+1);

  // --- Test for FFFF +control (earlier p+1) with \0 after second position --------- unit test failed with old version of incxstr() -----
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000008\0\U00000001";
  q = incxstr(p);
  assert(q == p+2);

  // --- Test for FFFF +control (earlier p+1) with \0 after third position ----- unit test failed with old version of incxstr() -----
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000008\U00000001\0";
  q = incxstr(p);
  assert(q == p+3)

  // --- Test for FFFF +control (earlier p+2) with \0 after fourth position ----- unit test failed with old version of incxstr() ----
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000002\U00000001\U00000001\0";
  q = incxstr(p);
  assert(q == p+4);

  // --- Test for FFFF +control (earlier p+3) with \0 after fifth  position ----- unit test failed with old version of incxstr() ---------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000014\U00000001\U00000001\U00000001\0";
  q = incxstr(p);
  assert(q == p+5);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 6.  position  ----- unit test failed with old version of incxstr() -----
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\0\U00000005\U00000006\U00000007\U00000010";
  q = incxstr(p);
  assert(q == p + 6);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 7.  position  ----- unit test failed with old version of incxstr()
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\0\U00000006\U00000007\U00000010";
  q = incxstr(p);
  assert(q == p + 7);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 8.  position  ----- unit test failed with old version of incxstr() ----------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\U00000006\0\U00000007\U00000010";
  q = incxstr(p);
  assert(q == p + 8);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 9.  position  ----- unit test failed with old version of incxstr() ---
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\U00000006\U00000007\0\U00000010";
  q = incxstr(p);
  assert(q == p + 9);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 10.  position  ----- unit test failed with old version of incxstr() -----------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\U00000006\U00000007\U00000010\0";
  q = incxstr(p);
  assert(q == p + 10);

  // --------------------------------------------------------------------------------------------------------------------------------------------------
  // ---- UC_SENTINEL, INCOMPLETE & UNUSUAL SEQUENCES--------------------------------------------------------------------------------------------------
  // --------------------------------------------------------------------------------------------------------------------------------------------------

  // --- Test for FFFF + \0 --------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\0";
  q = incxstr(p);
  assert(q == p + 1);

  // --- Test for FFFF +one character ------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000062";
  q = incxstr(p);
  assert(q == p + 2);

  // --- Test for FFFF +one <control> -----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000004";
  q = incxstr(p);
  assert(q == p + 2);

  // --- Test for FFFF + one <control> + character -------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000004\U00000062";
  q = incxstr(p);
  assert(q == p + 2);

}

constexpr const auto help_str = "\
debug_api [--color] <SOURCE_PATH>|--print-sizeof\n\
\n\
  --color         Force color output\n\
  --print-sizeof  Emit structure sizes for interop debug\n\
  SOURCE_PATH     Path where debug_api.cpp is found; kmx files are\n\
                  located relative to this path.\n";

int error_args() {
  std::cerr << "debug_api: Invalid arguments." << std::endl;
  std::cout << help_str;
  return 1;
}

int main(int argc, char *argv []) {
  // TODO: choose/create a keyboard which has some rules
  // Global setup
  //std::string path(argv[1]);

  if(argc < 2) {
   return error_args();
 }

  auto arg_color = std::string(argv[1]) == "--color";
  if(arg_color && argc < 3) {
    return error_args();
  }
  console_color::enabled = console_color::isaterminal() || arg_color;

  //arg_path = argv[arg_color ? 2 : 1];

  test_incxstr();

  // Destroy them
  //teardown();

  return 0;
}
<commit_msg>Update common/core/desktop/tests/unit/kmnkbd/test_kmx_xstring.cpp<commit_after>/*
 * Keyman is copyright (C) SIL International. MIT License.
 *
 * Keyman Core - Debugger API unit tests
 */

#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>
#include "../../../src/kmx/kmx_xstring.h"
#include "../test_assert.h"

using namespace km::kbp::kmx;
using namespace std;

void test_incxstr() {

  PKMX_WCHAR p;   // pointer input to incxstr()
  PKMX_WCHAR q;   // pointer output to incxstr()

  // --------------------------------------------------------------------------------------------------------------------------------------------------
  // ---- NULL, SURROGATE PAIRS, NON_UC_SENTINEL, ONE CHARACTER ---------------------------------------------------------------------------------------
  // --------------------------------------------------------------------------------------------------------------------------------------------------

  // --- Test for empty string ------------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR) u"\0";
  q = incxstr(p);
  assert(q == p);

  // --- Test for character ---------------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR) u"\U00001234";
  q = incxstr(p);
  assert(q == p+1);

  // --- Test for surrogate pair ----------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR) u"\U0001F609";
  q = incxstr(p);
  assert(q == p+2);

    // --- Test for one <control> -----------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U00000012";
  q = incxstr(p);
  assert(q == p + 1);

  // --- Test for FFFF only -------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF";
  q = incxstr(p);
  assert(q == p + 1);

  // --------------------------------------------------------------------------------------------------------------------------------------------------
  // ---- UC_SENTINEL WITHOUT \0 ----------------------------------------------------------------------------------------------------------------------
  // --------------------------------------------------------------------------------------------------------------------------------------------------

  // --- Test for FFFF + CODE_ANY ----------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000001\U00000001";
  q = incxstr(p);
  assert(q == p + 3);
  //
  // --- Test for FFFF +CODE_NOTANY ----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000012\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_INDEX --------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000002\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 4);

  // --- Test for FFFF +CODE_USE ----------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000005\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_DEADKEY ------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000008\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF  CODE_EXTENDED --------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\U00000006\U00000010";
  q = incxstr(p);
  assert(q == p + 9);

  // --- Test for FFFF +CODE_CLEARCONTEXT ------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000E\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_CALL ----------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000F\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_CONTEXTEX ---------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000011\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_IFOPT -------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000014\U00000002\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 5);

  // --- Test for FFFF +CODE_IFSYSTEMSTORE ------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000017\U00000002\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 5);

  // --- Test for FFFF +CODE_SETOPT ----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000013\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 4);

  // --- Test for FFFF +CODE_SETSYSTEMRESTORE ---------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000018\U00000002\U00000001";
  q = incxstr(p);
  assert(q == p + 4);

  // --- Test for FFFF +CODE_RESETOPT -----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000016\U00000001";
  q = incxstr(p);
  assert(q == p + 3);

  // --- Test for FFFF +CODE_SAVEOPT -----------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000015\U00000001";
  q = incxstr(p);
  assert(q == p + 3 );

  // --- Test for FFFF +default ----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000004";
  q = incxstr(p);
  assert(q == p + 2);

  // --------------------------------------------------------------------------------------------------------------------------------------------------
  // ---- UC_SENTINEL WITH \0 AT DIFFERENT POSITIONS --------------------------------------------------------------------------------------------------
  // --------------------------------------------------------------------------------------------------------------------------------------------------

  // --- Test for FFFF + control (earlier p+1) with \0 after first position --------------- unit test failed with old version of incxstr() -----
  p = (PKMX_WCHAR)u"\U0000FFFF\0\U00000008\U00000001";
  q = incxstr(p);
  assert(q == p+1);

  // --- Test for FFFF +control (earlier p+1) with \0 after second position --------- unit test failed with old version of incxstr() -----
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000008\0\U00000001";
  q = incxstr(p);
  assert(q == p+2);

  // --- Test for FFFF +control (earlier p+1) with \0 after third position ----- unit test failed with old version of incxstr() -----
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000008\U00000001\0";
  q = incxstr(p);
  assert(q == p+3)

  // --- Test for FFFF +control (earlier p+2) with \0 after fourth position ----- unit test failed with old version of incxstr() ----
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000002\U00000001\U00000001\0";
  q = incxstr(p);
  assert(q == p+4);

  // --- Test for FFFF +control (earlier p+3) with \0 after fifth  position ----- unit test failed with old version of incxstr() ---------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000014\U00000001\U00000001\U00000001\0";
  q = incxstr(p);
  assert(q == p+5);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 6.  position  ----- unit test failed with old version of incxstr() -----
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\0\U00000005\U00000006\U00000007\U00000010";
  q = incxstr(p);
  assert(q == p + 6);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 7.  position  ----- unit test failed with old version of incxstr()
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\0\U00000006\U00000007\U00000010";
  q = incxstr(p);
  assert(q == p + 7);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 8.  position  ----- unit test failed with old version of incxstr() ----------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\U00000006\0\U00000007\U00000010";
  q = incxstr(p);
  assert(q == p + 8);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 9.  position  ----- unit test failed with old version of incxstr() ---
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\U00000006\U00000007\0\U00000010";
  q = incxstr(p);
  assert(q == p + 9);

  // --- Test for FFFF +control CODE_EXTENDED ----- (earlier p+n) with \0 after 10.  position  ----- unit test failed with old version of incxstr() -----------
  p = (PKMX_WCHAR)u"\U0000FFFF\U0000000A\U00000001\U00000002\U00000003\U00000004\U00000005\U00000006\U00000007\U00000010\0";
  q = incxstr(p);
  assert(q == p + 10);

  // --------------------------------------------------------------------------------------------------------------------------------------------------
  // ---- UC_SENTINEL, INCOMPLETE & UNUSUAL SEQUENCES--------------------------------------------------------------------------------------------------
  // --------------------------------------------------------------------------------------------------------------------------------------------------

  // --- Test for FFFF + \0 --------------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\0";
  q = incxstr(p);
  assert(q == p + 1);

  // --- Test for FFFF +one character ------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000062";
  q = incxstr(p);
  assert(q == p + 2);

  // --- Test for FFFF +one <control> -----------------------------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000004";
  q = incxstr(p);
  assert(q == p + 2);

  // --- Test for FFFF + one <control> + character -------------------------------------------------------------------------------------------
  p = (PKMX_WCHAR)u"\U0000FFFF\U00000004\U00000062";
  q = incxstr(p);
  assert(q == p + 2);

}

constexpr const auto help_str = "\
debug_api [--color] <SOURCE_PATH>|--print-sizeof\n\
\n\
  --color         Force color output\n\
  --print-sizeof  Emit structure sizes for interop debug\n\
  SOURCE_PATH     Path where debug_api.cpp is found; kmx files are\n\
                  located relative to this path.\n";

int error_args() {
  std::cerr << "debug_api: Invalid arguments." << std::endl;
  std::cout << help_str;
  return 1;
}

int main(int argc, char *argv []) {
  // TODO: choose/create a keyboard which has some rules
  // Global setup
  //std::string path(argv[1]);

  if(argc < 2) {
   return error_args();
 }

  auto arg_color = std::string(argv[1]) == "--color";
  if(arg_color && argc < 3) {
    return error_args();
  }
  console_color::enabled = console_color::isaterminal() || arg_color;

  //arg_path = argv[arg_color ? 2 : 1];

  test_incxstr();

  return 0;
}
<|endoftext|>


<commit_before>/*
  Copyright:    © 2018 SIL International.
  Description:  Implementation of the context API functions using internal
                data structures and functions.
  Create Date:  27 Sep 2018
  Authors:      Tim Eves (TSE)
  History:      27 Sep 2018 - TSE - Initial implementation.
                5  Oct 2018 - TSE - Refactor out adaptor and internal classes
                                    into context.hpp
*/
#include <cassert>
#include <algorithm>
#include <vector>

#include <keyman/keyboardprocessor.h>

#include "context.hpp"
#include "json.hpp"
#include "utfcodec.hpp"

namespace {
  template<class U>
  km_kbp_status
  _context_items_from(typename U::codeunit_t const *text,
                      km_kbp_context_item **out_ptr)
  {
    assert(text); assert(out_ptr);
    if (!text || !out_ptr)  return KM_KBP_STATUS_INVALID_ARGUMENT;

    *out_ptr = nullptr;
    try
    {
      std::vector<km_kbp_context_item> res;

      for (auto i = typename U::const_iterator(text); *i; ++i)
      {
        if(i.error())   return KM_KBP_STATUS_INVALID_UTF;
        res.emplace_back(km_kbp_context_item {KM_KBP_CT_CHAR, {0,}, {*i}});
      }
      // Terminate the context_items array.
      res.emplace_back(km_kbp_context_item KM_KBP_CONTEXT_ITEM_END);

      *out_ptr = new km_kbp_context_item[res.size()];
      std::move(res.begin(), res.end(), *out_ptr);
    }
    catch (std::bad_alloc &)
    {
      return KM_KBP_STATUS_NO_MEM;
    }

    return KM_KBP_STATUS_OK;
  }

  template<class U>
  km_kbp_status _context_items_to(km_kbp_context_item const *ci,
                                  typename U::codeunit_t *buf,
                                  size_t * sz_ptr)
  {
    assert(ci); assert(sz_ptr);
    if (!ci || !sz_ptr)   return KM_KBP_STATUS_INVALID_ARGUMENT;
    auto const buf_size = *sz_ptr;

    if (buf && buf_size > 0)
    {
      auto i = typename U::iterator(buf);
      auto const e = decltype(i)(buf + buf_size - 1);

      for (;i != e && ci->type != KM_KBP_CT_END && !i.error(); ++ci)
      {
        if (ci->type == KM_KBP_CT_CHAR)
        {
          *i = ci->character; ++i;
        }
      }
      *i = 0; // null terminate the UTF16 string.

      *sz_ptr = buf_size - (e - i);

      return ci->type == KM_KBP_CT_END
              ? KM_KBP_STATUS_OK
              : KM_KBP_STATUS_INSUFFICENT_BUFFER;
    }
    else
    {
      auto n = 0;
      typename U::codeunit_t cps[4];

      do
      {
        if (ci->type == KM_KBP_CT_CHAR)
        {
          int8_t l = 4;
          U::codec::put(cps, ci->character, l);
          n += l;
        }
      }
      while(ci++->type != KM_KBP_CT_END);

      *sz_ptr = n+1;
      return KM_KBP_STATUS_OK;
    }
  }
}

km_kbp_status
km_kbp_context_items_from_utf16(km_kbp_cp const *text,
                                km_kbp_context_item **out_ptr)
{
  return _context_items_from<utf16>(reinterpret_cast<utf16::codeunit_t const *>(text), out_ptr);
}

km_kbp_status
km_kbp_context_items_from_utf8(char const *text,
                                km_kbp_context_item **out_ptr)
{
  return _context_items_from<utf8>(reinterpret_cast<utf8::codeunit_t const *>(text), out_ptr);
}

km_kbp_status km_kbp_context_items_to_utf8(km_kbp_context_item const *ci,
                                            char *buf, size_t * sz_ptr)
{
  return _context_items_to<utf8>(ci,
            reinterpret_cast<utf8::codeunit_t *>(buf),
            sz_ptr);
}

km_kbp_status km_kbp_context_items_to_utf16(km_kbp_context_item const *ci,
                                            km_kbp_cp *buf, size_t * sz_ptr)
{
  return _context_items_to<utf16>(ci,
            reinterpret_cast<utf16::codeunit_t *>(buf),
            sz_ptr);
}

void km_kbp_context_items_dispose(km_kbp_context_item *ci)
{
  delete [] ci;
}

km_kbp_status km_kbp_context_set(km_kbp_context *ctxt, km_kbp_context_item const *ci)
{
    km_kbp_context_clear(ctxt);
    return km_kbp_context_append(ctxt, ci);
}

km_kbp_status km_kbp_context_get(km_kbp_context const *ctxt,
                                 km_kbp_context_item **out_ptr)
{
  assert(ctxt); assert(out_ptr);
  if (!ctxt || !out_ptr)   return KM_KBP_STATUS_INVALID_ARGUMENT;

  try
  {
    *out_ptr = new km_kbp_context_item[ctxt->size() + 1];
  }
  catch (std::bad_alloc &)
  {
    return KM_KBP_STATUS_NO_MEM;
  }
  std::copy(ctxt->begin(), ctxt->end(), *out_ptr);
  (*out_ptr)[ctxt->size()].type = KM_KBP_CT_END;

  return KM_KBP_STATUS_OK;
}

void km_kbp_context_clear(km_kbp_context *ctxt)
{
  assert(ctxt);
  if (ctxt)
  {
    ctxt->clear();
  }
}

size_t km_kbp_context_length(km_kbp_context *ctxt)
{
  assert(ctxt);
  return ctxt ? ctxt->size() : 0;
}

km_kbp_status km_kbp_context_append(km_kbp_context *ctxt,
                                    km_kbp_context_item const *ci)
{
  assert(ctxt); assert(ci);
  if (!ctxt || !ci)   return KM_KBP_STATUS_INVALID_ARGUMENT;

  try
  {
    for (;ci->type != KM_KBP_CT_END; ++ci)
    {
      ctxt->emplace_back(*ci);
    }
  } catch (std::bad_alloc &) {
    return KM_KBP_STATUS_NO_MEM;
  }

  return KM_KBP_STATUS_OK;
}

km_kbp_status km_kbp_context_shrink(km_kbp_context *ctxt, size_t num,
                           km_kbp_context_item const * ci)
{
  assert(ctxt);
  if (!ctxt)   return KM_KBP_STATUS_INVALID_ARGUMENT;

  try
  {
    ctxt->resize(ctxt->size() - std::min(num, ctxt->size()));

    if (ci)
    {
      auto const ip = ctxt->begin();
      while(num-- && ci->type != KM_KBP_CT_END)
      {
        ctxt->emplace(ip, *ci);
        ci++;
      }
    }
  } catch (std::bad_alloc &) {
    return KM_KBP_STATUS_NO_MEM;
  }

  return KM_KBP_STATUS_OK;
}

json & operator << (json & j, km::kbp::context const & ctxt) {
  j << json::array;
  for (auto & i: ctxt)  j << i;
  return j << json::close;
}

json & operator << (json & j, km_kbp_context_item const & i)
{
  utf8::codeunit_t cps[4] = {0,};
  int8_t l = 4;

  switch (i.type)
  {
    case KM_KBP_CT_CHAR:
      utf8::codec::put(cps, i.character, l);
      j << json::string(&cps[0]);
      break;
    case KM_KBP_CT_MARKER:
      j << json::integer_u(i.marker);
      break;
    default:
      j << json::flat << json::object
          << "invalid type" << i.type
          << json::close;
  }
  return j;
}
<commit_msg>fix(common/core): buffer overrun in context api<commit_after>/*
  Copyright:    © 2018 SIL International.
  Description:  Implementation of the context API functions using internal
                data structures and functions.
  Create Date:  27 Sep 2018
  Authors:      Tim Eves (TSE)
  History:      27 Sep 2018 - TSE - Initial implementation.
                5  Oct 2018 - TSE - Refactor out adaptor and internal classes
                                    into context.hpp
*/
#include <cassert>
#include <algorithm>
#include <vector>

#include <keyman/keyboardprocessor.h>

#include "context.hpp"
#include "json.hpp"
#include "utfcodec.hpp"

namespace {
  template<class U>
  km_kbp_status
  _context_items_from(typename U::codeunit_t const *text,
                      km_kbp_context_item **out_ptr)
  {
    assert(text); assert(out_ptr);
    if (!text || !out_ptr)  return KM_KBP_STATUS_INVALID_ARGUMENT;

    *out_ptr = nullptr;
    try
    {
      std::vector<km_kbp_context_item> res;

      for (auto i = typename U::const_iterator(text); *i; ++i)
      {
        if(i.error())   return KM_KBP_STATUS_INVALID_UTF;
        res.emplace_back(km_kbp_context_item {KM_KBP_CT_CHAR, {0,}, {*i}});
      }
      // Terminate the context_items array.
      res.emplace_back(km_kbp_context_item KM_KBP_CONTEXT_ITEM_END);

      *out_ptr = new km_kbp_context_item[res.size()];
      std::move(res.begin(), res.end(), *out_ptr);
    }
    catch (std::bad_alloc &)
    {
      return KM_KBP_STATUS_NO_MEM;
    }

    return KM_KBP_STATUS_OK;
  }

  template<class U>
  km_kbp_status _context_items_to(km_kbp_context_item const *ci,
                                  typename U::codeunit_t *buf,
                                  size_t * sz_ptr)
  {
    assert(ci); assert(sz_ptr);
    if (!ci || !sz_ptr)   return KM_KBP_STATUS_INVALID_ARGUMENT;
    auto const buf_size = *sz_ptr;

    if (buf && buf_size > 0)
    {
      auto i = typename U::iterator(buf);
      auto const e = decltype(i)(buf + buf_size - 1);

      for (;i != e && ci->type != KM_KBP_CT_END && !i.error(); ++ci)
      {
        if (ci->type == KM_KBP_CT_CHAR)
        {
          *i = ci->character; ++i;
        }
      }
      *i = 0; // null terminate the UTF16 string.

      *sz_ptr = buf_size - (e - i);

      return ci->type == KM_KBP_CT_END
              ? KM_KBP_STATUS_OK
              : KM_KBP_STATUS_INSUFFICENT_BUFFER;
    }
    else
    {
      auto n = 0;
      typename U::codeunit_t cps[4];

      do
      {
        if (ci->type == KM_KBP_CT_CHAR)
        {
          int8_t l = 4;
          U::codec::put(cps, ci->character, l);
          n += l;
        }
      }
      while(ci++->type != KM_KBP_CT_END);

      *sz_ptr = n+1;
      return KM_KBP_STATUS_OK;
    }
  }
}

km_kbp_status
km_kbp_context_items_from_utf16(km_kbp_cp const *text,
                                km_kbp_context_item **out_ptr)
{
  return _context_items_from<utf16>(reinterpret_cast<utf16::codeunit_t const *>(text), out_ptr);
}

km_kbp_status
km_kbp_context_items_from_utf8(char const *text,
                                km_kbp_context_item **out_ptr)
{
  return _context_items_from<utf8>(reinterpret_cast<utf8::codeunit_t const *>(text), out_ptr);
}

km_kbp_status km_kbp_context_items_to_utf8(km_kbp_context_item const *ci,
                                            char *buf, size_t * sz_ptr)
{
  return _context_items_to<utf8>(ci,
            reinterpret_cast<utf8::codeunit_t *>(buf),
            sz_ptr);
}

km_kbp_status km_kbp_context_items_to_utf16(km_kbp_context_item const *ci,
                                            km_kbp_cp *buf, size_t * sz_ptr)
{
  return _context_items_to<utf16>(ci,
            reinterpret_cast<utf16::codeunit_t *>(buf),
            sz_ptr);
}

void km_kbp_context_items_dispose(km_kbp_context_item *ci)
{
  delete [] ci;
}

km_kbp_status km_kbp_context_set(km_kbp_context *ctxt, km_kbp_context_item const *ci)
{
    km_kbp_context_clear(ctxt);
    return km_kbp_context_append(ctxt, ci);
}

km_kbp_status km_kbp_context_get(km_kbp_context const *ctxt,
                                 km_kbp_context_item **out_ptr)
{
  assert(ctxt); assert(out_ptr);
  if (!ctxt || !out_ptr)   return KM_KBP_STATUS_INVALID_ARGUMENT;

  try
  {
    *out_ptr = new km_kbp_context_item[ctxt->size() + 1];
  }
  catch (std::bad_alloc &)
  {
    return KM_KBP_STATUS_NO_MEM;
  }
  std::copy(ctxt->begin(), ctxt->end(), *out_ptr);
  (*out_ptr)[ctxt->size()].type = KM_KBP_CT_END;

  return KM_KBP_STATUS_OK;
}

void km_kbp_context_clear(km_kbp_context *ctxt)
{
  assert(ctxt);
  if (ctxt)
  {
    ctxt->clear();
  }
}

size_t km_kbp_context_length(km_kbp_context *ctxt)
{
  assert(ctxt);
  return ctxt ? ctxt->size() : 0;
}

km_kbp_status km_kbp_context_append(km_kbp_context *ctxt,
                                    km_kbp_context_item const *ci)
{
  assert(ctxt); assert(ci);
  if (!ctxt || !ci)   return KM_KBP_STATUS_INVALID_ARGUMENT;

  try
  {
    for (;ci->type != KM_KBP_CT_END; ++ci)
    {
      ctxt->emplace_back(*ci);
    }
  } catch (std::bad_alloc &) {
    return KM_KBP_STATUS_NO_MEM;
  }

  return KM_KBP_STATUS_OK;
}

km_kbp_status km_kbp_context_shrink(km_kbp_context *ctxt, size_t num,
                           km_kbp_context_item const * ci)
{
  assert(ctxt);
  if (!ctxt)   return KM_KBP_STATUS_INVALID_ARGUMENT;

  try
  {
    ctxt->resize(ctxt->size() - std::min(num, ctxt->size()));

    if (ci)
    {
      auto const ip = ctxt->begin();
      while(num-- && ci->type != KM_KBP_CT_END)
      {
        ctxt->emplace(ip, *ci);
        ci++;
      }
    }
  } catch (std::bad_alloc &) {
    return KM_KBP_STATUS_NO_MEM;
  }

  return KM_KBP_STATUS_OK;
}

json & operator << (json & j, km::kbp::context const & ctxt) {
  j << json::array;
  for (auto & i: ctxt)  j << i;
  return j << json::close;
}

json & operator << (json & j, km_kbp_context_item const & i)
{
  utf8::codeunit_t cps[7] = {0,};
  int8_t l = 4;

  switch (i.type)
  {
    case KM_KBP_CT_CHAR:
      utf8::codec::put(cps, i.character, l);
      j << json::string(&cps[0]);
      break;
    case KM_KBP_CT_MARKER:
      j << json::integer_u(i.marker);
      break;
    default:
      j << json::flat << json::object
          << "invalid type" << i.type
          << json::close;
  }
  return j;
}
<|endoftext|>


<commit_before>#ifndef MJOLNIR_CORE_G_BAOAB_LANGEVIN_INTEGRATOR_HPP
#define MJOLNIR_CORE_G_BAOAB_LANGEVIN_INTEGRATOR_HPP
#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/core/RandomNumberGenerator.hpp>
#include <mjolnir/core/System.hpp>
#include <mjolnir/core/ForceField.hpp>
#include <mjolnir/core/SystemMotionRemover.hpp>
#include <mjolnir/core/Unit.hpp>
#include <mjolnir/util/logger.hpp>

namespace mjolnir
{

// g-BAOAB Langevin integrator developed by the following papers
// Leimkuhler B., Matthews C., Proc. R. Soc. A Math. Phys. Eng. Sci. (2016)
template<typename traitsT>
class GBAOABLangevinIntegrator
{
    public:
      using traits_type      = traitsT;
      using real_type        = typename traits_type::real_type;
      using coordinate_type  = typename traits_type::coordinate_type;
      using indices_type     = std::array<std::size_t, 2>;
      using constraints_type = std::vector<std::pair<indices_type, real_type>>;
      using system_type      = System<traitsT>;
      using forcefield_type  = ForceField<traitsT>;
      using rng_type         = RandomNumberGenerator<traits_type>;
      using remover_type     = SystemMotionRemover<traits_type>;

    public:

      GBAOABLangevinIntegrator(const real_type dt, std::vector<real_type>&& gamma,
                               constraints_type&& constraint, remover_type&& remover,
                               std::size_t correction_iter_num, std::size_t max_iter_correction,
                               real_type correction_tolerance)
          : dt_(dt), halfdt_(dt / 2), gammas_(std::move(gamma)),
            exp_gamma_dt_(gammas_.size()), noise_coeff_ (gammas_.size()),
            constraints_(constraint), square_v0s_(constraint.size()),
            r_reduced_mass_(constraint.size()), remover_(std::move(remover)),
            correction_iter_num_(correction_iter_num), correction_max_iter_(max_iter_correction),
            correction_tolerance_dt_(correction_tolerance/ dt),
            correction_tolerance_dt_itr_(correction_tolerance/ dt * correction_iter_num),
            old_position_(gammas_.size())
      {}
      ~GBAOABLangevinIntegrator() = default;

      void initialize(system_type& sys, forcefield_type& ff, rng_type& rng);

      real_type step(const real_type time, system_type& sys, forcefield_type& ff,
                     rng_type& rng);

      void update(const system_type& sys)
      {
          if(!sys.has_attribute("temperature"))
          {
              throw std::out_of_range("mjolnir::g-BAOABLangevinIntegrator: "
                  "Langevin Integrator requires reference temperature, but "
                  "`temperature` is not found in `system.attribute`.");
          }
          this->temperature_ = sys.attribute("temperature");
          this->reset_parameter(sys);
          return;
      }

      real_type delta_t() const noexcept {return dt_;}
      std::vector<real_type> const& parameters() const noexcept {return gammas_;}

    private:

      void reset_parameter(const system_type& sys) noexcept
      {
          const auto kBT = physics::constants<real_type>::kB() * this->temperature_;
          for(std::size_t i=0; i<sys.size(); ++i)
          {
              const auto gamma     = this->gammas_.at(i);
              const auto gamma_dt  = -1 * gamma * this->dt_;
              this->exp_gamma_dt_.at(i) = std::exp(gamma_dt);
              this->noise_coeff_ .at(i) = std::sqrt(
                      kBT * (1 - std::exp(2 * gamma_dt)) * sys.rmass(i));
          }
          return;
      };

      coordinate_type gen_R(rng_type& rng) noexcept
      {
          const auto x = rng.gaussian();
          const auto y = rng.gaussian();
          const auto z = rng.gaussian();
          return math::make_coordinate<coordinate_type>(x, y, z);
      }

      void correct_coordinate(coordinate_type& positions, coordinate_type& velocities)
      {
          return;
      }

      void correct_velocity(system_type sys, const real_type tolerance)
      {
          MJOLNIR_GET_DEFAULT_LOGGER();
          MJOLNIR_LOG_FUNCTION();

          std::size_t rattle_step = 0;
          while(rattle_step<correction_max_iter_)
          {
              bool corrected = false;
              for(std::size_t i=0; i<this->constraints_.size(); ++i)
              {
                  const auto& constraint       = constraints_[i];
                  const auto& indices          = constraint.first;
                  auto& p1  = sys.position(indices[0]);
                  auto& p2  = sys.position(indices[1]);
                  auto& v1  = sys.velocity(indices[0]);
                  auto& v2  = sys.velocity(indices[1]);
                  auto& rm1 = sys.rmass(indices[0]);
                  auto& rm2 = sys.rmass(indices[1]);

                  const auto pos_diff      = p2 - p1;
                  const auto vel_diff      = v2 - v1;
                  const real_type dot_pdvd = math::dot_product(pos_diff, vel_diff);
                  const real_type lambda   = dot_pdvd * r_reduced_mass_[i] * square_v0s_[i];

                  if(abs(lambda) > tolerance)
                  {
                      const auto correction_vec = lambda * pos_diff;
                      v1 += correction_vec * rm1;
                      v2 -= correction_vec * rm2;
                      corrected = true;
                  }
              }

              if(!corrected) break;

              ++rattle_step;
          }

          if(rattle_step >= correction_max_iter_)
          {
              throw std::runtime_error("rattle iteration number exceeds rattle max iteration.");
          }
          return;
     }

    private:
      real_type   dt_;
      real_type   halfdt_;
      std::vector<real_type>       gammas_;
      std::vector<real_type>       exp_gamma_dt_;
      std::vector<real_type>       noise_coeff_;
      constraints_type             constraints_; // pair of index pair and v0.
      std::vector<real_type>       square_v0s_;
      std::vector<real_type>       r_reduced_mass_;
      remover_type remover_;

      real_type   temperature_;
      std::size_t correction_iter_num_;
      std::size_t correction_max_iter_;
      real_type   correction_tolerance_dt_;
      real_type   correction_tolerance_dt_itr_;

      std::vector<coordinate_type> old_position_;
};

template<typename traitsT>
void GBAOABLangevinIntegrator<traitsT>::initialize(
        system_type& system, forcefield_type& ff, rng_type&)
{
    MJOLNIR_GET_DEFAULT_LOGGER();
    MJOLNIR_LOG_FUNCTION();

    // calculate parameters for each particles
    this->update(system);

    // calculate force
    for(std::size_t i=0; i<system.size(); ++i)
    {
        system.force(i) = math::make_coordinate<coordinate_type>(0, 0, 0);
    }
    ff.calc_force(system);

    // buffering old position
    for(std::size_t i=0; i<system.size(); ++i)
    {
        old_position_[i] = system.position(i);
    }

    for(std::size_t i=0; i<constraints_.size(); ++i)
    {
        // calculate square v0
        square_v0s_[i] = std::pow(constraints_[i].second, 2);

        // calculate inverse reduced mass
        const auto& constraint = constraints_[i];
        std::size_t first_idx  = constraint.first[0];
        std::size_t second_idx = constraint.first[1];
        r_reduced_mass_[i] = 1.0 / (system.rmass(first_idx) + system.rmass(second_idx));
    }

    return;
}

template<typename traitsT>
typename GBAOABLangevinIntegrator<traitsT>::real_type
GBAOABLangevinIntegrator<traitsT>::step(
        const real_type time, system_type& sys, forcefield_type& ff, rng_type& rng)
{
    // B step
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        sys.velocity(i)  += this->halfdt_ * sys.rmass(i) * sys.force(i);
    }
    // velocity correction step
    correct_velocity(sys, correction_tolerance_dt_);
    // A step
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        sys.position(i) += this->halfdt_ * sys.velocity(i);
    }
    // coordinate correction step
    // TODO
    // velocity correction step
    // TODO
    // O step
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        sys.velocity(i) *= this->exp_gamma_dt_[i]; // *= exp(- gamma dt)
        sys.velocity(i) += this->noise_coeff_[i] * this->gen_R(rng);
    }
    // velocity correction step
    // TODO
    // A step
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        sys.position(i) += this->halfdt_ * sys.velocity(i);
    }
    // coordinate correction step
    // TODO
    // velocity correction step
    // TODO
    // update neighbor list; reduce margin, reconstruct the list if needed;
    real_type largest_disp2(0.0);
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        coordinate_type displacement = sys.position(i) - this->old_position_[i];
        largest_disp2 = std::max(largest_disp2, math::length_sq(displacement));
        sys.position(i) = sys.adjust_position(sys.position(i));

        // reset force
        sys.force(i) = math::make_coordinate<coordinate_type>(0, 0, 0);
    }

    ff.reduce_margin(2 * std::sqrt(largest_disp2), sys);

    // B step
    ff.calc_force(sys);
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        sys.velocity(i) += this->halfdt_ * sys.rmass(i) * sys.force(i);
    }
    // velocity correction step
    // TODO

    remover_.remove(sys);

    for(std::size_t i=0; i<sys.size(); ++i)
    {
        this->old_position_[i] = sys.position(i);
    }

    return time + dt_;
}

} // mjolnir
#endif /* MJOLNIR_CORE_G_BAOAB_LANGEVIN_INTEGRATOR_HPP */
<commit_msg>fix: velocity correction step<commit_after>#ifndef MJOLNIR_CORE_G_BAOAB_LANGEVIN_INTEGRATOR_HPP
#define MJOLNIR_CORE_G_BAOAB_LANGEVIN_INTEGRATOR_HPP
#include <mjolnir/core/SimulatorTraits.hpp>
#include <mjolnir/core/RandomNumberGenerator.hpp>
#include <mjolnir/core/System.hpp>
#include <mjolnir/core/ForceField.hpp>
#include <mjolnir/core/SystemMotionRemover.hpp>
#include <mjolnir/core/Unit.hpp>
#include <mjolnir/util/logger.hpp>

namespace mjolnir
{

// g-BAOAB Langevin integrator developed by the following papers
// Leimkuhler B., Matthews C., Proc. R. Soc. A Math. Phys. Eng. Sci. (2016)
template<typename traitsT>
class GBAOABLangevinIntegrator
{
  public:
    using traits_type      = traitsT;
    using real_type        = typename traits_type::real_type;
    using coordinate_type  = typename traits_type::coordinate_type;
    using indices_type     = std::array<std::size_t, 2>;
    using constraints_type = std::vector<std::pair<indices_type, real_type>>;
    using system_type      = System<traitsT>;
    using forcefield_type  = ForceField<traitsT>;
    using rng_type         = RandomNumberGenerator<traits_type>;
    using remover_type     = SystemMotionRemover<traits_type>;

  public:

    GBAOABLangevinIntegrator(const real_type dt, std::vector<real_type>&& gamma,
                             constraints_type&& constraint, remover_type&& remover,
                             std::size_t correction_iter_num, std::size_t max_iter_correction,
                             real_type correction_tolerance)
        : dt_(dt), halfdt_(dt / 2), gammas_(std::move(gamma)),
          exp_gamma_dt_(gammas_.size()), noise_coeff_ (gammas_.size()),
          constraints_(constraint),
          square_v0s_(constraint.size()),
          r_reduced_mass_(constraint.size()),
          remover_(std::move(remover)),
          correction_iter_num_(correction_iter_num),
          correction_max_iter_(max_iter_correction),
          correction_tolerance_dt_(correction_tolerance/ dt),
          correction_tolerance_dt_itr_(correction_tolerance/ dt * correction_iter_num),
          dt_in_correction_(dt * 0.5 / correction_iter_num_),
          r_dt_in_correction_(1 / dt_in_correction_),
          old_position_(gammas_.size()),
          old_pos_rattle_(gammas_.size())
    {}
    ~GBAOABLangevinIntegrator() = default;

    void initialize(system_type& sys, forcefield_type& ff, rng_type& rng);

    real_type step(const real_type time, system_type& sys, forcefield_type& ff,
                   rng_type& rng);

    void update(const system_type& sys)
    {
        if(!sys.has_attribute("temperature"))
        {
            throw std::out_of_range("mjolnir::g-BAOABLangevinIntegrator: "
                "Langevin Integrator requires reference temperature, but "
                "`temperature` is not found in `system.attribute`.");
        }
        this->temperature_ = sys.attribute("temperature");
        this->reset_parameter(sys);
        return;
    }

    real_type delta_t() const noexcept {return dt_;}
    std::vector<real_type> const& parameters() const noexcept {return gammas_;}

  private:

    void reset_parameter(const system_type& sys) noexcept
    {
        const auto kBT = physics::constants<real_type>::kB() * this->temperature_;
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            const auto gamma     = this->gammas_.at(i);
            const auto gamma_dt  = -1 * gamma * this->dt_;
            this->exp_gamma_dt_.at(i) = std::exp(gamma_dt);
            this->noise_coeff_ .at(i) = std::sqrt(
                    kBT * (1 - std::exp(2 * gamma_dt)) * sys.rmass(i));
        }
        return;
    };

    coordinate_type gen_R(rng_type& rng) noexcept
    {
        const auto x = rng.gaussian();
        const auto y = rng.gaussian();
        const auto z = rng.gaussian();
        return math::make_coordinate<coordinate_type>(x, y, z);
    }

    void correct_coordinate(system_type& sys, const real_type tolerance)
    {
        MJOLNIR_GET_DEFAULT_LOGGER();

        std::size_t rattle_step = 0;
        while(rattle_step<correction_max_iter_)
        {
            bool corrected = false;
            for(std::size_t i=0; i<this->constraints_.size(); ++i)
            {
                const auto& constraint = constraints_[i];
                const auto& indices    = constraint.first;
                auto& p1  = sys.position(indices[0]);
                auto& p2  = sys.position(indices[1]);
                auto& v1  = sys.velocity(indices[0]);
                auto& v2  = sys.velocity(indices[1]);
                auto& op1 = this->old_pos_rattle_[indices[0]];
                auto& op2 = this->old_pos_rattle_[indices[1]];
                auto& rm1 = sys.rmass(indices[0]);
                auto& rm2 = sys.rmass(indices[1]);

                const auto      dp = p2 - p1;
                const real_type dp2 = math::length_sq(dp);
                const real_type missmatch = square_v0s_[i] - dp2;

                if(abs(missmatch) > tolerance)
                {
                    corrected = true;
                    const auto old_dp = op2 - op1;
                    const real_type dot_old_new_dp = math::dot_product(old_dp, dp);
                    const real_type lambda =
                        0.5 * missmatch * r_reduced_mass_[i] * dot_old_new_dp;
                    const coordinate_type correction_force = lambda * old_dp;
                    const coordinate_type correction_vec1  = correction_force * rm1;
                    const coordinate_type correction_vec2  = correction_force * rm2;
                    p1 -= correction_vec1;
                    p2 += correction_vec2;
                    v1 -= correction_vec1 * r_dt_in_correction_;
                    v2 += correction_vec2 * r_dt_in_correction_;
                    op1 = p1;
                    op2 = p2;
                }
            }

            if(!corrected) break;

            ++rattle_step;
        }

        if(rattle_step >= correction_max_iter_)
        {
            MJOLNIR_LOG_WARN("rattle iteration number exceeds rattle max iteration");
        }

        return;
    }

    void correct_velocity(system_type& sys, const real_type tolerance)
    {
        MJOLNIR_GET_DEFAULT_LOGGER();

        std::size_t rattle_step = 0;
        while(rattle_step<correction_max_iter_)
        {
            bool corrected = false;
            for(std::size_t i=0; i<this->constraints_.size(); ++i)
            {
                const auto& constraint       = constraints_[i];
                const auto& indices          = constraint.first;
                auto& p1  = sys.position(indices[0]);
                auto& p2  = sys.position(indices[1]);
                auto& v1  = sys.velocity(indices[0]);
                auto& v2  = sys.velocity(indices[1]);
                auto& rm1 = sys.rmass(indices[0]);
                auto& rm2 = sys.rmass(indices[1]);

                const auto pos_diff      = p2 - p1;
                const auto vel_diff      = v2 - v1;
                const real_type dot_pdvd = math::dot_product(pos_diff, vel_diff);
                const real_type lambda   = dot_pdvd * r_reduced_mass_[i] * square_v0s_[i];

                if(std::abs(lambda) > tolerance)
                {
                    const auto correction_vec = lambda * pos_diff;
                    v1 += correction_vec * rm1;
                    v2 -= correction_vec * rm2;
                    corrected = true;
                }
            }

            if(!corrected) break;

            ++rattle_step;
        }

        if(rattle_step >= correction_max_iter_)
        {
            MJOLNIR_LOG_WARN("rattle iteration number exceeds rattle max iteration.");
        }
        return;
   }

  private:
    real_type   dt_;
    real_type   halfdt_;
    std::vector<real_type>       gammas_;
    std::vector<real_type>       exp_gamma_dt_;
    std::vector<real_type>       noise_coeff_;
    constraints_type             constraints_; // pair of index pair and v0.
    std::vector<real_type>       square_v0s_;
    std::vector<real_type>       r_reduced_mass_;
    remover_type remover_;

    real_type   temperature_;
    std::size_t correction_iter_num_;
    std::size_t correction_max_iter_;
    real_type   correction_tolerance_dt_;
    real_type   correction_tolerance_dt_itr_;
    real_type   dt_in_correction_;
    real_type   r_dt_in_correction_;

    std::vector<coordinate_type> old_position_;
    std::vector<coordinate_type> old_pos_rattle_;

};

template<typename traitsT>
void GBAOABLangevinIntegrator<traitsT>::initialize(
        system_type& system, forcefield_type& ff, rng_type&)
{
    MJOLNIR_GET_DEFAULT_LOGGER();
    MJOLNIR_LOG_FUNCTION();

    // calculate parameters for each particles
    this->update(system);

    // calculate force
    for(std::size_t i=0; i<system.size(); ++i)
    {
        system.force(i) = math::make_coordinate<coordinate_type>(0, 0, 0);
    }
    ff.calc_force(system);

    // buffering old position
    for(std::size_t i=0; i<system.size(); ++i)
    {
        old_position_[i] = system.position(i);
    }

    for(std::size_t i=0; i<constraints_.size(); ++i)
    {
        // calculate square v0
        square_v0s_[i] = std::pow(constraints_[i].second, 2);

        // calculate inverse reduced mass
        const auto& constraint = constraints_[i];
        std::size_t first_idx  = constraint.first[0];
        std::size_t second_idx = constraint.first[1];
        r_reduced_mass_[i] = system.rmass(first_idx) + system.rmass(second_idx);
    }

    return;
}

template<typename traitsT>
typename GBAOABLangevinIntegrator<traitsT>::real_type
GBAOABLangevinIntegrator<traitsT>::step(
        const real_type time, system_type& sys, forcefield_type& ff, rng_type& rng)
{
    // B step
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        sys.velocity(i)  += this->halfdt_ * sys.rmass(i) * sys.force(i);
    }
    // velocity correction step
    correct_velocity(sys, correction_tolerance_dt_);
    // A step
    for(std::size_t correction_step=0; correction_step<correction_iter_num_; ++correction_step)
    {
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            this->old_pos_rattle_[i] = sys.position(i);
            sys.position(i) += this->dt_in_correction_ * sys.velocity(i);
        }
        // coordinate correction step
        // TODO
        // velocity correction step
        correct_velocity(sys, correction_tolerance_dt_itr_);
    }
    // O step
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        sys.velocity(i) *= this->exp_gamma_dt_[i]; // *= exp(- gamma dt)
        sys.velocity(i) += this->noise_coeff_[i] * this->gen_R(rng);
    }
    // velocity correction step
    // TODO
    // A step
    for(std::size_t correction_step=0; correction_step<correction_iter_num_; ++correction_step)
    {
        for(std::size_t i=0; i<sys.size(); ++i)
        {
            this->old_pos_rattle_[i] = sys.position(i);
            sys.position(i) += this->dt_in_correction_ * sys.velocity(i);
        }
        // coordinate correction step
        // TODO
        // velocity correction step
        correct_velocity(sys, correction_tolerance_dt_itr_);
    }
    // update neighbor list; reduce margin, reconstruct the list if needed;
    real_type largest_disp2(0.0);
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        coordinate_type displacement = sys.position(i) - this->old_position_[i];
        largest_disp2 = std::max(largest_disp2, math::length_sq(displacement));
        sys.position(i) = sys.adjust_position(sys.position(i));

        // reset force
        sys.force(i) = math::make_coordinate<coordinate_type>(0, 0, 0);
    }

    ff.reduce_margin(2 * std::sqrt(largest_disp2), sys);

    // B step
    ff.calc_force(sys);
    for(std::size_t i=0; i<sys.size(); ++i)
    {
        sys.velocity(i) += this->halfdt_ * sys.rmass(i) * sys.force(i);
    }
    // velocity correction step
    correct_velocity(sys, correction_tolerance_dt_);

    remover_.remove(sys);

    for(std::size_t i=0; i<sys.size(); ++i)
    {
        this->old_position_[i] = sys.position(i);
    }

    return time + dt_;
}

} // mjolnir
#endif /* MJOLNIR_CORE_G_BAOAB_LANGEVIN_INTEGRATOR_HPP */
<|endoftext|>


<commit_before>// Test.cpp

#include <iostream>
#include <fstream>
#include <time.h>
#include "StabilizerChain.h"

enum Puzzle
{
	Bubbloid3x3x3,
	Rubiks2x2x2,
	Rubiks3x3x3
};

int main( int argc, char** argv )
{
	clock_t t = clock();

	std::cout << "Generating stabilizer chain...\n";

	StabilizerChainGroup* chainGroup = new StabilizerChainGroup();

	Permutation permutation;

	uint* pointArray = nullptr;
	uint pointArraySize = 0;

	const char* fileName = nullptr;

	Puzzle puzzle = Rubiks3x3x3;

	switch( puzzle )
	{
		case Bubbloid3x3x3:
		{
			permutation.DefineIdentity();
			permutation.Define( 0, 4 );
			permutation.Define( 4, 7 );
			permutation.Define( 7, 0 );
			permutation.Define( 12, 13 );
			permutation.Define( 13, 14 );
			permutation.Define( 14, 12 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 1, 5 );
			permutation.Define( 5, 4 );
			permutation.Define( 4, 1 );
			permutation.Define( 15, 16 );
			permutation.Define( 16, 17 );
			permutation.Define( 17, 15 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 2, 6 );
			permutation.Define( 6, 5 );
			permutation.Define( 5, 2 );
			permutation.Define( 18, 19 );
			permutation.Define( 19, 20 );
			permutation.Define( 20, 18 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 3, 7 );
			permutation.Define( 7, 6 );
			permutation.Define( 6, 3 );
			permutation.Define( 21, 22 );
			permutation.Define( 22, 23 );
			permutation.Define( 23, 21 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 0, 11 );
			permutation.Define( 11, 8 );
			permutation.Define( 8, 0 );
			permutation.Define( 24, 25 );
			permutation.Define( 25, 26 );
			permutation.Define( 26, 24 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 1, 8 );
			permutation.Define( 8, 9 );
			permutation.Define( 9, 1 );
			permutation.Define( 27, 28 );
			permutation.Define( 28, 29 );
			permutation.Define( 29, 27 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 2, 9 );
			permutation.Define( 9, 10 );
			permutation.Define( 10, 2 );
			permutation.Define( 30, 31 );
			permutation.Define( 31, 32 );
			permutation.Define( 32, 30 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 3, 10 );
			permutation.Define( 10, 11 );
			permutation.Define( 11, 3 );
			permutation.Define( 33, 34 );
			permutation.Define( 34, 35 );
			permutation.Define( 35, 33 );
			chainGroup->generatorSet.insert( permutation );

			static uint __pointArray[20] = { 12, 15, 18, 21, 24, 27, 30, 33, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
			uint __pointArraySize = sizeof( __pointArray ) / sizeof( uint );

			pointArray = __pointArray;
			pointArraySize = __pointArraySize;

			fileName = "Bubbloid3x3x3.txt";

			break;
		}
		case Rubiks2x2x2:
		{
			permutation.DefineIdentity();
			permutation.DefineCycle( 0, 1, 2, 3 );
			permutation.DefineCycle( 13, 18, 7, 20 );
			permutation.DefineCycle( 14, 19, 4, 21 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 4, 5, 6, 7 );
			permutation.DefineCycle( 2, 18, 8, 22 );
			permutation.DefineCycle( 1, 17, 11, 21 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 8, 9, 10, 11 );
			permutation.DefineCycle( 5, 16, 15, 22 );
			permutation.DefineCycle( 6, 17, 12, 23 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 12, 13, 14, 15 );
			permutation.DefineCycle( 0, 20, 10, 16 );
			permutation.DefineCycle( 3, 23, 9, 19 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 16, 17, 18, 19 );
			permutation.DefineCycle( 0, 12, 8, 4 );
			permutation.DefineCycle( 1, 13, 9, 5 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 20, 21, 22, 23 );
			permutation.DefineCycle( 2, 6, 10, 14 );
			permutation.DefineCycle( 3, 7, 11, 15 );
			chainGroup->generatorSet.insert( permutation );

			static uint __pointArray[24] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
			uint __pointArraySize = sizeof( __pointArray ) / sizeof( uint );

			pointArray = __pointArray;
			pointArraySize = __pointArraySize;

			fileName = "Rubiks2x2x2.txt";

			break;
		}
		case Rubiks3x3x3:
		{
			// TODO: Unless I can reduce the number of Schreier generators, this
			//       will always run out of memory.  Hmmm...

			// +Y
			permutation.DefineIdentity();
			permutation.DefineCycle( 0, 5, 7, 2 );
			permutation.DefineCycle( 1, 3, 6, 4 );
			permutation.DefineCycle( 8, 16, 24, 32 );
			permutation.DefineCycle( 9, 17, 25, 33 );
			permutation.DefineCycle( 10, 18, 26, 34 );
			chainGroup->generatorSet.insert( permutation );

			// -X
			permutation.DefineIdentity();
			permutation.DefineCycle( 8, 13, 15, 10 );
			permutation.DefineCycle( 9, 11, 14, 12 );
			permutation.DefineCycle( 0, 39, 40, 16 );
			permutation.DefineCycle( 3, 36, 43, 19 );
			permutation.DefineCycle( 5, 34, 45, 21 );
			chainGroup->generatorSet.insert( permutation );

			// +Z
			permutation.DefineIdentity();
			permutation.DefineCycle( 16, 21, 23, 18 );
			permutation.DefineCycle( 17, 19, 22, 20 );
			permutation.DefineCycle( 5, 15, 42, 24 );
			permutation.DefineCycle( 6, 12, 41, 27 );
			permutation.DefineCycle( 7, 10, 40, 29 );
			chainGroup->generatorSet.insert( permutation );

			// +X
			permutation.DefineIdentity();
			permutation.DefineCycle( 24, 29, 31, 26 );
			permutation.DefineCycle( 25, 27, 30, 28 );
			permutation.DefineCycle( 2, 18, 42, 37 );
			permutation.DefineCycle( 4, 20, 44, 35 );
			permutation.DefineCycle( 7, 23, 47, 32 );
			chainGroup->generatorSet.insert( permutation );

			// -Z
			permutation.DefineIdentity();
			permutation.DefineCycle( 32, 37, 39, 24 );
			permutation.DefineCycle( 33, 35, 38, 36 );
			permutation.DefineCycle( 2, 31, 45, 8 );
			permutation.DefineCycle( 1, 28, 46, 11 );
			permutation.DefineCycle( 0, 26, 47, 13 );
			chainGroup->generatorSet.insert( permutation );

			// -Y
			permutation.DefineIdentity();
			permutation.DefineCycle( 40, 45, 47, 42 );
			permutation.DefineCycle( 41, 43, 46, 44 );
			permutation.DefineCycle( 13, 37, 29, 21 );
			permutation.DefineCycle( 14, 38, 30, 22 );
			permutation.DefineCycle( 15, 39, 31, 23 );
			chainGroup->generatorSet.insert( permutation );

			static uint __pointArray[20];

			// Stabilize corners...
			__pointArray[0] = 5;
			__pointArray[1] = 7;
			__pointArray[2] = 15;
			__pointArray[3] = 23;
			__pointArray[4] = 0;
			__pointArray[5] = 2;
			__pointArray[6] = 45;
			__pointArray[7] = 47;

			// Stabilize edges...
			__pointArray[8] = 12;
			__pointArray[9] = 20;
			__pointArray[10] = 17;
			__pointArray[11] = 22;
			__pointArray[12] = 3;
			__pointArray[13] = 4;
			__pointArray[14] = 44;
			__pointArray[15] = 43;
			__pointArray[16] = 35;
			__pointArray[17] = 36;
			__pointArray[18] = 33;
			__pointArray[19] = 38;

			uint __pointArraySize = sizeof( __pointArray ) / sizeof( uint );

			pointArray = __pointArray;
			pointArraySize = __pointArraySize;

			fileName = "Rubiks3x3x3.txt";

			break;
		}
	}

	chainGroup->Generate( pointArray, pointArraySize, 0, false, &std::cout );

	std::cout << "Optimizing stabilizer chain...\n";

	chainGroup->NameGenerators();

	CompressInfo compressInfo;
	chainGroup->MakeCompressInfo( compressInfo );

	chainGroup->Optimize( compressInfo, &std::cout );

	chainGroup->Print( std::cout );

	std::string jsonString;
	chainGroup->SaveToJsonString( jsonString );

	t = clock() - t;
	double elapsed_time = double(t) / double( CLOCKS_PER_SEC );
	std::cout << "Time taken: " << elapsed_time << " sec\n";

	std::fstream fstream;
	fstream.open( fileName, std::fstream::out );
	fstream << jsonString;
	fstream.close();

	getchar();

	delete chainGroup;

	return 0;
}

// Test.cpp<commit_msg>thought<commit_after>// Test.cpp

#include <iostream>
#include <fstream>
#include <time.h>
#include "StabilizerChain.h"

enum Puzzle
{
	Bubbloid3x3x3,
	Rubiks2x2x2,
	Rubiks3x3x3
};

int main( int argc, char** argv )
{
	clock_t t = clock();

	std::cout << "Generating stabilizer chain...\n";

	StabilizerChainGroup* chainGroup = new StabilizerChainGroup();

	Permutation permutation;

	uint* pointArray = nullptr;
	uint pointArraySize = 0;

	const char* fileName = nullptr;

	Puzzle puzzle = Rubiks3x3x3;

	switch( puzzle )
	{
		case Bubbloid3x3x3:
		{
			permutation.DefineIdentity();
			permutation.Define( 0, 4 );
			permutation.Define( 4, 7 );
			permutation.Define( 7, 0 );
			permutation.Define( 12, 13 );
			permutation.Define( 13, 14 );
			permutation.Define( 14, 12 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 1, 5 );
			permutation.Define( 5, 4 );
			permutation.Define( 4, 1 );
			permutation.Define( 15, 16 );
			permutation.Define( 16, 17 );
			permutation.Define( 17, 15 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 2, 6 );
			permutation.Define( 6, 5 );
			permutation.Define( 5, 2 );
			permutation.Define( 18, 19 );
			permutation.Define( 19, 20 );
			permutation.Define( 20, 18 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 3, 7 );
			permutation.Define( 7, 6 );
			permutation.Define( 6, 3 );
			permutation.Define( 21, 22 );
			permutation.Define( 22, 23 );
			permutation.Define( 23, 21 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 0, 11 );
			permutation.Define( 11, 8 );
			permutation.Define( 8, 0 );
			permutation.Define( 24, 25 );
			permutation.Define( 25, 26 );
			permutation.Define( 26, 24 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 1, 8 );
			permutation.Define( 8, 9 );
			permutation.Define( 9, 1 );
			permutation.Define( 27, 28 );
			permutation.Define( 28, 29 );
			permutation.Define( 29, 27 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 2, 9 );
			permutation.Define( 9, 10 );
			permutation.Define( 10, 2 );
			permutation.Define( 30, 31 );
			permutation.Define( 31, 32 );
			permutation.Define( 32, 30 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.Define( 3, 10 );
			permutation.Define( 10, 11 );
			permutation.Define( 11, 3 );
			permutation.Define( 33, 34 );
			permutation.Define( 34, 35 );
			permutation.Define( 35, 33 );
			chainGroup->generatorSet.insert( permutation );

			static uint __pointArray[20] = { 12, 15, 18, 21, 24, 27, 30, 33, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
			uint __pointArraySize = sizeof( __pointArray ) / sizeof( uint );

			pointArray = __pointArray;
			pointArraySize = __pointArraySize;

			fileName = "Bubbloid3x3x3.txt";

			break;
		}
		case Rubiks2x2x2:
		{
			permutation.DefineIdentity();
			permutation.DefineCycle( 0, 1, 2, 3 );
			permutation.DefineCycle( 13, 18, 7, 20 );
			permutation.DefineCycle( 14, 19, 4, 21 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 4, 5, 6, 7 );
			permutation.DefineCycle( 2, 18, 8, 22 );
			permutation.DefineCycle( 1, 17, 11, 21 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 8, 9, 10, 11 );
			permutation.DefineCycle( 5, 16, 15, 22 );
			permutation.DefineCycle( 6, 17, 12, 23 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 12, 13, 14, 15 );
			permutation.DefineCycle( 0, 20, 10, 16 );
			permutation.DefineCycle( 3, 23, 9, 19 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 16, 17, 18, 19 );
			permutation.DefineCycle( 0, 12, 8, 4 );
			permutation.DefineCycle( 1, 13, 9, 5 );
			chainGroup->generatorSet.insert( permutation );

			permutation.DefineIdentity();
			permutation.DefineCycle( 20, 21, 22, 23 );
			permutation.DefineCycle( 2, 6, 10, 14 );
			permutation.DefineCycle( 3, 7, 11, 15 );
			chainGroup->generatorSet.insert( permutation );

			static uint __pointArray[24] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
			uint __pointArraySize = sizeof( __pointArray ) / sizeof( uint );

			pointArray = __pointArray;
			pointArraySize = __pointArraySize;

			fileName = "Rubiks2x2x2.txt";

			break;
		}
		case Rubiks3x3x3:
		{
			// TODO: Unless I can reduce the number of Schreier generators, this
			//       will always run out of memory.  Hmmm...  What if I came up with
			//       my own generators for each stage based on what I know about the 3x3x3?

			// +Y
			permutation.DefineIdentity();
			permutation.DefineCycle( 0, 5, 7, 2 );
			permutation.DefineCycle( 1, 3, 6, 4 );
			permutation.DefineCycle( 8, 16, 24, 32 );
			permutation.DefineCycle( 9, 17, 25, 33 );
			permutation.DefineCycle( 10, 18, 26, 34 );
			chainGroup->generatorSet.insert( permutation );

			// -X
			permutation.DefineIdentity();
			permutation.DefineCycle( 8, 13, 15, 10 );
			permutation.DefineCycle( 9, 11, 14, 12 );
			permutation.DefineCycle( 0, 39, 40, 16 );
			permutation.DefineCycle( 3, 36, 43, 19 );
			permutation.DefineCycle( 5, 34, 45, 21 );
			chainGroup->generatorSet.insert( permutation );

			// +Z
			permutation.DefineIdentity();
			permutation.DefineCycle( 16, 21, 23, 18 );
			permutation.DefineCycle( 17, 19, 22, 20 );
			permutation.DefineCycle( 5, 15, 42, 24 );
			permutation.DefineCycle( 6, 12, 41, 27 );
			permutation.DefineCycle( 7, 10, 40, 29 );
			chainGroup->generatorSet.insert( permutation );

			// +X
			permutation.DefineIdentity();
			permutation.DefineCycle( 24, 29, 31, 26 );
			permutation.DefineCycle( 25, 27, 30, 28 );
			permutation.DefineCycle( 2, 18, 42, 37 );
			permutation.DefineCycle( 4, 20, 44, 35 );
			permutation.DefineCycle( 7, 23, 47, 32 );
			chainGroup->generatorSet.insert( permutation );

			// -Z
			permutation.DefineIdentity();
			permutation.DefineCycle( 32, 37, 39, 24 );
			permutation.DefineCycle( 33, 35, 38, 36 );
			permutation.DefineCycle( 2, 31, 45, 8 );
			permutation.DefineCycle( 1, 28, 46, 11 );
			permutation.DefineCycle( 0, 26, 47, 13 );
			chainGroup->generatorSet.insert( permutation );

			// -Y
			permutation.DefineIdentity();
			permutation.DefineCycle( 40, 45, 47, 42 );
			permutation.DefineCycle( 41, 43, 46, 44 );
			permutation.DefineCycle( 13, 37, 29, 21 );
			permutation.DefineCycle( 14, 38, 30, 22 );
			permutation.DefineCycle( 15, 39, 31, 23 );
			chainGroup->generatorSet.insert( permutation );

			static uint __pointArray[20];

			// Stabilize corners...
			__pointArray[0] = 5;
			__pointArray[1] = 7;
			__pointArray[2] = 15;
			__pointArray[3] = 23;
			__pointArray[4] = 0;
			__pointArray[5] = 2;
			__pointArray[6] = 45;
			__pointArray[7] = 47;

			// Stabilize edges...
			__pointArray[8] = 12;
			__pointArray[9] = 20;
			__pointArray[10] = 17;
			__pointArray[11] = 22;
			__pointArray[12] = 3;
			__pointArray[13] = 4;
			__pointArray[14] = 44;
			__pointArray[15] = 43;
			__pointArray[16] = 35;
			__pointArray[17] = 36;
			__pointArray[18] = 33;
			__pointArray[19] = 38;

			uint __pointArraySize = sizeof( __pointArray ) / sizeof( uint );

			pointArray = __pointArray;
			pointArraySize = __pointArraySize;

			fileName = "Rubiks3x3x3.txt";

			break;
		}
	}

	chainGroup->Generate( pointArray, pointArraySize, 0, false, &std::cout );

	std::cout << "Optimizing stabilizer chain...\n";

	chainGroup->NameGenerators();

	CompressInfo compressInfo;
	chainGroup->MakeCompressInfo( compressInfo );

	chainGroup->Optimize( compressInfo, &std::cout );

	chainGroup->Print( std::cout );

	std::string jsonString;
	chainGroup->SaveToJsonString( jsonString );

	t = clock() - t;
	double elapsed_time = double(t) / double( CLOCKS_PER_SEC );
	std::cout << "Time taken: " << elapsed_time << " sec\n";

	std::fstream fstream;
	fstream.open( fileName, std::fstream::out );
	fstream << jsonString;
	fstream.close();

	getchar();

	delete chainGroup;

	return 0;
}

// Test.cpp<|endoftext|>