Copie figée du cœur de GNU Backgammon (commit 14b6798)
Fichiers repris sans modification dans engine/gnubg/, poids et table d'équité Kazaross-XG2 dans engine/data/, provenance détaillée dans engine/gnubg/PROVENANCE.md. Le lint ne vérifie plus les espaces de ces copies, pour qu'elles restent identiques à l'original. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
d0f44b689b
commit
792f6395bf
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright (C) 1997-2000 Gary Wong <gtw@gnu.org>
|
||||
* Copyright (C) 2002-2022 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cache.h"
|
||||
#include "positionid.h"
|
||||
|
||||
#if defined(USE_MULTITHREAD)
|
||||
#include "multithread.h"
|
||||
|
||||
#if defined(__GNUC__) && ( \
|
||||
(( __GNUC__ * 100 + __GNUC_MINOR__ >= 401 ) \
|
||||
&& (defined (__i486) || defined (__x86_64))) \
|
||||
|| (( __GNUC__ * 100 + __GNUC_MINOR__ >= 404 ) \
|
||||
&& defined (__i386)) \
|
||||
)
|
||||
|
||||
static inline void
|
||||
cache_lock(evalCache * pc, uint32_t k)
|
||||
{
|
||||
while (__sync_lock_test_and_set(&(pc->entries[k].lock), 1))
|
||||
while (__sync_fetch_and_add(&pc->entries[k].lock, 0))
|
||||
__asm__ __volatile__ ("pause":::"memory");
|
||||
}
|
||||
|
||||
static inline void
|
||||
cache_unlock(evalCache * pc, uint32_t k)
|
||||
{
|
||||
__sync_lock_release(&(pc->entries[k].lock));
|
||||
}
|
||||
|
||||
#else /* not x86 or older gcc: no suitable intrinsics */
|
||||
|
||||
static inline void
|
||||
WaitForLock(volatile int *lock)
|
||||
{
|
||||
do {
|
||||
MT_SafeDec(lock);
|
||||
} while (MT_SafeIncCheck(lock));
|
||||
}
|
||||
|
||||
static inline void
|
||||
cache_lock(evalCache * pc, uint32_t k)
|
||||
{
|
||||
if (MT_SafeIncCheck(&(pc->entries[k].lock)))
|
||||
WaitForLock(&(pc->entries[k].lock));
|
||||
}
|
||||
|
||||
static inline void
|
||||
cache_unlock(evalCache * pc, uint32_t k)
|
||||
{
|
||||
MT_SafeDec(&(pc->entries[k].lock));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* USE_MULTITHREAD */
|
||||
|
||||
|
||||
static int
|
||||
CacheGetSize(unsigned int s)
|
||||
{
|
||||
unsigned int size;
|
||||
|
||||
if (s == 0)
|
||||
return 0;
|
||||
|
||||
if (s > 1u << 31)
|
||||
return -1;
|
||||
|
||||
/* A cache size of 1 would allocate zero cache nodes */
|
||||
if (s == 1)
|
||||
s = 2;
|
||||
|
||||
size = s;
|
||||
/* adjust size to smallest power of 2 GE to s */
|
||||
while ((s & (s - 1)) != 0)
|
||||
s &= (s - 1);
|
||||
|
||||
size = (s < size) ? 2 * s : s;
|
||||
|
||||
/* CacheResize() returns the cache size as an int. */
|
||||
if (size > (unsigned int) INT_MAX)
|
||||
return -1;
|
||||
|
||||
return (int) size;
|
||||
}
|
||||
|
||||
int
|
||||
CacheCreate(evalCache * pc, unsigned int s)
|
||||
{
|
||||
int size = CacheGetSize(s);
|
||||
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
#if CACHE_STATS
|
||||
pc->cLookup = 0;
|
||||
pc->cHit = 0;
|
||||
pc->nAdds = 0;
|
||||
#endif
|
||||
|
||||
pc->size = (unsigned int) size;
|
||||
if (pc->size == 0) {
|
||||
pc->hashMask = 0;
|
||||
pc->entries = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pc->hashMask = (pc->size >> 1) - 1;
|
||||
|
||||
/* Avoid "comparison is always false due to limited range of data type"
|
||||
* warning on 64bits architectures.
|
||||
* Adapt (or remove the condition) if the involved types are changed.
|
||||
*/
|
||||
#if (SIZE_MAX / 64 < UINT_MAX)
|
||||
/* Ensure the allocation size fits in size_t */
|
||||
if ((size_t) (pc->size / 2) > SIZE_MAX / sizeof(*pc->entries))
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
pc->entries = (cacheNode *) malloc((pc->size / 2) * sizeof(*pc->entries));
|
||||
if (pc->entries == NULL)
|
||||
return -1;
|
||||
|
||||
CacheFlush(pc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* MurmurHash3 https://code.google.com/p/smhasher/wiki/MurmurHash */
|
||||
|
||||
extern uint32_t
|
||||
GetHashKey(uint32_t hashMask, const cacheNodeDetail * restrict e)
|
||||
{
|
||||
uint32_t hash = (uint32_t) e->nEvalContext;
|
||||
int i;
|
||||
|
||||
hash *= 0xcc9e2d51;
|
||||
hash = (hash << 15) | (hash >> (32 - 15));
|
||||
hash *= 0x1b873593;
|
||||
|
||||
hash = (hash << 13) | (hash >> (32 - 13));
|
||||
hash = hash * 5 + 0xe6546b64;
|
||||
|
||||
for (i = 0; i < 7; i++) {
|
||||
uint32_t k = e->key.data[i];
|
||||
|
||||
k *= 0xcc9e2d51;
|
||||
k = (k << 15) | (k >> (32 - 15));
|
||||
k *= 0x1b873593;
|
||||
|
||||
hash ^= k;
|
||||
hash = (hash << 13) | (hash >> (32 - 13));
|
||||
hash = hash * 5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
/* Real MurmurHash3 has a "hash ^= len" here,
|
||||
* but for us len is constant. Skip it */
|
||||
|
||||
hash ^= hash >> 16;
|
||||
hash *= 0x85ebca6b;
|
||||
hash ^= hash >> 13;
|
||||
hash *= 0xc2b2ae35;
|
||||
hash ^= hash >> 16;
|
||||
|
||||
return (hash & hashMask);
|
||||
}
|
||||
|
||||
|
||||
uint32_t
|
||||
CacheLookupWithLocking(evalCache * restrict pc, const cacheNodeDetail * restrict e, float * restrict arOut, float * restrict arCubeful)
|
||||
{
|
||||
uint32_t const l = GetHashKey(pc->hashMask, e);
|
||||
|
||||
#if CACHE_STATS
|
||||
#if defined(USE_MULTITHREAD)
|
||||
MT_SafeInc(&pc->cLookup);
|
||||
#else
|
||||
++pc->cLookup;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(USE_MULTITHREAD)
|
||||
cache_lock(pc, l);
|
||||
#endif
|
||||
|
||||
if (!EqualKeys(pc->entries[l].nd_primary.key, e->key) || pc->entries[l].nd_primary.nEvalContext != e->nEvalContext) { /* Not in primary slot */
|
||||
if (!EqualKeys(pc->entries[l].nd_secondary.key, e->key) || pc->entries[l].nd_secondary.nEvalContext != e->nEvalContext) { /* Cache miss */
|
||||
#if defined(USE_MULTITHREAD)
|
||||
cache_unlock(pc, l);
|
||||
#endif
|
||||
return l;
|
||||
} else { /* Found in second slot, promote "hot" entry */
|
||||
cacheNodeDetail tmp = pc->entries[l].nd_primary;
|
||||
|
||||
pc->entries[l].nd_primary = pc->entries[l].nd_secondary;
|
||||
pc->entries[l].nd_secondary = tmp;
|
||||
}
|
||||
}
|
||||
/* Cache hit */
|
||||
memcpy(arOut, pc->entries[l].nd_primary.ar, sizeof(float) * 5 /*NUM_OUTPUTS */ );
|
||||
if (arCubeful)
|
||||
*arCubeful = pc->entries[l].nd_primary.ar[5]; /* Cubeful equity stored in slot 5 */
|
||||
|
||||
#if defined(USE_MULTITHREAD)
|
||||
cache_unlock(pc, l);
|
||||
#endif
|
||||
|
||||
#if CACHE_STATS
|
||||
#if defined(USE_MULTITHREAD)
|
||||
MT_SafeInc(&pc->cHit);
|
||||
#else
|
||||
++pc->cHit;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return CACHEHIT;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
CacheLookupNoLocking(evalCache * restrict pc, const cacheNodeDetail * restrict e, float *restrict arOut, float * restrict arCubeful)
|
||||
{
|
||||
uint32_t const l = GetHashKey(pc->hashMask, e);
|
||||
|
||||
#if CACHE_STATS
|
||||
++pc->cLookup;
|
||||
#endif
|
||||
if (!EqualKeys(pc->entries[l].nd_primary.key, e->key) || pc->entries[l].nd_primary.nEvalContext != e->nEvalContext) { /* Not in primary slot */
|
||||
if (!EqualKeys(pc->entries[l].nd_secondary.key, e->key) || pc->entries[l].nd_secondary.nEvalContext != e->nEvalContext) { /* Cache miss */
|
||||
return l;
|
||||
} else { /* Found in second slot, promote "hot" entry */
|
||||
cacheNodeDetail tmp = pc->entries[l].nd_primary;
|
||||
|
||||
pc->entries[l].nd_primary = pc->entries[l].nd_secondary;
|
||||
pc->entries[l].nd_secondary = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cache hit */
|
||||
memcpy(arOut, pc->entries[l].nd_primary.ar, sizeof(float) * 5 /*NUM_OUTPUTS */ );
|
||||
if (arCubeful)
|
||||
*arCubeful = pc->entries[l].nd_primary.ar[5]; /* Cubeful equity stored in slot 5 */
|
||||
|
||||
#if CACHE_STATS
|
||||
++pc->cHit;
|
||||
#endif
|
||||
|
||||
return CACHEHIT;
|
||||
}
|
||||
|
||||
void
|
||||
CacheAddWithLocking(evalCache * restrict pc, const cacheNodeDetail * restrict e, uint32_t l)
|
||||
{
|
||||
#if defined(USE_MULTITHREAD)
|
||||
cache_lock(pc, l);
|
||||
#endif
|
||||
|
||||
pc->entries[l].nd_secondary = pc->entries[l].nd_primary;
|
||||
pc->entries[l].nd_primary = *e;
|
||||
|
||||
#if defined(USE_MULTITHREAD)
|
||||
cache_unlock(pc, l);
|
||||
#endif
|
||||
|
||||
#if CACHE_STATS
|
||||
#if defined(USE_MULTITHREAD)
|
||||
MT_SafeInc(&pc->nAdds);
|
||||
#else
|
||||
++pc->nAdds;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/* CacheAddNoLocking() is inlined and in cache.h */
|
||||
|
||||
void
|
||||
CacheDestroy(const evalCache * pc)
|
||||
{
|
||||
free(pc->entries);
|
||||
}
|
||||
|
||||
void
|
||||
CacheFlush(const evalCache * pc)
|
||||
{
|
||||
unsigned int k;
|
||||
for (k = 0; k < pc->size / 2; ++k) {
|
||||
pc->entries[k].nd_primary.key.data[0] = (unsigned int) -1;
|
||||
pc->entries[k].nd_secondary.key.data[0] = (unsigned int) -1;
|
||||
#if defined(USE_MULTITHREAD)
|
||||
pc->entries[k].lock = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
CacheResize(evalCache * pc, unsigned int cNew)
|
||||
{
|
||||
evalCache newCache = { 0 };
|
||||
int size = CacheGetSize(cNew);
|
||||
|
||||
if (size < 0)
|
||||
return -1;
|
||||
|
||||
if ((unsigned int) size == pc->size)
|
||||
return size;
|
||||
|
||||
if (CacheCreate(&newCache, cNew) != 0)
|
||||
return -1;
|
||||
|
||||
CacheDestroy(pc);
|
||||
*pc = newCache;
|
||||
|
||||
return (int) pc->size;
|
||||
}
|
||||
|
||||
#if CACHE_STATS
|
||||
void
|
||||
CacheStats(const evalCache * pc, unsigned int *pcLookup, unsigned int *pcHit, unsigned int *pcUsed)
|
||||
{
|
||||
if (pcLookup)
|
||||
*pcLookup = pc->cLookup;
|
||||
|
||||
if (pcHit)
|
||||
*pcHit = pc->cHit;
|
||||
|
||||
if (pcUsed)
|
||||
*pcUsed = pc->nAdds;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 1997-2000 Gary Wong <gtw@gnu.org>
|
||||
* Copyright (C) 2002-2018 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CACHE_H
|
||||
#define CACHE_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#else
|
||||
typedef unsigned int uint32_t;
|
||||
#endif
|
||||
|
||||
#include "gnubg-types.h"
|
||||
|
||||
/* Set to calculate simple cache stats */
|
||||
#define CACHE_STATS 0
|
||||
|
||||
typedef struct {
|
||||
positionkey key; /* size = 28 */
|
||||
int nEvalContext;
|
||||
float ar[6];
|
||||
} cacheNodeDetail;
|
||||
|
||||
typedef struct {
|
||||
cacheNodeDetail nd_primary;
|
||||
cacheNodeDetail nd_secondary;
|
||||
#if defined(USE_MULTITHREAD)
|
||||
int lock;
|
||||
#endif
|
||||
} cacheNode;
|
||||
|
||||
/* name used in eval.c */
|
||||
typedef cacheNodeDetail evalcache;
|
||||
|
||||
typedef struct {
|
||||
cacheNode *entries;
|
||||
|
||||
unsigned int size;
|
||||
uint32_t hashMask;
|
||||
|
||||
#if CACHE_STATS
|
||||
unsigned int nAdds;
|
||||
unsigned int cLookup;
|
||||
unsigned int cHit;
|
||||
#endif
|
||||
} evalCache;
|
||||
|
||||
/* Cache size will be adjusted to a power of 2 */
|
||||
int CacheCreate(evalCache * pc, unsigned int size);
|
||||
int CacheResize(evalCache * pc, unsigned int cNew);
|
||||
|
||||
#define CACHEHIT ((uint32_t)-1)
|
||||
|
||||
/* returns a value which is passed to CacheAdd (if a miss) */
|
||||
uint32_t CacheLookupWithLocking(evalCache * pc, const cacheNodeDetail * e, float *arOut, float *arCubeful);
|
||||
uint32_t CacheLookupNoLocking(evalCache * pc, const cacheNodeDetail * e, float *arOut, float *arCubeful);
|
||||
|
||||
void CacheAddWithLocking(evalCache * pc, const cacheNodeDetail * e, uint32_t l);
|
||||
|
||||
static inline void
|
||||
CacheAddNoLocking(evalCache * pc, const cacheNodeDetail * e, const uint32_t l)
|
||||
{
|
||||
pc->entries[l].nd_secondary = pc->entries[l].nd_primary;
|
||||
pc->entries[l].nd_primary = *e;
|
||||
#if CACHE_STATS
|
||||
++pc->nAdds;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CacheFlush(const evalCache * pc);
|
||||
void CacheDestroy(const evalCache * pc);
|
||||
|
||||
#if CACHE_STATS
|
||||
void CacheStats(const evalCache * pc, unsigned int *pcLookup, unsigned int *pcHit, unsigned int *pcUsed);
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_FUNC_ATTRIBUTE_PURE)
|
||||
uint32_t GetHashKey(uint32_t hashMask, const cacheNodeDetail * e) __attribute((pure));
|
||||
#else
|
||||
uint32_t GetHashKey(uint32_t hashMask, const cacheNodeDetail * e);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2009 Christian Anthon <anthon@kiku.dk>
|
||||
* Copyright (C) 2007-2011 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* $Id: gnubg-types.h,v 1.9 2013/06/16 02:16:23 mdpetch Exp $
|
||||
*/
|
||||
|
||||
#ifndef GNUBG_TYPES_H
|
||||
#define GNUBG_TYPES_H
|
||||
|
||||
typedef unsigned int TanBoard[2][25];
|
||||
typedef const unsigned int (*ConstTanBoard)[25];
|
||||
|
||||
typedef enum {
|
||||
VARIATION_STANDARD, /* standard backgammon */
|
||||
VARIATION_NACKGAMMON, /* standard backgammon with nackgammon starting
|
||||
* position */
|
||||
VARIATION_HYPERGAMMON_1, /* 1-chequer hypergammon */
|
||||
VARIATION_HYPERGAMMON_2, /* 2-chequer hypergammon */
|
||||
VARIATION_HYPERGAMMON_3, /* 3-chequer hypergammon */
|
||||
NUM_VARIATIONS
|
||||
} bgvariation;
|
||||
|
||||
typedef enum {
|
||||
GAME_NONE, GAME_PLAYING, GAME_OVER, GAME_RESIGNED, GAME_DROP
|
||||
} gamestate;
|
||||
|
||||
typedef struct {
|
||||
TanBoard anBoard;
|
||||
unsigned int anDice[2]; /* (0,0) for unrolled dice */
|
||||
int fTurn; /* who makes the next decision */
|
||||
int fResigned;
|
||||
int fResignationDeclined;
|
||||
int fDoubled;
|
||||
int cGames;
|
||||
int fMove; /* player on roll */
|
||||
int fCubeOwner;
|
||||
int fCrawford;
|
||||
int fPostCrawford;
|
||||
int nMatchTo;
|
||||
int anScore[2];
|
||||
int nCube;
|
||||
unsigned int cBeavers;
|
||||
bgvariation bgv;
|
||||
int fCubeUse;
|
||||
int fJacoby;
|
||||
gamestate gs;
|
||||
} matchstate;
|
||||
|
||||
typedef union {
|
||||
unsigned int data[7];
|
||||
} positionkey;
|
||||
|
||||
typedef union {
|
||||
unsigned char auch[10];
|
||||
} oldpositionkey;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (C) 2006 Oystein Johansen <oystein@gnubg.org>
|
||||
* Copyright (C) 2011-2018 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* $Id: inputs.c,v 1.12 2018/05/12 20:59:35 plm Exp $
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "gnubg-types.h"
|
||||
#include "simd.h"
|
||||
#include "eval.h"
|
||||
|
||||
#if defined(USE_SIMD_INSTRUCTIONS)
|
||||
#if defined(USE_NEON)
|
||||
#include <arm_neon.h>
|
||||
#elif defined(USE_AVX)
|
||||
#include <immintrin.h>
|
||||
#elif defined(USE_SSE2)
|
||||
#include <emmintrin.h>
|
||||
#else
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#else
|
||||
typedef float float_vector[4];
|
||||
#endif /* USE_SIMD_INSTRUCTIONS */
|
||||
|
||||
typedef SSE_ALIGN(float float_vec_aligned[sizeof(float_vector)/sizeof(float)]);
|
||||
|
||||
SSE_ALIGN (static float_vec_aligned inpvec[16]) = {
|
||||
/* 0 */ { 0.0, 0.0, 0.0, 0.0},
|
||||
/* 1 */ { 1.0, 0.0, 0.0, 0.0},
|
||||
/* 2 */ { 0.0, 1.0, 0.0, 0.0},
|
||||
/* 3 */ { 0.0, 0.0, 1.0, 0.0},
|
||||
/* 4 */ { 0.0, 0.0, 1.0, 0.5},
|
||||
/* 5 */ { 0.0, 0.0, 1.0, 1.0},
|
||||
/* 6 */ { 0.0, 0.0, 1.0, 1.5},
|
||||
/* 7 */ { 0.0, 0.0, 1.0, 2.0},
|
||||
/* 8 */ { 0.0, 0.0, 1.0, 2.5},
|
||||
/* 9 */ { 0.0, 0.0, 1.0, 3.0},
|
||||
/* 10 */ { 0.0, 0.0, 1.0, 3.5},
|
||||
/* 11 */ { 0.0, 0.0, 1.0, 4.0},
|
||||
/* 12 */ { 0.0, 0.0, 1.0, 4.5},
|
||||
/* 13 */ { 0.0, 0.0, 1.0, 5.0},
|
||||
/* 14 */ { 0.0, 0.0, 1.0, 5.5},
|
||||
/* 15 */ { 0.0, 0.0, 1.0, 6.0}};
|
||||
|
||||
SSE_ALIGN(static float_vec_aligned inpvecb[16]) = {
|
||||
/* 0 */ { 0.0, 0.0, 0.0, 0.0},
|
||||
/* 1 */ { 1.0, 0.0, 0.0, 0.0},
|
||||
/* 2 */ { 1.0, 1.0, 0.0, 0.0},
|
||||
/* 3 */ { 1.0, 1.0, 1.0, 0.0},
|
||||
/* 4 */ { 1.0, 1.0, 1.0, 0.5},
|
||||
/* 5 */ { 1.0, 1.0, 1.0, 1.0},
|
||||
/* 6 */ { 1.0, 1.0, 1.0, 1.5},
|
||||
/* 7 */ { 1.0, 1.0, 1.0, 2.0},
|
||||
/* 8 */ { 1.0, 1.0, 1.0, 2.5},
|
||||
/* 9 */ { 1.0, 1.0, 1.0, 3.0},
|
||||
/* 10 */ { 1.0, 1.0, 1.0, 3.5},
|
||||
/* 11 */ { 1.0, 1.0, 1.0, 4.0},
|
||||
/* 12 */ { 1.0, 1.0, 1.0, 4.5},
|
||||
/* 13 */ { 1.0, 1.0, 1.0, 5.0},
|
||||
/* 14 */ { 1.0, 1.0, 1.0, 5.5},
|
||||
/* 15 */ { 1.0, 1.0, 1.0, 6.0}};
|
||||
|
||||
#if defined(USE_SIMD_INSTRUCTIONS)
|
||||
extern SIMD_AVX_STACKALIGN void
|
||||
baseInputs(const TanBoard anBoard, float arInput[])
|
||||
{
|
||||
int i = 3;
|
||||
|
||||
const unsigned int *pB = &anBoard[0][0];
|
||||
float *pInput = &arInput[0];
|
||||
|
||||
#if defined(HAVE_SSE)
|
||||
__m128 vec0;
|
||||
__m128 vec1;
|
||||
__m128 vec2;
|
||||
__m128 vec3;
|
||||
__m128 vec4;
|
||||
__m128 vec5;
|
||||
__m128 vec6;
|
||||
__m128 vec7;
|
||||
#elif defined(HAVE_NEON)
|
||||
float32x4_t vec0;
|
||||
float32x4_t vec1;
|
||||
float32x4_t vec2;
|
||||
float32x4_t vec3;
|
||||
float32x4_t vec4;
|
||||
float32x4_t vec5;
|
||||
float32x4_t vec6;
|
||||
float32x4_t vec7;
|
||||
#endif
|
||||
|
||||
while (i--) {
|
||||
#if defined(HAVE_SSE)
|
||||
vec0 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput, vec0);
|
||||
vec1 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec1);
|
||||
vec2 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec2);
|
||||
vec3 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec3);
|
||||
vec4 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec4);
|
||||
vec5 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec5);
|
||||
vec6 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec6);
|
||||
vec7 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec7);
|
||||
#else
|
||||
vec0 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput, vec0);
|
||||
vec1 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec1);
|
||||
vec2 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec2);
|
||||
vec3 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec3);
|
||||
vec4 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec4);
|
||||
vec5 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec5);
|
||||
vec6 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec6);
|
||||
vec7 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec7);
|
||||
#endif
|
||||
pInput += 4;
|
||||
}
|
||||
|
||||
/* bar */
|
||||
#if defined(HAVE_SSE)
|
||||
vec0 = _mm_load_ps(inpvecb[*pB++]);
|
||||
_mm_store_ps(pInput, vec0);
|
||||
#else
|
||||
vec0 = vld1q_f32(inpvecb[*pB++]);
|
||||
vst1q_f32(pInput, vec0);
|
||||
#endif
|
||||
pInput += 4;
|
||||
|
||||
i = 3;
|
||||
while (i--) {
|
||||
#if defined(HAVE_SSE)
|
||||
vec0 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput, vec0);
|
||||
vec1 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec1);
|
||||
vec2 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec2);
|
||||
vec3 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec3);
|
||||
vec4 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec4);
|
||||
vec5 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec5);
|
||||
vec6 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec6);
|
||||
vec7 = _mm_load_ps(inpvec[*pB++]);
|
||||
_mm_store_ps(pInput += 4, vec7);
|
||||
#else
|
||||
vec0 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput, vec0);
|
||||
vec1 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec1);
|
||||
vec2 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec2);
|
||||
vec3 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec3);
|
||||
vec4 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec4);
|
||||
vec5 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec5);
|
||||
vec6 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec6);
|
||||
vec7 = vld1q_f32(inpvec[*pB++]);
|
||||
vst1q_f32(pInput += 4, vec7);
|
||||
#endif
|
||||
pInput += 4;
|
||||
}
|
||||
|
||||
/* bar */
|
||||
#if defined(HAVE_SSE)
|
||||
vec0 = _mm_load_ps(inpvecb[*pB]);
|
||||
_mm_store_ps(pInput, vec0);
|
||||
#else
|
||||
vec0 = vld1q_f32(inpvecb[*pB++]);
|
||||
vst1q_f32(pInput, vec0);
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
#else
|
||||
extern void
|
||||
baseInputs(const TanBoard anBoard, float arInput[])
|
||||
{
|
||||
int j, i;
|
||||
|
||||
for (j = 0; j < 2; ++j) {
|
||||
float *afInput = arInput + j * 25 * 4;
|
||||
const unsigned int *board = anBoard[j];
|
||||
|
||||
/* Points */
|
||||
for (i = 0; i < 24; i++) {
|
||||
const unsigned int nc = board[i];
|
||||
|
||||
afInput[i * 4 + 0] = inpvec[nc][0];
|
||||
afInput[i * 4 + 1] = inpvec[nc][1];
|
||||
afInput[i * 4 + 2] = inpvec[nc][2];
|
||||
afInput[i * 4 + 3] = inpvec[nc][3];
|
||||
}
|
||||
|
||||
/* Bar */
|
||||
{
|
||||
const unsigned int nc = board[24];
|
||||
|
||||
afInput[24 * 4 + 0] = inpvecb[nc][0];
|
||||
afInput[24 * 4 + 1] = inpvecb[nc][1];
|
||||
afInput[24 * 4 + 2] = inpvecb[nc][2];
|
||||
afInput[24 * 4 + 3] = inpvecb[nc][3];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* rand.c: By Bob Jenkins. My random number generator, ISAAC. Public Domain.
|
||||
* MODIFIED:
|
||||
* 960327: Creation (addition of randinit, really)
|
||||
* 970719: use context, not global variables, for internal state
|
||||
* 980324: added main (ifdef'ed out), also rearranged randinit()
|
||||
* 010626: Note that this is public domain
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Minor modifications for use with GNU Backgammon.
|
||||
* Copyright (C) 1999-2007 the AUTHORS
|
||||
*
|
||||
* $Id: isaac.c,v 1.8 2013/06/16 02:16:24 mdpetch Exp $
|
||||
*/
|
||||
|
||||
/*lint --e{661,662} Ignore warnings of out of bounds array access (as ok) */
|
||||
|
||||
/* No configuration used in this file
|
||||
* #include "config.h" */
|
||||
#include "isaac.h"
|
||||
|
||||
#define ind(mm,x) ((mm)[((x)>>2)&(RANDSIZ-1)])
|
||||
#define rngstep(mix,a,b,mm,m,m2,r,x) \
|
||||
{ \
|
||||
x = *m; \
|
||||
a = ((a^(mix)) + *(m2++)) & 0xffffffff; \
|
||||
*(m++) = y = (ind(mm,x) + a + b) & 0xffffffff; \
|
||||
*(r++) = b = (ind(mm,y>>RANDSIZL) + x) & 0xffffffff; \
|
||||
}
|
||||
|
||||
void
|
||||
isaac(randctx * ctx)
|
||||
{
|
||||
register ub4 a, b, x, y, *m, *mm, *m2, *r, *mend;
|
||||
mm = ctx->randmem;
|
||||
r = ctx->randrsl;
|
||||
a = ctx->randa;
|
||||
b = (ctx->randb + (++ctx->randc)) & 0xffffffff;
|
||||
for (m = mm, mend = m2 = m + (RANDSIZ / 2); m < mend;) {
|
||||
rngstep(a << 13, a, b, mm, m, m2, r, x);
|
||||
rngstep(a >> 6, a, b, mm, m, m2, r, x);
|
||||
rngstep(a << 2, a, b, mm, m, m2, r, x);
|
||||
rngstep(a >> 16, a, b, mm, m, m2, r, x);
|
||||
}
|
||||
for (m2 = mm; m2 < mend;) {
|
||||
rngstep(a << 13, a, b, mm, m, m2, r, x);
|
||||
rngstep(a >> 6, a, b, mm, m, m2, r, x);
|
||||
rngstep(a << 2, a, b, mm, m, m2, r, x);
|
||||
rngstep(a >> 16, a, b, mm, m, m2, r, x);
|
||||
}
|
||||
ctx->randb = b;
|
||||
ctx->randa = a;
|
||||
}
|
||||
|
||||
|
||||
#define mix(a,b,c,d,e,f,g,h) \
|
||||
{ \
|
||||
a^=b<<11; d+=a; b+=c; \
|
||||
b^=c>>2; e+=b; c+=d; \
|
||||
c^=d<<8; f+=c; d+=e; \
|
||||
d^=e>>16; g+=d; e+=f; \
|
||||
e^=f<<10; h+=e; f+=g; \
|
||||
f^=g>>4; a+=f; g+=h; \
|
||||
g^=h<<8; b+=g; h+=a; \
|
||||
h^=a>>9; c+=h; a+=b; \
|
||||
}
|
||||
|
||||
/* if (flag==TRUE), then use the contents of randrsl[] to initialize mm[]. */
|
||||
void
|
||||
irandinit(randctx * ctx, word flag)
|
||||
{
|
||||
word i;
|
||||
ub4 a, b, c, d, e, f, g, h;
|
||||
ub4 *m, *r;
|
||||
ctx->randa = ctx->randb = ctx->randc = 0;
|
||||
m = ctx->randmem;
|
||||
r = ctx->randrsl;
|
||||
a = b = c = d = e = f = g = h = 0x9e3779b9; /* the golden ratio */
|
||||
|
||||
for (i = 0; i < 4; ++i) { /* scramble it */
|
||||
mix(a, b, c, d, e, f, g, h);
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
/* initialize using the contents of r[] as the seed */
|
||||
for (i = 0; i < RANDSIZ; i += 8) {
|
||||
a += r[i];
|
||||
b += r[i + 1];
|
||||
c += r[i + 2];
|
||||
d += r[i + 3];
|
||||
e += r[i + 4];
|
||||
f += r[i + 5];
|
||||
g += r[i + 6];
|
||||
h += r[i + 7];
|
||||
mix(a, b, c, d, e, f, g, h);
|
||||
m[i] = a;
|
||||
m[i + 1] = b;
|
||||
m[i + 2] = c;
|
||||
m[i + 3] = d;
|
||||
m[i + 4] = e;
|
||||
m[i + 5] = f;
|
||||
m[i + 6] = g;
|
||||
m[i + 7] = h;
|
||||
}
|
||||
/* do a second pass to make all of the seed affect all of m */
|
||||
for (i = 0; i < RANDSIZ; i += 8) {
|
||||
a += m[i];
|
||||
b += m[i + 1];
|
||||
c += m[i + 2];
|
||||
d += m[i + 3];
|
||||
e += m[i + 4];
|
||||
f += m[i + 5];
|
||||
g += m[i + 6];
|
||||
h += m[i + 7];
|
||||
mix(a, b, c, d, e, f, g, h);
|
||||
m[i] = a;
|
||||
m[i + 1] = b;
|
||||
m[i + 2] = c;
|
||||
m[i + 3] = d;
|
||||
m[i + 4] = e;
|
||||
m[i + 5] = f;
|
||||
m[i + 6] = g;
|
||||
m[i + 7] = h;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < RANDSIZ; i += 8) {
|
||||
/* fill in mm[] with messy stuff */
|
||||
mix(a, b, c, d, e, f, g, h);
|
||||
m[i] = a;
|
||||
m[i + 1] = b;
|
||||
m[i + 2] = c;
|
||||
m[i + 3] = d;
|
||||
m[i + 4] = e;
|
||||
m[i + 5] = f;
|
||||
m[i + 6] = g;
|
||||
m[i + 7] = h;
|
||||
}
|
||||
}
|
||||
|
||||
isaac(ctx); /* fill in the first set of results */
|
||||
ctx->randcnt = RANDSIZ; /* prepare to use the first set of results */
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* rand.h: definitions for a random number generator
|
||||
* By Bob Jenkins, 1996, Public Domain
|
||||
* MODIFIED:
|
||||
* 960327: Creation (addition of randinit, really)
|
||||
* 970719: use context, not global variables, for internal state
|
||||
* 980324: renamed seed to flag
|
||||
* 980605: recommend RANDSIZL=4 for noncryptography.
|
||||
* 010626: note this is public domain
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Minor modifications for use with GNU Backgammon.
|
||||
* Copyright (C) 1999-2006 the AUTHORS
|
||||
*
|
||||
* $Id: isaac.h,v 1.5 2013/06/16 02:16:24 mdpetch Exp $
|
||||
*/
|
||||
|
||||
#include "isaacs.h"
|
||||
|
||||
#ifndef ISAAC_H
|
||||
#define ISAAC_H
|
||||
|
||||
#define RANDSIZL (4) /* I recommend 8 for crypto, 4 for simulations */
|
||||
#define RANDSIZ (1<<RANDSIZL)
|
||||
|
||||
/* context of random number generator */
|
||||
struct randctx {
|
||||
ub4 randcnt;
|
||||
ub4 randrsl[RANDSIZ];
|
||||
ub4 randmem[RANDSIZ];
|
||||
ub4 randa;
|
||||
ub4 randb;
|
||||
ub4 randc;
|
||||
};
|
||||
typedef struct randctx randctx;
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------------
|
||||
* If (flag==TRUE), then use the contents of randrsl[0..RANDSIZ-1] as the seed.
|
||||
* ------------------------------------------------------------------------------
|
||||
*/
|
||||
void irandinit(randctx * r, word flag);
|
||||
|
||||
void isaac(randctx * r);
|
||||
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------------------------------
|
||||
* Call irand(/o_ randctx *r _o/) to retrieve a single 32-bit random value
|
||||
* ------------------------------------------------------------------------------
|
||||
*/
|
||||
#define irand(r) \
|
||||
(!(r)->randcnt-- ? \
|
||||
(isaac(r), (r)->randcnt=RANDSIZ-1, (r)->randrsl[(r)->randcnt]) : \
|
||||
(r)->randrsl[(r)->randcnt])
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* -----------------------------------------------------------------------------
|
||||
* standard.hi: Standard definitions and types, Bob Jenkins
|
||||
* -----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Minor modifications for use with GNU Backgammon.
|
||||
* Copyright (C) 1999-2013 the AUTHORS
|
||||
*
|
||||
* $Id: isaacs.h,v 1.5 2013/09/09 21:06:21 plm Exp $
|
||||
*/
|
||||
|
||||
#ifndef ISAACS_H
|
||||
#define ISAACS_H
|
||||
|
||||
#if 0
|
||||
|
||||
/* Pre-64bits relics. Most of them are not used anyway */
|
||||
|
||||
typedef unsigned long int ub4; /* unsigned 4-byte quantities */
|
||||
#define UB4MAXVAL 0xffffffff
|
||||
typedef signed long int sb4;
|
||||
#define SB4MAXVAL 0x7fffffff
|
||||
typedef unsigned short int ub2;
|
||||
#define UB2MAXVAL 0xffff
|
||||
typedef signed short int sb2;
|
||||
#define SB2MAXVAL 0x7fff
|
||||
typedef unsigned char ub1;
|
||||
#define UB1MAXVAL 0xff
|
||||
typedef signed char sb1; /* signed 1-byte quantities */
|
||||
#define SB1MAXVAL 0x7f
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STDINT_H
|
||||
#include <stdint.h>
|
||||
#else
|
||||
typedef unsigned int uint32_t;
|
||||
#endif
|
||||
|
||||
typedef uint32_t ub4; /* unsigned 4-byte quantities */
|
||||
#define UB4MAXVAL 0xffffffff
|
||||
|
||||
typedef int word; /* fastest type available */
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 1996-2001 Gary Wong <gtw@gnu.org>
|
||||
* Copyright (C) 2004-2026 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* No configuration used in this file
|
||||
* #include "config.h" */
|
||||
#include "list.h"
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
int
|
||||
ListCreate(listOLD * pl)
|
||||
{
|
||||
pl->plPrev = pl->plNext = pl;
|
||||
pl->p = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
listOLD *
|
||||
ListInsert(listOLD * pl, void *p)
|
||||
{
|
||||
listOLD *plNew = g_malloc(sizeof(listOLD));
|
||||
|
||||
plNew->p = p;
|
||||
|
||||
plNew->plNext = pl;
|
||||
plNew->plPrev = pl->plPrev;
|
||||
|
||||
pl->plPrev = plNew;
|
||||
plNew->plPrev->plNext = plNew;
|
||||
|
||||
return plNew;
|
||||
}
|
||||
|
||||
void
|
||||
ListDelete(listOLD * pl)
|
||||
{
|
||||
pl->plPrev->plNext = pl->plNext;
|
||||
pl->plNext->plPrev = pl->plPrev;
|
||||
|
||||
g_free(pl);
|
||||
}
|
||||
|
||||
void
|
||||
ListDeleteAll(const listOLD * pl)
|
||||
{
|
||||
listOLD *pc = pl->plNext;
|
||||
|
||||
while (pc != pl) {
|
||||
listOLD *pn = pc->plNext;
|
||||
|
||||
g_free(pc->p);
|
||||
ListDelete(pc);
|
||||
pc = pn;
|
||||
}
|
||||
|
||||
g_assert(ListEmpty(pl));
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 1996-1999 Gary Wong <gtw@gnu.org>
|
||||
* Copyright (C) 2004-2026 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef LIST_H
|
||||
#define LIST_H
|
||||
|
||||
typedef struct list {
|
||||
struct list *plPrev;
|
||||
struct list *plNext;
|
||||
void *p;
|
||||
} listOLD;
|
||||
/* Renamed to listOLD - use GList instead (hopefullly replace existing usage eventually */
|
||||
|
||||
extern int ListCreate(listOLD * pl);
|
||||
extern listOLD *ListInsert(listOLD * pl, void *p);
|
||||
extern void ListDelete(listOLD * pl);
|
||||
extern void ListDeleteAll(const listOLD * pl);
|
||||
|
||||
static inline int ListEmpty(const listOLD *pl)
|
||||
{
|
||||
return ((pl->plNext == pl) && (pl->plPrev == pl));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* Functions to compute MD5 message digest of files or memory blocks.
|
||||
* according to the definition of MD5 in RFC 1321 from April 1992,
|
||||
* by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
|
||||
* Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
|
||||
*
|
||||
* Minor modifications for use with GNU Backgammon.
|
||||
* Copyright (C) 2000-2019 the AUTHORS
|
||||
*
|
||||
* License changed from the GNU LGPL to the GNU GPL (as permitted under Term 3
|
||||
* of the GNU LGPL) by Gary Wong for distribution with GNU Backgammon.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* $Id: md5.c,v 1.15 2019/12/19 09:23:04 Superfly_Jon Exp $
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "md5.h"
|
||||
|
||||
/* Be conservative and always perform explicit byte-ordering. This is
|
||||
* redundant on little-endian architectures, but is portable. */
|
||||
#define WRITE( p, n ) \
|
||||
( ( (unsigned char *) (p) )[ 0 ] = (unsigned char)( (n) & 0xFF ), \
|
||||
( (unsigned char *) (p) )[ 1 ] = (unsigned char)(( (n) & 0xFF00 ) >> 8), \
|
||||
( (unsigned char *) (p) )[ 2 ] = (unsigned char)(( (n) & 0xFF0000 ) >> 16), \
|
||||
( (unsigned char *) (p) )[ 3 ] = (unsigned char)(( (n) & 0xFF000000 ) >> 24) )
|
||||
|
||||
#define READ( p ) \
|
||||
( ( (const unsigned char *) (p) )[ 0 ] | \
|
||||
( (unsigned int)((const unsigned char *) (p) )[ 1 ]) << 8 | \
|
||||
( (unsigned int)((const unsigned char *) (p) )[ 2 ]) << 16 | \
|
||||
( (unsigned int)((const unsigned char *) (p) )[ 3 ]) << 24 )
|
||||
|
||||
/* This array contains the bytes used to pad the buffer to the next
|
||||
* 64-byte boundary. (RFC 1321, 3.1: Step 1) */
|
||||
/* But this isn't used ???? */
|
||||
/*lint -e{785} Ignore not enough initilizer warning */
|
||||
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
|
||||
|
||||
|
||||
/* Initialize structure containing state of computation.
|
||||
* (RFC 1321, 3.3: Step 3) */
|
||||
void
|
||||
md5_init_ctx(struct md5_ctx *ctx)
|
||||
{
|
||||
ctx->A = 0x67452301;
|
||||
ctx->B = 0xefcdab89;
|
||||
ctx->C = 0x98badcfe;
|
||||
ctx->D = 0x10325476;
|
||||
|
||||
ctx->total[0] = ctx->total[1] = 0;
|
||||
ctx->buflen = 0;
|
||||
}
|
||||
|
||||
/* Put result from CTX in first 16 bytes following RESBUF. The result
|
||||
* must be in little endian byte order.
|
||||
*
|
||||
* IMPORTANT: On some systems it is required that RESBUF is correctly
|
||||
* aligned for a 32 bits value. */
|
||||
void *
|
||||
md5_read_ctx(const struct md5_ctx *ctx, void *resbuf)
|
||||
{
|
||||
char *r = (char *) resbuf;
|
||||
WRITE(r, ctx->A);
|
||||
WRITE(r + 4, ctx->B);
|
||||
WRITE(r + 8, ctx->C);
|
||||
WRITE(r + 12, ctx->D);
|
||||
|
||||
return resbuf;
|
||||
}
|
||||
|
||||
/* Process the remaining bytes in the internal buffer and the usual
|
||||
* prolog according to the standard and write the result to RESBUF.
|
||||
*
|
||||
* IMPORTANT: On some systems it is required that RESBUF is correctly
|
||||
* aligned for a 32 bits value. */
|
||||
void *
|
||||
md5_finish_ctx(struct md5_ctx *ctx, void *resbuf)
|
||||
{
|
||||
/* Take yet unprocessed bytes into account. */
|
||||
md5_uint32 bytes = ctx->buflen;
|
||||
size_t pad;
|
||||
|
||||
/* Now count remaining bytes. */
|
||||
ctx->total[0] += bytes;
|
||||
if (ctx->total[0] < bytes)
|
||||
++ctx->total[1];
|
||||
|
||||
pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes;
|
||||
memcpy(&ctx->buffer[bytes], fillbuf, pad);
|
||||
|
||||
/* Put the 64-bit file length in *bits* at the end of the buffer. */
|
||||
WRITE(ctx->buffer + bytes + pad, ctx->total[0] << 3);
|
||||
WRITE(ctx->buffer + bytes + pad + 4, ((ctx->total[1] << 3) | (ctx->total[0] >> 29)));
|
||||
|
||||
/* Process last bytes. */
|
||||
md5_process_block(ctx->buffer, bytes + pad + 8, ctx);
|
||||
|
||||
return md5_read_ctx(ctx, resbuf);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Compute MD5 message digest for bytes read from STREAM. The
|
||||
* resulting message digest number will be written into the 16 bytes
|
||||
* beginning at RESBLOCK. */
|
||||
int
|
||||
md5_stream(FILE * stream, void *resblock)
|
||||
{
|
||||
/* Important: BLOCKSIZE must be a multiple of 64. */
|
||||
#define BLOCKSIZE 4096
|
||||
struct md5_ctx ctx;
|
||||
char buffer[BLOCKSIZE + 72];
|
||||
size_t sum;
|
||||
|
||||
/* Initialize the computation context. */
|
||||
md5_init_ctx(&ctx);
|
||||
|
||||
/* Iterate over full file contents. */
|
||||
for (;;) {
|
||||
/* We read the file in blocks of BLOCKSIZE bytes. One call of the
|
||||
* computation function processes the whole buffer so that with the
|
||||
* next round of the loop another block can be read. */
|
||||
size_t n;
|
||||
sum = 0;
|
||||
|
||||
/* Read block. Take care for partial reads. */
|
||||
do {
|
||||
n = fread(buffer + sum, 1, BLOCKSIZE - sum, stream);
|
||||
|
||||
sum += n;
|
||||
}
|
||||
while (sum < BLOCKSIZE && n != 0);
|
||||
if (n == 0 && ferror(stream))
|
||||
return 1;
|
||||
|
||||
/* If end of file is reached, end the loop. */
|
||||
if (n == 0)
|
||||
break;
|
||||
|
||||
/* Process buffer with BLOCKSIZE bytes. Note that
|
||||
* BLOCKSIZE % 64 == 0
|
||||
*/
|
||||
md5_process_block(buffer, BLOCKSIZE, &ctx);
|
||||
}
|
||||
|
||||
/* Add the last bytes if necessary. */
|
||||
if (sum > 0)
|
||||
md5_process_bytes(buffer, sum, &ctx);
|
||||
|
||||
/* Construct result in desired memory. */
|
||||
md5_finish_ctx(&ctx, resblock);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
|
||||
* result is always in little endian byte order, so that a byte-wise
|
||||
* output yields to the wanted ASCII representation of the message
|
||||
* digest. */
|
||||
void *
|
||||
md5_buffer(const char *buffer, size_t len, void *resblock)
|
||||
{
|
||||
struct md5_ctx ctx;
|
||||
|
||||
/* Initialize the computation context. */
|
||||
md5_init_ctx(&ctx);
|
||||
|
||||
/* Process whole buffer but last len % 64 bytes. */
|
||||
md5_process_bytes(buffer, len, &ctx);
|
||||
|
||||
/* Put result in desired memory area. */
|
||||
return md5_finish_ctx(&ctx, resblock);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
md5_process_bytes(const void *buffer, size_t len, struct md5_ctx *ctx)
|
||||
{
|
||||
/* const void aligned_buffer = buffer; */
|
||||
|
||||
/* When we already have some bits in our internal buffer concatenate
|
||||
* both inputs first. */
|
||||
if (ctx->buflen != 0) {
|
||||
size_t left_over = ctx->buflen;
|
||||
size_t add = 128 - left_over > len ? len : 128 - left_over;
|
||||
|
||||
/* Only put full words in the buffer. */
|
||||
add -= add % sizeof(md5_uint32);
|
||||
|
||||
memcpy(&ctx->buffer[left_over], buffer, add);
|
||||
ctx->buflen += (md5_uint32) add;
|
||||
|
||||
if (ctx->buflen > 64) {
|
||||
md5_process_block(ctx->buffer, ctx->buflen & ~63U, ctx);
|
||||
|
||||
ctx->buflen &= 63;
|
||||
/* The regions in the following copy operation cannot overlap. */
|
||||
memcpy(ctx->buffer, &ctx->buffer[(left_over + add) & ~63U], ctx->buflen);
|
||||
}
|
||||
|
||||
buffer = (const char *) buffer + add;
|
||||
len -= add;
|
||||
}
|
||||
|
||||
/* Process available complete blocks. */
|
||||
if (len > 64) {
|
||||
md5_process_block(buffer, len & ~63U, ctx);
|
||||
buffer = (const char *) buffer + (len & ~63U);
|
||||
len &= 63;
|
||||
}
|
||||
|
||||
/* Move remaining bytes in internal buffer. */
|
||||
if (len > 0) {
|
||||
size_t left_over = ctx->buflen;
|
||||
|
||||
memcpy(&ctx->buffer[left_over], buffer, len);
|
||||
left_over += len;
|
||||
if (left_over >= 64) {
|
||||
md5_process_block(ctx->buffer, 64, ctx);
|
||||
left_over -= 64;
|
||||
memcpy(ctx->buffer, &ctx->buffer[64], left_over);
|
||||
}
|
||||
ctx->buflen = (md5_uint32) left_over;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* These are the four functions used in the four steps of the MD5 algorithm
|
||||
* and defined in the RFC 1321. The first function is a little bit optimized
|
||||
* (as found in Colin Plumbs public domain implementation). */
|
||||
/* #define FF(b, c, d) ((b & c) | (~b & d)) */
|
||||
#define FF(b, c, d) (d ^ (b & (c ^ d)))
|
||||
#define FG(b, c, d) FF (d, b, c)
|
||||
#define FH(b, c, d) (b ^ c ^ d)
|
||||
#define FI(b, c, d) (c ^ (b | ~d))
|
||||
|
||||
/* Process LEN bytes of BUFFER, accumulating context into CTX.
|
||||
* It is assumed that LEN % 64 == 0. */
|
||||
|
||||
void
|
||||
md5_process_block(const void *buffer, size_t len, struct md5_ctx *ctx)
|
||||
{
|
||||
md5_uint32 correct_words[16];
|
||||
const md5_uint32 *words = (const md5_uint32 *) buffer;
|
||||
size_t nwords = len / sizeof(md5_uint32);
|
||||
const md5_uint32 *endp = words + nwords;
|
||||
md5_uint32 A = ctx->A;
|
||||
md5_uint32 B = ctx->B;
|
||||
md5_uint32 C = ctx->C;
|
||||
md5_uint32 D = ctx->D;
|
||||
|
||||
/* First increment the byte count. RFC 1321 specifies the possible
|
||||
* length of the file up to 2^64 bits. Here we only compute the
|
||||
* number of bytes. Do a double word increment. */
|
||||
ctx->total[0] += (md5_uint32) len;
|
||||
if (ctx->total[0] < len)
|
||||
++ctx->total[1];
|
||||
|
||||
/* Process all bytes in the buffer with 64 bytes in each round of
|
||||
* the loop. */
|
||||
while (words < endp) {
|
||||
md5_uint32 *cwp = correct_words;
|
||||
md5_uint32 A_save = A;
|
||||
md5_uint32 B_save = B;
|
||||
md5_uint32 C_save = C;
|
||||
md5_uint32 D_save = D;
|
||||
|
||||
/* First round: using the given function, the context and a constant
|
||||
* the next context is computed. Because the algorithms processing
|
||||
* unit is a 32-bit word and it is determined to work on words in
|
||||
* little endian byte order we perhaps have to change the byte order
|
||||
* before the computation. To reduce the work for the next steps
|
||||
* we store the swapped words in the array CORRECT_WORDS. */
|
||||
|
||||
#define OP(a, b, c, d, s, T) \
|
||||
do \
|
||||
{ \
|
||||
a += FF (b, c, d) + (*cwp++ = READ (words)) + T; \
|
||||
++words; \
|
||||
CYCLIC (a, s); \
|
||||
a += b; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/* It is unfortunate that C does not provide an operator for
|
||||
* cyclic rotation. Hope the C compiler is smart enough. */
|
||||
#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s)))
|
||||
|
||||
/* Before we start, one word to the strange constants.
|
||||
* They are defined in RFC 1321 as
|
||||
*
|
||||
* T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64
|
||||
*/
|
||||
|
||||
/* Round 1. */
|
||||
/*lint --e{123, 717} Ignore macro warning */
|
||||
OP(A, B, C, D, 7, 0xd76aa478);
|
||||
OP(D, A, B, C, 12, 0xe8c7b756);
|
||||
OP(C, D, A, B, 17, 0x242070db);
|
||||
OP(B, C, D, A, 22, 0xc1bdceee);
|
||||
OP(A, B, C, D, 7, 0xf57c0faf);
|
||||
OP(D, A, B, C, 12, 0x4787c62a);
|
||||
OP(C, D, A, B, 17, 0xa8304613);
|
||||
OP(B, C, D, A, 22, 0xfd469501);
|
||||
OP(A, B, C, D, 7, 0x698098d8);
|
||||
OP(D, A, B, C, 12, 0x8b44f7af);
|
||||
OP(C, D, A, B, 17, 0xffff5bb1);
|
||||
OP(B, C, D, A, 22, 0x895cd7be);
|
||||
OP(A, B, C, D, 7, 0x6b901122);
|
||||
OP(D, A, B, C, 12, 0xfd987193);
|
||||
OP(C, D, A, B, 17, 0xa679438e);
|
||||
OP(B, C, D, A, 22, 0x49b40821);
|
||||
|
||||
/* For the second to fourth round we have the possibly swapped words
|
||||
* in CORRECT_WORDS. Redefine the macro to take an additional first
|
||||
* argument specifying the function to use. */
|
||||
#undef OP
|
||||
#define OP(f, a, b, c, d, k, s, T) \
|
||||
do \
|
||||
{ \
|
||||
a += f (b, c, d) + correct_words[k] + T; \
|
||||
CYCLIC (a, s); \
|
||||
a += b; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
/* Round 2. */
|
||||
OP(FG, A, B, C, D, 1, 5, 0xf61e2562);
|
||||
OP(FG, D, A, B, C, 6, 9, 0xc040b340);
|
||||
OP(FG, C, D, A, B, 11, 14, 0x265e5a51);
|
||||
OP(FG, B, C, D, A, 0, 20, 0xe9b6c7aa);
|
||||
OP(FG, A, B, C, D, 5, 5, 0xd62f105d);
|
||||
OP(FG, D, A, B, C, 10, 9, 0x02441453);
|
||||
OP(FG, C, D, A, B, 15, 14, 0xd8a1e681);
|
||||
OP(FG, B, C, D, A, 4, 20, 0xe7d3fbc8);
|
||||
OP(FG, A, B, C, D, 9, 5, 0x21e1cde6);
|
||||
OP(FG, D, A, B, C, 14, 9, 0xc33707d6);
|
||||
OP(FG, C, D, A, B, 3, 14, 0xf4d50d87);
|
||||
OP(FG, B, C, D, A, 8, 20, 0x455a14ed);
|
||||
OP(FG, A, B, C, D, 13, 5, 0xa9e3e905);
|
||||
OP(FG, D, A, B, C, 2, 9, 0xfcefa3f8);
|
||||
OP(FG, C, D, A, B, 7, 14, 0x676f02d9);
|
||||
OP(FG, B, C, D, A, 12, 20, 0x8d2a4c8a);
|
||||
|
||||
/* Round 3. */
|
||||
OP(FH, A, B, C, D, 5, 4, 0xfffa3942);
|
||||
OP(FH, D, A, B, C, 8, 11, 0x8771f681);
|
||||
OP(FH, C, D, A, B, 11, 16, 0x6d9d6122);
|
||||
OP(FH, B, C, D, A, 14, 23, 0xfde5380c);
|
||||
OP(FH, A, B, C, D, 1, 4, 0xa4beea44);
|
||||
OP(FH, D, A, B, C, 4, 11, 0x4bdecfa9);
|
||||
OP(FH, C, D, A, B, 7, 16, 0xf6bb4b60);
|
||||
OP(FH, B, C, D, A, 10, 23, 0xbebfbc70);
|
||||
OP(FH, A, B, C, D, 13, 4, 0x289b7ec6);
|
||||
OP(FH, D, A, B, C, 0, 11, 0xeaa127fa);
|
||||
OP(FH, C, D, A, B, 3, 16, 0xd4ef3085);
|
||||
OP(FH, B, C, D, A, 6, 23, 0x04881d05);
|
||||
OP(FH, A, B, C, D, 9, 4, 0xd9d4d039);
|
||||
OP(FH, D, A, B, C, 12, 11, 0xe6db99e5);
|
||||
OP(FH, C, D, A, B, 15, 16, 0x1fa27cf8);
|
||||
OP(FH, B, C, D, A, 2, 23, 0xc4ac5665);
|
||||
|
||||
/* Round 4. */
|
||||
OP(FI, A, B, C, D, 0, 6, 0xf4292244);
|
||||
OP(FI, D, A, B, C, 7, 10, 0x432aff97);
|
||||
OP(FI, C, D, A, B, 14, 15, 0xab9423a7);
|
||||
OP(FI, B, C, D, A, 5, 21, 0xfc93a039);
|
||||
OP(FI, A, B, C, D, 12, 6, 0x655b59c3);
|
||||
OP(FI, D, A, B, C, 3, 10, 0x8f0ccc92);
|
||||
OP(FI, C, D, A, B, 10, 15, 0xffeff47d);
|
||||
OP(FI, B, C, D, A, 1, 21, 0x85845dd1);
|
||||
OP(FI, A, B, C, D, 8, 6, 0x6fa87e4f);
|
||||
OP(FI, D, A, B, C, 15, 10, 0xfe2ce6e0);
|
||||
OP(FI, C, D, A, B, 6, 15, 0xa3014314);
|
||||
OP(FI, B, C, D, A, 13, 21, 0x4e0811a1);
|
||||
OP(FI, A, B, C, D, 4, 6, 0xf7537e82);
|
||||
OP(FI, D, A, B, C, 11, 10, 0xbd3af235);
|
||||
OP(FI, C, D, A, B, 2, 15, 0x2ad7d2bb);
|
||||
OP(FI, B, C, D, A, 9, 21, 0xeb86d391);
|
||||
|
||||
/* Add the starting values of the context. */
|
||||
A += A_save;
|
||||
B += B_save;
|
||||
C += C_save;
|
||||
D += D_save;
|
||||
}
|
||||
|
||||
/* Put checksum in context given as argument. */
|
||||
ctx->A = A;
|
||||
ctx->B = B;
|
||||
ctx->C = C;
|
||||
ctx->D = D;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Declaration of functions and data types used for MD5 sum computing
|
||||
* library functions.
|
||||
* Copyright (C) 1995, 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
|
||||
*
|
||||
* Minor modifications for use with GNU Backgammon.
|
||||
* Copyright (C) 2009-2021 the AUTHORS
|
||||
*
|
||||
* License changed from the GNU LGPL to the GNU GPL (as permitted under Term 3
|
||||
* of the GNU LGPL) by Gary Wong for distribution with GNU Backgammon.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* $Id: md5.h,v 1.8 2014/05/11 15:20:32 plm Exp $
|
||||
*/
|
||||
|
||||
#ifndef MD5_H
|
||||
#define MD5_H 1
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "common.h"
|
||||
|
||||
typedef uint32_t md5_uint32;
|
||||
|
||||
/* Structure to save state of computation between the single steps. */
|
||||
struct md5_ctx {
|
||||
md5_uint32 A;
|
||||
md5_uint32 B;
|
||||
md5_uint32 C;
|
||||
md5_uint32 D;
|
||||
|
||||
md5_uint32 total[2];
|
||||
md5_uint32 buflen;
|
||||
char buffer[128] __attribute__ ((__aligned__(__alignof__(md5_uint32))));
|
||||
};
|
||||
|
||||
/*
|
||||
* The following three functions are build up the low level used in
|
||||
* the functions `md5_stream' and `md5_buffer'.
|
||||
*/
|
||||
|
||||
/* Initialize structure containing state of computation.
|
||||
* (RFC 1321, 3.3: Step 3) */
|
||||
extern void md5_init_ctx(struct md5_ctx *ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
* initialization function) update the context for the next LEN bytes
|
||||
* starting at BUFFER.
|
||||
* It is necessary that LEN is a multiple of 64!!! */
|
||||
extern void md5_process_block(const void *buffer, size_t len, struct md5_ctx *ctx);
|
||||
|
||||
/* Starting with the result of former calls of this function (or the
|
||||
* initialization function) update the context for the next LEN bytes
|
||||
* starting at BUFFER.
|
||||
* It is NOT required that LEN is a multiple of 64. */
|
||||
extern void md5_process_bytes(const void *buffer, size_t len, struct md5_ctx *ctx);
|
||||
|
||||
/* Process the remaining bytes in the buffer and put result from CTX
|
||||
* in first 16 bytes following RESBUF. The result is always in little
|
||||
* endian byte order, so that a byte-wise output yields to the wanted
|
||||
* ASCII representation of the message digest.
|
||||
*
|
||||
* IMPORTANT: On some systems it is required that RESBUF is correctly
|
||||
* aligned for a 32 bits value. */
|
||||
extern void *md5_finish_ctx(struct md5_ctx *ctx, void *resbuf);
|
||||
|
||||
|
||||
/* Put result from CTX in first 16 bytes following RESBUF. The result is
|
||||
* always in little endian byte order, so that a byte-wise output yields
|
||||
* to the wanted ASCII representation of the message digest.
|
||||
*
|
||||
* IMPORTANT: On some systems it is required that RESBUF is correctly
|
||||
* aligned for a 32 bits value. */
|
||||
extern void *md5_read_ctx(const struct md5_ctx *ctx, void *resbuf);
|
||||
|
||||
|
||||
#if 0
|
||||
/* Compute MD5 message digest for bytes read from STREAM. The
|
||||
* resulting message digest number will be written into the 16 bytes
|
||||
* beginning at RESBLOCK. */
|
||||
extern int md5_stream(FILE * stream, void *resblock);
|
||||
#endif
|
||||
|
||||
/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The
|
||||
* result is always in little endian byte order, so that a byte-wise
|
||||
* output yields to the wanted ASCII representation of the message
|
||||
* digest. */
|
||||
extern void *md5_buffer(const char *buffer, size_t len, void *resblock);
|
||||
|
||||
#endif /* md5.h */
|
||||
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* Copyright (C) 1998-2002 Gary Wong <gtw@gnu.org>
|
||||
* Copyright (C) 2000-2019 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "backgammon.h"
|
||||
#include "common.h"
|
||||
#include <glib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "neuralnet.h"
|
||||
#include "simd.h"
|
||||
#include "sigmoid.h"
|
||||
|
||||
static int
|
||||
NeuralNetCreate(neuralnet * pnn, unsigned int cInput, unsigned int cHidden,
|
||||
unsigned int cOutput, float rBetaHidden, float rBetaOutput)
|
||||
{
|
||||
pnn->cInput = cInput;
|
||||
pnn->cHidden = cHidden;
|
||||
pnn->cOutput = cOutput;
|
||||
pnn->rBetaHidden = rBetaHidden;
|
||||
pnn->rBetaOutput = rBetaOutput;
|
||||
pnn->nTrained = 0;
|
||||
|
||||
if ((pnn->arHiddenWeight = sse_malloc(cHidden * cInput * sizeof(float))) == NULL)
|
||||
return -1;
|
||||
|
||||
if ((pnn->arOutputWeight = sse_malloc(cOutput * cHidden * sizeof(float))) == NULL) {
|
||||
sse_free(pnn->arHiddenWeight);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((pnn->arHiddenThreshold = sse_malloc(cHidden * sizeof(float))) == NULL) {
|
||||
sse_free(pnn->arOutputWeight);
|
||||
sse_free(pnn->arHiddenWeight);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((pnn->arOutputThreshold = sse_malloc(cOutput * sizeof(float))) == NULL) {
|
||||
sse_free(pnn->arHiddenThreshold);
|
||||
sse_free(pnn->arOutputWeight);
|
||||
sse_free(pnn->arHiddenWeight);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern void
|
||||
NeuralNetDestroy(neuralnet * pnn)
|
||||
{
|
||||
sse_free(pnn->arHiddenWeight);
|
||||
pnn->arHiddenWeight = 0;
|
||||
sse_free(pnn->arOutputWeight);
|
||||
pnn->arOutputWeight = 0;
|
||||
sse_free(pnn->arHiddenThreshold);
|
||||
pnn->arHiddenThreshold = 0;
|
||||
sse_free(pnn->arOutputThreshold);
|
||||
pnn->arOutputThreshold = 0;
|
||||
}
|
||||
|
||||
#if !defined(USE_SIMD_INSTRUCTIONS)
|
||||
|
||||
/* separate context for race, crashed, contact
|
||||
* -1: regular eval
|
||||
* 0: save base
|
||||
* 1: from base
|
||||
*/
|
||||
|
||||
static inline NNEvalType
|
||||
NNevalAction(NNState * pnState)
|
||||
{
|
||||
if (!pnState)
|
||||
return NNEVAL_NONE;
|
||||
|
||||
switch (pnState->state) {
|
||||
case NNSTATE_NONE:
|
||||
{
|
||||
/* incremental evaluation not useful */
|
||||
return NNEVAL_NONE;
|
||||
}
|
||||
case NNSTATE_INCREMENTAL:
|
||||
{
|
||||
/* next call should return FROMBASE */
|
||||
pnState->state = NNSTATE_DONE;
|
||||
|
||||
/* starting a new context; save base in the hope it will be useful */
|
||||
return NNEVAL_SAVE;
|
||||
}
|
||||
case NNSTATE_DONE:
|
||||
{
|
||||
/* context hit! use the previously computed base */
|
||||
return NNEVAL_FROMBASE;
|
||||
}
|
||||
}
|
||||
/* never reached */
|
||||
return NNEVAL_NONE; /* for the picky compiler */
|
||||
}
|
||||
|
||||
static void
|
||||
Evaluate(const neuralnet * pnn, const float arInput[], float ar[], float arOutput[], float *saveAr)
|
||||
{
|
||||
const unsigned int cHidden = pnn->cHidden;
|
||||
unsigned int i, j;
|
||||
float *prWeight;
|
||||
|
||||
/* Calculate activity at hidden nodes */
|
||||
for (i = 0; i < cHidden; i++)
|
||||
ar[i] = pnn->arHiddenThreshold[i];
|
||||
|
||||
prWeight = pnn->arHiddenWeight;
|
||||
|
||||
for (i = 0; i < pnn->cInput; i++) {
|
||||
float const ari = arInput[i];
|
||||
|
||||
if (ari == 0.0f)
|
||||
prWeight += cHidden;
|
||||
else {
|
||||
float *pr = ar;
|
||||
|
||||
if (ari == 1.0f)
|
||||
for (j = cHidden; j; j--)
|
||||
*pr++ += *prWeight++;
|
||||
else
|
||||
for (j = cHidden; j; j--)
|
||||
*pr++ += *prWeight++ * ari;
|
||||
}
|
||||
}
|
||||
|
||||
if (saveAr)
|
||||
memcpy(saveAr, ar, cHidden * sizeof(*saveAr));
|
||||
|
||||
for (i = 0; i < cHidden; i++)
|
||||
ar[i] = sigmoid(-pnn->rBetaHidden * ar[i]);
|
||||
|
||||
/* Calculate activity at output nodes */
|
||||
prWeight = pnn->arOutputWeight;
|
||||
|
||||
for (i = 0; i < pnn->cOutput; i++) {
|
||||
float r = pnn->arOutputThreshold[i];
|
||||
|
||||
for (j = 0; j < cHidden; j++)
|
||||
r += ar[j] * *prWeight++;
|
||||
|
||||
arOutput[i] = sigmoid(-pnn->rBetaOutput * r);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
EvaluateFromBase(const neuralnet * pnn, const float arInputDif[], float ar[], float arOutput[])
|
||||
{
|
||||
unsigned int i, j;
|
||||
float *prWeight;
|
||||
|
||||
/* Calculate activity at hidden nodes */
|
||||
/* for( i = 0; i < pnn->cHidden; i++ )
|
||||
* ar[ i ] = pnn->arHiddenThreshold[ i ]; */
|
||||
|
||||
prWeight = pnn->arHiddenWeight;
|
||||
|
||||
for (i = 0; i < pnn->cInput; ++i) {
|
||||
float const ari = arInputDif[i];
|
||||
|
||||
if (ari == 0.0f)
|
||||
prWeight += pnn->cHidden;
|
||||
else {
|
||||
float *pr = ar;
|
||||
|
||||
if (ari == 1.0f)
|
||||
for (j = pnn->cHidden; j; j--)
|
||||
*pr++ += *prWeight++;
|
||||
else if (ari == -1.0f)
|
||||
for (j = pnn->cHidden; j; j--)
|
||||
*pr++ -= *prWeight++;
|
||||
else
|
||||
for (j = pnn->cHidden; j; j--)
|
||||
*pr++ += *prWeight++ * ari;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < pnn->cHidden; i++)
|
||||
ar[i] = sigmoid(-pnn->rBetaHidden * ar[i]);
|
||||
|
||||
/* Calculate activity at output nodes */
|
||||
prWeight = pnn->arOutputWeight;
|
||||
|
||||
for (i = 0; i < pnn->cOutput; i++) {
|
||||
float r = pnn->arOutputThreshold[i];
|
||||
|
||||
for (j = 0; j < pnn->cHidden; j++)
|
||||
r += ar[j] * *prWeight++;
|
||||
|
||||
arOutput[i] = sigmoid(-pnn->rBetaOutput * r);
|
||||
}
|
||||
}
|
||||
|
||||
extern int
|
||||
NeuralNetEvaluate(const neuralnet * pnn, float arInput[], float arOutput[], NNState * pnState)
|
||||
{
|
||||
float *ar = (float *) g_alloca(pnn->cHidden * sizeof(float));
|
||||
switch (NNevalAction(pnState)) {
|
||||
case NNEVAL_NONE:
|
||||
{
|
||||
Evaluate(pnn, arInput, ar, arOutput, 0);
|
||||
break;
|
||||
}
|
||||
case NNEVAL_SAVE:
|
||||
{
|
||||
pnState->cSavedIBase = pnn->cInput;
|
||||
memcpy(pnState->savedIBase, arInput, pnn->cInput * sizeof(*ar));
|
||||
Evaluate(pnn, arInput, ar, arOutput, pnState->savedBase);
|
||||
break;
|
||||
}
|
||||
case NNEVAL_FROMBASE:
|
||||
{
|
||||
if (pnState->cSavedIBase != pnn->cInput) {
|
||||
Evaluate(pnn, arInput, ar, arOutput, 0);
|
||||
break;
|
||||
}
|
||||
memcpy(ar, pnState->savedBase, pnn->cHidden * sizeof(*ar));
|
||||
|
||||
{
|
||||
float *r = arInput;
|
||||
float *s = pnState->savedIBase;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < pnn->cInput; ++i, ++r, ++s) {
|
||||
if (*r != *s /*lint --e(777) */ ) {
|
||||
*r -= *s;
|
||||
} else {
|
||||
*r = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
EvaluateFromBase(pnn, arInput, ar, arOutput);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
extern int
|
||||
NeuralNetLoad(neuralnet * pnn, FILE * pf)
|
||||
{
|
||||
|
||||
unsigned int i;
|
||||
float *pr;
|
||||
char dummy[16];
|
||||
|
||||
if (fscanf(pf, "%u %u %u %15s %f %f\n", &pnn->cInput, &pnn->cHidden,
|
||||
&pnn->cOutput, dummy, &pnn->rBetaHidden,
|
||||
&pnn->rBetaOutput) < 5 || pnn->cInput < 1 ||
|
||||
pnn->cHidden < 1 || pnn->cOutput < 1 || pnn->rBetaHidden <= 0.0f || pnn->rBetaOutput <= 0.0f) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NeuralNetCreate(pnn, pnn->cInput, pnn->cHidden, pnn->cOutput, pnn->rBetaHidden, pnn->rBetaOutput))
|
||||
return -1;
|
||||
|
||||
pnn->nTrained = 1;
|
||||
|
||||
for (i = pnn->cInput * pnn->cHidden, pr = pnn->arHiddenWeight; i; i--)
|
||||
if (fscanf(pf, "%f\n", pr++) < 1)
|
||||
return -1;
|
||||
|
||||
for (i = pnn->cHidden * pnn->cOutput, pr = pnn->arOutputWeight; i; i--)
|
||||
if (fscanf(pf, "%f\n", pr++) < 1)
|
||||
return -1;
|
||||
|
||||
for (i = pnn->cHidden, pr = pnn->arHiddenThreshold; i; i--)
|
||||
if (fscanf(pf, "%f\n", pr++) < 1)
|
||||
return -1;
|
||||
|
||||
for (i = pnn->cOutput, pr = pnn->arOutputThreshold; i; i--)
|
||||
if (fscanf(pf, "%f\n", pr++) < 1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int
|
||||
NeuralNetLoadBinary(neuralnet * pnn, FILE * pf)
|
||||
{
|
||||
|
||||
int dummy;
|
||||
|
||||
#define FREAD( p, c ) \
|
||||
if ( fread( (p), sizeof( *(p) ), (c), pf ) < (unsigned int)(c) ) return -1
|
||||
|
||||
FREAD(&pnn->cInput, 1);
|
||||
FREAD(&pnn->cHidden, 1);
|
||||
FREAD(&pnn->cOutput, 1);
|
||||
FREAD(&dummy, 1);
|
||||
FREAD(&pnn->rBetaHidden, 1);
|
||||
FREAD(&pnn->rBetaOutput, 1);
|
||||
|
||||
if (pnn->cInput < 1 || pnn->cHidden < 1 || pnn->cOutput < 1 || pnn->rBetaHidden <= 0.0f || pnn->rBetaOutput <= 0.0f) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NeuralNetCreate(pnn, pnn->cInput, pnn->cHidden, pnn->cOutput, pnn->rBetaHidden, pnn->rBetaOutput))
|
||||
return -1;
|
||||
|
||||
pnn->nTrained = 1;
|
||||
|
||||
FREAD(pnn->arHiddenWeight, pnn->cInput * pnn->cHidden);
|
||||
FREAD(pnn->arOutputWeight, pnn->cHidden * pnn->cOutput);
|
||||
FREAD(pnn->arHiddenThreshold, pnn->cHidden);
|
||||
FREAD(pnn->arOutputThreshold, pnn->cOutput);
|
||||
#undef FREAD
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern int
|
||||
NeuralNetSaveBinary(const neuralnet * pnn, FILE * pf)
|
||||
{
|
||||
|
||||
#define FWRITE( p, c ) \
|
||||
if ( fwrite( (p), sizeof( *(p) ), (c), pf ) < (unsigned int)(c) ) return -1
|
||||
|
||||
FWRITE(&pnn->cInput, 1);
|
||||
FWRITE(&pnn->cHidden, 1);
|
||||
FWRITE(&pnn->cOutput, 1);
|
||||
FWRITE(&pnn->nTrained, 1);
|
||||
FWRITE(&pnn->rBetaHidden, 1);
|
||||
FWRITE(&pnn->rBetaOutput, 1);
|
||||
|
||||
FWRITE(pnn->arHiddenWeight, pnn->cInput * pnn->cHidden);
|
||||
FWRITE(pnn->arOutputWeight, pnn->cHidden * pnn->cOutput);
|
||||
FWRITE(pnn->arHiddenThreshold, pnn->cHidden);
|
||||
FWRITE(pnn->arOutputThreshold, pnn->cOutput);
|
||||
#undef FWRITE
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if defined(USE_SIMD_INSTRUCTIONS)
|
||||
|
||||
#if defined(DISABLE_SIMD_TEST)
|
||||
|
||||
int
|
||||
SIMD_Supported(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#if defined(HAVE_SSE)
|
||||
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
static int
|
||||
check_for_cpuid(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
#if defined(ENVIRONMENT32) && defined(__PIC__)
|
||||
/* We have to be careful to not destroy ebx if using PIC on 32bit builds */
|
||||
"pushl %%ebx\n\t"
|
||||
#endif
|
||||
"mov $1, %%eax\n\t"
|
||||
"shl $21, %%eax\n\t"
|
||||
"mov %%eax, %%edx\n\t"
|
||||
#if defined(ENVIRONMENT64)
|
||||
"pushf\n\t"
|
||||
"pop %%rax\n\t"
|
||||
#else
|
||||
"pushfl\n\t"
|
||||
"popl %%eax\n\t"
|
||||
#endif
|
||||
"mov %%eax, %%ecx\n\t"
|
||||
"xor %%edx, %%eax\n\t"
|
||||
#if defined(ENVIRONMENT64)
|
||||
"push %%rax\n\t"
|
||||
"popf\n\t"
|
||||
"pushf\n\t"
|
||||
"pop %%rax\n\t"
|
||||
#else
|
||||
"pushl %%eax\n\t"
|
||||
"popfl\n\t"
|
||||
"pushfl\n\t"
|
||||
"popl %%eax\n\t"
|
||||
#endif
|
||||
"xor %%ecx, %%eax\n\t"
|
||||
"test %%edx, %%eax\n\t"
|
||||
"jnz cpuid_success\n\t"
|
||||
/* Failed (non-pentium compatible machine) */
|
||||
"mov $-1, %%eax\n\t"
|
||||
"jmp cpuid_finished\n\t"
|
||||
|
||||
"cpuid_success:"
|
||||
"xor %%eax, %%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"cmp $1, %%eax\n\t"
|
||||
/* If 0 returned processor doesn't hav feature test */
|
||||
"jge feature_success\n\t"
|
||||
|
||||
/* Unlucky - somehow cpuid 1 isn't supported */
|
||||
"mov $-2, %%eax\n\t"
|
||||
"jmp cpuid_finished\n\t"
|
||||
|
||||
"feature_success:"
|
||||
"xor %%eax, %%eax\n\t"
|
||||
|
||||
"cpuid_finished:"
|
||||
#if defined(ENVIRONMENT32) && defined(__PIC__)
|
||||
"popl %%ebx\n\t"
|
||||
#endif
|
||||
: "=a"(result)
|
||||
:
|
||||
: "%ecx",
|
||||
#if !defined(ENVIRONMENT32) || !defined(__PIC__)
|
||||
"%ebx",
|
||||
#endif
|
||||
"%edx");
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
CheckSSE(void)
|
||||
{
|
||||
int result;
|
||||
int cpuidchk;
|
||||
|
||||
if ((cpuidchk = check_for_cpuid()) < 0)
|
||||
return cpuidchk;
|
||||
|
||||
#if defined(USE_AVX)
|
||||
|
||||
__asm__ __volatile__ (
|
||||
#if defined(ENVIRONMENT32) && defined(__PIC__)
|
||||
/* We have to be careful to not destroy ebx if using PIC on 32bit builds */
|
||||
"pushl %%ebx\n\t"
|
||||
#endif
|
||||
"mov $1, %%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"and $0x018000000, %%ecx\n\t" /* Check bit 27 (OS uses XSAVE/XRSTOR)*/
|
||||
"cmp $0x018000000, %%ecx\n\t" /* and bit 28 (AVX supported by CPU) */
|
||||
"jne avx_unsupported\n\t"
|
||||
"xor %%ecx, %%ecx\n\t" /* XFEATURE_ENABLED_MASK/XCR0 register number = 0 */
|
||||
"xgetbv\n\t" /* XFEATURE_ENABLED_MASK register is in edx:eax" */
|
||||
"and $0b110, %%eax\n\t"
|
||||
"cmp $0b110, %%eax\n\t" /* check the AVX registers restore at context switch */
|
||||
"jne avx_unsupported\n\t"
|
||||
"mov $1, %%eax\n\t"
|
||||
"jmp avx_end\n\t"
|
||||
|
||||
"avx_unsupported:"
|
||||
"xor %%eax, %%eax\n\t"
|
||||
|
||||
"avx_end:"
|
||||
#if defined(ENVIRONMENT32) && defined(__PIC__)
|
||||
"popl %%ebx\n\t"
|
||||
#endif
|
||||
: "=a"(result) /* Result returned in result variable */
|
||||
:
|
||||
: "%ecx",
|
||||
#if !defined(ENVIRONMENT32) || !defined(__PIC__)
|
||||
"%ebx",
|
||||
#endif
|
||||
"%edx"); /* These are all destroyed by cpuid */
|
||||
|
||||
#else
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
size_t length = sizeof(result);
|
||||
#if defined(__APPLE__)
|
||||
int error = sysctlbyname("hw.optional.sse", &result, &length, NULL, 0);
|
||||
#endif
|
||||
#if defined(__FreeBSD__)
|
||||
int error = sysctlbyname("hw.instruction_sse", &result, &length, NULL, 0);
|
||||
#endif
|
||||
if (error != 0)
|
||||
result = 0;
|
||||
|
||||
#else
|
||||
|
||||
__asm__ __volatile__ (
|
||||
#if defined(ENVIRONMENT32) && defined(__PIC__)
|
||||
/* We have to be careful to not destroy ebx if using PIC on I386 */
|
||||
"pushl %%ebx\n\t"
|
||||
#endif
|
||||
/* Check if sse is supported (bit 25/26 in edx from cpuid 1) */
|
||||
"mov $1, %%eax\n\t"
|
||||
"cpuid\n\t"
|
||||
"mov $1, %%eax\n\t"
|
||||
#if defined(USE_SSE2)
|
||||
"shl $26, %%eax\n\t"
|
||||
#else
|
||||
"shl $25, %%eax\n\t"
|
||||
#endif
|
||||
"test %%eax, %%edx\n\t"
|
||||
"jnz sse_success\n\t"
|
||||
/* Not supported */
|
||||
"mov $0, %%eax\n\t"
|
||||
"jmp sse_end\n\t"
|
||||
|
||||
"sse_success:"
|
||||
/* Supported */
|
||||
"mov $1, %%eax\n\t"
|
||||
|
||||
"sse_end:"
|
||||
#if defined(ENVIRONMENT32) && defined(__PIC__)
|
||||
"pop %%ebx\n\t"
|
||||
#endif
|
||||
|
||||
: "=a"(result)
|
||||
:
|
||||
: "%ecx",
|
||||
#if !defined(ENVIRONMENT32) || !defined(__PIC__)
|
||||
"%ebx",
|
||||
#endif
|
||||
"%edx");
|
||||
#endif /* APPLE/BSD */
|
||||
|
||||
#endif /* USE_AVX */
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* HAVE_SSE */
|
||||
|
||||
int
|
||||
SIMD_Supported(void)
|
||||
{
|
||||
static int state = -3;
|
||||
|
||||
if (state == -3) {
|
||||
#if defined(HAVE_NEON)
|
||||
state = CheckNEON();
|
||||
#elif defined(HAVE_SSE)
|
||||
state = CheckSSE();
|
||||
#else
|
||||
state = -2;
|
||||
#endif
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 1999-2002 Gary Wong <gtw@gnu.org>
|
||||
* Copyright (C) 2002-2017 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* $Id: neuralnet.h,v 1.36 2020/06/05 20:08:31 plm Exp $
|
||||
*/
|
||||
|
||||
#ifndef NEURALNET_H
|
||||
#define NEURALNET_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "common.h"
|
||||
|
||||
typedef struct {
|
||||
unsigned int cInput;
|
||||
unsigned int cHidden;
|
||||
unsigned int cOutput;
|
||||
int nTrained;
|
||||
float rBetaHidden;
|
||||
float rBetaOutput;
|
||||
float *arHiddenWeight;
|
||||
float *arOutputWeight;
|
||||
float *arHiddenThreshold;
|
||||
float *arOutputThreshold;
|
||||
} neuralnet;
|
||||
|
||||
typedef enum {
|
||||
NNEVAL_NONE,
|
||||
NNEVAL_SAVE,
|
||||
NNEVAL_FROMBASE
|
||||
} NNEvalType;
|
||||
|
||||
typedef enum {
|
||||
NNSTATE_NONE = -1,
|
||||
NNSTATE_INCREMENTAL,
|
||||
NNSTATE_DONE
|
||||
} NNStateType;
|
||||
|
||||
typedef struct {
|
||||
NNStateType state;
|
||||
float *savedBase;
|
||||
float *savedIBase;
|
||||
#if !defined(USE_SIMD_INSTRUCTIONS)
|
||||
unsigned int cSavedIBase;
|
||||
#endif
|
||||
} NNState;
|
||||
|
||||
extern void NeuralNetDestroy(neuralnet * pnn);
|
||||
#if !defined(USE_SIMD_INSTRUCTIONS)
|
||||
extern int NeuralNetEvaluate(const neuralnet * pnn, float arInput[], float arOutput[], NNState * pnState);
|
||||
#else
|
||||
extern int NeuralNetEvaluateSSE(const neuralnet * pnn, float arInput[], float arOutput[], NNState * pnState);
|
||||
#endif
|
||||
extern int NeuralNetLoad(neuralnet * pnn, FILE * pf);
|
||||
extern int NeuralNetLoadBinary(neuralnet * pnn, FILE * pf);
|
||||
extern int NeuralNetSaveBinary(const neuralnet * pnn, FILE * pf);
|
||||
extern int SIMD_Supported(void);
|
||||
|
||||
/* Try to determine whether we are 64-bit or 32-bit */
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#if defined(_WIN64)
|
||||
#define ENVIRONMENT64
|
||||
#else
|
||||
#define ENVIRONMENT32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#if defined(__x86_64__)
|
||||
#define ENVIRONMENT64
|
||||
#else
|
||||
#define ENVIRONMENT32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* Copyright (C) 2006-2009 Jon Kinsey <jonkinsey@gmail.com>
|
||||
* Copyright (C) 2007-2021 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
|
||||
#if defined(USE_SIMD_INSTRUCTIONS)
|
||||
|
||||
#define DEBUG_SSE 0
|
||||
|
||||
#include "simd.h"
|
||||
#include "neuralnet.h"
|
||||
#include <string.h>
|
||||
|
||||
#if defined(USE_NEON)
|
||||
#include <arm_neon.h>
|
||||
#elif defined(USE_AVX)
|
||||
#include <immintrin.h>
|
||||
#elif defined(USE_SSE2)
|
||||
#include <emmintrin.h>
|
||||
#else
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#include <glib.h>
|
||||
#include "sigmoid.h"
|
||||
|
||||
#if defined(HAVE_NEON)
|
||||
#include <signal.h>
|
||||
#include <setjmp.h>
|
||||
#endif
|
||||
|
||||
float *
|
||||
sse_malloc(size_t size)
|
||||
{
|
||||
#if defined(HAVE_POSIX_MEMALIGN)
|
||||
void *ptr = NULL;
|
||||
int ret;
|
||||
|
||||
ret = posix_memalign(&ptr, ALIGN_SIZE, size);
|
||||
|
||||
if (ret == 0)
|
||||
return (float *)ptr;
|
||||
|
||||
/* mimic g_malloc() error message */
|
||||
g_error("%s: failed to allocate %"G_GSIZE_FORMAT" bytes", G_STRLOC, size);
|
||||
|
||||
/* avoid warning: "control reaches end of non-void function" for
|
||||
compilers that don't understand that g_error() never returns. */
|
||||
return NULL;
|
||||
|
||||
#elif defined(HAVE__ALIGNED_MALLOC)
|
||||
return (float *) _aligned_malloc(size, ALIGN_SIZE);
|
||||
#else
|
||||
return (float *) _mm_malloc(size, ALIGN_SIZE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
sse_free(float *ptr)
|
||||
{
|
||||
#if defined(HAVE_POSIX_MEMALIGN)
|
||||
free(ptr);
|
||||
#elif defined(HAVE__ALIGNED_MALLOC)
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
_mm_free(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(HAVE_NEON)
|
||||
static jmp_buf env;
|
||||
|
||||
static void
|
||||
my_handler(int status)
|
||||
{
|
||||
longjmp(env, status);
|
||||
}
|
||||
|
||||
int
|
||||
CheckNEON(void)
|
||||
{
|
||||
void (*oldsig)(int);
|
||||
|
||||
oldsig = signal(SIGILL, my_handler);
|
||||
if (setjmp(env)) {
|
||||
signal(SIGILL, oldsig);
|
||||
return 0;
|
||||
} else {
|
||||
#if defined(__ARM_FEATURE_SIMD32)
|
||||
/* v7 */
|
||||
__asm__ __volatile__ ("vmin.f32 q0, q0, q0");
|
||||
#elif defined(__ARM_NEON)
|
||||
/* aarch64 */
|
||||
__asm__ __volatile__ ("fmin v0.4s, v0.4s, v0.4s");
|
||||
#else
|
||||
#error "Unexpected ARM architecture"
|
||||
#endif
|
||||
signal(SIGILL, oldsig);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_AVX) || defined(USE_SSE2) || defined(USE_NEON)
|
||||
#include <stdint.h>
|
||||
|
||||
static const union {
|
||||
float f[VEC_SIZE];
|
||||
float_vector ps;
|
||||
#if defined(USE_AVX)
|
||||
} ones = { {
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}};
|
||||
#else
|
||||
} ones = { {
|
||||
1.0f, 1.0f, 1.0f, 1.0f}};
|
||||
#endif
|
||||
|
||||
static const union {
|
||||
float f[VEC_SIZE];
|
||||
float_vector ps;
|
||||
#if defined(USE_AVX)
|
||||
} tens = { {
|
||||
10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f}};
|
||||
#else
|
||||
} tens = { {
|
||||
10.0f, 10.0f, 10.0f, 10.0f}};
|
||||
#endif
|
||||
|
||||
static const union {
|
||||
int32_t i32[VEC_SIZE];
|
||||
float_vector ps;
|
||||
#if defined(USE_AVX)
|
||||
} abs_mask = { {
|
||||
0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF}};
|
||||
#else
|
||||
} abs_mask = { {
|
||||
0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF}};
|
||||
#endif
|
||||
|
||||
static inline float_vector
|
||||
sigmoid_positive_ps(float_vector xin)
|
||||
{
|
||||
union {
|
||||
int_vector i;
|
||||
int32_t i32[VEC_SIZE];
|
||||
} i;
|
||||
float_vector ex;
|
||||
float *ex_elem = (float *) &ex;
|
||||
#if defined(USE_AVX)
|
||||
float_vector x1 = _mm256_min_ps(xin, tens.ps);
|
||||
#elif defined(HAVE_SSE)
|
||||
float_vector x1 = _mm_min_ps(xin, tens.ps);
|
||||
#else
|
||||
float_vector x1 = vminq_f32(xin, tens.ps);
|
||||
float_vector rec;
|
||||
#endif
|
||||
|
||||
#if defined(USE_AVX)
|
||||
x1 = _mm256_mul_ps(x1, tens.ps);
|
||||
i.i = _mm256_cvttps_epi32(x1);
|
||||
#elif defined(HAVE_SSE)
|
||||
x1 = _mm_mul_ps(x1, tens.ps);
|
||||
i.i = _mm_cvttps_epi32(x1);
|
||||
#else
|
||||
x1 = vmulq_f32(x1, tens.ps);
|
||||
i.i = vcvtq_s32_f32(x1);
|
||||
#endif
|
||||
ex_elem[0] = e[i.i32[0]];
|
||||
ex_elem[1] = e[i.i32[1]];
|
||||
ex_elem[2] = e[i.i32[2]];
|
||||
ex_elem[3] = e[i.i32[3]];
|
||||
#if defined(USE_AVX)
|
||||
ex_elem[4] = e[i.i32[4]];
|
||||
ex_elem[5] = e[i.i32[5]];
|
||||
ex_elem[6] = e[i.i32[6]];
|
||||
ex_elem[7] = e[i.i32[7]];
|
||||
#endif
|
||||
|
||||
#if defined(USE_AVX)
|
||||
x1 = _mm256_sub_ps(x1, _mm256_cvtepi32_ps(i.i));
|
||||
x1 = _mm256_add_ps(x1, tens.ps);
|
||||
#if defined(USE_FMA3)
|
||||
x1 = _mm256_fmadd_ps(x1, ex, ones.ps);
|
||||
#else
|
||||
x1 = _mm256_mul_ps(x1, ex);
|
||||
x1 = _mm256_add_ps(x1, ones.ps);
|
||||
#endif
|
||||
#ifdef __FAST_MATH__
|
||||
return _mm256_rcp_ps(x1);
|
||||
#else
|
||||
return _mm256_div_ps(ones.ps, x1);
|
||||
#endif
|
||||
#elif defined(HAVE_SSE)
|
||||
x1 = _mm_sub_ps(x1, _mm_cvtepi32_ps(i.i));
|
||||
x1 = _mm_add_ps(x1, tens.ps);
|
||||
x1 = _mm_mul_ps(x1, ex);
|
||||
x1 = _mm_add_ps(x1, ones.ps);
|
||||
#ifdef __FAST_MATH__
|
||||
return _mm_rcp_ps(x1);
|
||||
#else
|
||||
return _mm_div_ps(ones.ps, x1);
|
||||
#endif
|
||||
#else
|
||||
x1 = vsubq_f32(x1, vcvtq_f32_s32(i.i));
|
||||
x1 = vaddq_f32(x1, tens.ps);
|
||||
x1 = vmulq_f32(x1, ex);
|
||||
x1 = vaddq_f32(x1, ones.ps);
|
||||
|
||||
/* TODO: Check how many Newton-Raphson iterations are needed to match x86 rcp and div accuracy */
|
||||
#ifdef __FAST_MATH__
|
||||
rec = vrecpeq_f32(x1);
|
||||
return vmulq_f32(vrecpsq_f32(x1, rec), rec);
|
||||
#else
|
||||
rec = vrecpeq_f32(x1);
|
||||
rec = vmulq_f32(vrecpsq_f32(x1, rec), rec);
|
||||
return vmulq_f32(vrecpsq_f32(x1, rec), rec);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline float_vector
|
||||
sigmoid_ps(float_vector xin)
|
||||
{
|
||||
#if defined(USE_AVX)
|
||||
float_vector mask = _mm256_cmp_ps(xin, _mm256_setzero_ps(), _CMP_LT_OS);
|
||||
float_vector c;
|
||||
xin = _mm256_and_ps(xin, abs_mask.ps); /* Abs. value by clearing signbit */
|
||||
c = sigmoid_positive_ps(xin);
|
||||
return _mm256_blendv_ps(c, _mm256_sub_ps(ones.ps, c), mask);
|
||||
#elif defined(HAVE_SSE)
|
||||
float_vector mask = _mm_cmplt_ps(xin, _mm_setzero_ps());
|
||||
float_vector c;
|
||||
xin = _mm_and_ps(xin, abs_mask.ps); /* Abs. value by clearing signbit */
|
||||
c = sigmoid_positive_ps(xin);
|
||||
/* _mm_blendv_ps() is only available with SSE4.1 or later */
|
||||
return _mm_or_ps(_mm_andnot_ps(mask, c), _mm_and_ps(mask, _mm_sub_ps(ones.ps, c)));
|
||||
#else
|
||||
int_vector mask = (int_vector)vcltq_f32(xin, vdupq_n_f32(0.0f));
|
||||
float_vector c;
|
||||
xin = (float_vector)vandq_s32((int_vector)xin, (int_vector)abs_mask.ps); /* Abs. value by clearing signbit */
|
||||
c = sigmoid_positive_ps(xin);
|
||||
return vbslq_f32((uint32x4_t)mask, vsubq_f32(ones.ps, c), c);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // USE_SSE2 or USE_AVX
|
||||
|
||||
#if defined(USE_SSE2)
|
||||
#define INPUT_ADD() \
|
||||
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
|
||||
vec0 = _mm_load_ps(pr); \
|
||||
vec1 = _mm_load_ps(prWeight); \
|
||||
sum = _mm_add_ps(vec0, vec1); \
|
||||
_mm_store_ps(pr, sum); \
|
||||
}
|
||||
#define INPUT_MULTADD() \
|
||||
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
|
||||
vec0 = _mm_load_ps(pr); \
|
||||
vec1 = _mm_load_ps(prWeight); \
|
||||
vec3 = _mm_mul_ps(vec1, scalevec); \
|
||||
sum = _mm_add_ps(vec0, vec3); \
|
||||
_mm_store_ps(pr, sum); \
|
||||
}
|
||||
#endif
|
||||
#if defined(USE_AVX)
|
||||
#define INPUT_ADD() \
|
||||
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
|
||||
vec0 = _mm256_load_ps(pr); \
|
||||
vec1 = _mm256_load_ps(prWeight); \
|
||||
sum = _mm256_add_ps(vec0, vec1); \
|
||||
_mm256_store_ps(pr, sum); \
|
||||
}
|
||||
#if defined(USE_FMA3)
|
||||
#define INPUT_MULTADD() \
|
||||
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
|
||||
vec0 = _mm256_load_ps(pr); \
|
||||
vec1 = _mm256_load_ps(prWeight); \
|
||||
sum = _mm256_fmadd_ps(vec1, scalevec, vec0); \
|
||||
_mm256_store_ps(pr, sum); \
|
||||
}
|
||||
#else
|
||||
#define INPUT_MULTADD() \
|
||||
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
|
||||
vec0 = _mm256_load_ps(pr); \
|
||||
vec1 = _mm256_load_ps(prWeight); \
|
||||
vec3 = _mm256_mul_ps(vec1, scalevec); \
|
||||
sum = _mm256_add_ps(vec0, vec3); \
|
||||
_mm256_store_ps(pr, sum); \
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#if defined(USE_NEON)
|
||||
#define INPUT_ADD() \
|
||||
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
|
||||
vec0 = vld1q_f32(pr); \
|
||||
vec1 = vld1q_f32(prWeight); \
|
||||
sum = vaddq_f32(vec0, vec1); \
|
||||
vst1q_f32(pr, sum); \
|
||||
}
|
||||
#define INPUT_MULTADD() \
|
||||
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, pr += VEC_SIZE, prWeight += VEC_SIZE) { \
|
||||
vec0 = vld1q_f32(pr); \
|
||||
vec1 = vld1q_f32(prWeight); \
|
||||
vec3 = vmulq_f32(vec1, scalevec); \
|
||||
sum = vaddq_f32(vec0, vec3); \
|
||||
vst1q_f32(pr, sum); \
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
EvaluateSSE(const neuralnet * restrict pnn, const float arInput[], float ar[], float arOutput[])
|
||||
{
|
||||
const unsigned int cHidden = pnn->cHidden;
|
||||
unsigned int i, j;
|
||||
float *prWeight;
|
||||
#if defined(USE_SSE2) || defined(USE_AVX) || defined(USE_NEON)
|
||||
float *par;
|
||||
#if defined(USE_FMA3)
|
||||
float_vector vec0, vec1, scalevec, sum;
|
||||
#else
|
||||
float_vector vec0, vec1, vec3, scalevec, sum;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Calculate activity at hidden nodes */
|
||||
memcpy(ar, pnn->arHiddenThreshold, cHidden * sizeof(float));
|
||||
|
||||
prWeight = pnn->arHiddenWeight;
|
||||
|
||||
if (pnn->cInput != 214) { /* everything but the racing net */
|
||||
for (i = 0; i < 200;) { /* base inputs */
|
||||
float ari = arInput[i++];
|
||||
|
||||
/* 3 binaries, 1 float */
|
||||
|
||||
if (likely(ari == 0.0f))
|
||||
prWeight += cHidden;
|
||||
else {
|
||||
float *pr = ar;
|
||||
INPUT_ADD();
|
||||
}
|
||||
|
||||
ari = arInput[i++];
|
||||
|
||||
if (likely(ari == 0.0f))
|
||||
prWeight += cHidden;
|
||||
else {
|
||||
float *pr = ar;
|
||||
INPUT_ADD();
|
||||
}
|
||||
|
||||
ari = arInput[i++];
|
||||
|
||||
if (likely(ari == 0.0f)) {
|
||||
prWeight += cHidden;
|
||||
/* If 3rd element is 0, so is 4th. Skip it */
|
||||
prWeight += cHidden;
|
||||
i++;
|
||||
continue;
|
||||
} else {
|
||||
float *pr = ar;
|
||||
INPUT_ADD();
|
||||
}
|
||||
|
||||
ari = arInput[i++];
|
||||
|
||||
if (likely(ari == 0.0f))
|
||||
prWeight += cHidden;
|
||||
else {
|
||||
float *pr = ar;
|
||||
|
||||
#if defined(USE_FMA3)
|
||||
scalevec = _mm256_set1_ps(ari);
|
||||
INPUT_MULTADD();
|
||||
#elif defined(USE_NEON)
|
||||
scalevec = vdupq_n_f32(ari);
|
||||
INPUT_MULTADD();
|
||||
#else
|
||||
if (unlikely(ari == 1.0f)) {
|
||||
INPUT_ADD();
|
||||
} else {
|
||||
#if defined(USE_AVX)
|
||||
scalevec = _mm256_set1_ps(ari);
|
||||
#elif defined(HAVE_SSE)
|
||||
scalevec = _mm_set1_ps(ari);
|
||||
#endif
|
||||
INPUT_MULTADD();
|
||||
}
|
||||
#endif
|
||||
} /* base inputs are done */
|
||||
}
|
||||
|
||||
if (pnn->cInput == 250) /* Pruning nets are over, contact/crashed still have 2 * 25 floats */
|
||||
for (i = 200; i < 250; i++) {
|
||||
float const ari = arInput[i];
|
||||
|
||||
if (unlikely(ari == 0.0f))
|
||||
prWeight += cHidden;
|
||||
else {
|
||||
float *pr = ar;
|
||||
|
||||
#if defined(USE_AVX)
|
||||
scalevec = _mm256_set1_ps(ari);
|
||||
#elif defined(HAVE_SSE)
|
||||
scalevec = _mm_set1_ps(ari);
|
||||
#else
|
||||
scalevec = vdupq_n_f32(ari);
|
||||
#endif
|
||||
INPUT_MULTADD();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else /* racing net */
|
||||
for (i = 0; i < pnn->cInput; i++) {
|
||||
float const ari = arInput[i];
|
||||
|
||||
if (likely(ari == 0.0f))
|
||||
prWeight += cHidden;
|
||||
else {
|
||||
float *pr = ar;
|
||||
#if defined(USE_FMA3)
|
||||
scalevec = _mm256_set1_ps(ari);
|
||||
INPUT_MULTADD();
|
||||
#elif defined(USE_NEON)
|
||||
scalevec = vdupq_n_f32(ari);
|
||||
INPUT_MULTADD();
|
||||
#else
|
||||
if (likely(ari == 1.0f)) {
|
||||
INPUT_ADD();
|
||||
} else {
|
||||
#if defined(USE_AVX)
|
||||
scalevec = _mm256_set1_ps(ari);
|
||||
#elif defined(HAVE_SSE)
|
||||
scalevec = _mm_set1_ps(ari);
|
||||
#endif
|
||||
INPUT_MULTADD();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_SSE2) || defined(USE_AVX) || defined(USE_NEON)
|
||||
#if defined(USE_AVX)
|
||||
scalevec = _mm256_set1_ps(-pnn->rBetaHidden);
|
||||
#elif defined(HAVE_SSE)
|
||||
scalevec = _mm_set1_ps(-pnn->rBetaHidden);
|
||||
#else
|
||||
scalevec = vdupq_n_f32(-pnn->rBetaHidden);
|
||||
#endif
|
||||
|
||||
for (par = ar, i = (cHidden >> LOG2VEC_SIZE); i; i--, par += VEC_SIZE) {
|
||||
#if defined(USE_AVX)
|
||||
float_vector vec = _mm256_load_ps(par);
|
||||
vec = _mm256_mul_ps(vec, scalevec);
|
||||
vec = sigmoid_ps(vec);
|
||||
_mm256_store_ps(par, vec);
|
||||
#elif defined(HAVE_SSE)
|
||||
float_vector vec = _mm_load_ps(par);
|
||||
vec = _mm_mul_ps(vec, scalevec);
|
||||
vec = sigmoid_ps(vec);
|
||||
_mm_store_ps(par, vec);
|
||||
#else
|
||||
float_vector vec = vld1q_f32(par);
|
||||
vec = vmulq_f32(vec, scalevec);
|
||||
vec = sigmoid_ps(vec);
|
||||
vst1q_f32(par, vec);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < cHidden; i++)
|
||||
ar[i] = sigmoid(-pnn->rBetaHidden * ar[i]);
|
||||
#endif
|
||||
|
||||
/* Calculate activity at output nodes */
|
||||
prWeight = pnn->arOutputWeight;
|
||||
|
||||
for (i = 0; i < pnn->cOutput; i++) {
|
||||
|
||||
#if defined(USE_AVX)
|
||||
SSE_ALIGN(float r[8]);
|
||||
#else
|
||||
float r;
|
||||
#endif
|
||||
float *pr = ar;
|
||||
#if defined(USE_AVX)
|
||||
sum = _mm256_setzero_ps();
|
||||
#elif defined(HAVE_SSE)
|
||||
sum = _mm_setzero_ps();
|
||||
#else
|
||||
sum = vdupq_n_f32(0.0f);
|
||||
#endif
|
||||
for (j = (cHidden >> LOG2VEC_SIZE); j; j--, prWeight += VEC_SIZE, pr += VEC_SIZE) {
|
||||
#if defined(USE_AVX)
|
||||
vec0 = _mm256_load_ps(pr); /* Eight floats into vec0 */
|
||||
vec1 = _mm256_load_ps(prWeight); /* Eight weights into vec1 */
|
||||
#if defined(USE_FMA3)
|
||||
sum = _mm256_fmadd_ps(vec0, vec1, sum);
|
||||
#else
|
||||
vec3 = _mm256_mul_ps(vec0, vec1); /* Multiply */
|
||||
sum = _mm256_add_ps(sum, vec3); /* Add */
|
||||
#endif
|
||||
#elif defined(HAVE_SSE)
|
||||
vec0 = _mm_load_ps(pr); /* Four floats into vec0 */
|
||||
vec1 = _mm_load_ps(prWeight); /* Four weights into vec1 */
|
||||
vec3 = _mm_mul_ps(vec0, vec1); /* Multiply */
|
||||
sum = _mm_add_ps(sum, vec3); /* Add */
|
||||
#else
|
||||
vec0 = vld1q_f32(pr); /* Four floats into vec0 */
|
||||
vec1 = vld1q_f32(prWeight); /* Four weights into vec1 */
|
||||
vec3 = vmulq_f32(vec0, vec1); /* Multiply */
|
||||
sum = vaddq_f32(sum, vec3); /* Add */
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USE_AVX)
|
||||
vec0 = _mm256_hadd_ps(sum, sum);
|
||||
vec1 = _mm256_hadd_ps(vec0, vec0);
|
||||
_mm256_store_ps(r, vec1);
|
||||
|
||||
arOutput[i] = sigmoid(-pnn->rBetaOutput * (r[0] + r[4] + pnn->arOutputThreshold[i]));
|
||||
#elif defined(HAVE_SSE)
|
||||
vec0 = _mm_shuffle_ps(sum, sum, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
vec1 = _mm_add_ps(sum, vec0);
|
||||
vec0 = _mm_shuffle_ps(vec1, vec1, _MM_SHUFFLE(1, 1, 3, 3));
|
||||
sum = _mm_add_ps(vec1, vec0);
|
||||
_mm_store_ss(&r, sum);
|
||||
|
||||
arOutput[i] = sigmoid(-pnn->rBetaOutput * (r + pnn->arOutputThreshold[i]));
|
||||
|
||||
#else
|
||||
{
|
||||
float32x2_t vec0_h, vec0_l, vec1;
|
||||
|
||||
vec0_h = vget_high_f32(sum);
|
||||
vec0_l = vget_low_f32(sum);
|
||||
vec1 = vpadd_f32(vec0_h, vec0_l);
|
||||
vec1 = vpadd_f32(vec1, vec1);
|
||||
vst1_lane_f32(&r, vec1, 0);
|
||||
|
||||
arOutput[i] = sigmoid(-pnn->rBetaOutput * (r + pnn->arOutputThreshold[i]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if defined(USE_AVX)
|
||||
_mm256_zeroupper();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
extern int
|
||||
NeuralNetEvaluateSSE(const neuralnet * restrict pnn, /*lint -e{818} */ float arInput[],
|
||||
float arOutput[], NNState * UNUSED(pnState))
|
||||
{
|
||||
SSE_ALIGN(float ar[pnn->cHidden]);
|
||||
|
||||
#if DEBUG_SSE
|
||||
g_assert(sse_aligned(arOutput));
|
||||
g_assert(sse_aligned(ar));
|
||||
g_assert(sse_aligned(arInput));
|
||||
#endif
|
||||
|
||||
EvaluateSSE(pnn, arInput, ar, arOutput);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (C) 1998-2002 Gary Wong <gtw@gnu.org>
|
||||
* Copyright (C) 2008-2017 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SIGMOID_H
|
||||
#define SIGMOID_H
|
||||
/* e[k] = exp(k/10) / 10 */
|
||||
static float e[101] = {
|
||||
0.10000000000000001f,
|
||||
0.11051709180756478f,
|
||||
0.12214027581601698f,
|
||||
0.13498588075760032f,
|
||||
0.14918246976412702f,
|
||||
0.16487212707001281f,
|
||||
0.18221188003905089f,
|
||||
0.20137527074704767f,
|
||||
0.22255409284924679f,
|
||||
0.245960311115695f,
|
||||
0.27182818284590454f,
|
||||
0.30041660239464335f,
|
||||
0.33201169227365473f,
|
||||
0.36692966676192446f,
|
||||
0.40551999668446748f,
|
||||
0.44816890703380646f,
|
||||
0.49530324243951152f,
|
||||
0.54739473917271997f,
|
||||
0.60496474644129461f,
|
||||
0.66858944422792688f,
|
||||
0.73890560989306509f,
|
||||
0.81661699125676512f,
|
||||
0.90250134994341225f,
|
||||
0.99741824548147184f,
|
||||
1.1023176380641602f,
|
||||
1.2182493960703473f,
|
||||
1.3463738035001691f,
|
||||
1.4879731724872838f,
|
||||
1.6444646771097049f,
|
||||
1.817414536944306f,
|
||||
2.0085536923187668f,
|
||||
2.2197951281441637f,
|
||||
2.4532530197109352f,
|
||||
2.7112638920657881f,
|
||||
2.9964100047397011f,
|
||||
3.3115451958692312f,
|
||||
3.6598234443677988f,
|
||||
4.0447304360067395f,
|
||||
4.4701184493300818f,
|
||||
4.9402449105530168f,
|
||||
5.4598150033144233f,
|
||||
6.034028759736195f,
|
||||
6.6686331040925158f,
|
||||
7.3699793699595784f,
|
||||
8.1450868664968148f,
|
||||
9.0017131300521811f,
|
||||
9.9484315641933776f,
|
||||
10.994717245212353f,
|
||||
12.151041751873485f,
|
||||
13.428977968493552f,
|
||||
14.841315910257659f,
|
||||
16.402190729990171f,
|
||||
18.127224187515122f,
|
||||
20.033680997479166f,
|
||||
22.140641620418716f,
|
||||
24.469193226422039f,
|
||||
27.042640742615255f,
|
||||
29.886740096706028f,
|
||||
33.029955990964865f,
|
||||
36.503746786532886f,
|
||||
40.34287934927351f,
|
||||
44.585777008251675f,
|
||||
49.274904109325632f,
|
||||
54.457191012592901f,
|
||||
60.184503787208222f,
|
||||
66.514163304436181f,
|
||||
73.509518924197266f,
|
||||
81.24058251675433f,
|
||||
89.784729165041753f,
|
||||
99.227471560502622f,
|
||||
109.66331584284585f,
|
||||
121.19670744925763f,
|
||||
133.9430764394418f,
|
||||
148.02999275845451f,
|
||||
163.59844299959269f,
|
||||
180.80424144560632f,
|
||||
199.81958951041173f,
|
||||
220.83479918872089f,
|
||||
244.06019776244983f,
|
||||
269.72823282685101f,
|
||||
298.09579870417281f,
|
||||
329.44680752838406f,
|
||||
364.09503073323521f,
|
||||
402.38723938223131f,
|
||||
444.7066747699858f,
|
||||
491.47688402991344f,
|
||||
543.16595913629783f,
|
||||
600.29122172610175f,
|
||||
663.42440062778894f,
|
||||
733.19735391559948f,
|
||||
810.3083927575384f,
|
||||
895.52927034825075f,
|
||||
989.71290587439091f,
|
||||
1093.8019208165192f,
|
||||
1208.8380730216988f,
|
||||
1335.9726829661872f,
|
||||
1476.4781565577266f,
|
||||
1631.7607198015421f,
|
||||
1803.3744927828525f,
|
||||
1993.0370438230298f,
|
||||
1993.0370438230298f /* one extra :-) */
|
||||
};
|
||||
|
||||
/* Calculate an approximation to the sigmoid function 1 / ( 1 + e^x ).
|
||||
* This is executed very frequently during neural net evaluation, so
|
||||
* careful optimisation here pays off.
|
||||
*
|
||||
* Statistics on sigmoid(x) calls:
|
||||
* * >99% of the time, x is positive.
|
||||
* * 82% of the time, 3 < abs(x) < 8.
|
||||
*
|
||||
* 02/2017: The numbers above are 10+ years old
|
||||
* (old neural nets, possibly pruning nets not yet in use).
|
||||
* Current stats :
|
||||
* * 85% of the time, x is positive, comprising 80% < 10 and 20% > 10
|
||||
* * 15% of the time, x is negative with 99%+ > -10
|
||||
*/
|
||||
|
||||
static inline float
|
||||
sigmoid(float const xin)
|
||||
{
|
||||
if (likely(xin >= 0.0f)) {
|
||||
/* xin is almost always positive; we place this branch of the `if'
|
||||
* first, in the hope that the compiler/processor will predict the
|
||||
* conditional branch will not be taken. */
|
||||
if (likely(xin < 10.0f)) {
|
||||
/* again, predict the branch not to be taken */
|
||||
const float x1 = 10.0f * xin;
|
||||
const int i = (int) x1;
|
||||
|
||||
return 1.0f / (1.0f + e[i] * ((float)(10 - i) + x1));
|
||||
} else
|
||||
return 1.0f / 19931.370438230298f;
|
||||
} else {
|
||||
if (likely(xin > -10.0f)) {
|
||||
const float x1 = -10.0f * xin;
|
||||
const int i = (int) x1;
|
||||
|
||||
return 1.0f - 1.0f / (1 + e[i] * ((float)(10 - i) + x1));
|
||||
} else
|
||||
return 19930.370438230298f / 19931.370438230298f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2006 Jon Kinsey <jonkinsey@gmail.com>
|
||||
* Copyright (C) 2005-2018 the AUTHORS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* $Id: simd.h,v 1.9 2018/05/12 21:42:33 plm Exp $
|
||||
*/
|
||||
|
||||
#ifndef SIMD_H
|
||||
#define SIMD_H
|
||||
|
||||
#if defined(USE_SIMD_INSTRUCTIONS)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "common.h"
|
||||
|
||||
#if defined(USE_AVX)
|
||||
#define ALIGN_SIZE 32
|
||||
#define VEC_SIZE 8
|
||||
#define LOG2VEC_SIZE 3
|
||||
#define float_vector __m256
|
||||
#define int_vector __m256i
|
||||
#else
|
||||
#define ALIGN_SIZE 16
|
||||
#define VEC_SIZE 4
|
||||
#define LOG2VEC_SIZE 2
|
||||
#if defined(HAVE_SSE)
|
||||
#define float_vector __m128
|
||||
#define int_vector __m128i
|
||||
#elif defined(HAVE_NEON)
|
||||
#define float_vector float32x4_t
|
||||
#define int_vector int32x4_t
|
||||
/*
|
||||
* Workaround for clang on raspbian 9
|
||||
* It defines __ARM_FEATURE_SIMD32 but its arm_neon.h needs __ARM_NEON
|
||||
*/
|
||||
#define __ARM_NEON 1
|
||||
#else
|
||||
#error "Inconsistent SIMD defines"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define SSE_ALIGN(D) __declspec(align(ALIGN_SIZE)) D
|
||||
#else
|
||||
#define SSE_ALIGN(D) D __attribute__ ((aligned(ALIGN_SIZE)))
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && defined(WIN32)
|
||||
/* Align stack pointer on 16 byte boundary so SSE variables work correctly */
|
||||
#define SIMD_STACKALIGN __attribute__((force_align_arg_pointer))
|
||||
#if defined(USE_AVX)
|
||||
#define SIMD_AVX_STACKALIGN __attribute__((force_align_arg_pointer))
|
||||
#else
|
||||
#define SIMD_AVX_STACKALIGN
|
||||
#endif /* USE_AVX */
|
||||
#else
|
||||
#define SIMD_STACKALIGN
|
||||
#define SIMD_AVX_STACKALIGN
|
||||
#endif /* GNUC/WIN32 */
|
||||
|
||||
#define sse_aligned(ar) (!(((size_t)ar) % ALIGN_SIZE))
|
||||
|
||||
extern float *sse_malloc(size_t size);
|
||||
extern void sse_free(float *ptr);
|
||||
|
||||
#if defined(HAVE_NEON)
|
||||
extern int CheckNEON(void);
|
||||
#endif
|
||||
|
||||
#else /* USE_SIMD_INSTRUCTIONS */
|
||||
|
||||
#define SSE_ALIGN(D) D
|
||||
#define sse_malloc malloc
|
||||
#define sse_free free
|
||||
#define SIMD_STACKALIGN
|
||||
#define SIMD_AVX_STACKALIGN
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* SIMD_H */
|
||||
Reference in New Issue
Block a user