<commit_before>/* ===========================
 *
 * Copyright (c) 2013 Philippe Tillet - National Chiao Tung University
 *
 * curveica - Hybrid ICA using ViennaCL + Eigen
 *
 * License : MIT X11 - See the LICENSE file in the root folder
 * ===========================*/

#include "tests/benchmark-utils.hpp"

#include "curveica.h"

#include "umintl/check_grad.hpp"
#include "umintl/minimize.hpp"

#include "src/whiten.hpp"
#include "src/utils.hpp"
#include "src/backend.hpp"

#include "src/nonlinearities/extended_infomax.h"

namespace curveica{

template<class ScalarType, class NonlinearityType>
struct ica_functor{
public:
    ica_functor(ScalarType const * data, std::size_t NF, std::size_t NC) : data_(data), NC_(NC), NF_(NF), nonlinearity_(NC,NF){
        is_first_ = true;

        ipiv_ =  new typename backend<ScalarType>::size_t[NC_+1];
        Z = new ScalarType[NC_*NF_];
        RZ = new ScalarType[NC_*NF_];

        phi = new ScalarType[NC_*NF_];

        psi = new ScalarType[NC_*NC_];
        dweights = new ScalarType[NC_*NC_];

        W = new ScalarType[NC_*NC_];
        WLU = new ScalarType[NC_*NC_];
        V = new ScalarType[NC_*NC_];
        HV = new ScalarType[NC_*NC_];
        WinvV = new ScalarType[NC_*NC_];

        means_logp = new ScalarType[NC_];
        first_signs = new int[NC_];

        for(unsigned int c = 0 ; c < NC_ ; ++c){
            ScalarType m2 = 0, m4 = 0;
            for(unsigned int f = 0; f < NF_ ; f++){
                ScalarType X = data_[c*NF_+f];
                m2 += std::pow(X,2);
                m4 += std::pow(X,4);
            }
            m2 = std::pow(1/(ScalarType)NF_*m2,2);
            m4 = 1/(ScalarType)NF_*m4;
            ScalarType k = m4/m2 - 3;
            first_signs[c] = (k+0.02>0)?1:-1;
        }
    }

    bool recompute_signs(){
        bool sign_change = false;

        for(unsigned int c = 0 ; c < NC_ ; ++c){
            ScalarType m2 = 0, m4 = 0;
            //ScalarType b = b_[c];
            for(unsigned int f = 0; f < NF_ ; f++){
                ScalarType X = Z[c*NF_+f];
                m2 += std::pow(X,2);
                m4 += std::pow(X,4);
            }

            m2 = std::pow(1/(ScalarType)NF_*m2,2);
            m4 = 1/(ScalarType)NF_*m4;
            ScalarType k = m4/m2 - 3;
            int new_sign = (k+0.02>0)?1:-1;
            sign_change |= (new_sign!=first_signs[c]);
            first_signs[c] = new_sign;
        }
        return sign_change;
    }

    ~ica_functor(){
        delete[] ipiv_;

        delete[] Z;
        delete[] RZ;

        delete[] phi;
        delete[] psi;
        delete[] dweights;
        delete[] W;
        delete[] WLU;
        delete[] V;
        delete[] HV;
        delete[] WinvV;

        delete[] means_logp;
    }

    void compute_Hv(ScalarType const * x, ScalarType const * v, ScalarType * Hv) const{
        std::memcpy(W, x,sizeof(ScalarType)*NC_*NC_);
        std::memcpy(WLU,x,sizeof(ScalarType)*NC_*NC_);
        std::memcpy(V, v,sizeof(ScalarType)*NC_*NC_);

        backend<ScalarType>::gemm(NoTrans,NoTrans,NF_,NC_,NC_,1,data_,NF_,W,NC_,0,Z,NF_);
        backend<ScalarType>::gemm(NoTrans,NoTrans,NF_,NC_,NC_,1,data_,NF_,V,NC_,0,RZ,NF_);

        //Psi = dphi(Z).*RZ
        nonlinearity_.compute_dphi(Z,first_signs,psi);
        for(unsigned int c = 0 ; c < NC_ ; ++c)
            for(unsigned int f = 0; f < NF_ ; ++f)
                psi[c*NF_+f] *= RZ[c*NF_+f];

        //HV = (inv(W)*V*inv(w))' + 1/n*Psi*X'
        backend<ScalarType>::getrf(NC_,NC_,WLU,NC_,ipiv_);
        backend<ScalarType>::getri(NC_,WLU,NC_,ipiv_);
        backend<ScalarType>::gemm(Trans,NoTrans,NC_,NC_,NF_ ,1,WLU,NC_,V,NC_,0,WinvV,NC_);
        backend<ScalarType>::gemm(Trans,NoTrans,NC_,NC_,NF_ ,1,WinvV,NC_,WLU,NC_,0,HV,NC_);
        for(std::size_t i = 0 ; i < NC_; ++i)
            for(std::size_t j = 0 ; j <= i; ++j)
                std::swap(HV[i*NC_+j],HV[i*NC_+j]);
        backend<ScalarType>::gemm(Trans,NoTrans,NC_,NC_,NF_ ,1/(ScalarType)NF_,data_,NF_,psi,NF_,1,HV,NC_);

        //Copy back
        for(std::size_t i = 0 ; i < NC_*NC_; ++i)
            Hv[i] = HV[i];
    }

    void operator()(ScalarType const * x, ScalarType* value, ScalarType ** grad) const {
        //Rerolls the variables into the appropriates datastructures
        std::memcpy(W, x,sizeof(ScalarType)*NC_*NC_);
        std::memcpy(WLU,W,sizeof(ScalarType)*NC_*NC_);

        //z1 = W*data_;
        backend<ScalarType>::gemm(NoTrans,NoTrans,NF_,NC_,NC_,1,data_,NF_,W,NC_,0,Z,NF_);

        //phi = mean(mata.*abs(z2).^(mata-1).*sign(z2),2);
        nonlinearity_.compute_means_logp(Z,first_signs,means_logp);

        //LU Decomposition
        backend<ScalarType>::getrf(NC_,NC_,WLU,NC_,ipiv_);

        //det = prod(diag(WLU))
        ScalarType absdet = 1;
        for(std::size_t i = 0 ; i < NC_ ; ++i){
            absdet*=std::abs(WLU[i*NC_+i]);
        }

        //H = log(abs(det(w))) + sum(means_logp);
        ScalarType H = std::log(absdet);
        for(std::size_t i = 0; i < NC_ ; ++i){
            H+=means_logp[i];
        }

        if(value)
            *value = -H;

        if(grad){
            nonlinearity_.compute_phi(Z,first_signs,phi);

            //dweights = W^-T - 1/n*Phi*X'
            backend<ScalarType>::getri(NC_,WLU,NC_,ipiv_);
            for(std::size_t i = 0 ; i < NC_; ++i)
                for(std::size_t j = 0 ; j < NC_; ++j)
                    dweights[i*NC_+j] = WLU[j*NC_+i];
            backend<ScalarType>::gemm(Trans,NoTrans,NC_,NC_,NF_ ,-1/(ScalarType)NF_,data_,NF_,phi,NF_,1,dweights,NC_);

            //Copy back
            for(std::size_t i = 0 ; i < NC_*NC_; ++i)
                (*grad)[i] = -dweights[i];
        }

    }

private:
    ScalarType const * data_;
    int * first_signs;

    std::size_t NC_;
    std::size_t NF_;

    typename backend<ScalarType>::size_t *ipiv_;

    //MiniBatch
    ScalarType* Z;
    ScalarType* RZ;
    ScalarType* phi;

    //Mixing
    ScalarType* psi;
    ScalarType* dweights;
    ScalarType* V;
    ScalarType* HV;
    ScalarType* WinvV;
    ScalarType* W;
    ScalarType* WLU;
    ScalarType* means_logp;

    NonlinearityType nonlinearity_;

    mutable bool is_first_;
};

options make_default_options(){
    options opt;
    opt.max_iter = 200;
    opt.verbosity_level = 2;
    opt.optimization_method = LBFGS;
    return opt;
}

template<class ScalarType>
void inplace_linear_ica(ScalarType const * data, ScalarType * out, std::size_t NC, std::size_t DataNF, options const & opt, double* W, double* S){
    typedef typename umintl_backend<ScalarType>::type BackendType;
    typedef ica_functor<ScalarType, extended_infomax_ica<ScalarType> > IcaFunctorType;

    std::size_t N = NC*NC;
    std::size_t padsize = 4;
    std::size_t NF=(DataNF%padsize==0)?DataNF:(DataNF/padsize)*padsize;

    ScalarType * Sphere = new ScalarType[NC*NC];
    ScalarType * Weights = new ScalarType[NC*NC];
    //ScalarType * b = new ScalarType[NC];
    ScalarType * X = new ScalarType[N];
    std::memset(X,0,N*sizeof(ScalarType));
    ScalarType * white_data = new ScalarType[NC*NF];

    //Optimization Vector

    //Solution vector
    //Initial guess W_0 = I
    //b_0 = 0
    for(unsigned int i = 0 ; i < NC; ++i)
        X[i*(NC+1)] = 1;

    //Whiten Data
    whiten<ScalarType>(NC, DataNF, NF, data,Sphere,white_data);
    detail::shuffle(white_data,NC,NF);
    IcaFunctorType objective(white_data,NF,NC);

    umintl::minimizer<BackendType> minimizer;
    if(opt.optimization_method==SD)
        minimizer.direction = new umintl::steepest_descent<BackendType>();
    else if(opt.optimization_method==LBFGS)
        minimizer.direction = new umintl::quasi_newton<BackendType>(new umintl::lbfgs<BackendType>(16));
    else if(opt.optimization_method==NCG)
        minimizer.direction = new umintl::conjugate_gradient<BackendType>(new umintl::polak_ribiere<BackendType>());
    else if(opt.optimization_method==BFGS)
        minimizer.direction = new umintl::quasi_newton<BackendType>(new umintl::bfgs<BackendType>());
    else if(opt.optimization_method==HESSIAN_FREE){
        minimizer.direction = new umintl::truncated_newton<BackendType>(
                                           umintl::hessian_free::options<BackendType>(30
                                                                             , new umintl::hessian_free::hessian_vector_product_custom<BackendType,IcaFunctorType>(objective)));
    }
    minimizer.verbosity_level = opt.verbosity_level;
    minimizer.max_iter = opt.max_iter;
    minimizer.stopping_criterion = new umintl::gradient_treshold<BackendType>(1e-4);
    do{
        minimizer(X,objective,X,N);
    }while(objective.recompute_signs());

    //Copies into datastructures
    std::memcpy(Weights, X,sizeof(ScalarType)*NC*NC);
    //std::memcpy(b, X+NC*NC, sizeof(ScalarType)*NC);

    //out = W*Sphere*data;
    backend<ScalarType>::gemm(NoTrans,NoTrans,NF,NC,NC,1,data,DataNF,Sphere,NC,0,white_data,NF);
    backend<ScalarType>::gemm(NoTrans,NoTrans,NF,NC,NC,1,white_data,NF,Weights,NC,0,out,NF);

    for(std::size_t i = 0 ; i < NC ; ++i){
        for(std::size_t j = 0 ; j < NC ; ++j){
            if(W)
                W[i*NC+j] = Weights[j*NC+i];
            if(S)
                S[i*NC+j] = Sphere[j*NC+i];
        }
    }

    delete[] Weights;
    //delete[] b;
    delete[] X;
    delete[] white_data;

}

template void inplace_linear_ica<float>(float const * data, float * out, std::size_t NC, std::size_t NF, curveica::options const & opt, double* Weights, double* Sphere);
template void inplace_linear_ica<double>(double const * data, double * out, std::size_t NC, std::size_t NF, curveica::options const & opt, double * Weights, double * Sphere);

}

<commit_msg>Algorithm : Bugfix in Hessian-free vector product<commit_after>/* ===========================
 *
 * Copyright (c) 2013 Philippe Tillet - National Chiao Tung University
 *
 * curveica - Hybrid ICA using ViennaCL + Eigen
 *
 * License : MIT X11 - See the LICENSE file in the root folder
 * ===========================*/

#include "tests/benchmark-utils.hpp"

#include "curveica.h"

#include "umintl/check_grad.hpp"
#include "umintl/minimize.hpp"

#include "src/whiten.hpp"
#include "src/utils.hpp"
#include "src/backend.hpp"

#include "src/nonlinearities/extended_infomax.h"

namespace curveica{

template<class ScalarType, class NonlinearityType>
struct ica_functor{
public:
    ica_functor(ScalarType const * data, std::size_t NF, std::size_t NC) : data_(data), NC_(NC), NF_(NF), nonlinearity_(NC,NF){
        is_first_ = true;

        ipiv_ =  new typename backend<ScalarType>::size_t[NC_+1];
        Z = new ScalarType[NC_*NF_];
        RZ = new ScalarType[NC_*NF_];

        phi = new ScalarType[NC_*NF_];
        psi = new ScalarType[NC_*NF_];

        dweights = new ScalarType[NC_*NC_];

        W = new ScalarType[NC_*NC_];
        WLU = new ScalarType[NC_*NC_];
        V = new ScalarType[NC_*NC_];
        HV = new ScalarType[NC_*NC_];
        WinvV = new ScalarType[NC_*NC_];

        means_logp = new ScalarType[NC_];
        first_signs = new int[NC_];

        for(unsigned int c = 0 ; c < NC_ ; ++c){
            ScalarType m2 = 0, m4 = 0;
            for(unsigned int f = 0; f < NF_ ; f++){
                ScalarType X = data_[c*NF_+f];
                m2 += std::pow(X,2);
                m4 += std::pow(X,4);
            }
            m2 = std::pow(1/(ScalarType)NF_*m2,2);
            m4 = 1/(ScalarType)NF_*m4;
            ScalarType k = m4/m2 - 3;
            first_signs[c] = (k+0.02>0)?1:-1;
        }
    }

    bool recompute_signs(){
        bool sign_change = false;

        for(unsigned int c = 0 ; c < NC_ ; ++c){
            ScalarType m2 = 0, m4 = 0;
            //ScalarType b = b_[c];
            for(unsigned int f = 0; f < NF_ ; f++){
                ScalarType X = Z[c*NF_+f];
                m2 += std::pow(X,2);
                m4 += std::pow(X,4);
            }

            m2 = std::pow(1/(ScalarType)NF_*m2,2);
            m4 = 1/(ScalarType)NF_*m4;
            ScalarType k = m4/m2 - 3;
            int new_sign = (k+0.02>0)?1:-1;
            sign_change |= (new_sign!=first_signs[c]);
            first_signs[c] = new_sign;
        }
        return sign_change;
    }

    ~ica_functor(){
        delete[] ipiv_;

        delete[] Z;
        delete[] RZ;

        delete[] phi;
        delete[] psi;
        delete[] dweights;
        delete[] W;
        delete[] WLU;
        delete[] V;
        delete[] HV;
        delete[] WinvV;

        delete[] means_logp;
    }

    void compute_Hv(ScalarType const * x, ScalarType const * v, ScalarType * Hv) const{
        std::memcpy(W, x,sizeof(ScalarType)*NC_*NC_);
        std::memcpy(WLU,x,sizeof(ScalarType)*NC_*NC_);
        std::memcpy(V, v,sizeof(ScalarType)*NC_*NC_);

        backend<ScalarType>::gemm(NoTrans,NoTrans,NF_,NC_,NC_,1,data_,NF_,W,NC_,0,Z,NF_);
        backend<ScalarType>::gemm(NoTrans,NoTrans,NF_,NC_,NC_,1,data_,NF_,V,NC_,0,RZ,NF_);

        //Psi = dphi(Z).*RZ
        nonlinearity_.compute_dphi(Z,first_signs,psi);
        for(unsigned int c = 0 ; c < NC_ ; ++c)
            for(unsigned int f = 0; f < NF_ ; ++f)
                psi[c*NF_+f] *= RZ[c*NF_+f];

        //HV = (inv(W)*V*inv(w))' + 1/n*Psi*X'
        backend<ScalarType>::getrf(NC_,NC_,WLU,NC_,ipiv_);
        backend<ScalarType>::getri(NC_,WLU,NC_,ipiv_);
        backend<ScalarType>::gemm(Trans,Trans,NC_,NC_,NC_ ,1,WLU,NC_,V,NC_,0,WinvV,NC_);
        backend<ScalarType>::gemm(NoTrans,Trans,NC_,NC_,NC_ ,1,WinvV,NC_,WLU,NC_,0,HV,NC_);

        backend<ScalarType>::gemm(Trans,NoTrans,NC_,NC_,NF_ ,1/(ScalarType)NF_,data_,NF_,psi,NF_,1,HV,NC_);

        //Copy back
        for(std::size_t i = 0 ; i < NC_*NC_; ++i)
            Hv[i] = HV[i];
    }

    void operator()(ScalarType const * x, ScalarType* value, ScalarType ** grad) const {
        //Rerolls the variables into the appropriates datastructures
        std::memcpy(W, x,sizeof(ScalarType)*NC_*NC_);
        std::memcpy(WLU,W,sizeof(ScalarType)*NC_*NC_);

        //z1 = W*data_;
        backend<ScalarType>::gemm(NoTrans,NoTrans,NF_,NC_,NC_,1,data_,NF_,W,NC_,0,Z,NF_);

        //phi = mean(mata.*abs(z2).^(mata-1).*sign(z2),2);
        nonlinearity_.compute_means_logp(Z,first_signs,means_logp);

        //LU Decomposition
        backend<ScalarType>::getrf(NC_,NC_,WLU,NC_,ipiv_);

        //det = prod(diag(WLU))
        ScalarType absdet = 1;
        for(std::size_t i = 0 ; i < NC_ ; ++i){
            absdet*=std::abs(WLU[i*NC_+i]);
        }

        //H = log(abs(det(w))) + sum(means_logp);
        ScalarType H = std::log(absdet);
        for(std::size_t i = 0; i < NC_ ; ++i){
            H+=means_logp[i];
        }

        if(value)
            *value = -H;

        if(grad){
            nonlinearity_.compute_phi(Z,first_signs,phi);

            //dweights = W^-T - 1/n*Phi*X'
            backend<ScalarType>::getri(NC_,WLU,NC_,ipiv_);
            for(std::size_t i = 0 ; i < NC_; ++i)
                for(std::size_t j = 0 ; j < NC_; ++j)
                    dweights[i*NC_+j] = WLU[j*NC_+i];
            backend<ScalarType>::gemm(Trans,NoTrans,NC_,NC_,NF_ ,-1/(ScalarType)NF_,data_,NF_,phi,NF_,1,dweights,NC_);

            //Copy back
            for(std::size_t i = 0 ; i < NC_*NC_; ++i)
                (*grad)[i] = -dweights[i];
        }

    }

private:
    ScalarType const * data_;
    int * first_signs;

    std::size_t NC_;
    std::size_t NF_;

    typename backend<ScalarType>::size_t *ipiv_;

    //MiniBatch
    ScalarType* Z;
    ScalarType* RZ;
    ScalarType* phi;

    //Mixing
    ScalarType* psi;
    ScalarType* dweights;
    ScalarType* V;
    ScalarType* HV;
    ScalarType* WinvV;
    ScalarType* W;
    ScalarType* WLU;
    ScalarType* means_logp;

    NonlinearityType nonlinearity_;

    mutable bool is_first_;
};

options make_default_options(){
    options opt;
    opt.max_iter = 200;
    opt.verbosity_level = 2;
    opt.optimization_method = LBFGS;
    return opt;
}

template<class ScalarType>
void inplace_linear_ica(ScalarType const * data, ScalarType * out, std::size_t NC, std::size_t DataNF, options const & opt, double* W, double* S){
    typedef typename umintl_backend<ScalarType>::type BackendType;
    typedef ica_functor<ScalarType, extended_infomax_ica<ScalarType> > IcaFunctorType;

    std::size_t N = NC*NC;
    std::size_t padsize = 4;
    std::size_t NF=(DataNF%padsize==0)?DataNF:(DataNF/padsize)*padsize;

    ScalarType * Sphere = new ScalarType[NC*NC];
    ScalarType * Weights = new ScalarType[NC*NC];
    //ScalarType * b = new ScalarType[NC];
    ScalarType * X = new ScalarType[N];
    std::memset(X,0,N*sizeof(ScalarType));
    ScalarType * white_data = new ScalarType[NC*NF];

    //Optimization Vector

    //Solution vector
    //Initial guess W_0 = I
    //b_0 = 0
    for(unsigned int i = 0 ; i < NC; ++i)
        X[i*(NC+1)] = 1;

    //Whiten Data
    whiten<ScalarType>(NC, DataNF, NF, data,Sphere,white_data);
    detail::shuffle(white_data,NC,NF);
    IcaFunctorType objective(white_data,NF,NC);

    umintl::minimizer<BackendType> minimizer;
    if(opt.optimization_method==SD)
        minimizer.direction = new umintl::steepest_descent<BackendType>();
    else if(opt.optimization_method==LBFGS)
        minimizer.direction = new umintl::quasi_newton<BackendType>(new umintl::lbfgs<BackendType>(16));
    else if(opt.optimization_method==NCG)
        minimizer.direction = new umintl::conjugate_gradient<BackendType>(new umintl::polak_ribiere<BackendType>());
    else if(opt.optimization_method==BFGS)
        minimizer.direction = new umintl::quasi_newton<BackendType>(new umintl::bfgs<BackendType>());
    else if(opt.optimization_method==HESSIAN_FREE){
        minimizer.direction = new umintl::truncated_newton<BackendType>(
                                           umintl::hessian_free::options<BackendType>(30
                                                                             , new umintl::hessian_free::hessian_vector_product_custom<BackendType,IcaFunctorType>(objective)));
        //minimizer.direction = new umintl::truncated_newton<BackendType>();
    }
    minimizer.verbosity_level = opt.verbosity_level;
    minimizer.max_iter = opt.max_iter;
    minimizer.stopping_criterion = new umintl::gradient_treshold<BackendType>(1e-4);
    do{
        minimizer(X,objective,X,N);
    }while(objective.recompute_signs());

    //Copies into datastructures
    std::memcpy(Weights, X,sizeof(ScalarType)*NC*NC);
    //std::memcpy(b, X+NC*NC, sizeof(ScalarType)*NC);

    //out = W*Sphere*data;
    backend<ScalarType>::gemm(NoTrans,NoTrans,NF,NC,NC,1,data,DataNF,Sphere,NC,0,white_data,NF);
    backend<ScalarType>::gemm(NoTrans,NoTrans,NF,NC,NC,1,white_data,NF,Weights,NC,0,out,NF);

    for(std::size_t i = 0 ; i < NC ; ++i){
        for(std::size_t j = 0 ; j < NC ; ++j){
            if(W)
                W[i*NC+j] = Weights[j*NC+i];
            if(S)
                S[i*NC+j] = Sphere[j*NC+i];
        }
    }

    delete[] Weights;
    //delete[] b;
    delete[] X;
    delete[] white_data;

}

template void inplace_linear_ica<float>(float const * data, float * out, std::size_t NC, std::size_t NF, curveica::options const & opt, double* Weights, double* Sphere);
template void inplace_linear_ica<double>(double const * data, double * out, std::size_t NC, std::size_t NF, curveica::options const & opt, double * Weights, double * Sphere);

}

<|endoftext|>


<commit_before>#include <limits>
#include <algorithm>
#include <tr1/array>
#include <cstdlib>
#include "conversions.h"
#include "chars.h"
#include "ustringpiece.h"
#include "jsarray.h"
#include "jsobject.h"
#include "property.h"
#include "jsstring.h"
#include "context.h"
#include "class.h"

namespace iv {
namespace lv5 {

JSArray::JSArray(Context* ctx, std::size_t len)
  : JSObject(),
    vector_((len < detail::kMaxVectorSize) ? len : 4, JSEmpty),
    map_(NULL),
    dense_(true),
    length_(len) {
  JSObject::DefineOwnProperty(ctx, ctx->length_symbol(),
                              DataDescriptor(len,
                                             PropertyDescriptor::WRITABLE),
                                             false, ctx->error());
}

PropertyDescriptor JSArray::GetOwnProperty(Context* ctx, Symbol name) const {
  uint32_t index;
  if (core::ConvertToUInt32(ctx->GetContent(name), &index)) {
    return GetOwnPropertyWithIndex(ctx, index);
  }
  return JSObject::GetOwnProperty(ctx, name);
}

PropertyDescriptor JSArray::GetOwnPropertyWithIndex(Context* ctx,
                                                    uint32_t index) const {
  if (detail::kMaxVectorSize > index) {
    // this target included in vector (if dense array)
    if (vector_.size() > index) {
      const JSVal& val = vector_[index];
      if (!val.IsEmpty()) {
        // current is target
        return DataDescriptor(val,
                              PropertyDescriptor::ENUMERABLE |
                              PropertyDescriptor::CONFIGURABLE |
                              PropertyDescriptor::WRITABLE);
      } else if (dense_) {
        // if dense array, target is undefined
        return JSUndefined;
      }
    }
  } else {
    // target is index and included in map
    if (map_) {
      const Map::const_iterator it = map_->find(index);
      if (it != map_->end()) {
        // target is found
        return DataDescriptor(it->second,
                              PropertyDescriptor::ENUMERABLE |
                              PropertyDescriptor::CONFIGURABLE |
                              PropertyDescriptor::WRITABLE);
      } else if (dense_) {
        // if target is not found and dense array,
        // target is undefined
        return JSUndefined;
      }
    } else if (dense_) {
      // if map is none and dense array, target is undefined
      return JSUndefined;
    }
  }
  return JSObject::GetOwnProperty(ctx, ctx->InternIndex(index));
}

#define REJECT(str)\
  do {\
    if (th) {\
      res->Report(Error::Type, str);\
    }\
    return false;\
  } while (0)

bool JSArray::DefineOwnProperty(Context* ctx,
                                Symbol name,
                                const PropertyDescriptor& desc,
                                bool th,
                                Error* res) {
  uint32_t index;
  if (core::ConvertToUInt32(ctx->GetContent(name), &index)) {
    return DefineOwnPropertyWithIndex(ctx, index, desc, th, res);
  }

  const Symbol length_symbol = ctx->length_symbol();
  PropertyDescriptor old_len_desc_prop = GetOwnProperty(ctx, length_symbol);
  DataDescriptor* const old_len_desc = old_len_desc_prop.AsDataDescriptor();
  const JSVal& len_value = old_len_desc->value();
  assert(len_value.IsNumber());

  if (name == length_symbol) {
    if (desc.IsDataDescriptor()) {
      const DataDescriptor* const data = desc.AsDataDescriptor();
      if (data->IsValueAbsent()) {
        // changing attribute [[Writable]] or TypeError.
        // [[Value]] not changed.
        return JSObject::DefineOwnProperty(ctx, name, desc, th, res);
      }
      DataDescriptor new_len_desc(*data);
      const double new_len_double = new_len_desc.value().ToNumber(ctx, res);
      if (*res) {
        return false;
      }
      // length must be uint32_t
      const uint32_t new_len = core::DoubleToUInt32(new_len_double);
      if (new_len != new_len_double) {
        res->Report(Error::Range, "out of range");
        return false;
      }
      new_len_desc.set_value(new_len);
      uint32_t old_len = core::DoubleToUInt32(len_value.number());
      if (*res) {
        return false;
      }
      if (new_len >= old_len) {
        return JSObject::DefineOwnProperty(ctx, length_symbol,
                                           new_len_desc, th, res);
      }
      if (!old_len_desc->IsWritable()) {
        REJECT("\"length\" not writable");
      }
      const bool new_writable =
          new_len_desc.IsWritableAbsent() || new_len_desc.IsWritable();
      new_len_desc.set_writable(true);
      const bool succeeded = JSObject::DefineOwnProperty(ctx, length_symbol,
                                                         new_len_desc, th, res);
      if (!succeeded) {
        return false;
      }

      if (new_len < old_len) {
        if (dense_) {
          // dense array version
          CompactionToLength(new_len);
        } else if (old_len - new_len < (1 << 24)) {
          while (new_len < old_len) {
            old_len -= 1;
            // see Eratta
            const bool delete_succeeded = DeleteWithIndex(ctx, old_len, false, res);
            if (*res) {
              return false;
            }
            if (!delete_succeeded) {
              new_len_desc.set_value(old_len + 1);
              if (!new_writable) {
                new_len_desc.set_writable(false);
              }
              JSObject::DefineOwnProperty(ctx, length_symbol,
                                          new_len_desc, false, res);
              if (*res) {
                return false;
              }
              REJECT("shrink array failed");
            }
          }
        } else {
          using std::sort;
          std::vector<Symbol> keys;
          JSObject::GetOwnPropertyNames(ctx, &keys, JSObject::kIncludeNotEnumerable);
          std::vector<uint32_t> ix;
          for (std::vector<Symbol>::const_iterator it = keys.begin(),
               last = keys.end(); it != last; ++it) {
            uint32_t index;
            if (core::ConvertToUInt32(ctx->GetContent(*it), &index)) {
              ix.push_back(index);
            }
          }
          sort(ix.begin(), ix.end());
          for (std::vector<uint32_t>::const_reverse_iterator it = ix.rbegin(),
               last = ix.rend(); it != last; --it) {
            if (*it < new_len) {
              break;
            }
            const bool delete_succeeded = DeleteWithIndex(ctx, *it, false, res);
            if (!delete_succeeded) {
              const uint32_t result_len = *it + 1;
              CompactionToLength(result_len);
              new_len_desc.set_value(result_len);
              if (!new_writable) {
                new_len_desc.set_writable(false);
              }
              JSObject::DefineOwnProperty(ctx, length_symbol,
                                          new_len_desc, false, res);
              if (*res) {
                return false;
              }
              REJECT("shrink array failed");
            }
          }
          CompactionToLength(new_len);
        }
      }
      if (!new_writable) {
        JSObject::DefineOwnProperty(ctx, length_symbol,
                                    DataDescriptor(
                                        PropertyDescriptor::WRITABLE |
                                        PropertyDescriptor::UNDEF_ENUMERABLE |
                                        PropertyDescriptor::UNDEF_CONFIGURABLE),
                                    false, res);
      }
      return true;
    } else {
      // length is not configurable
      // so length is not changed
      return JSObject::DefineOwnProperty(ctx, name, desc, th, res);
    }
  } else {
    return JSObject::DefineOwnProperty(ctx, name, desc, th, res);
  }
}

bool JSArray::DefineOwnPropertyWithIndex(Context* ctx,
                                         uint32_t index,
                                         const PropertyDescriptor& desc,
                                         bool th,
                                         Error* res) {
  // array index
  PropertyDescriptor old_len_desc_prop = GetOwnProperty(ctx,
                                                        ctx->length_symbol());
  DataDescriptor* const old_len_desc = old_len_desc_prop.AsDataDescriptor();
  const double old_len = old_len_desc->value().ToNumber(ctx, res);
  if (*res) {
    return false;
  }
  if (index >= old_len && !old_len_desc->IsWritable()) {
    return false;
  }

  // define step
  const bool descriptor_is_default_property = IsDefaultDescriptor(desc);
  if (descriptor_is_default_property &&
      (dense_ || JSObject::GetOwnProperty(ctx, ctx->InternIndex(index)).IsEmpty())) {
    JSVal target;
    if (desc.IsDataDescriptor()) {
      target = desc.AsDataDescriptor()->value();
    } else {
      target = JSUndefined;
    }
    if (detail::kMaxVectorSize > index) {
      if (vector_.size() > index) {
        vector_[index] = target;
      } else {
        vector_.resize(index + 1, JSEmpty);
        vector_[index] = target;
      }
    } else {
      if (!map_) {
        map_ = new (GC) Map();
      }
      (*map_)[index] = target;
    }
  } else {
    const bool succeeded = JSObject::DefineOwnProperty(
        ctx, ctx->InternIndex(index), desc, false, res);
    if (*res) {
      return false;
    }

    if (succeeded) {
      dense_ = false;
      if (detail::kMaxVectorSize > index) {
        if (vector_.size() > index) {
          vector_[index] = JSEmpty;
        }
      } else {
        if (map_) {
          const Map::iterator it = map_->find(index);
          if (it != map_->end()) {
            map_->erase(it);
          }
        }
      }
    } else {
      REJECT("define own property failed");
    }
  }
  if (index >= old_len) {
    old_len_desc->set_value(index+1);
    JSObject::DefineOwnProperty(ctx, ctx->length_symbol(),
                                *old_len_desc, false, res);
  }
  return true;
}

#undef REJECT

bool JSArray::Delete(Context* ctx, Symbol name, bool th, Error* res) {
  uint32_t index;
  if (core::ConvertToUInt32(ctx->GetContent(name), &index)) {
    return DeleteWithIndex(ctx, index, th, res);
  }
  return JSObject::Delete(ctx, name, th, res);
}

bool JSArray::DeleteWithIndex(Context* ctx, uint32_t index, bool th, Error* res) {
  if (detail::kMaxVectorSize > index) {
    if (vector_.size() > index) {
      JSVal& val = vector_[index];
      if (!val.IsEmpty()) {
        val = JSEmpty;
        return true;
      } else if (dense_) {
        return true;
      }
    }
  } else {
    if (map_) {
      const Map::iterator it = map_->find(index);
      if (it != map_->end()) {
        map_->erase(it);
        return true;
      } else if (dense_) {
        return true;
      }
    } else if (dense_) {
      return true;
    }
  }
  return JSObject::Delete(ctx, ctx->InternIndex(index), th, res);
}

void JSArray::GetOwnPropertyNames(Context* ctx,
                                  std::vector<Symbol>* vec,
                                  EnumerationMode mode) const {
  using std::find;
  if (vec->empty()) {
    uint32_t index = 0;
    for (Vector::const_iterator it = vector_.begin(),
         last = vector_.end(); it != last; ++it, ++index) {
      if (!it->IsEmpty()) {
        vec->push_back(ctx->InternIndex(index));
      }
    }
    if (map_) {
      for (Map::const_iterator it = map_->begin(),
           last = map_->end(); it != last; ++it) {
        if (!it->second.IsEmpty()) {
          vec->push_back(ctx->InternIndex(it->first));
        }
      }
    }
  } else {
    uint32_t index = 0;
    for (Vector::const_iterator it = vector_.begin(),
         last = vector_.end(); it != last; ++it, ++index) {
      if (!it->IsEmpty()) {
        const Symbol sym = ctx->InternIndex(index);
        if (find(vec->begin(), vec->end(), sym) == vec->end()) {
          vec->push_back(sym);
        }
      }
    }
    if (map_) {
      for (Map::const_iterator it = map_->begin(),
           last = map_->end(); it != last; ++it) {
        if (!it->second.IsEmpty()) {
          const Symbol sym = ctx->InternIndex(it->first);
          if (find(vec->begin(), vec->end(), sym) == vec->end()) {
            vec->push_back(sym);
          }
        }
      }
    }
  }
  JSObject::GetOwnPropertyNames(ctx, vec, mode);
}

JSArray* JSArray::New(Context* ctx) {
  JSArray* const ary = new JSArray(ctx, 0);
  const Class& cls = ctx->Cls("Array");
  ary->set_class_name(cls.name);
  ary->set_prototype(cls.prototype);
  return ary;
}

JSArray* JSArray::New(Context* ctx, std::size_t n) {
  JSArray* const ary = new JSArray(ctx, n);
  const Class& cls = ctx->Cls("Array");
  ary->set_class_name(cls.name);
  ary->set_prototype(cls.prototype);
  return ary;
}

JSArray* JSArray::NewPlain(Context* ctx) {
  return new JSArray(ctx, 0);
}

bool JSArray::IsDefaultDescriptor(const PropertyDescriptor& desc) {
  if (!(desc.IsEnumerable() || desc.IsEnumerableAbsent())) {
    return false;
  }
  if (!(desc.IsConfigurable() || desc.IsConfigurableAbsent())) {
    return false;
  }
  if (desc.IsAccessorDescriptor()) {
    return false;
  }
  if (desc.IsDataDescriptor()) {
    const DataDescriptor* const data = desc.AsDataDescriptor();
    return data->IsWritable() || data->IsWritableAbsent();
  }
  return true;
}

void JSArray::CompactionToLength(uint32_t length) {
  if (length > detail::kMaxVectorSize) {
    if (map_) {
      map_->erase(
          map_->upper_bound(length - 1), map_->end());
    }
  } else {
    if (map_) {
      map_->clear();
    }
    if (vector_.size() > length) {
      vector_.resize(length, JSEmpty);
    }
  }
}

} }  // namespace iv::lv5
<commit_msg>change vector to set<commit_after>#include <limits>
#include <vector>
#include <set>
#include <algorithm>
#include <tr1/array>
#include <cstdlib>
#include "conversions.h"
#include "chars.h"
#include "ustringpiece.h"
#include "jsarray.h"
#include "jsobject.h"
#include "property.h"
#include "jsstring.h"
#include "context.h"
#include "class.h"

namespace iv {
namespace lv5 {

JSArray::JSArray(Context* ctx, std::size_t len)
  : JSObject(),
    vector_((len < detail::kMaxVectorSize) ? len : 4, JSEmpty),
    map_(NULL),
    dense_(true),
    length_(len) {
  JSObject::DefineOwnProperty(ctx, ctx->length_symbol(),
                              DataDescriptor(len,
                                             PropertyDescriptor::WRITABLE),
                                             false, ctx->error());
}

PropertyDescriptor JSArray::GetOwnProperty(Context* ctx, Symbol name) const {
  uint32_t index;
  if (core::ConvertToUInt32(ctx->GetContent(name), &index)) {
    return GetOwnPropertyWithIndex(ctx, index);
  }
  return JSObject::GetOwnProperty(ctx, name);
}

PropertyDescriptor JSArray::GetOwnPropertyWithIndex(Context* ctx,
                                                    uint32_t index) const {
  if (detail::kMaxVectorSize > index) {
    // this target included in vector (if dense array)
    if (vector_.size() > index) {
      const JSVal& val = vector_[index];
      if (!val.IsEmpty()) {
        // current is target
        return DataDescriptor(val,
                              PropertyDescriptor::ENUMERABLE |
                              PropertyDescriptor::CONFIGURABLE |
                              PropertyDescriptor::WRITABLE);
      } else if (dense_) {
        // if dense array, target is undefined
        return JSUndefined;
      }
    }
  } else {
    // target is index and included in map
    if (map_) {
      const Map::const_iterator it = map_->find(index);
      if (it != map_->end()) {
        // target is found
        return DataDescriptor(it->second,
                              PropertyDescriptor::ENUMERABLE |
                              PropertyDescriptor::CONFIGURABLE |
                              PropertyDescriptor::WRITABLE);
      } else if (dense_) {
        // if target is not found and dense array,
        // target is undefined
        return JSUndefined;
      }
    } else if (dense_) {
      // if map is none and dense array, target is undefined
      return JSUndefined;
    }
  }
  return JSObject::GetOwnProperty(ctx, ctx->InternIndex(index));
}

#define REJECT(str)\
  do {\
    if (th) {\
      res->Report(Error::Type, str);\
    }\
    return false;\
  } while (0)

bool JSArray::DefineOwnProperty(Context* ctx,
                                Symbol name,
                                const PropertyDescriptor& desc,
                                bool th,
                                Error* res) {
  uint32_t index;
  if (core::ConvertToUInt32(ctx->GetContent(name), &index)) {
    return DefineOwnPropertyWithIndex(ctx, index, desc, th, res);
  }

  const Symbol length_symbol = ctx->length_symbol();
  PropertyDescriptor old_len_desc_prop = GetOwnProperty(ctx, length_symbol);
  DataDescriptor* const old_len_desc = old_len_desc_prop.AsDataDescriptor();
  const JSVal& len_value = old_len_desc->value();
  assert(len_value.IsNumber());

  if (name == length_symbol) {
    if (desc.IsDataDescriptor()) {
      const DataDescriptor* const data = desc.AsDataDescriptor();
      if (data->IsValueAbsent()) {
        // changing attribute [[Writable]] or TypeError.
        // [[Value]] not changed.
        return JSObject::DefineOwnProperty(ctx, name, desc, th, res);
      }
      DataDescriptor new_len_desc(*data);
      const double new_len_double = new_len_desc.value().ToNumber(ctx, res);
      if (*res) {
        return false;
      }
      // length must be uint32_t
      const uint32_t new_len = core::DoubleToUInt32(new_len_double);
      if (new_len != new_len_double) {
        res->Report(Error::Range, "out of range");
        return false;
      }
      new_len_desc.set_value(new_len);
      uint32_t old_len = core::DoubleToUInt32(len_value.number());
      if (*res) {
        return false;
      }
      if (new_len >= old_len) {
        return JSObject::DefineOwnProperty(ctx, length_symbol,
                                           new_len_desc, th, res);
      }
      if (!old_len_desc->IsWritable()) {
        REJECT("\"length\" not writable");
      }
      const bool new_writable =
          new_len_desc.IsWritableAbsent() || new_len_desc.IsWritable();
      new_len_desc.set_writable(true);
      const bool succeeded = JSObject::DefineOwnProperty(ctx, length_symbol,
                                                         new_len_desc, th, res);
      if (!succeeded) {
        return false;
      }

      if (new_len < old_len) {
        if (dense_) {
          // dense array version
          CompactionToLength(new_len);
        } else if (old_len - new_len < (1 << 24)) {
          while (new_len < old_len) {
            old_len -= 1;
            // see Eratta
            const bool delete_succeeded = DeleteWithIndex(ctx, old_len, false, res);
            if (*res) {
              return false;
            }
            if (!delete_succeeded) {
              new_len_desc.set_value(old_len + 1);
              if (!new_writable) {
                new_len_desc.set_writable(false);
              }
              JSObject::DefineOwnProperty(ctx, length_symbol,
                                          new_len_desc, false, res);
              if (*res) {
                return false;
              }
              REJECT("shrink array failed");
            }
          }
        } else {
          std::vector<Symbol> keys;
          JSObject::GetOwnPropertyNames(ctx, &keys, JSObject::kIncludeNotEnumerable);
          std::set<uint32_t> ix;
          for (std::vector<Symbol>::const_iterator it = keys.begin(),
               last = keys.end(); it != last; ++it) {
            uint32_t index;
            if (core::ConvertToUInt32(ctx->GetContent(*it), &index)) {
              ix.insert(index);
            }
          }
          for (std::set<uint32_t>::const_reverse_iterator it = ix.rbegin(),
               last = ix.rend(); it != last; --it) {
            if (*it < new_len) {
              break;
            }
            const bool delete_succeeded = DeleteWithIndex(ctx, *it, false, res);
            if (!delete_succeeded) {
              const uint32_t result_len = *it + 1;
              CompactionToLength(result_len);
              new_len_desc.set_value(result_len);
              if (!new_writable) {
                new_len_desc.set_writable(false);
              }
              JSObject::DefineOwnProperty(ctx, length_symbol,
                                          new_len_desc, false, res);
              if (*res) {
                return false;
              }
              REJECT("shrink array failed");
            }
          }
          CompactionToLength(new_len);
        }
      }
      if (!new_writable) {
        JSObject::DefineOwnProperty(ctx, length_symbol,
                                    DataDescriptor(
                                        PropertyDescriptor::WRITABLE |
                                        PropertyDescriptor::UNDEF_ENUMERABLE |
                                        PropertyDescriptor::UNDEF_CONFIGURABLE),
                                    false, res);
      }
      return true;
    } else {
      // length is not configurable
      // so length is not changed
      return JSObject::DefineOwnProperty(ctx, name, desc, th, res);
    }
  } else {
    return JSObject::DefineOwnProperty(ctx, name, desc, th, res);
  }
}

bool JSArray::DefineOwnPropertyWithIndex(Context* ctx,
                                         uint32_t index,
                                         const PropertyDescriptor& desc,
                                         bool th,
                                         Error* res) {
  // array index
  PropertyDescriptor old_len_desc_prop = GetOwnProperty(ctx,
                                                        ctx->length_symbol());
  DataDescriptor* const old_len_desc = old_len_desc_prop.AsDataDescriptor();
  const double old_len = old_len_desc->value().ToNumber(ctx, res);
  if (*res) {
    return false;
  }
  if (index >= old_len && !old_len_desc->IsWritable()) {
    return false;
  }

  // define step
  const bool descriptor_is_default_property = IsDefaultDescriptor(desc);
  if (descriptor_is_default_property &&
      (dense_ || JSObject::GetOwnProperty(ctx, ctx->InternIndex(index)).IsEmpty())) {
    JSVal target;
    if (desc.IsDataDescriptor()) {
      target = desc.AsDataDescriptor()->value();
    } else {
      target = JSUndefined;
    }
    if (detail::kMaxVectorSize > index) {
      if (vector_.size() > index) {
        vector_[index] = target;
      } else {
        vector_.resize(index + 1, JSEmpty);
        vector_[index] = target;
      }
    } else {
      if (!map_) {
        map_ = new (GC) Map();
      }
      (*map_)[index] = target;
    }
  } else {
    const bool succeeded = JSObject::DefineOwnProperty(
        ctx, ctx->InternIndex(index), desc, false, res);
    if (*res) {
      return false;
    }

    if (succeeded) {
      dense_ = false;
      if (detail::kMaxVectorSize > index) {
        if (vector_.size() > index) {
          vector_[index] = JSEmpty;
        }
      } else {
        if (map_) {
          const Map::iterator it = map_->find(index);
          if (it != map_->end()) {
            map_->erase(it);
          }
        }
      }
    } else {
      REJECT("define own property failed");
    }
  }
  if (index >= old_len) {
    old_len_desc->set_value(index+1);
    JSObject::DefineOwnProperty(ctx, ctx->length_symbol(),
                                *old_len_desc, false, res);
  }
  return true;
}

#undef REJECT

bool JSArray::Delete(Context* ctx, Symbol name, bool th, Error* res) {
  uint32_t index;
  if (core::ConvertToUInt32(ctx->GetContent(name), &index)) {
    return DeleteWithIndex(ctx, index, th, res);
  }
  return JSObject::Delete(ctx, name, th, res);
}

bool JSArray::DeleteWithIndex(Context* ctx, uint32_t index, bool th, Error* res) {
  if (detail::kMaxVectorSize > index) {
    if (vector_.size() > index) {
      JSVal& val = vector_[index];
      if (!val.IsEmpty()) {
        val = JSEmpty;
        return true;
      } else if (dense_) {
        return true;
      }
    }
  } else {
    if (map_) {
      const Map::iterator it = map_->find(index);
      if (it != map_->end()) {
        map_->erase(it);
        return true;
      } else if (dense_) {
        return true;
      }
    } else if (dense_) {
      return true;
    }
  }
  return JSObject::Delete(ctx, ctx->InternIndex(index), th, res);
}

void JSArray::GetOwnPropertyNames(Context* ctx,
                                  std::vector<Symbol>* vec,
                                  EnumerationMode mode) const {
  using std::find;
  if (vec->empty()) {
    uint32_t index = 0;
    for (Vector::const_iterator it = vector_.begin(),
         last = vector_.end(); it != last; ++it, ++index) {
      if (!it->IsEmpty()) {
        vec->push_back(ctx->InternIndex(index));
      }
    }
    if (map_) {
      for (Map::const_iterator it = map_->begin(),
           last = map_->end(); it != last; ++it) {
        if (!it->second.IsEmpty()) {
          vec->push_back(ctx->InternIndex(it->first));
        }
      }
    }
  } else {
    uint32_t index = 0;
    for (Vector::const_iterator it = vector_.begin(),
         last = vector_.end(); it != last; ++it, ++index) {
      if (!it->IsEmpty()) {
        const Symbol sym = ctx->InternIndex(index);
        if (find(vec->begin(), vec->end(), sym) == vec->end()) {
          vec->push_back(sym);
        }
      }
    }
    if (map_) {
      for (Map::const_iterator it = map_->begin(),
           last = map_->end(); it != last; ++it) {
        if (!it->second.IsEmpty()) {
          const Symbol sym = ctx->InternIndex(it->first);
          if (find(vec->begin(), vec->end(), sym) == vec->end()) {
            vec->push_back(sym);
          }
        }
      }
    }
  }
  JSObject::GetOwnPropertyNames(ctx, vec, mode);
}

JSArray* JSArray::New(Context* ctx) {
  JSArray* const ary = new JSArray(ctx, 0);
  const Class& cls = ctx->Cls("Array");
  ary->set_class_name(cls.name);
  ary->set_prototype(cls.prototype);
  return ary;
}

JSArray* JSArray::New(Context* ctx, std::size_t n) {
  JSArray* const ary = new JSArray(ctx, n);
  const Class& cls = ctx->Cls("Array");
  ary->set_class_name(cls.name);
  ary->set_prototype(cls.prototype);
  return ary;
}

JSArray* JSArray::NewPlain(Context* ctx) {
  return new JSArray(ctx, 0);
}

bool JSArray::IsDefaultDescriptor(const PropertyDescriptor& desc) {
  if (!(desc.IsEnumerable() || desc.IsEnumerableAbsent())) {
    return false;
  }
  if (!(desc.IsConfigurable() || desc.IsConfigurableAbsent())) {
    return false;
  }
  if (desc.IsAccessorDescriptor()) {
    return false;
  }
  if (desc.IsDataDescriptor()) {
    const DataDescriptor* const data = desc.AsDataDescriptor();
    return data->IsWritable() || data->IsWritableAbsent();
  }
  return true;
}

void JSArray::CompactionToLength(uint32_t length) {
  if (length > detail::kMaxVectorSize) {
    if (map_) {
      map_->erase(
          map_->upper_bound(length - 1), map_->end());
    }
  } else {
    if (map_) {
      map_->clear();
    }
    if (vector_.size() > length) {
      vector_.resize(length, JSEmpty);
    }
  }
}

} }  // namespace iv::lv5
<|endoftext|>


<commit_before>#include <main.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>

#include <Modules.h>
#include <IRCSock.h>
#include <User.h>
#include <Nick.h>
#include <Chan.h>

class CMailer : public CModule {

protected:
    CUser *user;

public:

    MODCONSTRUCTOR(CMailer) {
        user = GetUser();
    }

    virtual ~CMailer() {}

    virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {

        CString user_nick = user->GetNick().AsLower();

        if (user->IsUserAttached()){
            return CONTINUE;
        }

        size_t found = sMessage.AsLower().find(user_nick);

        if (found!=string::npos){

            PutModule("Sending");

            FILE *email= popen("mail -s 'IRC Notification' dougal85@gmail.com", "w");

            if (!email){
                PutModule("Problems with pipe");
                return CONTINUE;
            }

            CString message = "<" + Nick.GetNick() + "> " + sMessage + "\n\n";
            PutModule(message);
            fprintf(email, "%s", (char*) message.c_str());
            pclose(email);

            PutModule("Sent");

        }

        return CONTINUE;
    }

    bool send_notification(){
        return false;

    }

};

MODULEDEFS(CMailer, "To be used to send mentions as an email")
<commit_msg>Refactored the code to make it a bit easier to work with while adding basic rate limiting and the ability to change some of the settings<commit_after>#include <main.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <list>

#include <Modules.h>
#include <IRCSock.h>
#include <User.h>
#include <Nick.h>
#include <Chan.h>

class CMailerTimer: public CTimer {
public:

    CMailerTimer(CModule* pModule, unsigned int uInterval, unsigned int uCycles, const CString& sLabel, const CString& sDescription) : CTimer(pModule, uInterval, uCycles, sLabel, sDescription) {}
    virtual ~CMailerTimer() {}

protected:
    virtual void RunJob();

};

class CMailer : public CModule {

protected:
    CUser *user;
    list<CString> messages_list;
    CString notification_subject;
    CString notification_email;
    unsigned int max_notifications;

public:

    MODCONSTRUCTOR(CMailer) {
        user = GetUser();
    }

    virtual ~CMailer() {}

    virtual bool OnLoad(const CString& sArgs, CString& sErrorMsg) {
        AddTimer(new CMailerTimer(this, 1200, 0, "Mail", "Send emails every 20 mins."));
        this->max_notifications = 50;
        PutModule("Please tell me what email address you want notifications to be sent to with 'email <address>'");
        return true;

    }

    virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) {

        Handle(Nick, Channel.GetName(), sMessage);

        return CONTINUE;
    }

    virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage) {

        Handle(Nick, "PRIVATE", sMessage);

        return CONTINUE;
    }

    virtual void OnIRCConnected() {
        PutModule("Connected");
    }

    void OnModCommand(const CString& command)
    {
        VCString tokens;
        int token_count = command.Split(" ", tokens, false);

        if (token_count < 1)
        {
            return;
        }

        CString action = tokens[0].AsLower();

        if (action == "email"){

            if (token_count < 2)
            {
                PutModule("Usage: email <email address>");
                return;
            }

            this->notification_email = tokens[1].AsLower();

            PutModule("email address set");

        } else if (action == "subject"){

            if (token_count < 2)
            {
                PutModule("Usage: subject <email subject>");
                return;
            }

            this->notification_subject = tokens[1].AsLower();

            PutModule("email subject set.");
        } else if (action == "help") {

                PutModule("View the documentation at https://github.com/d0ugal/znc_mailer/blob/master/README");

        } else {

            PutModule("Error: invalid command, try `help`");

        }

    }

    void Handle(CNick& Nick, const CString& location, CString& sMessage){

        if (SendNotification(Nick, sMessage) || location == "PRIVATE"){

            CString message = "<" + location + ":" + Nick.GetNick() + "> " + sMessage + "\n\n";
            messages_list.push_front(message);

            if (messages_list.size() > this->max_notifications){
                messages_list.pop_back();
            }

        }
    }

    bool SendNotification(CNick& Nick, CString& sMessage){

        if (user->IsUserAttached()){
            return false;
        }

        CString UserNick = user->GetNick().AsLower();

        size_t found = sMessage.AsLower().find(UserNick);

        if (found!=string::npos){
            return true;
        } else {
            return false;
        }

    }

    void BatchSend(){

        if (user->IsUserAttached()){
            return;
        }

        list<CString>::iterator it;

        CString message;

        if (messages_list.size() <= 0){
            return;
        }

        for ( it=messages_list.begin() ; it != messages_list.end(); it++ ){
            message = message + *it;
        }

        Send(message);

        messages_list.erase(messages_list.begin(),messages_list.end());

    }

    void Send(CString& sMessage){

        PutModule("Sending");

        FILE *email= popen(("mail -s '" + this->notification_email + "' " + this->notification_subject).c_str(), "w");

        if (!email){
            PutModule("Problems with pipe");
            return;
        }

        fprintf(email, "%s", (char*) sMessage.c_str());
        pclose(email);

        PutModule("Sent");

    }

};

void CMailerTimer::RunJob(){

    CMailer *p = (CMailer *)m_pModule;

    p->BatchSend();

}

MODULEDEFS(CMailer, "To be used to send mentions as an email")
<|endoftext|>


<commit_before>#include "defs.h"
#include "search.h"
#include "eval.h"
#include "movegen.h"
#include "transptable.h"
#include <string>
#include <algorithm>
#include <time.h>
#include <iostream>

Search::Search(const Board& board, bool logUci) {
  _logUci = logUci;
  _board = board;
}

void Search::perform(int depth) {
  _rootMax(_board, depth);

  MoveBoardList pv = _getPv(_board);
  if (_logUci) {
    _logUciInfo(pv, depth, _bestMove, _bestScore);
  }
}

void Search::_logUciInfo(const MoveBoardList& pv, int depth, CMove bestMove, int bestScore) {
  std::string pvString;
  for(auto moveBoard : pv) {
    pvString += moveBoard.first.getNotation() + " ";
  }

  std::string scoreString;
  if (bestScore == INF) {
    scoreString = "mate " + std::to_string(pv.size());
  } else if (_bestScore == -INF) {
    scoreString = "mate -" + std::to_string(pv.size());
  } else {
    scoreString = "cp " + std::to_string(bestScore);
  }

  std::cout << "info depth " + std::to_string(depth) + " ";
  std::cout << "score " + scoreString + " ";
  std::cout << "pv " + pvString;
  std::cout << std::endl;
}

MoveBoardList Search::_getPv(const Board& board) {
  int bestScore = INF;
  MoveBoard best;
  bool foundBest = false;
  for (auto moveBoard : MoveGen(board).getLegalMoves()) {
    Board movedBoard = moveBoard.second;

    ZKey movedZKey = movedBoard.getZKey();

    if (_tt.contains(movedZKey) && _tt.getFlag(movedZKey) == TranspTable::EXACT && _tt.getScore(movedZKey) < bestScore) {
      foundBest = true;
      bestScore = _tt.getScore(movedZKey);
      best = moveBoard;
    }
  }

  if (!foundBest) {
    return MoveBoardList();
  } else {
    MoveBoardList pvList = _getPv(best.second);
    pvList.insert(pvList.begin(), best);
    return pvList;
  }
}

CMove Search::getBestMove() {
  return _bestMove;
}

void Search::_rootMax(const Board& board, int depth) {
  MoveGen movegen(board);
  MoveBoardList legalMoves = movegen.getLegalMoves();
  _orderMoves(legalMoves);

  int bestScore = -INF;
  int currScore;
  CMove bestMove;
  for (auto moveBoard : legalMoves) {
    CMove move = moveBoard.first;
    Board movedBoard = moveBoard.second;

    currScore = -_negaMax(movedBoard, depth-1, -INF, -bestScore);

    if (currScore > bestScore) {
      bestMove = move;
      bestScore = currScore;
    }
  }

  // If we couldn't find a path other than checkmate, just pick the first legal move
  if (bestMove.getFlags() & CMove::NULL_MOVE) {
    bestMove = legalMoves.at(0).first;
  }

  _tt.set(board.getZKey(), bestScore, depth, TranspTable::EXACT);

  _bestMove = bestMove;
  _bestScore = bestScore;
}

void Search::_orderMoves(MoveBoardList& moveBoardList) {
  std::sort(moveBoardList.begin(), moveBoardList.end(), [this](MoveBoard a, MoveBoard b) {
    ZKey aKey = a.second.getZKey();
    ZKey bKey = b.second.getZKey();
    int aScore = _tt.contains(aKey) ? _tt.getScore(aKey) : -INF;
    int bScore = _tt.contains(bKey) ? _tt.getScore(bKey) : -INF;

    return aScore < bScore;
  });
}

void Search::_orderMovesQSearch(MoveBoardList & moveBoardList) {
  std::sort(moveBoardList.begin(), moveBoardList.end(), [this](MoveBoard a, MoveBoard b) {
    bool aIsCapture = a.first.getFlags() & CMove::CAPTURE;
    bool bIsCapture = b.first.getFlags() & CMove::CAPTURE;

    if (aIsCapture && !bIsCapture) {
      return true;
    } else if (bIsCapture && !aIsCapture) {
      return false;
    } else { // Both captures
      // MVV/LVA
      int aPieceValue = _getPieceValue(a.first.getPieceType());
      int bPieceValue = _getPieceValue(b.first.getPieceType());
      int aCaptureValue = _getPieceValue(a.first.getCapturedPieceType());
      int bCaptureValue = _getPieceValue(b.first.getCapturedPieceType());

      return (aCaptureValue - aPieceValue) > (bCaptureValue - bPieceValue);
    }
  });
}

int Search::_getPieceValue(PieceType pieceType) {
  int score = 0;

  switch(pieceType) {
    case PAWN: score = 1;
      break;
    case KNIGHT: score = 3;
      break;
    case BISHOP: score = 3;
      break;
    case ROOK: score = 5;
      break;
    case QUEEN: score = 9;
      break;
    default: break;
  }

  return score;
}

int Search::_negaMax(const Board& board, int depth, int alpha, int beta) {
  int alphaOrig = alpha;

  ZKey zKey = board.getZKey();
  // Check transposition table cache
  if (_tt.contains(zKey) && (_tt.getDepth(zKey) >= depth)) {
    switch(_tt.getFlag(zKey)) {
      case TranspTable::EXACT:
        return _tt.getScore(zKey);
      case TranspTable::UPPER_BOUND:
        alpha = std::max(alpha, _tt.getScore(zKey));
        break;
      case TranspTable::LOWER_BOUND:
        beta = std::min(beta, _tt.getScore(zKey));
        break;
    }

    if (alpha > beta) {
      return _tt.getScore(zKey);
    }
  }

  // Transposition table lookups are inconclusive, generate moves and recurse
  MoveGen movegen(board);
  MoveBoardList legalMoves = movegen.getLegalMoves();

  // Check for checkmate
  if (legalMoves.size() == 0 && board.colorIsInCheck(board.getActivePlayer())) {
    _tt.set(board.getZKey(), -INF, depth, TranspTable::EXACT);
    return -INF;
  }

  // Eval if depth is 0
  if (depth == 0) {
    int score = _qSearch(board, alpha, beta);
    _tt.set(board.getZKey(), score, 0, TranspTable::EXACT);
    return score;
  }

  _orderMoves(legalMoves);

  int bestScore = -INF;
  for (auto moveBoard : legalMoves) {
    Board movedBoard = moveBoard.second;

    bestScore = std::max(bestScore, -_negaMax(movedBoard, depth-1, -beta, -alpha));

    alpha = std::max(alpha, bestScore);
    if (alpha > beta) {
      break;
    }
  }

  // Store bestScore in transposition table
  TranspTable::Flag flag;
  if (bestScore < alphaOrig) {
    flag = TranspTable::UPPER_BOUND;
  } else if (bestScore >= beta) {
    flag = TranspTable::LOWER_BOUND;
  } else {
    flag = TranspTable::EXACT;
  }
  _tt.set(zKey, bestScore, depth, flag);

  return bestScore;
}

int Search::_qSearch(const Board& board, int alpha, int beta) {
  MoveGen movegen(board);
  MoveBoardList legalMoves = movegen.getLegalMoves();

  // Check for checkmate
  if (legalMoves.size() == 0 && board.colorIsInCheck(board.getActivePlayer())) {
    return -INF;
  }

  _orderMovesQSearch(legalMoves);
  int standPat = Eval(board, board.getActivePlayer()).getScore();

  // If node is quiet, just return eval
  if (!(legalMoves.at(0).first.getFlags() & CMove::CAPTURE)) {
    return standPat;
  }

  if (standPat >= beta) {
    return beta;
  }
  if (alpha < standPat) {
    alpha = standPat;
  }

  for (auto moveBoard : legalMoves) {
    CMove move = moveBoard.first;
    Board movedBoard = moveBoard.second;
    if ((move.getFlags() & CMove::CAPTURE) == 0) {
      break;
    }

    int score = -_qSearch(movedBoard, -beta, -alpha);

    if (score >= beta) {
      return beta;
    }
    if (score > alpha) {
      alpha = score;
    }
  }
  return alpha;
}
<commit_msg>Check for stalemate in _qSearch to avoid out of bounds crash<commit_after>#include "defs.h"
#include "search.h"
#include "eval.h"
#include "movegen.h"
#include "transptable.h"
#include <string>
#include <algorithm>
#include <time.h>
#include <iostream>

Search::Search(const Board& board, bool logUci) {
  _logUci = logUci;
  _board = board;
}

void Search::perform(int depth) {
  _rootMax(_board, depth);

  MoveBoardList pv = _getPv(_board);
  if (_logUci) {
    _logUciInfo(pv, depth, _bestMove, _bestScore);
  }
}

void Search::_logUciInfo(const MoveBoardList& pv, int depth, CMove bestMove, int bestScore) {
  std::string pvString;
  for(auto moveBoard : pv) {
    pvString += moveBoard.first.getNotation() + " ";
  }

  std::string scoreString;
  if (bestScore == INF) {
    scoreString = "mate " + std::to_string(pv.size());
  } else if (_bestScore == -INF) {
    scoreString = "mate -" + std::to_string(pv.size());
  } else {
    scoreString = "cp " + std::to_string(bestScore);
  }

  std::cout << "info depth " + std::to_string(depth) + " ";
  std::cout << "score " + scoreString + " ";
  std::cout << "pv " + pvString;
  std::cout << std::endl;
}

MoveBoardList Search::_getPv(const Board& board) {
  int bestScore = INF;
  MoveBoard best;
  bool foundBest = false;
  for (auto moveBoard : MoveGen(board).getLegalMoves()) {
    Board movedBoard = moveBoard.second;

    ZKey movedZKey = movedBoard.getZKey();

    if (_tt.contains(movedZKey) && _tt.getFlag(movedZKey) == TranspTable::EXACT && _tt.getScore(movedZKey) < bestScore) {
      foundBest = true;
      bestScore = _tt.getScore(movedZKey);
      best = moveBoard;
    }
  }

  if (!foundBest) {
    return MoveBoardList();
  } else {
    MoveBoardList pvList = _getPv(best.second);
    pvList.insert(pvList.begin(), best);
    return pvList;
  }
}

CMove Search::getBestMove() {
  return _bestMove;
}

void Search::_rootMax(const Board& board, int depth) {
  MoveGen movegen(board);
  MoveBoardList legalMoves = movegen.getLegalMoves();
  _orderMoves(legalMoves);

  int bestScore = -INF;
  int currScore;
  CMove bestMove;
  for (auto moveBoard : legalMoves) {
    CMove move = moveBoard.first;
    Board movedBoard = moveBoard.second;

    currScore = -_negaMax(movedBoard, depth-1, -INF, -bestScore);

    if (currScore > bestScore) {
      bestMove = move;
      bestScore = currScore;
    }
  }

  // If we couldn't find a path other than checkmate, just pick the first legal move
  if (bestMove.getFlags() & CMove::NULL_MOVE) {
    bestMove = legalMoves.at(0).first;
  }

  _tt.set(board.getZKey(), bestScore, depth, TranspTable::EXACT);

  _bestMove = bestMove;
  _bestScore = bestScore;
}

void Search::_orderMoves(MoveBoardList& moveBoardList) {
  std::sort(moveBoardList.begin(), moveBoardList.end(), [this](MoveBoard a, MoveBoard b) {
    ZKey aKey = a.second.getZKey();
    ZKey bKey = b.second.getZKey();
    int aScore = _tt.contains(aKey) ? _tt.getScore(aKey) : -INF;
    int bScore = _tt.contains(bKey) ? _tt.getScore(bKey) : -INF;

    return aScore < bScore;
  });
}

void Search::_orderMovesQSearch(MoveBoardList & moveBoardList) {
  std::sort(moveBoardList.begin(), moveBoardList.end(), [this](MoveBoard a, MoveBoard b) {
    bool aIsCapture = a.first.getFlags() & CMove::CAPTURE;
    bool bIsCapture = b.first.getFlags() & CMove::CAPTURE;

    if (aIsCapture && !bIsCapture) {
      return true;
    } else if (bIsCapture && !aIsCapture) {
      return false;
    } else { // Both captures
      // MVV/LVA
      int aPieceValue = _getPieceValue(a.first.getPieceType());
      int bPieceValue = _getPieceValue(b.first.getPieceType());
      int aCaptureValue = _getPieceValue(a.first.getCapturedPieceType());
      int bCaptureValue = _getPieceValue(b.first.getCapturedPieceType());

      return (aCaptureValue - aPieceValue) > (bCaptureValue - bPieceValue);
    }
  });
}

int Search::_getPieceValue(PieceType pieceType) {
  int score = 0;

  switch(pieceType) {
    case PAWN: score = 1;
      break;
    case KNIGHT: score = 3;
      break;
    case BISHOP: score = 3;
      break;
    case ROOK: score = 5;
      break;
    case QUEEN: score = 9;
      break;
    default: break;
  }

  return score;
}

int Search::_negaMax(const Board& board, int depth, int alpha, int beta) {
  int alphaOrig = alpha;

  ZKey zKey = board.getZKey();
  // Check transposition table cache
  if (_tt.contains(zKey) && (_tt.getDepth(zKey) >= depth)) {
    switch(_tt.getFlag(zKey)) {
      case TranspTable::EXACT:
        return _tt.getScore(zKey);
      case TranspTable::UPPER_BOUND:
        alpha = std::max(alpha, _tt.getScore(zKey));
        break;
      case TranspTable::LOWER_BOUND:
        beta = std::min(beta, _tt.getScore(zKey));
        break;
    }

    if (alpha > beta) {
      return _tt.getScore(zKey);
    }
  }

  // Transposition table lookups are inconclusive, generate moves and recurse
  MoveGen movegen(board);
  MoveBoardList legalMoves = movegen.getLegalMoves();

  // Check for checkmate
  if (legalMoves.size() == 0 && board.colorIsInCheck(board.getActivePlayer())) {
    _tt.set(board.getZKey(), -INF, depth, TranspTable::EXACT);
    return -INF;
  }

  // Eval if depth is 0
  if (depth == 0) {
    int score = _qSearch(board, alpha, beta);
    _tt.set(board.getZKey(), score, 0, TranspTable::EXACT);
    return score;
  }

  _orderMoves(legalMoves);

  int bestScore = -INF;
  for (auto moveBoard : legalMoves) {
    Board movedBoard = moveBoard.second;

    bestScore = std::max(bestScore, -_negaMax(movedBoard, depth-1, -beta, -alpha));

    alpha = std::max(alpha, bestScore);
    if (alpha > beta) {
      break;
    }
  }

  // Store bestScore in transposition table
  TranspTable::Flag flag;
  if (bestScore < alphaOrig) {
    flag = TranspTable::UPPER_BOUND;
  } else if (bestScore >= beta) {
    flag = TranspTable::LOWER_BOUND;
  } else {
    flag = TranspTable::EXACT;
  }
  _tt.set(zKey, bestScore, depth, flag);

  return bestScore;
}

int Search::_qSearch(const Board& board, int alpha, int beta) {
  MoveGen movegen(board);
  MoveBoardList legalMoves = movegen.getLegalMoves();

  // Check for checkmate / stalemate
  if (legalMoves.size() == 0) {
    if (board.colorIsInCheck(board.getActivePlayer())) { // Checkmate
      return -INF;
    } else { // Stalemate
      return 0;
    }
  }

  _orderMovesQSearch(legalMoves);
  int standPat = Eval(board, board.getActivePlayer()).getScore();

  // If node is quiet, just return eval
  if (!(legalMoves.at(0).first.getFlags() & CMove::CAPTURE)) {
    return standPat;
  }

  if (standPat >= beta) {
    return beta;
  }
  if (alpha < standPat) {
    alpha = standPat;
  }

  for (auto moveBoard : legalMoves) {
    CMove move = moveBoard.first;
    Board movedBoard = moveBoard.second;
    if ((move.getFlags() & CMove::CAPTURE) == 0) {
      break;
    }

    int score = -_qSearch(movedBoard, -beta, -alpha);

    if (score >= beta) {
      return beta;
    }
    if (score > alpha) {
      alpha = score;
    }
  }
  return alpha;
}
<|endoftext|>


<commit_before>#include "solver.h"

#include <map>
#include <vector>

// -----------------------------------------------------------------------------
cyclus::Solver::Solver() {
  index_ = std::map<cyclus::VariablePtr, int>();
}

// -----------------------------------------------------------------------------
void cyclus::Solver::PopulateIndices(
    std::vector<cyclus::VariablePtr>& variables) {
  for (int i = 0; i < variables.size(); i++) {
    index_[variables.at(i)] = i;
  }
}
<commit_msg>put solver impl under cyclus namespace<commit_after>#include "solver.h"

#include <map>
#include <vector>

namespace cyclus {

// -----------------------------------------------------------------------------
Solver::Solver() {
  index_ = std::map<VariablePtr, int>();
}

// -----------------------------------------------------------------------------
void Solver::PopulateIndices(std::vector<VariablePtr>& variables) {
  for (int i = 0; i < variables.size(); i++) {
    index_[variables.at(i)] = i;
  }
}

} // namespace cyclus
<|endoftext|>


<commit_before>#include "sound.hpp"
#include "oddlib/audio/SequencePlayer.h"
#include "core/audiobuffer.hpp"
#include "logger.hpp"
#include "resourcemapper.hpp"
#include "audioconverter.hpp"
#include "alive_version.h"

SoundCache::SoundCache(OSBaseFileSystem& fs)
    : mFs(fs)
{

}

void SoundCache::Sync()
{
    mSoundDataCache.clear();

    bool ok = false;
    std::string versionFile = "{CacheDir}/CacheVersion.txt";
    if (mFs.FileExists(versionFile))
    {
        if (mFs.Open(versionFile)->LoadAllToString() == ALIVE_VERSION)
        {
            ok = true;
        }
    }

    if (!ok)
    {
        DeleteAll();
    }
}

void SoundCache::DeleteAll()
{
    TRACE_ENTRYEXIT;

    // Delete *.wav from {CacheDir}/disk
    std::string dirName = mFs.ExpandPath("{CacheDir}");
    const auto wavFiles = mFs.EnumerateFiles(dirName, "*.wav");
    for (const auto& wavFile : wavFiles)
    {
        mFs.DeleteFile(dirName + "/" + wavFile);
    }

    // Remove from memory
    mSoundDataCache.clear();

    // Update cache version marker to current
    const std::string fileName = mFs.ExpandPath("{CacheDir}/CacheVersion.txt");
    Oddlib::FileStream versionFile(fileName, Oddlib::IStream::ReadMode::ReadWrite);
    versionFile.Write(std::string(ALIVE_VERSION));
}

bool SoundCache::ExistsInMemoryCache(const std::string& name) const
{
    return mSoundDataCache.find(name) != std::end(mSoundDataCache);
}

class WavSound : public ISound
{
public:
    WavSound(const std::string& name, std::shared_ptr<std::vector<u8>>& data)
        : mName(name), mData(data)
    {
        memcpy(&mHeader.mData, data->data(), sizeof(mHeader.mData));
        mOffsetInBytes = sizeof(mHeader.mData);
    }

    virtual void Load() override { }
    virtual void DebugUi() override {}

    virtual void Play(f32* stream, u32 len) override
    {
        u32 kLenInBytes = len * sizeof(f32);

        // Handle the case where the audio call back wants N data but we only have N-X left
        if (mOffsetInBytes + kLenInBytes > mData->size())
        {
            kLenInBytes = mData->size() - mOffsetInBytes;
        }

        const f32* src = reinterpret_cast<const f32*>(mData->data() + mOffsetInBytes);
        for (u32 i = 0; i < kLenInBytes/sizeof(f32); i++)
        {
            stream[i] += src[i];
        }
        mOffsetInBytes += kLenInBytes;
    }

    virtual bool AtEnd() const override
    {
        return mOffsetInBytes >= mData->size();
    }

    virtual void Restart() override
    {
        mOffsetInBytes = sizeof(mHeader.mData);
    }

    virtual void Update() override { }
    virtual const std::string& Name() const override { return mName; }

private:
    size_t mOffsetInBytes = 0;
    std::string mName;
    std::shared_ptr<std::vector<u8>> mData;
    WavHeader mHeader;
};

std::unique_ptr<ISound> SoundCache::GetCached(const std::string& name)
{
    auto it = mSoundDataCache.find(name);
    if (it != std::end(mSoundDataCache))
    {
        return std::make_unique<WavSound>(name, it->second);
    }
    return nullptr;
}

void SoundCache::AddToMemoryAndDiskCache(ISound& sound)
{
    const std::string fileName = mFs.ExpandPath("{CacheDir}/" + sound.Name() + ".wav");

    // TODO: mod files that are already wav shouldn't be converted
    sound.Load();
    AudioConverter::Convert<WavEncoder>(sound, fileName.c_str());

    auto stream = mFs.Open(fileName);
    mSoundDataCache[sound.Name()] = std::make_shared<std::vector<u8>>(Oddlib::IStream::ReadAll(*stream));
}

bool SoundCache::AddToMemoryCacheFromDiskCache(const std::string& name)
{
    std::string fileName = mFs.ExpandPath("{CacheDir}/" + name + ".wav");
    if (mFs.FileExists(fileName))
    {
        auto stream = mFs.Open(fileName);
        mSoundDataCache[name] = std::make_shared<std::vector<u8>>(Oddlib::IStream::ReadAll(*stream));
        return true;
    }
    return false;
}

void SoundCache::RemoveFromMemoryCache(const std::string& name)
{
    auto it = mSoundDataCache.find(name);
    if (it != std::end(mSoundDataCache))
    {
        mSoundDataCache.erase(it);
    }
}

// ====================================================================

void Sound::PlaySoundScript(const char* soundName)
{
    auto ret = PlaySound(soundName, nullptr, true, true, true);
    if (ret)
    {
        std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
        mSoundPlayers.push_back(std::move(ret));
    }
}
std::unique_ptr<ISound> Sound::PlaySound(const char* soundName, const char* explicitSoundBankName, bool useMusicRec, bool useSfxRec, bool useCache)
{
    if (useCache)
    {
        std::unique_ptr<ISound> pSound = mCache.GetCached(soundName);
        if (pSound)
        {
            LOG_INFO("Play sound cached: " << soundName);
        }
        else
        {
            LOG_ERROR("Cached sound not found: " << soundName);
        }
        return pSound;
    }
    else
    {
        std::unique_ptr<ISound> pSound = mLocator.LocateSound(soundName, explicitSoundBankName, useMusicRec, useSfxRec);
        if (pSound)
        {
            LOG_INFO("Play sound: " << soundName);
            pSound->Load();
        }
        else
        {
            LOG_ERROR("Sound: " << soundName << " not found");
        }
        return pSound;
    }
}

void Sound::SetTheme(const char* themeName)
{
    mAmbiance = nullptr;
    mMusicTrack = nullptr;
    const MusicTheme* newTheme = mLocator.LocateSoundTheme(themeName);

    // Remove current musics from in memory cache
    if (mActiveTheme)
    {
        CacheActiveTheme(false);
    }

    mActiveTheme = newTheme;

    // Add new musics to in memory cache
    if (mActiveTheme)
    {
        CacheActiveTheme(true);
    }
    else
    {
        LOG_ERROR("Music theme " << themeName << " was not found");
    }
}

void Sound::CacheMemoryResidentSounds()
{
    TRACE_ENTRYEXIT;

    // initial one time sync
    mCache.Sync();

    const std::vector<SoundResource>& resources = mLocator.GetSoundResources();
    for (const SoundResource& resource : resources)
    {
        if (resource.mIsCacheResident)
        {
            CacheSound(resource.mResourceName);
        }
    }
}

void Sound::CacheActiveTheme(bool add)
{
    for (auto& entry : mActiveTheme->mEntries)
    {
        for (auto& e : entry.second)
        {
            if (add)
            {
                CacheSound(e.mMusicName);
            }
            else
            {
                mCache.RemoveFromMemoryCache(e.mMusicName);
            }
        }
    }
}

void Sound::CacheSound(const std::string& name)
{
    if (mCache.ExistsInMemoryCache(name))
    {
        // Already in memory
        return;
    }

    if (mCache.AddToMemoryCacheFromDiskCache(name))
    {
        // Already on disk and now added to in memory cache
        return;
    }

    std::unique_ptr<ISound> pSound = mLocator.LocateSound(name.c_str(), nullptr, true, true);
    if (pSound)
    {
        // Write into disk cache and then load from disk cache into memory cache
        mCache.AddToMemoryAndDiskCache(*pSound);
    }
}

void Sound::HandleEvent(const char* eventName)
{
    // TODO: Need quarter beat transition in some cases
    EnsureAmbiance();

    if (strcmp(eventName, "AMBIANCE") == 0)
    {
        mMusicTrack = nullptr;
        return;
    }

    auto ret = PlayThemeEntry(eventName);
    if (ret)
    {
        mMusicTrack = std::move(ret);
    }
}

std::unique_ptr<ISound> Sound::PlayThemeEntry(const char* entryName)
{
    if (mActiveTheme)
    {
        mActiveThemeEntry.SetMusicThemeEntry(mActiveTheme->FindEntry(entryName));

        const MusicThemeEntry* entry = mActiveThemeEntry.Entry();
        if (entry)
        {
            return PlaySound(entry->mMusicName.c_str(), nullptr, true, true, true);
        }
    }
    return nullptr;
}

void Sound::EnsureAmbiance()
{
    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
    if (!mAmbiance)
    {
        mAmbiance = PlayThemeEntry("AMBIANCE");
    }
}

// Audio thread context
bool Sound::Play(f32* stream, u32 len)
{
    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);

    if (mAmbiance)
    {
        mAmbiance->Play(stream, len);
    }

    if (mMusicTrack)
    {
        mMusicTrack->Play(stream, len);
    }

    for (auto& player : mSoundPlayers)
    {
        player->Play(stream, len);
    }
    return false;
}

/*static*/ void Sound::RegisterScriptBindings()
{
    Sqrat::Class<Sound, Sqrat::NoConstructor<Sound>> c(Sqrat::DefaultVM::Get(), "Sound");
    c.Func("PlaySoundEffect", &Sound::PlaySoundScript);
    c.Func("SetTheme", &Sound::SetTheme);
    Sqrat::RootTable().Bind("Sound", c);
}

Sound::Sound(IAudioController& audioController, ResourceLocator& locator, OSBaseFileSystem& fs)
    : mAudioController(audioController), mLocator(locator), mCache(fs), mScriptInstance("gSound", this)
{
    mAudioController.AddPlayer(this);
}

Sound::~Sound()
{
    // Ensure the audio call back stops at this point else will be
    // calling back into freed objects
    SDL_AudioQuit();

    mAudioController.RemovePlayer(this);
}

void Sound::Update()
{
    if (Debugging().mBrowserUi.soundBrowserOpen)
    {
        {
            std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
            if (!mSoundPlayers.empty())
            {
                mSoundPlayers[0]->DebugUi();
            }

            if (ImGui::Begin("Active SEQs"))
            {
                if (mAmbiance)
                {
                   ImGui::Text("Ambiance: %s", mAmbiance->Name().c_str());
                }

                if (mMusicTrack)
                {
                    ImGui::Text("Music: %s", mMusicTrack->Name().c_str());
                }

                int i = 0;
                for (auto& player : mSoundPlayers)
                {
                    i++;
                    if (ImGui::Button((std::to_string(i) + player->Name()).c_str()))
                    {
                        // TODO: Kill whatever this SEQ is
                    }
                }
            }
            ImGui::End();
        }

        SoundBrowserUi();
    }

    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
    for (auto it = mSoundPlayers.begin(); it != mSoundPlayers.end();)
    {
        if ((*it)->AtEnd())
        {
            it = mSoundPlayers.erase(it);
        }
        else
        {
            it++;
        }
    }

    if (mAmbiance)
    {
        mAmbiance->Update();
        if (mAmbiance->AtEnd())
        {
            mAmbiance->Restart();
        }
    }

    if (mMusicTrack)
    {
        mMusicTrack->Update();
        if (mMusicTrack->AtEnd())
        {
            if (mActiveThemeEntry.ToNextEntry())
            {
                mMusicTrack = PlaySound(mActiveThemeEntry.Entry()->mMusicName.c_str(), nullptr, true, true, true);
            }
            else
            {
                mMusicTrack = nullptr;
            }
        }
    }

    for (auto& player : mSoundPlayers)
    {
        player->Update();
    }
}

static const char* kMusicEvents[] =
{
    "AMBIANCE",
    "BASE_LINE",
    "CRITTER_ATTACK",
    "CRITTER_PATROL",
    "SLIG_ATTACK",
    "SLIG_PATROL",
    "SLIG_POSSESSED",
};

void Sound::SoundBrowserUi()
{
    ImGui::Begin("Sound list");

    static const SoundResource* selected = nullptr;

    // left
    ImGui::BeginChild("left pane", ImVec2(200, 0), true);
    {
        for (const SoundResource& soundInfo : mLocator.GetSoundResources())
        {
            if (ImGui::Selectable(soundInfo.mResourceName.c_str()))
            {
                selected = &soundInfo;
            }
        }
        ImGui::EndChild();
    }
    ImGui::SameLine();

    // right
    ImGui::BeginGroup();
    {
        ImGui::BeginChild("item view");
        {
            if (selected)
            {
                ImGui::TextWrapped("Resource name: %s", selected->mResourceName.c_str());
                ImGui::Separator();
                ImGui::TextWrapped("Comment: %s", selected->mComment.empty() ? "(none)" : selected->mComment.c_str());
                ImGui::Separator();
                ImGui::TextWrapped("Is memory resident: %s", selected->mIsCacheResident ? "true" : "false");
                ImGui::TextWrapped("Is cached: %s", mCache.ExistsInMemoryCache(selected->mResourceName) ? "true" : "false");

                static bool bUseCache = false;
                ImGui::Checkbox("Use cache", &bUseCache);

                const bool bHasMusic = !selected->mMusic.mSoundBanks.empty();
                const bool bHasSample = !selected->mSoundEffect.mSoundBanks.empty();
                if (bHasMusic)
                {
                    if (ImGui::CollapsingHeader("SEQs"))
                    {
                        for (const std::string& sb : selected->mMusic.mSoundBanks)
                        {
                            if (ImGui::Selectable(sb.c_str()))
                            {
                                auto player = PlaySound(selected->mResourceName.c_str(), sb.c_str(), true, false, bUseCache);
                                if (player)
                                {
                                    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
                                    mSoundPlayers.push_back(std::move(player));
                                }
                            }
                        }
                    }
                }

                if (bHasSample)
                {
                    if (ImGui::CollapsingHeader("Samples"))
                    {
                        for (const SoundEffectResourceLocation& sbLoc : selected->mSoundEffect.mSoundBanks)
                        {
                            for (const std::string& sb : sbLoc.mSoundBanks)
                            {
                                if (ImGui::Selectable(sb.c_str()))
                                {
                                    auto player = PlaySound(selected->mResourceName.c_str(), sb.c_str(), false, true, bUseCache);
                                    if (player)
                                    {
                                        std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
                                        mSoundPlayers.push_back(std::move(player));
                                    }
                                }
                            }
                        }
                    }
                }

                if (ImGui::Button("Play (cached/scripted)"))
                {
                    PlaySoundScript(selected->mResourceName.c_str());
                }
            }
            else
            {
                ImGui::TextWrapped("Click an item to display its info");
            }
        }
        ImGui::EndChild();
    }
    ImGui::EndGroup();

    ImGui::End();

    ImGui::Begin("Sound themes");

    for (const MusicTheme& theme : mLocator.mResMapper.mSoundResources.mThemes)
    {
        if (ImGui::RadioButton(theme.mName.c_str(), mActiveTheme && theme.mName == mActiveTheme->mName))
        {
            SetTheme(theme.mName.c_str());
            HandleEvent("BASE_LINE");
        }
    }

    for (const char* eventName : kMusicEvents)
    {
        if (ImGui::Button(eventName))
        {
            HandleEvent(eventName);
        }
    }

    ImGui::End();

}

void Sound::Render(int /*w*/, int /*h*/)
{

}
<commit_msg>fix debug build<commit_after>#include "sound.hpp"
#include "oddlib/audio/SequencePlayer.h"
#include "core/audiobuffer.hpp"
#include "logger.hpp"
#include "resourcemapper.hpp"
#include "audioconverter.hpp"
#include "alive_version.h"

SoundCache::SoundCache(OSBaseFileSystem& fs)
    : mFs(fs)
{

}

void SoundCache::Sync()
{
    mSoundDataCache.clear();

    bool ok = false;
    std::string versionFile = "{CacheDir}/CacheVersion.txt";
    if (mFs.FileExists(versionFile))
    {
        if (mFs.Open(versionFile)->LoadAllToString() == ALIVE_VERSION)
        {
            ok = true;
        }
    }

    if (!ok)
    {
        DeleteAll();
    }
}

void SoundCache::DeleteAll()
{
    TRACE_ENTRYEXIT;

    // Delete *.wav from {CacheDir}/disk
    std::string dirName = mFs.ExpandPath("{CacheDir}");
    const auto wavFiles = mFs.EnumerateFiles(dirName, "*.wav");
    for (const auto& wavFile : wavFiles)
    {
        mFs.DeleteFile(dirName + "/" + wavFile);
    }

    // Remove from memory
    mSoundDataCache.clear();

    // Update cache version marker to current
    const std::string fileName = mFs.ExpandPath("{CacheDir}/CacheVersion.txt");
    Oddlib::FileStream versionFile(fileName, Oddlib::IStream::ReadMode::ReadWrite);
    versionFile.Write(std::string(ALIVE_VERSION));
}

bool SoundCache::ExistsInMemoryCache(const std::string& name) const
{
    return mSoundDataCache.find(name) != std::end(mSoundDataCache);
}

class WavSound : public ISound
{
public:
    WavSound(const std::string& name, std::shared_ptr<std::vector<u8>>& data)
        : mName(name), mData(data)
    {
        memcpy(&mHeader.mData, data->data(), sizeof(mHeader.mData));
        mOffsetInBytes = sizeof(mHeader.mData);
    }

    virtual void Load() override { }
    virtual void DebugUi() override {}

    virtual void Play(f32* stream, u32 len) override
    {
        u32 kLenInBytes = len * sizeof(f32);

        // Handle the case where the audio call back wants N data but we only have N-X left
        if (mOffsetInBytes + kLenInBytes > mData->size())
        {
            kLenInBytes = static_cast<u32>(mData->size()) - mOffsetInBytes;
        }

        const f32* src = reinterpret_cast<const f32*>(mData->data() + mOffsetInBytes);
        for (u32 i = 0; i < kLenInBytes/sizeof(f32); i++)
        {
            stream[i] += src[i];
        }
        mOffsetInBytes += kLenInBytes;
    }

    virtual bool AtEnd() const override
    {
        return mOffsetInBytes >= mData->size();
    }

    virtual void Restart() override
    {
        mOffsetInBytes = sizeof(mHeader.mData);
    }

    virtual void Update() override { }
    virtual const std::string& Name() const override { return mName; }

private:
    size_t mOffsetInBytes = 0;
    std::string mName;
    std::shared_ptr<std::vector<u8>> mData;
    WavHeader mHeader;
};

std::unique_ptr<ISound> SoundCache::GetCached(const std::string& name)
{
    auto it = mSoundDataCache.find(name);
    if (it != std::end(mSoundDataCache))
    {
        return std::make_unique<WavSound>(name, it->second);
    }
    return nullptr;
}

void SoundCache::AddToMemoryAndDiskCache(ISound& sound)
{
    const std::string fileName = mFs.ExpandPath("{CacheDir}/" + sound.Name() + ".wav");

    // TODO: mod files that are already wav shouldn't be converted
    sound.Load();
    AudioConverter::Convert<WavEncoder>(sound, fileName.c_str());

    auto stream = mFs.Open(fileName);
    mSoundDataCache[sound.Name()] = std::make_shared<std::vector<u8>>(Oddlib::IStream::ReadAll(*stream));
}

bool SoundCache::AddToMemoryCacheFromDiskCache(const std::string& name)
{
    std::string fileName = mFs.ExpandPath("{CacheDir}/" + name + ".wav");
    if (mFs.FileExists(fileName))
    {
        auto stream = mFs.Open(fileName);
        mSoundDataCache[name] = std::make_shared<std::vector<u8>>(Oddlib::IStream::ReadAll(*stream));
        return true;
    }
    return false;
}

void SoundCache::RemoveFromMemoryCache(const std::string& name)
{
    auto it = mSoundDataCache.find(name);
    if (it != std::end(mSoundDataCache))
    {
        mSoundDataCache.erase(it);
    }
}

// ====================================================================

void Sound::PlaySoundScript(const char* soundName)
{
    auto ret = PlaySound(soundName, nullptr, true, true, true);
    if (ret)
    {
        std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
        mSoundPlayers.push_back(std::move(ret));
    }
}
std::unique_ptr<ISound> Sound::PlaySound(const char* soundName, const char* explicitSoundBankName, bool useMusicRec, bool useSfxRec, bool useCache)
{
    if (useCache)
    {
        std::unique_ptr<ISound> pSound = mCache.GetCached(soundName);
        if (pSound)
        {
            LOG_INFO("Play sound cached: " << soundName);
        }
        else
        {
            LOG_ERROR("Cached sound not found: " << soundName);
        }
        return pSound;
    }
    else
    {
        std::unique_ptr<ISound> pSound = mLocator.LocateSound(soundName, explicitSoundBankName, useMusicRec, useSfxRec);
        if (pSound)
        {
            LOG_INFO("Play sound: " << soundName);
            pSound->Load();
        }
        else
        {
            LOG_ERROR("Sound: " << soundName << " not found");
        }
        return pSound;
    }
}

void Sound::SetTheme(const char* themeName)
{
    mAmbiance = nullptr;
    mMusicTrack = nullptr;
    const MusicTheme* newTheme = mLocator.LocateSoundTheme(themeName);

    // Remove current musics from in memory cache
    if (mActiveTheme)
    {
        CacheActiveTheme(false);
    }

    mActiveTheme = newTheme;

    // Add new musics to in memory cache
    if (mActiveTheme)
    {
        CacheActiveTheme(true);
    }
    else
    {
        LOG_ERROR("Music theme " << themeName << " was not found");
    }
}

void Sound::CacheMemoryResidentSounds()
{
    TRACE_ENTRYEXIT;

    // initial one time sync
    mCache.Sync();

    const std::vector<SoundResource>& resources = mLocator.GetSoundResources();
    for (const SoundResource& resource : resources)
    {
        if (resource.mIsCacheResident)
        {
            CacheSound(resource.mResourceName);
        }
    }
}

void Sound::CacheActiveTheme(bool add)
{
    for (auto& entry : mActiveTheme->mEntries)
    {
        for (auto& e : entry.second)
        {
            if (add)
            {
                CacheSound(e.mMusicName);
            }
            else
            {
                mCache.RemoveFromMemoryCache(e.mMusicName);
            }
        }
    }
}

void Sound::CacheSound(const std::string& name)
{
    if (mCache.ExistsInMemoryCache(name))
    {
        // Already in memory
        return;
    }

    if (mCache.AddToMemoryCacheFromDiskCache(name))
    {
        // Already on disk and now added to in memory cache
        return;
    }

    std::unique_ptr<ISound> pSound = mLocator.LocateSound(name.c_str(), nullptr, true, true);
    if (pSound)
    {
        // Write into disk cache and then load from disk cache into memory cache
        mCache.AddToMemoryAndDiskCache(*pSound);
    }
}

void Sound::HandleEvent(const char* eventName)
{
    // TODO: Need quarter beat transition in some cases
    EnsureAmbiance();

    if (strcmp(eventName, "AMBIANCE") == 0)
    {
        mMusicTrack = nullptr;
        return;
    }

    auto ret = PlayThemeEntry(eventName);
    if (ret)
    {
        mMusicTrack = std::move(ret);
    }
}

std::unique_ptr<ISound> Sound::PlayThemeEntry(const char* entryName)
{
    if (mActiveTheme)
    {
        mActiveThemeEntry.SetMusicThemeEntry(mActiveTheme->FindEntry(entryName));

        const MusicThemeEntry* entry = mActiveThemeEntry.Entry();
        if (entry)
        {
            return PlaySound(entry->mMusicName.c_str(), nullptr, true, true, true);
        }
    }
    return nullptr;
}

void Sound::EnsureAmbiance()
{
    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
    if (!mAmbiance)
    {
        mAmbiance = PlayThemeEntry("AMBIANCE");
    }
}

// Audio thread context
bool Sound::Play(f32* stream, u32 len)
{
    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);

    if (mAmbiance)
    {
        mAmbiance->Play(stream, len);
    }

    if (mMusicTrack)
    {
        mMusicTrack->Play(stream, len);
    }

    for (auto& player : mSoundPlayers)
    {
        player->Play(stream, len);
    }
    return false;
}

/*static*/ void Sound::RegisterScriptBindings()
{
    Sqrat::Class<Sound, Sqrat::NoConstructor<Sound>> c(Sqrat::DefaultVM::Get(), "Sound");
    c.Func("PlaySoundEffect", &Sound::PlaySoundScript);
    c.Func("SetTheme", &Sound::SetTheme);
    Sqrat::RootTable().Bind("Sound", c);
}

Sound::Sound(IAudioController& audioController, ResourceLocator& locator, OSBaseFileSystem& fs)
    : mAudioController(audioController), mLocator(locator), mCache(fs), mScriptInstance("gSound", this)
{
    mAudioController.AddPlayer(this);
}

Sound::~Sound()
{
    // Ensure the audio call back stops at this point else will be
    // calling back into freed objects
    SDL_AudioQuit();

    mAudioController.RemovePlayer(this);
}

void Sound::Update()
{
    if (Debugging().mBrowserUi.soundBrowserOpen)
    {
        {
            std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
            if (!mSoundPlayers.empty())
            {
                mSoundPlayers[0]->DebugUi();
            }

            if (ImGui::Begin("Active SEQs"))
            {
                if (mAmbiance)
                {
                   ImGui::Text("Ambiance: %s", mAmbiance->Name().c_str());
                }

                if (mMusicTrack)
                {
                    ImGui::Text("Music: %s", mMusicTrack->Name().c_str());
                }

                int i = 0;
                for (auto& player : mSoundPlayers)
                {
                    i++;
                    if (ImGui::Button((std::to_string(i) + player->Name()).c_str()))
                    {
                        // TODO: Kill whatever this SEQ is
                    }
                }
            }
            ImGui::End();
        }

        SoundBrowserUi();
    }

    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
    for (auto it = mSoundPlayers.begin(); it != mSoundPlayers.end();)
    {
        if ((*it)->AtEnd())
        {
            it = mSoundPlayers.erase(it);
        }
        else
        {
            it++;
        }
    }

    if (mAmbiance)
    {
        mAmbiance->Update();
        if (mAmbiance->AtEnd())
        {
            mAmbiance->Restart();
        }
    }

    if (mMusicTrack)
    {
        mMusicTrack->Update();
        if (mMusicTrack->AtEnd())
        {
            if (mActiveThemeEntry.ToNextEntry())
            {
                mMusicTrack = PlaySound(mActiveThemeEntry.Entry()->mMusicName.c_str(), nullptr, true, true, true);
            }
            else
            {
                mMusicTrack = nullptr;
            }
        }
    }

    for (auto& player : mSoundPlayers)
    {
        player->Update();
    }
}

static const char* kMusicEvents[] =
{
    "AMBIANCE",
    "BASE_LINE",
    "CRITTER_ATTACK",
    "CRITTER_PATROL",
    "SLIG_ATTACK",
    "SLIG_PATROL",
    "SLIG_POSSESSED",
};

void Sound::SoundBrowserUi()
{
    ImGui::Begin("Sound list");

    static const SoundResource* selected = nullptr;

    // left
    ImGui::BeginChild("left pane", ImVec2(200, 0), true);
    {
        for (const SoundResource& soundInfo : mLocator.GetSoundResources())
        {
            if (ImGui::Selectable(soundInfo.mResourceName.c_str()))
            {
                selected = &soundInfo;
            }
        }
        ImGui::EndChild();
    }
    ImGui::SameLine();

    // right
    ImGui::BeginGroup();
    {
        ImGui::BeginChild("item view");
        {
            if (selected)
            {
                ImGui::TextWrapped("Resource name: %s", selected->mResourceName.c_str());
                ImGui::Separator();
                ImGui::TextWrapped("Comment: %s", selected->mComment.empty() ? "(none)" : selected->mComment.c_str());
                ImGui::Separator();
                ImGui::TextWrapped("Is memory resident: %s", selected->mIsCacheResident ? "true" : "false");
                ImGui::TextWrapped("Is cached: %s", mCache.ExistsInMemoryCache(selected->mResourceName) ? "true" : "false");

                static bool bUseCache = false;
                ImGui::Checkbox("Use cache", &bUseCache);

                const bool bHasMusic = !selected->mMusic.mSoundBanks.empty();
                const bool bHasSample = !selected->mSoundEffect.mSoundBanks.empty();
                if (bHasMusic)
                {
                    if (ImGui::CollapsingHeader("SEQs"))
                    {
                        for (const std::string& sb : selected->mMusic.mSoundBanks)
                        {
                            if (ImGui::Selectable(sb.c_str()))
                            {
                                auto player = PlaySound(selected->mResourceName.c_str(), sb.c_str(), true, false, bUseCache);
                                if (player)
                                {
                                    std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
                                    mSoundPlayers.push_back(std::move(player));
                                }
                            }
                        }
                    }
                }

                if (bHasSample)
                {
                    if (ImGui::CollapsingHeader("Samples"))
                    {
                        for (const SoundEffectResourceLocation& sbLoc : selected->mSoundEffect.mSoundBanks)
                        {
                            for (const std::string& sb : sbLoc.mSoundBanks)
                            {
                                if (ImGui::Selectable(sb.c_str()))
                                {
                                    auto player = PlaySound(selected->mResourceName.c_str(), sb.c_str(), false, true, bUseCache);
                                    if (player)
                                    {
                                        std::lock_guard<std::mutex> lock(mSoundPlayersMutex);
                                        mSoundPlayers.push_back(std::move(player));
                                    }
                                }
                            }
                        }
                    }
                }

                if (ImGui::Button("Play (cached/scripted)"))
                {
                    PlaySoundScript(selected->mResourceName.c_str());
                }
            }
            else
            {
                ImGui::TextWrapped("Click an item to display its info");
            }
        }
        ImGui::EndChild();
    }
    ImGui::EndGroup();

    ImGui::End();

    ImGui::Begin("Sound themes");

    for (const MusicTheme& theme : mLocator.mResMapper.mSoundResources.mThemes)
    {
        if (ImGui::RadioButton(theme.mName.c_str(), mActiveTheme && theme.mName == mActiveTheme->mName))
        {
            SetTheme(theme.mName.c_str());
            HandleEvent("BASE_LINE");
        }
    }

    for (const char* eventName : kMusicEvents)
    {
        if (ImGui::Button(eventName))
        {
            HandleEvent(eventName);
        }
    }

    ImGui::End();

}

void Sound::Render(int /*w*/, int /*h*/)
{

}
<|endoftext|>