Click here to Skip to main content
15,886,806 members
Articles / Programming Languages / C#

A Skip List in C#

Rate me:
Please Sign up or sign in to vote.
4.94/5 (89 votes)
31 Aug 2003MIT9 min read 204.1K   4.4K   114  
Skip Lists, their Algorithms, and a SkipList class in C#.
/*
 * Created by: Leslie Sanford (08/27/2003)
 * Contact: jabberdabber@hotmail.com
 */

using System;
using System.Collections;
using System.Resources;
using System.Reflection;

namespace LSCollections
{
	/// <summary>
    /// Represents a collection of key-and-value pairs.
	/// </summary>
	/// <remarks>
	/// The SkipList class is an implementation of the IDictionary interface. It 
	/// is based on the data structure created by William Pugh.
	/// </remarks> 
	public class SkipList : IDictionary
	{        
        #region SkipList Members

        #region Constants

        // Maximum level any node in a skip list can have
        private const int MaxLevel = 32;

        // Probability factor used to determine the node level
        private const double Probability = 0.5;

        #endregion 

        #region Fields

        // The skip list header. It also serves as the NIL node.
        private Node header = new Node(MaxLevel);

        // Comparer for comparing keys.
        private IComparer comparer;

        // Random number generator for generating random node levels.
        private Random random = new Random();

        // Current maximum list level.
        private int listLevel;

        // Current number of elements in the skip list.
        private int count;

        // Version of the skip list. Used for validation checks with 
        // enumerators.
        private long version = 0;

        // Resource manager for retrieving exception messages.
        private ResourceManager resManager;  

        #endregion

        /// <summary>
        /// Initializes a new instance of the SkipList class that is empty and 
        /// is sorted according to the IComparable interface implemented by 
        /// each key added to the SkipList.
        /// </summary>
        /// <remarks>
        /// Each key must implement the IComparable interface to be capable of 
        /// comparisons with every other key in the SortedList. The elements 
        /// are sorted according to the IComparable implementation of each key 
        /// added to the SkipList.
        /// </remarks>
        public SkipList()
        {
            // Initialize the skip list.
            Initialize();

            // Load resources.
            resManager = 
                new ResourceManager("LSCollections.Resource", 
                this.GetType().Assembly);
        }

        /// <summary>
        /// Initializes a new instance of the SkipList class that is empty and 
        /// is sorted according to the specified IComparer interface.
        /// </summary>
        /// <param name="comparer">
        /// The IComparer implementation to use when comparing keys. 
        /// </param>
        /// <remarks>
        /// The elements are sorted according to the specified IComparer 
        /// implementation. If comparer is a null reference, the IComparable 
        /// implementation of each key is used; therefore, each key must 
        /// implement the IComparable interface to be capable of comparisons 
        /// with every other key in the SkipList.
        /// </remarks>
        public SkipList(IComparer comparer)
		{
            // Initialize comparer with the client provided comparer.
            this.comparer = comparer;

            // Initialize the skip list.
            Initialize();

            // Load resources.
            resManager = 
                new ResourceManager("LSCollections.Resource", 
                this.GetType().Assembly);
        }

        ~SkipList()
        {
            Clear();
        }

        #region Private Helper Methods

        /// <summary>
        /// Initializes the SkipList.
        /// </summary>
        private void Initialize()
        {
            listLevel = 1;
            count = 0; 

            // When the list is empty, make sure all forward references in the
            // header point back to the header. This is important because the 
            // header is used as the sentinel to mark the end of the skip list.
            for(int i = 0; i < MaxLevel; i++)
            {
                header.forward[i] = header;
            }
        }
 
        /// <summary>
        /// Returns a level value for a new SkipList node.
        /// </summary>
        /// <returns>
        /// The level value for a new SkipList node.
        /// </returns>
        private int GetNewLevel()
        {
            int level = 1;

            // Determines the next node level.
            while(random.NextDouble() < Probability && level < MaxLevel &&
                level <= listLevel)
            {
                level++;
            }

            return level;
        }

        /// <summary>
        /// Searches for the specified key.
        /// </summary>
        /// <param name="key">
        /// The key to search for.
        /// </param>
        /// <returns>
        /// Returns true if the specified key is in the SkipList.
        /// </returns>
        private bool Search(object key)
        {
            Node curr;
            Node[] dummy = new Node[MaxLevel];
            
            return Search(key, out curr, dummy);
        }

        /// <summary>
        /// Searches for the specified key.
        /// </summary>
        /// <param name="key">
        /// The key to search for.
        /// </param>
        /// <param name="curr">
        /// A SkipList node to hold the results of the search.
        /// </param>
        /// <returns>
        /// Returns true if the specified key is in the SkipList.
        /// </returns>
        /// <remarks>
        private bool Search(object key, out Node curr)
        {
            Node[] dummy = new Node[MaxLevel];

            return Search(key, out curr, dummy);
        }

        /// <summary>
        /// Searches for the specified key.
        /// </summary>
        /// <param name="key">
        /// The key to search for.
        /// </param>
        /// <param name="update">
        /// An array of nodes holding references to the places in the SkipList
        /// search in which the search dropped down one level.
        /// </param>
        /// <returns>
        /// Returns true if the specified key is in the SkipList.
        /// </returns>
        private bool Search(object key, Node[] update)
        {
            Node curr;

            return Search(key, out curr, update);
        }

        /// <summary>
        /// Searches for the specified key.
        /// </summary>
        /// <param name="key">
        /// The key to search for.
        /// </param>
        /// <param name="curr">
        /// A SkipList node to hold the results of the search.
        /// </param>
        /// <param name="update">
        /// An array of nodes holding references to the places in the SkipList
        /// search in which the search dropped down one level.
        /// </param>
        /// <returns>
        /// Returns true if the specified key is in the SkipList.
        /// </returns>
        private bool Search(object key, out Node curr, Node[] update)
        {
            // Make sure key isn't null.
            if(key == null)
            {
                string msg = resManager.GetString("NullKey");
                throw new ArgumentNullException(msg);
            }

            bool result;

            // Check to see if we will search with a comparer.
            if(comparer != null)
            {
                result = SearchWithComparer(key, out curr, update);
            }
            // Else we're using the IComparable interface.
            else
            {
                result = SearchWithComparable(key, out curr, update);
            }

            return result;
        }

        /// <summary>
        /// Search for the specified key using a comparer.
        /// </summary>
        /// <param name="key">
        /// The key to search for.
        /// </param>
        /// <param name="curr">
        /// A SkipList node to hold the results of the search.
        /// </param>
        /// <param name="update">
        /// An array of nodes holding references to the places in the SkipList
        /// search in which the search dropped down one level.
        /// </param>
        /// <returns>
        /// Returns true if the specified key is in the SkipList.
        /// </returns>
        private bool SearchWithComparer(object key, out Node curr,
            Node[] update)
        {
            bool found = false;

            // Start from the beginning of the skip list.
            curr = header;

            // Work our way down from the top of the skip list to the bottom.
            for(int i = listLevel - 1; i >= 0; i--)
            {
                // While we haven't reached the end of the skip list and the 
                // current key is less than the new key.
                while(curr.forward[i] != header && 
                    comparer.Compare(curr.forward[i].entry.Key, key) < 0)
                {
                    // Move forward in the skip list.
                    curr = curr.forward[i];
                }

                // Keep track of each node where we move down a level. This 
                // will be used later to rearrange node references when 
                // inserting a new element.
                update[i] = curr;
            }

            // Move ahead in the skip list. If the new key doesn't already 
            // exist in the skip list, this should put us at either the end of
            // the skip list or at a node with a key greater than the sarch key.
            // If the new key already exists in the skip list, this should put 
            // us at a node with a key equal to the search key.
            curr = curr.forward[0];

            // If we haven't reached the end of the skip list and the 
            // current key is equal to the search key.
            if(curr != header && comparer.Compare(key, curr.entry.Key) == 0)
            {
                // Indicate that we've found the search key.
                found = true;
            }

            return found;
        } 

        /// <summary>
        /// Search for the specified key using the IComparable interface 
        /// implemented by each key.
        /// </summary>
        /// <param name="key">
        /// The key to search for.
        /// </param>
        /// <param name="curr">
        /// A SkipList node to hold the results of the search.
        /// </param>
        /// <param name="update">
        /// An array of nodes holding references to the places in the SkipList
        /// search in which the search dropped down one level.
        /// </param>
        /// <returns>
        /// Returns true if the specified key is in the SkipList.
        /// </returns>
        /// <remarks>
        /// Assumes each key inserted into the SkipList implements the 
        /// IComparable interface.
        /// 
        /// If the specified key is in the SkipList, the curr parameter will
        /// reference the node with the key. If the specified key is not in the
        /// SkipList, the curr paramater will either hold the node with the 
        /// first key value greater than the specified key or null indicating 
        /// that the search reached the end of the SkipList.
        /// </remarks>
        private bool SearchWithComparable(object key, out Node curr, 
            Node[] update)
        {            
            // Make sure key is comparable.
            if(!(key is IComparable))
            {
                string msg = resManager.GetString("ComparableError");
                throw new ArgumentException(msg);
            }
            
            bool found = false;            
            IComparable comp;

            // Begin at the start of the skip list.
            curr = header;
            
            // Work our way down from the top of the skip list to the bottom.
            for(int i = listLevel - 1; i >= 0; i--)
            { 
                // Get the comparable interface for the current key.
                comp = (IComparable)curr.forward[i].entry.Key;

                // While we haven't reached the end of the skip list and the 
                // current key is less than the search key.
                while(curr.forward[i] != header && comp.CompareTo(key) < 0)
                {
                    // Move forward in the skip list.
                    curr = curr.forward[i];
                    // Get the comparable interface for the current key.
                    comp = (IComparable)curr.forward[i].entry.Key;
                }

                // Keep track of each node where we move down a level. This 
                // will be used later to rearrange node references when 
                // inserting a new element.
                update[i] = curr;
            }

            // Move ahead in the skip list. If the new key doesn't already 
            // exist in the skip list, this should put us at either the end of
            // the skip list or at a node with a key greater than the search key.
            // If the new key already exists in the skip list, this should put 
            // us at a node with a key equal to the search key.
            curr = curr.forward[0];

            // Get the comparable interface for the current key.
            comp = (IComparable)curr.entry.Key;

            // If we haven't reached the end of the skip list and the 
            // current key is equal to the search key.
            if(curr != header && comp.CompareTo(key) == 0)
            {
                // Indicate that we've found the search key.
                found = true;
            }

            return found;
        } 

        /// <summary>
        /// Inserts a key/value pair into the SkipList.
        /// </summary>
        /// <param name="key">
        /// The key to insert into the SkipList.
        /// </param>
        /// <param name="val">
        /// The value to insert into the SkipList.
        /// </param>
        /// <param name="update">
        /// An array of nodes holding references to places in the SkipList in 
        /// which the search for the place to insert the new key/value pair 
        /// dropped down one level.
        /// </param>
        private void Insert(object key, object val, Node[] update)
        {
            // Get the level for the new node.
            int newLevel = GetNewLevel();

            // If the level for the new node is greater than the skip list 
            // level.
            if(newLevel > listLevel)
            {
                // Make sure our update references above the current skip list
                // level point to the header. 
                for(int i = listLevel; i < newLevel; i++)
                {
                    update[i] = header;
                }

                // The current skip list level is now the new node level.
                listLevel = newLevel;
            }

            // Create the new node.
            Node newNode = new Node(newLevel, key, val);

            // Insert the new node into the skip list.
            for(int i = 0; i < newLevel; i++)
            {
                // The new node forward references are initialized to point to
                // our update forward references which point to nodes further 
                // along in the skip list.
                newNode.forward[i] = update[i].forward[i];

                // Take our update forward references and point them towards 
                // the new node. 
                update[i].forward[i] = newNode;
            }

            // Keep track of the number of nodes in the skip list.
            count++;
            // Indicate that the skip list has changed.
            version++;
        }  

        #endregion

        #endregion

        #region Node Class

        /// <summary>
        /// Represents a node in the SkipList.
        /// </summary>
        private class Node : IDisposable
        {
            #region Fields

            // References to nodes further along in the skip list.
            public Node[] forward;

            // The key/value pair.
            public DictionaryEntry entry;

            #endregion

            /// <summary>
            /// Initializes an instant of a Node with its node level.
            /// </summary>
            /// <param name="level">
            /// The node level.
            /// </param>
            public Node(int level)
            {
                forward = new Node[level];
            }

            /// <summary>
            /// Initializes an instant of a Node with its node level and 
            /// key/value pair.
            /// </summary>
            /// <param name="level">
            /// The node level.
            /// </param>
            /// <param name="key">
            /// The key for the node.
            /// </param>
            /// <param name="val">
            /// The value for the node.
            /// </param>
            public Node(int level, object key, object val)
            {
                forward = new Node[level];
                entry.Key = key;
                entry.Value = val;
            }

            #region IDisposable Members

            /// <summary>
            /// Disposes the Node.
            /// </summary>
            public void Dispose()
            {
                for(int i = 0; i < forward.Length; i++)
                {
                    forward[i] = null;
                }
            }

            #endregion
        }

        #endregion

        #region SkipListEnumerator Class

        /// <summary>
        /// Enumerates the elements of a skip list.
        /// </summary>
        private class SkipListEnumerator : IDictionaryEnumerator
        {  
            #region SkipListEnumerator Members

            #region Fields

            // The skip list to enumerate.
            private SkipList list;

            // The current node.
            private Node current;

            // The version of the skip list we are enumerating.
            private long version;

            // Keeps track of previous move result so that we can know 
            // whether or not we are at the end of the skip list.
            private bool moveResult = true;

            #endregion

            /// <summary>
            /// Initializes an instance of a SkipListEnumerator.
            /// </summary>
            /// <param name="list"></param>
            public SkipListEnumerator(SkipList list)
            {
                this.list = list;
                version = list.version;
                current = list.header;
            }

            #endregion
       
            #region IDictionaryEnumerator Members

            /// <summary>
            /// Gets both the key and the value of the current dictionary 
            /// entry.
            /// </summary>
            public DictionaryEntry Entry
            {
                get
                {
                    DictionaryEntry entry;

                    // Make sure the skip list hasn't been modified since the
                    // enumerator was created.
                    if(version != list.version)
                    {
                        string msg = list.resManager.GetString("InvalidEnum");
                        throw new InvalidOperationException(msg);
                    }
                    // Make sure we are not before the beginning or beyond the 
                    // end of the skip list.
                    else if(current == list.header)
                    {
                        string msg = list.resManager.GetString("BadEnumAccess");                        
                        throw new InvalidOperationException();
                    }
                    // Finally, all checks have passed. Get the current entry.
                    else
                    {
                        entry = current.entry;
                    }

                    return entry;
                }
            }

            /// <summary>
            /// Gets the key of the current dictionary entry.
            /// </summary>
            public object Key
            {
                get
                {
                    object key = Entry.Key;

                    return key;
                }
            }

            /// <summary>
            /// Gets the value of the current dictionary entry.
            /// </summary>
            public object Value
            {
                get
                {
                    object val = Entry.Value;

                    return val;
                }
            }

            #endregion

            #region IEnumerator Members

            /// <summary>
            /// Advances the enumerator to the next element of the skip list.
            /// </summary>
            /// <returns>
            /// true if the enumerator was successfully advanced to the next 
            /// element; false if the enumerator has passed the end of the 
            /// skip list.
            /// </returns>
            public bool MoveNext()
            {
                // Make sure the skip list hasn't been modified since the
                // enumerator was created.
                if(version == list.version)
                {       
                    // If the result of the previous move operation was true
                    // we can still move forward in the skip list.
                    if(moveResult)
                    {
                        // Move forward in the skip list.
                        current = current.forward[0];

                        // If we are at the end of the skip list.
                        if(current == list.header)
                        {
                            // Indicate that we've reached the end of the skip 
                            // list.
                            moveResult = false;
                        }
                    }
                }
                // Else this version of the enumerator doesn't match that of 
                // the skip list. The skip list has been modified since the 
                // creation of the enumerator.
                else
                {
                    string msg = list.resManager.GetString("InvalidEnum");
                    throw new InvalidOperationException(msg);
                }

                return moveResult;
            }

            /// <summary>
            /// Sets the enumerator to its initial position, which is before 
            /// the first element in the skip list.
            /// </summary>
            public void Reset()
            {
                // Make sure the skip list hasn't been modified since the
                // enumerator was created.
                if(version == list.version)
                {
                    current = list.header;
                    moveResult = true;
                }
                // Else this version of the enumerator doesn't match that of 
                // the skip list. The skip list has been modified since the 
                // creation of the enumerator.
                else
                {
                    string msg = list.resManager.GetString("InvalidEnum");
                    throw new InvalidOperationException(msg);
                }
            }

            /// <summary>
            /// Gets the current element in the skip list.
            /// </summary>
            public object Current
            {
                get
                {                    
                    return Value;
                }
            }

            #endregion
        }   

        #endregion

        #region IDictionary Members

        /// <summary>
        /// Adds an element with the provided key and value to the SkipList.
        /// </summary>
        /// <param name="key">
        /// The Object to use as the key of the element to add. 
        /// </param>
        /// <param name="value">
        /// The Object to use as the value of the element to add. 
        /// </param>
        public void Add(object key, object value)
        {
            Node[] update = new Node[MaxLevel];
            
            // If key does not already exist in the skip list.
            if(!Search(key, update))
            {
                // Inseart key/value pair into the skip list.
                Insert(key, value, update);
            }
            // Else throw an exception. The IDictionary Add method throws an
            // exception if an attempt is made to add a key that already 
            // exists in the skip list.
            else
            {
                string msg = resManager.GetString("KeyExistsAdd");
                throw new ArgumentException(msg);
            }
        }

        /// <summary>
        /// Removes all elements from the SkipList.
        /// </summary>
        public void Clear()
        {
            // Start at the beginning of the skip list.
            Node curr = header.forward[0];
            Node prev;

            // While we haven't reached the end of the skip list.
            while(curr != header)
            {
                // Keep track of the previous node.
                prev = curr;
                // Move forward in the skip list.
                curr = curr.forward[0];
                // Dispose of the previous node.
                prev.Dispose();
            }

            // Initialize skip list and indicate that it has been changed.
            Initialize();
            version++;
        }

        /// <summary>
        /// Determines whether the SkipList contains an element with the 
        /// specified key.
        /// </summary>
        /// <param name="key">
        /// The key to locate in the SkipList.
        /// </param>
        /// <returns>
        /// true if the SkipList contains an element with the key; otherwise, 
        /// false.
        /// </returns>
        public bool Contains(object key)
        {
            return Search(key);
        }

        /// <summary>
        /// Returns an IDictionaryEnumerator for the SkipList.
        /// </summary>
        /// <returns>
        /// An IDictionaryEnumerator for the SkipList.
        /// </returns>
        public IDictionaryEnumerator GetEnumerator()
        {
            return new SkipListEnumerator(this);
        }

        /// <summary>
        /// Removes the element with the specified key from the SkipList.
        /// </summary>
        /// <param name="key">
        /// The key of the element to remove.
        /// </param>
        public void Remove(object key)
        {
            Node[] update = new Node[MaxLevel];
            Node curr;

            if(Search(key, out curr, update))
            {
                // Take the forward references that point to the node to be 
                // removed and reassign them to the nodes that come after it.
                for(int i = 0; i < listLevel && 
                    update[i].forward[i] == curr; i++)
                {
                    update[i].forward[i] = curr.forward[i];
                }

                curr.Dispose();

                // After removing the node, we may need to lower the current 
                // skip list level if the node had the highest level of all of
                // the nodes.
                while(listLevel > 1 && header.forward[listLevel - 1] == header)
                {
                    listLevel--;
                }

                // Keep track of the number of nodes.
                count--;
                // Indicate that the skip list has changed.
                version++;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the SkipList has a fixed size.
        /// </summary>
        public bool IsFixedSize
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the IDictionary is read-only.
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets or sets the element with the specified key. This is the 
        /// indexer for the SkipList. 
        /// </summary>
        public object this[object key]
        {
            get
            {
                object val = null;
                Node curr;

                if(Search(key, out curr))
                {
                    val = curr.entry.Value;
                }

                return val;
            }
            set
            {
                Node[] update = new Node[MaxLevel];
                Node curr;

                // If the search key already exists in the skip list.
                if(Search(key, out curr, update))
                {
                    // Replace the current value with the new value.
                    curr.entry.Value = value;
                    // Indicate that the skip list has changed.
                    version++;
                }
                // Else the key doesn't exist in the skip list.
                else
                {
                    // Insert the key and value into the skip list.
                    Insert(key, value, update);
                }
            }
        }

        /// <summary>
        /// Gets an ICollection containing the keys of the SkipList.
        /// </summary>
        public ICollection Keys
        {
            get
            {
                // Start at the beginning of the skip list.
                Node curr = header.forward[0];
                // Create a collection to hold the keys.
                ArrayList collection = new ArrayList();

                // While we haven't reached the end of the skip list.
                while(curr != header)
                {
                    // Add the key to the collection.
                    collection.Add(curr.entry.Key);
                    // Move forward in the skip list.
                    curr = curr.forward[0];
                }

                return collection;
            }
        }

        /// <summary>
        /// Gets an ICollection containing the values of the SkipList.
        /// </summary>
        public ICollection Values
        {
            get
            {
                // Start at the beginning of the skip list.
                Node curr = header.forward[0];
                // Create a collection to hold the values.
                ArrayList collection = new ArrayList();

                // While we haven't reached the end of the skip list.
                while(curr != header)
                {
                    // Add the value to the collection.
                    collection.Add(curr.entry.Value);
                    // Move forward in the skip list.
                    curr = curr.forward[0];
                }

                return collection;
            }
        } 

        #endregion

        #region ICollection Members

        /// <summary>
        /// Copies the elements of the SkipList to an Array, starting at a 
        /// particular Array index.
        /// </summary>
        /// <param name="array">
        /// The one-dimensional Array that is the destination of the elements 
        /// copied from SkipList.
        /// </param>
        /// <param name="index">
        /// The zero-based index in array at which copying begins.
        /// </param>
        public void CopyTo(Array array, int index)
        {
            // Make sure array isn't null.
            if(array == null)
            {
                string msg = resManager.GetString("NullArrayCopyTo");
                throw new ArgumentNullException(msg);
            }
            // Make sure index is not negative.
            else if(index < 0)
            {
                string msg = resManager.GetString("BadIndexCopyTo");
                throw new ArgumentOutOfRangeException(msg);
            }
            // Array bounds checking.
            else if(index >= array.Length)
            {
                string msg = resManager.GetString("BadIndexCopyTo");
                throw new ArgumentException(msg);
            }
            // Make sure that the number of elements in the skip list is not 
            // greater than the available space from index to the end of the 
            // array.
            else if((array.Length - index) < Count)
            {
                string msg = resManager.GetString("BadIndexCopyTo");
                throw new ArgumentException(msg);
            }
            // Else copy elements from skip list into array.
            else
            {
                // Start at the beginning of the skip list.
                Node curr = header.forward[0];

                // While we haven't reached the end of the skip list.
                while(curr != header)
                {
                    // Copy current value into array.
                    array.SetValue(curr.entry.Value, index);

                    // Move forward in the skip list and array.
                    curr = curr.forward[0];
                    index++;
                }
            }
        }

        /// <summary>
        /// Gets the number of elements contained in the SkipList.
        /// </summary>
        public int Count
        {
            get
            {                
                return count;
            }
        }

        /// <summary>
        /// Gets a value indicating whether access to the SkipList is 
        /// synchronized (thread-safe).
        /// </summary>
        public bool IsSynchronized
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets an object that can be used to synchronize access to the 
        /// SkipList.
        /// </summary>
        public object SyncRoot
        {
            get
            {
                return this;
            }
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an enumerator that can iterate through the SkipList.
        /// </summary>
        /// <returns>
        /// An IEnumerator that can be used to iterate through the collection.
        /// </returns>
        IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new SkipListEnumerator(this);
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
United States United States
Aside from dabbling in BASIC on his old Atari 1040ST years ago, Leslie's programming experience didn't really begin until he discovered the Internet in the late 90s. There he found a treasure trove of information about two of his favorite interests: MIDI and sound synthesis.

After spending a good deal of time calculating formulas he found on the Internet for creating new sounds by hand, he decided that an easier way would be to program the computer to do the work for him. This led him to learn C. He discovered that beyond using programming as a tool for synthesizing sound, he loved programming in and of itself.

Eventually he taught himself C++ and C#, and along the way he immersed himself in the ideas of object oriented programming. Like many of us, he gotten bitten by the design patterns bug and a copy of GOF is never far from his hands.

Now his primary interest is in creating a complete MIDI toolkit using the C# language. He hopes to create something that will become an indispensable tool for those wanting to write MIDI applications for the .NET framework.

Besides programming, his other interests are photography and playing his Les Paul guitars.

Comments and Discussions