浏览代码

BigInt: Fix BigInt(0) constructors; add division and other operators; add Abs(BigInt) function | Improve BindingGenerator

1vanK 3 年之前
父节点
当前提交
3aae07e122

+ 4 - 0
Docs/AngelScriptAPI.h

@@ -1805,6 +1805,9 @@ BigInt&  operator+=(const BigInt&);
 int  operator<(const BigInt&) const;
 BigInt&  operator=(const BigInt&);
 bool  operator==(const BigInt&) const;
+bool IsNegative() const;
+bool IsPositive() const;
+bool IsZero() const;
 String ToString() const;
 };
 
@@ -37928,6 +37931,7 @@ WM_CLAMP,
 };
 
 // Global functions
+BigInt Abs(const BigInt&);
 float Abs(float);
 float Acos(float);
 String AddTrailingSlash(const String&);

+ 4 - 0
Docs/ScriptAPI.dox

@@ -4786,6 +4786,9 @@ Methods:
 - int  operator<(const BigInt&) const
 - BigInt&  operator=(const BigInt&)
 - bool  operator==(const BigInt&) const
+- bool IsNegative() const
+- bool IsPositive() const
+- bool IsZero() const
 - String ToString() const
 
 <a name="Class_Billboard"></a>
@@ -38274,6 +38277,7 @@ Properties:
 - WM_CLAMP
 
 \section ScriptAPI_GlobalFunctions Global functions
+- BigInt Abs(const BigInt&)
 - float Abs(float)
 - float Acos(float)
 - String AddTrailingSlash(const String&)

+ 12 - 0
Source/Tools/BindingGenerator/ASClassBinder.cpp

@@ -265,6 +265,9 @@ static string CppMethodNameToAS(const MethodAnalyzer& methodAnalyzer)
     if (name == "operator/")
         return "opDiv";
 
+    if (name == "operator%")
+        return "opMod";
+
     if (name == "operator+=")
         return "opAddAssign";
 
@@ -277,6 +280,9 @@ static string CppMethodNameToAS(const MethodAnalyzer& methodAnalyzer)
     if (name == "operator/=")
         return "opDivAssign";
 
+    if (name == "operator%=")
+        return "opModAssign";
+
     if (name == "operator==")
         return "opEquals";
 
@@ -301,6 +307,12 @@ static string CppMethodNameToAS(const MethodAnalyzer& methodAnalyzer)
     if (name == "operator>")
         throw Exception("Registerd as opCmp separately");
 
+    if (name == "operator<=")
+        throw Exception("Registerd as opCmp separately");
+
+    if (name == "operator>=")
+        throw Exception("Registerd as opCmp separately");
+
     if (methodAnalyzer.IsPrefixIncrementOperator())
         return "opPreInc";
 

+ 4 - 0
Source/Tools/BindingGenerator/XmlAnalyzer.cpp

@@ -225,6 +225,9 @@ static string BeautifyDefinition(const string& definition)
     result = ReplaceAll(result, "- = ", "-=");
     result = ReplaceAll(result, "* = ", "*=");
     result = ReplaceAll(result, "/ = ", "/=");
+    result = ReplaceAll(result, "% = ", "%=");
+    result = ReplaceAll(result, "< = ", "<=");
+    result = ReplaceAll(result, "> = ", "<=");
     result = ReplaceAll(result, "operator = ", "operator=");
 
     result = ReplaceAll(result, "operator", "operator ");
@@ -237,6 +240,7 @@ static string BeautifyDefinition(const string& definition)
     result = ReplaceAll(result, " >", ">");
 
     result = ReplaceAll(result, "template<", "template <");
+    result = ReplaceAll(result, "operator>", "operator >");
 
     return result;
 }

+ 54 - 0
Source/Tools/Tests/Math/BigInt.cpp

@@ -14,6 +14,7 @@ void Test_Math_BigInt()
 {
     // Constructors
     assert(BigInt().ToString() == "0");
+    assert(BigInt(0).ToString() == "0");
     assert(BigInt((i32)0x7FFFFFFF).ToString() == "2147483647");
     assert(BigInt((u32)0x80000000).ToString() == "2147483648");
     assert(BigInt((u32)0xFFFFFFFF).ToString() == "4294967295");
@@ -81,10 +82,63 @@ void Test_Math_BigInt()
         BigInt bi1{"-99999999999999999999999999999999999999999999999999999999999999999999"};
         BigInt bi2{"99999999999999999999999999999999999999999999999999999999999999999999"};
         String str = (bi1 * bi2).ToString();
+        
         assert(str == "-99999999999999999999999999999999999999999999999999999999999999999998"
                       "00000000000000000000000000000000000000000000000000000000000000000001");
     }
 
+    // Divison
+    assert((BigInt("0") / BigInt("-0")).ToString() == "0");
+    assert((BigInt("0") % BigInt("0")).ToString() == "0");
+    assert((BigInt("999999") / 1234).ToString() == "810");
+    assert((BigInt("999999") % 1234).ToString() == "459");
+    assert(1234 / BigInt("999999") == 0);
+    assert(1234 % BigInt("999999") == 1234);
+
+    {
+        // https://en.cppreference.com/w/cpp/language/operator_arithmetic
+        // (a/b)*b + a%b == a
+        BigInt a("9999999843");
+        BigInt b("99999998");
+        assert(a/b*b + a%b == a);
+
+        a = {"-9999999843"};
+        b = {"99999998"};
+        assert(a/b*b + a%b == a);
+
+        a = {"9999999843"};
+        b = {"-99999998"};
+        assert(a/b*b + a%b == a);
+
+        a = {"-9999999843"};
+        b = {"-99999998"};
+        assert(a/b*b + a%b == a);
+    }
+
+#if false // Current division implementation too slow
+    {
+        BigInt num{"-99999999999999999999999999999999999999999999999999999999999999999998"
+                   "00000000000000000000000000000000000000000000000000000000000000000001"};
+            
+        BigInt denom{"-99999999999999999999999999999999999999999999999999999999999999999999"};
+        String str = (num / denom).ToString();
+        assert(str == "99999999999999999999999999999999999999999999999999999999999999999999");
+        str = (num % denom).ToString();
+        assert(str == "0");
+    }
+#else
+    {
+        BigInt num{"-999998"
+                   "000001"};
+            
+        BigInt denom{"-999999"};
+        String str = (num / denom).ToString();
+        assert(str == "999999");
+        str = (num % denom).ToString();
+        assert(str == "0");
+    }
+#endif
+
     // Additional operators
     {
         BigInt bi{(i32)1};

+ 3 - 0
Source/Urho3D/AngelScript/Generated_GlobalFunctions.cpp

@@ -32,6 +32,9 @@ static bool bool_WriteDrawablesToOBJ_constspPODVectorlesDrawablestargreamp_Files
 
 void ASRegisterGeneratedGlobalFunctions(asIScriptEngine* engine)
 {
+    // BigInt Abs(const BigInt& value) | File: ../Math/BigInt.h
+    engine->RegisterGlobalFunction("BigInt Abs(const BigInt&in)", AS_FUNCTIONPR(Abs, (const BigInt&), BigInt), AS_CALL_CDECL);
+
     // template <class T> T Abs(T value) | File: ../Math/MathDefs.h
     engine->RegisterGlobalFunction("float Abs(float)", AS_FUNCTIONPR(Abs, (float), float), AS_CALL_CDECL);
 

+ 32 - 4
Source/Urho3D/AngelScript/Generated_Members.h

@@ -529,8 +529,27 @@ template <class T> void RegisterMembers_BigInt(asIScriptEngine* engine, const ch
     // Only operator == is needed
     // bool BigInt::operator <(const BigInt& rhs) const
     // Registerd as opCmp separately
-    // bool BigInt::operator>(const BigInt& rhs) const
+    // bool BigInt::operator <=(const BigInt& rhs) const
     // Registerd as opCmp separately
+    // bool BigInt::operator >(const BigInt& rhs) const
+    // Registerd as opCmp separately
+    // bool BigInt::operator <=(const BigInt& rhs) const
+    // Registerd as opCmp separately
+
+    // bool BigInt::IsNegative() const
+    engine->RegisterObjectMethod(className, "bool IsNegative() const", AS_METHODPR(T, IsNegative, () const, bool), AS_CALL_THISCALL);
+
+    // bool BigInt::IsPositive() const
+    engine->RegisterObjectMethod(className, "bool IsPositive() const", AS_METHODPR(T, IsPositive, () const, bool), AS_CALL_THISCALL);
+
+    // bool BigInt::IsZero() const
+    engine->RegisterObjectMethod(className, "bool IsZero() const", AS_METHODPR(T, IsZero, () const, bool), AS_CALL_THISCALL);
+
+    // BigInt BigInt::operator %(const BigInt& rhs) const
+    engine->RegisterObjectMethod(className, "BigInt opMod(const BigInt&in) const", AS_METHODPR(T, operator%, (const BigInt&) const, BigInt), AS_CALL_THISCALL);
+
+    // BigInt& BigInt::operator %=(const BigInt& rhs)
+    engine->RegisterObjectMethod(className, "BigInt& opModAssign(const BigInt&in)", AS_METHODPR(T, operator%=, (const BigInt&), BigInt&), AS_CALL_THISCALL);
 
     // BigInt BigInt::operator *(const BigInt& rhs) const
     engine->RegisterObjectMethod(className, "BigInt opMul(const BigInt&in) const", AS_METHODPR(T, operator*, (const BigInt&) const, BigInt), AS_CALL_THISCALL);
@@ -553,6 +572,9 @@ template <class T> void RegisterMembers_BigInt(asIScriptEngine* engine, const ch
     // BigInt BigInt::operator -(const BigInt& rhs) const
     engine->RegisterObjectMethod(className, "BigInt opSub(const BigInt&in) const", AS_METHODPR(T, operator-, (const BigInt&) const, BigInt), AS_CALL_THISCALL);
 
+    // BigInt BigInt::operator -() const
+    engine->RegisterObjectMethod(className, "BigInt opNeg() const", AS_METHODPR(T, operator-, () const, BigInt), AS_CALL_THISCALL);
+
     // BigInt& BigInt::operator --()
     engine->RegisterObjectMethod(className, "BigInt& opPreDec()", AS_METHODPR(T, operator--, (), BigInt&), AS_CALL_THISCALL);
 
@@ -562,6 +584,12 @@ template <class T> void RegisterMembers_BigInt(asIScriptEngine* engine, const ch
     // BigInt& BigInt::operator -=(const BigInt& rhs)
     engine->RegisterObjectMethod(className, "BigInt& opSubAssign(const BigInt&in)", AS_METHODPR(T, operator-=, (const BigInt&), BigInt&), AS_CALL_THISCALL);
 
+    // BigInt BigInt::operator /(const BigInt& rhs) const
+    engine->RegisterObjectMethod(className, "BigInt opDiv(const BigInt&in) const", AS_METHODPR(T, operator/, (const BigInt&) const, BigInt), AS_CALL_THISCALL);
+
+    // BigInt& BigInt::operator /=(const BigInt& rhs)
+    engine->RegisterObjectMethod(className, "BigInt& opDivAssign(const BigInt&in)", AS_METHODPR(T, operator/=, (const BigInt&), BigInt&), AS_CALL_THISCALL);
+
     // bool BigInt::operator ==(const BigInt& rhs) const
     engine->RegisterObjectMethod(className, "bool opEquals(const BigInt&in) const", AS_METHODPR(T, operator==, (const BigInt&) const, bool), AS_CALL_THISCALL);
 
@@ -4928,9 +4956,9 @@ template <class T> void RegisterMembers_String(asIScriptEngine* engine, const ch
     // Error: type "const char*" can not automatically bind
     // bool String::operator ==(const char* rhs) const
     // Error: type "const char*" can not automatically bind
-    // bool String::operator>(const String& rhs) const
+    // bool String::operator >(const String& rhs) const
     // Registerd as opCmp separately
-    // bool String::operator>(const char* rhs) const
+    // bool String::operator >(const char* rhs) const
     // Error: type "const char*" can not automatically bind
     // void String::Replace(unsigned pos, unsigned length, const char* replaceWith)
     // Error: type "const char*" can not automatically bind
@@ -5186,7 +5214,7 @@ template <class T> void RegisterMembers_StringHash(asIScriptEngine* engine, cons
     // Only operator == is needed
     // bool StringHash::operator <(const StringHash& rhs) const
     // Registerd as opCmp separately
-    // bool StringHash::operator>(const StringHash& rhs) const
+    // bool StringHash::operator >(const StringHash& rhs) const
     // Registerd as opCmp separately
 
     // explicit StringHash::operator bool() const

+ 102 - 0
Source/Urho3D/Math/BigInt.cpp

@@ -3,6 +3,7 @@
 
 #include "BigInt.h"
 
+#include "../Container/Pair.h"
 #include "../Core/StringUtils.h"
 
 #include "../DebugNew.h"
@@ -95,6 +96,46 @@ static Vector<BigInt::Digit> DiffMagnitudes(const Vector<BigInt::Digit>& a, cons
     return ret;
 }
 
+// Return quotient and remainder.
+// If denominator == 0, then return {0, 0}
+Pair<BigInt, BigInt> DivMod(BigInt numerator, BigInt denominator)
+{
+    if (denominator == 0)
+        return {0, 0};
+
+    BigInt absNum = Abs(numerator);
+    BigInt absDenom = Abs(denominator);
+
+    BigInt quotient = 0;
+    BigInt remainder = absNum;
+
+    // The Simplest and slowest way
+    while (remainder >= absDenom)
+    {
+        ++quotient;
+        remainder -= absDenom;
+
+        String strQ = quotient.ToString();
+        String strRem = remainder.ToString();
+        int a = 0;
+    }
+
+    // https://en.cppreference.com/w/cpp/language/operator_arithmetic
+    // (a/b)*b + a%b == a
+    // 7/3 = {2, 1}    | 2*3 + 1 == 7
+    // -7/3 = {-2, -1} | -2*3 + -1 == -7
+    // 7/-3 = {-2, 1}  | -2*-3 + 1 == 7
+    // -7/-3 = {2, -1} | 2*-3 + -1 == -7
+
+    if (numerator.IsPositive() != denominator.IsPositive())
+        quotient = -quotient;
+
+    if (numerator.IsNegative())
+        remainder = -remainder;
+
+    return {quotient, remainder};
+}
+
 BigInt::BigInt()
 {
     // Init as zero
@@ -182,6 +223,9 @@ BigInt::BigInt(i32 value)
         magnitude_.Push(mod);
         value /= BASE;
     }
+
+    if (!magnitude_.Size()) // value == 0
+        magnitude_.Push(0);
 }
 
 BigInt::BigInt(i64 value)
@@ -195,6 +239,9 @@ BigInt::BigInt(i64 value)
         magnitude_.Push(mod);
         value /= BASE;
     }
+    
+    if (!magnitude_.Size()) // value == 0
+        magnitude_.Push(0);
 }
 
 BigInt::BigInt(u32 value)
@@ -207,6 +254,9 @@ BigInt::BigInt(u32 value)
         magnitude_.Push(mod);
         value /= BASE;
     }
+
+    if (!magnitude_.Size()) // value == 0
+        magnitude_.Push(0);
 }
 
 BigInt::BigInt(u64 value)
@@ -219,6 +269,22 @@ BigInt::BigInt(u64 value)
         magnitude_.Push(mod);
         value /= BASE;
     }
+
+    if (!magnitude_.Size()) // value == 0
+        magnitude_.Push(0);
+}
+
+bool BigInt::IsZero() const
+{
+    assert(magnitude_.Size() > 0);
+
+    if (magnitude_.Size() == 1 && magnitude_[0] == 0)
+    {
+        assert(positive_);
+        return true;
+    }
+
+    return false;
 }
 
 bool BigInt::operator <(const BigInt& rhs) const
@@ -344,6 +410,26 @@ BigInt BigInt::operator *(const BigInt& rhs) const
     return ret;
 }
 
+BigInt BigInt::operator /(const BigInt& rhs) const
+{
+    Pair<BigInt, BigInt> divMod = DivMod(*this, rhs);
+    return divMod.first_;
+}
+
+BigInt BigInt::operator %(const BigInt& rhs) const
+{
+    Pair<BigInt, BigInt> divMod = DivMod(*this, rhs);
+    return divMod.second_;
+}
+
+BigInt BigInt::operator -() const
+{
+    BigInt ret = *this;
+    if (!ret.IsZero())
+        ret.positive_ = !ret.positive_;
+    return ret;
+}
+
 BigInt& BigInt::operator +=(const BigInt& rhs)
 {
     BigInt result = *this + rhs;
@@ -368,4 +454,20 @@ BigInt& BigInt::operator *=(const BigInt& rhs)
     return *this;
 }
 
+BigInt& BigInt::operator /=(const BigInt& rhs)
+{
+    BigInt result = *this / rhs;
+    Swap(this->positive_, result.positive_);
+    Swap(this->magnitude_, result.magnitude_);
+    return *this;
+}
+
+BigInt& BigInt::operator %=(const BigInt& rhs)
+{
+    BigInt result = *this % rhs;
+    Swap(this->positive_, result.positive_);
+    Swap(this->magnitude_, result.magnitude_);
+    return *this;
+}
+
 } // namespace Urho3D

+ 53 - 2
Source/Urho3D/Math/BigInt.h

@@ -3,7 +3,6 @@
 
 #pragma once
 
-#include "../Container/Pair.h"
 #include "../Container/Str.h"
 #include "../Container/Vector.h"
 #include "MathDefs.h"
@@ -18,7 +17,7 @@ public:
     using Digit = i64;
 
 private:
-    /// Sign.
+    /// Sign (zero is positive).
     bool positive_;
 
     /// Array of digits with base 10^9 and reverse order (each element contains value in range [0 .. BASE-1]).
@@ -32,19 +31,36 @@ public:
     BigInt(u32 value);
     BigInt(u64 value);
 
+    bool IsPositive() const { return positive_; }
+    bool IsNegative() const { return !positive_; }
+    bool IsZero() const;
+
     bool operator ==(const BigInt& rhs) const { return positive_ == rhs.positive_ && magnitude_ == rhs.magnitude_; }
     bool operator !=(const BigInt& rhs) const { return positive_ != rhs.positive_ || magnitude_ != rhs.magnitude_; }
 
     bool operator <(const BigInt& rhs) const;
     bool operator >(const BigInt& rhs) const { return rhs < *this; }
 
+    bool operator <=(const BigInt& rhs) const { return *this == rhs || *this < rhs; }
+    bool operator >=(const BigInt& rhs) const { return *this == rhs || *this > rhs; }
+
     BigInt operator +(const BigInt& rhs) const;
     BigInt operator -(const BigInt& rhs) const;
     BigInt operator *(const BigInt& rhs) const;
 
+    /// Return 0 if rhs zero.
+    BigInt operator /(const BigInt& rhs) const;
+    
+    /// Return 0 if rhs zero.
+    BigInt operator %(const BigInt& rhs) const;
+
+    BigInt operator -() const;
+
     BigInt& operator +=(const BigInt& rhs);
     BigInt& operator -=(const BigInt& rhs);
     BigInt& operator *=(const BigInt& rhs);
+    BigInt& operator /=(const BigInt& rhs);
+    BigInt& operator %=(const BigInt& rhs);
 
     /// Prefix increment operator.
     BigInt& operator++() { this->operator +=(1); return *this; }
@@ -61,4 +77,39 @@ public:
     String ToString() const;
 };
 
+#if false // Without constraints this can cause conflicts
+template <typename T> BigInt operator +(T lhs, const BigInt& rhs) { return BigInt(lhs) + rhs; }
+template <typename T> BigInt operator -(T lhs, const BigInt& rhs) { return BigInt(lhs) - rhs; }
+template <typename T> BigInt operator *(T lhs, const BigInt& rhs) { return BigInt(lhs) * rhs; }
+template <typename T> BigInt operator /(T lhs, const BigInt& rhs) { return BigInt(lhs) / rhs; }
+template <typename T> BigInt operator %(T lhs, const BigInt& rhs) { return BigInt(lhs) % rhs; }
+#else
+inline BigInt operator +(i32 lhs, const BigInt& rhs) { return BigInt(lhs) + rhs; }
+inline BigInt operator +(u32 lhs, const BigInt& rhs) { return BigInt(lhs) + rhs; }
+inline BigInt operator +(i64 lhs, const BigInt& rhs) { return BigInt(lhs) + rhs; }
+inline BigInt operator +(u64 lhs, const BigInt& rhs) { return BigInt(lhs) + rhs; }
+
+inline BigInt operator -(i32 lhs, const BigInt& rhs) { return BigInt(lhs) - rhs; }
+inline BigInt operator -(u32 lhs, const BigInt& rhs) { return BigInt(lhs) - rhs; }
+inline BigInt operator -(i64 lhs, const BigInt& rhs) { return BigInt(lhs) - rhs; }
+inline BigInt operator -(u64 lhs, const BigInt& rhs) { return BigInt(lhs) - rhs; }
+
+inline BigInt operator *(i32 lhs, const BigInt& rhs) { return BigInt(lhs) * rhs; }
+inline BigInt operator *(u32 lhs, const BigInt& rhs) { return BigInt(lhs) * rhs; }
+inline BigInt operator *(i64 lhs, const BigInt& rhs) { return BigInt(lhs) * rhs; }
+inline BigInt operator *(u64 lhs, const BigInt& rhs) { return BigInt(lhs) * rhs; }
+
+inline BigInt operator /(i32 lhs, const BigInt& rhs) { return BigInt(lhs) / rhs; }
+inline BigInt operator /(u32 lhs, const BigInt& rhs) { return BigInt(lhs) / rhs; }
+inline BigInt operator /(i64 lhs, const BigInt& rhs) { return BigInt(lhs) / rhs; }
+inline BigInt operator /(u64 lhs, const BigInt& rhs) { return BigInt(lhs) / rhs; }
+
+inline BigInt operator %(i32 lhs, const BigInt& rhs) { return BigInt(lhs) % rhs; }
+inline BigInt operator %(u32 lhs, const BigInt& rhs) { return BigInt(lhs) % rhs; }
+inline BigInt operator %(i64 lhs, const BigInt& rhs) { return BigInt(lhs) % rhs; }
+inline BigInt operator %(u64 lhs, const BigInt& rhs) { return BigInt(lhs) % rhs; }
+#endif
+
+inline BigInt Abs(const BigInt& value) { return value.IsNegative() ? -value : value; }
+
 } // namespace Urho3D

+ 1 - 0
bin/Data/Scripts/Tests.as

@@ -5,5 +5,6 @@
 void Start()
 {
     Test_Math_BigInt();
+    log.Info("Success!");
     engine.Exit();
 }

+ 13 - 0
bin/Data/Scripts/Tests/Math/BigInt.as

@@ -16,6 +16,17 @@ void Test_Math_BigInt()
         Assert((bi += 1).ToString() == "10000000000000");
     }
 
+    {
+        BigInt num("-999998"
+                   "000001");
+
+        BigInt denom("-999999");
+        String str = (num / denom).ToString();
+        Assert(str == "999999");
+        str = (num % denom).ToString();
+        Assert(str == "0");
+    }
+
     // Additional operators
     {
         BigInt bi(int(1));
@@ -30,5 +41,7 @@ void Test_Math_BigInt()
         Assert((bi += 10).ToString() == "11");
         Assert((bi -= 2).ToString() == "9");
         Assert((bi *= 2).ToString() == "18");
+        Assert((bi /= 2).ToString() == "9");
+        Assert((bi %= 2).ToString() == "1");
     }
 }