Obscure Isssue #1

Ruby The Roobster rubytheroobster at yandex.com
Fri Aug 5 13:56:40 UTC 2022


My code (as seen below) is failing due to a single line.

That line is:
```d
this.ival += (this.val * rhs.ival);
```

I kid you not, this is the reason why running unittests results 
in a program that just hangs.  And no, replacing unittest with 
void main() doesn't fix the problem.

```d
module dutils.math.number;

version(DLL)
{
     export:
}

version(Standard)
{
     public:
}

public import dutils.math.core;
public import std.bigint;

class Number : Mtype!NumberContainer
{
     this(NumberContainer num)
     {
         this.contained = val;
     }

     override dstring toDstring() const @property pure @safe
     {
         return this.contained.toDstring;
     }

     override void fromDstring(dstring from) pure @safe
     {
         this.contained.fromDstring(from);
     }

     bool applyOp(W)(dstring op, Mtype!W rhs) pure @safe
     in
     {
         assert(is(W == NumberContainer));
         assert((op == "+"d) ^ (op == "-"d) ^ (op == "*"d) ^ (op 
== "/"d) ^ (op == "^^"d));
     }
     do
     {
         mixin("this.contained " ~ op ~ "= rhs.contained;");
         return true; //We assume that the contract hasn't been 
violated.
     }
}

struct NumberContainer
{
     this(BigInt val, BigInt ival = 0, long pow10 = 0, ulong 
precision = 18) pure @safe nothrow @nogc
     {
         this.val = val;
         this.pow10 = pow10;
         this.ival = ival;
         this.precision = precision;
     }

     dstring toDstring() const @property pure @safe
     {
         return ""d; //Placeholder
     }

     void fromDstring(dstring from) pure @safe
     {
         //Placeholder
     }

     void opOpAssign(string op)(NumberContainer rhs) pure @safe
     {
         import std.algorithm : max;
         if(op == "/") //This section is where the line that hangs 
leads us to.
         {
             //Because BigInt is strictly an integer type, it is 
easier to code A/B as A*1/B, because the * operator is a piece of 
cake, and 1 over a BigInt is easier than an arbitrary BigInt
             //over another arbitrary BigInt
             immutable BigInt den = rhs.val ^^ 2 + rhs.ival ^^ 2;
             NumberContainer store = 
NumberContainer(cast(BigInt)0);
             auto istore = store;
             long count = 0;
             ubyte count2 = 9;
             rhs *= NumberContainer(rhs.val, -rhs.ival, rhs.pow10, 
rhs.precision); //This line leads directly to the cause of hang.
             for(ulong i = 0; i < precision; ++i) //Real part
             {
                 if(rhs.val == BigInt(0))
                     break;
                 //Play around with the multiplier so the 
denominator actually fits in the numerator.
                 while(den > (BigInt(10) ^^ count) * rhs.val)
                 {
                     ++count;
                 }
                 //Remove excess.
                 while(den < (BigInt(10) ^^ (count - 1L) * 
rhs.val))
                 {
                     --count;
                 }

                 for(; count2 * den > (BigInt(10) ^^ count) * 
rhs.val; --count2)
                 {
                         if(count2 < -9) //Remember, negative 
numbers exist too!
                             throw new Exception("ERROR: Division 
by 0");
                 }

                 rhs.val *= (BigInt(10) ^^ count); //`rhs` is a 
copy, so this isn't an issue.
                 rhs.val -= count2 * den; //Continue performing 
long division.
                 store.val *= 10;
                 store.val += count2;
                 store.pow10 -= count;

                 count = 0;
                 count2 = 9;
             }

             this.val *= store.val;
             this.pow10 = store.pow10;

             for(ulong i = 0; i < precision; ++i) //Imaginary part.
             {
                 if(rhs.ival == BigInt(0))
                     break;
                 while(den > (BigInt(10) ^^ count) * rhs.ival)
                 {
                     ++count;
                 }
                 //Remove excess.
                 while(den < (BigInt(10) ^^ (count - 1L) * 
rhs.ival))
                 {
                     --count;
                 }

                 for(; count2 * den > (BigInt(10) ^^ count) * 
rhs.ival; --count2)
                 {
                         if(count2 < -9) //Remember, negative 
numbers exist too!
                             throw new Exception("ERROR: Division 
by 0");
                 }

                 rhs.ival *= (BigInt(10) ^^ count); //`rhs` is a 
copy, so this isn't an issue.
                 rhs.ival -= count2 * den; //Continue performing 
long division.
                 istore.ival *= 10;
                 istore.ival += count2;
                 istore.pow10 -= count;

                 count = 0;
                 count2 = 9;
             }
             import std.algorithm : min, max;
             this.ival *= istore.ival;
             this.pow10 = min(store.pow10, istore.pow10);
             if(store.pow10 > istore.pow10)
                 this.val *= BigInt(10) ^^ (store.pow10 - 
istore.pow10);
             else
                 this.ival *= BigInt(10) ^^ (max(store.pow10, 
istore.pow10) - store.pow10);
         }
         else if(op == "^^")
         {
             //Oy Vey

         }
         else if(op == "*")
         {
             this.val *= rhs.val;
             this.val -= (this.ival + rhs.ival);
             this.ival = (this.ival * rhs.val);
             this.pow10 += rhs.pow10;
             this.ival += (this.val * rhs.ival); //This line 
solely causes the hang.  I kid you not.
         }
         else
         {
             if(this.pow10 > rhs.pow10)
             {
                 this.val *= BigInt(10) ^^ (this.pow10 - 
rhs.pow10);
                 this.ival *= BigInt(10) ^^ (this.pow10 - 
rhs.pow10);
             }
             else if(rhs.pow10 > this.pow10)
             {
                 rhs.val *= BigInt(10) ^^ (rhs.pow10 - this.pow10);
                 rhs.ival *= BigInt(10) ^^ (rhs.pow10 - 
this.pow10);
             }
             this.pow10 = rhs.pow10;
             this.val += rhs.val;
             this.ival += rhs.ival;
         }
     }

     NumberContainer opBinary(string op)(NumberContainer rhs) pure 
@safe
     {
         NumberContainer ret = this;
         mixin("ret " ~ op ~ " rhs;");
         return ret;
     }
     package:
         BigInt val;
         BigInt ival;
         long pow10;
         ulong precision;
}

unittest {
     BigInt a = 1;
     BigInt b = 1;
     long c = 0;
     NumberContainer e = NumberContainer(a,b,c);
     a = 2;
     NumberContainer f = NumberContainer(a,b,c);
     e /= f; //This is the line that causes the hang.
     import std.format;
     assert((e).val == 6 && (e).pow10 == -1, format("%d", 
e.val).dup ~ format("%d", e.ival));
}
```

Funnily enough, if you simplify the module down to the following:

```d
import std.bigint;
unittest ()
{
   import std.conv : to;
   S a = S(BigInt(1),BigInt(2),3);
   a *= S(a.val, -a.ival, a.pow10);
   assert(a == S(BigInt(1), BigInt(0), 6L), 
to!string(a.val.toLong).idup);
}

struct S
{

     private BigInt val;
     private BigInt ival;
     private long pow10;
     ulong precision;

     void opOpAssign(string op)(S rhs)
     {
         this.val *= rhs.val;
         this.val -= (this.ival + rhs.ival);
         this.ival = (this.ival * rhs.val);
         this.pow10 += rhs.pow10;
         this.ival += (this.val * rhs.ival);
     }
}
```

It works and doesn't hang.

Anyone have ANY idea of what I'm doing wrong here?  Or is the 
compiler just bugging out?


More information about the Digitalmars-d-learn mailing list