diff --git a/build.vc11/mpir.net/mpir.net-tests/mpir.net-tests.csproj b/build.vc11/mpir.net/mpir.net-tests/mpir.net-tests.csproj
index 4a169b77..3c265ec4 100644
--- a/build.vc11/mpir.net/mpir.net-tests/mpir.net-tests.csproj
+++ b/build.vc11/mpir.net/mpir.net-tests/mpir.net-tests.csproj
@@ -118,6 +118,9 @@
HugeFloatTests\Math.cs
+
+ HugeFloatTests\Precision.cs
+
HugeIntTests\Arithmetic.cs
diff --git a/mpir.net/mpir.net-tests/HugeFloatTests/Precision.cs b/mpir.net/mpir.net-tests/HugeFloatTests/Precision.cs
new file mode 100644
index 00000000..94cbcf70
--- /dev/null
+++ b/mpir.net/mpir.net-tests/HugeFloatTests/Precision.cs
@@ -0,0 +1,244 @@
+/*
+Copyright 2014 Alex Dyachenko
+
+This file is part of the MPIR Library.
+
+The MPIR Library is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published
+by the Free Software Foundation; either version 3 of the License, or (at
+your option) any later version.
+
+The MPIR Library 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 Lesser General Public
+License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with the MPIR Library. If not, see http://www.gnu.org/licenses/.
+*/
+
+using System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace MPIR.Tests.HugeFloatTests
+{
+ ///
+ /// tests in this class verify that the correct precision is used when calculating floating point numbers
+ ///
+ [TestClass]
+ public class Precision
+ {
+ [ClassInitialize]
+ public static void Setup(TestContext context)
+ {
+ HugeFloat.DefaultPrecision = 128;
+ }
+
+ [ClassCleanup]
+ public static void Cleanup()
+ {
+ HugeFloat.DefaultPrecision = 64;
+ }
+
+ #region Expression arithmetic
+
+ [TestMethod]
+ public void ExpressionsCalculatedToDestinationPrecision()
+ {
+ using (var a = new HugeFloat(1))
+ using (var b = new HugeFloat(3))
+ using (var c = new HugeFloat(5))
+ using (var d = new HugeFloat(a / b + c))
+ {
+ Assert.AreEqual("0.5333333333333333333333333333333333333332@1", d.ToString());
+ d.Reallocate(256);
+ d.Value = a / b + c;
+ Assert.AreEqual("0.533333333333333333333333333333333333333333333333333333333333333333333333333333@1", d.ToString());
+ }
+ }
+
+ [TestMethod]
+ public void ExpressionsCalculatedToSpecificPrecisionForEquals()
+ {
+ using (var a = new HugeFloat(1))
+ using (var b = new HugeFloat(3))
+ using (var c = new HugeFloat("12345234589234059823475029384572323452034958723049823408955"))
+ using (var d = HugeFloat.Allocate(256))
+ {
+ Assert.IsTrue(c.Equals(c + a / b, 128));
+ Assert.IsFalse(c.Equals(c + a / b, 256));
+
+ d.SetTo("12345234589234059823475029384572323452034958723049823408955.333333333333333333333333333333333");
+ Assert.IsTrue(d.Equals(c + a / b, 256));
+ Assert.IsTrue(d.Equals(c + a / b, 128));
+ }
+ }
+
+ [TestMethod]
+ public void ExpressionHashCodeCalculatedToDefaultPrecision()
+ {
+ using (var a = new HugeFloat(1))
+ using (var b = new HugeFloat(13))
+ using (var c = new HugeFloat("12345234589234059823475029384572323"))
+ {
+ c.Value *= 0x4000000000000000L;
+ var cHash = c.GetHashCode();
+ var expr = a / b + c;
+ Assert.AreEqual(cHash, expr.GetHashCode());
+ HugeFloat.DefaultPrecision = 256;
+ Assert.AreEqual(cHash, c.GetHashCode());
+ Assert.AreNotEqual(cHash, expr.GetHashCode());
+ HugeFloat.DefaultPrecision = 128;
+ }
+ }
+
+ [TestMethod]
+ public void CompareToCalculatedToDefaultPrecision()
+ {
+ using (var a = new HugeFloat(1))
+ using (var b = new HugeFloat(13))
+ using (var c = new HugeFloat("12345234589234059823475029384572323"))
+ using (var d = HugeFloat.Allocate(256))
+ {
+ c.Value *= 0x4000000000000000L;
+ d.Value = c;
+ var expr = a / b + c;
+ Assert.AreEqual(0, c.CompareTo(expr)); //to precision of c
+ Assert.AreEqual(0, expr.CompareTo(c)); //to precision of c
+ Assert.IsFalse(expr > c); //to precision of c
+ Assert.IsTrue(c == expr); //to precision of c
+ Assert.AreEqual(0, (c + 0).CompareTo(expr)); //to default precision
+ Assert.AreEqual(0, expr.CompareTo(c + 0)); //to default precision
+ Assert.IsFalse(expr > c + 0); //to default precision
+ Assert.IsTrue(c + 0 == expr); //to default precision
+ HugeFloat.DefaultPrecision = 256;
+ Assert.AreEqual(0, c.CompareTo(expr)); //to precision of c
+ Assert.AreEqual(0, expr.CompareTo(c)); //to precision of c
+ Assert.IsTrue(c == expr); //to precision of c
+ Assert.IsFalse(expr > c); //to precision of c
+ Assert.AreEqual(-1, d.CompareTo(expr)); //to precision of d
+ Assert.AreEqual(1, expr.CompareTo(d)); //to precision of d
+ Assert.IsFalse(d == expr); //to precision of d
+ Assert.IsTrue(expr > d); //to precision of d
+ Assert.AreEqual(-1, (c * 1).CompareTo(expr)); //to default precision
+ Assert.AreEqual(1, expr.CompareTo(c + 0)); //to default precision
+ Assert.IsFalse(c + 0 == expr); //to default precision
+ Assert.IsTrue(expr > c + 0); //to default precision
+ HugeFloat.DefaultPrecision = 128;
+ }
+ }
+
+ [TestMethod]
+ public void CompareToPrimitiveCalculatedToDefaultPrecision()
+ {
+ using (var a = new HugeFloat(1))
+ using (var b = new HugeFloat(13))
+ using (var c = new HugeFloat("12345234589234059823475029384572323"))
+ using (var d = HugeFloat.Allocate(256))
+ {
+ c.Value *= 0x4000000000000000L;
+ d.Value = c;
+ var expr = a / b + c - c;
+ Assert.AreEqual(0, Math.Sign(expr.CompareTo(0L)));
+ Assert.AreEqual(0, Math.Sign(expr.CompareTo(0UL)));
+ Assert.AreEqual(0, Math.Sign(expr.CompareTo(0.0)));
+ HugeFloat.DefaultPrecision = 256;
+ Assert.AreEqual(1, Math.Sign(expr.CompareTo(0L)));
+ Assert.AreEqual(1, Math.Sign(expr.CompareTo(0UL)));
+ Assert.AreEqual(1, Math.Sign(expr.CompareTo(0.0)));
+ HugeFloat.DefaultPrecision = 128;
+ }
+ }
+
+ [TestMethod]
+ public void EqualsToPrimitiveCalculatedToDefaultPrecision()
+ {
+ using (var a = new HugeFloat(1))
+ using (var b = new HugeFloat(13))
+ using (var c = new HugeFloat("12345234589234059823475029384572323"))
+ using (var d = HugeFloat.Allocate(256))
+ {
+ c.Value *= 0x4000000000000000L;
+ d.Value = c;
+ var expr = a / b + c - c;
+ Assert.IsTrue(expr.Equals(0L));
+ Assert.IsTrue(expr.Equals(0UL));
+ Assert.IsTrue(expr.Equals(0.0));
+ HugeFloat.DefaultPrecision = 256;
+ Assert.IsFalse(expr.Equals(0L));
+ Assert.IsFalse(expr.Equals(0UL));
+ Assert.IsFalse(expr.Equals(0.0));
+ HugeFloat.DefaultPrecision = 128;
+ }
+ }
+
+ [TestMethod]
+ public void SignCalculatedToDefaultPrecision()
+ {
+ using (var a = new HugeFloat(1))
+ using (var b = new HugeFloat(13))
+ using (var c = new HugeFloat("12345234589234059823475029384572323"))
+ using (var d = HugeFloat.Allocate(256))
+ {
+ c.Value *= 0x4000000000000000L;
+ var expr = (a / b + c) - c;
+ d.Value = expr;
+ Assert.AreEqual(0, expr.Sign());
+ Assert.AreEqual(1, d.Sign());
+ HugeFloat.DefaultPrecision = 256;
+ Assert.AreEqual(1, expr.Sign());
+ Assert.AreEqual(1, d.Sign());
+ d.Precision = 128;
+ Assert.AreEqual(1, d.Sign());
+ d.Value = expr;
+ Assert.AreEqual(0, d.Sign());
+ HugeFloat.DefaultPrecision = 128;
+ }
+ }
+
+ [TestMethod]
+ public void HugeIntSetToPerformedToDefaultPrecision()
+ {
+ using (var a = new HugeFloat(14))
+ using (var b = new HugeFloat(13))
+ using (var c = new HugeFloat("1234523458923405982347445029384572323"))
+ using (var d = new HugeInt())
+ {
+ c.Value *= 0x4000000000000000L;
+ c.Value *= 0x4000000000000000L;
+ var expr = a / b + c - c;
+ d.SetTo(expr);
+ Assert.IsTrue(d == 0);
+
+ HugeFloat.DefaultPrecision = 256;
+ d.SetTo(expr);
+ Assert.IsTrue(d == 1);
+ HugeFloat.DefaultPrecision = 128;
+ }
+ }
+
+ [TestMethod]
+ public void HugeRationalSetToPerformedToDefaultPrecision()
+ {
+ using (var a = new HugeFloat(14))
+ using (var b = new HugeFloat(13))
+ using (var c = new HugeFloat("1234523458923405982347445029384572323"))
+ using (var d = new HugeRational())
+ {
+ c.Value *= 0x4000000000000000L;
+ c.Value *= 0x4000000000000000L;
+ var expr = a / b + c - c;
+ d.SetTo(expr);
+ Assert.IsTrue(d == 0);
+
+ HugeFloat.DefaultPrecision = 256;
+ d.SetTo(expr);
+ Assert.IsTrue(d > 1);
+ Assert.IsTrue(d < 2);
+ HugeFloat.DefaultPrecision = 128;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/mpir.net/mpir.net/Common.h b/mpir.net/mpir.net/Common.h
index eba5ca91..535a5138 100644
--- a/mpir.net/mpir.net/Common.h
+++ b/mpir.net/mpir.net/Common.h
@@ -136,6 +136,7 @@ struct EvaluationContext
};
__int64 Zero;
};
+ mp_bitcnt_t FloatPrecision;
void inline Initialized(EvaluationOptions flag)
{
@@ -145,6 +146,7 @@ struct EvaluationContext
EvaluationContext()
{
Zero = 0;
+ FloatPrecision = 0;
}
#define CTXT_ADD_RATIONAL_SI(numerator, denominator) \
diff --git a/mpir.net/mpir.net/ExpressionMacros.h b/mpir.net/mpir.net/ExpressionMacros.h
index 2147f53c..a519f788 100644
--- a/mpir.net/mpir.net/ExpressionMacros.h
+++ b/mpir.net/mpir.net/ExpressionMacros.h
@@ -20,16 +20,13 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/.
#pragma region Expression macros
#define IN_CONTEXT_1(a) \
- EvaluationContext context; \
a->ASSIGN_TO(context)
#define IN_CONTEXT_2(a, b) \
- EvaluationContext context; \
a->ASSIGN_TO(context); \
b->ASSIGN_TO(context)
#define IN_CONTEXT_3(a, b, c) \
- EvaluationContext context; \
a->ASSIGN_TO(context); \
b->ASSIGN_TO(context); \
c->ASSIGN_TO(context)
@@ -41,7 +38,21 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/.
#define MACRO_CHOOSE1(prefix, number) MACRO_CHOOSE2(prefix, number)
#define MACRO_CHOOSE(prefix, number) MACRO_CHOOSE1(prefix, number)
#define MACRO_GLUE(x, y) x y
-#define IN_CONTEXT(...) MACRO_GLUE(MACRO_CHOOSE(IN_CONTEXT_, COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
+
+#define IN_CONTEXT(...) \
+ EvaluationContext context; \
+ SET_CONTEXT_PRECISION \
+ MACRO_GLUE(MACRO_CHOOSE(IN_CONTEXT_, COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
+
+#define IN_DEFAULT_CONTEXT(...) \
+ EvaluationContext context; \
+ context.FloatPrecision = HugeFloat::DefaultPrecision; \
+ MACRO_GLUE(MACRO_CHOOSE(IN_CONTEXT_, COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
+
+#define IN_SPECIFIC_CONTEXT(precision, ...) \
+ EvaluationContext context; \
+ context.FloatPrecision = precision; \
+ MACRO_GLUE(MACRO_CHOOSE(IN_CONTEXT_, COUNT_ARGS(__VA_ARGS__)), (__VA_ARGS__))
//defines a unary expression class
#define DEFINE_UNARY_EXPRESSION(base, name, type) \
diff --git a/mpir.net/mpir.net/HugeFloat.cpp b/mpir.net/mpir.net/HugeFloat.cpp
index fce8074c..f83e64f5 100644
--- a/mpir.net/mpir.net/HugeFloat.cpp
+++ b/mpir.net/mpir.net/HugeFloat.cpp
@@ -180,7 +180,7 @@ namespace MPIR
int MPEXPR_NAME::GetHashCode()
{
- IN_CONTEXT(this);
+ IN_DEFAULT_CONTEXT(this);
mp_limb_t hash = CTXT(0)->_mp_exp;
mp_limb_t* ptr = CTXT(0)->_mp_d;
@@ -208,23 +208,24 @@ namespace MPIR
if(!IS_NULL(expr))
return CompareTo(expr);
- EvaluationContext context;
+ auto f = dynamic_cast(this);
+ auto precision = IS_NULL(f) ? MPTYPE::DefaultPrecision : f->Precision;
if(a->GetType() == mpir_ui::typeid)
{
- ASSIGN_TO(context);
+ IN_SPECIFIC_CONTEXT(precision, this);
return MP(cmp_ui)(CTXT(0), (mpir_ui)a);
}
if(a->GetType() == mpir_si::typeid)
{
- ASSIGN_TO(context);
+ IN_SPECIFIC_CONTEXT(precision, this);
return MP(cmp_si)(CTXT(0), (mpir_si)a);
}
if(a->GetType() == double::typeid)
{
- ASSIGN_TO(context);
+ IN_SPECIFIC_CONTEXT(precision, this);
return MP(cmp_d)(CTXT(0), (double)a);
}
@@ -248,7 +249,11 @@ namespace MPIR
if (IS_NULL(a))
return 1;
- IN_CONTEXT(this, a);
+ auto f = dynamic_cast(this);
+ if (IS_NULL(f)) f = dynamic_cast(a);
+ auto precision = IS_NULL(f) ? MPTYPE::DefaultPrecision : f->Precision;
+
+ IN_SPECIFIC_CONTEXT(precision, this, a);
return MP(cmp)(CTXT(0), CTXT(1));
}
@@ -340,6 +345,12 @@ namespace MPIR
DEFINE_BINARY_ASSIGNMENT_REF_VAL(Power, Flt, Ui, MP(pow_ui))
DEFINE_BINARY_ASSIGNMENT_REF_REF(RelativeDifferenceFrom, Flt, MP(reldiff))
+ int MPEXPR_NAME::Sign()
+ {
+ IN_DEFAULT_CONTEXT(this);
+ return MP(sgn)(CTXT(0));
+ }
+
#pragma endregion
#pragma region IO
@@ -437,13 +448,13 @@ namespace MPIR
void HugeInt::SetTo(MPEXPR_NAME^ value)
{
- IN_CONTEXT(value);
+ IN_DEFAULT_CONTEXT(value);
mpz_set_f(_value, CTXT(0));
}
void HugeRational::SetTo(MPEXPR_NAME^ value)
{
- IN_CONTEXT(value);
+ IN_DEFAULT_CONTEXT(value);
mpq_set_f(_value, CTXT(0));
}
diff --git a/mpir.net/mpir.net/HugeFloat.h b/mpir.net/mpir.net/HugeFloat.h
index bdf9379a..36197edc 100644
--- a/mpir.net/mpir.net/HugeFloat.h
+++ b/mpir.net/mpir.net/HugeFloat.h
@@ -36,6 +36,7 @@ using namespace System::Runtime::InteropServices;
#undef CTXTI
#undef ASSIGN_TO
#undef Mpt
+#undef SET_CONTEXT_PRECISION
#endif
#define SPECIALIZE_EXPRESSIONS
#define Mpt Flt
@@ -50,6 +51,7 @@ using namespace System::Runtime::InteropServices;
#define CTXTI(x) context.IntArgs[x]
#define CTXTR(x) context.RationalArgs[x]
#define ASSIGN_TO CONCAT(AssignTo, LIT(MPTYPE_NAME))
+#define SET_CONTEXT_PRECISION context.FloatPrecision = mpf_get_prec(destination);
#include "ExpressionMacros.h"
namespace MPIR
@@ -86,7 +88,7 @@ namespace MPIR
context.Initialized(FloatInitialized);
auto ptr = &context.Temp[context.Index].MPTYPE_NAME;
CTXT(context.Index++) = ptr;
- MP(init)(ptr);
+ MP(init2)(ptr, context.FloatPrecision);
AssignTo(ptr);
}
@@ -333,14 +335,17 @@ namespace MPIR
/// Compares two numbers.
/// If any argument is an expression, it is evaluated into a temporary variable before the comparison is performed.
- /// When the argument is a double, it may be an infinity, but results are undefined for a NaN.
+ /// The precision of the calculation is the precision of this instance if it is a computed number, otherwise the precision of if that is a computed number,
+ /// otherwise the current default float precision.
+ /// When the argument is a double, it may be an infinity, but results are undefined for a NaN.
/// Value to compare the source with
/// A positive number if the source is greater than , negative if less, and zero if they are equal.
virtual int CompareTo(Object^ a) sealed;
/// Compares two numbers.
/// If any argument is an expression, it is evaluated into a temporary variable before the comparison is performed.
- ///
+ /// The precision of the calculation is the precision of this instance if it is a computed number, otherwise the precision of if that is a computed number,
+ /// otherwise the current default float precision if both are expressions.
/// Value to compare the source with
/// A positive number if the source is greater than , negative if less, and zero if they are equal.
virtual int CompareTo(MPEXPR_NAME^ a) sealed;
@@ -360,16 +365,16 @@ namespace MPIR
virtual bool Equals(Object^ a) override sealed;
/// Compares two numbers approximately, taking into account most significant bits of the mantissa.
- /// If any argument is an expression, it is evaluated into a temporary variable before the comparison is performed.
+ /// If any argument is an expression, it is evaluated into a temporary variable with the specified before the comparison is performed.
/// In the future values like 1000 and 0111 may be considered the same to 3 bits (on the basis that their difference is that small).
///
/// Value to compare the source with
/// The number of most significant bits that must match for the two numbers to be considered equal
/// true if the values of the source and are equal to , false otherwise.
- bool Equals(MPEXPR_NAME^ a, mp_bitcnt_t precision) { IN_CONTEXT(this, a); return MP(eq)(CTXT(0), CTXT(1), precision) != 0; }
+ bool Equals(MPEXPR_NAME^ a, mp_bitcnt_t precision) { IN_SPECIFIC_CONTEXT(precision, this, a); return MP(eq)(CTXT(0), CTXT(1), precision) != 0; }
/// Computes the hash code of the source value.
- /// If called on an expression, it is evaluated into a temporary variable before the comparison is performed.
+ /// If called on an expression, it is evaluated into a temporary variable with the current default float precision before the calculation is performed.
/// Multi-precision classes are mutable with value semantics. The hash code is based on the value, and will change if the value changes.
/// For this reason, the value of an object must not be modified while the object is contained in a hash table.
/// a signed integer hash code for the value.
@@ -712,10 +717,10 @@ namespace MPIR
static bool operator == (double b, MPEXPR_NAME^ a) { return !IS_NULL(a) && a->CompareTo(b) == 0; }
/// Calculates the sign (+1, 0, or -1) of the source value.
- /// If the source is an expression, it is evaluated into a temporary variable before the sign is computed.
+ /// If the source is an expression, it is evaluated into a temporary variable with the current default float precision before the sign is computed.
///
/// +1 if the source is positive, -1 if negative, and 0 if zero.
- int Sign() { IN_CONTEXT(this); return MP(sgn)(CTXT(0)); }
+ int Sign();
/// Compares two numbers.
/// If any argument is an expression, it is evaluated into a temporary variable before the comparison is performed.
diff --git a/mpir.net/mpir.net/HugeInt.h b/mpir.net/mpir.net/HugeInt.h
index e823486a..8a11f690 100644
--- a/mpir.net/mpir.net/HugeInt.h
+++ b/mpir.net/mpir.net/HugeInt.h
@@ -35,6 +35,7 @@ using namespace System::Runtime::InteropServices;
#undef CTXT
#undef ASSIGN_TO
#undef Mpt
+#undef SET_CONTEXT_PRECISION
#endif
#define SPECIALIZE_EXPRESSIONS
#define Mpt Int
@@ -47,6 +48,7 @@ using namespace System::Runtime::InteropServices;
#define MPEXPR(x) LIT(MPTYPE_NAME)##x##Expression
#define CTXT(x) context.IntArgs[x]
#define ASSIGN_TO CONCAT(AssignTo, LIT(MPTYPE_NAME))
+#define SET_CONTEXT_PRECISION
#include "ExpressionMacros.h"
extern __mpz_struct HugeIntConst1;
@@ -1665,7 +1667,7 @@ namespace MPIR
///
/// Sets the value of the integer object. Any fractional portion is truncated.
/// Do not change the value of an object while it is contained in a hash table, because that changes its hash code.
- ///
+ /// If the argument is an expression, it is evaluated with the current default float precision.
/// new value for the object
void SetTo(FloatExpression^ value);
diff --git a/mpir.net/mpir.net/HugeRational.h b/mpir.net/mpir.net/HugeRational.h
index ed6fadfa..c9ac7dec 100644
--- a/mpir.net/mpir.net/HugeRational.h
+++ b/mpir.net/mpir.net/HugeRational.h
@@ -36,6 +36,7 @@ using namespace System::Runtime::InteropServices;
#undef CTXTI
#undef ASSIGN_TO
#undef Mpt
+#undef SET_CONTEXT_PRECISION
#endif
#define SPECIALIZE_EXPRESSIONS
#define Mpt Rat
@@ -49,6 +50,7 @@ using namespace System::Runtime::InteropServices;
#define CTXT(x) context.RationalArgs[x]
#define CTXTI(x) context.IntArgs[x]
#define ASSIGN_TO CONCAT(AssignTo, LIT(MPTYPE_NAME))
+#define SET_CONTEXT_PRECISION
#include "ExpressionMacros.h"
namespace MPIR
@@ -1177,7 +1179,7 @@ namespace MPIR
///
/// Sets the value of the rational object. There is no rounding, this conversion is exact.
/// Do not change the value of an object while it is contained in a hash table, because that changes its hash code.
- ///
+ /// If the argument is an expression, it is evaluated with the current default float precision.
/// new value for the object
void SetTo(FloatExpression^ value);
diff --git a/mpir.net/mpir.net/RandomFloat.cpp b/mpir.net/mpir.net/RandomFloat.cpp
index 2548ee6a..ba6733e4 100644
--- a/mpir.net/mpir.net/RandomFloat.cpp
+++ b/mpir.net/mpir.net/RandomFloat.cpp
@@ -45,7 +45,6 @@ namespace MPIR
MP(rrandomb)(destination, Left->_value, BITS_TO_LIMBS(MP(get_prec)(destination)), Right);
}
- //TODO implement "precision of destination" for context ops
//TODO investigate implementing raw IO for floats
DEFINE_ASSIGNMENT_PROLOG(RandomLimbsChunky)