C#

You are currently browsing articles tagged C#.

To mark the 63rd anniversary of the first stored computer program to run (June 21), I rewrote the original code in C# last night, both using the original algorithm (that was never intended to be efficient) and using a more modern structure. The Kilburn Highest Factor Routine finds the highest factor of 2^18, and completed with the correct answer in 52 minutes with 3.5 million operations. It was written by the late computer pioneer Tom Kilburn and run on the Machester SSEM (“Baby”).

The code is written in lines 1-19, lines 20-22 contain some control flow values, lines 23 and 24 contain 2^18 and the current factor to test, and lines 25-27 are working memory.  Click for a larger version of the original image from The National Archive for the History of Computing.

Kilburn Highest Factor Routine

Kilburn Highest Factor Routine

The last three columns (labelled 13, 14, and 15) are the instructions, and there are only a few of them. They are:

  • 100 – Jump forward or backward in code by the amount at the address given.
  • 010 – Load the negative of the value at the given address into the accumulator.
  • 110 – Store the value in the accumulator to the given address.
  • 001 – Subtract the value at the given address from the value in the accumulator, and keep the result in the accumulator.
  • 011 – Skip the next line if the value in the accumulator is greater than zero.
  • 111 – Halt

The first version in the listing below follows the original algorithm (and yes, you can use “goto” in C#). There’s a lot of subtraction involved because if you’re short on space and only want to implement one arithmetic operator, you’re better off implementing subtraction, because you can so addition with it, too. “S” is used here to indicate the Storage tube, which stored both the program and the working memory. The accumulator (represented by the variable “acc”) and the program counter used different tubes. The program counter is what kept track of where the computer was in the program during execution.

The only slightly optimized C# version at the bottom takes less than a millisecond today.


Stopwatch sw = new Stopwatch();
sw.Start();
int s20_jump_rel = -3;     // unused in code, provided for completeness
int s21_const_1 = 1;       // the amount to decrement the number being tested
int s22_jump_addy = 4;     // unused in code, provided for completeness
int s23_number_neg = -262144; // the number to solve (find the factor of)
int s24_div_init = 262143; // the initial number to test

int acc = 0;

acc = -s24_div_init;        // S01 - Load and negate the initial number to test
int s26_div_neg = acc;      // S02 - Store that number into S26

S03:
acc = -s26_div_neg;         // S03 - Load and negate the negated current number to test

int s27_div_pos = acc;      // S04 - Store that number into S27

acc = -s23_number_neg;      // S05 - Load and negate the number to solve

S06:
acc -= s27_div_pos;         // S06 - Subtract the current number to test

if( acc >= 0 )           // S07 - If the accumulator is > 0...
goto S06;                   // S08 - ...go back to S06

acc -= s26_div_neg;         // S09 - Subtract the negated current number to test
int s25 = acc;              // S10 - Store that number into S25
acc = -s25;                 // S11 - Load and negate the number in S25

if( acc >= 0 )           // S12 - If the accumulator is > 0...
goto HALT;                  // S13 - ...End

acc = -s26_div_neg;         // S14 - Load and negate the negated current number to test
acc -= s21_const_1;         // S15 - Subtract 1

s27_div_pos = acc;          // S16 - Store that number into S27
acc = -s27_div_pos;         // S17 - Load and negate the number in S27
s26_div_neg = acc;          // S18 - Store that number into S26

goto S03;                   // S19 - Go back to S03

HALT:
sw.Stop();
Console.WriteLine( "Elapsed={0}", sw.Elapsed );  // Write the elapsed time
Console.WriteLine( s27_div_pos );                // Write the result

sw.Restart();                // Restart the stopwatch for a more modern version
int i = 262143;              // Set the initial number to test
while( 262144 % i-- != 0 ) ; // Decrement until the number to solve modulus zero is zero
sw.Stop();
Console.WriteLine( "Elapsed={0}", sw.Elapsed );  // Write the elapsed time
Console.WriteLine( i + 1 );                      // Write the result

Share

We were using a web service that wasn’t quite up to interoperability standards. When attempting to use the service from .Net, we saw a number of errors and warnings related to the how the response is deserialized. In particular, .Net seemed to have trouble with the arrays (ArrayOfString or ArrayOfStrings).

Some of the warning or exceptions I saw are listed here, the solution is below it, and some of the root causes are below that. I don’t think it’s important to know about the specific web service or WSDL I was using, except to say that it didn’t pass the WS-I Basic Profile.

These were the original symptoms:

  • Error in deserializing body of reply message for operation ‘myMethodName’. (System.ServiceModel.CommunicationException)
  • There is an error in XML document ([line], [position]). (This was the inner exception to the above, and is common when an XML document doesn’t deserialize.)
  • The specified type was not recognized: name=’ArrayOfStrings’, namespace='[namespace]’, at [location]. (The was the inner exception of the above line, and explains where deserialization failed.)
  • Exceptions were thrown specifically at System.Xml.Serialization.XmlSerializationReader.GetPrimitiveType()
  • Undefined complexType ‘http://schemas.xmlsoap.org/soap/encoding/:Array’ is used as a base for complex type

The solution was to intercept the XML within WCF and deserialize it manually. There’s not a lot of instruction or examples for this online, so I thought I’d provide some here. Read the comments in the code carefully, and remember to change the return values in the Reference.cs file.

public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click( object sender, EventArgs e )
        {
            MyServiceReference.MyService service = new MyServiceReference.MyService();

            // In this case we have HTTP authentication
            service.ClientCredentials.UserName.UserName = "username";
            service.ClientCredentials.UserName.Password = "password";

            // Add a behavior to the operations we want to override
            service.Endpoint.Contract.Operations.Find( "getArrayOfStrings" ).Behaviors.Add( new FormatterBahavior() );
            service.Endpoint.Contract.Operations.Find( "getSomthingElse" ).Behaviors.Add( new FormatterBahavior() );

            // Call the web services
            var result1 = service.getArrayOfStrings();
            var result2 = service.getSomthingElse();
        }

        // This operation behavior changes the formatter for a specific set of operations in a web service. 
        public class FormatterBahavior : IOperationBehavior
        {
            #region IOperationBehavior Members
            public void AddBindingParameters( OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters )
            { }

            public void ApplyClientBehavior( OperationDescription operationDescription, ClientOperation clientOperation )
            {
                // The client should use a different, custom formatter depending upon which operation is called.
                switch( operationDescription.Name )
                {
                    case "getArrayOfStrings":
                        clientOperation.Formatter = new MyCustomFormatter1( clientOperation.Formatter );
                        break;
                    case "getSomthingElse":
                        clientOperation.Formatter = new MyCustomFormatter2( clientOperation.Formatter );
                        break;
                }
            }

            public void ApplyDispatchBehavior( OperationDescription operationDescription, DispatchOperation dispatchOperation )
            { }

            public void Validate( OperationDescription operationDescription )
            { }
            #endregion
        }

        // This customized formatter intercepts the deserialization process and handles it manually.
        public class MyCustomFormatter1 : IClientMessageFormatter
        {
            // Hold on to the original formatter so we can use it to return values for method calls we don't need.
            private IClientMessageFormatter _InnerFormatter;

            public MyCustomFormatter1( IClientMessageFormatter innerFormatter )
            {
                // Save the original formatter
                _InnerFormatter = innerFormatter;
            }

            #region IClientMessageFormatter Members
            public object DeserializeReply( System.ServiceModel.Channels.Message message, object[] parameters )
            {
                // Create a new response object.
                MyCustomResponseObject retVal = new MyCustomResponseObject();
                System.Xml.XPath.XPathDocument doc = new System.Xml.XPath.XPathDocument( message.GetReaderAtBodyContents() );
                var nav = doc.CreateNavigator();

                // Pulling out the data we need from the XML
                foreach( System.Xml.XPath.XPathNavigator item in nav.Select...() )
                {
                    // Populate retVal with the data we need from the XML
                }

                // IMPORTANT: Be sure to change the return type of the operation (and also its interface) in
                // the service's Reference.cs file to object or you will get a cast error.
                return retVal;
            }

            public System.ServiceModel.Channels.Message SerializeRequest( System.ServiceModel.Channels.MessageVersion messageVersion, object[] parameters )
            {
                // Use the inner formatter for this so we don't have to rebuild it.
                return _InnerFormatter.SerializeRequest( messageVersion, parameters );
            }

            #endregion
        }

        public class MyCustomFormatter2 : IClientMessageFormatter
        {
            // ...
        }
    }

    public class MyCustomResponseObject
    {
        public List<Report> ReportList = new List<Report>();
    }

Here are some specifics on where the web service failed to pass the WS-I Basic profile:

  • R2110 specifies that soapend:Array declarations must not extend or restrict the soapenc:Array type.
    • ArrayOfStrings restricts this type. ArrayOfStrings is an array type, and the other complex types in the service depend on ArrayOfStrings.
  • R2111 specifies that soapenc:Array types must not use wsdl:arrayType attribute in the type declaration.
    • It was used in the only attribute of the only restriction on that type.
  • R2706 specifies that a wsdl:binding in a description must use the value of “literal” for the “use” attribute in body (and other) elements because the profile prohibits the use of encodings.
    • The body elements of the binding use the value “encoded” for the “use” attributes.
  • R2801 specifies that a description must use XML Schema 1.0 recommendations as the basis for user-defined types and structures.
    • The prefix “wsdl” in the attribute of ArrayOfStrings is unbound.
Share