Click here to Skip to main content
15,892,537 members
Articles / Programming Languages / C#

The List Trifecta, Part 1

Rate me:
Please Sign up or sign in to vote.
4.97/5 (21 votes)
20 May 2016LGPL321 min read 37.1K   161   40  
The A-list is an all-purpose list, a data structure that can support most standard list operation in O(log n) time and does lots of other stuff, too
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Loyc.Math;

namespace Loyc
{
	/// <summary>
	/// A wrapper around <see cref="Stopwatch"/> with the convenient interface
	/// similar to <see cref="SimpleTimer"/>.
	/// </summary>
	/// <remarks>
	/// EzStopwatch is a wrapper around the normal <see cref="Stopwatch"/> that
	/// is less clumsy to use: you can get the elapsed time and restart the 
	/// timer from zero with a single call to Restart(). The Stopwatch class
	/// (prior to .NET 4, anyway) requires you to make three separate method 
	/// calls to do the same thing: you have to call ElapsedMilliseconds, then 
	/// Reset(), then Start().
	/// <para/>
	/// Unlike <see cref="SimpleTimer"/>, this class does not start timing when
	/// it is created, which allows it to be a struct without a constructor.
	/// <para/>
	/// EzStopwatch behaves differently from Stopwatch when restarting, because I
	/// observed a problem using the timer to measure short time intervals. I ran
	/// trials of three operations in a loop, and the loop was programmed to run 
	/// until the total elapsed time for one of the operations exceeded 100 ms. On 
	/// each iteration I restarted the timer three times because there were three 
	/// operations to measure, and when I replaced <see cref="SimpleTimer"/> with 
	/// <see cref="EzStopwatch"/> for greater accuracy, the loop ran forever! The
	/// problem was that (depending on the benchmark's input parameters) the 
	/// operation could take less than 1 millisecond to complete, so 
	/// <see cref="Millisec"/> always returned zero, and the total never reached
	/// 100.
	/// <para/>
	/// To solve this problem, when you "<see cref="Restart"/>" the timer, it is
	/// not completely reset to zero, but rather the current value of Millisec is
	/// subtracted from the timer. This leaves a fractional amount of time less
	/// than 1 millisecond in the timer, so that if you take two measurements 
	/// that each take 0.6 milliseconds, Millisec will return 0 the first time
	/// and 1 the second time, leaving 0.2 milliseconds on the clock.
	/// <para/>
	/// TODO: change interfaces of SimpleTimer and EzStopwatch to better resemble
	/// Stopwatch, even though the behavior of "Pause" and "Resume" is more obvious
	/// than "Stop" and "Start".
	/// </remarks>
	public struct EzStopwatch
	{
		Stopwatch _timer;
		long _base;

		void AutoInit()
		{
			if (_timer == null)
				_timer = new Stopwatch();
		}

		/// <summary>Gets or sets the current time on the clock.</summary>
		/// <remarks>This property can be used whether the timer is running or not,
		/// and it does not affect the value of <see cref="Paused"/>. It is legal
		/// to make the current value negative.</remarks>
		public int Millisec
		{
			get { return (int)LongMillisec; }
			set { LongMillisec = value; }
		}
		public long LongMillisec
		{
			get { return (_timer == null ? 0 : _timer.ElapsedMilliseconds) - _base; }
			set { _base = LongMillisec - value; }
		}

		/// <summary>Restarts the timer from zero (unpausing it if it is paused), 
		/// and returns the number of elapsed milliseconds prior to the reset.</summary>
		public int Restart()
		{
			AutoInit();
			long ms = LongMillisec;
			_base += ms; // reset to zero
			if ((int)_base != _base)
				Reset(); // _base getting huge => re-center on zero
			_timer.Start();
			return (int)MathEx.InRange(ms, int.MinValue, int.MaxValue);
		}
		/// <summary>Resets the timer to 0 and pauses it there.</summary>
		public void Reset()
		{
			_timer.Reset();
			_base = 0;
		}
		public bool Paused 
		{
			get { return _timer != null && _timer.IsRunning; }
		}
		public void Pause()
		{
			if (_timer != null)
				_timer.Stop();
		}
		public void Resume()
		{
			AutoInit();
			_timer.Start();
		}
		/// <summary>Restarts the timer from zero if the specified number of 
		/// milliseconds have passed, and returns the former value of Millisec.</summary>
		/// <returns>If the timer was restarted, this method returns the number of 
		/// elapsed milliseconds prior to the reset. Returns 0 if the timer was not 
		/// reset.</returns>
		/// <remarks>If this method resets a paused timer, it remains paused but 
		/// Millisec is set to zero.</remarks>
		public int ClearAfter(int minimumMillisec)
		{
			long millisec = Millisec;
			if (millisec < minimumMillisec)
				return 0;
			else {
				Millisec = 0;
				return (int)millisec;
			}
		}
	}
}

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 GNU Lesser General Public License (LGPLv3)


Written By
Software Developer None
Canada Canada
Since I started programming when I was 11, I wrote the SNES emulator "SNEqr", the FastNav mapping component, the Enhanced C# programming language (in progress), the parser generator LLLPG, and LES, a syntax to help you start building programming languages, DSLs or build systems.

My overall focus is on the Language of your choice (Loyc) initiative, which is about investigating ways to improve interoperability between programming languages and putting more power in the hands of developers. I'm also seeking employment.

Comments and Discussions