5

I'd like to write a LinearInterpolator class, where X is the type of the X axis value, and Y the type of the Y axis value. I can't see how to do this such that X could be a DateTime or a double. The class is something like below (which is untested):

class LinearInterpolator<X, Y>
{
    private List<X> m_xAxis;
    private List<Y> m_yAxis;

    public LinearInterpolator(List<X> x, List<Y> y)
    {
        m_xAxis = x;
        m_yAxis = y;
    }

    public Y interpolate(X x)
    {
        int i = m_xAxis.BinarySearch(x);
        if (i >= 0)
        {
            return m_yAxis[i];
        }
        else
        {
            // Must interpolate.
            int rightIdx = ~i;
            if (rightIdx >= m_xAxis.Count)
                --rightIdx;
            int leftIdx = rightIdx - 1;

            X xRight = m_xAxis[rightIdx];
            X xLeft = m_xAxis[leftIdx];
            Y yRight = m_yAxis[rightIdx];
            Y yLeft = m_yAxis[leftIdx];

            // This is the expression I'd like to write generically.
            // I'd also like X to be compilable as a DateTime.
            Y y = yLeft + ((x - xLeft) / (xRight - xLeft)) * (yRight - yLeft);
            return y;
        }
    }
}

}

It'd be easy in C++, but I'm new to C# generics so any help would be appreciated.

  • Ideally this would work, but since `TimeSpan` doesn't support `TimeSpan / TimeSpan -> double` or `TimeSpan * double -> TimeSpan` (for reasons that might even be considered sensible) I think you're out in the cold. You can subtract times to get intervals and add them back again, but you can't find e.g. half of an interval without writing non-generic code. – hobbs Mar 02 '10 at 21:47
  • 2
    C++ generics are not the same as C# generics. The C++ compiler specializes templates at compile time. C# generates generic code that is specialized at runtime. One thing that does not work is the operators, there is no generic "add" or "divide" operator. You can't make this code work by design. – Hans Passant Mar 02 '10 at 21:55

4 Answers4

1

Use DateTime.Ticks as the interpolated value. You can use a long type as your generic to interpolate between times.

spoulson
  • 21,335
  • 15
  • 77
  • 102
  • If `long` isn't preferred, you could instead use `int` to interpolate intervals from 0..*n* as a tick offset from a base `DateTime`. e.g. `DateTime.AddTicks(offset)`. – spoulson Mar 02 '10 at 21:59
1

One thing to know is that C# does not support operator override. So such a code does not work.

One solution as spoulson said is to not use generics, and use int or long instead of T, and use DateTime.Ticks.

Nicolas Dorier
  • 7,383
  • 11
  • 58
  • 71
  • 1
    More accurately, it doesn't support signature-based polymorphism the way C++ templates do, so you can't have a class that's generic over arithmetic types. – Jeffrey Hantin Mar 02 '10 at 22:08
1

There's no good way to do math from C# generics, so you have to do something like this:

Y y = FromDouble<Y>(ToDouble(yLeft) + ((ToDouble(x) - ToDouble(xLeft)) /
          (ToDouble(xRight) - ToDouble(xLeft))) *
          (ToDouble(yRight) - ToDouble(yLeft)));

double ToDouble(object val)
{
    if (val is DateTime)
        return (double)((DateTime)val).Ticks;
    else
        return Convert.ToDouble(val);
}

T FromDouble<T>(double val)
{
    if (typeof(T) == typeof(DateTime))
        return (T)Convert.ChangeType(new DateTime((long)val), typeof(T));
    else
        return (T)Convert.ChangeType(val, typeof(T));
}

I haven't tested the code, so consider it pseudo-code.

Gabe
  • 84,912
  • 12
  • 139
  • 238
0

Thanks for the advice. I'm changing my approach and writing an interpolator that works only for doubles; I want interpolation to be fast, so prefer not to do type checks at run-time.