diff --git a/Documents/DoubleConverter.md b/Documents/DoubleConverter.md
new file mode 100644
index 0000000..21af878
--- /dev/null
+++ b/Documents/DoubleConverter.md
@@ -0,0 +1,229 @@
+Double Converter
+================
+
+Originally from http://www.yoda.arachsys.com/csharp/DoubleConverter.cs
+
+This class converts doubles to strings without relying on the current locale.
+The problem itself is quite deep and in the C++ side (including the supporting
+libraries used to implement the .NET BCL), various techniques such as Dragon4,
+Ryu and DragonBox are used.
+
+The main problems are a) speed and b) full round-trip support such that a float
+converted to a string (even if the decimal places are an infinite series or
+have been rounded) will parse back to the exact same floating point number.
+
+ using System;
+ using System.Globalization;
+
+ ///
+ /// A class to allow the conversion of doubles to string representations of
+ /// their exact decimal values. The implementation aims for readability over
+ /// efficiency.
+ ///
+ public class DoubleConverter
+ {
+ ///
+ /// Converts the given double to a string representation of its
+ /// exact decimal value.
+ ///
+ /// The double to convert.
+ /// A string representation of the double's exact decimal value.
+ public static string ToExactString (double d)
+ {
+ if (double.IsPositiveInfinity(d))
+ return "+Infinity";
+ if (double.IsNegativeInfinity(d))
+ return "-Infinity";
+ if (double.IsNaN(d))
+ return "NaN";
+
+ // Translate the double into sign, exponent and mantissa.
+ long bits = BitConverter.DoubleToInt64Bits(d);
+ // Note that the shift is sign-extended, hence the test against -1 not 1
+ bool negative = (bits < 0);
+ int exponent = (int) ((bits >> 52) & 0x7ffL);
+ long mantissa = bits & 0xfffffffffffffL;
+
+ // Subnormal numbers; exponent is effectively one higher,
+ // but there's no extra normalisation bit in the mantissa
+ if (exponent==0)
+ {
+ exponent++;
+ }
+ // Normal numbers; leave exponent as it is but add extra
+ // bit to the front of the mantissa
+ else
+ {
+ mantissa = mantissa | (1L<<52);
+ }
+
+ // Bias the exponent. It's actually biased by 1023, but we're
+ // treating the mantissa as m.0 rather than 0.m, so we need
+ // to subtract another 52 from it.
+ exponent -= 1075;
+
+ if (mantissa == 0)
+ {
+ return "0";
+ }
+
+ /* Normalize */
+ while((mantissa & 1) == 0)
+ { /* i.e., Mantissa is even */
+ mantissa >>= 1;
+ exponent++;
+ }
+
+ /// Construct a new decimal expansion with the mantissa
+ ArbitraryDecimal ad = new ArbitraryDecimal (mantissa);
+
+ // If the exponent is less than 0, we need to repeatedly
+ // divide by 2 - which is the equivalent of multiplying
+ // by 5 and dividing by 10.
+ if (exponent < 0)
+ {
+ for (int i=0; i < -exponent; i++)
+ ad.MultiplyBy(5);
+ ad.Shift(-exponent);
+ }
+ // Otherwise, we need to repeatedly multiply by 2
+ else
+ {
+ for (int i=0; i < exponent; i++)
+ ad.MultiplyBy(2);
+ }
+
+ // Finally, return the string with an appropriate sign
+ if (negative)
+ return "-"+ad.ToString();
+ else
+ return ad.ToString();
+ }
+
+ /// Private class used for manipulating
+ class ArbitraryDecimal
+ {
+ /// Digits in the decimal expansion, one byte per digit
+ byte[] digits;
+ ///
+ /// How many digits are *after* the decimal point
+ ///
+ int decimalPoint=0;
+
+ ///
+ /// Constructs an arbitrary decimal expansion from the given long.
+ /// The long must not be negative.
+ ///
+ internal ArbitraryDecimal (long x)
+ {
+ string tmp = x.ToString(CultureInfo.InvariantCulture);
+ digits = new byte[tmp.Length];
+ for (int i=0; i < tmp.Length; i++)
+ digits[i] = (byte) (tmp[i]-'0');
+ Normalize();
+ }
+
+ ///
+ /// Multiplies the current expansion by the given amount, which should
+ /// only be 2 or 5.
+ ///
+ internal void MultiplyBy(int amount)
+ {
+ byte[] result = new byte[digits.Length+1];
+ for (int i=digits.Length-1; i >= 0; i--)
+ {
+ int resultDigit = digits[i]*amount+result[i+1];
+ result[i]=(byte)(resultDigit/10);
+ result[i+1]=(byte)(resultDigit%10);
+ }
+ if (result[0] != 0)
+ {
+ digits=result;
+ }
+ else
+ {
+ Array.Copy (result, 1, digits, 0, digits.Length);
+ }
+ Normalize();
+ }
+
+ ///
+ /// Shifts the decimal point; a negative value makes
+ /// the decimal expansion bigger (as fewer digits come after the
+ /// decimal place) and a positive value makes the decimal
+ /// expansion smaller.
+ ///
+ internal void Shift (int amount)
+ {
+ decimalPoint += amount;
+ }
+
+ ///
+ /// Removes leading/trailing zeroes from the expansion.
+ ///
+ internal void Normalize()
+ {
+ int first;
+ for (first=0; first < digits.Length; first++)
+ if (digits[first]!=0)
+ break;
+ int last;
+ for (last=digits.Length-1; last >= 0; last--)
+ if (digits[last]!=0)
+ break;
+
+ if (first==0 && last==digits.Length-1)
+ return;
+
+ byte[] tmp = new byte[last-first+1];
+ for (int i=0; i < tmp.Length; i++)
+ tmp[i]=digits[i+first];
+
+ decimalPoint -= digits.Length-(last+1);
+ digits=tmp;
+ }
+
+ ///
+ /// Converts the value to a proper decimal string representation.
+ ///
+ public override String ToString()
+ {
+ char[] digitString = new char[digits.Length];
+ for (int i=0; i < digits.Length; i++)
+ digitString[i] = (char)(digits[i]+'0');
+
+ // Simplest case - nothing after the decimal point,
+ // and last real digit is non-zero, eg value=35
+ if (decimalPoint==0)
+ {
+ return new string (digitString);
+ }
+
+ // Fairly simple case - nothing after the decimal
+ // point, but some 0s to add, eg value=350
+ if (decimalPoint < 0)
+ {
+ return new string (digitString)+
+ new string ('0', -decimalPoint);
+ }
+
+ // Nothing before the decimal point, eg 0.035
+ if (decimalPoint >= digitString.Length)
+ {
+ return "0."+
+ new string ('0',(decimalPoint-digitString.Length))+
+ new string (digitString);
+ }
+
+ // Most complicated case - part of the string comes
+ // before the decimal point, part comes after it,
+ // eg 3.5
+ return new string (digitString, 0,
+ digitString.Length-decimalPoint)+
+ "."+
+ new string (digitString,
+ digitString.Length-decimalPoint,
+ decimalPoint);
+ }
+ }
+ }
diff --git a/Documents/DoubleConverter.txt b/Documents/DoubleConverter.txt
deleted file mode 100644
index 370bed8..0000000
--- a/Documents/DoubleConverter.txt
+++ /dev/null
@@ -1,218 +0,0 @@
-From http://www.yoda.arachsys.com/csharp/DoubleConverter.cs
-
-
-using System;
-using System.Globalization;
-
-///
-/// A class to allow the conversion of doubles to string representations of
-/// their exact decimal values. The implementation aims for readability over
-/// efficiency.
-///
-public class DoubleConverter
-{
- ///
- /// Converts the given double to a string representation of its
- /// exact decimal value.
- ///
- /// The double to convert.
- /// A string representation of the double's exact decimal value.
- public static string ToExactString (double d)
- {
- if (double.IsPositiveInfinity(d))
- return "+Infinity";
- if (double.IsNegativeInfinity(d))
- return "-Infinity";
- if (double.IsNaN(d))
- return "NaN";
-
- // Translate the double into sign, exponent and mantissa.
- long bits = BitConverter.DoubleToInt64Bits(d);
- // Note that the shift is sign-extended, hence the test against -1 not 1
- bool negative = (bits < 0);
- int exponent = (int) ((bits >> 52) & 0x7ffL);
- long mantissa = bits & 0xfffffffffffffL;
-
- // Subnormal numbers; exponent is effectively one higher,
- // but there's no extra normalisation bit in the mantissa
- if (exponent==0)
- {
- exponent++;
- }
- // Normal numbers; leave exponent as it is but add extra
- // bit to the front of the mantissa
- else
- {
- mantissa = mantissa | (1L<<52);
- }
-
- // Bias the exponent. It's actually biased by 1023, but we're
- // treating the mantissa as m.0 rather than 0.m, so we need
- // to subtract another 52 from it.
- exponent -= 1075;
-
- if (mantissa == 0)
- {
- return "0";
- }
-
- /* Normalize */
- while((mantissa & 1) == 0)
- { /* i.e., Mantissa is even */
- mantissa >>= 1;
- exponent++;
- }
-
- /// Construct a new decimal expansion with the mantissa
- ArbitraryDecimal ad = new ArbitraryDecimal (mantissa);
-
- // If the exponent is less than 0, we need to repeatedly
- // divide by 2 - which is the equivalent of multiplying
- // by 5 and dividing by 10.
- if (exponent < 0)
- {
- for (int i=0; i < -exponent; i++)
- ad.MultiplyBy(5);
- ad.Shift(-exponent);
- }
- // Otherwise, we need to repeatedly multiply by 2
- else
- {
- for (int i=0; i < exponent; i++)
- ad.MultiplyBy(2);
- }
-
- // Finally, return the string with an appropriate sign
- if (negative)
- return "-"+ad.ToString();
- else
- return ad.ToString();
- }
-
- /// Private class used for manipulating
- class ArbitraryDecimal
- {
- /// Digits in the decimal expansion, one byte per digit
- byte[] digits;
- ///
- /// How many digits are *after* the decimal point
- ///
- int decimalPoint=0;
-
- ///
- /// Constructs an arbitrary decimal expansion from the given long.
- /// The long must not be negative.
- ///
- internal ArbitraryDecimal (long x)
- {
- string tmp = x.ToString(CultureInfo.InvariantCulture);
- digits = new byte[tmp.Length];
- for (int i=0; i < tmp.Length; i++)
- digits[i] = (byte) (tmp[i]-'0');
- Normalize();
- }
-
- ///
- /// Multiplies the current expansion by the given amount, which should
- /// only be 2 or 5.
- ///
- internal void MultiplyBy(int amount)
- {
- byte[] result = new byte[digits.Length+1];
- for (int i=digits.Length-1; i >= 0; i--)
- {
- int resultDigit = digits[i]*amount+result[i+1];
- result[i]=(byte)(resultDigit/10);
- result[i+1]=(byte)(resultDigit%10);
- }
- if (result[0] != 0)
- {
- digits=result;
- }
- else
- {
- Array.Copy (result, 1, digits, 0, digits.Length);
- }
- Normalize();
- }
-
- ///
- /// Shifts the decimal point; a negative value makes
- /// the decimal expansion bigger (as fewer digits come after the
- /// decimal place) and a positive value makes the decimal
- /// expansion smaller.
- ///
- internal void Shift (int amount)
- {
- decimalPoint += amount;
- }
-
- ///
- /// Removes leading/trailing zeroes from the expansion.
- ///
- internal void Normalize()
- {
- int first;
- for (first=0; first < digits.Length; first++)
- if (digits[first]!=0)
- break;
- int last;
- for (last=digits.Length-1; last >= 0; last--)
- if (digits[last]!=0)
- break;
-
- if (first==0 && last==digits.Length-1)
- return;
-
- byte[] tmp = new byte[last-first+1];
- for (int i=0; i < tmp.Length; i++)
- tmp[i]=digits[i+first];
-
- decimalPoint -= digits.Length-(last+1);
- digits=tmp;
- }
-
- ///
- /// Converts the value to a proper decimal string representation.
- ///
- public override String ToString()
- {
- char[] digitString = new char[digits.Length];
- for (int i=0; i < digits.Length; i++)
- digitString[i] = (char)(digits[i]+'0');
-
- // Simplest case - nothing after the decimal point,
- // and last real digit is non-zero, eg value=35
- if (decimalPoint==0)
- {
- return new string (digitString);
- }
-
- // Fairly simple case - nothing after the decimal
- // point, but some 0s to add, eg value=350
- if (decimalPoint < 0)
- {
- return new string (digitString)+
- new string ('0', -decimalPoint);
- }
-
- // Nothing before the decimal point, eg 0.035
- if (decimalPoint >= digitString.Length)
- {
- return "0."+
- new string ('0',(decimalPoint-digitString.Length))+
- new string (digitString);
- }
-
- // Most complicated case - part of the string comes
- // before the decimal point, part comes after it,
- // eg 3.5
- return new string (digitString, 0,
- digitString.Length-decimalPoint)+
- "."+
- new string (digitString,
- digitString.Length-decimalPoint,
- decimalPoint);
- }
- }
-}
diff --git a/Documents/Request Framework.md b/Documents/Request Framework.md
new file mode 100644
index 0000000..610d31a
--- /dev/null
+++ b/Documents/Request Framework.md
@@ -0,0 +1,184 @@
+Request Framework
+=================
+
+These are just some ponderings on the design. Most of what I was trying to accomplish
+via my request framework has long since been covered by System.Threading.Tasks and
+most of what I designed is not in the separate and forgotten Nuclex.Support.Transactions
+library.
+
+
+Ponderings
+----------
+
+The request framework should not require that .NET multithreading is used for
+asynchronous requests.
+
+Otherwise, it would prvent overlapped operations, 3rd party APIs (eg. used
+via P/Invoke) from being able to use the request framework and possibly even
+spawn duplicate implementations.
+
+
+Design using interfaces
+-----------------------
+
+ interface IWaitable {
+
+ /// Fired when the background process has finished
+ ///
+ /// If the process is already finished when a client registers to this event,
+ /// the registered callback will be invoked synchronously right when the
+ /// registration takes place.
+ ///
+ event EventHandler Finished;
+
+ /// Waits until the background process finishes
+ void Wait();
+
+ /// Waits until the background process finishes or a timeout occurs
+ ///
+ /// Number of milliseconds after which to stop waiting and return immediately
+ ///
+ ///
+ /// True if the background process completed, false if the timeout was reached
+ ///
+ bool Wait(int timeoutMilliseconds);
+
+ /// Whether the background process has finished
+ bool Finished { get; }
+
+ // ?
+ /// Wait handle that can be used to wait for multiple waitables
+ ///
+ /// Only use the WaitHandle to wait if you're running in a different thread than
+ /// the request, or you may deadlock. Libraries based on single-threaded
+ /// multitasking may employ concepts such as window message processing to achieve
+ /// parallelism which could be stalled by improper waiting using the WaitHandle
+ /// whereas the Wait() method typically has a suitable implementation compatible
+ /// with the request's requirements.
+ ///
+ WaitHandle WaitHandle { get; }
+
+ }
+
+ interface IThreadedWaitable : IWaitable {
+
+ WaitHandle WaitHandle { get; }
+
+ }
+
+ interface IRequest : IWaitable {
+
+ ///
+ /// Waits for the background process to complete and re-throws the exception to
+ /// the caller when an error has occured
+ ///
+ void Join();
+
+ }
+
+ interface IRequest : IRequest {
+
+ ///
+ /// Waits for the background process to complete and re-throws the exception to
+ /// the caller when an error has occured
+ ///
+ /// The result of the background processing
+ new ResultType Join();
+
+ }
+
+ interface IThreadedRequest : IRequest, IThreadedWaitable { }
+
+ interface IThreadedRequest : IRequest, IThreadedRequest { }
+
+
+Impossible implementation
+-------------------------
+
+ class Request : IRequest {
+
+ event EventHandler Finished;
+ void Wait();
+ bool Wait(int timeoutMilliseconds);
+ bool Finished { get; }
+ void Join();
+ protected virtual void ReraiseExceptions() { }
+
+ }
+
+ class Request : Request, IRequest {
+
+ new ResultType Join();
+ protected abstract ResultType GatherResults();
+
+ }
+
+Do not provide: (see conflict in second version)
+
+ class ThreadedRequest : Request, IThreadedRequest {
+
+ WaitHandle WaitHandle { get; }
+
+ }
+
+ class ThreadedRequest : ThreadedRequest, Request { }
+
+ // However, not providing these, the user would have to rewrite
+ // the complex threading routines everywhere he uses then. Bad.
+
+
+Inelegant implementation
+------------------------
+
+ class Void {}
+
+ class Request : IRequest {
+
+ new ResultType Join();
+ protected abstract ResultType GatherResults();
+
+ }
+
+ class ThreadedRequest : Request { }
+
+ // However, not providing these, the user would have to rewrite
+ // the complex threading routines everywhere he uses then. Bad.
+
+Maybe keeping threaded and non-threaded requests apart is a good thing?
+
+ IWaitable (without Finished event)
+ Waitable (Finished event)
+ Request
+ Request
+
+ IWaitable (without Finished event)
+ ThreadedWaitable (AsyncFinished event)
+ ThreadedRequest
+ ThreadedRequest
+
+Or just dump the WaitHandle schmonder
+
+ Waitable (with virtual protected SyncRoot { get { return this; } })
+ Request
+ Request
+
+ LazyWaitHandle
+ WaitHandle Get(Waitable waitable)
+
+Or use policy classes (waithandle trouble)
+
+ Waitable
+ Request
+ Request
+
+ RequestImpl
+ RequestImpl
+
+ LazyWaitHandle
+ WaitHandle Get(Waitable waitable)
+ WaitHandle Get(Waitable waitable, object syncRoot)
+
+ ThreadPolicy {
+ virtual void lock() {}
+ virtual void unlock() {}
+ }
diff --git a/Documents/Request Framework.txt b/Documents/Request Framework.txt
deleted file mode 100644
index d8feaa0..0000000
--- a/Documents/Request Framework.txt
+++ /dev/null
@@ -1,172 +0,0 @@
-The request framework should not require that .NET multithreading is used for
-asynchronous requests.
-
-Otherwise, it would prvent overlapped operations, 3rd party APIs (eg. used
-via P/Invoke) from being able to use the request framework and possibly even
-spawn duplicate implementations.
-
-
-Design using interfaces:
-
- interface IWaitable {
-
- /// Fired when the background process has finished
- ///
- /// If the process is already finished when a client registers to this event,
- /// the registered callback will be invoked synchronously right when the
- /// registration takes place.
- ///
- event EventHandler Finished;
-
- /// Waits until the background process finishes
- void Wait();
-
- /// Waits until the background process finishes or a timeout occurs
- ///
- /// Number of milliseconds after which to stop waiting and return immediately
- ///
- ///
- /// True if the background process completed, false if the timeout was reached
- ///
- bool Wait(int timeoutMilliseconds);
-
- /// Whether the background process has finished
- bool Finished { get; }
-
-// ?
- /// Wait handle that can be used to wait for multiple waitables
- ///
- /// Only use the WaitHandle to wait if you're running in a different thread than
- /// the request, or you may deadlock. Libraries based on single-threaded
- /// multitasking may employ concepts such as window message processing to achieve
- /// parallelism which could be stalled by improper waiting using the WaitHandle
- /// whereas the Wait() method typically has a suitable implementation compatible
- /// with the request's requirements.
- ///
- WaitHandle WaitHandle { get; }
-
- }
-
- interface IThreadedWaitable : IWaitable {
-
- WaitHandle WaitHandle { get; }
-
- }
-
- interface IRequest : IWaitable {
-
- ///
- /// Waits for the background process to complete and re-throws the exception to
- /// the caller when an error has occured
- ///
- void Join();
-
- }
-
- interface IRequest : IRequest {
-
- ///
- /// Waits for the background process to complete and re-throws the exception to
- /// the caller when an error has occured
- ///
- /// The result of the background processing
- new ResultType Join();
-
- }
-
- interface IThreadedRequest : IRequest, IThreadedWaitable { }
-
- interface IThreadedRequest : IRequest, IThreadedRequest { }
-
-
-Impossible implementation:
-
- class Request : IRequest {
-
- event EventHandler Finished;
- void Wait();
- bool Wait(int timeoutMilliseconds);
- bool Finished { get; }
- void Join();
- protected virtual void ReraiseExceptions() { }
-
- }
-
- class Request : Request, IRequest {
-
- new ResultType Join();
- protected abstract ResultType GatherResults();
-
- }
-
- Do not provide: (see conflict in second version)
-
- class ThreadedRequest : Request, IThreadedRequest {
-
- WaitHandle WaitHandle { get; }
-
- }
-
- class ThreadedRequest : ThreadedRequest, Request { }
-
- // However, not providing these, the user would have to rewrite
- // the complex threading routines everywhere he uses then. Bad.
-
-Inelegant implementation:
-
- class Void {}
-
- class Request : IRequest {
-
- new ResultType Join();
- protected abstract ResultType GatherResults();
-
- }
-
- class ThreadedRequest : Request { }
-
- // However, not providing these, the user would have to rewrite
- // the complex threading routines everywhere he uses then. Bad.
-
-
-
-Maybe keeping threaded and non-threaded requests apart is a good thing?
-
- IWaitable (without Finished event)
- Waitable (Finished event)
- Request
- Request
-
- IWaitable (without Finished event)
- ThreadedWaitable (AsyncFinished event)
- ThreadedRequest
- ThreadedRequest
-
-
-Or just dump the WaitHandle schmonder
-
- Waitable (with virtual protected SyncRoot { get { return this; } })
- Request
- Request
-
- LazyWaitHandle
- WaitHandle Get(Waitable waitable)
-
-
-Or use policy classes (waithandle trouble)
-
- Waitable
- Request
- Request
-
- RequestImpl
- RequestImpl
-
- LazyWaitHandle
- WaitHandle Get(Waitable waitable)
- WaitHandle Get(Waitable waitable, object syncRoot)
-
- ThreadPolicy {
- virtual void lock() {}
- virtual void unlock() {}
- }
diff --git a/Documents/Nuclex.Support.txt b/Documents/ShuffleTableGenerator.md
similarity index 56%
rename from Documents/Nuclex.Support.txt
rename to Documents/ShuffleTableGenerator.md
index 4e29d84..4dd3710 100644
--- a/Documents/Nuclex.Support.txt
+++ b/Documents/ShuffleTableGenerator.md
@@ -1,5 +1,15 @@
- #if DEBUG
-
+Shuffle Table Generator
+=======================
+
+The `LicenseKey` class converts a GUID (or any 128 bits of data) into a license key similar
+to the ones used by Microsoft products (5 groups of 5 alphanumeric characters).
+
+For additional obfuscation, bits can be mapped in a random order. That will offer some
+protection when you have, for example, a running serial number. With shuffling, each
+increment will result in likely several alphanumeric characters changing in seemingly
+unpredictable ways (base-2 to base-36 conversion pretty much guarantees that any small
+change affects many following alphanumeric "digits").
+
/// Generates a new random shuffle table
///
/// Number of iterations in which to randomize the shuffle table
@@ -23,6 +33,3 @@
return shuffleTable;
}
-
- #endif
-