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. 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.

    For more information how this program came to be see the other thread called
    "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
    somewhat more computation-overhead.

    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
    would lead to wrong results.
    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
    overloading.

    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
    direct access to the memory.

    }

    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;

    if ParaDimensionCount > 1 then
    begin
    mDimensionCount := ParaDimensionCount;
    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;
    ReadLn;
    end.

    // *** End of Program ***

    Bye,
    Skybuck.
    Skybuck Flying, Jul 1, 2011
    #1
    1. Advertising

  2. 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.

    For more information how this program came to be see the other thread called
    "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
    somewhat more computation-overhead.

    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
    would lead to wrong results.
    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
    overloading.

    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
    direct access to the memory.

    }

    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;

    if ParaDimensionCount > 1 then
    begin
    mDimensionCount := ParaDimensionCount;
    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;
    ReadLn;
    end.

    // *** End of Program ***

    Bye,
    Skybuck.
    Skybuck Flying, Jul 1, 2011
    #2
    1. Advertising

  3. 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 (ParaDimensionCount > 1)
    {
    mDimensionCount = ParaDimensionCount;
    }

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

    mVolume = mDimensionCount * mStride;
    }

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


    }

    // overloaded subscript operator
    TDynamicIndex TDynamicIndex::eek: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
    #3
  4. Skybuck Flying

    Billy Mays Guest

    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
    #4
  5. "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
    #5
  6. 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.

    For more information how this program came to be see the other thread called
    "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
    somewhat more computation-overhead.

    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
    would lead to wrong results.
    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
    overloading.

    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
    direct access to the memory.

    }

    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;

    if ParaDimensionCount > 1 then
    begin
    mDimensionCount := ParaDimensionCount;
    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;
    ReadLn;
    end.

    // *** End of Program ***

    Bye,
    Skybuck.
    Skybuck Flying, Jul 2, 2011
    #6
  7. 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
    #7
  8. 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 );

    // overloaded add operator
    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 (ParaDimensionCount > 1)
    {
    mDimensionCount = ParaDimensionCount;
    }

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

    mVolume = mDimensionCount * mStride;
    }

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


    }

    // overloaded subscript operator
    TDynamicIndex TDynamicIndex::eek: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;
    }

    // overloaded add operator
    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
    #8
  9. "
    I'd enjoy seeing your implementation in C, if you please. :)
    "

    a C version can probably be made with "variadic" functions:

    http://en.wikipedia.org/wiki/Variadic_function

    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
    #9
  10. Skybuck Flying

    Shao Miller Guest

    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.

    I'd enjoy seeing your implementation in C, if you please. :)
    Shao Miller, Jul 2, 2011
    #10
  11. 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.

    [...]

    --
    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
    #11
  12. Skybuck Flying

    Shao Miller Guest

    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?

    [1] http://www.jwz.org/dadadodo/

    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
    #12
  13. 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.

    + pointer support added.
    + 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
    and is instead
    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;

    if ParaDimension > 1 then
    begin
    mDimension := ParaDimension;
    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;
    ReadLn;
    end.

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

    Bye,
    Skybuck.
    Skybuck Flying, Jul 2, 2011
    #13
  14. 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
    #14
  15. 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
    base address.

    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.

    + pointer support added.
    + 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
    and is instead
    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;

    if ParaDimension > 1 then
    begin
    mDimension := ParaDimension;
    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;
    ReadLn;
    end.

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

    Bye,
    Skybuck.
    Skybuck Flying, Jul 2, 2011
    #15
  16. 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
    #16
  17. 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.

    + pointer support added.
    + 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
    and is instead
    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.
    + .Value property added
    + .Reference property added
    + .Offset function added

    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;

    if ParaDimension > 1 then
    begin
    mDimension := ParaDimension;
    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;
    ReadLn;
    end.

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

    Bye,
    Skybuck.
    Skybuck Flying, Jul 2, 2011
    #17
  18. 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
    readable/understandable/self documenting as possible
    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;

    if ParaDimension > 1 then
    begin
    mDimension := ParaDimension;
    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;
    ReadLn;
    end.

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

    Bye,
    Skybuck.
    Skybuck Flying, Jul 3, 2011
    #18
  19. 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
    operater overloader.
    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
    #19
  20. 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
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Microsoft News Server

    you can do thi$

    Microsoft News Server, Feb 6, 2004, in forum: ASP .Net
    Replies:
    0
    Views:
    375
    Microsoft News Server
    Feb 6, 2004
  2. Showjumper

    What does thi s error message mean?

    Showjumper, Jul 31, 2003, in forum: ASP .Net
    Replies:
    0
    Views:
    316
    Showjumper
    Jul 31, 2003
  3. Christopher Brandsdal

    most valuable professional.

    Christopher Brandsdal, Jul 22, 2003, in forum: ASP General
    Replies:
    6
    Views:
    140
    Jeff Cochran
    Jul 22, 2003
  4. Wirianto Djunaidi
    Replies:
    2
    Views:
    194
    Wirianto Djunaidi
    Apr 29, 2008
  5. troll greenЪ
    Replies:
    0
    Views:
    273
    troll greenЪ
    Jan 21, 2013
Loading...

Share This Page