CodeCamp: Delegates

Wow, I’m sitting here in a C# 3.0 – A Whirlwind Tour talk, and the group has stalled on Delegates, a .Net 1.0 feature. Anonymous Delegates lost more people, Lambda functions are now messing with peoples heads….

Eeek, this is all 101 stuff, people!

It’s almost like nobody gets C function pointers or C++ functors.

TimeDirection graph

A work college was wanting some trivial code to draw a time based direction plot, and insisted that I do it.

So here is my drawing class:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace LineCurve
{
    public class TimeDirection
    {
        public void Draw(Graphics g, Panel pnl, List<KeyValuePair<int,float>> data, int maxtime)
        {
            using (Brush brush = new SolidBrush(Color.Blue))
            {
                float halfx = pnl.Width / 2.0f;
                float halfy = pnl.Height / 2.0f;
                float scale = Math.Min(halfx, halfy) / (float)maxtime;

                foreach (KeyValuePair<int, float> kvp in data)
                {
                    int time = kvp.Key;
                    double dir = kvp.Value / 180.0 * Math.PI;

                    // subtrack y as 0 is top, and +y is bottom.
                    float fx = (float)(halfx + (Math.Sin(dir) * scale * time));
                    float fy = (float)(halfy - (Math.Cos(dir) * scale * time));

                    g.FillRectangle(brush,fx, fy, 1.0f, 1.0f);
                }
            }
        }
    }
}

And given the input:

List<KeyValuePair<int,float>> data = new List<KeyValuePair<int,float>>();
for (int i = 0; i < 40; i++)
{
    data.Add(new KeyValuePair<int, float>(i, i * 9.0f));
}

Looks like this:

time/direction based graph

This code is released under the MIT license, except it may not be used for web-based weather applications.

Cedric’s Coding challenge

I noticed Cedric’s Coding challenge while reading Robert Fisher’s blog.

Here is an interesting coding challenge: write a counter function that counts from 1 to max but only returns numbers whose digits don’t repeat.

For example, part of the output would be:

  • 8, 9, 10, 12 (11 is not valid)
  • 98, 102, 103 (99, 100 and 101 are not valid)
  • 5432, 5436, 5437 (5433, 5434 and 5435 are not valid)

Also:

  • Display the biggest jump (in the sequences above, it’s 4: 98 -> 102)
  • Display the total count of numbers
  • Give these two values for max=10000

Because I love coding problems, I whipped up a C# 2.0 solution which is not as succinct as some of the functional solutions, but it works on a ‘fast enough for me’ time scale.

using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace ConsoleApplication1
{
    class Program
    {
        static bool[] used = new bool[10];
        static int value = 0;

        static void Main(string[] args)
        {
            int gap = 0;
            int last = 0;
            int count = 0;
            Stopwatch timer = new Stopwatch();

            timer.Start();
            int mag = 1;
            for (int i = 1; i < 10; i++)
            {
                foreach (int x in Step(1, mag))
                {
                    gap = Math.Max(gap, x - last);
                    last = x;
                    count++;
                }

                mag *= 10;
            }
            timer.Stop();
            Console.WriteLine("Count: {0}", count);
            Console.WriteLine("Gap: {0}", gap);
            Console.WriteLine("Time: {0}", timer.Elapsed);
            Console.ReadKey();
        }

        static IEnumerable<int> Step(int start, int mag)
        {
            for (int i = start; i < 10; i++)
            {
                if (used[i] == false)
                {
                    if (mag == 1)
                    {
                        yield return value + i;
                    }
                    else
                    {
                       used[i] = true;
                       value += mag * i;

                       foreach (int x in Step(0, mag / 10))
                       {
                           yield return x;
                       }

                       used[i] = false;
                       value -= mag * i;
                   }
               }
           }
       }
    }
}

Giving the following output (for 1 – MaxIn, as compared to 1-10,000 in the original challenge)

Count: 5611770
Gap: 10469135
Time: 00:00:02.8350765

Finding reference .dll’s

I am currently upgrading our build process from .bat files to CruiseControl.Net projects.  Mostly C++ projects but a few VB and one C#.

The C# project has a reference to NUnit.Framework.dll, and every thing worked well on the developer machines and the build server when building Visual Studio or using devenv.exe. But I have changed to using MSBuild.exe because that is what the CruiseControl.Net/NAnt example I’ve been following does.

When MSBuild.exe is ran at the command prompt the C# project runs fine, but when run from the CruiseControl.Net service I get the following errors:

errorCS0246: The type or namespace name 'NUnit' could not be found (are you missing a using directive or an assembly reference?)

After lots of hair pulling, I noticed one of the my C# projects had a <HintPath> element inside a <Reference> element.  The example was a relative path, but changing it to a full-path was required for my situation, as the projects are loaded to a different location on the build server.  So, in my C# project’s .csproj file, I have added this block:

<Reference Include="nunit.framework, Version=2.4.2.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>C:\Program Files\NUnit 2.4.2\bin\nunit.framework.dll</HintPath>
</Reference>

I’m not sure how you would set the <HintPath> element from inside Visual Studio.

Pascal 6-byte real to IEEE 8-byte double

A while ago I mentioned that System.BitConverter.Int64BitsToDouble would be useful for converting Pascal 6-byte real’s to IEEE 8-byte double’s.  Here is my C# .Net code to do just that.

My original code was based on Richard Biffl’s BPREAL.C.  My original code used System.Runtime.InteropServices.StructLayout( LayoutKind.Explicit ) attribute to make a C++ union data type.  The new code now uses a long (int64) and System.BitConverter.Int64BitsToDouble to do the dirty work.

I originally had my real’s loaded into registers CX, SI, and DI, so that is why the name/order of my functions parameters also.  I knew I had it correct when two of the original values turned out to be 2*PI and 100.0

Here’s the code, the old code is in class PascalReal and the newer version in class PascalRealNicer:

using System;
using System.Runtime.InteropServices;

namespace RealToDouble
{
    class MainProgram
    {
        [STAThread]
        static void Main(string[] args)
        {
            Console.WriteLine("{0}", PascalReal.ToDouble(new ushort[] { /*cx*/0xdc83, /*si*/0xcf80, /*di*/0x490F })); // 2 * PI
            Console.WriteLine("{0}", PascalReal.ToDouble(new ushort[] { /*cx*/0x0087, /*si*/0x0000, /*di*/0x4800 })); // 100.0
            Console.WriteLine("{0}", PascalReal.ToDouble(new ushort[] { /*cx*/0x0084, /*si*/0x0000, /*di*/0xa000 })); // -10.0
            Console.WriteLine("{0}", PascalReal.ToDouble(new ushort[] { /*cx*/0x0084, /*si*/0x0000, /*di*/0x0000 })); // 8.0
            Console.WriteLine("{0}", PascalReal.ToDouble(new ushort[] { /*cx*/0x0083, /*si*/0x0000, /*di*/0x0000 })); // 2.0
            Console.WriteLine("{0}", PascalReal.ToDouble(new ushort[] { /*cx*/0x0000, /*si*/0x0000, /*di*/0x0000 })); // 0.0

            Console.WriteLine();

            Console.WriteLine("{0}", PascalRealNicer.ToDouble( /*cx*/0xdc83, /*si*/0xcf80, /*di*/0x490F)); // 2 * PI
            Console.WriteLine("{0}", PascalRealNicer.ToDouble( /*cx*/0x0087, /*si*/0x0000, /*di*/0x4800)); // 100.0
            Console.WriteLine("{0}", PascalRealNicer.ToDouble( /*cx*/0x0084, /*si*/0x0000, /*di*/0xa000)); // -10.0
            Console.WriteLine("{0}", PascalRealNicer.ToDouble( /*cx*/0x0084, /*si*/0x0000, /*di*/0x0000)); // 8.0
            Console.WriteLine("{0}", PascalRealNicer.ToDouble( /*cx*/0x0083, /*si*/0x0000, /*di*/0x0000)); // 2.0
            Console.WriteLine("{0}", PascalRealNicer.ToDouble( /*cx*/0x0000, /*si*/0x0000, /*di*/0x0000)); // 0.0

            Console.ReadKey();
        }
    }

    class PascalReal
    {
        [ StructLayout( LayoutKind.Explicit )]
        public struct doublearray
        {
            [ FieldOffset( 0 )]
            public ushort a0;
            [ FieldOffset( 2 )]
            public ushort a1;
            [ FieldOffset( 4 )]
            public ushort a2;
            [ FieldOffset( 6 )]
            public ushort a3;

            [ FieldOffset( 0 )]
            public double d;
        }

        public static double ToDouble( ushort[] r )
        {
            doublearray da;

            da.d = 0.0;
            da.a0 = 0;
            da.a1 = 0;
            da.a2 = 0;
            da.a3 = 0;

            ushort x = (ushort)(r[0] & 0x00FF);  /* Real biased exponent in x */
            /* when exponent is 0, value is 0.0 */
            if (x == 0)
                da.d = 0.0;
            else
            {
                da.a3 = (ushort)(((x + 894) << 4) |  /* adjust exponent bias */
                    (r[2] & 0x8000) |  /* sign bit */
                    ((r[2] & 0x7800) >> 11));  /* begin significand */
                da.a2 |= (ushort)((r[2] << 5) |  /* continue shifting significand */
                    (r[1] >> 11));
                da.a1 |= (ushort)((r[1] << 5) |
                    (r[0] >> 11));
                da.a0 |= (ushort)((r[0] & 0xFF00) << 5); /* mask real's exponent */

            }
            return da.d;

        }
    }

    class PascalRealNicer
    {
        public static double ToDouble(ushort cx, ushort si, ushort di)
        {
            long double64 = 0;
            int exp = cx & 0x00ff;

            if (exp != 0)
            {
                // sign
                double64 = (long)(di & 0x8000) << 48;
                // biased exponent
                double64 |= (long)(exp + 894) << 52;
                // mantissa
                double64 |= (long)(di & 0x7FFF) << 37;
                double64 |= (long)si << 21;
                double64 |= (long)(cx & 0xFF00) << 5;
            }

            return System.BitConverter.Int64BitsToDouble(double64);
        }
    }
}