initial import from waif
This commit is contained in:
commit
745e06d1af
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal 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
1510
Doxyfile.in
Normal file
File diff suppressed because it is too large
Load Diff
53
Makefile.am
Normal file
53
Makefile.am
Normal 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
37
annotations.hpp
Normal 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
36
backtrace.cpp
Normal 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
22
backtrace.hpp
Normal 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
96
configure.ac
Normal 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
68
debug.cpp
Normal 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
132
debug.hpp
Normal 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
37
except.cpp
Normal 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
51
except.hpp
Normal 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
85
float.cpp
Normal 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
66
float.hpp
Normal 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
87
io.cpp
Normal 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
82
io.hpp
Normal 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
448
json.cpp.rl
Normal 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
209
json.hpp
Normal 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
1
m4/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.m4
|
64
m4/as_compiler_flag.m4
Normal file
64
m4/as_compiler_flag.m4
Normal 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
43
m4/ragel.m4
Normal 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
56
maths.cpp
Normal 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
92
maths.hpp
Normal 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
513
matrix.cpp
Normal 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
119
matrix.hpp
Normal 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
34
nocopy.hpp
Normal 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
23
noncopyable.hpp
Normal 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
120
range.cpp
Normal 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
74
range.hpp
Normal 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
86
region.cpp
Normal 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
68
region.hpp
Normal 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
17
stream.cpp
Normal 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
46
stream.hpp
Normal 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
7
test/.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/backtrace
|
||||||
|
/float
|
||||||
|
/range
|
||||||
|
/maths
|
||||||
|
/matrix
|
||||||
|
/version
|
||||||
|
/json-check
|
35
test/Makefile.am
Normal file
35
test/Makefile.am
Normal 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
12
test/backtrace.cpp
Normal 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
75
test/float.cpp
Normal 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
33
test/json-check.cpp
Normal 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
31
test/json.pl
Executable 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
|
1
test/json/bad/0000_object.json
Normal file
1
test/json/bad/0000_object.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{
|
1
test/json/bad/0001_object.json
Normal file
1
test/json/bad/0001_object.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
}
|
3
test/json/bad/2000_trailing_comma.json
Normal file
3
test/json/bad/2000_trailing_comma.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"foo" : [1, ]
|
||||||
|
}
|
2
test/json/good/0000_empty.json
Normal file
2
test/json/good/0000_empty.json
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
{
|
||||||
|
}
|
3
test/json/good/1000_pair_str.json
Normal file
3
test/json/good/1000_pair_str.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"foo" : "bar"
|
||||||
|
}
|
3
test/json/good/1001_pair_number.json
Normal file
3
test/json/good/1001_pair_number.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"zero" : 0
|
||||||
|
}
|
3
test/json/good/1002_pair_number.json
Normal file
3
test/json/good/1002_pair_number.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"neg" : -1
|
||||||
|
}
|
3
test/json/good/1003_pair_number.json
Normal file
3
test/json/good/1003_pair_number.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"pos_low_exp" : 1e-5
|
||||||
|
}
|
3
test/json/good/1004_pair_number.json
Normal file
3
test/json/good/1004_pair_number.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"pos_high_exp" : 1e5
|
||||||
|
}
|
3
test/json/good/1005_pair_number.json
Normal file
3
test/json/good/1005_pair_number.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"neg_low_exp" : -4e-9
|
||||||
|
}
|
3
test/json/good/1006_pair_number.json
Normal file
3
test/json/good/1006_pair_number.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"neg_high_exp" : 12e15
|
||||||
|
}
|
3
test/json/good/1007_pair_number.json
Normal file
3
test/json/good/1007_pair_number.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"frac" : 1.7e-23
|
||||||
|
}
|
3
test/json/good/1008_bool_true.json
Normal file
3
test/json/good/1008_bool_true.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"foo" : true
|
||||||
|
}
|
3
test/json/good/1009_bool_false.json
Normal file
3
test/json/good/1009_bool_false.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"foo" : false
|
||||||
|
}
|
3
test/json/good/1010_null.json
Normal file
3
test/json/good/1010_null.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"foo" : null
|
||||||
|
}
|
3
test/json/good/2000_array.json
Normal file
3
test/json/good/2000_array.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"numbers" : [0, 1, 2, 3]
|
||||||
|
}
|
5
test/json/good/3000_object.json
Normal file
5
test/json/good/3000_object.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"obj" : {
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
||||||
|
}
|
25
test/maths.cpp
Normal file
25
test/maths.cpp
Normal 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
132
test/matrix.cpp
Normal 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
31
test/range.cpp
Normal 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
50
test/version.cpp
Normal 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
12
types.cpp
Normal 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
56
types.hpp
Normal 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
80
vector.cpp
Normal 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
61
vector.hpp
Normal 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
134
version.cpp.rl
Normal 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
86
version.hpp
Normal 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
|
Loading…
Reference in New Issue
Block a user