Browse Source

math: start working on a bigint type, which will be the fundation

for the next implementation of large float numbers.
undefined
Sam Hocevar 10 years ago
parent
commit
ec00776d61
9 changed files with 221 additions and 145 deletions
  1. +9
    -143
      demos/test/sandbox/sample.cpp
  2. +1
    -1
      src/Makefile.am
  3. +1
    -0
      src/lol/math/all.h
  4. +143
    -0
      src/lol/math/bigint.h
  5. +1
    -0
      src/lolcore.vcxproj
  6. +3
    -0
      src/lolcore.vcxproj.filters
  7. +2
    -1
      src/t/Makefile.am
  8. +60
    -0
      src/t/math/bigint.cpp
  9. +1
    -0
      src/t/test-math.vcxproj

+ 9
- 143
demos/test/sandbox/sample.cpp View File

@@ -1,7 +1,7 @@
//
// Lol Engine - Sandbox program
//
// Copyright © 2005-2015 Sam Hocevar <sam@hocevar.net>
// Copyright © 20052015 Sam Hocevar <sam@hocevar.net>
//
// This program is free software. It comes without any warranty, to
// the extent permitted by applicable law. You can redistribute it
@@ -16,151 +16,17 @@

#include <lol/engine.h>

#include "pegtl.hh"
namespace lol { namespace parser = pegtl; }

#include "axe.h"
namespace lol { using namespace axe; }

using namespace lol;

//
// Parser tools for a simple calculator grammar with + - * /
//

struct calculator
{
void push(double d) { stack.Push(d); }
void pop() { printf("%f\n", stack.Pop()); }

void mul() { auto x = stack.Pop(); stack.Push(stack.Pop() * x); }
void div() { auto x = stack.Pop(); stack.Push(stack.Pop() / x); }
void add() { auto x = stack.Pop(); stack.Push(stack.Pop() + x); }
void sub() { auto x = stack.Pop(); stack.Push(stack.Pop() - x); }

array<double> stack;
};

static void parse_pegtl(std::string const &str);
static void parse_axe(std::string const &str);

int main()
{
std::string const str("42+2*(1-1+2+3-4*5)");

parse_axe(str);
parse_pegtl(str);

return EXIT_SUCCESS;
}

//
// PegTL -- a PEG parser
//

namespace pegtl_parser
{
using namespace lol::parser;

#define ACTION(name, code) \
struct name : action_base<name> { \
static void apply(std::string const &ctx, calculator &c) { \
code \
} \
};

ACTION( do_number, c.push(atof(ctx.c_str())); )
ACTION( do_op,
switch (ctx[0])
{
case '*': c.mul(); break;
case '/': c.div(); break;
case '+': c.add(); break;
case '-': c.sub(); break;
} )
ACTION( do_success, c.pop(); )

#undef ACTION

// number <- <digit> +
struct number : ifapply<plus<digit>, do_number> {};

// term <- number | "(" expr ")"
struct term : sor<number,
seq<one<'('>, struct expr, one<')'>>> {};

// factor <- term ( "*" term | "/" term ) *
struct factor : seq<term,
star<ifapply<sor<seq<one<'*'>, term>,
seq<one<'/'>, term>>, do_op>>> {};

// expr <- factor ( "+" factor | "-" factor ) *
struct expr : seq<factor,
star<ifapply<sor<seq<one<'+'>, factor>,
seq<one<'-'>, factor>>, do_op>>> {};

// stmt <- expr <end>
struct stmt : ifapply<seq<expr, eof>, do_success> {};
};

static void parse_pegtl(std::string const & str)
{
calculator c;
pegtl::basic_parse_string<pegtl_parser::stmt>(str, c);
}

//
// AXE -- a recursive descent parser (needs right-recursion)
//

template<typename IT>
static void parse_axe_helper(IT i1, IT i2)
{
calculator c;
double d;

#define ACTION(code) e_ref([&](...) { code })

r_rule<IT> number, term, factor, factor_tail, expr, expr_tail, stmt;

// number ::= <double>
number = r_double(d) >> ACTION( c.push(d); );

// term ::= number | ( expr )
term = number
| '(' & expr & ')';

// factor ::= term factor_tail
// factor_tail ::= * term factor_tail
// | / term factor_tail
// | ɛ
factor_tail = '*' & term >> ACTION( c.mul(); ) & factor_tail
| '/' & term >> ACTION( c.div(); ) & factor_tail
| r_empty();
factor = term & factor_tail;

// expr ::= factor expr_tail
// expr_tail ::= + factor expr_tail
// | - factor expr_tail
// | ɛ
expr_tail = '+' & factor >> ACTION( c.add(); ) & expr_tail
| '-' & factor >> ACTION( c.sub(); ) & expr_tail
| r_empty();
expr = factor & expr_tail;

// stmt ::= expr <end>
// | <fail>
stmt = expr & r_end() >> ACTION( c.pop(); )
| r_fail([](...) { printf("malformed expression\n"); });

#undef ACTION

// Evaluate expression
stmt(i1, i2);
}

static void parse_axe(std::string const &str)
{
return parse_axe_helper(str.begin(), str.end());
bigint<16> i;
bigint<16> x(2), y(12);
auto z = x + y;
printf("%d\n", (int)z);

bigint<0> lol;
auto w = z + lol;
printf("%d\n", (int)w);
}


+ 1
- 1
src/Makefile.am View File

@@ -42,7 +42,7 @@ liblolcore_headers = \
lol/math/functions.h lol/math/vector.h lol/math/half.h lol/math/real.h \
lol/math/geometry.h lol/math/interp.h lol/math/rand.h lol/math/array2d.h \
lol/math/array3d.h lol/math/constants.h lol/math/matrix.h lol/math/ops.h \
lol/math/transform.h lol/math/polynomial.h \
lol/math/transform.h lol/math/polynomial.h lol/math/bigint.h \
lol/math/noise/gradient.h lol/math/noise/perlin.h \
lol/math/noise/simplex.h \
\


+ 1
- 0
src/lol/math/all.h View File

@@ -13,6 +13,7 @@
#include <lol/math/constants.h>
#include <lol/math/functions.h>
#include <lol/math/half.h>
#include <lol/math/bigint.h>
#include <lol/math/real.h>
#include <lol/math/ops.h>
#include <lol/math/vector.h>


+ 143
- 0
src/lol/math/bigint.h View File

@@ -0,0 +1,143 @@
//
// Lol Engine
//
// Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
//
// This library is free software. It comes without any warranty, to
// the extent permitted by applicable law. You can redistribute it
// and/or modify it under the terms of the Do What the Fuck You Want
// to Public License, Version 2, as published by the WTFPL Task Force.
// See http://www.wtfpl.net/ for more details.
//

#pragma once

//
// The bigint class
// ----------------
//

#include <lol/base/types.h>

#include <stdint.h>

namespace lol
{

/* This is OUR namespace. Don't let Windows headers fuck with it. */
#undef min
#undef max

/* Avoid issues with NaCl headers */
#undef log2

/*
* A bigint stores its digits in an array of integers. The MSB of the
* integers are unused. The highest used bit is the sign bit.
*
* Digits are stored in little endian mode.
*/

template<unsigned int N = 16, typename T = uint32_t>
class bigint
{
int const bits_per_digit = sizeof(T) * 8 - 1;
T const digit_mask = ~((T)1 << bits_per_digit);

public:
inline bigint()
{
}

explicit bigint(int32_t x)
{
/* FIXME: doesn’t work if sizeof(T) < sizeof(int32_t) */
for (auto &digit : m_digits)
digit = x >= 0 ? (T)0 : digit_mask;
if (N > 0)
m_digits[0] = x & digit_mask;
}

explicit bigint(uint32_t x)
{
/* FIXME: doesn’t work if sizeof(T) > sizeof(uint32_t) */
for (auto &digit : m_digits)
digit = (T)0;
if (N > 0)
m_digits[0] = x & digit_mask;
if (N > 1)
m_digits[1] = x >> bits_per_digit;
}

explicit inline operator uint32_t() const
{
/* FIXME: doesn’t work if sizeof(T) != sizeof(int32_t) */
if (N < 1)
return 0;
if (N < 2)
return (uint32_t)(m_digits[0]
| ((m_digits[0] << 1) & ~digit_mask));

return (uint32_t)(m_digits[0] | (m_digits[1] << bits_per_digit));
}

inline operator int32_t() const
{
return (int32_t)(uint32_t)*this;
}

/*
* bigint cast to bigint of different size: we copy all digits then
* pad the rest (if applicable) with zeroes or ones to extend the
* sign bit.
*/
template<int M>
explicit bigint(bigint<M,T> const &x)
{
for (int i = 0; i < (N < M) ? N : M; ++i)
m_digits[i] = x.m_digits[i];

if (N > M)
{
T padding = x.is_negative() ? digit_mask : (T)0;
for (int i = M; i < N; ++i)
m_digits[i] = padding;
}
}

/*
* bigint addition: we add the digits one-to-one, carrying overflows,
* and replace digits with padding if one of the two operands is
* shorter.
*/
template<int M>
bigint<(N > M) ? N : M, T> operator +(bigint<M,T> const &x) const
{
bigint<(N > M) ? N : M, T> ret;
T padding = is_negative() ? digit_mask : (T)0;
T x_padding = x.is_negative() ? digit_mask : (T)0;
T carry(0);
for (int i = 0; i < (N > M) ? N : M; ++i)
{
T digit = (i < N ? m_digits[i] : padding)
+ (i < M ? x.m_digits[i] : x_padding)
+ carry;
ret.m_digits[i] = digit & digit_mask;
carry = (digit & ~digit_mask) ? T(1) : T(0);
}
return ret;
}

private:
inline bool is_negative() const
{
if (N < 1)
return false;
return (m_digits[N - 1] & ((T)1 << (bits_per_digit - 1))) != 0;
}

T m_digits[N];
};

} /* namespace lol */


+ 1
- 0
src/lolcore.vcxproj View File

@@ -287,6 +287,7 @@
<ClInclude Include="lol\math\all.h" />
<ClInclude Include="lol\math\array2d.h" />
<ClInclude Include="lol\math\array3d.h" />
<ClInclude Include="lol\math\bigint.h" />
<ClInclude Include="lol\math\constants.h" />
<ClInclude Include="lol\math\functions.h" />
<ClInclude Include="lol\math\geometry.h" />


+ 3
- 0
src/lolcore.vcxproj.filters View File

@@ -417,6 +417,9 @@
<ClInclude Include="lol\math\all.h">
<Filter>lol\math</Filter>
</ClInclude>
<ClInclude Include="lol\math\bigint.h">
<Filter>lol\math</Filter>
</ClInclude>
<ClInclude Include="lol\math\matrix.h">
<Filter>lol\math</Filter>
</ClInclude>


+ 2
- 1
src/t/Makefile.am View File

@@ -20,7 +20,8 @@ test_math_SOURCES = test-common.cpp \
math/array2d.cpp math/array3d.cpp math/arraynd.cpp math/box.cpp \
math/cmplx.cpp math/half.cpp math/interp.cpp math/matrix.cpp \
math/quat.cpp math/rand.cpp math/real.cpp math/rotation.cpp \
math/trig.cpp math/vector.cpp math/polynomial.cpp math/noise/simplex.cpp
math/trig.cpp math/vector.cpp math/polynomial.cpp math/noise/simplex.cpp \
math/bigint.cpp
test_math_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/tools/lolunit
test_math_DEPENDENCIES = @LOL_DEPS@



+ 60
- 0
src/t/math/bigint.cpp View File

@@ -0,0 +1,60 @@
//
// Lol Engine
//
// Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
//
// This program is free software. It comes without any warranty, to
// the extent permitted by applicable law. You can redistribute it
// and/or modify it under the terms of the Do What the Fuck You Want
// to Public License, Version 2, as published by the WTFPL Task Force.
// See http://www.wtfpl.net/ for more details.
//

#include <lol/engine-internal.h>

#include <cmath>

#include <lolunit.h>

namespace lol
{

lolunit_declare_fixture(bigint_test)
{
lolunit_declare_test(declaration)
{
bigint<> a;
bigint<0> b;
bigint<32> c;
}

lolunit_declare_test(int32_cast)
{
bigint<> a(0), b(1), c(-1);

lolunit_assert_equal((int32_t)a, 0);
lolunit_assert_equal((int32_t)b, 1);
lolunit_assert_equal((int32_t)c, -1);
}

lolunit_declare_test(uint32_cast)
{
bigint<> a(0), b(1), c(~(uint32_t)0);

lolunit_assert_equal((uint32_t)a, 0);
lolunit_assert_equal((uint32_t)b, 1);
lolunit_assert_equal((uint32_t)c, ~(uint32_t)0);
}

lolunit_declare_test(empty_bigint_is_zero)
{
bigint<0> a, b(1), c(-1);

lolunit_assert_equal((int)a, 0);
lolunit_assert_equal((int)b, 0);
lolunit_assert_equal((int)c, 0);
}
};

} /* namespace lol */


+ 1
- 0
src/t/test-math.vcxproj View File

@@ -40,6 +40,7 @@
<ClCompile Include="math\array3d.cpp" />
<ClCompile Include="math\arraynd.cpp" />
<ClCompile Include="math\box.cpp" />
<ClCompile Include="math\bigint.cpp" />
<ClCompile Include="math\cmplx.cpp" />
<ClCompile Include="math\half.cpp" />
<ClCompile Include="math\interp.cpp" />


Loading…
Cancel
Save