<commit_before>/// Implementation of the Number-Theoretical Transform in GF(P) Galois field
#include <iostream>
#include <algorithm>
#include <stdint.h>

#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__)
#define MY_CPU_AMD64
#endif

#if defined(MY_CPU_AMD64) || defined(_M_IA64)
#define MY_CPU_64BIT
#endif

typedef uint32_t ElementT;        // data items type, 32-bit unsigned integer for GF(P) computations with P>65536
typedef uint64_t DoubleElementT;  // twice wider type to hold intermediate results

template <ElementT P>
ElementT GF_Add (ElementT X, ElementT Y)
{
    ElementT res = X + Y;
    return res - ((res>=P)+(res<X))*P;   // (res>=P || res<X)? res-P : res;
}

template <ElementT P>
ElementT GF_Sub (ElementT X, ElementT Y)
{
    ElementT res = X - Y;
    return res + (res>X)*P;   // res<=X? res : res+P
}

#if 0
// Alternative GF_Mul64 implementation for GCC - unfortunately, GCC 4.9 generates over-smart code for it

#include <inttypes.h>
typedef unsigned __int128 uint128_t;
typedef uint128_t FourElement;    // 4x wider type to hold intermediate MUL results

template <ElementT P>
ElementT GF_Mul64 (ElementT X, ElementT Y)
{
    DoubleElementT res = DoubleElementT(X)*Y;
    DoubleElementT invP = DoubleElementT((FourElement(1)<<64) / P);
    res -= DoubleElementT(((res)*FourElement(invP)) >> 64) * P;
    return ElementT(res>=P? res-P : res);
}

#elif _MSC_VER
// Alternative GF_Mul64 implementation made with MSVC intrinsics

template <ElementT P>
ElementT GF_Mul64 (ElementT X, ElementT Y)
{
    const DoubleElementT estInvP = ((DoubleElementT(1)<<63) / P) << 1;
    const DoubleElementT invP    = (estInvP*P > (estInvP+1)*P? estInvP : estInvP+1);
    DoubleElementT res = DoubleElementT(X)*Y;
    res -= __umulh(res,invP) * P;
    return ElementT(res>=P? res-P : res);
}

#else

// GF_Mul64 is optimized for 64-bit CPUs
template <ElementT P>
ElementT GF_Mul64 (ElementT X, ElementT Y)
{
    return ElementT( (DoubleElementT(X)*Y) % P);
}

#endif

// GF_Mul32 is optimized for 32-bit CPUs, SIMD and GPUs
template <ElementT P>
ElementT GF_Mul32 (ElementT X, ElementT Y)
{
    // invP32 := (2**64)/P - 2**32  :  if 2**31<P<2**32, then 2**32 < (2**64)/P < 2**33, and invP32 is a 32-bit value
    const DoubleElementT estInvP = ((DoubleElementT(1)<<63) / P) << 1;                          // == invP & (~1)
    const ElementT       invP32  = ElementT(estInvP*P > (estInvP+1)*P? estInvP : estInvP+1);    // we can't use 1<<64 for exact invP computation so we add the posible 1 in other way
    DoubleElementT res = DoubleElementT(X)*Y;
    res  -=  ((res + (res>>32)*invP32) >> 32) * P;    // The same as res -= ((res*invP) >> 64) * P, where invP = (2**64)/P, but optimized for 32-bit computations
    return ElementT(res>=P? res-P : res);
}

#ifdef MY_CPU_64BIT
#define GF_Mul GF_Mul64
#else
#define GF_Mul GF_Mul32
#endif

template <typename T, T P>
void apply (T* data, size_t N, size_t SIZE, T root)
{
    T root_sqr = GF_Mul<P> (root, root);  // first root of power N of 1
    if (N>1024) {
        #pragma omp task
        apply<T,P> (data,        N/2, SIZE, root_sqr);
        #pragma omp task
        apply<T,P> (data+N*SIZE, N/2, SIZE, root_sqr);
        #pragma omp taskwait
    } else if (N>1) {
        apply<T,P> (data,        N/2, SIZE, root_sqr);
        apply<T,P> (data+N*SIZE, N/2, SIZE, root_sqr);
    }

    T root_i = root;   // first root of power 2N of 1
    for (size_t i=0; i<N*SIZE; i+=SIZE) {
        for (size_t k=0; k<SIZE; k++) {
            size_t i1 = i+k, i2 = i+k+N*SIZE;
            T temp   = GF_Mul<P> (root_i, data[i2]);
            data[i2] = GF_Sub<P> (data[i1], temp);
            data[i1] = GF_Add<P> (data[i1], temp);
        }
        root_i = GF_Mul<P> (root_i, root);  // next root of power 2N of 1
    }
}

template <size_t Exp, size_t SIZE, typename T, T P>
class NTT
{
    enum { N = 1<<Exp };
public:
    void ntt (T* data)
    {
        T root = 1557;                      // init 'root' with root of 1 of power 2**20 in GF(0xFFF00001)
        for (int i=20; --i>Exp; )
            root = GF_Mul<P> (root, root);  // find root of 1 of power 2N
        apply<T,P> (data, N, SIZE, root);
    }
};

// Find first root of 1 of power 2**N
template <typename T, T P>
void FindRoot (int N)
{
    for (T i=2; i<P; i++)
    {
        T q = i;
        for (int j=0; j<N; j++)
        {
            if (q==1)  goto next;
            q = GF_Mul<P> (q,q);
        }
        if (q==1)
        {
            std::cout << i << "\n";
            break;
        }
next:;
    }
}

// Test the GF_Mul32 correctness
template <ElementT P>
void Test_GF_Mul32()
{
    int n = 0;
    for (ElementT i=P; --i; )
    {
        std::cout << std::hex << "\r" << i << "...";
        for (ElementT j=i; --j; )
            if (GF_Mul64<P> (i,j)  !=  GF_Mul32<P> (i,j))
            {
                std::cout << std::hex << "\r" << i << "*" << j << "=" << GF_Mul64<P> (i,j) << " != " << GF_Mul32<P> (i,j) << "\n" ;
                if (++n>10) return;
            }
    }
}

int main()
{
    const ElementT P = 0xFFF00001;
    // Test_GF_Mul32<P>();
    // FindRoot<ElementT,P>(20);  // prints 1557

    const size_t SIZE = 128<<20;  // 512 MB
    ElementT *data = new ElementT[SIZE];
    for (int i=0; i<SIZE; i++)
        data[i] = i;

    NTT<19,SIZE/1048576,ElementT,P> transformer;
    #pragma omp parallel num_threads(16)
    #pragma omp single
    transformer.ntt(data);

    uint32_t sum = 314159253;
    for (int i=0; i<SIZE; i++)
        sum = (sum+data[i])*123456791;
    if (sum != 3267607014UL)
        std::cout << "checksum failed: " << sum << " ";

    return 0;
}
<commit_msg>NTT: simplified the OpenMP-specific code<commit_after>/// Implementation of the Number-Theoretical Transform in GF(P) Galois field
#include <iostream>
#include <algorithm>
#include <stdint.h>

#if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__)
#define MY_CPU_AMD64
#endif

#if defined(MY_CPU_AMD64) || defined(_M_IA64)
#define MY_CPU_64BIT
#endif

typedef uint32_t ElementT;        // data items type, 32-bit unsigned integer for GF(P) computations with P>65536
typedef uint64_t DoubleElementT;  // twice wider type to hold intermediate results

template <ElementT P>
ElementT GF_Add (ElementT X, ElementT Y)
{
    ElementT res = X + Y;
    return res - ((res>=P)+(res<X))*P;   // (res>=P || res<X)? res-P : res;
}

template <ElementT P>
ElementT GF_Sub (ElementT X, ElementT Y)
{
    ElementT res = X - Y;
    return res + (res>X)*P;   // res<=X? res : res+P
}

#if 0
// Alternative GF_Mul64 implementation for GCC - unfortunately, GCC 4.9 generates over-smart code for it

#include <inttypes.h>
typedef unsigned __int128 uint128_t;
typedef uint128_t FourElement;    // 4x wider type to hold intermediate MUL results

template <ElementT P>
ElementT GF_Mul64 (ElementT X, ElementT Y)
{
    DoubleElementT res = DoubleElementT(X)*Y;
    DoubleElementT invP = DoubleElementT((FourElement(1)<<64) / P);
    res -= DoubleElementT(((res)*FourElement(invP)) >> 64) * P;
    return ElementT(res>=P? res-P : res);
}

#elif _MSC_VER
// Alternative GF_Mul64 implementation made with MSVC intrinsics

template <ElementT P>
ElementT GF_Mul64 (ElementT X, ElementT Y)
{
    const DoubleElementT estInvP = ((DoubleElementT(1)<<63) / P) << 1;
    const DoubleElementT invP    = (estInvP*P > (estInvP+1)*P? estInvP : estInvP+1);
    DoubleElementT res = DoubleElementT(X)*Y;
    res -= __umulh(res,invP) * P;
    return ElementT(res>=P? res-P : res);
}

#else

// GF_Mul64 is optimized for 64-bit CPUs
template <ElementT P>
ElementT GF_Mul64 (ElementT X, ElementT Y)
{
    return ElementT( (DoubleElementT(X)*Y) % P);
}

#endif

// GF_Mul32 is optimized for 32-bit CPUs, SIMD and GPUs
template <ElementT P>
ElementT GF_Mul32 (ElementT X, ElementT Y)
{
    // invP32 := (2**64)/P - 2**32  :  if 2**31<P<2**32, then 2**32 < (2**64)/P < 2**33, and invP32 is a 32-bit value
    const DoubleElementT estInvP = ((DoubleElementT(1)<<63) / P) << 1;                          // == invP & (~1)
    const ElementT       invP32  = ElementT(estInvP*P > (estInvP+1)*P? estInvP : estInvP+1);    // we can't use 1<<64 for exact invP computation so we add the posible 1 in other way
    DoubleElementT res = DoubleElementT(X)*Y;
    res  -=  ((res + (res>>32)*invP32) >> 32) * P;    // The same as res -= ((res*invP) >> 64) * P, where invP = (2**64)/P, but optimized for 32-bit computations
    return ElementT(res>=P? res-P : res);
}

#ifdef MY_CPU_64BIT
#define GF_Mul GF_Mul64
#else
#define GF_Mul GF_Mul32
#endif

template <typename T, T P>
void apply (T* data, size_t N, size_t SIZE, T root)
{
    if (N>1) {
        T root_sqr = GF_Mul<P> (root, root);  // first root of power N of 1
        #pragma omp task if (N>1024)
        apply<T,P> (data,        N/2, SIZE, root_sqr);
        #pragma omp task if (N>1024)
        apply<T,P> (data+N*SIZE, N/2, SIZE, root_sqr);
        #pragma omp taskwait
    }

    T root_i = root;   // first root of power 2N of 1
    for (size_t i=0; i<N*SIZE; i+=SIZE) {
        for (size_t k=0; k<SIZE; k++) {
            size_t i1 = i+k, i2 = i+k+N*SIZE;
            T temp   = GF_Mul<P> (root_i, data[i2]);
            data[i2] = GF_Sub<P> (data[i1], temp);
            data[i1] = GF_Add<P> (data[i1], temp);
        }
        root_i = GF_Mul<P> (root_i, root);  // next root of power 2N of 1
    }
}

template <size_t Exp, size_t SIZE, typename T, T P>
class NTT
{
    enum { N = 1<<Exp };
public:
    void ntt (T* data)
    {
        T root = 1557;                      // init 'root' with root of 1 of power 2**20 in GF(0xFFF00001)
        for (int i=20; --i>Exp; )
            root = GF_Mul<P> (root, root);  // find root of 1 of power 2N
        apply<T,P> (data, N, SIZE, root);
    }
};

// Find first root of 1 of power 2**N
template <typename T, T P>
void FindRoot (int N)
{
    for (T i=2; i<P; i++)
    {
        T q = i;
        for (int j=0; j<N; j++)
        {
            if (q==1)  goto next;
            q = GF_Mul<P> (q,q);
        }
        if (q==1)
        {
            std::cout << i << "\n";
            break;
        }
next:;
    }
}

// Test the GF_Mul32 correctness
template <ElementT P>
void Test_GF_Mul32()
{
    int n = 0;
    for (ElementT i=P; --i; )
    {
        std::cout << std::hex << "\r" << i << "...";
        for (ElementT j=i; --j; )
            if (GF_Mul64<P> (i,j)  !=  GF_Mul32<P> (i,j))
            {
                std::cout << std::hex << "\r" << i << "*" << j << "=" << GF_Mul64<P> (i,j) << " != " << GF_Mul32<P> (i,j) << "\n" ;
                if (++n>10) return;
            }
    }
}

int main()
{
    const ElementT P = 0xFFF00001;
    // Test_GF_Mul32<P>();
    // FindRoot<ElementT,P>(20);  // prints 1557

    const size_t SIZE = 128<<20;  // 512 MB
    ElementT *data = new ElementT[SIZE];
    for (int i=0; i<SIZE; i++)
        data[i] = i;

    NTT<19,SIZE/1048576,ElementT,P> transformer;
    #pragma omp parallel num_threads(16)
    #pragma omp single
    transformer.ntt(data);

    uint32_t sum = 314159253;
    for (int i=0; i<SIZE; i++)
        sum = (sum+data[i])*123456791;
    if (sum != 3267607014UL)
        printf("checksum failed: %.0lf", double(sum));

    return 0;
}
<|endoftext|>


<commit_before>#ifndef MJOLNIR_NVE_NEWTONIAN_INTEGRATOR
#define MJOLNIR_NVE_NEWTONIAN_INTEGRATOR
#include "System.hpp"
#include <mjolnir/util/zip_iterator.hpp>
#include <mjolnir/util/make_zip.hpp>

namespace mjolnir
{

template<typename traitsT>
class NewtonianStepper
{
  public:
    typedef traitsT traits_type;
    typedef typename traits_type::boundary_type   boundary_type;
    typedef typename traits_type::real_type       real_type;
    typedef typename traits_type::coordinate_type coordinate_type;
    typedef System<traitsT> system_type;
    typedef ForceField<traitsT> forcefield_type;

  public:

    NewtonianStepper(const time_type dt, const std::size_t number_of_particles)
        : dt_(dt), halfdt_(dt * 0.5), halfdt2_(dt * dt * 0.5),
          acceleration_(number_of_particles)
    {}
    ~NewtonianStepper() = default;

    void initialize(const system_type& sys);

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

    real_type delta_t() const {return dt_;}
    void set_delta_t(const real_type dt)
    {
        dt_ = dt; halfdt_ = dt * 0.5; halfdt2_ = halfdt_ * dt_;
    }

  private:

    void adjust_translation(system_type& sys) const;
    void adjust_rotation   (system_type& sys) const;

  private:
    time_type dt_;      //!< dt
    time_type halfdt_;  //!< dt/2
    time_type halfdt2_; //!< dt^2/2
    std::vector<coordinate_type> acceleration_;
};

template<typename traitsT>
void NewtonianStepper<traitsT>::initialize(const system_type& sys)
{
    for(auto iter = make_zip(sys.cbegin(), acceleration_.begin());
            iter != make_zip(sys.cend(),   acceleration_.end()); ++iter)
    {
        *get<1>(iter) = get<0>(iter)->force / get<0>(iter)->mass;
    }
    return;
}

template<typename traitsT>
typename NewtonianStepper<traitsT>::real_type
NewtonianStepper<traitsT>::step(
        const time_type time, system_type& system, forcefield_type& ff)
{
    // calc r(t+dt)
    for(auto iter = make_zip(system.begin(), acceleration_.cbegin());
            iter != make_zip(system.end(), acceleration_.cend()); ++iter)
    {
        get<0>(iter)->position = system.adjust_position(
            get<0>(iter)->position + dt_ * (get<0>(iter)->velocity) +
            halfdt2_ * (*get<1>(iter)));
        get<0>(iter)->velocity += halfdt_ * (*get<1>(iter));
    }

    // calc f(t+dt)
    ff.calc_force(system);

    // calc a(t+dt) and v(t+dt)
    for(auto iter = make_zip(system.begin(), acceleration_.begin());
            iter != make_zip(system.end(), acceleration_.end()); ++iter)
    {
        // consider cash 1/m
        const coordinate_type acc = get<0>(iter)->force / (get<0>(iter)->mass);
        *get<1>(iter) = acc;
        get<0>(iter)->velocity += halfdt_ * acc;
        get<0>(iter)->force = coordinate_type(0., 0., 0.);
    }

    this->adjust_translation(system);
    this->adjust_rotation(system);

    return time + dt_;
}

template<typename traitsT>
void NewtonianStepper<traitsT>::adjust_translation(system_type& pcon) const
{
    coordinate_type translation(0, 0, 0);
    for(auto iter = pcon.begin(); iter != pcon.end(); ++iter)
        translation += iter->velocity;

    translation *= (1. / static_cast<real_type>(pcon.size()));

    for(auto iter = pcon.begin(); iter != pcon.end(); ++iter)
        iter->velocity -= translation;
    return;
}

template<typename traitsT>
void NewtonianStepper<traitsT>::adjust_rotation(system_type& pcon) const
{
    coordinate_type L(0, 0, 0); // total angular momentum
    for(auto iter = pcon.cbegin(); iter != pcon.cend(); ++iter)
        L += iter->mass * cross_product(iter->position, iter->velocity);

    // TODO 1. define matrix_type in traits type
    //      2. SymmetricMatrix is needed
    Matrix<real_type, 3, 3> I(0,0,0,0,0,0,0,0,0);// inertia tensor
    for(auto iter = pcon.cbegin(); iter != pcon.cend(); ++iter)
    {
        const real_type        m = iter->mass;
        const coordinate_type& r = iter->position;

        const real_type ixx = m * r[0] * r[0];
        const real_type iyy = m * r[1] * r[1];
        const real_type izz = m * r[2] * r[2];
        const real_type ixy = m * r[0] * r[1];
        const real_type iyz = m * r[1] * r[2];
        const real_type izx = m * r[2] * r[1];

        I(0, 0) += iyy + izz;
        I(0, 1) += -ixy;
        I(0, 2) += -izx;
        I(1, 1) += izz + ixx;
        I(1, 2) += -iyz;
        I(2, 2) += ixx + iyy;
    }
    I(1, 0) = I(0, 1);
    I(2, 0) = I(0, 2);
    I(2, 1) = I(1, 2);

    // TODO more sophisticated way like LU decomposition is needed
    const coordinate_type omega = inverse(I) * L; // angular velocity

    for(auto iter = pcon.begin(); iter != pcon.end(); ++iter)
        iter->velocity -= cross_product(omega, iter->position);

    return;
}

} // mjolnir
#endif /* MJOLNIR_VELOCITY_VERLET_INTEGRATOR */
<commit_msg>remove number_of_particles from NewtonianStepper ctor<commit_after>#ifndef MJOLNIR_NVE_NEWTONIAN_INTEGRATOR
#define MJOLNIR_NVE_NEWTONIAN_INTEGRATOR
#include "System.hpp"
#include <mjolnir/util/zip_iterator.hpp>
#include <mjolnir/util/make_zip.hpp>

namespace mjolnir
{

template<typename traitsT>
class NewtonianStepper
{
  public:
    typedef traitsT traits_type;
    typedef typename traits_type::boundary_type   boundary_type;
    typedef typename traits_type::real_type       real_type;
    typedef typename traits_type::coordinate_type coordinate_type;
    typedef System<traitsT> system_type;
    typedef ForceField<traitsT> forcefield_type;

  public:

    NewtonianStepper(const time_type dt)
        : dt_(dt), halfdt_(dt * 0.5), halfdt2_(dt * dt * 0.5)
    {}
    ~NewtonianStepper() = default;

    void initialize(const system_type& sys);

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

    real_type delta_t() const {return dt_;}
    void set_delta_t(const real_type dt)
    {
        dt_ = dt; halfdt_ = dt * 0.5; halfdt2_ = halfdt_ * dt_;
    }

  private:

    void adjust_translation(system_type& sys) const;
    void adjust_rotation   (system_type& sys) const;

  private:
    time_type dt_;      //!< dt
    time_type halfdt_;  //!< dt/2
    time_type halfdt2_; //!< dt^2/2
    std::vector<coordinate_type> acceleration_;
};

template<typename traitsT>
void NewtonianStepper<traitsT>::initialize(const system_type& sys)
{
    acceleration_.resize(sys.size());

    for(auto iter = make_zip(sys.cbegin(), acceleration_.begin());
            iter != make_zip(sys.cend(),   acceleration_.end()); ++iter)
    {
        *get<1>(iter) = get<0>(iter)->force / get<0>(iter)->mass;
    }
    return;
}

template<typename traitsT>
typename NewtonianStepper<traitsT>::real_type
NewtonianStepper<traitsT>::step(
        const time_type time, system_type& system, forcefield_type& ff)
{
    // calc r(t+dt)
    for(auto iter = make_zip(system.begin(), acceleration_.cbegin());
            iter != make_zip(system.end(), acceleration_.cend()); ++iter)
    {
        get<0>(iter)->position = system.adjust_position(
            get<0>(iter)->position + dt_ * (get<0>(iter)->velocity) +
            halfdt2_ * (*get<1>(iter)));
        get<0>(iter)->velocity += halfdt_ * (*get<1>(iter));
    }

    // calc f(t+dt)
    ff.calc_force(system);

    // calc a(t+dt) and v(t+dt)
    for(auto iter = make_zip(system.begin(), acceleration_.begin());
            iter != make_zip(system.end(), acceleration_.end()); ++iter)
    {
        // consider cash 1/m
        const coordinate_type acc = get<0>(iter)->force / (get<0>(iter)->mass);
        *get<1>(iter) = acc;
        get<0>(iter)->velocity += halfdt_ * acc;
        get<0>(iter)->force = coordinate_type(0., 0., 0.);
    }

    this->adjust_translation(system);
    this->adjust_rotation(system);

    return time + dt_;
}

template<typename traitsT>
void NewtonianStepper<traitsT>::adjust_translation(system_type& pcon) const
{
    coordinate_type translation(0, 0, 0);
    for(auto iter = pcon.begin(); iter != pcon.end(); ++iter)
        translation += iter->velocity;

    translation *= (1. / static_cast<real_type>(pcon.size()));

    for(auto iter = pcon.begin(); iter != pcon.end(); ++iter)
        iter->velocity -= translation;
    return;
}

template<typename traitsT>
void NewtonianStepper<traitsT>::adjust_rotation(system_type& pcon) const
{
    coordinate_type L(0, 0, 0); // total angular momentum
    for(auto iter = pcon.cbegin(); iter != pcon.cend(); ++iter)
        L += iter->mass * cross_product(iter->position, iter->velocity);

    // TODO 1. define matrix_type in traits type
    //      2. SymmetricMatrix is needed
    Matrix<real_type, 3, 3> I(0,0,0,0,0,0,0,0,0);// inertia tensor
    for(auto iter = pcon.cbegin(); iter != pcon.cend(); ++iter)
    {
        const real_type        m = iter->mass;
        const coordinate_type& r = iter->position;

        const real_type ixx = m * r[0] * r[0];
        const real_type iyy = m * r[1] * r[1];
        const real_type izz = m * r[2] * r[2];
        const real_type ixy = m * r[0] * r[1];
        const real_type iyz = m * r[1] * r[2];
        const real_type izx = m * r[2] * r[1];

        I(0, 0) += iyy + izz;
        I(0, 1) += -ixy;
        I(0, 2) += -izx;
        I(1, 1) += izz + ixx;
        I(1, 2) += -iyz;
        I(2, 2) += ixx + iyy;
    }
    I(1, 0) = I(0, 1);
    I(2, 0) = I(0, 2);
    I(2, 1) = I(1, 2);

    // TODO more sophisticated way like LU decomposition is needed
    const coordinate_type omega = inverse(I) * L; // angular velocity

    for(auto iter = pcon.begin(); iter != pcon.end(); ++iter)
        iter->velocity -= cross_product(omega, iter->position);

    return;
}

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


<commit_before>#include "common.th"
#include "errno.th"

#define RANK_0_LOG 4
#define RANK_0_SIZE (1 << RANK_0_LOG)
#define RANKS 4

// NOTE there are some assumptions in the below code that NPER * BPER = 32,
// notably in DIFF()
#define NPERBITS    4
#define BPERBITS    1
#define NPER        (1 << NPERBITS)
#define BPER        (1 << BPERBITS)
#define TN          0
#define POOL        rel(pool)

// DATA ------------------------------------------------------------------------
counts: .zero (RANKS - 1) ; .word 1
nodes:  .zero (((1 << RANKS) / NPER) - (1 / NPER))
pool:   .zero (RANK_0_SIZE * (1 << (RANKS - 1)))

// Type `SZ' is a word-wide unsigned integer (some places used as a boolean).
// Type `NP' is a word-wide struct, where the word is split into a 28-bit word
// index into `nodes` and a 4-bit index into the word indexed.

ilog2:
    B   <- 0
    push(D)
L_ilog2_top:
    C   <- C >> 1
    D   <- C == 0
    jnzrel(D, L_ilog2_done)
    B   <- B + 1
    goto(L_ilog2_top)
L_ilog2_done:
    pop(D)
    ret

// NODE gets SZ index in C, returns NP node in B, clobbers C
#define NODE(B,C)               \
    B   <- C << BPERBITS      ; \
    B   <- B + TN             ; \
    C   <- C & (BPER - 1)     ; \
    B   <- B | C              ; \
    //

// IRANK gets SZ rank in C, returns SZ inverted-rank in B
#define IRANK(B,C)              \
    B   <- - C + (RANKS - 1)  ; \
    //

// DIFF gets NP a in C, NP b in D, returns SZ distance in B
#define DIFF(B,C,D)             \
    B   <- C - D              ; \
    //

// INDEX gets NP n in C, returns SZ distance from TN in B
#define INDEX(B,C)              \
    DIFF(B,C,TN)              ; \
    //

// LLINK gets NP n in C, returns NP left-child in B, clobbers C
#define LLINK(B,C)              \
    INDEX(B,C)                ; \
    C   <- B << 1             ; \
    C   <- C +  1             ; \
    NODE(B,C)                 ; \
    //

// RLINK gets NP n in C, returns NP right-child in B, clobbers C
#define RLINK(B,C)              \
    INDEX(B,C)                ; \
    C   <- B << 1             ; \
    C   <- C +  2             ; \
    NODE(B,C)                 ; \
    //

// ISLEFT gets NP n in C, returns SZ leftness in B
#define ISLEFT(B,C)             \
    INDEX(B,C)                ; \
    B   <- B & 1              ; \
    B   <- B == 1             ; \
    //

// ISRIGHT gets NP n in C, returns SZ rightness in B
#define ISRIGHT(B,C)            \
    INDEX(B,C)                ; \
    B   <- B & 1              ; \
    B   <- B == 0             ; \
    //

// PARENT gets NP n in C, returns NP parent in B, clobbers C
#define PARENT(B,C)             \
    INDEX(B,C)                ; \
    C   <- B -  1             ; \
    C   <- C >> 1             ; \
    NODE(B,C)                 ; \
    //

// SIBLING gets NP n in C, returns NP sibling in B, clobbers C
#define SIBLING(B,C)            \
    INDEX(B,C)                ; \
    ISLEFT(C,B)               ; \
    B   <- B - C              ; \
    C   <- ~ C                ; \
    B   <- B + C              ; \
    //

// NODE2RANK gets NP n in C, returns SZ rank in B
#define NODE2RANK(B,C)          \
    call(NODE2RANK_func)      ; \
    //

NODE2RANK_func:
    push(D)
    D   <- 0
L_NODE2RANK_top:
    INDEX(B,C)
    B   <- B == 0
    jnzrel(B, L_NODE2RANK_done)
    D   <- D + 1
    B   <- C
    PARENT(C,B)
    goto(L_NODE2RANK_top)
L_NODE2RANK_done:
    B   <- - D + (RANKS - 1)
    pop(D)
    ret

#define SIZE2RANK(B,C) \
    call(SIZE2RANK_func)
    //

SIZE2RANK_func:
    B   <- C <> 0
    jzrel(B, L_SIZE2RANK_zero)
    C   <- C - 1
    C   <- C >> RANK_0_LOG
    call(ilog2)
    B   <- B + 1
L_SIZE2RANK_zero:
    ret

// RANK2WORDS gets SZ rank in C, returns SZ word-count in B, clobbers C
#define RANK2WORDS(B,C)         \
    C   <- C + RANK_0_LOG     ; \
    B   <- 1                  ; \
    B   <- B << C             ; \
    //

// GET_COUNT gets SZ rank in C, returns SZ free-count in B
#define GET_COUNT(B,C)          \
    B   <- [C + rel(counts)]  ; \
    //

// SET_COUNT gets SZ rank in C, SZ free-count in D, returns nothing
#define SET_COUNT(C,D)          \
    D   -> [C + rel(counts)]  ; \
    //

// GET_NODE gets NP n in C, returns SZ shifted-word in B, clobbers C
#define GET_NODE(B,C)           \
    B   <- C >> BPERBITS      ; \
    C   <- C & (BPER - 1)     ; \
    C   <- C << BPERBITS      ; \
    B   <- [B + rel(nodes)]   ; \
    B   <- B >> C             ; \
    //

// SET_NODE gets NP n in C, SZ pre-shift-word in D, clobbers C and D
#define SET_NODE(C,D,T0,T1)                                                 \
    T0  <- C & (BPER - 1)       /* T0 is shift count in nodes        */   ; \
    T0  <- T0 << BPERBITS       /* T0 is shift count in bits         */   ; \
    D   <- D << T0              /* D  is now shifted over            */   ; \
    T1  <- (BPER - 1)           /* T1 is mask                        */   ; \
    T1  <- T1 << T0             /* T1 is shifted mask                */   ; \
    T0  <- C >> BPERBITS        /* T0 is index into node array       */   ; \
    C   <- [T0 + rel(nodes)]    /* C  is current word                */   ; \
    C   <- C &~ T1              /* C  is word with node masked out   */   ; \
    C   <- C | D                /* C  is word with new node included */   ; \
    C   -> [T0 + rel(nodes)]    /* C  gets written back to node      */   ; \
    //

// GET_LEAF gets NP n in C, returns SZ leafiness in B, clobbers C
#define GET_LEAF(B,C)           \
    GET_NODE(B,C)             ; \
    B   <- B &  1             ; \
    B   <- B == 0             ; \
    //

// GET_FULL gets NP n in C, returns SZ fullness in B, clobbers C
#define GET_FULL(B,C)           \
    GET_NODE(B,C)             ; \
    B   <- B &  2             ; \
    B   <- B == 2             ; \
    //

// GET_VALID gets NP n in C, returns SZ validity in B
#define GET_VALID(B,C,T0)       \
    T0  <- C                  ; \
    INDEX(B,C)                ; \
    PARENT(C,T0)              ; \
    GET_LEAF(T0,C)            ; \
    B   <- B &~ T0            ; \
    //

// SET_LEAF gets NP n in C, SZ truth in D, returns nothing, clobbers C
// We take a shortcut ; either we are setting the leaf true from false, and it
// is therefore not full, or we are setting it false from true, and fullness
// has no meaning. This allows us to avoid reading and rewriting the fullness
// value.
#define SET_LEAF(C,D,T0,T1)     \
    D   <- D & 1              ; \
    SET_NODE(C,D,T0,T1)       ; \
    //

// SET_FULL gets NP n in C, SZ truth in D, returns nothing, clobbers C
#define SET_FULL(C,D,T0,T1,T2)  \
    GET_NODE(T0,C)            ; \
    T1  <- D                  ; \
    T1  <- T1 &  2            ; \
    T0  <- T0 &~ 2            ; \
    T1  <- T1 |  T0           ; \
    SET_NODE(C,T1,T0,T2)      ; \
    //

// TODO NODE2ADDR

// ADDR2NODE gets SZ addr in C, returns NP node in B
#define ADDR2NODE(B,C)          \
    call(ADDR2NODE_func)      ; \
    //

// C is key
ADDR2NODE_func:
    pushall(D,E,F,G,H)
    D   <- (RANKS - 2)  // D is next rank smaller than node under test
    E   <- POOL         // E is base being built
    F   <- C            // F is a scratch register
    G   <- TN           // G is the node being checked
    H   <- G            // H is another scratch
L_ADDR2NODE_looptop:
    GET_LEAF(B,H)
    jnzrel(B,L_ADDR2NODE_loopdone)
    RANK2WORDS(B,F)
    H   <- C - E        // H is (key - base)
    F   <- H < B        // if true, then left-link
    jnzrel(F,L_ADDR2NODE_left)
    H   <- G
    RLINK(G,H)          // n = RLINK(n)
    E   <- E + B        // base += RANK2WORDS(rank)
    goto(L_ADDR2NODE_loopbottom)
L_ADDR2NODE_left:
    H   <- G
    LLINK(G,H)          // n = LLINK(n)
    // fallthrough

L_ADDR2NODE_loopbottom:
    D   <- D - 1
    goto(L_ADDR2NODE_looptop)

L_ADDR2NODE_loopdone:
    B   <- C == E
    jnzrel(B,L_ADDR2NODE_done)
    call(abort)

L_ADDR2NODE_done:
    popall(D,E,F,G,H)
    ret

.global buddy_malloc
buddy_malloc:
    goto(buddy_alloc)

.global buddy_calloc
buddy_calloc:
    C   <- C * D
    push(C)
    call(buddy_alloc)
    C   <- B
    D   <- 0
    pop(E)
    call(memset)
    ret

.global buddy_free
buddy_free:
    // TODO
    ret

.global buddy_realloc
buddy_realloc:
    // TODO
    ret

// -----------------------------------------------------------------------------

// buddy_splitnode gets NP n in C, SZ rank in D, returns SZ success in B
buddy_splitnode:
    pushall(E,F)
    B   <- 0
    E   <- D == 0
    jnzrel(E,L_buddy_splitnode_done)
    B   <- 1
    GET_FULL(E,C)
    E   <- E <> 0
    jnzrel(E,L_buddy_splitnode_notempty)

    GET_COUNT(E,D)
    E   <- E - 1
    SET_COUNT(D,E)

L_buddy_splitnode_notempty:
    D   <- D - 1
    GET_COUNT(E,D)
    E   <- E + 2
    SET_COUNT(D,E)

    D   <- 0
    SET_LEAF(C,D,E,F)

L_buddy_splitnode_done:
    popall(E,F)
    ret

buddy_autosplit:
    // TODO
    ret

// buddy_nosplit gets SZ rank in C, returns address or 0 in B
buddy_nosplit:
    pushall(D,E,F,G)
    // here is the main body, where we have a nonzero count of the needed
    // rank. B is rank, D is scratch, E is scratch and currently count
    D   <- B
    IRANK(E,D)
    F   <- 1
    F   <- F << E       // F is loop bound
    D   <- F - 1        // D is first index to check
    F   <- F + D        // F is loop bound adjust for `first'
L_buddy_nosplit_loop_top:
    NODE(E,D)           // E is NODE(D)
    GET_VALID(C,E,G)    // C is validity
    jzrel(C,L_buddy_nosplit_loop_bottom)
    GET_LEAF(C,E)       // C is leafiness
    jzrel(C,L_buddy_nosplit_loop_bottom)
    GET_FULL(C,E)       // C is fullness
    jnzrel(C,L_buddy_nosplit_loop_bottom)
    SET_FULL(E,-1,D,G,C)
    GET_COUNT(C,B)
    C   <- C - 1
    SET_COUNT(E,C)
    C   <- E
    ADDR2NODE(B,C)

L_buddy_nosplit_loop_bottom:
    D   <- D + 1    // D is loop index
    goto(L_buddy_nosplit_loop_top)

    popall(D,E,F,G)
    ret

// TODO check for clobbering
// buddy_alloc gets SZ size in C, returns address or 0 in B
buddy_alloc:
    pushall(D,E)
    SIZE2RANK(B,C)
    D   <- B < RANKS
    jzrel(D,L_buddy_alloc_error)
    D   <- B
    GET_COUNT(E,D)
    D   <- E == 0
    jnzrel(D,L_buddy_alloc_do_split)
    call(buddy_nosplit)
    goto(L_buddy_alloc_done)

L_buddy_alloc_do_split:
    D   <- E
    call(buddy_autosplit)

L_buddy_alloc_done:
    popall(D,E)
    ret

L_buddy_alloc_error:
    D   <- ENOMEM
    D   -> errno
    B   <- 0
    goto(L_buddy_alloc_done)

<commit_msg>Major rewrite of tenyr buddy allocator<commit_after>#include "common.th"
#include "errno.th"

#define RANK_0_LOG 4
#define RANK_0_SIZE (1 << RANK_0_LOG)
#define RANKS 4

// NOTE it is assumed in the below code that NPER * BPER = 32
#define NPERBITS    4
#define BPERBITS    1
#define NPER        (1 << NPERBITS)
#define BPER        (1 << BPERBITS)
#define TN          0
#define POOL        rel(pool)

// DATA ------------------------------------------------------------------------
counts: .zero (RANKS - 1) ; .word 1
nodes:  .zero (((1 << RANKS) / NPER) - (1 / NPER))
pool:   .zero (RANK_0_SIZE * (1 << (RANKS - 1)))

// Type `SZ' is a word-wide unsigned integer (some places used as a boolean).
// Type `NP' is a word-wide struct, where the word is split into a 28-bit word
// index into `nodes` and a 4-bit index into the word indexed.

ilog2:
    B   <- 0
    push(D)
L_ilog2_top:
    C   <- C >> 1
    D   <- C == 0
    jnzrel(D, L_ilog2_done)
    B   <- B + 1
    goto(L_ilog2_top)
L_ilog2_done:
    pop(D)
    ret

// IRANK gets SZ rank in C, returns SZ inverted-rank in B
#define IRANK(B,C)              \
    B   <- - C + (RANKS - 1)  ; \
    //

// LLINK gets NP n in C, returns NP left-child in B
#define LLINK(B,C)              \
    B   <- C << 1             ; \
    B   <- B +  1             ; \
    //

// RLINK gets NP n in C, returns NP right-child in B
#define RLINK(B,C)              \
    B   <- C << 1             ; \
    B   <- B +  2             ; \
    //

// ISLEFT gets NP n in C, returns SZ leftness in B
#define ISLEFT(B,C)             \
    B   <- C &  1             ; \
    B   <- B == 1             ; \
    //

// ISRIGHT gets NP n in C, returns SZ rightness in B
#define ISRIGHT(B,C)            \
    B   <- C &  1             ; \
    B   <- B == 0             ; \
    //

// PARENT gets NP n in C, returns NP parent in B
#define PARENT(B,C)             \
    B   <- C -  1             ; \
    B   <- B >> 1             ; \
    //

// SIBLING gets NP n in C, returns NP sibling in B, T may be C
#define SIBLING(B,C,T)          \
    ISLEFT(T,C)               ; \
    B   <- B - T              ; \
    T   <- ~ T                ; \
    B   <- B + T              ; \
    //

#define FUNCIFY2(Stem,B,C)      \
    push(c)                   ; \
    c   <- C                  ; \
    call(Stem##_func)         ; \
    pop(c)                    ; \
    B   <- b                  ; \
    //

// NODE2RANK gets NP n in C, returns SZ rank in B
#define NODE2RANK(B,C)  FUNCIFY2(NODE2RANK,B,C)
NODE2RANK_func:
    push(D)
    D   <- 0
L_NODE2RANK_top:
    B   <- C == 0
    jnzrel(B, L_NODE2RANK_done)
    D   <- D + 1
    PARENT(C,C)
    goto(L_NODE2RANK_top)
L_NODE2RANK_done:
    B   <- - D + (RANKS - 1)
    pop(D)
    ret

#define SIZE2RANK(B,C)  FUNCIFY2(SIZE2RANK,B,C)
SIZE2RANK_func:
    B   <- C <> 0
    jzrel(B, L_SIZE2RANK_zero)
    C   <- C - 1
    C   <- C >> RANK_0_LOG
    call(ilog2)
    B   <- B + 1
L_SIZE2RANK_zero:
    ret

// RANK2WORDS gets SZ rank in C, returns SZ word-count in B, T may be C
// TODO update grammar to permit expressions without parens to save insns
#define RANK2WORDS(B,X,T)       \
    T   <- X                  ; \
    T   <- T + RANK_0_LOG     ; \
    B   <- 1                  ; \
    B   <- B << T             ; \
    //

// GET_COUNT gets SZ rank in C, returns SZ free-count in B
#define GET_COUNT(B,C)          \
    B   <- [C + rel(counts)]  ; \
    //

// SET_COUNT gets SZ rank in C, SZ free-count in D, returns nothing
#define SET_COUNT(C,D)          \
    D   -> [C + rel(counts)]  ; \
    //

// GET_NODE gets NP n in C, returns SZ shifted-word in B, T may be C
#define GET_NODE(B,C,T)         \
    B   <- C >> BPERBITS      ; \
    T   <- C & (BPER - 1)     ; \
    T   <- T << BPERBITS      ; \
    B   <- [B + rel(nodes)]   ; \
    B   <- B >> T             ; \
    //

// SET_NODE gets NP n in C, SZ pre-shift-word in D, V can be D
#define SET_NODE(C,D,T,U,V)                                              \
    T   <- C & (BPER - 1)       /* T is shift count in nodes        */ ; \
    T   <- T << BPERBITS        /* T is shift count in bits         */ ; \
    U   <- (BPER - 1)           /* U is mask                        */ ; \
    U   <- U << T               /* U is shifted mask                */ ; \
    V   <- C >> BPERBITS        /* V is index into node array       */ ; \
    C   <- [V + rel(nodes)]     /* C is current word                */ ; \
    C   <- C &~ U               /* C is word with node masked out   */ ; \
    U   <- D << T               /* U is now D shifted over          */ ; \
    C   <- C | U                /* C is word with new node included */ ; \
    C   -> [V + rel(nodes)]     /* C gets written back to node      */ ; \
    //

// GET_LEAF gets NP n in C, returns SZ leafiness in B, T may be C
#define GET_LEAF(B,C,T)         \
    GET_NODE(B,C,T)           ; \
    B   <- B &  1             ; \
    B   <- B == 0             ; \
    //

// GET_FULL gets NP n in C, returns SZ fullness in B, T may be C
#define GET_FULL(B,C,T)         \
    GET_NODE(B,C,T)           ; \
    B   <- B &  2             ; \
    B   <- B == 2             ; \
    //

// GET_VALID gets NP n in C, returns SZ validity in B
#define GET_VALID(B,C,T,U)     \
    PARENT(C,C)              ; \
    GET_LEAF(T,C,U)          ; \
    B   <- C &~ T            ; \
    //

// SET_LEAF gets NP n in C, SZ truth in D, returns nothing
// T,W can be D, clobbers C
// We take a shortcut ; either we are setting the leaf true from false, and it
// is therefore not full, or we are setting it false from true, and fullness
// has no meaning. This allows us to avoid reading and rewriting the fullness
// value.
#define SET_LEAF(C,D,T,U,V,W)   \
    T   <- D & 1              ; \
    SET_NODE(C,T,U,V,W)       ; \
    //

// SET_FULL gets NP n in C, SZ truth in X, returns nothing, clobbers C
#define SET_FULL(C,X,T,U,V,W) \
    GET_NODE(T,C,U)         ; \
    U   <- X                ; \
    U   <- U &  2           ; \
    T   <- T &~ 2           ; \
    U   <- U | T            ; \
    SET_NODE(C,U,T,V,W)     ; \
    //

// NODE2ADDR gets NP n in C, returns address in B
#define NODE2ADDR(B,C)  FUNCIFY2(NODE2ADDR,B,C)
NODE2ADDR_func:
    pushall(D,E)
    B   <- 0
    NODE2RANK(E,C)
    E   <- RANK_0_LOG + E   // i = RANK_0_LOG + NODE2RANK(n)
NODE2ADDR_looptop:
    D   <- C == TN          // while (n != TN) {
    jnzrel(D,NODE2ADDR_loopbottom)
    ISRIGHT(D,C)            // ISRIGHT(n)
    D   <- - D              // -1 -> 1, 0 -> 0
    D   <- D << E           // ISRIGHT(n) << i
    B   <- B | D            // base |= ISRIGHT(n) << i
    PARENT(C,C)             // n = PARENT(n)
    E   <- E + 1            // i++
    goto(NODE2ADDR_looptop) // }
NODE2ADDR_loopbottom:
    B   <- B + rel(pool)
    ret

// ADDR2NODE gets SZ addr in C, returns NP node in B
#define ADDR2NODE(B,C)  FUNCIFY2(ADDR2NODE,B,C)
ADDR2NODE_func:
    pushall(D,E,F,G)    // F, G are scratch
    RANK2WORDS(D,(RANKS - 2),F)
    E   <- POOL         // E is base being built
    B   <- TN           // B is the node being checked
L_ADDR2NODE_looptop:
    GET_LEAF(G,B,F)
    jnzrel(G,L_ADDR2NODE_loopdone)
    F   <- C - E        // F is (key - base)
    F   <- F < D        // if true, then left-link
    jnzrel(F,L_ADDR2NODE_left)
    RLINK(B,B)          // n = RLINK(n)
    E   <- E + D        // base += RANK2WORDS(rank)
    goto(L_ADDR2NODE_loopbottom)
L_ADDR2NODE_left:
    LLINK(B,B)          // n = LLINK(n)
    // fallthrough

L_ADDR2NODE_loopbottom:
    D   <- D >> 1       // drop to next smaller rank
    goto(L_ADDR2NODE_looptop)

L_ADDR2NODE_loopdone:
#if DEBUG
    D   <- C == E
    jnzrel(D,L_ADDR2NODE_done)
    call(abort)

L_ADDR2NODE_done:
#endif
    popall(D,E,F,G)
    ret

.global buddy_malloc
buddy_malloc:
    SIZE2RANK(C,C)
    goto(buddy_alloc)

.global buddy_calloc
buddy_calloc:
    push(E)             // save E which we will use below
    C   <- C * D
    push(C)
    call(buddy_alloc)
    C   <- B
    D   <- 0
    pop(E)              // pop size from C into third memset param slot
    call(memset)
    pop(E)
    ret

.global buddy_free
buddy_free:
    // TODO
    ret

.global buddy_realloc
buddy_realloc:
    // TODO
    ret

// -----------------------------------------------------------------------------

// buddy_splitnode gets NP n in C, SZ rank in D, returns SZ success in B
buddy_splitnode:
    pushall(E,F)
    B   <- 0
    E   <- D == 0
    jnzrel(E,L_buddy_splitnode_done)
    B   <- 1
    GET_FULL(E,C,F)
    E   <- E <> 0
    jnzrel(E,L_buddy_splitnode_notempty)

    GET_COUNT(E,D)
    E   <- E - 1
    SET_COUNT(D,E)

L_buddy_splitnode_notempty:
    D   <- D - 1
    GET_COUNT(E,D)
    E   <- E + 2
    SET_COUNT(D,E)

    D   <- 0
    SET_LEAF(C,D,D,E,F,D)

L_buddy_splitnode_done:
    popall(E,F)
    ret

// XXX buddy_nosplit has been modified without sufficient care and appears to
// be internally inconsistent
// TODO move buddy_nosplit into buddy_alloc as in the C version
// buddy_nosplit gets SZ rank in C, returns address or 0 in B
buddy_nosplit:
    pushall(D,E,F,G)
    B   <- C
    IRANK(E,C)
    F   <- 1
    F   <- F << E       // F is loop bound
    D   <- F - 1        // D is first index to check
    F   <- F + D        // F is loop bound adjust for `first'
L_buddy_nosplit_loop_top:
    // here is the main body, where we have a nonzero count of the needed
    // rank. B is rank, D is scratch, E is scratch and currently count
    E   <- D
    GET_VALID(C,E,F,G)  // C is validity
    jzrel(C,L_buddy_nosplit_loop_bottom)
    GET_LEAF(C,E,G)     // C is leafiness
    jzrel(C,L_buddy_nosplit_loop_bottom)
    GET_FULL(C,E,G)     // C is fullness
    jnzrel(C,L_buddy_nosplit_loop_bottom)
    SET_FULL(E,-1,D,G,C,D)
    GET_COUNT(C,B)
    C   <- C - 1
    SET_COUNT(E,C)
    C   <- E
    ADDR2NODE(B,C)

L_buddy_nosplit_loop_bottom:
    D   <- D + 1    // D is loop index
    goto(L_buddy_nosplit_loop_top)

    popall(D,E,F,G)
    ret

// buddy_alloc gets SZ rank in C, returns address or 0 in B
buddy_alloc:
    pushall(D,E,F)                  // D,E are scratch, F is rank
    D   <- C < RANKS
    jzrel(D,L_buddy_alloc_error)    // bail if need is too large
L_buddy_alloc_rankplus:
    D   <- C < RANKS
    jzrel(D,L_buddy_alloc_rankdone)
    GET_COUNT(E,C)
    E   <- E <> 0
    jnzrel(E,L_buddy_alloc_rankdone)
    F   <- F + 1
    goto(L_buddy_alloc_rankplus)

L_buddy_alloc_rankdone:
    call(buddy_nosplit)
    goto(L_buddy_alloc_done)

L_buddy_alloc_do_split:
    D   <- E
    call(buddy_autosplit)

L_buddy_alloc_done:
    popall(D,E,F)
    ret

L_buddy_alloc_error:
    D   <- ENOMEM
    D   -> errno
    B   <- 0
    goto(L_buddy_alloc_done)

<|endoftext|>


<commit_before>#include <exotica/Loaders/XMLLoader.h>
#include <tinyxml2/tinyxml2.h>
#include <exotica/Setup.h>

namespace exotica
{

    boost::shared_ptr<XMLLoader> XMLLoader::instance_ = nullptr;

    XMLLoader::XMLLoader()
    {

    }

    bool parseXML(tinyxml2::XMLHandle& tag, Initializer& parent, const std::string& prefix);

    void appendChildXML(Initializer& parent, std::string& name, bool isAttribute, tinyxml2::XMLHandle& tag, const std::string& prefix)
    {
        if(isAttribute)
        {
            // Attributes are always a regular property
            std::string value = tag.ToElement()->Attribute(name.c_str());
            parent.addProperty(Property(name,true,value));
        }
        else
        {
            int count = 0;
            for( tinyxml2::XMLHandle child = tag.FirstChild(); child.ToElement(); child = child.NextSibling() ) count++;
            if(count==0)
            {
                // No child tags = this is a regular property
                std::string value;
                if (!tag.ToElement()->GetText())
                {
                    throw_pretty("Can't get value! ("+name+")");
                }
                value = tag.ToElement()->GetText();
                parent.addProperty(Property(name,true,value));
            }
            else
            {
                // Child tags found = this is an initializer
                // All initializers are treated as a vector
                std::vector<Initializer> ret;
                tinyxml2::XMLHandle child_tag(tag.FirstChildElement());
                while(child_tag.ToElement())
                {
                    ret.push_back(Initializer("New"+name));
                    if(!parseXML(child_tag,ret[ret.size()-1],prefix+"- "))
                    {
                        ret.pop_back();
                    }
                    else
                    {
                        //HIGHLIGHT(prefix<<". Adding parsed vector element "<<name<<" to " << parent.getName());
                    }
                    child_tag = child_tag.NextSiblingElement();
                }
                parent.addProperty(Property(name,true,ret));
            }
        }
    }

    // Parses an Initializer parameters from ints attributes and child tags
    bool parseXML(tinyxml2::XMLHandle& tag, Initializer& parent, const std::string& prefix)
    {
        // Get the name
        std::string name = std::string(tag.ToElement()->Name());
        //HIGHLIGHT(prefix<<name<<" added");
        parent.setName("exotica/"+name);

        // Parse values stored in attributes
        tinyxml2::XMLAttribute* attr = const_cast<tinyxml2::XMLAttribute*>(tag.ToElement()->FirstAttribute());
        while(attr)
        {
            std::string member_name = attr->Name();
            appendChildXML(parent,member_name,true,tag,prefix+"- ");
            attr = const_cast<tinyxml2::XMLAttribute*>(attr->Next());
        }

        // Parse values stored in tags
        tinyxml2::XMLHandle member_tag(tag.FirstChildElement());
        while(member_tag.ToElement())
        {
            std::string member_name = std::string(member_tag.ToElement()->Name());
            appendChildXML(parent,member_name,false,member_tag,prefix+"- ");
            member_tag = member_tag.NextSiblingElement();
        }
        //HIGHLIGHT(prefix<<name<<" finished parsing");
        return true;
    }

    Initializer XMLLoader::loadXML(std::string file_name, bool parsePathAsXML)
    {
        tinyxml2::XMLDocument xml_file;
        if(parsePathAsXML)
        {
            if (xml_file.Parse(file_name.c_str(),file_name.size()) != tinyxml2::XML_NO_ERROR)
            {
              throw_pretty("Can't load file!\nFile: '"+file_name+"'");
            }
        }
        else
        {
            if (xml_file.LoadFile(file_name.c_str()) != tinyxml2::XML_NO_ERROR)
            {
              throw_pretty("Can't load file!\nFile: '"+file_name+"'");
            }
        }

        Initializer ret("TopLevel");
        tinyxml2::XMLHandle root_tag(xml_file.RootElement()->FirstChildElement());
        if(!parseXML(root_tag,ret, ""))
        {
            throw_pretty("Can't parse XML!\nFile: '"+file_name+"'");
        }
        return ret;
    }

    void XMLLoader::loadXML(std::string file_name, Initializer& solver, Initializer& problem, const std::string& solver_name, const std::string& problem_name, bool parsePathAsXML)
    {
        tinyxml2::XMLDocument xml_file;
        if(parsePathAsXML)
        {
            if (xml_file.Parse(file_name.c_str(),file_name.size()) != tinyxml2::XML_NO_ERROR)
            {
              throw_pretty("Can't load file!\nFile: '"+file_name+"'");
            }
        }
        else
        {
            if (xml_file.LoadFile(file_name.c_str()) != tinyxml2::XML_NO_ERROR)
            {
              throw_pretty("Can't load file!\nFile: '"+file_name+"'");
            }
        }

        std::vector<Initializer> initializers;
        tinyxml2::XMLHandle root_tag(xml_file.RootElement()->FirstChildElement());
        while (root_tag.ToElement())
        {
            initializers.push_back(Initializer("TopLevel"));
            if(!parseXML(root_tag,initializers[initializers.size()-1], ""))
            {
                initializers.pop_back();
            }
            root_tag = root_tag.NextSiblingElement();
        }
        bool foundSolver = false;
        bool foundProblem = false;
        if (solver_name=="" || solver_name=="")
        {
            for(Initializer& i : initializers)
            {
                std::string initializer_type = i.getName();
                if(!foundSolver)
                {
                    for(std::string known_type : Setup::getSolvers())
                    {
                        if(known_type==initializer_type) {solver = i; foundSolver=true; break;}
                    }
                }
                if(!foundProblem)
                {
                    for(std::string known_type : Setup::getProblems())
                    {
                        if(known_type==initializer_type) {problem = i; foundProblem=true; break;}
                    }
                }
            }
        }
        else
        {
            for(Initializer& i : initializers)
            {
                std::string name = boost::any_cast<std::string>(i.getProperty("Name"));
                if(name==solver_name) {solver = i; foundSolver=true;}
                if(name==problem_name) {problem = i; foundProblem=true;}
            }
        }
        if(!foundSolver) throw_pretty("Can't find solver '"+solver_name+"' in '"+file_name+"'!");
        if(!foundProblem) throw_pretty("Can't find problem '"+problem_name+"' in '"+file_name+"'!");
    }

}
<commit_msg>Enable zero length initializer vectors in XML<commit_after>#include <exotica/Loaders/XMLLoader.h>
#include <tinyxml2/tinyxml2.h>
#include <exotica/Setup.h>

namespace exotica
{

    boost::shared_ptr<XMLLoader> XMLLoader::instance_ = nullptr;

    XMLLoader::XMLLoader()
    {

    }

    bool parseXML(tinyxml2::XMLHandle& tag, Initializer& parent, const std::string& prefix);

    void appendChildXML(Initializer& parent, std::string& name, bool isAttribute, tinyxml2::XMLHandle& tag, const std::string& prefix)
    {
        if(isAttribute)
        {
            // Attributes are always a regular property
            std::string value = tag.ToElement()->Attribute(name.c_str());
            parent.addProperty(Property(name,true,value));
        }
        else
        {
            int count = 0;
            for( tinyxml2::XMLHandle child = tag.FirstChild(); child.ToElement(); child = child.NextSibling() ) count++;
            if(count==0)
            {

                if (!tag.ToElement()->GetText())
                {
                    // No child tags = this is an empty vector of properties
                    return;
                }
                std::string value = tag.ToElement()->GetText();
                parent.addProperty(Property(name,true,value));
            }
            else
            {
                // Child tags found = this is an initializer
                // All initializers are treated as a vector
                std::vector<Initializer> ret;
                tinyxml2::XMLHandle child_tag(tag.FirstChildElement());
                while(child_tag.ToElement())
                {
                    ret.push_back(Initializer("New"+name));
                    if(!parseXML(child_tag,ret[ret.size()-1],prefix+"- "))
                    {
                        ret.pop_back();
                    }
                    else
                    {
                        //HIGHLIGHT(prefix<<". Adding parsed vector element "<<name<<" to " << parent.getName());
                    }
                    child_tag = child_tag.NextSiblingElement();
                }
                parent.addProperty(Property(name,true,ret));
            }
        }
    }

    // Parses an Initializer parameters from ints attributes and child tags
    bool parseXML(tinyxml2::XMLHandle& tag, Initializer& parent, const std::string& prefix)
    {
        // Get the name
        std::string name = std::string(tag.ToElement()->Name());
        //HIGHLIGHT(prefix<<name<<" added");
        parent.setName("exotica/"+name);

        // Parse values stored in attributes
        tinyxml2::XMLAttribute* attr = const_cast<tinyxml2::XMLAttribute*>(tag.ToElement()->FirstAttribute());
        while(attr)
        {
            std::string member_name = attr->Name();
            appendChildXML(parent,member_name,true,tag,prefix+"- ");
            attr = const_cast<tinyxml2::XMLAttribute*>(attr->Next());
        }

        // Parse values stored in tags
        tinyxml2::XMLHandle member_tag(tag.FirstChildElement());
        while(member_tag.ToElement())
        {
            std::string member_name = std::string(member_tag.ToElement()->Name());
            appendChildXML(parent,member_name,false,member_tag,prefix+"- ");
            member_tag = member_tag.NextSiblingElement();
        }
        //HIGHLIGHT(prefix<<name<<" finished parsing");
        return true;
    }

    Initializer XMLLoader::loadXML(std::string file_name, bool parsePathAsXML)
    {
        tinyxml2::XMLDocument xml_file;
        if(parsePathAsXML)
        {
            if (xml_file.Parse(file_name.c_str(),file_name.size()) != tinyxml2::XML_NO_ERROR)
            {
              throw_pretty("Can't load file!\nFile: '"+file_name+"'");
            }
        }
        else
        {
            if (xml_file.LoadFile(file_name.c_str()) != tinyxml2::XML_NO_ERROR)
            {
              throw_pretty("Can't load file!\nFile: '"+file_name+"'");
            }
        }

        Initializer ret("TopLevel");
        tinyxml2::XMLHandle root_tag(xml_file.RootElement()->FirstChildElement());
        if(!parseXML(root_tag,ret, ""))
        {
            throw_pretty("Can't parse XML!\nFile: '"+file_name+"'");
        }
        return ret;
    }

    void XMLLoader::loadXML(std::string file_name, Initializer& solver, Initializer& problem, const std::string& solver_name, const std::string& problem_name, bool parsePathAsXML)
    {
        tinyxml2::XMLDocument xml_file;
        if(parsePathAsXML)
        {
            if (xml_file.Parse(file_name.c_str(),file_name.size()) != tinyxml2::XML_NO_ERROR)
            {
              throw_pretty("Can't load file!\nFile: '"+file_name+"'");
            }
        }
        else
        {
            if (xml_file.LoadFile(file_name.c_str()) != tinyxml2::XML_NO_ERROR)
            {
              throw_pretty("Can't load file!\nFile: '"+file_name+"'");
            }
        }

        std::vector<Initializer> initializers;
        tinyxml2::XMLHandle root_tag(xml_file.RootElement()->FirstChildElement());
        while (root_tag.ToElement())
        {
            initializers.push_back(Initializer("TopLevel"));
            if(!parseXML(root_tag,initializers[initializers.size()-1], ""))
            {
                initializers.pop_back();
            }
            root_tag = root_tag.NextSiblingElement();
        }
        bool foundSolver = false;
        bool foundProblem = false;
        if (solver_name=="" || solver_name=="")
        {
            for(Initializer& i : initializers)
            {
                std::string initializer_type = i.getName();
                if(!foundSolver)
                {
                    for(std::string known_type : Setup::getSolvers())
                    {
                        if(known_type==initializer_type) {solver = i; foundSolver=true; break;}
                    }
                }
                if(!foundProblem)
                {
                    for(std::string known_type : Setup::getProblems())
                    {
                        if(known_type==initializer_type) {problem = i; foundProblem=true; break;}
                    }
                }
            }
        }
        else
        {
            for(Initializer& i : initializers)
            {
                std::string name = boost::any_cast<std::string>(i.getProperty("Name"));
                if(name==solver_name) {solver = i; foundSolver=true;}
                if(name==problem_name) {problem = i; foundProblem=true;}
            }
        }
        if(!foundSolver) throw_pretty("Can't find solver '"+solver_name+"' in '"+file_name+"'!");
        if(!foundProblem) throw_pretty("Can't find problem '"+problem_name+"' in '"+file_name+"'!");
    }

}
<|endoftext|>


<commit_before>/*  Namecoin RPC library.
 *  Copyright (C) 2013  Daniel Kraft <d@domob.eu>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  See the distributed file COPYING for additional permissions in addition
 *  to those of the GNU Affero General Public License.
 */

/* Utility program to update names.  */

#include "JsonRpc.hpp"
#include "NamecoinInterface.hpp"
#include "NameRegistration.hpp"

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <vector>

using namespace nmcrpc;

/**
 * Display the help message.
 */
static void
displayHelp ()
{
  std::cerr << "Usage: nmupdate COMMAND [OPTIONS]"
            << std::endl << std::endl;
  std::cerr << "Possible commands:" << std::endl;
  std::cerr << "  * help: Display this message." << std::endl;
  std::cerr << "  * list: List owned names and their expirey counter."
            << std::endl;
  std::cerr << "  * update NAME [VAL]: Update NAME to VAL (or existing value)."
            << std::endl;
  std::cerr << "  * send NAME ADDR [VAL]: Update NAME and send to ADDR."
            << std::endl;
  std::cerr << "  * update-multi FILE [VAL]: Update all names in FILE to VAL or"
            << " their current value." << std::endl;
  std::cerr << "  * send-multi FILE ADDR [VAL]: Send all names in FILE to ADDR."
            << std::endl;
}

/**
 * Perform a name update operation on an array of names.
 * @param rpc Json RPC connection.
 * @param nc Namecoin high-level interface.
 * @param names Array of names to update.
 * @param hasVal Whether to set the value or leave it.
 * @param val The value, is ignored if !hasVal.
 */
static void
performUpdate (JsonRpc& rpc, NamecoinInterface& nc,
               const std::vector<std::string>& names,
               bool hasVal, const std::string& val)
{
  for (const auto& nm : names)
    {
      std::cout << "Updating " << nm << ": ";
      NamecoinInterface::Name name = nc.queryName (nm);

      NameUpdate updater (rpc, nc, name);
      if (hasVal)
        updater.setValue (val);
      const std::string txid = updater.execute ();

      std::cout << txid << std::endl;
    }
}

/**
 * Main routine with the usual interface.
 */
int
main (int argc, char** argv)
{
  try
    {
      if (argc < 2)
        {
          displayHelp ();
          return EXIT_FAILURE;
        }
      const std::string command = argv[1];

      if (command == "help")
        {
          displayHelp ();
          return EXIT_SUCCESS;
        }

      RpcSettings settings;
      settings.readDefaultConfig ();
      JsonRpc rpc(settings);
      NamecoinInterface nc(rpc);

      if (command == "list")
        {
          std::vector<NamecoinInterface::Name> names;
          const auto addName = [&names] (const NamecoinInterface::Name& nm)
            {
              names.push_back (nm);
            };
          nc.forMyNames (addName);

          const auto comp = [] (const NamecoinInterface::Name& a,
                                const NamecoinInterface::Name& b) -> bool
            {
              int diff = a.getExpireCounter () - b.getExpireCounter ();
              if (diff != 0)
                return (diff > 0);
              return a.getName () < b.getName ();
            };
          std::sort (names.begin (), names.end (), comp);

          for (const auto& el : names)
            {
              std::cout.width (30);
              std::cout << el.getName () << ": "
                        << el.getExpireCounter () << std::endl;
            }
        }
      else
        {
          std::string passphrase;
          if (nc.needWalletPassphrase ())
            {
              std::cout << "Enter wallet passphrase: ";
              std::getline (std::cin, passphrase);
            }
          NamecoinInterface::WalletUnlocker unlock(nc, passphrase);

          std::vector<std::string> names;

          if (command == "update")
            {
              if (argc < 3 || argc > 4)
                throw std::runtime_error ("Expected: nmupdate update"
                                          " NAME [VAL]");

              const std::string name = argv[2];
              std::string val;
              if (argc == 4)
                val = argv[3];

              names.push_back (name);
              performUpdate (rpc, nc, names, argc == 4, val);
            }
          else
            throw std::runtime_error ("Unknown command '" + command + "'.");
        }
    }
  catch (const JsonRpc::RpcError& exc)
    {
      std::cerr << "JSON-RPC error:" << std::endl;
      std::cerr << exc.getErrorMessage () << std::endl;
      return EXIT_FAILURE;
    }
  catch (const std::exception& exc)
    {
      std::cerr << "Error: " << exc.what () << std::endl;
      return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}
<commit_msg>Wrap around help text.<commit_after>/*  Namecoin RPC library.
 *  Copyright (C) 2013  Daniel Kraft <d@domob.eu>
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  See the distributed file COPYING for additional permissions in addition
 *  to those of the GNU Affero General Public License.
 */

/* Utility program to update names.  */

#include "JsonRpc.hpp"
#include "NamecoinInterface.hpp"
#include "NameRegistration.hpp"

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <vector>

using namespace nmcrpc;

/**
 * Display the help message.
 */
static void
displayHelp ()
{
  std::cerr << "Usage: nmupdate COMMAND [OPTIONS]"
            << std::endl << std::endl;
  std::cerr << "Possible commands:" << std::endl;
  std::cerr << "  * help: Display this message." << std::endl;
  std::cerr << "  * list: List owned names and their expirey counter."
            << std::endl;
  std::cerr << "  * update NAME [VAL]: Update NAME to VAL (or existing value)."
            << std::endl;
  std::cerr << "  * send NAME ADDR [VAL]: Update NAME and send to ADDR."
            << std::endl;
  std::cerr << "  * update-multi FILE [VAL]: Update all names in FILE"
            << std::endl
            << "                             to VAL or"
            << " their current value." << std::endl;
  std::cerr << "  * send-multi FILE ADDR [VAL]: Send all names in FILE to ADDR."
            << std::endl;
}

/**
 * Perform a name update operation on an array of names.
 * @param rpc Json RPC connection.
 * @param nc Namecoin high-level interface.
 * @param names Array of names to update.
 * @param hasVal Whether to set the value or leave it.
 * @param val The value, is ignored if !hasVal.
 */
static void
performUpdate (JsonRpc& rpc, NamecoinInterface& nc,
               const std::vector<std::string>& names,
               bool hasVal, const std::string& val)
{
  for (const auto& nm : names)
    {
      std::cout << "Updating " << nm << ": ";
      NamecoinInterface::Name name = nc.queryName (nm);

      NameUpdate updater (rpc, nc, name);
      if (hasVal)
        updater.setValue (val);
      const std::string txid = updater.execute ();

      std::cout << txid << std::endl;
    }
}

/**
 * Main routine with the usual interface.
 */
int
main (int argc, char** argv)
{
  try
    {
      if (argc < 2)
        {
          displayHelp ();
          return EXIT_FAILURE;
        }
      const std::string command = argv[1];

      if (command == "help")
        {
          displayHelp ();
          return EXIT_SUCCESS;
        }

      RpcSettings settings;
      settings.readDefaultConfig ();
      JsonRpc rpc(settings);
      NamecoinInterface nc(rpc);

      if (command == "list")
        {
          std::vector<NamecoinInterface::Name> names;
          const auto addName = [&names] (const NamecoinInterface::Name& nm)
            {
              names.push_back (nm);
            };
          nc.forMyNames (addName);

          const auto comp = [] (const NamecoinInterface::Name& a,
                                const NamecoinInterface::Name& b) -> bool
            {
              int diff = a.getExpireCounter () - b.getExpireCounter ();
              if (diff != 0)
                return (diff > 0);
              return a.getName () < b.getName ();
            };
          std::sort (names.begin (), names.end (), comp);

          for (const auto& el : names)
            {
              std::cout.width (30);
              std::cout << el.getName () << ": "
                        << el.getExpireCounter () << std::endl;
            }
        }
      else
        {
          std::string passphrase;
          if (nc.needWalletPassphrase ())
            {
              std::cout << "Enter wallet passphrase: ";
              std::getline (std::cin, passphrase);
            }
          NamecoinInterface::WalletUnlocker unlock(nc, passphrase);

          std::vector<std::string> names;

          if (command == "update")
            {
              if (argc < 3 || argc > 4)
                throw std::runtime_error ("Expected: nmupdate update"
                                          " NAME [VAL]");

              const std::string name = argv[2];
              std::string val;
              if (argc == 4)
                val = argv[3];

              names.push_back (name);
              performUpdate (rpc, nc, names, argc == 4, val);
            }
          else
            throw std::runtime_error ("Unknown command '" + command + "'.");
        }
    }
  catch (const JsonRpc::RpcError& exc)
    {
      std::cerr << "JSON-RPC error:" << std::endl;
      std::cerr << exc.getErrorMessage () << std::endl;
      return EXIT_FAILURE;
    }
  catch (const std::exception& exc)
    {
      std::cerr << "Error: " << exc.what () << std::endl;
      return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/* Copyright (c) 2010 - 2021 Advanced Micro Devices, Inc.

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

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

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

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3528
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>SWDEV-2 - Change OpenCL version number from  3528 to 3529<commit_after>/* Copyright (c) 2010 - 2021 Advanced Micro Devices, Inc.

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

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

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

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3529
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>/* Copyright (c) 2010-present Advanced Micro Devices, Inc.

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

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

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

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3275
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>SWDEV-2 - Change OpenCL version number from  3275 to 3276<commit_after>/* Copyright (c) 2010-present Advanced Micro Devices, Inc.

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

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

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

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3276
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>/* Copyright (c) 2010-present Advanced Micro Devices, Inc.

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

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

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

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3270
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<commit_msg>SWDEV-2 - Change OpenCL version number from  3270 to 3271<commit_after>/* Copyright (c) 2010-present Advanced Micro Devices, Inc.

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

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

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

#ifndef VERSIONS_HPP_
#define VERSIONS_HPP_

#include "utils/macros.hpp"

#ifndef AMD_PLATFORM_NAME
#define AMD_PLATFORM_NAME "AMD Accelerated Parallel Processing"
#endif  // AMD_PLATFORM_NAME

#ifndef AMD_PLATFORM_BUILD_NUMBER
#define AMD_PLATFORM_BUILD_NUMBER 3271
#endif  // AMD_PLATFORM_BUILD_NUMBER

#ifndef AMD_PLATFORM_REVISION_NUMBER
#define AMD_PLATFORM_REVISION_NUMBER 0
#endif  // AMD_PLATFORM_REVISION_NUMBER

#ifndef AMD_PLATFORM_RELEASE_INFO
#define AMD_PLATFORM_RELEASE_INFO
#endif  // AMD_PLATFORM_RELEASE_INFO

#define AMD_BUILD_STRING                                                                           \
  XSTR(AMD_PLATFORM_BUILD_NUMBER)                                                                  \
  "." XSTR(AMD_PLATFORM_REVISION_NUMBER)

#ifndef AMD_PLATFORM_INFO
#define AMD_PLATFORM_INFO                                                                          \
  "AMD-APP" AMD_PLATFORM_RELEASE_INFO DEBUG_ONLY(                                                  \
      "." IF(IS_OPTIMIZED, "opt", "dbg")) " (" AMD_BUILD_STRING ")"
#endif  // ATI_PLATFORM_INFO

#endif  // VERSIONS_HPP_
<|endoftext|>


<commit_before>#include "pch.h"

#include <Kore/Log.h>
#include <Kore/System.h>
#include <Kore/Graphics/Graphics.h>
#include <Kore/Input/Keyboard.h>
#include <Kore/Network/Connection.h>

#include "Engine/Renderer.h"
#include "Gameplay/Rockets.h"
#include "Gameplay/Ship.h"

using namespace Kore;

namespace {
	const int width = 1024;
	const int height = 768;
	char* name = "MPShmup";

	bool isServer = true;
	int ownPort = 27403;
	const char* connectUrl = "localhost";
	int connectPort = 27734;

	int sendCounter = 0;
	double sendRate = 0.05;
	double sendRateCongested = 0.1;
	double sinceSend = 0;
	Connection *conn;
	unsigned char buff[256];

	double time;
	double serverOffsetAvg = 0;
	double serverOffsetNum = 0;
	int playerStates = 0;
	int localId = 0;
	Ship* ships[3];

	bool inputLeft = false;
	bool inputRight = false;
	bool inputFire = false;

	int packInput(bool left, bool right, bool fire) {
		return 4 * fire + 2 * left + right;
	}

	void keyDown(KeyCode code, wchar_t character) {
		if (code == Key_A) {
			inputLeft = true;
		}
		else if (code == Key_D) {
			inputRight = true;
		}
		else if (code == Key_Space) {
			inputFire = true;
		}
	}

	void keyUp(KeyCode code, wchar_t character) {
		if (code == Key_A) {
			inputLeft = false;
		}
		else if (code == Key_D) {
			inputRight = false;
		}
		else if (code == Key_Space) {
			inputFire = false;
		}
	}

	void update() {
		double now = System::time();
		double deltaT = now - time;
		time = now;

		// Receive data
		{
			int got;
			int id;
			while ((got = conn->receive(buff, id)) > 0) {
				// Information is assumed to be half the ping old
				double eventTime = System::time() - conn->pings[id] / 2;
				if (isServer) {
					ships[id + 1]->applyInput(eventTime, *(int*)buff);
				}
				else {
					if (*buff == '\0') {
						// Regular updates
						playerStates = *(int*)(buff + 1);
						localId = *((int*)(buff + 1)) >> 8;

						double serverTime = *((double*)(buff + 5)) + conn->pings[id] / 2;
						double serverOffset = time - serverTime;
						serverOffsetNum++;
						serverOffsetAvg = serverOffsetAvg + (serverOffset - serverOffsetAvg) / serverOffsetNum; // Incremental average
						log(LogLevel::Info, "Serveroffset: %f", serverOffsetAvg);

						ships[0]->applyPosition(eventTime, vec3(*((float*)(buff + 13)), *((float*)(buff + 17)), *((float*)(buff + 21))));
						ships[1]->applyPosition(eventTime, vec3(*((float*)(buff + 25)), *((float*)(buff + 29)), *((float*)(buff + 33))));
						ships[2]->applyPosition(eventTime, vec3(*((float*)(buff + 37)), *((float*)(buff + 41)), *((float*)(buff + 45))));

						// For opponent prediction
						ships[0]->applyInput(eventTime, *((int*)(buff + 49)));
						if (localId != 1) ships[1]->applyInput(eventTime, *((int*)(buff + 53)));
						if (localId != 2) ships[2]->applyInput(eventTime, *((int*)(buff + 57)));
					}
					else if (*buff == '\1') {
						// Rockets fired
						double serverTime = *((double*)(buff + 10));

						if (updateRocket(serverTime + serverOffsetAvg - time,
							*(buff + 1),
							*(buff + 5),
							vec3(*((float*)(buff + 18)),
								*((float*)(buff + 22)),
								*((float*)(buff + 26))))) {
							ships[*(buff + 1)]->resetFire(serverTime + serverOffsetAvg - time);
						}
					}
				}
			}
		}

		// Updated player states
		if (isServer) {
			playerStates = 4 * (conn->states[1] == Connection::Connected) + 2 * (conn->states[0] == Connection::Connected) + 1;
		}
		else if (conn->states[0] != Connection::Connected) {
			playerStates = 0;
		}

		// Update state
		{
			// Client prediction
			if (localId >= 0) {
				ships[localId]->applyInput(System::time(), packInput(inputLeft, inputRight, inputFire));
			}

			vec3 firePos;
			for (int i = 0; i < 3; i++) {
				if (ships[i]->update(deltaT, playerStates & (1 << i), firePos)) {

					int rId = fireRocket(i, firePos);
					if (isServer) {
						unsigned char data[30];
						*           data        = '\1';
						*          (data +  1)  = i;
						*          (data +  5)  = rId;
						*((double*)(data + 10)) = time;
						*((float*) (data + 18)) = firePos.x();
						*((float*) (data + 22)) = firePos.y();
						*((float*) (data + 26)) = firePos.z();
						conn->send(data, 30, -1, true);
					}
				}
			}

			updateRockets(deltaT);
		}

		// Send data
		{
			sinceSend -= deltaT;
			if (sinceSend < 0) {
				if (isServer) {
					unsigned char data[61];
					*           data        = '\0';
					*((double*)(data +  5)) = time;
					*((float*) (data + 13)) = ships[0]->position.x();
					*((float*) (data + 17)) = ships[0]->position.y();
					*((float*) (data + 21)) = ships[0]->position.z();
					*((float*) (data + 25)) = ships[1]->position.x();
					*((float*) (data + 29)) = ships[1]->position.y();
					*((float*) (data + 33)) = ships[1]->position.z();
					*((float*) (data + 37)) = ships[2]->position.x();
					*((float*) (data + 41)) = ships[2]->position.y();
					*((float*) (data + 45)) = ships[2]->position.z();
					*((int*)   (data + 49)) = ships[0]->getCurrentInput();
					*((int*)   (data + 53)) = ships[1]->getCurrentInput();
					*((int*)   (data + 57)) = ships[2]->getCurrentInput();
					for (int id = 0; id < conn->maxConns; ++id) {
						if (conn->states[id] != Connection::Connected)
							continue;

						// Halve send rate for congested clients
						if (conn->congests[id] && sendCounter != 0)
							continue;

						*((int*)(data + 1)) = playerStates + ((id + 1) << 8);
						conn->send(data, 61, id, false);
					}
				}
				else {
					// Halve send rate if congested
					if (conn->states[0] == Connection::Connected && (!conn->congests[0] || sendCounter == 0)) {
						unsigned char data[4];
						*((int*)data) = packInput(inputLeft, inputRight, inputFire);
						conn->send(data, 4, 0, false);
					}
				}

				sendCounter = (++sendCounter) % (int)(sendRateCongested / sendRate);
				sinceSend = sendRate;
			}
		}

		// TODO: Implement rocket fire and impact (reliable)

		Graphics::begin();
		Graphics::clear(Graphics::ClearColorFlag | Graphics::ClearDepthFlag, 0xFF696969, 1.0f);

		renderObjects();

		Graphics::end();
		Graphics::swapBuffers();
	}
}

int kore(int argc, char** argv) {
	if (argc > 1) {
		ownPort = strtol(argv[1], NULL, 10);
	}
	if (argc > 3) {
		isServer = false;
		localId = -1;
		connectUrl = argv[2];
		connectPort = strtol(argv[3], NULL, 10);
	}

	Kore::System::setName(name);
	Kore::System::setup();
	Kore::WindowOptions options;
	options.title = name;
	options.width = width;
	options.height = height;
	options.x = 100;
	options.y = 100;
	options.targetDisplay = -1;
	options.mode = WindowModeWindow;
	options.rendererOptions.depthBufferBits = 16;
	options.rendererOptions.stencilBufferBits = 8;
	options.rendererOptions.textureFormat = 0;
	options.rendererOptions.antialiasing = 0;
	Kore::System::initWindow(options);

	vec3 cameraPos = vec3(0, 0, -10);
	vec3 cameraDir = vec3(0, 0, 1);
	vec3 cameraUp = vec3(0, 1, 0);
	initRenderer(mat4::lookAlong(cameraDir, cameraPos, cameraUp),
		mat4::orthogonalProjection(-width / 2.f, width / 2.f, -height / 2.f, height / 2.f, -10.f, 10.f));

	time = System::time();
	ships[0] = new Ship(vec3(-width / 3.f, -height / 2.f + 50.f, 0.f), "player_0.png");
	ships[1] = new Ship(vec3(         0.f, -height / 2.f + 50.f, 0.f), "player_1.png");
	ships[2] = new Ship(vec3( width / 3.f, -height / 2.f + 50.f, 0.f), "player_2.png");

	initRockets();

	if (isServer) {
		conn = new Connection(ownPort, 2);
		conn->listen();
		log(LogLevel::Info, "Starting server on port %i", ownPort);
	}
	else {
		conn = new Connection(ownPort, 1);
		conn->connect(connectUrl, connectPort);
		log(LogLevel::Info, "Starting client on port %i, connecting to %s:%i", ownPort, connectUrl, connectPort);
	}

	Keyboard::the()->KeyDown = keyDown;
	Keyboard::the()->KeyUp = keyUp;

	Kore::System::setCallback(update);
	Kore::System::start();

	return 0;
}
<commit_msg>Updated todo comment.<commit_after>#include "pch.h"

#include <Kore/Log.h>
#include <Kore/System.h>
#include <Kore/Graphics/Graphics.h>
#include <Kore/Input/Keyboard.h>
#include <Kore/Network/Connection.h>

#include "Engine/Renderer.h"
#include "Gameplay/Rockets.h"
#include "Gameplay/Ship.h"

using namespace Kore;

namespace {
	const int width = 1024;
	const int height = 768;
	char* name = "MPShmup";

	bool isServer = true;
	int ownPort = 27403;
	const char* connectUrl = "localhost";
	int connectPort = 27734;

	int sendCounter = 0;
	double sendRate = 0.05;
	double sendRateCongested = 0.1;
	double sinceSend = 0;
	Connection *conn;
	unsigned char buff[256];

	double time;
	double serverOffsetAvg = 0;
	double serverOffsetNum = 0;
	int playerStates = 0;
	int localId = 0;
	Ship* ships[3];

	bool inputLeft = false;
	bool inputRight = false;
	bool inputFire = false;

	int packInput(bool left, bool right, bool fire) {
		return 4 * fire + 2 * left + right;
	}

	void keyDown(KeyCode code, wchar_t character) {
		if (code == Key_A) {
			inputLeft = true;
		}
		else if (code == Key_D) {
			inputRight = true;
		}
		else if (code == Key_Space) {
			inputFire = true;
		}
	}

	void keyUp(KeyCode code, wchar_t character) {
		if (code == Key_A) {
			inputLeft = false;
		}
		else if (code == Key_D) {
			inputRight = false;
		}
		else if (code == Key_Space) {
			inputFire = false;
		}
	}

	void update() {
		double now = System::time();
		double deltaT = now - time;
		time = now;

		// Receive data
		{
			int got;
			int id;
			while ((got = conn->receive(buff, id)) > 0) {
				// Information is assumed to be half the ping old
				double eventTime = System::time() - conn->pings[id] / 2;
				if (isServer) {
					ships[id + 1]->applyInput(eventTime, *(int*)buff);
				}
				else {
					if (*buff == '\0') {
						// Regular updates
						playerStates = *(int*)(buff + 1);
						localId = *((int*)(buff + 1)) >> 8;

						double serverTime = *((double*)(buff + 5)) + conn->pings[id] / 2;
						double serverOffset = time - serverTime;
						serverOffsetNum++;
						serverOffsetAvg = serverOffsetAvg + (serverOffset - serverOffsetAvg) / serverOffsetNum; // Incremental average
						log(LogLevel::Info, "Serveroffset: %f", serverOffsetAvg);

						ships[0]->applyPosition(eventTime, vec3(*((float*)(buff + 13)), *((float*)(buff + 17)), *((float*)(buff + 21))));
						ships[1]->applyPosition(eventTime, vec3(*((float*)(buff + 25)), *((float*)(buff + 29)), *((float*)(buff + 33))));
						ships[2]->applyPosition(eventTime, vec3(*((float*)(buff + 37)), *((float*)(buff + 41)), *((float*)(buff + 45))));

						// For opponent prediction
						ships[0]->applyInput(eventTime, *((int*)(buff + 49)));
						if (localId != 1) ships[1]->applyInput(eventTime, *((int*)(buff + 53)));
						if (localId != 2) ships[2]->applyInput(eventTime, *((int*)(buff + 57)));
					}
					else if (*buff == '\1') {
						// Rockets fired
						double serverTime = *((double*)(buff + 10));

						if (updateRocket(serverTime + serverOffsetAvg - time,
							*(buff + 1),
							*(buff + 5),
							vec3(*((float*)(buff + 18)),
								*((float*)(buff + 22)),
								*((float*)(buff + 26))))) {
							ships[*(buff + 1)]->resetFire(serverTime + serverOffsetAvg - time);
						}
					}
				}
			}
		}

		// Updated player states
		if (isServer) {
			playerStates = 4 * (conn->states[1] == Connection::Connected) + 2 * (conn->states[0] == Connection::Connected) + 1;
		}
		else if (conn->states[0] != Connection::Connected) {
			playerStates = 0;
		}

		// Update state
		{
			// Client prediction
			if (localId >= 0) {
				ships[localId]->applyInput(System::time(), packInput(inputLeft, inputRight, inputFire));
			}

			vec3 firePos;
			for (int i = 0; i < 3; i++) {
				if (ships[i]->update(deltaT, playerStates & (1 << i), firePos)) {

					int rId = fireRocket(i, firePos);
					if (isServer) {
						unsigned char data[30];
						*           data        = '\1';
						*          (data +  1)  = i;
						*          (data +  5)  = rId;
						*((double*)(data + 10)) = time;
						*((float*) (data + 18)) = firePos.x();
						*((float*) (data + 22)) = firePos.y();
						*((float*) (data + 26)) = firePos.z();
						conn->send(data, 30, -1, true);
					}
				}
			}

			updateRockets(deltaT);
		}

		// Send data
		{
			sinceSend -= deltaT;
			if (sinceSend < 0) {
				if (isServer) {
					unsigned char data[61];
					*           data        = '\0';
					*((double*)(data +  5)) = time;
					*((float*) (data + 13)) = ships[0]->position.x();
					*((float*) (data + 17)) = ships[0]->position.y();
					*((float*) (data + 21)) = ships[0]->position.z();
					*((float*) (data + 25)) = ships[1]->position.x();
					*((float*) (data + 29)) = ships[1]->position.y();
					*((float*) (data + 33)) = ships[1]->position.z();
					*((float*) (data + 37)) = ships[2]->position.x();
					*((float*) (data + 41)) = ships[2]->position.y();
					*((float*) (data + 45)) = ships[2]->position.z();
					*((int*)   (data + 49)) = ships[0]->getCurrentInput();
					*((int*)   (data + 53)) = ships[1]->getCurrentInput();
					*((int*)   (data + 57)) = ships[2]->getCurrentInput();
					for (int id = 0; id < conn->maxConns; ++id) {
						if (conn->states[id] != Connection::Connected)
							continue;

						// Halve send rate for congested clients
						if (conn->congests[id] && sendCounter != 0)
							continue;

						*((int*)(data + 1)) = playerStates + ((id + 1) << 8);
						conn->send(data, 61, id, false);
					}
				}
				else {
					// Halve send rate if congested
					if (conn->states[0] == Connection::Connected && (!conn->congests[0] || sendCounter == 0)) {
						unsigned char data[4];
						*((int*)data) = packInput(inputLeft, inputRight, inputFire);
						conn->send(data, 4, 0, false);
					}
				}

				sendCounter = (++sendCounter) % (int)(sendRateCongested / sendRate);
				sinceSend = sendRate;
			}
		}

		// TODO: Implement rocket impact (reliable)

		Graphics::begin();
		Graphics::clear(Graphics::ClearColorFlag | Graphics::ClearDepthFlag, 0xFF696969, 1.0f);

		renderObjects();

		Graphics::end();
		Graphics::swapBuffers();
	}
}

int kore(int argc, char** argv) {
	if (argc > 1) {
		ownPort = strtol(argv[1], NULL, 10);
	}
	if (argc > 3) {
		isServer = false;
		localId = -1;
		connectUrl = argv[2];
		connectPort = strtol(argv[3], NULL, 10);
	}

	Kore::System::setName(name);
	Kore::System::setup();
	Kore::WindowOptions options;
	options.title = name;
	options.width = width;
	options.height = height;
	options.x = 100;
	options.y = 100;
	options.targetDisplay = -1;
	options.mode = WindowModeWindow;
	options.rendererOptions.depthBufferBits = 16;
	options.rendererOptions.stencilBufferBits = 8;
	options.rendererOptions.textureFormat = 0;
	options.rendererOptions.antialiasing = 0;
	Kore::System::initWindow(options);

	vec3 cameraPos = vec3(0, 0, -10);
	vec3 cameraDir = vec3(0, 0, 1);
	vec3 cameraUp = vec3(0, 1, 0);
	initRenderer(mat4::lookAlong(cameraDir, cameraPos, cameraUp),
		mat4::orthogonalProjection(-width / 2.f, width / 2.f, -height / 2.f, height / 2.f, -10.f, 10.f));

	time = System::time();
	ships[0] = new Ship(vec3(-width / 3.f, -height / 2.f + 50.f, 0.f), "player_0.png");
	ships[1] = new Ship(vec3(         0.f, -height / 2.f + 50.f, 0.f), "player_1.png");
	ships[2] = new Ship(vec3( width / 3.f, -height / 2.f + 50.f, 0.f), "player_2.png");

	initRockets();

	if (isServer) {
		conn = new Connection(ownPort, 2);
		conn->listen();
		log(LogLevel::Info, "Starting server on port %i", ownPort);
	}
	else {
		conn = new Connection(ownPort, 1);
		conn->connect(connectUrl, connectPort);
		log(LogLevel::Info, "Starting client on port %i, connecting to %s:%i", ownPort, connectUrl, connectPort);
	}

	Keyboard::the()->KeyDown = keyDown;
	Keyboard::the()->KeyUp = keyUp;

	Kore::System::setCallback(update);
	Kore::System::start();

	return 0;
}
<|endoftext|>