initial import from waif

This commit is contained in:
Danny Robson 2011-05-23 17:18:52 +10:00
commit 745e06d1af
65 changed files with 5303 additions and 0 deletions

20
.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
/aclocal.m4
/autom4te.cache/
/compile
/config.*
/configure
/depcomp
.deps/
.dirstamp
/Doxyfile
/install-sh
/json.cpp
/libgim-*.tar.*
.libs
/libtool
/ltmain.sh
Makefile
Makefile.in
/missing
/stamp-h1
/version.cpp

1510
Doxyfile.in Normal file

File diff suppressed because it is too large Load Diff

53
Makefile.am Normal file
View File

@ -0,0 +1,53 @@
AUTOMAKE_OPTIONS = dist-bzip2 dist-xz foreign
ACLOCAL_AMFLAGS = -I m4
AM_CXXFLAGS = $(BOOST_CFLAGS) $(BOOST_FILESYSTEM_CFLAGS) $(COMMON_CXXFLAGS)
AM_LDFLAGS = $(BOOST_LIB) $(BOOST_FILESYSTEM_LIB) $(COMMON_LDFLAGS)
SUBDIRS = test
UTIL_INCLUDE = \
annotations.hpp \
backtrace.hpp \
debug.hpp \
except.hpp \
float.hpp \
io.hpp \
json.hpp \
maths.hpp \
matrix.hpp \
nocopy.hpp \
range.hpp \
region.hpp \
stream.hpp \
types.hpp \
vector.hpp \
version.hpp
UTIL_FILES = \
backtrace.cpp \
debug.cpp \
except.cpp \
float.cpp \
io.cpp \
json.cpp \
maths.cpp \
matrix.cpp \
range.cpp \
region.cpp \
stream.cpp \
types.cpp \
vector.cpp \
version.cpp
CLEANFILES = json.cpp version.cpp
EXTRA_DIST = json.cpp.rl version.cpp.rl
RAGELFLAGS = -F1
SUFFIXES = .cpp .cpp.rl
.cpp.rl.cpp:
$(RAGEL) $(RAGELFLAGS) -C $< -o $(builddir)/$@
lib_LTLIBRARIES = libutil.la
libutil_la_SOURCES = $(UTIL_INCLUDE) $(UTIL_FILES)
libutil_la_CXXFLAGS = -fPIC $(COMMON_CXXFLAGS)

37
annotations.hpp Normal file
View File

@ -0,0 +1,37 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __ANNOTATIONS_HPP
#define __ANNOTATIONS_HPP
// Don't use the name 'noreturn' as it interferes with other headers which
// may use __attribute__((noreturn)) explicitly.
#define terminal __attribute__ ((noreturn))
#define nonnull __attribute__ ((nonnull))
#define mustuse __attribute__ ((warn_unused_result))
#define pure __attribute__ ((pure))
#define likely(X) (__builtin_expect((X), 1))
#define unlikely(X) (__builtin_expect((X), 0))
#endif // __ANNOTATIONS_HPP

36
backtrace.cpp Normal file
View File

@ -0,0 +1,36 @@
#include "backtrace.hpp"
#include "debug.hpp"
#include <cstdlib>
#include <execinfo.h>
#include <algorithm>
#include <memory>
using namespace std;
debug::backtrace::backtrace (void):
m_frames (DEFAULT_DEPTH) {
int final;
while ((final = ::backtrace (&m_frames[0], m_frames.size ())) == m_frames.size ())
m_frames.resize (m_frames.size () * 2);
check_hard (final > 0);
m_frames.resize ((unsigned)final);
}
ostream&
operator <<(ostream &os, const debug::backtrace &rhs) {
const auto frames = rhs.frames ();
typedef unique_ptr<char *[], decltype(&std::free)> unique_str;
unique_str names (backtrace_symbols (&frames[0], frames.size ()), std::free);
for (unsigned int i = 0; i < frames.size (); ++i)
os << frames[i] << "\t" << names[i] << "\n";
return os;
}

22
backtrace.hpp Normal file
View File

@ -0,0 +1,22 @@
#include <string>
#include <vector>
#include <iostream>
namespace debug {
class backtrace {
protected:
static const unsigned int DEFAULT_DEPTH = 16;
std::vector<void *> m_frames;
public:
backtrace (void);
const decltype(m_frames)& frames(void) const
{ return m_frames; }
};
}
std::ostream&
operator <<(std::ostream&, const debug::backtrace&);

96
configure.ac Normal file
View File

@ -0,0 +1,96 @@
AC_INIT([libgim], [0.1.0], [danny@nerdcruft.net])
AC_CANONICAL_TARGET
## We remove CXXFLAGS as autotools inserts them /AFTER/ our own CXXFLAGS,
## thus overriding any variables that both set (specifically we're concerned
## about optimisation flags).
CXXFLAGS=""
AC_PROG_CXX
AC_LANG([C++])
AC_PROG_LIBTOOL
AM_PROG_CC_C_O
AC_CONFIG_MACRO_DIR([m4])
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER(config.h)
##
## Warnings
WARNING_FLAGS=""
AS_COMPILER_FLAG([-Wall], [WARNING_FLAGS="$WARNING_FLAGS -Wall"])
AS_COMPILER_FLAG([-Wextra], [WARNING_FLAGS="$WARNING_FLAGS -Wextra"])
AS_COMPILER_FLAG([-Wno-parentheses], [WARNING_FLAGS="$WARNING_FLAGS -Wno-parentheses"])
AS_COMPILER_FLAG([-Wpointer-arith], [WARNING_FLAGS="$WARNING_FLAGS -Wpointer-arith"])
AS_COMPILER_FLAG([-Wcast-qual], [WARNING_FLAGS="$WARNING_FLAGS -Wcast-qual"])
AS_COMPILER_FLAG([-Wcast-align], [WARNING_FLAGS="$WARNING_FLAGS -Wcast-align"])
AS_COMPILER_FLAG([-Wsign-compare], [WARNING_FLAGS="$WARNING_FLAGS -Wsign-compare"])
AS_COMPILER_FLAG([-Wsign-conversion], [WARNING_FLAGS="$WARNING_FLAGS -Wsign-conversion"])
AS_COMPILER_FLAG([-Wtype-limits], [WARNING_FLAGS="$WARNING_FLAGS -Wtype-limits"])
AS_COMPILER_FLAG([-Wfloat-equal], [WARNING_FLAGS="$WARNING_FLAGS -Wfloat-equal"])
#AS_COMPILER_FLAG([-Wswitch-default], [COMMON_CFLAGS="$COMMON_CFLAGS -Wswitch-default"])
AS_COMPILER_FLAG([-Wswitch-enum], [COMMON_CFLAGS="$COMMON_CFLAGS -Wswitch-enum"])
AS_COMPILER_FLAG([-Wunsafe-loop-optimizations],
[WARNING_FLAGS="$WARNING_FLAGS -Wunsafe-loop-optimizations"])
AS_COMPILER_FLAG([-Wunused-parameter], [WARNING_FLAGS="$WARNING_FLAGS -Wunused-parameter"])
AS_COMPILER_FLAG([-Wunused-but-set-variable], [WARNING_FLAGS="$WARNING_FLAGS -Wunused-parameter"])
AS_COMPILER_FLAG([-Wshadow], [WARNING_FLAGS="$WARNING_FLAGS -Wshadow"])
AS_COMPILER_FLAG([-Wredundant-decls], [WARNING_FLAGS="$WARNING_FLAGS -Wredundant-decls"])
AS_COMPILER_FLAG([-pedantic], [COMMON_CFLAGS="$COMMON_CFLAGS -pedantic"])
##
## Compilation
AS_COMPILER_FLAG([-fno-common ], [COMMON_CFLAGS="$COMMON_CFLAGS -fno-common "])
AS_COMPILER_FLAG([-fno-nonansi-builtins],
[COMMON_CFLAGS="$COMMON_CFLAGS -fno-nonansi-builtins"])
AS_COMPILER_FLAG([-fno-rtti], [COMMON_CFLAGS="$COMMON_CFLAGS -fno-rtti"])
AS_COMPILER_FLAG([-ggdb], [COMMON_CFLAGS="$COMMON_CFLAGS -ggdb"])
##
## Compiler features
AC_C_CONST
AC_C_RESTRICT
AC_C_INLINE
##
## stdlib features
AC_FUNC_MMAP
##
## Documentation
##
## Required packages
CHECK_RAGEL
AX_BOOST_BASE
AX_BOOST_FILESYSTEM
AX_BOOST_SYSTEM
## Optional packages
## Output
COMMON_CXXFLAGS="-O0 -g -std=c++0x -include config.h $WARNING_FLAGS $COMMON_CFLAGS"
COMMON_CFLAGS="-O0 -g -std=c99 -include config.h $WARNING_FLAGS $COMMON_CFLAGS"
COMMON_LDFLAGS=""
AC_SUBST(COMMON_CFLAGS)
AC_SUBST(COMMON_CXXFLAGS)
AC_OUTPUT([
Doxyfile
Makefile
test/Makefile
])

68
debug.cpp Normal file
View File

@ -0,0 +1,68 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#include "debug.hpp"
#include "backtrace.hpp"
#include <cstdlib>
#include <iostream>
using namespace std;
void
panic (const std::string& what) {
breakpoint ();
cerr << "PANIC: " << what << "\n" << debug::backtrace () << endl;
abort ();
}
void
panic (void)
{ panic ("NFI"); }
void
breakpoint (void) {
if (getenv ("DEBUG")) {
#if defined (__x86_64)
__asm__ ("int $3;");
#else
raise (SIGINT);
#endif
}
}
void
not_implemented (void)
{ panic ("Function not implemented"); }
void
unreachable (void) {
panic ("Unreachable code executed");
}
void
unreachable (const std::string& what) {
panic (" Unreachable code executed: " + what);
}

132
debug.hpp Normal file
View File

@ -0,0 +1,132 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __DEBUG_HPP
#define __DEBUG_HPP
#include "annotations.hpp"
#include <stdexcept>
#include <iostream>
#include <sstream>
#define verify_soft(C, COND) ({ \
const auto value = (C); \
check_soft(value COND); \
value; \
})
#define verify_hard(C, COND) ({ \
const auto value = (C); \
check_hard(value COND); \
value; \
})
#define _check_meta(C, SUCCESS, FAILURE) do { \
const auto value = (C); \
if (unlikely (!value)) { \
std::cerr << PACKAGE << ": " \
<< __FILE__ << ":" \
<< __LINE__ << ": " \
<< __FUNCTION__ \
<< ". Assertion '" << #C \
<< "' failed with: " << value << std::endl; \
\
{ FAILURE } \
} else { \
{ SUCCESS } \
} \
} while (0)
#define check_hard(C) _check_meta((C), { ; }, { panic (); })
#define check_soft(C) _check_meta((C), { ; }, { ; })
#define check_eq(A,B) do { \
const auto __a = (A); \
const auto __b = (B); \
_check_meta (almost_equal (__a, __b), \
{ ; }, \
{ \
std::ostringstream os; \
os << "expected equality.\n" \
<< #A << '(' << __a << ")" \
<< "\n != \n" \
<< #B << '(' << __b << ")"; \
panic (os.str ()); \
}); \
} while (0)
#define check_neq(A,B) do { \
const auto __a = (A); \
const auto __b = (B); \
_check_meta (!almost_equal (__a, __b), \
{ ; }, \
{ \
std::ostringstream os; \
os << "unexepected equality.\n" \
<< __a << "\n ==\n" << __b; \
panic (os.str ()); \
}); \
} while (0)
#define check_throws(E,C) do { \
bool caught = false; \
\
try \
{ C; } \
catch (E) \
{ caught = true; } \
\
if (unlikely (!caught)) \
panic ("expected exception: " #E); \
} while (0)
#define check(C) check_hard(C)
class panic_error {
protected:
std::string m_what;
public:
panic_error (const std::string &_what):
m_what (_what)
{ ; }
};
void panic (const std::string&) terminal;
void panic (void) terminal;
void not_implemented (void) terminal;
void unreachable (void) terminal;
void unreachable (const std::string&) terminal;
void breakpoint (void);
#endif // __DEBUG_HPP

37
except.cpp Normal file
View File

@ -0,0 +1,37 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#include "except.hpp"
#include <cstring>
#include <cerrno>
using namespace std;
errno_error::errno_error (int _errno):
runtime_error (strerror (_errno)),
id (_errno)
{ ; }
errno_error::errno_error ():
runtime_error (strerror (errno)),
id (errno)
{ ; }

51
except.hpp Normal file
View File

@ -0,0 +1,51 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __EXCEPT_HPP
#define __EXCEPT_HPP
#include <stdexcept>
class input_error : public std::runtime_error {
public:
input_error (const std::string &_what):
runtime_error (_what)
{ ; }
};
class unavailable_error : public std::runtime_error {
public:
unavailable_error (const std::string &_what):
runtime_error (_what)
{ ; }
};
class errno_error : public std::runtime_error {
public:
int id;
errno_error (int _errno);
errno_error ();
};
#endif

85
float.cpp Normal file
View File

@ -0,0 +1,85 @@
#include "float.hpp"
#include <cmath>
using namespace std;
/* Constructors */
template <unsigned int E, unsigned int S>
ieee_float<E, S>::ieee_float (void)
{ ; }
template <unsigned int E, unsigned int S>
ieee_float<E, S>::ieee_float (floating_t _floating):
m_floating (_floating)
{ ; }
template <unsigned int E, unsigned int S>
ieee_float<E, S>::ieee_float (const ieee_float &rhs):
m_bits (rhs.m_bits)
{ ; }
/* Classifiers */
template <unsigned int E, unsigned int S>
bool
ieee_float<E, S>::is_zero (void) const {
return m_components.exponent == 0 &&
m_components.significand == 0;
}
template <unsigned int E, unsigned int S>
bool
ieee_float<E, S>::is_subnormal (void) const {
return m_components.exponent == 0 &&
m_components.significand != 0;
}
template <unsigned int E, unsigned int S>
bool
ieee_float<E, S>::is_inifinity (void) const {
return m_components.exponent == (1 << EXPONENT_BITS) - 1 &&
m_components.significand == 0;
}
template <unsigned int E, unsigned int S>
bool
ieee_float<E, S>::is_nan (void) const {
return m_components.exponent == (1 << EXPONENT_BITS) - 1 &&
m_components.significand != 0;
}
template <unsigned int E, unsigned int S>
bool
ieee_float<E, S>::operator==(floating_t _floating) const {
// TODO: This method really shouldn't be generated if there's no
// representative native floating point type. But I'm sick of
// C++'s template bullshit for tonight.
check_hard (bits_type<TOTAL_BITS>::has_floating);
return m_bits == *(const uint_t*)&_floating;
}
#include <iostream>
template <unsigned int E, unsigned int S>
bool
ieee_float<E, S>::almost_equal (floating_t a,
floating_t b) {
static const double epsilon = 0.001;
return fabs(a - b) <= epsilon * std::fabs (a) ||
fabs(a - b) <= epsilon * std::fabs (b);
}
template class ieee_float< 5, 10>; // ieee_half
template class ieee_float< 8, 23>; // ieee_single;
template class ieee_float<11, 52>; // ieee_double;

66
float.hpp Normal file
View File

@ -0,0 +1,66 @@
#ifndef __FLOAT_HPP
#define __FLOAT_HPP
#include "types.hpp"
#include "debug.hpp"
template <unsigned int EXPONENT, unsigned int SIGNIFICAND>
class ieee_float {
public:
static const unsigned int EXPONENT_BITS = EXPONENT;
static const unsigned int SIGNIFICAND_BITS = SIGNIFICAND;
static const unsigned int TOTAL_BITS = 1 + EXPONENT + SIGNIFICAND;
static const unsigned int BIAS = (1 << (EXPONENT - 1)) - 1;
typedef typename bits_type<TOTAL_BITS>::uint uint_t;
typedef typename bits_type<TOTAL_BITS>::floating floating_t;
protected:
union {
uint_t m_bits;
floating_t m_floating;
struct {
uint_t sign : 1;
uint_t exponent : EXPONENT;
uint_t significand : SIGNIFICAND;
} m_components;
};
public:
ieee_float (void);
ieee_float (floating_t _floating);
ieee_float (const ieee_float &rhs);
static unsigned int bias (void)
{ return BIAS; }
void set_bits (uint_t _bits) { m_bits = _bits; }
uint_t bits (void) const { return m_bits; }
bool negative (void) const { return m_components.sign; }
void negate (void) { m_components.sign ^= m_components.sign; }
bool is_zero (void) const;
bool is_subnormal (void) const;
bool is_inifinity (void) const;
bool is_nan (void) const;
bool operator== (floating_t) const;
static bool almost_equal (floating_t, floating_t);
};
typedef ieee_float< 5, 10> ieee_half;
typedef ieee_float< 8, 23> ieee_single;
typedef ieee_float<11, 52> ieee_double;
static_assert (sizeof(ieee_half ) == 2, "ieee_half must be 2 bytes");
static_assert (sizeof(ieee_single ) == 4, "ieee_single must be 4 bytes");
static_assert (sizeof(ieee_double ) == 8, "ieee_double must be 8 bytes");
#endif // __FLOAT_HPP

87
io.cpp Normal file
View File

@ -0,0 +1,87 @@
#include "io.hpp"
#include "debug.hpp"
#include "except.hpp"
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;
fd_ref::fd_ref (int _fd):
m_fd (_fd)
{
if (m_fd < 0)
throw invalid_argument ("invalid file descriptor");
}
fd_ref::~fd_ref () {
check (m_fd >= 0);
close (m_fd);
}
fd_ref::operator int (void) const
{ return m_fd; }
mapped_file::mapped_file (const char *_path):
m_fd (open (_path, O_RDONLY))
{ load_fd (); }
mapped_file::mapped_file (const std::string &_path):
m_fd (open (_path.c_str (), O_RDONLY))
{ load_fd (); }
mapped_file::mapped_file (const boost::filesystem::path &_path):
m_fd (open (_path.native ().c_str (), O_RDONLY))
{ load_fd (); }
mapped_file::~mapped_file () {
check (m_data != NULL);
munmap (m_data, m_size);
}
void
mapped_file::load_fd (void) {
struct stat meta;
if (fstat (m_fd, &meta) < 0)
throw errno_error ();
m_size = (size_t)meta.st_size;
m_data = (uint8_t *)mmap (NULL, m_size, PROT_READ, MAP_PRIVATE, m_fd, 0);
if (m_data == MAP_FAILED)
throw errno_error ();
}
size_t
mapped_file::size (void) const {
check (m_size > 0);
check (m_data != NULL);
return m_size;
}
const uint8_t*
mapped_file::data (void) const {
check (m_size > 0);
check (m_data != NULL);
return m_data;
}

82
io.hpp Normal file
View File

@ -0,0 +1,82 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __UTIL_IO_HPP
#define __UTIL_IO_HPP
#include <cstdio>
#include <cstdint>
#include <memory>
#include <boost/filesystem.hpp>
enum access_t {
ACCESS_READ = 1 << 0,
ACCESS_WRITE = 1 << 1,
ACCESS_READWRITE = ACCESS_READ | ACCESS_WRITE
};
struct FILE_destructor {
void operator ()(FILE *f) { fclose (f); }
};
typedef std::unique_ptr <FILE, FILE_destructor> FILE_ref;
struct fd_destructor {
void operator ()(int fd) { close (fd); }
};
struct fd_ref {
public:
int m_fd;
fd_ref (int _fd);
~fd_ref ();
operator int (void) const;
};
class mapped_file {
protected:
fd_ref m_fd;
uint8_t *m_data;
size_t m_size;
void load_fd (void);
public:
mapped_file (const char *path);
mapped_file (const std::string &path);
mapped_file (const boost::filesystem::path &path);
mapped_file (const mapped_file &rhs);
mapped_file& operator =(const mapped_file &rhs);
~mapped_file ();
const uint8_t* data (void) const;
size_t size (void) const;
};
#endif

448
json.cpp.rl Normal file
View File

@ -0,0 +1,448 @@
#include "json.hpp"
#include "maths.hpp"
#include "io.hpp"
#include <cassert>
#include <deque>
#include <stdexcept>
#include <cstdlib>
#include <algorithm>
#include <sstream>
#include <sys/mman.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
/*
* Parsing
*/
struct parse_context {
parse_context(json::node *_root):
root (_root),
value (NULL),
key (NULL),
start (NULL),
stop (NULL)
{ ; }
json::node *root,
*value,
*key;
const char *start,
*stop;
};
%%{
machine json;
## Record whether parsing was successful for future use
action success
{ __success = true; }
action failure {
__success = false;
/*std::cerr << std::endl
<< "Failure on: '" << fc << "' in level " << top << " at " << fpc - p
<< std::endl;
*/
}
action new_object { nodestack.push_back (parse_context(new json::object)); }
action new_array { nodestack.push_back (parse_context(new json::array)); }
action new_object_value {
assert (nodestack.back ().root->is_object ());
assert (nodestack.back ().key);
assert (nodestack.back ().value);
if (!nodestack.back ().key->is_string ())
throw error ("object keys must be strings");
json::object *object = (json::object *)nodestack.back ().root;
object->insert (nodestack.back ().key->to_string (),
nodestack.back ().value);
nodestack.back ().key = NULL;
nodestack.back ().value = NULL;
}
action new_array_value {
assert (nodestack.back ().root->is_array ());
assert (nodestack.back ().value);
json::array *array = (json::array *)nodestack.back ().root;
array->insert (nodestack.back ().value);
nodestack.back ().value = NULL;
}
action new_string {
assert (!nodestack.empty ());
assert (!nodestack.back ().value);
std::string value (std::string (nodestack.back ().start,
nodestack.back ().stop));
nodestack.back ().value = new json::string(value);
}
action new_boolean {
assert (!nodestack.empty ());
assert (!nodestack.back ().value);
throw error ("unable to parse boolean");
}
action new_number {
assert (!nodestack.empty ());
assert (!nodestack.back ().value);
errno = 0;
double value = strtod (nodestack.back ().start, NULL);
if (errno)
throw error ("unable to parse number");
nodestack.back ().value = new json::number (value);
}
action new_null {
assert (!nodestack.empty ());
assert (!nodestack.back ().value);
nodestack.back().value = new json::null ();
}
action new_object_key {
assert (!nodestack.empty ());
assert (nodestack.back ().root->is_object ());
assert (nodestack.back ().value);
assert (!nodestack.back ().key);
nodestack.back ().key = nodestack.back ().value;
nodestack.back ().value = NULL;
}
prepush {
fsmstack.push_back (0);
}
postpop {
fsmstack.pop_back ();
__root = nodestack.back ().root;
if (nodestack.size () > 1)
(nodestack.rbegin () + 1)->value = nodestack.back ().root;
nodestack.pop_back ();
}
variable stack fsmstack;
alphtype char;
## numerical
exp = [eE]('-' | '+')? digit+;
frac = '.' digit+;
int = '-'? [1-9]? digit+;
number = int ( frac
| exp
| frac exp)?;
## textual
char =
any - (cntrl | '\"' | '\\')
| '\\\"'
| '\\\\'
| '\\/'
| '\\b'
| '\\f'
| '\\n'
| '\\r'
| '\\t'
| '\\u' xdigit{4};
string = ('"'
char* >{ nodestack.back ().start = fpc; }
%{ nodestack.back ().stop = fpc; })
'"'
@new_string;
## other
boolean =
'true' @{ nodestack.back ().value = new json::boolean ( true); }
| 'false' @{ nodestack.back ().value = new json::boolean (false); };
## components
object = '{' @{ fhold; fcall _object; } '}';
array = '[' @{ fhold; fcall _array; } ']';
value =
string
| boolean
| number >{ nodestack.back ().start = fpc; } %{ nodestack.back ().stop = fpc; } %new_number
| object
| array
| 'null' %new_null;
## compound data types
_array := ('[' @new_array
space* ((value %new_array_value space* ',' space*)* value %new_array_value space*)?
']')
$!failure
@{ fhold; fret; };
pair = string %new_object_key space* ':' space* value %new_object_value;
_object := ('{' @new_object
space* ((pair space* ',' space*)* pair space*)?
'}')
$!failure
@{ fhold; fret; };
json := (space* object space*)
$!failure
%success
>{ __success = false; };
write data;
}%%
/*
* Node
*/
namespace json {
json::node *
parse (const boost::filesystem::path &path) {
mapped_file file(path);
return parse ((const char *)file.data (),
(const char *)file.data () + file.size ());
}
node *
parse (const char *start,
const char *stop) {
bool __success = true;
json::node *__root = NULL;
int cs, top = 0;
deque <int> fsmstack;
deque <parse_context> nodestack;
const char *p = start,
*pe = stop,
*eof = stop;
%%write init;
%%write exec;
if (!__success)
throw error ("unable to parse json");
//__root->print (cout) << endl;
assert (*__root == *__root);
return __root;
}
node*
parse (const char *start)
{ return parse (start, start + strlen (start)); }
}
const json::object&
json::node::to_object (void) const
{ throw error ("node is not an object"); }
const json::array&
json::node::to_array (void) const
{ throw error ("node is not an array"); }
const json::string&
json::node::to_string (void) const
{ throw error ("node is not a string"); }
const json::number&
json::node::to_number (void) const
{ throw error ("node is not a number"); }
const json::boolean&
json::node::to_boolean (void) const
{ throw error ("node is not a boolean"); }
bool
json::node::operator!=(const node &rhs) const
{ return !(*this == rhs); }
/*
* Object
*/
json::object::~object () {
for (auto i = m_values.begin (); i != m_values.end (); ++i)
delete i->second;
}
bool
json::object::operator ==(const json::object &rhs) const {
for (auto i = rhs.m_values.begin (), j = m_values.begin ();
i != rhs.m_values.end () && j != m_values.end ();
++i, ++j)
{
if (i->first != j->first)
return false;
if ((*i->second) != (*j->second))
return false;
}
return true;
}
void
json::object::insert (const std::string _key, json::node* value)
{ m_values[_key] = value; }
const json::node&
json::object::operator[](const std::string &key) const {
auto value = m_values.find (key);
if (value == m_values.end ()) {
ostringstream ss;
ss << "no key: " << key;
throw json::error (ss.str());
}
return *value->second;
}
std::ostream&
json::object::print (std::ostream &os) const {
os << "{";
for (auto i = m_values.begin (); i != m_values.end ();) {
os << '"' << i->first << "\" : " << *i->second;
if (++i != m_values.end ())
os << ",\n";
}
os << "}";
return os;
}
/*
* Array
*/
json::array::~array() {
for (auto i = m_values.begin(); i != m_values.end (); ++i)
delete *i;
}
void
json::array::insert (json::node *_value)
{ m_values.push_back (_value); }
bool
json::array::operator ==(const json::array &rhs) const {
for (auto i = rhs.m_values.begin (), j = m_values.begin ();
i != rhs.m_values.end () && j != m_values.end ();
++i, ++j)
{ if ((**i) != (**j)) return false; }
return true;
}
std::ostream&
json::array::print (std::ostream &os) const {
os << "[ ";
for (auto i = m_values.begin (); i != m_values.end (); ++i) {
os << (*i)->print (os);
if (i != m_values.end () - 1)
os << ", ";
}
os << "]";
return os;
}
/*
* String
*/
std::ostream&
json::string::print (std::ostream &os) const {
os << '"' << m_value << '"';
return os;
}
bool
json::string::operator ==(const json::string &rhs) const
{ return rhs.m_value == m_value; }
/*
* Number
*/
std::ostream&
json::number::print (std::ostream &os) const {
os << '"' << m_value << '"';
return os;
}
bool
json::number::operator ==(const json::number &rhs) const
{ return almost_equal (rhs.m_value, m_value); }
/*
* Boolean
*/
std::ostream&
json::boolean::print (std::ostream &os) const {
os << '"' << (m_value ? "true" : "false") << '"';
return os;
}
bool
json::boolean::operator ==(const json::boolean &rhs) const
{ return rhs.m_value == m_value; }
/*
* Null
*/
std::ostream&
json::null::print (std::ostream &os) const {
os << "null";
return os;
}
ostream&
operator <<(ostream &os, const json::node &n)
{ return n.print (os); }

209
json.hpp Normal file
View File

@ -0,0 +1,209 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __UTIL_JSON_HPP
#define __UTIL_JSON_HPP
#include <map>
#include <string>
#include <iostream>
#include <vector>
#include <boost/filesystem.hpp>
namespace json {
class node;
class object;
class array;
class string;
class number;
class boolean;
class null;
extern node* parse (const boost::filesystem::path &path);
extern node* parse (const char *start, const char *stop);
extern node* parse (const char *start);
class node {
public:
virtual ~node () { ; }
virtual const object& to_object (void) const;
virtual const array& to_array (void) const;
virtual const string& to_string (void) const;
virtual const number& to_number (void) const;
virtual const boolean& to_boolean (void) const;
virtual bool is_object (void) const { return false; }
virtual bool is_array (void) const { return false; }
virtual bool is_string (void) const { return false; }
virtual bool is_number (void) const { return false; }
virtual bool is_boolean (void) const { return false; }
virtual bool is_null (void) const { return false; }
virtual bool operator==(const node &rhs) const = 0;
virtual bool operator!=(const node &rhs) const;
virtual bool operator==(const object &) const { return false; }
virtual bool operator==(const array &) const { return false; }
virtual bool operator==(const string &) const { return false; }
virtual bool operator==(const number &) const { return false; }
virtual bool operator==(const boolean &) const { return false; }
virtual bool operator==(const null &) const { return false; }
virtual std::ostream& print (std::ostream &os) const = 0;
};
class object : public node {
protected:
std::map<std::string, node*> m_values;
public:
object () { ; }
virtual ~object ();
virtual const object& to_object (void) const { return *this; }
virtual bool is_object (void) const { return true; }
virtual bool operator==(const object &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual void insert (const std::string _key, node* value);
virtual const node& operator[](const std::string &key) const;
virtual std::ostream& print (std::ostream &os) const;
};
class array : public node {
protected:
std::vector<node*> m_values;
typedef std::vector<node*>::iterator array_iterator;
typedef std::vector<node*>::const_iterator const_array_iterator;
public:
virtual ~array();
virtual const array& to_array (void) const { return *this; }
virtual bool is_array (void) const { return true; }
virtual bool operator==(const array &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual size_t size (void) const
{ return m_values.size (); }
virtual node& operator [](unsigned int idx)
{ return *m_values[idx]; }
virtual const node& operator [](unsigned int idx) const
{ return *m_values[idx]; }
virtual const_array_iterator begin (void) const { return m_values.begin (); }
virtual const_array_iterator end (void) const { return m_values.end (); }
virtual void insert (json::node *_value);
virtual std::ostream& print (std::ostream &os) const;
};
class string : public node {
protected:
std::string m_value;
public:
string (const std::string &_value): m_value (_value) { ; }
string (const char *_value): m_value (_value) { ; }
virtual const string& to_string (void) const { return *this; }
virtual bool is_string (void) const { return true; }
virtual bool operator==(const string &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
operator const std::string&(void) const { return m_value; }
virtual std::ostream& print (std::ostream &os) const;
};
class number : public node {
protected:
double m_value;
public:
number (double _value): m_value (_value) { ; }
number (int _value): m_value (_value) { ; }
virtual const number& to_number (void) const { return *this; }
virtual bool is_number (void) const { return true; }
virtual bool operator==(const number &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
operator double(void) const { return m_value; }
virtual std::ostream& print (std::ostream &os) const;
};
class boolean : public node {
protected:
bool m_value;
public:
boolean (bool _value): m_value (_value) { ; }
virtual const boolean& to_boolean (void) const { return *this; }
virtual bool is_boolean (void) const { return true; }
virtual bool operator==(const boolean &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual std::ostream& print (std::ostream &os) const;
};
class null : public node {
public:
virtual bool is_null (void) const { return true; }
virtual std::ostream& print (std::ostream &os) const;
virtual bool operator==(const null&) const { return true; }
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
};
class error : public std::runtime_error {
public:
error (const std::string &_what):
std::runtime_error (_what)
{ ; }
};
}
std::ostream&
operator <<(std::ostream &os, const json::node &n);
#endif

1
m4/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.m4

64
m4/as_compiler_flag.m4 Normal file
View File

@ -0,0 +1,64 @@
dnl as-compiler-flag.m4 0.1.0
dnl autostars m4 macro for detection of compiler flags
dnl David Schleef <ds@schleef.org>
dnl Tim-Philipp Müller <tim centricular net>
dnl AS_COMPILER_FLAG(CFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
dnl Tries to compile with the given CFLAGS.
dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
dnl and ACTION-IF-NOT-ACCEPTED otherwise.
AC_DEFUN([AS_COMPILER_FLAG],
[
AC_MSG_CHECKING([to see if compiler understands $1])
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $1"
AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
CFLAGS="$save_CFLAGS"
if test "X$flag_ok" = Xyes ; then
$2
true
else
$3
true
fi
AC_MSG_RESULT([$flag_ok])
])
dnl AS_CXX_COMPILER_FLAG(CPPFLAGS, ACTION-IF-ACCEPTED, [ACTION-IF-NOT-ACCEPTED])
dnl Tries to compile with the given CPPFLAGS.
dnl Runs ACTION-IF-ACCEPTED if the compiler can compile with the flags,
dnl and ACTION-IF-NOT-ACCEPTED otherwise.
AC_DEFUN([AS_CXX_COMPILER_FLAG],
[
AC_REQUIRE([AC_PROG_CXX])
AC_MSG_CHECKING([to see if c++ compiler understands $1])
save_CPPFLAGS="$CPPFLAGS"
CPPFLAGS="$CPPFLAGS $1"
AC_LANG_PUSH(C++)
AC_TRY_COMPILE([ ], [], [flag_ok=yes], [flag_ok=no])
CPPFLAGS="$save_CPPFLAGS"
if test "X$flag_ok" = Xyes ; then
$2
true
else
$3
true
fi
AC_LANG_POP(C++)
AC_MSG_RESULT([$flag_ok])
])

43
m4/ragel.m4 Normal file
View File

@ -0,0 +1,43 @@
dnl Check for presence of the Ragel State Machine generator.
dnl
dnl This macro checks for the presence of the ragel tool in the system,
dnl and whether the ragel tool is absolutely needed for a complete
dnl build.
dnl
dnl To check for the need for Ragel, you have to provide the relative
dnl path of a source file generated through Ragel: if the file is
dnl present in the source tree, a missing ragel command will not cause
dnl the configure to abort.
AC_DEFUN([_RAGEL_VARS], [
AC_ARG_VAR([RAGEL], [Ragel generator command])
AC_ARG_VAR([RAGELFLAGS], [Ragel generator flags])
])
AC_DEFUN([CHECK_RAGEL], [
AC_REQUIRE([_RAGEL_VARS])
AC_CHECK_PROG([RAGEL], [ragel], [ragel], [no])
dnl We set RAGEL to false so that it would execute the "false"
dnl command if needed.
AS_IF([test x"$RAGEL" = x"no"], [RAGEL=false])
dnl Only test the need if not found
AS_IF([test x"$RAGEL" = x"false"], [
AC_MSG_CHECKING([whether we need ragel to regenerate sources])
AS_IF([test -a ${srcdir}/$1], [ragel_needed=no], [ragel_needed=yes])
AC_MSG_RESULT([$ragel_needed])
AS_IF([test x"$ragel_needed" = x"yes"],
[AC_MSG_ERROR([dnl
You need Ragel to build from GIT checkouts.
You can find Ragel at http://www.complang.org/ragel/dnl
])])
])
])
AC_DEFUN([CHECK_RAGEL_AM], [
CHECK_RAGEL([$1])
AM_CONDITIONAL([HAVE_RAGEL], [test x"$RAGEL" != x"false"])
])

56
maths.cpp Normal file
View File

@ -0,0 +1,56 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#include "maths.hpp"
#include "float.hpp"
#include <cmath>
template <typename T>
T
pow2 (T value)
{ return value * value; }
template double pow2(double);
template int pow2( int);
template <typename T>
double
rootsquare (T a, T b)
{ return sqrt (pow2 (a) + pow2 (b)); }
template double rootsquare (double, double);
template double rootsquare ( int, int);
template <>
bool
almost_equal (float a, float b)
{ return ieee_single::almost_equal (a, b); }
template <>
bool
almost_equal (double a, double b)
{ return ieee_double::almost_equal (a, b); }

92
maths.hpp Normal file
View File

@ -0,0 +1,92 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __MATHS_HPP
#define __MATHS_HPP
template <typename T>
T
pow2 (T value);
template <typename T>
double
rootsquare (T a, T b);
/**
* Check if two floating point numbers are approximately equal. Returns true
* if the difference is less than a percentage of each individual value.
*
* @e maximum percentage difference for equal values
*/
template <typename T>
bool
almost_equal (T a, T b)
{ return a == b; }
template <typename Ta, typename Tb>
bool
almost_equal (Ta a, Tb b) {
return almost_equal <decltype(a + b)> (static_cast<decltype(a + b)>(a),
static_cast<decltype(a + b)>(b));
}
template <>
bool
almost_equal (float a, float b);
template <>
bool
almost_equal (double a, double b);
/// Variadic minimum
template <typename T>
const T&
min (const T &a)
{ return a ; }
template <typename T, typename ...Args>
const T&
min (const T &a , const T &b , const Args &...args )
{ return min ( b < a ? b : a, args...); }
/// Variadic maximum
template <typename T>
const T&
max (const T &a)
{ return a ; }
template <typename T, typename ...Args>
const T&
max (const T &a , const T &b , const Args &...args )
{ return max ( b > a ? b : a, args...); }
#endif // __MATHS_HPP

513
matrix.cpp Normal file
View File

@ -0,0 +1,513 @@
#include "matrix.hpp"
#include "debug.hpp"
#include "range.hpp"
#include "maths.hpp"
#include <algorithm>
using namespace maths;
matrix::matrix (size_t _rows, size_t _columns):
m_rows (_rows),
m_columns (_columns),
m_data (NULL) {
if (m_rows <= 0 || m_columns <= 0)
throw std::runtime_error ("rows and columns must be positive");
m_data = new double[size ()];
}
matrix::matrix (size_t _rows,
size_t _columns,
const std::initializer_list <double> &_data):
m_rows (_rows),
m_columns (_columns),
m_data (NULL)
{
if (m_rows <= 0 || m_columns <= 0)
throw std::runtime_error ("rows and columns must be positive");
if (size () != _data.size ())
throw std::runtime_error ("element and initializer size differs");
check_hard (m_rows * m_columns == _data.size());
m_data = new double[size ()];
std::copy (_data.begin (), _data.end (), m_data);
}
matrix::matrix (const std::initializer_list <vector> &rhs):
m_rows (rhs.size ()),
m_columns (rhs.begin()->size ()),
m_data (new double[m_rows * m_columns])
{
double *row_cursor = m_data;
for (auto i = rhs.begin (); i != rhs.end (); ++i) {
check (i->size () == m_columns);
std::copy (i->data (), i->data () + i->size (), row_cursor);
row_cursor += m_columns;
}
}
matrix::matrix (const matrix &rhs):
m_rows (rhs.m_rows),
m_columns (rhs.m_columns) {
m_data = new double [m_rows * m_columns];
std::copy (rhs.m_data, rhs.m_data + m_rows * m_columns, m_data);
}
matrix::matrix (matrix &&rhs):
m_rows (rhs.m_rows),
m_columns (rhs.m_columns),
m_data (rhs.m_data) {
rhs.m_data = NULL;
}
matrix::~matrix()
{ delete [] m_data; }
void
matrix::sanity (void) const {
check (m_rows > 0);
check (m_columns > 0);
check (m_data != NULL);
}
const double *
matrix::operator [] (unsigned int row) const {
check_hard (row < m_rows);
return m_data + row * m_columns;
}
double *
matrix::operator [] (unsigned int row) {
check_hard (row < m_rows);
return m_data + row * m_columns;
}
const double *
matrix::data (void) const
{ return m_data; }
matrix&
matrix::operator =(const matrix& rhs) {
if (size () != rhs.size ()) {
delete [] m_data;
m_data = new double [m_rows * m_columns];
}
m_rows = rhs.m_rows;
m_columns = rhs.m_columns;
std::copy (rhs.m_data, rhs.m_data + m_rows * m_columns, m_data);
return *this;
}
matrix
matrix::operator * (double scalar) const {
matrix val (*this);
for (unsigned int i = 0; i < m_rows; ++i)
for (unsigned int j = 0; j < m_columns; ++j)
val[i][j] *= scalar;
return val;
}
matrix&
matrix::operator *=(double scalar) {
for (unsigned int i = 0; i < m_rows; ++i)
for (unsigned int j = 0; j < m_columns; ++j)
(*this)[i][j] *= scalar;
return *this;
}
matrix&
matrix::operator /= (double scalar)
{ return (*this) *= (1.0 / scalar); }
matrix
matrix::operator + (double scalar) const {
matrix val (*this);
for (unsigned int i = 0; i < m_rows; ++i)
for (unsigned int j = 0; j < m_columns; ++j)
val[i][j] += scalar;
return val;
}
matrix&
matrix::operator +=(double scalar) {
for (unsigned int i = 0; i < m_rows; ++i)
for (unsigned int j = 0; j < m_columns; ++j)
(*this)[i][j] += scalar;
return *this;
}
matrix
matrix::operator * (const matrix& rhs) const {
if (m_columns != rhs.rows ())
throw std::invalid_argument ("matrices size mismatch in multiplication");
matrix val (matrix::zeroes (m_rows, rhs.columns ()));
for (unsigned int i = 0; i < m_rows; ++i)
for (unsigned int j = 0; j < rhs.columns (); ++j)
for (unsigned int k = 0; k < m_columns; ++k)
val[i][j] += (*this)[i][k] * rhs[k][j];
return val;
}
matrix&
matrix::operator *=(const matrix& rhs)
{ return *this = *this * rhs; }
bool
matrix::operator ==(const matrix& rhs) const {
if (rhs.rows () != rows () ||
rhs.columns () != columns ())
return false;
return std::equal (m_data, m_data + size (), rhs.data ());
}
//matrix transpose (void) const { ; }
size_t
matrix::rows (void) const
{ return m_rows; }
size_t
matrix::columns (void) const
{ return m_columns; }
size_t
matrix::size (void) const
{ return rows () * columns (); }
bool
matrix::is_square (void) const
{ return m_rows == m_columns; }
bool
matrix::is_magic (void) const {
if (!is_square ())
return false;
unsigned int expected = m_rows * (m_rows * m_rows + 1) / 2;
range<double> numbers (1, m_rows * m_rows);
for (unsigned int i = 0; i < m_rows; ++i) {
unsigned int sum1 = 0, sum2 = 0;
for (unsigned int j = 0; j < m_columns; ++j) {
if (!numbers.includes ((*this)[i][j]) ||
!numbers.includes ((*this)[j][i]))
return false;
sum1 += (*this)[i][j];
sum2 += (*this)[j][i];
}
if (sum1 != expected || sum2 != expected)
return false;
}
return true;
}
bool
matrix::is_homogeneous (void) const {
if (m_rows != m_columns)
return false;
// Check the final row is all zeroes
for (unsigned int i = 0; i < m_columns - 1; ++i) {
if (!almost_equal ((*this)[m_rows - 1][i], 0.))
return false;
}
// Except for the last element, which has to be one
return almost_equal ((*this)[m_rows - 1][m_columns - 1], 1.);
}
double
matrix::determinant (void) const {
if (m_rows != m_columns)
not_implemented ();
switch (m_rows) {
case 2: return determinant2x2 ();
case 3: return determinant3x3 ();
case 4: return determinant4x4 ();
}
not_implemented ();
}
// With matrix A = [ a, b ]
// [ c, d ]
//
// det (A) = ad - bc
double
matrix::determinant2x2 (void) const {
check_eq (m_rows, 2);
check_eq (m_columns, 2);
return (*this)[0][0] * (*this)[1][1] -
(*this)[0][1] * (*this)[1][0];
}
// [ a, b, c ]
// Given matrix A = [ d, e, f ]
// [ g, h, i ]
//
// det (A) = aei + bfg + cdh - afg - bdi - ceg
// det (A) = a(ei - fg) + b(fg - di) + c(dh - eg)
double
matrix::determinant3x3 (void) const {
check_eq (m_rows, 3);
check_eq (m_columns, 3);
return (*this)[0][0] * (*this)[1][1] * (*this)[2][2] + // aei
(*this)[0][1] * (*this)[1][2] * (*this)[2][0] + // bfg
(*this)[0][2] * (*this)[1][0] * (*this)[2][1] - // cdh
(*this)[0][0] * (*this)[1][2] * (*this)[2][1] - // afh
(*this)[0][1] * (*this)[1][0] * (*this)[2][2] - // bdi
(*this)[0][2] * (*this)[1][1] * (*this)[2][0]; // ceg
}
// From libMathematics, http://www.geometrictools.com/
double
matrix::determinant4x4 (void) const {
check_eq (m_rows, 4);
check_eq (m_columns, 4);
double a0 = m_data[ 0] * m_data[ 5] - m_data[ 1] * m_data[ 4],
a1 = m_data[ 0] * m_data[ 6] - m_data[ 2] * m_data[ 4],
a2 = m_data[ 0] * m_data[ 7] - m_data[ 3] * m_data[ 4],
a3 = m_data[ 1] * m_data[ 6] - m_data[ 2] * m_data[ 5],
a4 = m_data[ 1] * m_data[ 7] - m_data[ 3] * m_data[ 5],
a5 = m_data[ 2] * m_data[ 7] - m_data[ 3] * m_data[ 6],
b0 = m_data[ 8] * m_data[13] - m_data[ 9] * m_data[12],
b1 = m_data[ 8] * m_data[14] - m_data[10] * m_data[12],
b2 = m_data[ 8] * m_data[15] - m_data[11] * m_data[12],
b3 = m_data[ 9] * m_data[14] - m_data[10] * m_data[13],
b4 = m_data[ 9] * m_data[15] - m_data[11] * m_data[13],
b5 = m_data[10] * m_data[15] - m_data[11] * m_data[14];
return a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
}
matrix
matrix::inverse (void) const {
if (m_rows != m_columns)
not_implemented ();
switch (m_rows) {
case 2: return inverse2x2 ();
case 3: return inverse3x3 ();
case 4: return inverse4x4 ();
}
not_implemented ();
}
matrix
matrix::inverse2x2 (void) const {
check (m_rows == 2);
check (m_columns == 2);
double det = determinant2x2 ();
if (almost_equal (det, 0.))
throw not_invertible ();
return matrix (2, 2, { (*this)[1][1], -(*this)[0][1],
-(*this)[1][0], (*this)[0][0] }) /= det;
}
// [ a, b, c ]
// Given matrix A = [ d, e, f ]
// [ g, h, i ]
//
matrix
matrix::inverse3x3 (void) const {
check (m_rows == 3);
check (m_columns == 3);
double det = determinant3x3();
if (almost_equal (det, 0.))
throw not_invertible ();
matrix val (m_rows, m_columns, {
(*this)[1][1] * (*this)[2][2] - (*this)[1][2] * (*this)[2][1], // ei - fh
(*this)[0][2] * (*this)[2][1] - (*this)[0][1] * (*this)[2][2], // ch - bi
(*this)[0][1] * (*this)[1][2] - (*this)[0][2] * (*this)[1][1], // bf - ce
(*this)[1][2] * (*this)[2][0] - (*this)[1][0] * (*this)[2][2], // fg - di
(*this)[0][0] * (*this)[2][2] - (*this)[0][2] * (*this)[2][0], // ai - cg
(*this)[0][2] * (*this)[1][0] - (*this)[0][0] * (*this)[1][2], // cd - af
(*this)[1][0] * (*this)[2][1] - (*this)[1][1] * (*this)[2][0], // dh - eg
(*this)[0][1] * (*this)[2][0] - (*this)[0][0] * (*this)[2][1], // bg - ah
(*this)[0][0] * (*this)[1][1] - (*this)[0][1] * (*this)[1][0] // ae - bd
});
return val /= det;
//matrix val ({ vector::cross ((*this)[1], (*this)[2], 3),
// vector::cross ((*this)[2], (*this)[0], 3),
// vector::cross ((*this)[0], (*this)[1], 3) });
//return val /= determinant3x3 ();
}
matrix
matrix::inverse4x4 (void) const {
double a0 = m_data[ 0] * m_data[ 5] - m_data[ 1] * m_data[ 4],
a1 = m_data[ 0] * m_data[ 6] - m_data[ 2] * m_data[ 4],
a2 = m_data[ 0] * m_data[ 7] - m_data[ 3] * m_data[ 4],
a3 = m_data[ 1] * m_data[ 6] - m_data[ 2] * m_data[ 5],
a4 = m_data[ 1] * m_data[ 7] - m_data[ 3] * m_data[ 5],
a5 = m_data[ 2] * m_data[ 7] - m_data[ 3] * m_data[ 6],
b0 = m_data[ 8] * m_data[13] - m_data[ 9] * m_data[12],
b1 = m_data[ 8] * m_data[14] - m_data[10] * m_data[12],
b2 = m_data[ 8] * m_data[15] - m_data[11] * m_data[12],
b3 = m_data[ 9] * m_data[14] - m_data[10] * m_data[13],
b4 = m_data[ 9] * m_data[15] - m_data[11] * m_data[13],
b5 = m_data[10] * m_data[15] - m_data[11] * m_data[14];
double det = a0 * b5 - a1 * b4 + a2 * b3 + a3 * b2 - a4 * b1 + a5 * b0;
if (almost_equal (det, 0.))
throw not_invertible ();
return matrix (4, 4, {
+ m_data[ 5] * b5 - m_data[ 6] * b4 + m_data[ 7] * b3,
- m_data[ 1] * b5 + m_data[ 2] * b4 - m_data[ 3] * b3,
+ m_data[13] * a5 - m_data[14] * a4 + m_data[15] * a3,
- m_data[ 9] * a5 + m_data[10] * a4 - m_data[11] * a3,
- m_data[ 4] * b5 + m_data[ 6] * b2 - m_data[ 7] * b1,
+ m_data[ 0] * b5 - m_data[ 2] * b2 + m_data[ 3] * b1,
- m_data[12] * a5 + m_data[14] * a2 - m_data[15] * a1,
+ m_data[ 8] * a5 - m_data[10] * a2 + m_data[11] * a1,
+ m_data[ 4] * b4 - m_data[ 5] * b2 + m_data[ 7] * b0,
- m_data[ 0] * b4 + m_data[ 1] * b2 - m_data[ 3] * b0,
+ m_data[12] * a4 - m_data[13] * a2 + m_data[15] * a0,
- m_data[ 8] * a4 + m_data[ 9] * a2 - m_data[11] * a0,
- m_data[ 4] * b3 + m_data[ 5] * b1 - m_data[ 6] * b0,
+ m_data[ 0] * b3 - m_data[ 1] * b1 + m_data[ 2] * b0,
- m_data[12] * a3 + m_data[13] * a1 - m_data[14] * a0,
+ m_data[ 8] * a3 - m_data[ 9] * a1 + m_data[10] * a0
}) /= det;
}
matrix
matrix::zeroes (size_t diag)
{ return zeroes (diag, diag); }
matrix
matrix::zeroes (size_t rows, size_t columns) {
matrix m (rows, columns);
std::fill (m.m_data, m.m_data + m.size (), 0.0);
return m;
}
matrix
matrix::identity (size_t diag) {
matrix val (zeroes (diag));
for (unsigned int i = 0; i < diag; ++i)
val[i][i] = 1.0;
return val;
}
matrix
matrix::magic (size_t n) {
check_hard (n > 2);
if (n % 2 == 1)
return magic_odd (n);
if (n % 4 == 0)
return magic_even_single (n);
return magic_even_double (n);
}
// Use the 'siamese' method. Start from the top centre, progress up-left one.
// If filled then drop down one row instead. Wrap around indexing.
matrix
matrix::magic_odd (size_t n) {
check_hard (n > 2);
check_hard (n % 2 == 1);
matrix val (zeroes (n));
for (unsigned int i = 1, x = n / 2, y = 0; i <= n * n; ++i) {
val[y][x] = i;
unsigned int x1 = (x + 1) % n,
y1 = (y + n - 1) % n;
if (!almost_equal (val[y1][x1], 0)) {
x1 = x;
y1 = (y + 1) % n;
}
x = x1;
y = y1;
}
return val;
}
matrix
matrix::magic_even_single (size_t)
{ not_implemented (); }
matrix
matrix::magic_even_double (size_t)
{ not_implemented (); }

119
matrix.hpp Normal file
View File

@ -0,0 +1,119 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __UTIL_MATRIX_HPP
#define __UTIL_MATRIX_HPP
#include "vector.hpp"
#include <assert.h>
#include <algorithm>
#include <stdexcept>
#include <initializer_list>
#include <iostream>
namespace maths {
class matrix {
protected:
size_t m_rows,
m_columns;
double *restrict m_data;
public:
matrix (size_t _rows, size_t _columns);
matrix (size_t _rows,
size_t _columns,
const std::initializer_list <double> &_data);
matrix (const std::initializer_list <vector> &_data);
matrix (const matrix &rhs);
matrix (matrix &&rhs);
~matrix();
void sanity (void) const;
const double * operator [] (unsigned int row) const;
double * operator [] (unsigned int row);
const double * data (void) const;
matrix& operator =(const matrix &rhs);
matrix operator * (double scalar) const;
matrix& operator *=(double scalar);
matrix operator * (const matrix &rhs) const;
matrix& operator *=(const matrix &rhs);
matrix& operator /=(double scalar);
matrix operator + (double scalar) const;
matrix& operator +=(double scalar);
matrix& operator -=(double scalar);
bool operator ==(const matrix &rhs) const;
//matrix transpose (void) const { ; }
size_t rows (void) const;
size_t columns (void) const;
size_t size (void) const;
/// Checks if this is a sqaure matrix, with a zero final column
/// and row (excepting the final diagonal entry).
bool is_homogeneous (void) const;
bool is_square (void) const;
bool is_magic (void) const;
public:
double determinant (void) const;
matrix inverse (void) const;
protected:
double determinant2x2 (void) const;
double determinant3x3 (void) const;
double determinant4x4 (void) const;
matrix inverse2x2 (void) const;
matrix inverse3x3 (void) const;
matrix inverse4x4 (void) const;
public:
static matrix zeroes (size_t n);
static matrix zeroes (size_t rows, size_t columns);
static matrix identity (size_t n);
/// Generate a magic square of order 'n'
static matrix magic (size_t n);
protected:
/// Generate a magic square with 'n' odd
static matrix magic_odd (size_t n);
/// Generate a magic square with 'n' divisible by 2, and not 4
static matrix magic_even_single (size_t n);
/// Generate a magic square with 'n' divisible by 4, and not 2
static matrix magic_even_double (size_t n);
};
class not_invertible : public std::runtime_error {
public:
not_invertible ():
std::runtime_error ("not_invertible")
{ ; }
};
}
#endif

34
nocopy.hpp Normal file
View File

@ -0,0 +1,34 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __NOCOPY_HPP
#define __NOCOPY_HPP
#include "debug.hpp"
class nocopy {
public:
nocopy () { ; }
private:
nocopy (const nocopy &) { ; }
nocopy& operator =(const nocopy &) { unreachable (); }
};
#endif

23
noncopyable.hpp Normal file
View File

@ -0,0 +1,23 @@
// Derived from boost::noncopyable
//
// (C) Copyright Beman Dawes 1999-2003. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Contributed by Dave Abrahams
#ifndef __UTIL_NONCOPYABLE_HPP
#define __UTIL_NONCOPYABLE_HPP
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private: // emphasize the following members are private
noncopyable( const noncopyable& );
const noncopyable& operator=( const noncopyable& );
};
#endif

120
range.cpp Normal file
View File

@ -0,0 +1,120 @@
#include "range.hpp"
#include "debug.hpp"
#include "maths.hpp"
#include <limits>
#include <cstdint>
using namespace std;
/*
* Range
*/
template <typename T>
range<T>::range (const json::node &node) {
if (node.is_string () && (node.to_string () == "UNIT" ||
node.to_string () == "unit")) {
min = UNIT.min;
max = UNIT.max;
} else if (node.is_string () && (node.to_string () == "UNLIMITED" ||
node.to_string () == "unlimited")) {
min = UNLIMITED.min;
max = UNLIMITED.max;
} else {
min = node.to_array ()[0].to_number ();
max = node.to_array ()[0].to_number ();
}
sanity ();
}
template <typename T>
range<T>::range (T _min, T _max):
min (_min),
max (_max)
{ sanity (); }
template <typename T>
bool
range<T>::includes (T val) const
{ return val >= min && val <= max; }
template <typename T>
bool
range<T>::includes (const range <T> &r) const
{ return r.min >= min && r.max <= max; }
template <typename T>
void
range<T>::sanity (void) const
{ check (min <= max); }
template <typename T>
T
range<T>::clamp (T val) const
{ return std::max (min, std::min (val, max)); }
template <typename T>
double
range<T>::normalise (T val) const {
return ((double)val - min) /
((double)max - min);
}
template <typename T>
T
range<T>::rand (void) const {
double pos = ::rand () / (double)(RAND_MAX);
return (max - min) * pos;
}
template <>
bool
range<float>::operator ==(const range<float> &rhs) const
{ return almost_equal (min, rhs.min) &&
almost_equal (max, rhs.max); }
template <>
bool
range<double>::operator ==(const range<double> &rhs) const
{ return almost_equal (min, rhs.min) &&
almost_equal (max, rhs.max); }
template <typename T>
bool
range<T>::operator ==(const range<T> &rhs) const
{ return min == rhs.min && max == rhs.max; }
template <typename T>
const range<T>
range<T>::UNLIMITED (numeric_limits <T>::is_integer ? numeric_limits <T>::min () :
-numeric_limits <T>::infinity (),
numeric_limits <T>::is_integer ? numeric_limits <T>::max () :
numeric_limits <T>::infinity ());
template <typename T>
const range<T>
range<T>::UNIT (0.0, 1.0);
template struct range<double>;
template struct range<float>;
template struct range<uint8_t>;
template struct range<uint16_t>;

74
range.hpp Normal file
View File

@ -0,0 +1,74 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __UTIL_RANGE_HPP
#define __UTIL_RANGE_HPP
#include "json.hpp"
/**
* Represents a continuous range of values. Contains convenience functions
* and debugging checks.
*/
template <typename T>
struct range {
T min;
T max;
range (const json::node &node);
range (T _min, T _max);
/// Check whether value falls within this range (inclusive)
bool includes (T val) const;
/// Check whether a range falls completely within (inclusive) this range
bool includes (const range <T> &r) const;
/// Return the closest number that falls within the range.
T clamp (T val) const;
/// Normalise a number to [0, 1] within the range. Does not check bounds.
double normalise (T val) const;
/// Return a pseudo-random uniformly distributed value within the range.
/// There are no statistical randomness guarantees whatsoever.
T rand (void) const;
bool operator ==(const range<T>& rhs) const;
bool operator !=(const range<T>& rhs) const
{ return !(*this == rhs); }
/// A range which is guaranteed to contain all elements type T
static const range <T> UNLIMITED;
/// A range which only contains elements between 0 and 1 inclusive
static const range <T> UNIT;
void sanity (void) const;
};
template <typename T>
std::ostream&
operator <<(std::ostream &os, const range<T> &rhs) {
os << '[' << rhs.min << ", " << rhs.max << ']';
return os;
}
#endif

86
region.cpp Normal file
View File

@ -0,0 +1,86 @@
#include "region.hpp"
#include "debug.hpp"
/*
* Rect
*/
template <typename T>
_rect<T>::_rect (const T _width, const T _height):
width (_width),
height (_height)
{ ; }
template <typename T>
T
_rect<T>::area (void) const
{ return width * height; }
template <typename T>
bool
_rect<T>::empty (void) const
{ return area() == 0; }
template <typename T>
bool
_rect<T>::operator ==(const _rect& rhs) const
{ return width == rhs.width && height == rhs.height; }
template <typename T>
void
_rect<T>::sanity (void) const
{ check (width >= 0 && height >= 0); }
// Replace the check, as unsigned types do not ever go negative. This would
// trigger a compile warning if left.
template <>
void
_rect<unsigned int>::sanity (void) const
{ check (true); }
template struct _rect<unsigned int>;
/*
* Region
*/
template <typename T>
_region<T>::_region (T _x, T _y, T _width, T _height):
x (_x),
y (_y),
width (_width),
height (_height)
{ ; }
template <typename T>
T
_region<T>::area (void) const
{ return width * height; }
template <typename T>
bool
_region<T>::empty (void) const
{ return area () == 0; }
template <typename T>
bool
_region<T>::operator ==(const _region& rhs) const
{ return x == rhs.x &&
y == rhs.y &&
width == rhs.width &&
height == rhs.height; }
template <typename T>
void _region<T>::sanity (void) const
{ check (width >= 0 && height >= 0); }

68
region.hpp Normal file
View File

@ -0,0 +1,68 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __UTIL_REGION_HPP
#define __UTIL_REGION_HPP
/**
* A pure two-dimensional size, without positioning
*/
template <typename T>
struct _rect {
T width, height;
_rect (const T _width, const T _height);
T area (void) const;
bool empty (void) const;
bool operator ==(const _rect& rhs) const;
bool operator !=(const _rect& rhs) const
{ return !(*this == rhs); }
void sanity (void) const;
};
typedef _rect<unsigned int> rect;
/**
* A two-dimensional rectangle, with size and position.
*/
template <typename T>
struct _region {
T x, y;
T width, height;
_region (T _x, T _y, T _width, T _height);
T area (void) const;
bool empty (void) const;
bool operator ==(const _region<T>& rhs) const;
bool operator !=(const _region<T>& rhs) const
{ return !(*this == rhs); }
void sanity (void) const;
};
typedef _region<unsigned int> region;
#endif

17
stream.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "stream.hpp"
std::ostream&
nullstream::put (char c)
{ return *this; }
bool
nullstream::good (void) const
{ return !bad () && !eof () && !fail (); }
bool nullstream::bad (void) const { return false; }
bool nullstream::eof (void) const { return false; }
bool nullstream::fail (void) const { return false; }

46
stream.hpp Normal file
View File

@ -0,0 +1,46 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2011 Danny Robson <danny@blubinc.net>
*/
#ifndef __UTIL_STREAM_HPP
#define __UTIL_STREAM_HPP
#include <iostream>
class nullstream : public std::ostream {
public:
std::ostream & put (char c);
std::ostream & write (const char *s, std::streamsize n);
std::streampos tellp (void);
std::ostream & seekp (std::streampos pos);
std::ostream & seekp (std::streamoff off,
std::ios_base::seekdir dir);
std::ostream & flush (void);
bool good (void) const;
bool bad (void) const;
bool eof (void) const;
bool fail (void) const;
};
#endif

7
test/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
/backtrace
/float
/range
/maths
/matrix
/version
/json-check

35
test/Makefile.am Normal file
View File

@ -0,0 +1,35 @@
AM_CPPFLAGS = \
$(COMMON_CXXFLAGS) \
-I$(top_srcdir) \
-I$(top_srcdir)/include \
-I$(top_srcdir)/src/ \
-I$(top_srcdir)/lib/
AM_LDFLAGS = $(COMMON_LDFLAGS)
TEST_BIN = backtrace float range maths matrix version
TESTS = $(TEST_BIN) json.pl
check_PROGRAMS = $(TEST_BIN) json-check
EXTRA_DIST = json.pl
backtrace_SOURCES = backtrace.cpp
backtrace_CPPFLAGS = $(COMMON_CXXFLAGS)
backtrace_LDADD = $(top_builddir)/libutil.la $(BOOST_SYSTEM_LIB)
float_SOURCES = float.cpp
float_LDADD = $(top_builddir)/libutil.la $(BOOST_SYSTEM_LIB)
range_SOURCES = range.cpp
range_LDADD = $(top_builddir)/libutil.la $(BOOST_SYSTEM_LIB)
maths_SOURCES = maths.cpp
maths_LDADD = $(top_builddir)/libutil.la $(BOOST_SYSTEM_LIB)
matrix_SOURCES = matrix.cpp
matrix_LDADD = $(top_builddir)/libutil.la $(BOOST_SYSTEM_LIB)
version_SOURCES = version.cpp
version_LDADD = $(top_builddir)/libutil.la $(BOOST_SYSTEM_LIB)
json_check_SOURCES = json-check.cpp
json_check_LDADD = $(top_builddir)/libutil.la $(BOOST_FILESYSTEM_LIB)

12
test/backtrace.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "../backtrace.hpp"
#include <iostream>
using namespace std;
int
main (int argc, char **argv) {
cout << debug::backtrace() << endl;
return EXIT_SUCCESS;
}

75
test/float.cpp Normal file
View File

@ -0,0 +1,75 @@
#include "../float.hpp"
#include "../debug.hpp"
#include <limits>
using namespace std;
#define elems(x) (sizeof(x) / sizeof(0[x]))
void
test_double (void) {
struct sized_test {
ieee_double::uint_t bits;
double floating;
};
sized_test tests[] = {
{ 0x3ff0000000000000, 1.0 },
{ 0x3ff0000000000001, 1.0 + numeric_limits<double>::epsilon () },
{ 0x3ff0000000000002, 1.0 + numeric_limits<double>::epsilon () * 2},
{ 0x4000000000000000, 2.0 },
{ 0xc000000000000000, -2.0 },
{ 0x0000000000000001, numeric_limits<double>::denorm_min () },
{ 0x0010000000000000, numeric_limits<double>::min () }, // min positive normal
{ 0x7fefffffffffffff, numeric_limits<double>::max () }, // max
{ 0x0000000000000000, 0.0 },
{ 0x8000000000000000, -0.0 },
{ 0x7ff0000000000000, numeric_limits<double>::infinity() },
{ 0xfff0000000000000, -numeric_limits<double>::infinity() },
{ 0x3fd5555555555555, 1.0 / 3.0 }
};
for (unsigned int i = 0; i < elems (tests); ++i) {
ieee_double val;
val.set_bits (tests[i].bits);
check_hard (val == tests[i].floating);
}
}
void
test_single (void) {
struct sized_test {
ieee_single::uint_t bits;
float floating;
};
sized_test tests[] = {
{ 0x3f800000, 1.0f },
{ 0xc0000000, -2.0f },
{ 0x7f7fffff, numeric_limits<float>::max () },
{ 0x00000000, 0.0f },
{ 0x80000000, -0.0f },
{ 0x7f800000, numeric_limits<float>::infinity () },
{ 0xff800000, -numeric_limits<float>::infinity () },
{ 0x3eaaaaab, 1.0f / 3.0f }
};
for (unsigned int i = 0; i < elems (tests); ++i) {
ieee_single val;
val.set_bits (tests[i].bits);
check_hard (val == tests[i].floating);
}
}
int
main (int, char **) {
test_single ();
test_double ();
}

33
test/json-check.cpp Normal file
View File

@ -0,0 +1,33 @@
#include "../json.hpp"
#include <iostream>
#include <cstdlib>
#include <boost/filesystem.hpp>
enum {
ARG_CMD,
ARG_PATH,
NUM_ARGS
};
int
main (int argc, char ** argv) {
if (argc != NUM_ARGS) {
std::cerr << "Invalid arguments. "
<< argv[ARG_CMD] << " <path> "
<< std::endl;
return EXIT_FAILURE;
}
try {
json::parse (boost::filesystem::path (argv[ARG_PATH]));
} catch (json::error &x) {
std::cerr << x.what () << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

31
test/json.pl Executable file
View File

@ -0,0 +1,31 @@
#!/usr/bin/perl
$COMMAND = "./json-check";
@good = <json/good/*>;
@bad = <json/bad/*>;
$success = 1;
sub status_to_str {
$status = shift @_;
return $status ? "failed" : "passed";
}
foreach $testfile(@good) {
$status = system("$COMMAND $testfile &>/dev/null");
$success &&= $status == 0;
printf "%s\t%s\n", status_to_str($status), $testfile;
}
foreach $testfile(@bad) {
$status = system("$COMMAND $testfile &>/dev/null");
$success &&= $status != 0;
printf "%s\t%s\n", status_to_str(!$status), $testfile;
}
exit !$success

View File

@ -0,0 +1 @@
{

View File

@ -0,0 +1 @@
}

View File

@ -0,0 +1,3 @@
{
"foo" : [1, ]
}

View File

@ -0,0 +1,2 @@
{
}

View File

@ -0,0 +1,3 @@
{
"foo" : "bar"
}

View File

@ -0,0 +1,3 @@
{
"zero" : 0
}

View File

@ -0,0 +1,3 @@
{
"neg" : -1
}

View File

@ -0,0 +1,3 @@
{
"pos_low_exp" : 1e-5
}

View File

@ -0,0 +1,3 @@
{
"pos_high_exp" : 1e5
}

View File

@ -0,0 +1,3 @@
{
"neg_low_exp" : -4e-9
}

View File

@ -0,0 +1,3 @@
{
"neg_high_exp" : 12e15
}

View File

@ -0,0 +1,3 @@
{
"frac" : 1.7e-23
}

View File

@ -0,0 +1,3 @@
{
"foo" : true
}

View File

@ -0,0 +1,3 @@
{
"foo" : false
}

View File

@ -0,0 +1,3 @@
{
"foo" : null
}

View File

@ -0,0 +1,3 @@
{
"numbers" : [0, 1, 2, 3]
}

View File

@ -0,0 +1,5 @@
{
"obj" : {
"foo": "bar"
}
}

25
test/maths.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "../debug.hpp"
#include "../maths.hpp"
#include <cstdlib>
#include <cmath>
using std::sqrt;
int
main (int, char **) {
check_hard (!almost_equal (-2.0, 0.0));
check_hard (!almost_equal (-2.f, 0.f));
check_eq (min (-2, 0, 2), -2);
check_eq (max (-2, 0, 2), 2);
check_eq (pow2 (2), 4);
check_eq (pow2 (4), 16);
check_eq (rootsquare (2, 2), sqrt (8));
return EXIT_SUCCESS;
}

132
test/matrix.cpp Normal file
View File

@ -0,0 +1,132 @@
#include "../debug.hpp"
#include "../matrix.hpp"
#include "../maths.hpp"
#include <iostream>
#include <cmath>
using namespace maths;
using namespace std;
std::ostream&
operator <<(std::ostream &os, const matrix &m) {
for (unsigned int i = 0; i < m.rows (); ++i) {
for (unsigned int j = 0; j < m.columns (); ++j) {
os << m[i][j];
if (j != m.columns () - 1)
os << ", ";
}
if (i != m.rows () - 1)
os << "\n";
}
return os;
}
void
test_zeroes (const matrix &m) {
assert (m.rows ());
assert (m.columns ());
for (unsigned int i = 0; i < m.rows (); ++i)
for (unsigned int j = 0; j < m.columns (); ++j)
check_hard (almost_equal (m[i][j], 0.0));
}
void
test_identity (const matrix &m) {
assert (m.rows () == m.columns ());
for (unsigned int i = 0; i < m.rows (); ++i)
for (unsigned int j = 0; j < m.columns (); ++j)
if (i == j)
check_hard (almost_equal (m[i][j], 1.0));
else
check_hard (almost_equal (m[i][j], 0.0));
}
int
main (int, char **) {
for (unsigned int i = 1; i < 10; ++i) {
test_zeroes (matrix::zeroes (i));
test_identity (matrix::identity (i));
}
for (unsigned int i = 3; i < 10; i += 2)
check (matrix::magic (i).is_magic ());
// Create a small matrix with unique element values for comparison tests.
// This should be non-square so that row- vs. column-major problems can
// be seen.
matrix a4x2 (4, 2, { 0, 1,
2, 3,
4, 5,
6, 7 });
check_eq (a4x2, a4x2);
// Test that copy constructors work correctly. Keep this value around so
// that we can check the following operators don't modify the original
// value.
check_eq (a4x2, matrix(a4x2));
// Check multiplication by identity results in the original value.
check_eq (a4x2, a4x2 * matrix::identity (a4x2.columns ()));
matrix seq2x2(2, 2, { 1, 2, 3, 4 });
matrix magic3(3, 3, { 2, 7, 6,
9, 5, 1,
4, 3, 8 });
matrix magic4(4, 4, { 16, 2, 3, 13,
5, 11, 10, 8,
9, 7, 6, 12,
4, 14, 15, 1 });
check_eq (magic3[0][0], 2.0);
check_eq (magic3[0][1], 7.0);
check_eq (magic3[0][2], 6.0);
check_eq (magic3[1][0], 9.0);
check_eq (magic3[1][1], 5.0);
check_eq (magic3[1][2], 1.0);
check_eq (magic3[2][0], 4.0);
check_eq (magic3[2][1], 3.0);
check_eq (magic3[2][2], 8.0);
check_eq (seq2x2.determinant (), -2.0);
check_eq (magic3.determinant (), -360.0);
check_hard ( seq2x2.is_square ());
check_hard ( magic3.is_square ());
check_hard (! a4x2.is_square ());
check_eq (seq2x2.inverse (), matrix (2, 2, { -2.0, 1.0,
1.5, -0.5 }));
check_eq (magic3.inverse (), matrix (3, 3, { -37.0, 38.0, 23.0,
68.0, 8.0, -52.0,
- 7.0, -22.0, 53.0 }) /= 360.0);
matrix invertible4 (4, 4, { 4, 14, 15, 1,
9, 7, 6, 12,
5, 11, 10, 8,
0, 0, 0, 1 });
check_eq (invertible4.inverse (), matrix (4, 4, { 4, 25, -21, -136,
-60, -35, 111, -408,
64, 26, -98, 408,
0, 0, 0, 136 }) /= 136);
const matrix homo3x3 (3, 3, { 1, 2, 0,
3, 4, 0,
0, 0, 1 });
check_hard (homo3x3.is_homogeneous ());
check_hard (!matrix::zeroes (3).is_homogeneous ());
check_hard ( matrix::identity (3).is_homogeneous ());
check_hard (invertible4.is_homogeneous ());
return EXIT_SUCCESS;
}

31
test/range.cpp Normal file
View File

@ -0,0 +1,31 @@
#include <cstdlib>
#include <limits>
#include "../debug.hpp"
#include "../range.hpp"
using namespace std;
int
main (int, char **) {
check_hard ( range<double>::UNIT.includes ( 0.0));
check_hard ( range<double>::UNIT.includes ( 0.5));
check_hard ( range<double>::UNIT.includes ( 1.0));
check_hard (!range<double>::UNIT.includes (-0.00001));
check_hard (!range<double>::UNIT.includes ( 1.00001));
check_hard ( range<uint16_t>::UNIT.includes (0));
check_hard ( range<uint16_t>::UNIT.includes (1));
check_hard (!range<uint16_t>::UNIT.includes (2));
check_hard (!range<uint16_t>::UNIT.includes (numeric_limits <uint16_t>::max ()));
check_hard ( range<double>::UNLIMITED.includes (0.0));
check_hard ( range<double>::UNLIMITED.includes (+numeric_limits<double>::infinity ()));
check_hard ( range<double>::UNLIMITED.includes (-numeric_limits<double>::infinity ()));
check_hard (!range<double>::UNLIMITED.includes ( numeric_limits<double>::quiet_NaN ()));
check_hard ( range<uint16_t>::UNLIMITED.includes (numeric_limits<uint16_t>::min()));
check_hard ( range<uint16_t>::UNLIMITED.includes (numeric_limits<uint16_t>::max()));
return EXIT_SUCCESS;
}

50
test/version.cpp Normal file
View File

@ -0,0 +1,50 @@
#include <string>
#include <vector>
using namespace std;
#include "../version.hpp"
#include "../debug.hpp"
struct parsed_version {
string str;
vector <unsigned int> parts;
};
int
main (int, char **) {
vector <parsed_version> tests ({
{ "1", { 1 } },
{ "1.2", { 1, 2 } },
{ "1.2.3", { 1, 2, 3 } },
{ "1.2.3.4", { 1, 2, 3, 4 } },
{ "9.5a", { 9, 5 } },
{ "8.2.5b", { 8, 2, 5 } },
/*
{ "1.4.1-p8", { 1, 4, 1 } },
{ "4.2.0-r4", { 4, 2, 0 } },
{ "1.4 RC1", { 1, 4 } }
*/
});
for (auto i = tests.begin (); i != tests.end (); ++i) {
version v (i->str);
if (i->parts.size () > 0) check (v.major () == i->parts[0]);
if (i->parts.size () > 1) check (v.minor () == i->parts[1]);
if (i->parts.size () > 2) check (v.point () == i->parts[2]);
if (i->parts.size () > 3) check (v.build () == i->parts[3]);
check_hard (i->parts.size () <= 4);
}
return EXIT_SUCCESS;
}

12
types.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "types.hpp"
using namespace std;
#define do_type_to_string(T) \
template <> std::string type_to_string <T> (void) { return #T; } \
template <> std::string type_to_string <const T> (void) { return "const " #T; }
do_type_to_string (float)
do_type_to_string (double)

56
types.hpp Normal file
View File

@ -0,0 +1,56 @@
#ifndef __TYPES_HPP
#define __TYPES_HPP
#include <cstdint>
#include <string>
template <int BITS>
struct bits_type;
template <> struct bits_type< 8> {
static const bool has_floating = false;
typedef uint8_t uint;
typedef int8_t sint;
typedef uint8_t floating;
};
template <> struct bits_type<16> {
static const bool has_floating = false;
typedef uint16_t uint;
typedef int16_t sint;
typedef uint16_t floating;
};
template <> struct bits_type<32> {
static const bool has_floating = true;
typedef uint32_t uint;
typedef int32_t sint;
typedef float floating;
};
template <> struct bits_type<64> {
static const bool has_floating = true;
typedef uint64_t uint;
typedef int64_t sint;
typedef double floating;
};
template <typename T>
struct sized_type : public bits_type<sizeof(T) * 8>
{ };
template <typename T>
std::string type_to_string (void);
#endif // __TYPES_HPP

80
vector.cpp Normal file
View File

@ -0,0 +1,80 @@
#include "vector.hpp"
#include "debug.hpp"
#include <numeric>
using namespace maths;
/* Constructors */
vector::vector (const std::initializer_list<double> &_data):
m_data (_data)
{ ; }
vector::vector (unsigned int _size):
m_data (_size)
{ ; }
vector::vector (const double *restrict _data,
unsigned int _size):
m_data (_size)
{ std::copy (_data, _data + _size, m_data.begin ()); }
vector::vector (const vector &rhs):
m_data (rhs.m_data)
{ ; }
vector::vector (const vector &&rhs):
m_data (std::move (rhs.m_data))
{ ; }
vector::~vector (void)
{ ; }
/* element accessors */
const double*
vector::data (void) const
{ return &m_data[0]; }
double &
vector::operator[] (unsigned int offset)
{ return m_data[offset]; }
const double&
vector::operator[] (unsigned int offset) const
{ return m_data[offset]; }
unsigned int
vector::size (void) const
{ return m_data.size (); }
/* dot and cross products */
double vector::dot (const double *restrict A,
const double *restrict B,
unsigned int size)
{ return std::inner_product(A, A + size, B, 0.0); }
vector vector::cross (const double *restrict A,
const double *restrict B,
unsigned int size) {
check_hard (size == 3);
return vector ({ A[1] * B[2] - A[2] * B[1],
A[2] * B[0] - A[0] * B[2],
A[0] * B[1] - A[1] * B[0] });
}

61
vector.hpp Normal file
View File

@ -0,0 +1,61 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2011 Danny Robson <danny@blubinc.net>
*/
#ifndef __UTIL_VECTOR_HPP
#define __UTIL_VECTOR_HPP
#include <vector>
#include <initializer_list>
namespace maths {
class vector {
protected:
std::vector<double> m_data;
public:
vector (const std::initializer_list<double> &_data);
explicit
vector (unsigned int _size);
vector (const double *restrict _data,
unsigned int _size);
vector (const vector &rhs);
vector (const vector &&rhs);
~vector (void);
const double* data (void) const;
double& operator[] (unsigned int);
const double& operator[] (unsigned int) const;
unsigned int size (void) const;
static double dot (const double *restrict A,
const double *restrict B,
unsigned int size);
static vector cross (const double *restrict A,
const double *restrict B,
unsigned int size);
};
}
#endif

134
version.cpp.rl Normal file
View File

@ -0,0 +1,134 @@
#include "version.hpp"
#include <stdexcept>
#include "debug.hpp"
using namespace std;
version::version (unsigned int _major,
unsigned int _minor):
m_values (2),
m_release (RELEASE_PRODUCTION) {
m_values[OFFSET_MAJOR] = _major;
m_values[OFFSET_MINOR] = _minor;
}
version::version (const string& str):
m_values (NUM_OFFSETS, 0),
m_release (RELEASE_PRODUCTION) {
m_values.clear ();
parse (str);
}
static void
check_release (version::release_t r) {
switch (r) {
case version::RELEASE_ALPHA:
case version::RELEASE_BETA:
case version::RELEASE_GAMMA:
case version::RELEASE_PRODUCTION:
return;
}
panic ("invalid release_t value");
}
void
version::sanity (void) const {
check_release (m_release);
check (!m_values.empty ());
}
%%{
machine version;
action clear
{ current = 0; }
action increment
{ current *= 10;
current += (uintptr_t)(fc - (unsigned char)'0'); }
action finish
{ m_values.push_back (current); }
number = (digit+)
>clear
$increment
%finish;
dots = (number '.')* number;
type = ('beta'i | 'b'i) %{ m_release = RELEASE_BETA; }
| ('alpha'i | 'a'i) %{ m_release = RELEASE_ALPHA; }
| ('gamma'i | 'g'i) %{ m_release = RELEASE_GAMMA; };
version := (dots type?)
$!{ throw invalid_argument (str); };
write data;
}%%
void
version::parse (const string& str) {
unsigned int current;
int cs;
const char *p = str.data (),
*pe = str.data () + str.size (),
*eof = pe;
%%write init;
%%write exec;
}
static string
release_string (const version::release_t r) {
switch (r) {
case (version::RELEASE_ALPHA): return "a";
case (version::RELEASE_BETA): return "b";
case (version::RELEASE_GAMMA): return "g";
case (version::RELEASE_PRODUCTION): return "";
}
panic ("invalid release_t");
}
bool
version::operator > (const version &rhs) const {
unsigned int count = min (m_values.size (), rhs.m_values.size ());
for (unsigned int i = 0; i < count; ++i)
if (m_values[i] < rhs.m_values[i])
return false;
if (m_values.size () < rhs.m_values.size())
return false;
if (m_release <= rhs.m_release)
return false;
return true;
}
ostream&
operator <<(ostream& os, const version& rhs) {
auto i = rhs.m_values.begin();
os << *i; ++i;
for (; i != rhs.m_values.end(); ++i)
os << '.' << *i;
os << release_string (rhs.m_release);
return os;
}

86
version.hpp Normal file
View File

@ -0,0 +1,86 @@
/*
* This file is part of waif.
*
* Waif 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.
*
* Waif 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 waif. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010 Danny Robson <danny@blubinc.net>
*/
#ifndef __VERSION_HPP
#define __VERSION_HPP
#include <string>
#include <vector>
#include <iostream>
class version {
public:
enum release_t {
RELEASE_ALPHA,
RELEASE_BETA,
RELEASE_GAMMA,
RELEASE_PRODUCTION
};
version (unsigned int _major,
unsigned int _minor);
version (const std::string& str);
virtual ~version () { ; }
virtual void sanity (void) const;
protected:
enum {
OFFSET_MAJOR = 0,
OFFSET_MINOR = 1,
OFFSET_POINT = 2,
OFFSET_BUILD = 3,
NUM_OFFSETS
};
std::vector <unsigned int> m_values;
release_t m_release;
void parse (const std::string&);
public:
unsigned int major (void) const
{ return m_values[OFFSET_MAJOR]; }
unsigned int minor (void) const
{ return m_values[OFFSET_MINOR]; }
unsigned int point (void) const
{ return m_values[OFFSET_POINT]; }
unsigned int build (void) const
{ return m_values[OFFSET_BUILD]; }
bool operator < (const version& rhs) const;
bool operator > (const version& rhs) const;
bool operator >= (const version& rhs) const;
bool operator <= (const version& rhs) const;
bool operator == (const version& rhs) const
{ return m_values == rhs.m_values &&
m_release == rhs.m_release; }
friend std::ostream&
operator <<(std::ostream& os, const version& rhs);
};
std::ostream&
operator <<(std::ostream& os, const version& rhs);
#endif // __VERSION_HPP