Search code examples
c#biginteger

Bounds for C# Generic Random Array Generator


I have written this block of code, in an attempt to create a generic random array generator (quite an interesting approach, if I must say so myself)

public unsafe class RandomArray<T> : DynamicArray<T> where T : INumber<T> {

    Type _type;
    Random _r;
    T _min;
    T _max;
    int _type_size;
    T _MaxValue;
    T _MinValue;
    
    private void init(int capacity) {
        _MaxValue = (T)typeof(T).GetField("MaxValue").GetValue(null);
        _MinValue = (T)typeof(T).GetField("MinValue").GetValue(null);
        // T ratio = T
        // _MaxValue & _MinValue allow us to determine the ratio
        _r = new Random();
        generate(capacity);
        _size = capacity;
    }
    
    private void generate(int capacity) {
        // take into account _min & _max
        byte[] b = new byte[capacity*_type_size+1];
        _r.NextBytes(b);
        fixed (byte* ptr_b = b) {
            T* t = (T*)ptr_b;
            for (int i = 0; i < capacity; i++) {
                _data[i] = *(t+i);
            }
        };
    }
    
    public RandomArray(int capacity, T min, T max) : base(capacity+1) {
        _type_size = Marshal.SizeOf(min);
        _type = min.GetType();
        _min = min;
        _max = max;
        init(capacity);
    }

}

The code is a combination of concepts taken from the following sources:

To interpret bytes as generic type T:

Converting random bytes to integers within a specified range

To get the MinValue/MaxValue of a Generic primitive numeric type:

How do I get the max and min value of a generic number type in c#

However, the problem that I face is that I cannot seem to come up with a mechanism to set the bounds of the min & max elements/limit the range of the random numbers generated because of some complications related to casting.

What would be the simplest technique to mimic:

_min + (*(t+i) / ratio);

where ratio would be computed as (_MaxValue - _MinValue) / (_max - _min)

It's obvious that _MaxValue - _MinValue will be type overflow, outside the bounds of the generic T, so I have tried using BigIntegersto do this, but I can't figure out exactly how this should work.

The problem is that I can't figure out how I would cast the BigInteger result back to a generic type T as I have done in most the body of the code.

How would I handle the case of calculating the ratio so that it is well defined given the complications that arise from type casting?


Solution

  • To change type of BigInteger is not straight forward.

    I would suggest using Convert.ChangeType method, but it won't accept BigInteger, as it does not implement IConvertible.

    However, BigInteger has casts to numeric types, so I would pick big numeric type (to prevent any information loss), let's use decimal. When BigInteger is cast to decimal, then we can use mentioned method Convert.ChangeType.

    Here's sample code:

    Console.WriteLine(Get<int>());
    Console.WriteLine(Get<long>());
    Console.WriteLine(Get<decimal>());
    
    T Get<T>() where T : struct, IConvertible
    {
        BigInteger b = 1;
        var t = (T)Convert.ChangeType((decimal)b, typeof(T));
        return t;
    }
    

    UPDATE For the other way around (converting to BigInteger) here's code example:

    BigInteger GetBigInt<T>(T input) where T : struct
    {
        BigInteger t = Convert.ToInt64(input);
        return t;
    }