July 04, 2022

On Monday, 4 July 2022 at 10:19:36 UTC, Bastiaan Veelo wrote:

>

On Monday, 4 July 2022 at 09:43:11 UTC, user1234 wrote:

>

On Monday, 4 July 2022 at 09:40:45 UTC, Bastiaan Veelo wrote:

>

Not necessarily.

immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array;

Hi, to this alternative I'd say that it's not self documenting.
So it still has 50% of the initial problem.
There is a point to be made that this is more self-documenting than the hand-written alternative: Here it is obvious that the ranks cover all powers of 10 from 0 to 9 in order.

This advantage becomes much more clear in, let's say:

immutable long powersOfThree = iota(27).map!(p => 3.pow(p)).array;

Handwritten this is a very long chain of arbitrary numbers and you have to put a very close look, that those are indeed the powers of three.

July 04, 2022

On Monday, 4 July 2022 at 09:40:45 UTC, Bastiaan Veelo wrote:

>

[...]
Not necessarily.

immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array;

Your example requires

import std.range; // for iota
import std.algorithm; // for map
import std.math; // for pow of int

Then your example is prone to errors:

immutable decimalRanks = iota(13).map!(p => 10.pow(p)).array; // silent wrap around

(unit)test to the rescue:

auto make_decimal_rank ()
{
   import std.range;
   import std.algorithm;
   import std.math;
   return iota (13).map!(p => 10.pow (p)).array;
}

void main ()
{
   import std.stdio;
   immutable decimalRank = make_decimal_rank;
   decimalRank.writeln;
   typeof (decimalRank).stringof.writeln;
}

unittest {
   enum ar13 = [
         1,
         10,
         100,
         1000,
         10000,
         100000,
         1000000,
         10000000,
         100000000,
         1000000000,
         10000000000,
         100000000000,
         1000000000000
      ];
   pragma (msg, typeof (ar13));
   assert (make_decimal_rank == ar13);
}

Running:

dmd -checkaction=context -unittest -run decrank
long[]
decrank.d(35): [unittest] [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 1410065408, 1215752192, -727379968] != [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, 10000000000, 100000000000, 1000000000000]
1/1 modules FAILED unittests
July 05, 2022

On Monday, 4 July 2022 at 23:19:27 UTC, kdevel wrote:

>

Then your example is prone to errors:

immutable decimalRanks = iota(13).map!(p => 10.pow(p)).array; // silent wrap around

(unit)test to the rescue:

To be fair, original example also requires some testing.

July 06, 2022

On Tuesday, 5 July 2022 at 07:37:35 UTC, Alexandru Ermicioi wrote:
[...]

>

To be fair, original example also requires some testing.

Sure, but user1234 was never in favor of it. It is prone to the same wrap around which have been avoided by using strings in the first place:

auto sgenDecimalRanks()
{
    auto r = "1";
    auto result = "[";
    foreach (i; 1 .. 13)
    {
        result ~= r;
        if (i < 12)
            result ~= ", ";
        r ~= '0';
    }
    result ~= "]";
    return result;
}
July 06, 2022

On Monday, 4 July 2022 at 13:03:55 UTC, Dom Disc wrote:
[...]

>

This advantage becomes much more clear in, let's say:

immutable long powersOfThree = iota(27).map!(p => 3.pow(p)).array;

Handwritten this is a very long chain of arbitrary numbers and you have to put a very close look, that those are indeed the powers of three.

Really?

immutable long [] powersOfThree = [
      1,
      3,
      3 * 3,
      3 * 3 * 3,
      3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,
   ];
July 06, 2022

On Wednesday, 6 July 2022 at 00:11:58 UTC, kdevel wrote:

>

On Monday, 4 July 2022 at 13:03:55 UTC, Dom Disc wrote:
[...]

>

This advantage becomes much more clear in, let's say:

immutable long powersOfThree = iota(27).map!(p => 3.pow(p)).array;

Handwritten this is a very long chain of arbitrary numbers and you have to put a very close look, that those are indeed the powers of three.

Really?

immutable long [] powersOfThree = [
      1,
      3,
      3 * 3,
      3 * 3 * 3,
      3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,
      3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3,
   ];

Ok, but with this, you can't see where the overflow will occur. So you have the same problem as with the one-liner (and it is indeed very much longer).

December 28, 2022

On Monday, 4 July 2022 at 23:19:27 UTC, kdevel wrote:

> >
immutable decimalRanks = iota(10).map!(p => 10.pow(p)).array;

This example requires

import std.range; // for iota
import std.algorithm; // for map
import std.math; // for pow of int

Then your example is prone to errors:

immutable decimalRanks = iota(13).map!(p => 10.pow(p)).array; // silent wrap around

Sorry for the late reply, but I just stumbled over this old thread.
I now have a function

ubyte maxpow(const ulong base) { }

That returns the maximum power of the given base that fits in a ucent (for smaller target types, shift down the result by one for each halfing of the size).

With this you can create the function

```d
ulong[] powersOf(byte n)
{
   import std.range : iota;
   import std.algorithm : map;
   return iota(maxpow(n)>>1).map!(p => ulong(n)^^p).array;
}
```

If this function is used to create lookup-tables, it will be called only during compile-time, so the imports are not in the resulting executable (therefore it doesn't matter that I replaced pow by the buildin ^^).

static immutable ulong[maxpow(17)>>1] ranksOf17 = powersOf(17);

I think this is much more readable as a list of obscure number literals, it is safe to use, doesn't increase linker time and doesn't blow up the binary.
Hurray to the meta-programming! Hurray! Hurray!

January 01, 2023

On Wednesday, 28 December 2022 at 13:57:13 UTC, Dom Disc wrote:

>

[...]
I now have a function

ubyte maxpow(const ulong base) { }

Code?

>

That returns the maximum power of the given base that fits in a ucent

ucent? Cannot use cent/ucent or core.int128's types here.

>

(for smaller target types, shift down the result by one for each halfing of the size).

What do you mean by "shift down"? "Shift right" aka "divide by two"?

>

With this you can create the function

```d
ulong[] powersOf(byte n)
{
   import std.range : iota;
   import std.algorithm : map;
   return iota(maxpow(n)>>1).map!(p => ulong(n)^^p).array;
}
```

This generates too few elements.

import std.stdio;
import mod_maxpow;

auto powersOf (T) (ulong n)
{
   import std.range : iota;
   import std.algorithm : map;
   import std.array : array;
   return iota(maxpow!T (n) + 1).map!(p => n^^p).array;
}

int main (string [] args)
{
   static immutable ranksOf17 = powersOf!ulong (17);
   writeln (ranksOf17);
   return 0;
}

prints

[1, 17, 289, 4913, 83521, 1419857, 24137569, 410338673, 6975757441, 118587876497, 2015993900449, 34271896307633, 582622237229761, 9904578032905937, 168377826559400929, 2862423051509815793]

maxpow!ulong(17) is 15. Counting starts with 0 so we must add one.

>

If this function is used to create lookup-tables, it will be called only during compile-time, so the imports are not in the resulting executable (therefore it doesn't matter that I replaced pow by the buildin ^^).

static immutable ulong[maxpow(17)>>1] ranksOf17 = powersOf(17);

Unnecessary code duplication.

>

I think this is much more readable as a list of obscure number literals, it is safe to use,

Unit tests?

January 02, 2023

On Sunday, 1 January 2023 at 23:05:46 UTC, kdevel wrote:

>

On Wednesday, 28 December 2022 at 13:57:13 UTC, Dom Disc wrote:

>

[...]
I now have a function

ubyte maxpow(const ulong base) { }

Code?

>

That returns the maximum power of the given base that fits in a ucent

ucent? Cannot use cent/ucent or core.int128's types here.

Yeah, I implemented this long time ago, when I still had hope that ucent will be available soon :-/

> >

(for smaller target types, shift down the result by one for each halfing of the size).

What do you mean by "shift down"? "Shift right" aka "divide by two"?

Yes. Divide by two to find out what power fits in uint, divide by four for ushort and divide by 8 for ubyte.

>

[...]
This generates too few elements.

Ok, but I tend to omit n^^0 and n^^1 - so I would say it even generates too many elements :-)

> >
static immutable ulong[maxpow(17)>>1] ranksOf17 = powersOf(17);

Unnecessary code duplication.

Yes, I hate that too. But

ulong[] ranksOf17 = powersOf(17);

will generate a dynamic array, and unfortunately an inherited length for static arrays (e.g. something like uint[$] x = [1,2,3]; or whatever syntax we could agree upon) is still not available :-(

>

Unit tests?

Of course. Nothing that you can do will ever make those obsolete.
Mine are rather exhaustive:

/// exponentiation without overflow
/// return invalid if overflow would occur.
T safePow(T)(const(T) base, const int exp) if(isIntegral!T)
{
   if(exp < 2) return !exp ? 1 : (exp < 0 && base.abs != 1) ? 0 : odd(exp) ? base : 1;
   static if(isUnsigned!T)
   {
      if(base < 2) return base;
      if(base >= 1<<(T.sizeof<<2)) return invalid!T; // base too large to fit any powers of it into the same type
      if(exp > (maxpow(base)>>(5-bitlen(T.sizeof)))) return invalid!T;
      return T(base^^exp); // calc only if no overflow for sure (very efficient)
   }
   else
   {
      auto r = safePow(abs(base), exp); // max power may not fit in the signed type
      if(r > T.max) return invalid!T; // but at least we can detect it
      return (base < 0 && odd(exp)) ? -T(r) : T(r);
   }
}

unittest
{
   import std.conv;
   void test(T)()
   {
      T r;
      foreach(T base; (-128 * isSigned!T)..128)
      {
         foreach(uint exp; 0..256)
         {
            r = safePow(base, exp);
            if(exp==1) assert(r == base);
            else if(r == invalid!T)
            {
               assert(T.max / safePow(base, exp-1).abs < base.abs,
                      "max wrong: ",base,"^^",exp," <= "~T.stringof~".max");
               break;
            }
            assert(r == base^^exp, "calc wrong: ",base,"^^",exp," != ",r);
         }
      }
   }
   test!byte;
   test!ubyte;
   test!short;
   test!ushort;
   test!int;
   test!uint;
   test!long;
   test!ulong;
}
1 2
Next ›   Last »