# Dynamic indexing (multi-dimensional-indexing) (probably my most important/valuable posting up to thi

Discussion in 'C Programming' started by Skybuck Flying, Jul 1, 2011.

1. ### Skybuck FlyingGuest

This is probably the most important test/demonstration program I ever wrote
and published/released onto the internet (up to this date (7 juli 2011)).

I hope compiler writers/programming language designers will study it
carefully and integrate it into their language.
(It's a bit long/lengthy read by well worth it ! Also casual programmers can
benefit from it as well !)

See comments for more details about the usefullness and the value of it for
present day and the future.

Also see test program way down below for usage example and verification
program.

Especially the formula's are very important.

"dynamic pointer indexing"

Which takes this step a bit further to provide direct memory access
(concept/thread which is still under development).

This posting takes a step back to evaluate and verify the formula's to be
used in "dynamic pointer indexing".

For now this example/concept can be thought of as "dynamic indexing".

Which can also be thought of as "multi-dimensional indexing".

A future which is currently missing in C/C++/CUDA C/C++ and in a way also
Delphi since Delphi requires a pointer array per additional dimension.

The technique illustrated below needs none of that Delphi overhead and
introduces it's own much smaller overhead, memory-wise, it does introduce a

There is also a hidden potential problem if parenthesis would be used.

A small explanation for the people that did not follow the other thread
mentioned:

The multiply operator is used as a passing mechanism to calculate a linear
index for the multi dimension indexes.

// *** Begin of Program ***

program TestProgram;

{\$APPTYPE CONSOLE}

{

Test dynamic multi dimensional index calculations

version 0.01 created on 1 juli 2011 by Skybuck Flying

To illustrate the theory behind dynamic multi dimension index calculations I
shall now
create a little test/demonstration program to show you how to write this
code and what
formula's to use.

The example deviates from the written theory so far in regard to the order,
the order
in the theory was assumed to be z, y, x however delphi's order appears to be
x, y, z.

Therefore the formula's will be adepted to Delphi's order.

For completeness sakes I shall mention the formula's in this comments one
more time.

How these formula's where derived I will not explain here because it was a
bit vague and messy but all in all not to bad.
But you don't need to know that... all that matters are the formula's you
can probably derive/come to the conclusion
how these were formed yourself and if not consider these a gift to you from
me !

Formula for index calculation in abstract form:

result.index =
(lower dimension parameter.index * lower dimension parameter.stride) +
(higher dimension parameter.index * lower dimension parameter.volume);

the resulting stride should be 1 until further notice
result.stride = 1;

Valueable notes for you to understand:

Strides always have a minimum of 1.
Volumes always have a minimum of 1.
DimensionCounts always have a minimum of 1.

These ones assure that none of the multiplications are zero-ed out, that
This is why these general formula's work. No matter if the stride is 1 and
the volume is 1 it will still work.
So some unnecessary multiplications with 1 might be done but so be it for
generalities sake.

The last formula you will need is the volume calculation in abstract form
which is actually quite simply:

result.volume = (lower dimension parameter.volume * higher dimension
parameter.volume);

It should be apperent to you now why this will work, but I shall explain it
anyway.

Since the order is from left to right which means low to high this will
work.

The lowest dimension is multiplied with the higher dimension giving a new
volume, in the first two dimension case
this would be the area (a volume with a depth of 1) the next dimension it is
multiplied with would give a new volume.
(a real 3d volume with a depth of depth). The next dimension it is
multiplied against gives a 4D volume. I use
the term "volume" for a lack of a better term for a 4D quanitity. So think
of a volume is that which describes
the entire (coordinate) space (or index space if you will).

I shall also give these two formula's in a somewhat more concrete and short
hand form for direct/easy/correct implementation purposes:

result.stride := 1;
result.index := (ParaA.Index * ParaA.Stride) + (ParaB.Index * ParaA.Volume);
result.volume := (ParaA.Volume * ParaB.Volume);

Now it's time for a test/demonstration program to proof to you that this is
not bullshit, to prove to you that is is valuable
and that this works and to show the power/flexibility of it so you will
hopefully see how usefull this would become
if this became a primary language feature with further support behind it !
=D

However the current code which shall be provided can already be of high
value for current day usasage ! =D

To show the usefullness of it a type will be introduced which shall
encompass this theory and these formula's.

This type shall be called "TDynamicIndex" it's purposes is many-fold:

To construct "dynamic" multi dimensional array indexing automatically.

So that the user programmer can easily adjust it's code to support all kinds
of 1D, 2D, 3D and xD dimensions.

Possibly layed out over a single dimension.

So this type calculates a linear index (which is the final/result index)
which can then be used to access for example
the memory system (which is a 1D entity).

Also to simply the design, the indexed property will be replaced with a
procedure, this illustrates
that the method is applieable to other languages which do not have
properties like c/c++, so this also illustrates
that the method works not because of properties but thanks to operator

However I have no way of setting the index value with indexed properties (a
procedure call would look ugly so I am going
to leave it in ! ) but rename it also a little bit to show what it does.

This test/demonstration program will also function as a verifier to verify
that the calculations are indeed correct.

Conclusion:

It works flawlessly so far.

This is probably one of the, if not the, most important test program and
theory I have ever published.

I hope compiler writers will study it carefully and will hopefully be able
to integrate this "technology" into
their programming languages so that we programmers can finally use "dynamic
pointers" and "dynamic indexes".. so
that we programmers can finally program easily in multiple dimensions which
translate to a linear/1d dimension for memory access.

So that we can finally have multi dimensional array indexing with paying
penalties like additional pointers or difficult data
structure construction involving multiple array creations and such.

This demonstration and code will become more valuable as CUDA/OpenCL becomes
more valuable and widely used.

CUDA C/C++ is a perfect example of the
"multi dimensional indexing hell/linear index calculation hell, problem
distribution hell" that CUDA C/C++ programmers are now facing.

This code demonstration demonstrates how to solve it in a flexible way.

The usage example is hard coded to use the dimensions of x: 320 y: 200 z: 60
which represents the classic ms-dos vga
screen and a refresh rate of 60, (which was actually 70 at the time but ok),
so some programmers might be used to these dimensions
and might recgonize some of it.

Hopefully needless to say, this example is not limited to these dimensions,
and any dimension can be used.

A word of caution: integer has been used for volume. It's pretty easy to see
how the volume calculation might overflow
for large dimensions so keep an eye out of that. For now I shall let it be
an integer for speed's sake.

If an overflow occurs for your usage consider using an int64 for larger
dimension support.

This demonstration code has limited itself to indexes to keep things simple.

Strides have also been set to 1.

This represent a byte.

Changing the stride to 4 would represent an integer.

Only the first stride for the X dimension would need to be set to 4.

The other strides should be left alone and be set to 1.

Changing the stride would invalidate the verification program ofcourse.

Changing the code to automatically adept to changes should be obvious, but
for clearities sake
will not be done in version 0.01.

Perhaps version 0.02 might do this, but for now I see little value in it !

I shall now continue working on my TDynamicPointer concept which uses
the concepts presented here in TDynamicIndex and expands on it to support

}

uses
SysUtils;

type
TDynamicIndex = record
private
mIndex : integer;
mStride : integer;
mDimensionCount : integer;
mVolume : integer;

function StoreIndex( ParaIndex : integer ) : TDynamicIndex;
public
constructor Create( ParaDimensionCount : integer; ParaStride : integer );

property IndexOperator[ ParaIndex : integer ] : TDynamicIndex read
StoreIndex; default;

class operator Multiply( ParaA, ParaB : TDynamicIndex ) : TDynamicIndex;
end;

constructor TDynamicIndex.Create( ParaDimensionCount : integer; ParaStride :
integer );
begin
mIndex := 0;
mStride := 1;
mDimensionCount := 1;
mVolume := 1;

begin
end;

if ParaStride > 1 then
begin
mStride := ParaStride;
end;

mVolume := mDimensionCount * mStride;
end;

// store index, and return a copy, not because we want to, but because we
must
// I would much rather return a pointer, but unfortunately calling multiple
operator on pointer derefence won't work well ?!)
// perhaps it should be retried sometime...
function TDynamicIndex.StoreIndex( ParaIndex : integer ) : TDynamicIndex;
begin
mIndex := ParaIndex;
result.mIndex := mIndex;
result.mStride := mStride;
result.mDimensionCount := mDimensionCount;
result.mVolume := mVolume;
end;

class operator TDynamicIndex.Multiply( ParaA, ParaB : TDynamicIndex ) :
TDynamicIndex;
begin
result.mStride := 1;
result.mIndex := (ParaA.mIndex * ParaA.mStride) + (ParaB.mIndex *
ParaA.mVolume);
result.mVolume := (ParaA.mVolume * ParaB.mVolume);
end;

procedure Main;
var
X : TDynamicIndex;
Y : TDynamicIndex;
Z : TDynamicIndex;
Result : TDynamicIndex;
vErrorDetected : boolean;

vX : integer;
vY : integer;
vZ : integer;

vCheck : integer;
begin
X := TDynamicIndex.Create( 320, 1 );
Y := TDynamicIndex.Create( 200, 1 );
Z := TDynamicIndex.Create( 60, 1 );

// first a few single tests to see/show how it works.
Result := X[ 0 ] * Y[ 0 ] * Z[ 1 ]; // should display 320*200*1*1=64000
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 0 ] * Z[ 0 ]; // should display 319*1*1*1=319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 0 ]; // should display 319*1 +
(100*320)*1*1 = 32319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 5 ]; // should display 319*1 +
(100*320)*1 + (5*200*320)*1 = 32319 + 320000 = 352319
writeln( Result.mIndex );

// so far so could so now let's do some exhaustive/full verifications.
writeln('Verification started.');

vErrorDetected := false;
for vZ := 0 to 59 do
begin
for vY := 0 to 199 do
begin
for vX := 0 to 319 do
begin
Result := X[ vX ] * Y[ vY ] * Z[ vZ ];

vCheck := (vZ * 200 * 320) + (vY * 320) + vX;

if Result.mIndex <> vCheck then
begin
writeln('Index calculation error detected at (vX, vY, vZ): ' + '(' +
IntToStr(vX) + ',' + IntToStr(vY) + ',' + IntToStr(vZ) + ')' );
vErrorDetected := true;
end;
end;
end;
end;

writeln('Verification done.');

if vErrorDetected then
begin
writeln('Verification result: errors detected !');
end else
begin
writeln('Verification result: no errors detected.');
end;
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

// *** End of Program ***

Bye,
Skybuck.

Skybuck Flying, Jul 1, 2011

2. ### Skybuck FlyingGuest

Little typo corrected at (*****) (with changed to without):

This is probably the most important test/demonstration program I ever wrote
and published/released onto the internet (up to this date (7 juli 2011)).

I hope compiler writers/programming language designers will study it
carefully and integrate it into their language.
(It's a bit long/lengthy read by well worth it ! Also casual programmers can
benefit from it as well !)

See comments for more details about the usefullness and the value of it for
present day and the future.

Also see test program way down below for usage example and verification
program.

Especially the formula's are very important.

"dynamic pointer indexing"

Which takes this step a bit further to provide direct memory access
(concept/thread which is still under development).

This posting takes a step back to evaluate and verify the formula's to be
used in "dynamic pointer indexing".

For now this example/concept can be thought of as "dynamic indexing".

Which can also be thought of as "multi-dimensional indexing".

A future which is currently missing in C/C++/CUDA C/C++ and in a way also
Delphi since Delphi requires a pointer array per additional dimension.

The technique illustrated below needs none of that Delphi overhead and
introduces it's own much smaller overhead, memory-wise, it does introduce a

There is also a hidden potential problem if parenthesis would be used.

A small explanation for the people that did not follow the other thread
mentioned:

The multiply operator is used as a passing mechanism to calculate a linear
index for the multi dimension indexes.

// *** Begin of Program ***

program TestProgram;

{\$APPTYPE CONSOLE}

{

Test dynamic multi dimensional index calculations

version 0.01 created on 1 juli 2011 by Skybuck Flying

To illustrate the theory behind dynamic multi dimension index calculations I
shall now
create a little test/demonstration program to show you how to write this
code and what
formula's to use.

The example deviates from the written theory so far in regard to the order,
the order
in the theory was assumed to be z, y, x however delphi's order appears to be
x, y, z.

Therefore the formula's will be adepted to Delphi's order.

For completeness sakes I shall mention the formula's in this comments one
more time.

How these formula's where derived I will not explain here because it was a
bit vague and messy but all in all not to bad.
But you don't need to know that... all that matters are the formula's you
can probably derive/come to the conclusion
how these were formed yourself and if not consider these a gift to you from
me !

Formula for index calculation in abstract form:

result.index =
(lower dimension parameter.index * lower dimension parameter.stride) +
(higher dimension parameter.index * lower dimension parameter.volume);

the resulting stride should be 1 until further notice
result.stride = 1;

Valueable notes for you to understand:

Strides always have a minimum of 1.
Volumes always have a minimum of 1.
DimensionCounts always have a minimum of 1.

These ones assure that none of the multiplications are zero-ed out, that
This is why these general formula's work. No matter if the stride is 1 and
the volume is 1 it will still work.
So some unnecessary multiplications with 1 might be done but so be it for
generalities sake.

The last formula you will need is the volume calculation in abstract form
which is actually quite simply:

result.volume = (lower dimension parameter.volume * higher dimension
parameter.volume);

It should be apperent to you now why this will work, but I shall explain it
anyway.

Since the order is from left to right which means low to high this will
work.

The lowest dimension is multiplied with the higher dimension giving a new
volume, in the first two dimension case
this would be the area (a volume with a depth of 1) the next dimension it is
multiplied with would give a new volume.
(a real 3d volume with a depth of depth). The next dimension it is
multiplied against gives a 4D volume. I use
the term "volume" for a lack of a better term for a 4D quanitity. So think
of a volume is that which describes
the entire (coordinate) space (or index space if you will).

I shall also give these two formula's in a somewhat more concrete and short
hand form for direct/easy/correct implementation purposes:

result.stride := 1;
result.index := (ParaA.Index * ParaA.Stride) + (ParaB.Index * ParaA.Volume);
result.volume := (ParaA.Volume * ParaB.Volume);

Now it's time for a test/demonstration program to proof to you that this is
not bullshit, to prove to you that is is valuable
and that this works and to show the power/flexibility of it so you will
hopefully see how usefull this would become
if this became a primary language feature with further support behind it !
=D

However the current code which shall be provided can already be of high
value for current day usasage ! =D

To show the usefullness of it a type will be introduced which shall
encompass this theory and these formula's.

This type shall be called "TDynamicIndex" it's purposes is many-fold:

To construct "dynamic" multi dimensional array indexing automatically.

So that the user programmer can easily adjust it's code to support all kinds
of 1D, 2D, 3D and xD dimensions.

Possibly layed out over a single dimension.

So this type calculates a linear index (which is the final/result index)
which can then be used to access for example
the memory system (which is a 1D entity).

Also to simply the design, the indexed property will be replaced with a
procedure, this illustrates
that the method is applieable to other languages which do not have
properties like c/c++, so this also illustrates
that the method works not because of properties but thanks to operator

However I have no way of setting the index value with indexed properties (a
procedure call would look ugly so I am going
to leave it in ! ) but rename it also a little bit to show what it does.

This test/demonstration program will also function as a verifier to verify
that the calculations are indeed correct.

Conclusion:

It works flawlessly so far.

This is probably one of the, if not the, most important test program and
theory I have ever published.

I hope compiler writers will study it carefully and will hopefully be able
to integrate this "technology" into
their programming languages so that we programmers can finally use "dynamic
pointers" and "dynamic indexes".. so
that we programmers can finally program easily in multiple dimensions which
translate to a linear/1d dimension for memory access.

So that we can finally have multi dimensional array indexing without (*****)
paying
penalties like additional pointers or difficult data
structure construction involving multiple array creations and such.

This demonstration and code will become more valuable as CUDA/OpenCL becomes
more valuable and widely used.

CUDA C/C++ is a perfect example of the
"multi dimensional indexing hell/linear index calculation hell, problem
distribution hell" that CUDA C/C++ programmers are now facing.

This code demonstration demonstrates how to solve it in a flexible way.

The usage example is hard coded to use the dimensions of x: 320 y: 200 z: 60
which represents the classic ms-dos vga
screen and a refresh rate of 60, (which was actually 70 at the time but ok),
so some programmers might be used to these dimensions
and might recgonize some of it.

Hopefully needless to say, this example is not limited to these dimensions,
and any dimension can be used.

A word of caution: integer has been used for volume. It's pretty easy to see
how the volume calculation might overflow
for large dimensions so keep an eye out of that. For now I shall let it be
an integer for speed's sake.

If an overflow occurs for your usage consider using an int64 for larger
dimension support.

This demonstration code has limited itself to indexes to keep things simple.

Strides have also been set to 1.

This represent a byte.

Changing the stride to 4 would represent an integer.

Only the first stride for the X dimension would need to be set to 4.

The other strides should be left alone and be set to 1.

Changing the stride would invalidate the verification program ofcourse.

Changing the code to automatically adept to changes should be obvious, but
for clearities sake
will not be done in version 0.01.

Perhaps version 0.02 might do this, but for now I see little value in it !

I shall now continue working on my TDynamicPointer concept which uses
the concepts presented here in TDynamicIndex and expands on it to support

}

uses
SysUtils;

type
TDynamicIndex = record
private
mIndex : integer;
mStride : integer;
mDimensionCount : integer;
mVolume : integer;

function StoreIndex( ParaIndex : integer ) : TDynamicIndex;
public
constructor Create( ParaDimensionCount : integer; ParaStride : integer );

property IndexOperator[ ParaIndex : integer ] : TDynamicIndex read
StoreIndex; default;

class operator Multiply( ParaA, ParaB : TDynamicIndex ) : TDynamicIndex;
end;

constructor TDynamicIndex.Create( ParaDimensionCount : integer; ParaStride :
integer );
begin
mIndex := 0;
mStride := 1;
mDimensionCount := 1;
mVolume := 1;

begin
end;

if ParaStride > 1 then
begin
mStride := ParaStride;
end;

mVolume := mDimensionCount * mStride;
end;

// store index, and return a copy, not because we want to, but because we
must
// I would much rather return a pointer, but unfortunately calling multiple
operator on pointer derefence won't work well ?!)
// perhaps it should be retried sometime...
function TDynamicIndex.StoreIndex( ParaIndex : integer ) : TDynamicIndex;
begin
mIndex := ParaIndex;
result.mIndex := mIndex;
result.mStride := mStride;
result.mDimensionCount := mDimensionCount;
result.mVolume := mVolume;
end;

class operator TDynamicIndex.Multiply( ParaA, ParaB : TDynamicIndex ) :
TDynamicIndex;
begin
result.mStride := 1;
result.mIndex := (ParaA.mIndex * ParaA.mStride) + (ParaB.mIndex *
ParaA.mVolume);
result.mVolume := (ParaA.mVolume * ParaB.mVolume);
end;

procedure Main;
var
X : TDynamicIndex;
Y : TDynamicIndex;
Z : TDynamicIndex;
Result : TDynamicIndex;
vErrorDetected : boolean;

vX : integer;
vY : integer;
vZ : integer;

vCheck : integer;
begin
X := TDynamicIndex.Create( 320, 1 );
Y := TDynamicIndex.Create( 200, 1 );
Z := TDynamicIndex.Create( 60, 1 );

// first a few single tests to see/show how it works.
Result := X[ 0 ] * Y[ 0 ] * Z[ 1 ]; // should display 320*200*1*1=64000
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 0 ] * Z[ 0 ]; // should display 319*1*1*1=319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 0 ]; // should display 319*1 +
(100*320)*1*1 = 32319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 5 ]; // should display 319*1 +
(100*320)*1 + (5*200*320)*1 = 32319 + 320000 = 352319
writeln( Result.mIndex );

// so far so could so now let's do some exhaustive/full verifications.
writeln('Verification started.');

vErrorDetected := false;
for vZ := 0 to 59 do
begin
for vY := 0 to 199 do
begin
for vX := 0 to 319 do
begin
Result := X[ vX ] * Y[ vY ] * Z[ vZ ];

vCheck := (vZ * 200 * 320) + (vY * 320) + vX;

if Result.mIndex <> vCheck then
begin
writeln('Index calculation error detected at (vX, vY, vZ): ' + '(' +
IntToStr(vX) + ',' + IntToStr(vY) + ',' + IntToStr(vZ) + ')' );
vErrorDetected := true;
end;
end;
end;
end;

writeln('Verification done.');

if vErrorDetected then
begin
writeln('Verification result: errors detected !');
end else
begin
writeln('Verification result: no errors detected.');
end;
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

// *** End of Program ***

Bye,
Skybuck.

Skybuck Flying, Jul 1, 2011

3. ### Skybuck FlyingGuest

I do consider this an important program at least for Delphi and perhaps
other languages.

I am not so sure about C++, perhaps C++ has other features which makes this
less interesting, then again it could still be interesting.

So I have converted the Delphi example to C++ example so C++ programmers can
have a looksy as well and enjoy it for what it's worth !

This code compiles and builds and runs properly in Visual Studio C++ 2010:

// *** Begin of Test Program ***
// TestProgram.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

struct TDynamicIndex
{
// private:
public:
int mIndex;
int mStride;
int mDimensionCount;
int mVolume;

public:

// default constructor
TDynamicIndex();

// custom constructor
TDynamicIndex( int ParaDimensionCount, int ParaStride );

// default destructor, not really needed by included for completeness
sake.
~TDynamicIndex();

// overloaded subscript operator, mimics delphi's indexed properties:
// intrigueing perhaps c++ can do references which delphi might not (or
perhaps c++ will be just as buggy, this can be explored/investigated
further/later),
// for now a value shall be returned to be consistent with delphi and
which was proven to work.
// TDynamicIndex & operator[] (const int ParaIndex);
TDynamicIndex operator[] (const int ParaIndex);

// overloaded multiply operator, mimics delphi's multiply operator
friend TDynamicIndex operator* (const TDynamicIndex &ParaA, const
TDynamicIndex &ParaB );
};

// default constructor
TDynamicIndex::TDynamicIndex()
{
mIndex = 0;
mStride = 1;
mDimensionCount = 1;
mVolume = 1;
}

// custom constructor
TDynamicIndex::TDynamicIndex( int ParaDimensionCount, int ParaStride )
{
mIndex = 0;
mStride = 1;
mDimensionCount = 1;
mVolume = 1;

{
}

if (ParaStride > 1)
{
mStride = ParaStride;
}

mVolume = mDimensionCount * mStride;
}

// default destructor
TDynamicIndex::~TDynamicIndex()
{

}

TDynamicIndex TDynamicIndex:perator[] (const int ParaIndex)
{
struct TDynamicIndex result;

mIndex = ParaIndex;
result.mIndex = mIndex;
result.mStride = mStride;
result.mDimensionCount = mDimensionCount;
result.mVolume = mVolume;

return result;
}

TDynamicIndex operator* (const TDynamicIndex &ParaA, const TDynamicIndex
&ParaB )
{
struct TDynamicIndex result;

result.mStride = 1;
result.mIndex = (ParaA.mIndex * ParaA.mStride) + (ParaB.mIndex *
ParaA.mVolume);
result.mVolume = (ParaA.mVolume * ParaB.mVolume);

return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
TDynamicIndex X;
TDynamicIndex Y;
TDynamicIndex Z;
TDynamicIndex Result;
bool vErrorDetected;

int vX;
int vY;
int vZ;

int vCheck;

X = TDynamicIndex( 320, 1 );
Y = TDynamicIndex( 200, 1 );
Z = TDynamicIndex( 60, 1 );

// first a few single tests to see/show how it works.
Result = X[ 0 ] * Y[ 0 ] * Z[ 1 ]; // should display 320*200*1*1=64000
printf( "%d \n", Result.mIndex );

Result = X[ 319 ] * Y[ 0 ] * Z[ 0 ]; // should display 319*1*1*1=319
printf( "%d \n", Result.mIndex );

Result = X[ 319 ] * Y[ 100 ] * Z[ 0 ]; // should display 319*1 +
(100*320)*1*1 = 32319
printf( "%d \n", Result.mIndex );

Result = X[ 319 ] * Y[ 100 ] * Z[ 5 ]; // should display 319*1 + (100*320)*1
+ (5*200*320)*1 = 32319 + 320000 = 352319
printf( "%d \n", Result.mIndex );

// so far so good so now let's do some exhaustive/full verifications.
printf("Verification started.\n");

vErrorDetected = false;
for (vZ=0; vZ <= 59; vZ++)
{
for (vY=0; vY <= 199; vY++)
{
for (vX=0; vX <= 319; vX++)
{
Result = X[ vX ] * Y[ vY ] * Z[ vZ ];

vCheck = (vZ * 200 * 320) + (vY * 320) + vX;

if (Result.mIndex != vCheck)
{
printf("Index calculation error detected at (vX, vY, vZ): (%d, %d, %d)
\n", vX, vY, vZ );
vErrorDetected = true;
}
}
}
}

printf("Verification done.\n");

if (vErrorDetected == true)
{
printf("Verification result: errors detected !\n");
} else
{
printf("Verification result: no errors detected.\n");
};

return 0; // set a breakpoint here to pause the program and view the output.
}

// *** End of Test Program ***

Bye,

Skybuck.

Skybuck Flying, Jul 1, 2011
4. ### Billy MaysGuest

Re: Dynamic indexing (multi-dimensional-indexing) (probably my mostimportant/valuable posting up to this date)

On 7/1/2011 12:18 AM, Skybuck Flying wrote:
> I do consider this an important program at least for Delphi and perhaps
> ...
>

Unless you have a question or a C program, please don't post to c.l.c

--
Bill

Billy Mays, Jul 1, 2011
5. ### Skybuck FlyingGuest

"Billy Mays" wrote in message news:iujma5\$qkg\$...

On 7/1/2011 12:18 AM, Skybuck Flying wrote:
> I do consider this an important program at least for Delphi and perhaps
> ...
>

"
Unless you have a question or a C program, please don't post to c.l.c
"

I have a question for you:

Are there C compilers which were written in C++ ?

Bye,
Skybuck.

Skybuck Flying, Jul 1, 2011
6. ### Skybuck FlyingGuest

Example updated for pointer support.

For pointer support an add-operator has been added, the idea is quite
simple:

x[ ... ] * y[ ... ] * z[...] + pointer := 100;

In practice because of left-side language limitations this has to be:

PSomeType( x[...] * y[...] * z[...] + pointer )^ := SomeType;

Furthermore my apperently favorite newsgroup has been added as well for a
total of 6 newsgroups which is 1 over the recommended ammount but worth
it for this posting

I shall leave original posting intact and only add the interface and
implementation code for this new feature, integrating this into the example
is left as an exercise for the reader and shouldn't be too difficult

TDynamicIndex = record
private
< ... snip... >
public
< ... snip... >
// *** NEW CODE ***:
class operator Add( ParaA : TDynamicIndex; ParaB : pointer ) : pointer;
end;

<... snip... <

// *** NEW CODE ***

class operator TDynamicIndex.Add( ParaA : TDynamicIndex; ParaB : pointer ) :
pointer;
begin
result := pointer( longword(ParaB) + longword(ParaA.mIndex) );
end;

Perhaps later I will convert this new code to C/C++ as well for now I am too
lazy for it and what to proceed with my Delphi code or so

But later I will probably need C/C++ code as well

// Original posting:

This is probably the most important test/demonstration program I ever wrote
and published/released onto the internet (up to this date (7 juli 2011)).

I hope compiler writers/programming language designers will study it
carefully and integrate it into their language.
(It's a bit long/lengthy read by well worth it ! Also casual programmers can
benefit from it as well !)

See comments for more details about the usefullness and the value of it for
present day and the future.

Also see test program way down below for usage example and verification
program.

Especially the formula's are very important.

"dynamic pointer indexing"

Which takes this step a bit further to provide direct memory access
(concept/thread which is still under development).

This posting takes a step back to evaluate and verify the formula's to be
used in "dynamic pointer indexing".

For now this example/concept can be thought of as "dynamic indexing".

Which can also be thought of as "multi-dimensional indexing".

A future which is currently missing in C/C++/CUDA C/C++ and in a way also
Delphi since Delphi requires a pointer array per additional dimension.

The technique illustrated below needs none of that Delphi overhead and
introduces it's own much smaller overhead, memory-wise, it does introduce a

There is also a hidden potential problem if parenthesis would be used.

A small explanation for the people that did not follow the other thread
mentioned:

The multiply operator is used as a passing mechanism to calculate a linear
index for the multi dimension indexes.

// *** Begin of Program ***

program TestProgram;

{\$APPTYPE CONSOLE}

{

Test dynamic multi dimensional index calculations

version 0.01 created on 1 juli 2011 by Skybuck Flying

To illustrate the theory behind dynamic multi dimension index calculations I
shall now
create a little test/demonstration program to show you how to write this
code and what
formula's to use.

The example deviates from the written theory so far in regard to the order,
the order
in the theory was assumed to be z, y, x however delphi's order appears to be
x, y, z.

Therefore the formula's will be adepted to Delphi's order.

For completeness sakes I shall mention the formula's in this comments one
more time.

How these formula's where derived I will not explain here because it was a
bit vague and messy but all in all not to bad.
But you don't need to know that... all that matters are the formula's you
can probably derive/come to the conclusion
how these were formed yourself and if not consider these a gift to you from
me !

Formula for index calculation in abstract form:

result.index =
(lower dimension parameter.index * lower dimension parameter.stride) +
(higher dimension parameter.index * lower dimension parameter.volume);

the resulting stride should be 1 until further notice
result.stride = 1;

Valueable notes for you to understand:

Strides always have a minimum of 1.
Volumes always have a minimum of 1.
DimensionCounts always have a minimum of 1.

These ones assure that none of the multiplications are zero-ed out, that
This is why these general formula's work. No matter if the stride is 1 and
the volume is 1 it will still work.
So some unnecessary multiplications with 1 might be done but so be it for
generalities sake.

The last formula you will need is the volume calculation in abstract form
which is actually quite simply:

result.volume = (lower dimension parameter.volume * higher dimension
parameter.volume);

It should be apperent to you now why this will work, but I shall explain it
anyway.

Since the order is from left to right which means low to high this will
work.

The lowest dimension is multiplied with the higher dimension giving a new
volume, in the first two dimension case
this would be the area (a volume with a depth of 1) the next dimension it is
multiplied with would give a new volume.
(a real 3d volume with a depth of depth). The next dimension it is
multiplied against gives a 4D volume. I use
the term "volume" for a lack of a better term for a 4D quanitity. So think
of a volume is that which describes
the entire (coordinate) space (or index space if you will).

I shall also give these two formula's in a somewhat more concrete and short
hand form for direct/easy/correct implementation purposes:

result.stride := 1;
result.index := (ParaA.Index * ParaA.Stride) + (ParaB.Index * ParaA.Volume);
result.volume := (ParaA.Volume * ParaB.Volume);

Now it's time for a test/demonstration program to proof to you that this is
not bullshit, to prove to you that is is valuable
and that this works and to show the power/flexibility of it so you will
hopefully see how usefull this would become
if this became a primary language feature with further support behind it !
=D

However the current code which shall be provided can already be of high
value for current day usasage ! =D

To show the usefullness of it a type will be introduced which shall
encompass this theory and these formula's.

This type shall be called "TDynamicIndex" it's purposes is many-fold:

To construct "dynamic" multi dimensional array indexing automatically.

So that the user programmer can easily adjust it's code to support all kinds
of 1D, 2D, 3D and xD dimensions.

Possibly layed out over a single dimension.

So this type calculates a linear index (which is the final/result index)
which can then be used to access for example
the memory system (which is a 1D entity).

Also to simply the design, the indexed property will be replaced with a
procedure, this illustrates
that the method is applieable to other languages which do not have
properties like c/c++, so this also illustrates
that the method works not because of properties but thanks to operator

However I have no way of setting the index value with indexed properties (a
procedure call would look ugly so I am going
to leave it in ! ) but rename it also a little bit to show what it does.

This test/demonstration program will also function as a verifier to verify
that the calculations are indeed correct.

Conclusion:

It works flawlessly so far.

This is probably one of the, if not the, most important test program and
theory I have ever published.

I hope compiler writers will study it carefully and will hopefully be able
to integrate this "technology" into
their programming languages so that we programmers can finally use "dynamic
pointers" and "dynamic indexes".. so
that we programmers can finally program easily in multiple dimensions which
translate to a linear/1d dimension for memory access.

So that we can finally have multi dimensional array indexing without
paying
penalties like additional pointers or difficult data
structure construction involving multiple array creations and such.

This demonstration and code will become more valuable as CUDA/OpenCL becomes
more valuable and widely used.

CUDA C/C++ is a perfect example of the
"multi dimensional indexing hell/linear index calculation hell, problem
distribution hell" that CUDA C/C++ programmers are now facing.

This code demonstration demonstrates how to solve it in a flexible way.

The usage example is hard coded to use the dimensions of x: 320 y: 200 z: 60
which represents the classic ms-dos vga
screen and a refresh rate of 60, (which was actually 70 at the time but ok),
so some programmers might be used to these dimensions
and might recgonize some of it.

Hopefully needless to say, this example is not limited to these dimensions,
and any dimension can be used.

A word of caution: integer has been used for volume. It's pretty easy to see
how the volume calculation might overflow
for large dimensions so keep an eye out of that. For now I shall let it be
an integer for speed's sake.

If an overflow occurs for your usage consider using an int64 for larger
dimension support.

This demonstration code has limited itself to indexes to keep things simple.

Strides have also been set to 1.

This represent a byte.

Changing the stride to 4 would represent an integer.

Only the first stride for the X dimension would need to be set to 4.

The other strides should be left alone and be set to 1.

Changing the stride would invalidate the verification program ofcourse.

Changing the code to automatically adept to changes should be obvious, but
for clearities sake
will not be done in version 0.01.

Perhaps version 0.02 might do this, but for now I see little value in it !

I shall now continue working on my TDynamicPointer concept which uses
the concepts presented here in TDynamicIndex and expands on it to support

}

uses
SysUtils;

type
TDynamicIndex = record
private
mIndex : integer;
mStride : integer;
mDimensionCount : integer;
mVolume : integer;

function StoreIndex( ParaIndex : integer ) : TDynamicIndex;
public
constructor Create( ParaDimensionCount : integer; ParaStride : integer );

property IndexOperator[ ParaIndex : integer ] : TDynamicIndex read
StoreIndex; default;

class operator Multiply( ParaA, ParaB : TDynamicIndex ) : TDynamicIndex;
end;

constructor TDynamicIndex.Create( ParaDimensionCount : integer; ParaStride :
integer );
begin
mIndex := 0;
mStride := 1;
mDimensionCount := 1;
mVolume := 1;

begin
end;

if ParaStride > 1 then
begin
mStride := ParaStride;
end;

mVolume := mDimensionCount * mStride;
end;

// store index, and return a copy, not because we want to, but because we
must
// I would much rather return a pointer, but unfortunately calling multiple
operator on pointer derefence won't work well ?!)
// perhaps it should be retried sometime...
function TDynamicIndex.StoreIndex( ParaIndex : integer ) : TDynamicIndex;
begin
mIndex := ParaIndex;
result.mIndex := mIndex;
result.mStride := mStride;
result.mDimensionCount := mDimensionCount;
result.mVolume := mVolume;
end;

class operator TDynamicIndex.Multiply( ParaA, ParaB : TDynamicIndex ) :
TDynamicIndex;
begin
result.mStride := 1;
result.mIndex := (ParaA.mIndex * ParaA.mStride) + (ParaB.mIndex *
ParaA.mVolume);
result.mVolume := (ParaA.mVolume * ParaB.mVolume);
end;

procedure Main;
var
X : TDynamicIndex;
Y : TDynamicIndex;
Z : TDynamicIndex;
Result : TDynamicIndex;
vErrorDetected : boolean;

vX : integer;
vY : integer;
vZ : integer;

vCheck : integer;
begin
X := TDynamicIndex.Create( 320, 1 );
Y := TDynamicIndex.Create( 200, 1 );
Z := TDynamicIndex.Create( 60, 1 );

// first a few single tests to see/show how it works.
Result := X[ 0 ] * Y[ 0 ] * Z[ 1 ]; // should display 320*200*1*1=64000
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 0 ] * Z[ 0 ]; // should display 319*1*1*1=319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 0 ]; // should display 319*1 +
(100*320)*1*1 = 32319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 5 ]; // should display 319*1 +
(100*320)*1 + (5*200*320)*1 = 32319 + 320000 = 352319
writeln( Result.mIndex );

// so far so could so now let's do some exhaustive/full verifications.
writeln('Verification started.');

vErrorDetected := false;
for vZ := 0 to 59 do
begin
for vY := 0 to 199 do
begin
for vX := 0 to 319 do
begin
Result := X[ vX ] * Y[ vY ] * Z[ vZ ];

vCheck := (vZ * 200 * 320) + (vY * 320) + vX;

if Result.mIndex <> vCheck then
begin
writeln('Index calculation error detected at (vX, vY, vZ): ' + '(' +
IntToStr(vX) + ',' + IntToStr(vY) + ',' + IntToStr(vZ) + ')' );
vErrorDetected := true;
end;
end;
end;
end;

writeln('Verification done.');

if vErrorDetected then
begin
writeln('Verification result: errors detected !');
end else
begin
writeln('Verification result: no errors detected.');
end;
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

// *** End of Program ***

Bye,
Skybuck.

Skybuck Flying, Jul 2, 2011
7. ### Skybuck FlyingGuest

Also for completeness sake I did add this line to the multiply operator to
provide a valid dimension count, this is also an interesting idea so that
multiplieing dimensions can also be tought of as creating one new single
dimension with a large count:

// not really needed for current code but let's do it anyway in case someone
or somebody or something might need it in the future
// at least this can give the user some kind of idea how many elements there
are in the multiplied dimension/plane/volume/etc.
// so that the record/variable/multiplication can also be considered a
lineair dimension which in itself is interesting too !
result.mDimensionCount := ParaA.mDimensionCount * ParaB.mDimensionCount;

Bye,
Skybuck.

Skybuck Flying, Jul 2, 2011
8. ### Skybuck FlyingGuest

C/C++ example updated with pointer support and valid dimension count for
multiplied dimensions.

int used to typecasts pointers, could be changed to size_t or so.

(Example assumes 32 bit machine).

Idea is to use add-operator for pointer support as follows:

x[...] * y[...] * z[...] + pointer = 100;

// *** Begin of Test Program ***

// TestProgram.cpp : Defines the entry point for the console application.
//

// Test dynamic indexing (multi dimensional indexing)

// Converted from Delph to C++ on 1 juli 2011 by Skybuck Flying.

// version 0.01

// version 0.03 updated with pointer support and valid DimensionCount for
multiplied dimensions.

#include "stdafx.h"
#include "stdlib.h" // for malloc and free

struct TDynamicIndex
{
// private:
public:
int mIndex;
int mStride;
int mDimensionCount;
int mVolume;

public:

// default constructor
TDynamicIndex();

// custom constructor
TDynamicIndex( int ParaDimensionCount, int ParaStride );

// default destructor, not really needed but included for completeness
sake.
~TDynamicIndex();

// overloaded subscript operator, mimics delphi's indexed properties:
// intrigueing perhaps c++ can do references which delphi might not (or
perhaps c++ will be just as buggy, this can be explored/investigated
further/later),
// for now a value shall be returned to be consistent with delphi and
which was proven to work.
// TDynamicIndex & operator[] (const int ParaIndex);
TDynamicIndex operator[] (const int ParaIndex);

// overloaded multiply operator, mimics delphi's multiply operator
friend TDynamicIndex operator* (const TDynamicIndex &ParaA, const
TDynamicIndex &ParaB );

friend void* operator+ (const TDynamicIndex &ParaA, const void* ParaB );

};

// default constructor
TDynamicIndex::TDynamicIndex()
{
mIndex = 0;
mStride = 1;
mDimensionCount = 1;
mVolume = 1;
}

// custom constructor
TDynamicIndex::TDynamicIndex( int ParaDimensionCount, int ParaStride )
{
mIndex = 0;
mStride = 1;
mDimensionCount = 1;
mVolume = 1;

{
}

if (ParaStride > 1)
{
mStride = ParaStride;
}

mVolume = mDimensionCount * mStride;
}

// default destructor
TDynamicIndex::~TDynamicIndex()
{

}

TDynamicIndex TDynamicIndex:perator[] (const int ParaIndex)
{
struct TDynamicIndex result;

mIndex = ParaIndex;
result.mIndex = mIndex;
result.mStride = mStride;
result.mDimensionCount = mDimensionCount;
result.mVolume = mVolume;

return result;
}

TDynamicIndex operator* (const TDynamicIndex &ParaA, const TDynamicIndex
&ParaB )
{
struct TDynamicIndex result;

result.mStride = 1;
result.mIndex = (ParaA.mIndex * ParaA.mStride) + (ParaB.mIndex *
ParaA.mVolume);
result.mVolume = (ParaA.mVolume * ParaB.mVolume);
result.mDimensionCount = ParaA.mDimensionCount * ParaB.mDimensionCount;

return result;
}

void* operator+ (const TDynamicIndex &ParaA, const void* ParaB )
{
return ( (void *)( int(ParaB) + ParaA.mIndex ) );
}

int _tmain(int argc, _TCHAR* argv[])
{
void *Memory;

TDynamicIndex X;
TDynamicIndex Y;
TDynamicIndex Z;
TDynamicIndex Result;
bool vErrorDetected;

int vX;
int vY;
int vZ;

int vCheck;

Memory = malloc( 320 * 200 * 60 * 1 );

X = TDynamicIndex( 320, 1 );
Y = TDynamicIndex( 200, 1 );
Z = TDynamicIndex( 60, 1 );

// usage example for pointer functionality:
*((unsigned char*)( X[33] * Y[114] * Z[43] + Memory )) = 233;

vX = 33;
vY = 114;
vZ = 43;

vCheck = ( (vZ * 200 * 320) + (vY * 320) + vX ) * 1;

printf("%d \n", *((unsigned char *)( int(Memory) + vCheck )) ); // should
display 233

// first a few single tests to see/show how it works.
Result = X[ 0 ] * Y[ 0 ] * Z[ 1 ]; // should display 320*200*1*1=64000
printf( "%d \n", Result.mIndex );

Result = X[ 319 ] * Y[ 0 ] * Z[ 0 ]; // should display 319*1*1*1=319
printf( "%d \n", Result.mIndex );

Result = X[ 319 ] * Y[ 100 ] * Z[ 0 ]; // should display 319*1 +
(100*320)*1*1 = 32319
printf( "%d \n", Result.mIndex );

Result = X[ 319 ] * Y[ 100 ] * Z[ 5 ]; // should display 319*1 + (100*320)*1
+ (5*200*320)*1 = 32319 + 320000 = 352319
printf( "%d \n", Result.mIndex );

// so far so good so now let's do some exhaustive/full verifications.
printf("Verification started.\n");

vErrorDetected = false;
for (vZ=0; vZ <= 59; vZ++)
{
for (vY=0; vY <= 199; vY++)
{
for (vX=0; vX <= 319; vX++)
{
Result = X[ vX ] * Y[ vY ] * Z[ vZ ];

vCheck = (vZ * 200 * 320) + (vY * 320) + vX;

if (Result.mIndex != vCheck)
{
printf("Index calculation error detected at (vX, vY, vZ): (%d, %d, %d)
\n", vX, vY, vZ );
vErrorDetected = true;
}
}
}
}

printf("Verification done.\n");

if (vErrorDetected == true)
{
printf("Verification result: errors detected !\n");
} else
{
printf("Verification result: no errors detected.\n");
};

free( Memory );

return 0; // set a breakpoint here to pause the program and view the output.
}

// *** End of Test Program ***

Bye,

Skybuck.

Skybuck Flying, Jul 2, 2011
9. ### Skybuck FlyingGuest

"
"

However I do not know how to configure Visual Studio 2010 to allow C only.

Furthermore it seems a bit a waste of time at least for me, perhaps not for
others which might be stuck using C compilers.

Here is a hint how it could be created, the function would look like:

output pointer FunctionName( input pointer, dimension structures, ... );

^ This function does the same as the multiply operator except it does it for
an entire array of parameters.

The parameters are then passed in in the following call:

FunctionName( Memory, X, 34, Y, 66, Z, 5 );

The numbers represents the indexes for x,y,z.

The x,y,z are the structures containing information like dimension size.

A create routine could be created which mimics the constructor:

CreateDynamicIndex( &X, 320, 1 ); // sets dimension and stride
CreateDynamicIndex( &Y, 200, 1 );
CreateDynamicIndex( &Z, 5, 1 );

The FunctionName could be named:

Pointer = MultiplyDynamicIndexes( Memory, X, 34, Y, 66, Z, 5 );

The rest should be obvious

Bye,
Skybuck.

Skybuck Flying, Jul 2, 2011
10. ### Shao MillerGuest

Re: Dynamic indexing (multi-dimensional-indexing) (probably my mostimportant/valuable posting up to this date)

On 7/2/2011 12:21 AM, Skybuck Flying wrote:
> C/C++ example updated with pointer support and valid dimension count for
> multiplied dimensions.
>

Given that posters have suggested that "C/C++" is not really
appropriate, why do you continue to use it?

> #include "stdlib.h" // for malloc and free
> [...code...]
> struct TDynamicIndex
> {
> // private:
> public:
> int mIndex;
> int mStride;
> int mDimensionCount;
> int mVolume;
>
> public:
> [...code...]

This isn't C.

Shao Miller, Jul 2, 2011
11. ### Keith ThompsonGuest

Shao Miller <> writes:
> On 7/2/2011 12:21 AM, Skybuck Flying wrote:
>> C/C++ example updated with pointer support and valid dimension count for
>> multiplied dimensions.

>
> Given that posters have suggested that "C/C++" is not really
> appropriate, why do you continue to use it?

[...]

--
Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Keith Thompson, Jul 2, 2011
12. ### Shao MillerGuest

Re: Dynamic indexing (multi-dimensional-indexing) (probably my mostimportant/valuable posting up to this date)

On 7/2/2011 1:43 AM, Keith Thompson wrote:
> Shao Miller<> writes:
>> On 7/2/2011 12:21 AM, Skybuck Flying wrote:
>>> C/C++ example updated with pointer support and valid dimension count for
>>> multiplied dimensions.

>>
>> Given that posters have suggested that "C/C++" is not really
>> appropriate, why do you continue to use it?

>
> Please don't feed the troll.
>
> [...]
>

I enjoy sticking to the C as much as possible, but felt the need to
probe with that one. These posts don't strike me as DadaDodo output[1],
nor as minimal-effort baits, nor as copy'n'pastes of old posts, nor as
being especially crafted for strong emotional reactions. I haven't been
Usenetting forever, so perhaps I'll learn otherwise.

Unfortunately, there was no response to this question.

Fortunately, the response (there was one) seemed to have to do with C,
seemed to involve effort (always a good sign) and seemed sincere.

The response has given me something to think about. Namely, how one
might go about providing an interface for something akin to pointers to
single- and multi-dimensional arrays with non-constant element counts.
I could hardly understand the majority of the other posts, so maybe
"Rorschach ink-blot test" applies?

P. S. Blatant spam is one thing, but for the rest, even if I were to
find someone extremely irritating, uncivil, or disagreeable I think
"plonking" has led to a dysfunctional Usenet experience, personally. If
one must, one must, I guess... So anyway, back to C (I hope)!

Shao Miller, Jul 2, 2011
13. ### Skybuck FlyingGuest

This is the simplified and optimized version 0.04, it uses generics to get
rid of the stride field, instead the sizeof the generic is used.

The volume field is also no longer required because everything is done via
call by value, so dimension can safely be modified/multiplied while leaving
orignals intact.

The DimensionCount field is renamed to simply dimension.

The stride (/element size) is only applied when the add-operator with a
pointer is used. Otherwise the user should take care to apply the stride
(/element size) manually.

Perhaps there is a way to make the code even more user-friendly... I would
like to get rid of the typecast in the usage code...

// *** Begin of Test Program ***

program TestProgram;

{\$APPTYPE CONSOLE}

{

TDynamicIndex

version 0.03 created on 2 july 2011 by Skybuck Flying.

+ multiplieing dimensions can now also be thought of as creating a new
larger single dimension.
(a valid DimensionCount is now provided by the multiply operator)

}

{

TDynamicIndex<T>

version 0.04 created on 2 july 2011 by Skybuck Flying.

Simplified and optimized version, stride is left out of multiply operation
applied when applieing pointer conversion.

If there would be a pointer for each type then stride could be left out
completely,
perhaps generic might be of some use. Yup generics were usefull.

This will probably make volume also somewhat redundant so can be left out as
well.

And DimensionCount will be renamed to Dimension.

The interesting thing is that Dimension will remain as it was initialized
since the multiply operation returns a value, so it's like call by value.
Which means the dimension field for the result can be changed and the
originals
will remain intact.

}

uses
SysUtils;

type
TDynamicIndex<T> = record
private
mIndex : integer;
mDimension : integer;

function StoreIndex( ParaIndex : integer ) : TDynamicIndex<T>;
public
constructor Create( ParaDimension : integer );

property IndexOperator[ ParaIndex : integer ] : TDynamicIndex<T> read
StoreIndex; default;

class operator Multiply( ParaA, ParaB : TDynamicIndex<T> ) :
TDynamicIndex<T>;

class operator Add( ParaA : TDynamicIndex<T>; ParaB : pointer ) : pointer;
end;

constructor TDynamicIndex<T>.Create( ParaDimension : integer );
begin
mIndex := 0;
mDimension := 1;

begin
end;
end;

// store index, and return a copy, not because we want to, but because we
must
// I would much rather return a pointer, but unfortunately calling multiple
operator on pointer derefence won't work well ?!)
// perhaps it should be retried sometime...
function TDynamicIndex<T>.StoreIndex( ParaIndex : integer ) :
TDynamicIndex<T>;
begin
mIndex := ParaIndex;
result.mIndex := mIndex;
result.mDimension := mDimension;
end;

class operator TDynamicIndex<T>.Multiply( ParaA, ParaB : TDynamicIndex<T> )
: TDynamicIndex<T>;
begin
result.mIndex := ParaA.mIndex + (ParaA.mDimension * ParaB.mIndex);
result.mDimension := ParaA.mDimension * ParaB.mDimension;
end;

class operator TDynamicIndex<T>.Add( ParaA : TDynamicIndex<T>; ParaB :
pointer ) : pointer;
begin
result := pointer( longword(ParaB) + longword(ParaA.mIndex) * SizeOf(T) );
end;

procedure Main;
var
Memory : pointer;

X : TDynamicIndex<integer>;
Y : TDynamicIndex<integer>;
Z : TDynamicIndex<integer>;

Result : TDynamicIndex<integer>;
vErrorDetected : boolean;

vX : integer;
vY : integer;
vZ : integer;

vCheck : integer;
begin
GetMem( Memory, 320*200*60*4 );

X := TDynamicIndex<integer>.Create( 320 );
Y := TDynamicIndex<integer>.Create( 200 );
Z := TDynamicIndex<integer>.Create( 60 );

// Pbyte( X[ 122 ] * Y[ 66 ] * Z[ 5 ] + Memory )^ := 233;
Pinteger( X[ 122 ] * Y[ 66 ] * Z[ 5 ] + Memory )^ := 666666666;

vX := 122;
vY := 66;
vZ := 5;
vCheck := ( (vZ * 200 * 320) + (vY * 320) + vX ) * 4;

writeln( Pinteger( longword(Memory) + longword(vCheck) )^ ); // should
display 666666666

// verification is for stride 1 (byte)
// first a few single tests to see/show how it works.
Result := X[ 0 ] * Y[ 0 ] * Z[ 1 ]; // should display 320*200*1*1=64000
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 0 ] * Z[ 0 ]; // should display 319*1*1*1=319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 0 ]; // should display 319*1 +
(100*320)*1*1 = 32319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 5 ]; // should display 319*1 +
(100*320)*1 + (5*200*320)*1 = 32319 + 320000 = 352319
writeln( Result.mIndex );

// so far so good so now let's do some exhaustive/full verifications.
writeln('Verification started.');

vErrorDetected := false;
for vZ := 0 to 59 do
begin
for vY := 0 to 199 do
begin
for vX := 0 to 319 do
begin
Result := X[ vX ] * Y[ vY ] * Z[ vZ ];

vCheck := ( (vZ * 200 * 320) + (vY * 320) + vX ) * 4;

if Result.mIndex * 4 <> vCheck then
begin
writeln('Index calculation error detected at (vX, vY, vZ): ' + '(' +
IntToStr(vX) + ',' + IntToStr(vY) + ',' + IntToStr(vZ) + ')' );
vErrorDetected := true;
end;
end;
end;
end;

writeln('Verification done.');

if vErrorDetected then
begin
writeln('Verification result: errors detected !');
end else
begin
writeln('Verification result: no errors detected.');
end;

FreeMem( Memory, 320*200*60*4 );
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

// *** End of Test Program ***

Bye,
Skybuck.

Skybuck Flying, Jul 2, 2011
14. ### Skybuck FlyingGuest

There is ofcourse a little drawback to version 0.04, it becomes compile time
bound again because of the generic type.

The stride field/version was more runtime friendly and flexible.

None the less, the same code can be used with stride, but care most be taken
to apply the stride properly.

It does create a bit of problem... which stride to use... a,b,c or d etc...
however if assumption is that all use the same element size then that is not
a problem, an assert could be used to check for it.

Bye,
Skybuck.

Skybuck Flying, Jul 2, 2011
15. ### Skybuck FlyingGuest

I have modified version 0.04 further so I can now write:

( X[ 122 ] * Y[ 66 ] * Z[ 5 ] ).Value[ Memory ] := 666666666;

This is done by using another property called "value".

Apperently for some reason it is possible to call a property for the
left-side.

Then memory is passed like it was an index, but instead it's used as the

Inside the handler the stride (sizeof<t>) is applied.

This is already a whole lot better since this gets rid of the nasting
type-casting.

Perhaps I will re-introduce the memory to the type so that just .Value could
be used for a shorter notation, that would be cool.

I am not yet statisfied with modified version 0.04 so I will probably create
another one, but for now here is version 0.04 (modified)

// add operator is no longer used.

// *** Begin of Test Program ***

program TestProgram;

{\$APPTYPE CONSOLE}

{

TDynamicIndex

version 0.03 created on 2 july 2011 by Skybuck Flying.

+ multiplieing dimensions can now also be thought of as creating a new
larger single dimension.
(a valid DimensionCount is now provided by the multiply operator)

}

{

TDynamicIndex<T>

version 0.04 created on 2 july 2011 by Skybuck Flying.

Simplified and optimized version, stride is left out of multiply operation
applied when applieing pointer conversion.

If there would be a pointer for each type then stride could be left out
completely,
perhaps generic might be of some use. Yup generics were usefull.

This will probably make volume also somewhat redundant so can be left out as
well.

And DimensionCount will be renamed to Dimension.

The interesting thing is that Dimension will remain as it was initialized
since the multiply operation returns a value, so it's like call by value.
Which means the dimension field for the result can be changed and the
originals
will remain intact.

There is ofcourse a little drawback to version 0.04, it becomes compile time
bound again because of the generic type.

The stride field/version was more runtime friendly and flexible.

None the less, the same code can be used with stride, but care most be taken
to apply the stride properly.

It does create a bit of problem... which stride to use... a,b,c or d etc...
however if assumption is that all use
the same element size then that is not a problem, an assert could be used to
check for it.

}

uses
SysUtils;

type
TDynamicIndex<T> = record
private
mIndex : integer;
mDimension : integer;

function StoreIndex( ParaIndex : integer ) : TDynamicIndex<T>;

procedure SetValue( ParaMemory : pointer; ParaValue : T );
public
constructor Create( ParaDimension : integer );

property IndexOperator[ ParaIndex : integer ] : TDynamicIndex<T> read
StoreIndex; default;
property Value[ ParaMemory : pointer ] : T write SetValue;

class operator Multiply( ParaA, ParaB : TDynamicIndex<T> ) :
TDynamicIndex<T>;

// class operator Add( ParaA : TDynamicIndex<T>; ParaB : pointer ) :
pointer;
end;

procedure TDynamicIndex<T>.SetValue( ParaMemory : pointer; ParaValue : T );
begin
T( pointer( longword(ParaMemory) + ( longword(mIndex) * SizeOf(T) ) )^ ) :=
ParaValue

// writeln( '!!!', mIndex, '!!!' );
end;

constructor TDynamicIndex<T>.Create( ParaDimension : integer );
begin
mIndex := 0;
mDimension := 1;

begin
end;
end;

// store index, and return a copy, not because we want to, but because we
must
// I would much rather return a pointer, but unfortunately calling multiple
operator on pointer derefence won't work well ?!)
// perhaps it should be retried sometime...
function TDynamicIndex<T>.StoreIndex( ParaIndex : integer ) :
TDynamicIndex<T>;
begin
mIndex := ParaIndex;
result.mIndex := mIndex;
result.mDimension := mDimension;
end;

class operator TDynamicIndex<T>.Multiply( ParaA, ParaB : TDynamicIndex<T> )
: TDynamicIndex<T>;
begin
result.mIndex := ParaA.mIndex + (ParaA.mDimension * ParaB.mIndex);
result.mDimension := ParaA.mDimension * ParaB.mDimension;
end;

(*
class operator TDynamicIndex<T>.Add( ParaA : TDynamicIndex<T>; ParaB :
pointer ) : pointer;
begin
result := pointer( longword(ParaB) + longword(ParaA.mIndex) * SizeOf(T) );
end;
*)

procedure Main;
var
Memory : pointer;

X : TDynamicIndex<integer>;
Y : TDynamicIndex<integer>;
Z : TDynamicIndex<integer>;

Result : TDynamicIndex<integer>;
vErrorDetected : boolean;

vX : integer;
vY : integer;
vZ : integer;

vCheck : integer;
begin
GetMem( Memory, 320*200*60*4 );

X := TDynamicIndex<integer>.Create( 320 );
Y := TDynamicIndex<integer>.Create( 200 );
Z := TDynamicIndex<integer>.Create( 60 );

// Pbyte( X[ 122 ] * Y[ 66 ] * Z[ 5 ] + Memory )^ := 233;
// Pinteger( X[ 122 ] * Y[ 66 ] * Z[ 5 ] + Memory )^ := 666666666;

// ( X[ 122 ] * Y[ 66 ] * Z[ 5 ] ).Value[0] := 666666666;

// ( X[ 122 ] * Y[ 66 ] * Z[ 5 ] ).Value2 := 666666666;

( X[ 122 ] * Y[ 66 ] * Z[ 5 ] ).Value[ Memory ] := 666666666;

vX := 122;
vY := 66;
vZ := 5;
vCheck := ( (vZ * 200 * 320) + (vY * 320) + vX ) * 4;

writeln( Pinteger( longword(Memory) + longword(vCheck) )^ ); // should
display 666666666

// verification is for stride 1 (byte)
// first a few single tests to see/show how it works.
Result := X[ 0 ] * Y[ 0 ] * Z[ 1 ]; // should display 320*200*1*1=64000
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 0 ] * Z[ 0 ]; // should display 319*1*1*1=319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 0 ]; // should display 319*1 +
(100*320)*1*1 = 32319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 5 ]; // should display 319*1 +
(100*320)*1 + (5*200*320)*1 = 32319 + 320000 = 352319
writeln( Result.mIndex );

// so far so good so now let's do some exhaustive/full verifications.
writeln('Verification started.');

vErrorDetected := false;
for vZ := 0 to 59 do
begin
for vY := 0 to 199 do
begin
for vX := 0 to 319 do
begin
Result := X[ vX ] * Y[ vY ] * Z[ vZ ];

vCheck := ( (vZ * 200 * 320) + (vY * 320) + vX ) * 4;

if Result.mIndex * 4 <> vCheck then
begin
writeln('Index calculation error detected at (vX, vY, vZ): ' + '(' +
IntToStr(vX) + ',' + IntToStr(vY) + ',' + IntToStr(vZ) + ')' );
vErrorDetected := true;
end;
end;
end;
end;

writeln('Verification done.');

if vErrorDetected then
begin
writeln('Verification result: errors detected !');
end else
begin
writeln('Verification result: no errors detected.');
end;

FreeMem( Memory, 320*200*60*4 );
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

// *** End of Test Program ***

Bye,
Skybuck.

Skybuck Flying, Jul 2, 2011
16. ### Skybuck FlyingGuest

Little setback... compiler hang when trying to create a pointer to a generic
type.

I was trying different/multiple ways and apperently it lead to a compiler
recurssion so I suspect ! :

http://www.skybuck.org/DelphiXE/CompilerHang.png

Bye,
Skybuck.

Skybuck Flying, Jul 2, 2011
17. ### Skybuck FlyingGuest

This is some fine code I got going on here:

It's worthy of a Snoop-Dog comment:

"It's one of my best !" =D LOL.

version 0.05:

I think I am going to have a lot of fun with this version !

// *** Begin of Test Program ***

program TestProgram;

{\$APPTYPE CONSOLE}

{

TDynamicIndex

version 0.03 created on 2 july 2011 by Skybuck Flying.

+ multiplieing dimensions can now also be thought of as creating a new
larger single dimension.
(a valid DimensionCount is now provided by the multiply operator)

}

{

TDynamicIndex<T>

version 0.04 created on 2 july 2011 by Skybuck Flying.

Simplified and optimized version, stride is left out of multiply operation
applied when applieing pointer conversion.

If there would be a pointer for each type then stride could be left out
completely,
perhaps generic might be of some use. Yup generics were usefull.

This will probably make volume also somewhat redundant so can be left out as
well.

And DimensionCount will be renamed to Dimension.

The interesting thing is that Dimension will remain as it was initialized
since the multiply operation returns a value, so it's like call by value.
Which means the dimension field for the result can be changed and the
originals
will remain intact.

There is ofcourse a little drawback to version 0.04, it becomes compile time
bound again because of the generic type.

The stride field/version was more runtime friendly and flexible.

None the less, the same code can be used with stride, but care most be taken
to apply the stride properly.

It does create a bit of problem... which stride to use... a,b,c or d etc...
however if assumption is that all use
the same element size then that is not a problem, an assert could be used to
check for it.

}

{

TDynamicIndex<T>

version 0.05 created on 2 july 2011 by Skybuck Flying.

+ Base re-introduced for shorter notation.
+ Pointer to generic type trick used.

Be carefull: Experimentally changing TDynamicIndex<T> to TDynamicIndex<T,P>
might cause
a compiler recursion and will make the compiler/ide hang ?!

}

uses
SysUtils;

type
TDynamicIndex<T> = record
type
P = ^T;
private
mBase : pointer;
mIndex : integer;
mDimension : integer;

// inlining seems to have no effect, but I'll let it be anyway to show the
none-effect
function StoreIndex( ParaIndex : integer ) : TDynamicIndex<T>; inline;

function GetValue : T; inline;
procedure SetValue( ParaValue : T ); inline;

function GetReference : P; inline;
public
constructor Create( ParaBase : pointer; ParaDimension : integer );

property IndexOperator[ ParaIndex : integer ] : TDynamicIndex<T> read
StoreIndex; default;
property Value : T read GetValue write SetValue;
property Reference : P read GetReference;

function Offset : longword; inline;

class operator Multiply( ParaA, ParaB : TDynamicIndex<T> ) :
TDynamicIndex<T>;
end;

function TDynamicIndex<T>.GetValue : T;
begin
result := T( pointer( longword(mBase) + ( longword(mIndex) *
SizeOf(T) ) )^ );
end;

procedure TDynamicIndex<T>.SetValue( ParaValue : T );
begin
T( pointer( longword(mBase) + ( longword(mIndex) * SizeOf(T) ) )^ ) :=
ParaValue
end;

function TDynamicIndex<T>.GetReference : P;
begin
result := P( longword(mBase) + ( longword(mIndex) * SizeOf(T) ) );
end;

function TDynamicIndex<T>.Offset : longword;
begin
result := longword(mIndex) * SizeOf(T);
end;

constructor TDynamicIndex<T>.Create( ParaBase : pointer; ParaDimension :
integer );
begin
mBase := ParaBase;
mIndex := 0;
mDimension := 1;

begin
end;
end;

// store index, and return a copy, not because we want to, but because we
must
// I would much rather return a pointer, but unfortunately calling multiple
operator on pointer derefence won't work well ?!)
// perhaps it should be retried sometime...
function TDynamicIndex<T>.StoreIndex( ParaIndex : integer ) :
TDynamicIndex<T>;
begin
mIndex := ParaIndex;
result.mBase := mBase;
result.mIndex := mIndex;
result.mDimension := mDimension;
end;

class operator TDynamicIndex<T>.Multiply( ParaA, ParaB : TDynamicIndex<T> )
: TDynamicIndex<T>;
begin
// this assert is only ment for debugging/paranoya purposes, so make sure to
disable asserts for release versions.
ASSERT( ParaA.mBase = ParaB.mBase, 'TDynamicIndex<T>.Multiply: ParaA(' +
IntToStr(longword(ParaA.mBase)) + ') = ParaB(' +
IntToStr(longword(ParaB.mBase)) + ')' );
result.mBase := ParaA.mBase;
result.mIndex := ParaA.mIndex + (ParaA.mDimension * ParaB.mIndex);
result.mDimension := ParaA.mDimension * ParaB.mDimension;
end;

(*
class operator TDynamicIndex<T>.Add( ParaA : TDynamicIndex<T>; ParaB :
pointer ) : pointer;
begin
result := pointer( longword(ParaB) + longword(ParaA.mIndex) * SizeOf(T) );
end;
*)

type
// PMyRecord = ^TMyRecord;
TMyRecord = record
mField1 : integer;
mField2 : word;
mField3 : byte;
end;

procedure Main;
var
Memory : pointer;

X : TDynamicIndex<TMyRecord>;
Y : TDynamicIndex<TMyRecord>;
Z : TDynamicIndex<TMyRecord>;

vMyRecord : TMyRecord;
Result : TDynamicIndex<TMyRecord>;
vOffset : longword;
vErrorDetected : boolean;

vX : integer;
vY : integer;
vZ : integer;

vCheck : longword;
begin
GetMem( Memory, 320*200*60*SizeOf(TMyRecord) );

X := TDynamicIndex<TMyRecord>.Create( Memory, 320 );
Y := TDynamicIndex<TMyRecord>.Create( Memory, 200 );
Z := TDynamicIndex<TMyRecord>.Create( Memory, 60 );

// .Value test
vMyRecord.mField1 := 333333333;
( X[ 122 ] * Y[ 66 ] * Z[ 5 ] ).Value := vMyRecord; // copies the whole
record.

vX := 122;
vY := 66;
vZ := 5;
vCheck := ( (vZ * 200 * 320) + (vY * 320) + vX ) * SizeOf(TMyRecord);

writeln( Pinteger( longword(Memory) + longword(vCheck) )^ ); // should
display 333333333

// .Reference test
( X[ 122 ] * Y[ 66 ] * Z[ 5 ] ).Reference.mField1 := 666666666; // accesses
and copies just the field for potentially increased performance.

vX := 122;
vY := 66;
vZ := 5;
vCheck := ( (vZ * 200 * 320) + (vY * 320) + vX ) * SizeOf(TMyRecord);

writeln( Pinteger( longword(Memory) + longword(vCheck) )^ ); // should
display 666666666

// first a few single tests to see/show how it works.
Result := X[ 0 ] * Y[ 0 ] * Z[ 1 ]; // should display 320*200*1*1=64000
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 0 ] * Z[ 0 ]; // should display 319*1*1*1=319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 0 ]; // should display 319*1 +
(100*320)*1*1 = 32319
writeln( Result.mIndex );

Result := X[ 319 ] * Y[ 100 ] * Z[ 5 ]; // should display 319*1 +
(100*320)*1 + (5*200*320)*1 = 32319 + 320000 = 352319
writeln( Result.mIndex );

// so far so good so now let's do some exhaustive/full verifications.
writeln('Verification started.');

vErrorDetected := false;
for vZ := 0 to 59 do
begin
for vY := 0 to 199 do
begin
for vX := 0 to 319 do
begin
vOffset := ( X[ vX ] * Y[ vY ] * Z[ vZ ] ).Offset;

vCheck := ( (vZ * 200 * 320) + (vY * 320) + vX ) * SizeOf(TMyRecord);

if vOffset <> vCheck then
begin
writeln('Index calculation error detected at (vX, vY, vZ): ' + '(' +
IntToStr(vX) + ',' + IntToStr(vY) + ',' + IntToStr(vZ) + ')' );
vErrorDetected := true;
end;
end;
end;
end;

writeln('Verification done.');

if vErrorDetected then
begin
writeln('Verification result: errors detected !');
end else
begin
writeln('Verification result: no errors detected.');
end;

FreeMem( Memory, 320*200*60*SizeOf(TMyRecord) );
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

// *** End of Test Program ***

Bye,
Skybuck.

Skybuck Flying, Jul 2, 2011
18. ### Skybuck FlyingGuest

After a night of sleep it's time to produce a somewhat more realistic
example of how I would use it.

I wasn't completely happen with the TDynamicIndex being a generic... that
required to much typing and was still a little bit odd...

The integrated pointer/base was also kinda odd which would somewhat limit
it... ofcourse multiple designs could be integrated except for the
generic... I am not sure if it's possible to leave it out... probably not.

So I have yet again come up with a alternative (example) version, this
probably most closesly resembles version 0.04 before it was modified... I
actually didn't keep it on my drive it seems, so it's good I made another
example/version. which I call alternative version 0.06.

This time it uses a TMemory<generic> and a TDynamicIndex

I considered renaming TDynamicIndex to TDimensionIndex... but for now I let
it be... TDynamicIndex sounds a bit more cool/dynamic =D
TDimensionIndex could be a bit confusing

Since the formula's have already been verified, the verification section has
been removed, and the example/code is now much shorter and cleaner !

Here it is:

// *** Begin of Test Program ***

program TestProgram;

{\$APPTYPE CONSOLE}

{

TDynamicIndex

alternative version 0.06 created on 3 july 2011 by Skybuck Flying.

TDynamicIndex reduced to the most basic/simpelst form so that it can be used
for many other types/arrays and so forth.

Instead TMemory is created which accepts TDynamicIndex as parameters to do
lookups. TMemory also has been made more generic so it can contain
different kinds of types.

A more realistic example.

There might still be room to improve this technique if TDynamicIndex could
be used as the loop variable. This will be examined in a next (alternative)
version !

(It's all about reducing the ammount of code and keep it as simple and well
with quick insight into what everthing does which is good for algorithm
design/development/inspection)

}

uses
SysUtils;

type
TDynamicIndex = record
private
mIndex : integer;
mDimension : integer;

function StoreIndex( ParaIndex : integer ) : TDynamicIndex; inline;
public
constructor Create( ParaDimension : integer );

property IndexOperator[ ParaIndex : integer ] : TDynamicIndex read
StoreIndex; default;

property xIndex : integer read mIndex;

class operator Multiply( ParaA, ParaB : TDynamicIndex ) : TDynamicIndex;
end;

constructor TDynamicIndex.Create( ParaDimension : integer );
begin
mIndex := 0;
mDimension := 1;

begin
end;
end;

function TDynamicIndex.StoreIndex( ParaIndex : integer ) : TDynamicIndex;
begin
mIndex := ParaIndex;
result.mIndex := mIndex;
result.mDimension := mDimension;
end;

class operator TDynamicIndex.Multiply( ParaA, ParaB : TDynamicIndex ) :
TDynamicIndex;
begin
result.mIndex := ParaA.mIndex + (ParaA.mDimension * ParaB.mIndex);
result.mDimension := ParaA.mDimension * ParaB.mDimension;
end;

type
TMemory<GenericType> = class
private

protected
mMemory : array of GenericType;
mCount : integer;

procedure SetCount( ParaCount : integer );

function GetElement( ParaIndex : TDynamicIndex ) : GenericType;
procedure SetElement( ParaIndex : TDynamicIndex; ParaValue :
GenericType );
public
constructor Create;
destructor Destroy; override;

property Count : integer read mCount write SetCount;

property Element[ ParaIndex : TDynamicIndex ] : GenericType read
GetElement write SetElement; default;
end;

constructor TMemory<GenericType>.Create;
begin
inherited Create;

end;

destructor TMemory<GenericType>.Destroy;
begin

inherited Destroy;
end;

procedure TMemory<GenericType>.SetCount( ParaCount : integer );
begin
mCount := ParaCount;
SetLength( mMemory, mCount );
end;

function TMemory<GenericType>.GetElement( ParaIndex : TDynamicIndex ) :
GenericType;
begin
result := mMemory[ParaIndex.xIndex];
end;

procedure TMemory<GenericType>.SetElement( ParaIndex : TDynamicIndex;
ParaValue : GenericType );
begin
mMemory[ParaIndex.xIndex] := ParaValue;
end;

procedure Main;
var
mBlockCount : integer;
mElementCount : integer;

mMemory : TMemory<integer>;

vBlockIndex : integer;
vElementIndex : integer;

Block : TDynamicIndex;
Element : TDynamicIndex;
begin
mBlockCount := 2000;
mElementCount := 8000;

mMemory := TMemory<integer>.Create;
mMemory.Count := mBlockCount * mElementCount;

Block := TDynamicIndex.Create( mBlockCount );
Element := TDynamicIndex.Create( mElementCount );

for vBlockIndex := 0 to mBlockCount-1 do
begin
for vElementIndex := 0 to mElementCount-1 do
begin
mMemory[ Block[vBlockIndex] * Element[vElementIndex] ] := vElementIndex;
end;
end;

for vBlockIndex := 0 to mBlockCount-1 do
begin
for vElementIndex := 0 to mElementCount-1 do
begin
writeln( 'BlockIndex: ', vBlockIndex, ' ElementIndex: ', vElementIndex, '
mMemory: ', mMemory[ Block[vBlockIndex] * Element[vElementIndex] ] );
end;
end;

mMemory.Free;
end;

begin
try
Main;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

// *** End of Test Program ***

Bye,
Skybuck.

Skybuck Flying, Jul 3, 2011
19. ### Skybuck FlyingGuest

This would be an interesting short-hand/notation form if it's possible:

The property can't be used as a for loop variable it's not allowed, but
perhaps "for each in" might work.

// doesn't compile yet, perhaps "for each in" can be used... maybe needs
for Block.xIndex := 0 to mBlockCount-1 do
begin
for Element.xIndex := 0 to mElementCount-1 do
begin
// mMemory[ Block[vBlockIndex] * Element[vElementIndex] ] :=
vElementIndex;

mMemory[ Block * Element ] := vElementIndex; // nice and short !
end;
end;

So I will now investigate this idea, if it doesn't work out to bad... but at
least this shows what I would like to be able to do ! =D

Bye,
Skybuck.

Skybuck Flying, Jul 3, 2011
20. ### Skybuck FlyingGuest

Here is my first attempt, which is kinda interesting too:

Block * Element can be considered a new dimension.

so the following code makes sense:

for vInteger in Block * Element do
begin

Added benefit would be even simpler/shorter code and only one loop.

So far Delphi XE returns the following interesting error message:

"
[DCC Error] TestProgram.dpr(168): E2431 for-in statement cannot operate on
collection type 'TDynamicIndex' because 'TDynamicIndex' does not contain a
member for 'GetEnumerator', or it is inaccessible
"

Apperently there is something new called: "GetEnumerator".

Bye,
Skybuck.

Skybuck Flying, Jul 3, 2011