diff --git a/.vs/VolvoWrench/v14/.suo b/.vs/VolvoWrench/v14/.suo index 7e05894..95f8972 100644 Binary files a/.vs/VolvoWrench/v14/.suo and b/.vs/VolvoWrench/v14/.suo differ diff --git a/VolvoWrench/Demo stuff/GoldSource/Verification.Designer.cs b/VolvoWrench/Demo stuff/GoldSource/Verification.Designer.cs index ac0a8c0..583528a 100644 --- a/VolvoWrench/Demo stuff/GoldSource/Verification.Designer.cs +++ b/VolvoWrench/Demo stuff/GoldSource/Verification.Designer.cs @@ -1,6 +1,6 @@ namespace VolvoWrench.Demo_stuff.GoldSource { - partial class Verification + sealed partial class Verification { /// /// Required designer variable. @@ -124,6 +124,9 @@ private void InitializeComponent() this.ShowIcon = false; this.ShowInTaskbar = false; this.Text = "Verification"; + this.DragDrop += new System.Windows.Forms.DragEventHandler(this.Verification_DragDrop); + this.DragEnter += new System.Windows.Forms.DragEventHandler(this.Verification_DragEnter); + this.DragLeave += new System.EventHandler(this.Verification_DragLeave); this.splitContainer1.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); this.splitContainer1.ResumeLayout(false); diff --git a/VolvoWrench/Demo stuff/GoldSource/Verification.cs b/VolvoWrench/Demo stuff/GoldSource/Verification.cs index 1a32129..d08b485 100644 --- a/VolvoWrench/Demo stuff/GoldSource/Verification.cs +++ b/VolvoWrench/Demo stuff/GoldSource/Verification.cs @@ -3,18 +3,21 @@ using System.Drawing; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows.Forms; +using MoreLinq; namespace VolvoWrench.Demo_stuff.GoldSource { - public partial class Verification : Form + public sealed partial class Verification : Form { public List DemopathList; public Verification() { InitializeComponent(); DemopathList = new List(); + AllowDrop = true; } public static Dictionary Df = new Dictionary(); @@ -27,75 +30,119 @@ private void button1_Click(object sender, EventArgs e) Multiselect = true }; - if (of.ShowDialog() == DialogResult.OK) + if (of.ShowDialog() == DialogResult.OK && of.FileNames.Length > 1) { - mrtb.Text = @"Please wait. Parsing demos..."; + Verify(of.FileNames); + } + else + { + mrtb.Text = @"No file selected/bad file/only 1 file selected!"; + } + } + + private void exitToolStripMenuItem_Click(object sender, EventArgs e) + { + this.Close(); + } + + private void demostartCommandToClipboardToolStripMenuItem_Click(object sender, EventArgs e) + { + if (DemopathList.ToArray().Length <= 32) + Clipboard.SetText("startdemos " + DemopathList + .Select(Path.GetFileNameWithoutExtension) + .OrderBy(x => int.Parse(Regex.Match(x+"0", @"\d+").Value)) + .ToList() + .Aggregate((c, n) => c + " " + n)); + else + { + Clipboard.SetText(DemopathList + .Select(Path.GetFileNameWithoutExtension) + .OrderBy(x => int.Parse(Regex.Match(x+"0", @"\d+").Value)) + .Batch(32) + .Aggregate(String.Empty,(x,y) => x + ";startdemos " + (y.Aggregate((c,n) => c + " " + n))).Substring(1)); + + } + using (var ni = new NotifyIcon()) + { + ni.Icon = SystemIcons.Exclamation; + ni.Visible = true; + ni.ShowBalloonTip(5000, "VolvoWrench", "Demo names copied to clipboard", ToolTipIcon.Info); + } + } + + public void Verify(string[] files) + { + mrtb.Text = $@"Please wait. Parsing demos... 0/{files.Length}"; + mrtb.Invalidate(); + mrtb.Update(); + mrtb.Refresh(); + Application.DoEvents(); + var curr = 0; + foreach (var dt in files.Where(file => File.Exists(file) && Path.GetExtension(file) == ".dem")) + { + DemopathList.Add(dt); + Df.Add(dt, CrossDemoParser.Parse(dt)); //If someone bothers me that its slow make it async. + mrtb.Text = $@"Please wait. Parsing demos... {curr++}/{files.Length}"; mrtb.Invalidate(); mrtb.Update(); mrtb.Refresh(); Application.DoEvents(); - foreach (var dt in of.FileNames.Where(file => File.Exists(file) && Path.GetExtension(file) == ".dem")) - { - DemopathList.Add(dt); - Df.Add(dt, CrossDemoParser.Parse(dt)); //If someone bothers me that its slow make it async. - } - if (Df.Any(x => x.Value.Type != Parseresult.GoldSource)) - MessageBox.Show(@"Only goldsource supported"); - else + } + if (Df.Any(x => x.Value.Type != Parseresult.GoldSource)) + MessageBox.Show(@"Only goldsource supported"); + else + { + mrtb.Text = ""; + mrtb.AppendText("" + "\n"); + mrtb.AppendText("Parsed demos. Results:" + "\n"); + mrtb.AppendText("General stats:" + "\n"); + var frametimeMax = new List(); + var frametimeMin = new List(); + var frametimeSum = new List(); + var msecMin = new List(); + var msecMax = new List(); + var avgmsec = new List(); + foreach (var d in Df) { - Task.WaitAll(); - mrtb.Text = ""; - mrtb.AppendText("" + "\n"); - mrtb.AppendText("Parsed demos. Results:" + "\n"); - mrtb.AppendText("General stats:" + "\n"); - var frametimeMax = new List(); - var frametimeMin = new List(); - var frametimeSum = new List(); - var msecMin = new List(); - var msecMax = new List(); - var avgmsec = new List(); - foreach (var d in Df) + float ftm = 0f, ftmx = 0f; + var fts = 0.0; + var count = 0; + int mm = 0, mmx = 0; + long msecSum = 0; + var first = true; + foreach (var f in from entry in d.Value.GsDemoInfo.DirectoryEntries + from frame in entry.Frames + where (int)frame.Key.Type < 2 || (int)frame.Key.Type > 9 + select (GoldSource.NetMsgFrame)frame.Value) { - float ftm = 0f, ftmx = 0f; - var fts = 0.0; - var count = 0; - int mm = 0, mmx = 0; - long msecSum = 0; - var first = true; - foreach (var f in from entry in d.Value.GsDemoInfo.DirectoryEntries - from frame in entry.Frames - where (int) frame.Key.Type < 2 || (int) frame.Key.Type > 9 - select (Demo_stuff.GoldSource.GoldSource.NetMsgFrame) frame.Value) - { - fts += f.RParms.Frametime; - msecSum += f.UCmd.Msec; - count++; + fts += f.RParms.Frametime; + msecSum += f.UCmd.Msec; + count++; - if (first) - { - first = false; - ftm = f.RParms.Frametime; - ftmx = f.RParms.Frametime; - mm = f.UCmd.Msec; - mmx = f.UCmd.Msec; - } - else - { - ftm = Math.Min(ftm, f.RParms.Frametime); - ftmx = Math.Max(ftmx, f.RParms.Frametime); - mm = Math.Min(mm, f.UCmd.Msec); - mmx = Math.Max(mmx, f.UCmd.Msec); - } + if (first) + { + first = false; + ftm = f.RParms.Frametime; + ftmx = f.RParms.Frametime; + mm = f.UCmd.Msec; + mmx = f.UCmd.Msec; + } + else + { + ftm = Math.Min(ftm, f.RParms.Frametime); + ftmx = Math.Max(ftmx, f.RParms.Frametime); + mm = Math.Min(mm, f.UCmd.Msec); + mmx = Math.Max(mmx, f.UCmd.Msec); } - frametimeMax.Add(1/ftmx); - frametimeMin.Add(1/ftm); - frametimeSum.Add(count/fts); - msecMin.Add(1000.0/mm); - msecMax.Add(1000.0/mmx); - avgmsec.Add(1000.0/(msecSum/(double) count)); } - mrtb.AppendText( - $@" + frametimeMax.Add(1 / ftmx); + frametimeMin.Add(1 / ftm); + frametimeSum.Add(count / fts); + msecMin.Add(1000.0 / mm); + msecMax.Add(1000.0 / mmx); + avgmsec.Add(1000.0 / (msecSum / (double)count)); + } + mrtb.AppendText($@" Highest FPS: {(frametimeMin.Max()).ToString("N2")} Lowest FPS: {(frametimeMin.Min()).ToString("N2")} Average FPS: {frametimeSum.Average().ToString("N2")} @@ -104,45 +151,41 @@ from frame in entry.Frames Average msec: {avgmsec.Average().ToString("N2")} FPS Total time of the demos: {Df.Sum(x => x.Value.GsDemoInfo.DirectoryEntries.Sum(y => y.TrackTime))}s" + "\n\n"); - mrtb.AppendText("Demo cheat check:" + "\n"); - foreach (var dem in Df) - { - mrtb.AppendText(Path.GetFileName(dem.Key) + " -> " + dem.Value.GsDemoInfo.Header.MapName + "\n"); - foreach (var f in dem.Value.GsDemoInfo.DirectoryEntries.SelectMany(entry => - (from frame in entry.Frames.Where( - x => x.Key.Type == Demo_stuff.GoldSource.GoldSource.DemoFrameType.ConsoleCommand) - select (Demo_stuff.GoldSource.GoldSource.ConsoleCommandFrame) frame.Value into f - let cheats = new List - { + mrtb.AppendText("Demo cheat check:" + "\n"); + foreach (var dem in Df) + { + mrtb.AppendText(Path.GetFileName(dem.Key) + " -> " + dem.Value.GsDemoInfo.Header.MapName + "\n"); + foreach (var f in dem.Value.GsDemoInfo.DirectoryEntries.SelectMany(entry => + (from frame in entry.Frames.Where( + x => x.Key.Type == GoldSource.DemoFrameType.ConsoleCommand) + select (GoldSource.ConsoleCommandFrame)frame.Value into f + let cheats = new List + { "+lookup", "+lookdown", "+left", "+right" //TODO: When yalter is not lazy and adds the anticheat frames add them here. - } where cheats.Contains(f.Command) select f))) {mrtb.AppendText(f.Command + "\n");} - } + } + where cheats.Contains(f.Command) + select f))) { mrtb.AppendText(f.Command + "\n"); } } } - else - { - mrtb.Text = @"No file selected/bad file!"; - } } - private void exitToolStripMenuItem_Click(object sender, EventArgs e) + private void Verification_DragEnter(object sender, DragEventArgs e) { - this.Close(); + if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy; } - private void demostartCommandToClipboardToolStripMenuItem_Click(object sender, EventArgs e) + private void Verification_DragDrop(object sender, DragEventArgs e) { + var dropfiles = (string[])e.Data.GetData(DataFormats.FileDrop); + Verify(dropfiles); + } - Clipboard.SetText(DemopathList.Select(Path.GetFileNameWithoutExtension).ToList().Aggregate((c,n) =>c + " " + n)); - using (var ni = new NotifyIcon()) - { - ni.Icon = SystemIcons.Exclamation; - ni.Visible = true; - ni.ShowBalloonTip(5000, "VolvoWrench", "Demo names copied to clipboard", ToolTipIcon.Info); - } + private void Verification_DragLeave(object sender, EventArgs e) + { + } } } \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Acquire.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Acquire.cs new file mode 100644 index 0000000..f347ecc --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Acquire.cs @@ -0,0 +1,60 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2012 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Ensures that a source sequence of + /// objects are all acquired successfully. If the acquisition of any + /// one fails then those successfully + /// acquired till that point are disposed. + /// + /// Type of elements in sequence. + /// Source sequence of objects. + /// + /// Returns an array of all the acquired + /// object and in source order. + /// + /// + /// This operator executes immediately. + /// + + public static TSource[] Acquire(this IEnumerable source) + where TSource : IDisposable + { + if (source == null) throw new ArgumentNullException("source"); + + var disposables = new List(); + try + { + disposables.AddRange(source); + return disposables.ToArray(); + } + catch + { + foreach (var disposable in disposables) + disposable.Dispose(); + throw; + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Assert.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Assert.cs new file mode 100644 index 0000000..620d9fd --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Assert.cs @@ -0,0 +1,83 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2013 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Asserts that all elements of a sequence meet a given condition + /// otherwise throws an object. + /// + /// Type of elements in sequence. + /// Source sequence. + /// Function that asserts an element of the sequence for a condition. + /// + /// Returns the original sequence. + /// + /// The input sequence + /// contains an element that does not meet the condition being + /// asserted. + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Assert(this IEnumerable source, Func predicate) + { + return Assert(source, predicate, null); + } + + /// + /// Asserts that all elements of a sequence meet a given condition + /// otherwise throws an object. + /// + /// Type of elements in sequence. + /// Source sequence. + /// Function that asserts an element of the input sequence for a condition. + /// Function that returns the object to throw. + /// + /// Returns the original sequence. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable Assert(this IEnumerable source, + Func predicate, Func errorSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (predicate == null) throw new ArgumentNullException("predicate"); + + return AssertImpl(source, predicate, errorSelector ?? delegate { return null; }); + } + + private static IEnumerable AssertImpl(IEnumerable source, + Func predicate, Func errorSelector) + { + foreach (var element in source) + { + var success = predicate(element); + if (!success) + throw errorSelector(element) ?? new InvalidOperationException("Sequence contains an invalid item."); + yield return element; + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/AssertCount.cs b/VolvoWrench/ExtensionMethods/MoreLinq/AssertCount.cs new file mode 100644 index 0000000..46a37d9 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/AssertCount.cs @@ -0,0 +1,124 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + #if MORELINQ + + private static readonly Func defaultErrorSelector = OnAssertCountFailure; + + /// + /// Asserts that a source sequence contains a given count of elements. + /// + /// Type of elements in sequence. + /// Source sequence. + /// Count to assert. + /// + /// Returns the original sequence as long it is contains the + /// number of elements specified by . + /// Otherwise it throws . + /// + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable AssertCount(this IEnumerable source, + int count) + { + if (source == null) throw new ArgumentNullException("source"); + if (count < 0) throw new ArgumentOutOfRangeException("count"); + + return AssertCountImpl(source, count, defaultErrorSelector); + } + + /// + /// Asserts that a source sequence contains a given count of elements. + /// A parameter specifies the exception to be thrown. + /// + /// Type of elements in sequence. + /// Source sequence. + /// Count to assert. + /// Function that returns the object to throw. + /// + /// Returns the original sequence as long it is contains the + /// number of elements specified by . + /// Otherwise it throws the object + /// returned by calling . + /// + /// + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable AssertCount(this IEnumerable source, + int count, Func errorSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (count < 0) throw new ArgumentException(null, "count"); + if (errorSelector == null) throw new ArgumentNullException("errorSelector"); + + return AssertCountImpl(source, count, errorSelector); + } + + private static Exception OnAssertCountFailure(int cmp, int count) + { + var message = cmp < 0 + ? "Sequence contains too few elements when exactly {0} were expected." + : "Sequence contains too many elements when exactly {0} were expected."; + return new SequenceException(string.Format(message, count.ToString("N0"))); + } + + #endif + + private static IEnumerable AssertCountImpl(IEnumerable source, + int count, Func errorSelector) + { + var collection = source as ICollection; // Optimization for collections + if (collection != null) + { + if (collection.Count != count) + throw errorSelector(collection.Count.CompareTo(count), count); + return source; + } + + return ExpectingCountYieldingImpl(source, count, errorSelector); + } + + private static IEnumerable ExpectingCountYieldingImpl(IEnumerable source, + int count, Func errorSelector) + { + var iterations = 0; + foreach (var element in source) + { + iterations++; + if (iterations > count) + { + throw errorSelector(1, count); + } + yield return element; + } + if (iterations != count) + { + throw errorSelector(-1, count); + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/AtLeast.cs b/VolvoWrench/ExtensionMethods/MoreLinq/AtLeast.cs new file mode 100644 index 0000000..ef47799 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/AtLeast.cs @@ -0,0 +1,63 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2015 sholland. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Returns true when the number of elements in the given sequence is greater than + /// or equal to the given integer. + /// This method throws an exception if the given integer is negative. + /// + /// + /// The number of items streamed will be less than or equal to the given integer. + /// + /// Element type of sequence + /// The source sequence + /// The minimum number of items a sequence must have for this + /// function to return true + /// source is null + /// count is negative + /// true if the number of elements in the sequence is greater than + /// or equal to the given integer or false otherwise. + /// + /// + /// var numbers = { 123, 456, 789 }; + /// var result = numbers.AtLeast(2); + /// + /// The result variable will contain true. + /// + public static bool AtLeast(this IEnumerable source, int count) + { + if (source == null) throw new ArgumentNullException("source"); + if (count < 0) throw new ArgumentOutOfRangeException("count", "The count must not be negative."); + + var collection = source as ICollection; + if (collection != null) + { + return collection.Count >= count; + } + + return source.Take(count).Count() == count; + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Batch.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Batch.cs new file mode 100644 index 0000000..9a33385 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Batch.cs @@ -0,0 +1,106 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Batches the source sequence into sized buckets. + /// + /// Type of elements in sequence. + /// The source sequence. + /// Size of buckets. + /// A sequence of equally sized buckets containing elements of the source collection. + /// + /// This operator uses deferred execution and streams its results (buckets and bucket content). + /// It is also identical to . + /// + + public static IEnumerable> Batch(this IEnumerable source, int size) + { + return Batch(source, size, x => x); + } + + /// + /// Batches the source sequence into sized buckets and applies a projection to each bucket. + /// + /// Type of elements in sequence. + /// Type of result returned by . + /// The source sequence. + /// Size of buckets. + /// The projection to apply to each bucket. + /// A sequence of projections on equally sized buckets containing elements of the source collection. + /// + /// This operator uses deferred execution and streams its results (buckets and bucket content). + /// It is also identical to . + /// + + public static IEnumerable Batch(this IEnumerable source, int size, + Func, TResult> resultSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (size <= 0) throw new ArgumentOutOfRangeException("size"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + return BatchImpl(source, size, resultSelector); + } + + private static IEnumerable BatchImpl(this IEnumerable source, int size, + Func, TResult> resultSelector) + { + Debug.Assert(source != null); + Debug.Assert(size > 0); + Debug.Assert(resultSelector != null); + + TSource[] bucket = null; + var count = 0; + + foreach (var item in source) + { + if (bucket == null) + { + bucket = new TSource[size]; + } + + bucket[count++] = item; + + // The bucket is fully buffered before it's yielded + if (count != size) + { + continue; + } + + // Select is necessary so bucket contents are streamed too + yield return resultSelector(bucket.Select(x => x)); + + bucket = null; + count = 0; + } + + // Return the last bucket with all remaining elements + if (bucket != null && count > 0) + { + yield return resultSelector(bucket.Take(count)); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Cartesian.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Cartesian.cs new file mode 100644 index 0000000..bcdfe41 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Cartesian.cs @@ -0,0 +1,49 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Returns the Cartesian product of two sequences by combining each element of the first set with each in the second + /// and applying the user=define projection to the pair. + /// + /// The type of the elements of + /// The type of the elements of + /// The type of the elements of the result sequence + /// The first sequence of elements + /// The second sequence of elements + /// A projection function that combines elements from both sequences + /// A sequence representing the Cartesian product of the two source sequences + + public static IEnumerable Cartesian(this IEnumerable first, IEnumerable second, Func resultSelector) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return from item1 in first + from item2 in second // TODO buffer to avoid multiple enumerations + select resultSelector(item1, item2); + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Concat.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Concat.cs new file mode 100644 index 0000000..48dd838 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Concat.cs @@ -0,0 +1,56 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using LinqEnumerable = System.Linq.Enumerable; + + static partial class MoreEnumerable + { + /// + /// Returns a sequence consisting of the head element and the given tail elements. + /// + /// Type of sequence + /// Head element of the new sequence. + /// All elements of the tail. Must not be null. + /// A sequence consisting of the head elements and the given tail elements. + /// This operator uses deferred execution and streams its results. + + public static IEnumerable Concat(this T head, IEnumerable tail) + { + if (tail == null) throw new ArgumentNullException("tail"); + return tail.Prepend(head); + } + + /// + /// Returns a sequence consisting of the head elements and the given tail element. + /// + /// Type of sequence + /// All elements of the head. Must not be null. + /// Tail element of the new sequence. + /// A sequence consisting of the head elements and the given tail element. + /// This operator uses deferred execution and streams its results. + + public static IEnumerable Concat(this IEnumerable head, T tail) + { + if (head == null) throw new ArgumentNullException("head"); + return LinqEnumerable.Concat(head, LinqEnumerable.Repeat(tail, 1)); + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Consume.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Consume.cs new file mode 100644 index 0000000..dce3935 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Consume.cs @@ -0,0 +1,40 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Completely consumes the given sequence. This method uses immediate execution, + /// and doesn't store any data during execution. + /// + /// Element type of the sequence + /// Source to consume + + public static void Consume(this IEnumerable source) + { + if (source == null) throw new ArgumentNullException("source"); + foreach (var element in source) + { + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/DistinctBy.cs b/VolvoWrench/ExtensionMethods/MoreLinq/DistinctBy.cs new file mode 100644 index 0000000..c61208d --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/DistinctBy.cs @@ -0,0 +1,86 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns all distinct elements of the given source, where "distinctness" + /// is determined via a projection and the default equality comparer for the projected type. + /// + /// + /// This operator uses deferred execution and streams the results, although + /// a set of already-seen keys is retained. If a key is seen multiple times, + /// only the first element with that key is returned. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Projection for determining "distinctness" + /// A sequence consisting of distinct elements from the source sequence, + /// comparing them by the specified key projection. + + public static IEnumerable DistinctBy(this IEnumerable source, + Func keySelector) + { + return source.DistinctBy(keySelector, null); + } + + /// + /// Returns all distinct elements of the given source, where "distinctness" + /// is determined via a projection and the specified comparer for the projected type. + /// + /// + /// This operator uses deferred execution and streams the results, although + /// a set of already-seen keys is retained. If a key is seen multiple times, + /// only the first element with that key is returned. + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Projection for determining "distinctness" + /// The equality comparer to use to determine whether or not keys are equal. + /// If null, the default equality comparer for TSource is used. + /// A sequence consisting of distinct elements from the source sequence, + /// comparing them by the specified key projection. + + public static IEnumerable DistinctBy(this IEnumerable source, + Func keySelector, IEqualityComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + return DistinctByImpl(source, keySelector, comparer); + } + + private static IEnumerable DistinctByImpl(IEnumerable source, + Func keySelector, IEqualityComparer comparer) + { + var knownKeys = new HashSet(comparer); + foreach (var element in source) + { + if (knownKeys.Add(keySelector(element))) + { + yield return element; + } + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/EndsWith.cs b/VolvoWrench/ExtensionMethods/MoreLinq/EndsWith.cs new file mode 100644 index 0000000..3521c2a --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/EndsWith.cs @@ -0,0 +1,81 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2016 Andreas Gullberg Larsen. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Determines whether the end of the first sequence is equivalent to + /// the second sequence, using the default equality comparer. + /// + /// Type of elements. + /// The sequence to check. + /// The sequence to compare to. + /// + /// true if ends with elements + /// equivalent to . + /// + /// + /// This is the equivalent of + /// and + /// it calls using + /// on pairs of elements at + /// the same index. + /// + public static bool EndsWith(this IEnumerable first, IEnumerable second) + { + return EndsWith(first, second, null); + } + + /// + /// Determines whether the end of the first sequence is equivalent to + /// the second sequence, using the specified element equality comparer. + /// + /// Type of elements. + /// The sequence to check. + /// The sequence to compare to. + /// Equality comparer to use. + /// + /// true if ends with elements + /// equivalent to . + /// + /// + /// This is the equivalent of + /// and it calls + /// on pairs of + /// elements at the same index. + /// + public static bool EndsWith(this IEnumerable first, IEnumerable second, IEqualityComparer comparer) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + + comparer = comparer ?? EqualityComparer.Default; + + var secondCollection = second as ICollection ?? second.ToList(); + using (var firstIter = first.TakeLast(secondCollection.Count).GetEnumerator()) + { + return secondCollection.All(item => firstIter.MoveNext() && comparer.Equals(firstIter.Current, item)); + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/EquiZip.cs b/VolvoWrench/ExtensionMethods/MoreLinq/EquiZip.cs new file mode 100644 index 0000000..4453cca --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/EquiZip.cs @@ -0,0 +1,190 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns a projection of tuples, where each tuple contains the N-th element + /// from each of the argument sequences. + /// + /// + /// + /// int[] numbers = { 1, 2, 3, 4 }; + /// string[] letters = { "A", "B", "C", "D" }; + /// var zipped = numbers.EquiZip(letters, (n, l) => n + l); + /// + /// The zipped variable, when iterated over, will yield "1A", "2B", "3C", "4D" in turn. + /// + /// Type of elements in first sequence + /// Type of elements in second sequence + /// Type of elements in result sequence + /// First sequence + /// Second sequence + /// Function to apply to each pair of elements + /// + /// A sequence that contains elements of the two input sequences, + /// combined by . + /// + /// + /// If the two input sequences are of different lengths then + /// is thrown. + /// This operator uses deferred execution and streams its results. + /// + + public static IEnumerable EquiZip(this IEnumerable first, + IEnumerable second, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return EquiZipImpl(first, second, null, null, (a, b, c, d) => resultSelector(a, b)); + } + + /// + /// Returns a projection of tuples, where each tuple contains the N-th element + /// from each of the argument sequences. + /// + /// + /// If the three input sequences are of different lengths then + /// is thrown. + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// var numbers = { 1, 2, 3, 4 }; + /// var letters = { "A", "B", "C", "D" }; + /// var chars = { 'a', 'b', 'c', 'd' }; + /// var zipped = numbers.EquiZip(letters, chars, (n, l, c) => n + l + c); + /// + /// The zipped variable, when iterated over, will yield "1Aa", "2Bb", "3Cc", "4Dd" in turn. + /// + /// Type of elements in first sequence + /// Type of elements in second sequence + /// Type of elements in third sequence + /// Type of elements in result sequence + /// First sequence + /// Second sequence + /// Third sequence + /// Function to apply to each triplet of elements + /// + /// A sequence that contains elements of the three input sequences, + /// combined by . + /// + + public static IEnumerable EquiZip(this IEnumerable first, + IEnumerable second, IEnumerable third, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (third == null) throw new ArgumentNullException("third"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return EquiZipImpl(first, second, third, null, (a, b, c, _) => resultSelector(a, b, c)); + } + + /// + /// Returns a projection of tuples, where each tuple contains the N-th element + /// from each of the argument sequences. + /// + /// + /// If the three input sequences are of different lengths then + /// is thrown. + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// var numbers = { 1, 2, 3, 4 }; + /// var letters = { "A", "B", "C", "D" }; + /// var chars = { 'a', 'b', 'c', 'd' }; + /// var flags = { true, false, true, false }; + /// var zipped = numbers.EquiZip(letters, chars, flags, (n, l, c, f) => n + l + c + f); + /// + /// The zipped variable, when iterated over, will yield "1AaTrue", "2BbFalse", "3CcTrue", "4DdFalse" in turn. + /// + /// Type of elements in first sequence + /// Type of elements in second sequence + /// Type of elements in third sequence + /// Type of elements in fourth sequence + /// Type of elements in result sequence + /// First sequence + /// Second sequence + /// Third sequence + /// Fourth sequence + /// Function to apply to each quadruplet of elements + /// + /// A sequence that contains elements of the four input sequences, + /// combined by . + /// + + public static IEnumerable EquiZip(this IEnumerable first, + IEnumerable second, IEnumerable third, IEnumerable fourth, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (third == null) throw new ArgumentNullException("third"); + if (fourth == null) throw new ArgumentNullException("fourth"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return EquiZipImpl(first, second, third, fourth, resultSelector); + } + + static IEnumerable EquiZipImpl( + IEnumerable first, + IEnumerable second, + IEnumerable third, + IEnumerable fourth, + Func resultSelector) + { + using (var e1 = first.GetEnumerator()) + using (var e2 = second.GetEnumerator()) + using (var e3 = third != null ? third.GetEnumerator() : null) + using (var e4 = fourth != null ? fourth.GetEnumerator() : null) + { + while (e1.MoveNext()) + { + bool m2, m3 = false; + if ((m2 = e2.MoveNext()) && (m3 = (e3 == null || e3.MoveNext())) + && ((e4 == null || e4.MoveNext()))) + { + yield return resultSelector(e1.Current, e2.Current, + e3 != null ? e3.Current : default(T3), + e4 != null ? e4.Current : default(T4)); + } + else + { + var message = string.Format("{0} sequence too short.", !m2 ? "Second" : !m3 ? "Third" : "Fourth"); + throw new InvalidOperationException(message); + } + } + if (e2.MoveNext() || (e3 != null && e3.MoveNext()) + || (e4 != null && e4.MoveNext())) + { + throw new InvalidOperationException("First sequence too short."); + } + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/ExceptBy.cs b/VolvoWrench/ExtensionMethods/MoreLinq/ExceptBy.cs new file mode 100644 index 0000000..0de05fb --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/ExceptBy.cs @@ -0,0 +1,102 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Returns the set of elements in the first sequence which aren't + /// in the second sequence, according to a given key selector. + /// + /// + /// This is a set operation; if multiple elements in have + /// equal keys, only the first such element is returned. + /// This operator uses deferred execution and streams the results, although + /// a set of keys from is immediately selected and retained. + /// + /// The type of the elements in the input sequences. + /// The type of the key returned by . + /// The sequence of potentially included elements. + /// The sequence of elements whose keys may prevent elements in + /// from being returned. + /// The mapping from source element to key. + /// A sequence of elements from whose key was not also a key for + /// any element in . + + public static IEnumerable ExceptBy(this IEnumerable first, + IEnumerable second, + Func keySelector) + { + return ExceptBy(first, second, keySelector, null); + } + + /// + /// Returns the set of elements in the first sequence which aren't + /// in the second sequence, according to a given key selector. + /// + /// + /// This is a set operation; if multiple elements in have + /// equal keys, only the first such element is returned. + /// This operator uses deferred execution and streams the results, although + /// a set of keys from is immediately selected and retained. + /// + /// The type of the elements in the input sequences. + /// The type of the key returned by . + /// The sequence of potentially included elements. + /// The sequence of elements whose keys may prevent elements in + /// from being returned. + /// The mapping from source element to key. + /// The equality comparer to use to determine whether or not keys are equal. + /// If null, the default equality comparer for TSource is used. + /// A sequence of elements from whose key was not also a key for + /// any element in . + + public static IEnumerable ExceptBy(this IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + return ExceptByImpl(first, second, keySelector, keyComparer); + } + + private static IEnumerable ExceptByImpl(this IEnumerable first, + IEnumerable second, + Func keySelector, + IEqualityComparer keyComparer) + { + var keys = new HashSet(second.Select(keySelector), keyComparer); + foreach (var element in first) + { + var key = keySelector(element); + if (keys.Contains(key)) + { + continue; + } + yield return element; + keys.Add(key); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Exclude.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Exclude.cs new file mode 100644 index 0000000..35429bb --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Exclude.cs @@ -0,0 +1,61 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + public static partial class MoreEnumerable + { + /// + /// Excludes elements from a sequence starting at a given index + /// + /// The type of the elements of the sequence + /// The sequence to exclude elements from + /// The zero-based index at which to begin excluding elements + /// The number of elements to exclude + /// A sequence that excludes the specified portion of elements + + public static IEnumerable Exclude(this IEnumerable sequence, int startIndex, int count) + { + if (sequence == null) throw new ArgumentNullException("sequence"); + if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex"); + if (count < 0) throw new ArgumentOutOfRangeException("count"); + + return ExcludeImpl(sequence, startIndex, count); + } + + private static IEnumerable ExcludeImpl(IEnumerable sequence, int startIndex, int count) + { + var index = -1; + var endIndex = startIndex + count; + using (var iter = sequence.GetEnumerator()) + { + // yield the first part of the sequence + while (iter.MoveNext() && ++index < startIndex) + yield return iter.Current; + // skip the next part (up to count items) + while (++index < endIndex && iter.MoveNext()) + continue; + // yield the remainder of the sequence + while (iter.MoveNext()) + yield return iter.Current; + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/FallbackIfEmpty.cs b/VolvoWrench/ExtensionMethods/MoreLinq/FallbackIfEmpty.cs new file mode 100644 index 0000000..e7088eb --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/FallbackIfEmpty.cs @@ -0,0 +1,207 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2016 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Returns the elements of the specified sequence or the specified + /// value in a singleton collection if the sequence is empty. + /// + /// The type of the elements in the sequences. + /// The source sequence. + /// The value to return in a singleton + /// collection if is empty. + /// + /// An that contains + /// if is empty; otherwise, . + /// + /// + /// + /// var numbers = { 123, 456, 789 }; + /// var result = numbers.Where(x => x == 100).FallbackIfEmpty(-1).Single(); + /// + /// The result variable will contain -1. + /// + + public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback) + { + if (source == null) throw new ArgumentNullException("source"); + return FallbackIfEmptyImpl(source, 1, fallback, default(T), default(T), default(T), null); + } + + /// + /// Returns the elements of a sequence, but if it is empty then + /// returns an altenate sequence of values. + /// + /// The type of the elements in the sequences. + /// The source sequence. + /// First value of the alternate sequence that + /// is returned if is empty. + /// Second value of the alternate sequence that + /// is returned if is empty. + /// + /// An that containing fallback values + /// if is empty; otherwise, . + /// + + public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback1, T fallback2) + { + if (source == null) throw new ArgumentNullException("source"); + return FallbackIfEmptyImpl(source, 2, fallback1, fallback2, default(T), default(T), null); + } + + /// + /// Returns the elements of a sequence, but if it is empty then + /// returns an altenate sequence of values. + /// + /// The type of the elements in the sequences. + /// The source sequence. + /// First value of the alternate sequence that + /// is returned if is empty. + /// Second value of the alternate sequence that + /// is returned if is empty. + /// Third value of the alternate sequence that + /// is returned if is empty. + /// + /// An that containing fallback values + /// if is empty; otherwise, . + /// + + public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback1, T fallback2, T fallback3) + { + if (source == null) throw new ArgumentNullException("source"); + return FallbackIfEmptyImpl(source, 3, fallback1, fallback2, fallback3, default(T), null); + } + + /// + /// Returns the elements of a sequence, but if it is empty then + /// returns an altenate sequence of values. + /// + /// The type of the elements in the sequences. + /// The source sequence. + /// First value of the alternate sequence that + /// is returned if is empty. + /// Second value of the alternate sequence that + /// is returned if is empty. + /// Third value of the alternate sequence that + /// is returned if is empty. + /// Fourth value of the alternate sequence that + /// is returned if is empty. + /// + /// An that containing fallback values + /// if is empty; otherwise, . + /// + + public static IEnumerable FallbackIfEmpty(this IEnumerable source, T fallback1, T fallback2, T fallback3, T fallback4) + { + if (source == null) throw new ArgumentNullException("source"); + return FallbackIfEmptyImpl(source, 4, fallback1, fallback2, fallback3, fallback4, null); + } + + /// + /// Returns the elements of a sequence, but if it is empty then + /// returns an altenate sequence from an array of values. + /// + /// The type of the elements in the sequences. + /// The source sequence. + /// The array that is returned as the alternate + /// sequence if is empty. + /// + /// An that containing fallback values + /// if is empty; otherwise, . + /// + + public static IEnumerable FallbackIfEmpty(this IEnumerable source, params T[] fallback) + { + if (source == null) throw new ArgumentNullException("source"); + if (fallback == null) throw new ArgumentNullException("fallback"); + return source.FallbackIfEmpty((IEnumerable)fallback); + } + + /// + /// Returns the elements of a sequence, but if it is empty then + /// returns an altenate sequence of values. + /// + /// The type of the elements in the sequences. + /// The source sequence. + /// The alternate sequence that is returned + /// if is empty. + /// + /// An that containing fallback values + /// if is empty; otherwise, . + /// + + public static IEnumerable FallbackIfEmpty(this IEnumerable source, IEnumerable fallback) + { + if (source == null) throw new ArgumentNullException("source"); + if (fallback == null) throw new ArgumentNullException("fallback"); + return FallbackIfEmptyImpl(source, 0, default(T), default(T), default(T), default(T), fallback); + } + + static IEnumerable FallbackIfEmptyImpl(IEnumerable source, + int? count, T fallback1, T fallback2, T fallback3, T fallback4, + IEnumerable fallback) + { + var collection = source as ICollection; + if (collection != null && collection.Count == 0) + { + // + // Replace the empty collection with an empty sequence and + // carry on. LINQ's Enumerable.Empty is implemented + // intelligently to return the same enumerator instance and so + // does not incur an allocation. However, the same cannot be + // said for a collection like an empty array or list. This + // permits the rest of the logic while keeping the call to + // source.GetEnumerator() cheap. + // + + source = Enumerable.Empty(); + } + + using (var e = source.GetEnumerator()) + { + if (e.MoveNext()) + { + do { yield return e.Current; } + while (e.MoveNext()); + } + else + { + e.Dispose(); // eager disposal + if (count > 0 && count <= 4) + { + yield return fallback1; + if (count > 1) yield return fallback2; + if (count > 2) yield return fallback3; + if (count > 3) yield return fallback4; + } + else + { + foreach (var item in fallback) + yield return item; + } + } + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Fold.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Fold.cs new file mode 100644 index 0000000..fac137a --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Fold.cs @@ -0,0 +1,64 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2013 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + static TResult FoldImpl(IEnumerable source, int count, + Func folder1, + Func folder2, + Func folder3, + Func folder4) + { + if (source == null) throw new ArgumentNullException("source"); + if ( count == 1 && folder1 == null + || count == 2 && folder2 == null + || count == 3 && folder3 == null + || count == 4 && folder4 == null) + { // ReSharper disable NotResolvedInText + throw new ArgumentNullException("folder"); // ReSharper restore NotResolvedInText + } + + var elements = new T[count]; + foreach (var e in AssertCountImpl(source.Index(), count, OnFolderSourceSizeErrorSelector)) + elements[e.Key] = e.Value; + + switch (count) + { + case 1: return folder1(elements[0]); + case 2: return folder2(elements[0], elements[1]); + case 3: return folder3(elements[0], elements[1], elements[2]); + case 4: return folder4(elements[0], elements[1], elements[2], elements[3]); + default: throw new NotSupportedException(); + } + } + + static readonly Func OnFolderSourceSizeErrorSelector = OnFolderSourceSizeError; + + static Exception OnFolderSourceSizeError(int cmp, int count) + { + var message = cmp < 0 + ? "Sequence contains too few elements when exactly {0} {1} expected." + : "Sequence contains too many elements when exactly {0} {1} expected."; + return new Exception(string.Format(message, count.ToString("N0"), count == 1 ? "was" : "were")); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Fold.g.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Fold.g.cs new file mode 100644 index 0000000..7fb57a3 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Fold.g.cs @@ -0,0 +1,102 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2013 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + partial class MoreEnumerable + { + /// + /// Returns the result of applying a function to a sequence of + /// 1 element. + /// + /// + /// This operator uses immediate execution and effectively buffers + /// as many items of the source sequence as necessary. + /// + /// Type of element in the source sequence + /// Type of the result + /// The sequence of items to fold. + /// Function to apply to the elements in the sequence. + /// The folded value returned by . + + public static TResult Fold(this IEnumerable source, Func folder) + { + return FoldImpl(source, 1, folder, null, null, null); + } + + /// + /// Returns the result of applying a function to a sequence of + /// 2 elements. + /// + /// + /// This operator uses immediate execution and effectively buffers + /// as many items of the source sequence as necessary. + /// + /// Type of element in the source sequence + /// Type of the result + /// The sequence of items to fold. + /// Function to apply to the elements in the sequence. + /// The folded value returned by . + + public static TResult Fold(this IEnumerable source, Func folder) + { + return FoldImpl(source, 2, null, folder, null, null); + } + + /// + /// Returns the result of applying a function to a sequence of + /// 3 elements. + /// + /// + /// This operator uses immediate execution and effectively buffers + /// as many items of the source sequence as necessary. + /// + /// Type of element in the source sequence + /// Type of the result + /// The sequence of items to fold. + /// Function to apply to the elements in the sequence. + /// The folded value returned by . + + public static TResult Fold(this IEnumerable source, Func folder) + { + return FoldImpl(source, 3, null, null, folder, null); + } + + /// + /// Returns the result of applying a function to a sequence of + /// 4 elements. + /// + /// + /// This operator uses immediate execution and effectively buffers + /// as many items of the source sequence as necessary. + /// + /// Type of element in the source sequence + /// Type of the result + /// The sequence of items to fold. + /// Function to apply to the elements in the sequence. + /// The folded value returned by . + + public static TResult Fold(this IEnumerable source, Func folder) + { + return FoldImpl(source, 4, null, null, null, folder); + } + + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/ForEach.cs b/VolvoWrench/ExtensionMethods/MoreLinq/ForEach.cs new file mode 100644 index 0000000..cb60229 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/ForEach.cs @@ -0,0 +1,60 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Immediately executes the given action on each element in the source sequence. + /// + /// The type of the elements in the sequence + /// The sequence of elements + /// The action to execute on each element + + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null) throw new ArgumentNullException("source"); + if (action == null) throw new ArgumentNullException("action"); + + foreach (var element in source) + action(element); + } + + /// + /// Immediately executes the given action on each element in the source sequence. + /// Each element's index is used in the logic of the action. + /// + /// The type of the elements in the sequence + /// The sequence of elements + /// The action to execute on each element; the second parameter + /// of the action represents the index of the source element. + + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null) throw new ArgumentNullException("source"); + if (action == null) throw new ArgumentNullException("action"); + + var index = 0; + foreach (var element in source) + action(element, index++); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/FullGroupJoin.cs b/VolvoWrench/ExtensionMethods/MoreLinq/FullGroupJoin.cs new file mode 100644 index 0000000..3578e35 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/FullGroupJoin.cs @@ -0,0 +1,118 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2015 Felipe Sateler. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + // Inspiration & credit: http://stackoverflow.com/a/13503860/6682 + static partial class MoreEnumerable + { + /// + /// Performs a Full Group Join between the and sequences. + /// + /// + /// This operator uses deferred execution and streams the results. + /// The results are yielded in the order of the elements found in the first sequence + /// followed by those found only in the second. In addition, the callback responsible + /// for projecting the results is supplied with sequences which preserve their source order. + /// + /// The type of the elements in the first input sequence + /// The type of the elements in the first input sequence + /// The type of the key to use to join + /// The type of the elements of the resulting sequence + /// First sequence + /// Second sequence + /// The mapping from first sequence to key + /// The mapping from second sequence to key + /// Function to apply to each pair of elements plus the key + /// A sequence of elements joined from and . + /// + public static IEnumerable FullGroupJoin(this IEnumerable first, + IEnumerable second, + Func firstKeySelector, + Func secondKeySelector, + Func, IEnumerable, TResult> resultSelector) + { + return FullGroupJoin(first, second, firstKeySelector, secondKeySelector, resultSelector, EqualityComparer.Default); + } + + /// + /// Performs a Full Group Join between the and sequences. + /// + /// + /// This operator uses deferred execution and streams the results. + /// The results are yielded in the order of the elements found in the first sequence + /// followed by those found only in the second. In addition, the callback responsible + /// for projecting the results is supplied with sequences which preserve their source order. + /// + /// The type of the elements in the first input sequence + /// The type of the elements in the first input sequence + /// The type of the key to use to join + /// The type of the elements of the resulting sequence + /// First sequence + /// Second sequence + /// The mapping from first sequence to key + /// The mapping from second sequence to key + /// Function to apply to each pair of elements plus the key + /// The equality comparer to use to determine whether or not keys are equal. + /// If null, the default equality comparer for TKey is used. + /// A sequence of elements joined from and . + /// + public static IEnumerable FullGroupJoin(this IEnumerable first, + IEnumerable second, + Func firstKeySelector, + Func secondKeySelector, + Func, IEnumerable, TResult> resultSelector, + IEqualityComparer comparer) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (firstKeySelector == null) throw new ArgumentNullException("firstKeySelector"); + if (secondKeySelector == null) throw new ArgumentNullException("secondKeySelector"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return FullGroupJoinImpl(first, second, firstKeySelector, secondKeySelector, resultSelector, comparer); + } + + private static IEnumerable FullGroupJoinImpl(IEnumerable first, + IEnumerable second, + Func firstKeySelector, + Func secondKeySelector, + Func, IEnumerable, TResult> resultSelector, + IEqualityComparer comparer) + { + comparer = comparer ?? EqualityComparer.Default; + + var alookup = Lookup.CreateForJoin(first, firstKeySelector, comparer); + var blookup = Lookup.CreateForJoin(second, secondKeySelector, comparer); + + foreach (var a in alookup) { + yield return resultSelector(a.Key, a, blookup[a.Key]); + } + + foreach (var b in blookup) { + if (alookup.Contains(b.Key)) + continue; + // We can skip the lookup because we are iterating over keys not found in the first sequence + yield return resultSelector(b.Key, Enumerable.Empty(), b); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Generate.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Generate.cs new file mode 100644 index 0000000..9f1a5fb --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Generate.cs @@ -0,0 +1,60 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Chris Ammerman. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns a sequence of values consecutively generated by a generator function. + /// + /// Type of elements to generate. + /// Value of first element in sequence + /// + /// Generator function which takes the previous series element and uses it to generate the next element. + /// + /// A sequence containing the generated values. + /// + /// This function defers element generation until needed and streams the results. + /// + /// + /// + /// IEnumerable<int> result = Sequence.Generate(2, n => n * n).Take(5); + /// + /// The result variable, when iterated over, will yield 2, 4, 16, 256, and 65536, in turn. + /// + + public static IEnumerable Generate(TResult initial, Func generator) + { + if (generator == null) throw new ArgumentNullException("generator"); + return GenerateImpl(initial, generator); + } + + private static IEnumerable GenerateImpl(TResult initial, Func generator) + { + var current = initial; + while (true) + { + yield return current; + current = generator(current); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/GenerateByIndex.cs b/VolvoWrench/ExtensionMethods/MoreLinq/GenerateByIndex.cs new file mode 100644 index 0000000..8a4dbe3 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/GenerateByIndex.cs @@ -0,0 +1,56 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns a sequence of values based on indexes. + /// + /// + /// The sequence is (practically) infinite + /// - the index ranges from 0 to int.MaxValue inclusive. This function defers + /// execution and streams the results. + /// + /// Type of result to generate + /// Generation function to apply to each index + /// A sequence + + public static IEnumerable GenerateByIndex(Func generator) + { + // Would just use Enumerable.Range(0, int.MaxValue).Select(generator) but that doesn't + // include int.MaxValue. Picky, I know... + if (generator == null) throw new ArgumentNullException("generator"); + return GenerateByIndexImpl(generator); + } + + private static IEnumerable GenerateByIndexImpl(Func generator) + { + // Looping over 0...int.MaxValue inclusive is a pain. Simplest is to go exclusive, + // then go again for int.MaxValue. + for (var i = 0; i < int.MaxValue; i++) + { + yield return generator(i); + } + yield return generator(int.MaxValue); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/GroupAdjacent.cs b/VolvoWrench/ExtensionMethods/MoreLinq/GroupAdjacent.cs new file mode 100644 index 0000000..fea68bb --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/GroupAdjacent.cs @@ -0,0 +1,342 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2012 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Groups the adjacent elements of a sequence according to a + /// specified key selector function. + /// + /// The type of the elements of + /// . + /// The type of the key returned by + /// . + /// A sequence whose elements to group. + /// A function to extract the key for each + /// element. + /// A sequence of groupings where each grouping + /// () contains the key + /// and the adjacent elements in the same order as found in the + /// source sequence. + /// + /// This method is implemented by using deferred execution and + /// streams the groupings. The grouping elements, however, are + /// buffered. Each grouping is therefore yielded as soon as it + /// is complete and before the next grouping occurs. + /// + + public static IEnumerable> GroupAdjacent( + this IEnumerable source, + Func keySelector) + { + return GroupAdjacent(source, keySelector, null); + } + + /// + /// Groups the adjacent elements of a sequence according to a + /// specified key selector function and compares the keys by using a + /// specified comparer. + /// + /// The type of the elements of + /// . + /// The type of the key returned by + /// . + /// A sequence whose elements to group. + /// A function to extract the key for each + /// element. + /// An to + /// compare keys. + /// A sequence of groupings where each grouping + /// () contains the key + /// and the adjacent elements in the same order as found in the + /// source sequence. + /// + /// This method is implemented by using deferred execution and + /// streams the groupings. The grouping elements, however, are + /// buffered. Each grouping is therefore yielded as soon as it + /// is complete and before the next grouping occurs. + /// + + public static IEnumerable> GroupAdjacent( + this IEnumerable source, + Func keySelector, + IEqualityComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return GroupAdjacent(source, keySelector, e => e, comparer); + } + + /// + /// Groups the adjacent elements of a sequence according to a + /// specified key selector function and projects the elements for + /// each group by using a specified function. + /// + /// The type of the elements of + /// . + /// The type of the key returned by + /// . + /// The type of the elements in the + /// resulting groupings. + /// A sequence whose elements to group. + /// A function to extract the key for each + /// element. + /// A function to map each source + /// element to an element in the resulting grouping. + /// A sequence of groupings where each grouping + /// () contains the key + /// and the adjacent elements (of type ) + /// in the same order as found in the source sequence. + /// + /// This method is implemented by using deferred execution and + /// streams the groupings. The grouping elements, however, are + /// buffered. Each grouping is therefore yielded as soon as it + /// is complete and before the next grouping occurs. + /// + + public static IEnumerable> GroupAdjacent( + this IEnumerable source, + Func keySelector, + Func elementSelector) + { + return GroupAdjacent(source, keySelector, elementSelector, null); + } + + /// + /// Groups the adjacent elements of a sequence according to a + /// specified key selector function. The keys are compared by using + /// a comparer and each group's elements are projected by using a + /// specified function. + /// + /// The type of the elements of + /// . + /// The type of the key returned by + /// . + /// The type of the elements in the + /// resulting groupings. + /// A sequence whose elements to group. + /// A function to extract the key for each + /// element. + /// A function to map each source + /// element to an element in the resulting grouping. + /// An to + /// compare keys. + /// A sequence of groupings where each grouping + /// () contains the key + /// and the adjacent elements (of type ) + /// in the same order as found in the source sequence. + /// + /// This method is implemented by using deferred execution and + /// streams the groupings. The grouping elements, however, are + /// buffered. Each grouping is therefore yielded as soon as it + /// is complete and before the next grouping occurs. + /// + + public static IEnumerable> GroupAdjacent( + this IEnumerable source, + Func keySelector, + Func elementSelector, + IEqualityComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + if (elementSelector == null) throw new ArgumentNullException("elementSelector"); + + return GroupAdjacentImpl(source, keySelector, elementSelector, CreateGroupAdjacentGrouping, + comparer ?? EqualityComparer.Default); + } + + /// + /// Groups the adjacent elements of a sequence according to a + /// specified key selector function. The keys are compared by using + /// a comparer and each group's elements are projected by using a + /// specified function. + /// + /// The type of the elements of + /// . + /// The type of the key returned by + /// . + /// The type of the elements in the + /// resulting sequence. + /// A sequence whose elements to group. + /// A function to extract the key for each + /// element. + /// A function to map each key and + /// associated source elements to a result object. + /// A collection of elements of type + /// where each element represents + /// a projection over a group and its key. + /// + /// This method is implemented by using deferred execution and + /// streams the groupings. The grouping elements, however, are + /// buffered. Each grouping is therefore yielded as soon as it + /// is complete and before the next grouping occurs. + /// + + public static IEnumerable GroupAdjacent( + this IEnumerable source, + Func keySelector, + Func, TResult> resultSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + // This should be removed once the target framework is bumped to something that supports covariance + Func, TResult> resultSelectorWrapper = (key, group) => resultSelector(key, group); + + return GroupAdjacentImpl(source, keySelector, i => i, resultSelectorWrapper, + EqualityComparer.Default); + } + + /// + /// Groups the adjacent elements of a sequence according to a + /// specified key selector function. The keys are compared by using + /// a comparer and each group's elements are projected by using a + /// specified function. + /// + /// The type of the elements of + /// . + /// The type of the key returned by + /// . + /// The type of the elements in the + /// resulting sequence. + /// A sequence whose elements to group. + /// A function to extract the key for each + /// element. + /// A function to map each key and + /// associated source elements to a result object. + /// An to + /// compare keys. + /// A collection of elements of type + /// where each element represents + /// a projection over a group and its key. + /// + /// This method is implemented by using deferred execution and + /// streams the groupings. The grouping elements, however, are + /// buffered. Each grouping is therefore yielded as soon as it + /// is complete and before the next grouping occurs. + /// + + public static IEnumerable GroupAdjacent( + this IEnumerable source, + Func keySelector, + Func, TResult> resultSelector, + IEqualityComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + // This should be removed once the target framework is bumped to something that supports covariance + Func, TResult> resultSelectorWrapper = (key, group) => resultSelector(key, group); + return GroupAdjacentImpl(source, keySelector, i => i, resultSelectorWrapper, + comparer ?? EqualityComparer.Default); + } + + private static IEnumerable GroupAdjacentImpl( + this IEnumerable source, + Func keySelector, + Func elementSelector, + Func, TResult> resultSelector, + IEqualityComparer comparer) + { + Debug.Assert(source != null); + Debug.Assert(keySelector != null); + Debug.Assert(elementSelector != null); + Debug.Assert(resultSelector != null); + Debug.Assert(comparer != null); + + using (var iterator = source.GetEnumerator()) + { + var group = default(TKey); + var members = (List) null; + + while (iterator.MoveNext()) + { + var key = keySelector(iterator.Current); + var element = elementSelector(iterator.Current); + if (members != null && comparer.Equals(group, key)) + { + members.Add(element); + } + else + { + if (members != null) + yield return resultSelector(group, members); + group = key; + members = new List { element }; + } + } + + if (members != null) + yield return resultSelector(group, members); + } + } + + private static IGrouping CreateGroupAdjacentGrouping(TKey key, IList members) + { + Debug.Assert(members != null); + return Grouping.Create(key, members.IsReadOnly ? members : new ReadOnlyCollection(members)); + } + + static class Grouping + { + public static Grouping Create(TKey key, IEnumerable members) + { + return new Grouping(key, members); + } + } + + #if !NO_SERIALIZATION_ATTRIBUTES + [Serializable] + #endif + private sealed class Grouping : IGrouping + { + private readonly IEnumerable _members; + + public Grouping(TKey key, IEnumerable members) + { + Debug.Assert(members != null); + Key = key; + _members = members; + } + + public TKey Key { get; private set; } + + public IEnumerator GetEnumerator() + { + return _members.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Incremental.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Incremental.cs new file mode 100644 index 0000000..1a0be40 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Incremental.cs @@ -0,0 +1,89 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + public static partial class MoreEnumerable + { + /// + /// Computes an incremental value between every adjacent element in a sequence: {N,N+1}, {N+1,N+2}, ... + /// + /// + /// The projection function is passed the previous and next element (in that order) and may use + /// either or both in computing the result.
+ /// If the sequence has less than two items, the result is always an empty sequence.
+ /// The number of items in the resulting sequence is always one less than in the source sequence.
+ ///
+ /// The type of the elements in the source sequence + /// The type of the elements in the result sequence + /// The sequence of elements to incrementally process + /// A projection applied to each pair of adjacent elements in the sequence + /// A sequence of elements resulting from projection every adjacent pair + + public static IEnumerable Incremental(this IEnumerable sequence, Func resultSelector) + { + if (sequence == null) throw new ArgumentNullException("sequence"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return IncrementalImpl(sequence, (prev, next, index) => resultSelector(prev, next)); + } + + /// + /// Computes an incremental value between every adjacent element in a sequence: {N,N+1}, {N+1,N+2}, ... + /// + /// + /// The projection function is passed the previous element, next element, and the zero-based index of + /// the next element (in that order) and may use any of these values in computing the result.
+ /// If the sequence has less than two items, the result is always an empty sequence.
+ /// The number of items in the resulting sequence is always one less than in the source sequence.
+ ///
+ /// The type of the elements in the source sequence + /// The type of the elements in the result sequence + /// The sequence of elements to incrementally process + /// A projection applied to each pair of adjacent elements in the sequence + /// A sequence of elements resulting from projection every adjacent pair + + public static IEnumerable Incremental(this IEnumerable sequence, Func resultSelector) + { + if (sequence == null) throw new ArgumentNullException("sequence"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return IncrementalImpl(sequence, resultSelector); + } + + private static IEnumerable IncrementalImpl(IEnumerable sequence, Func resultSelector) + { + using (var iter = sequence.GetEnumerator()) + { + if (iter.MoveNext()) + { + var index = 0; + var prevItem = iter.Current; + while (iter.MoveNext()) + { + var nextItem = iter.Current; + yield return resultSelector(prevItem, nextItem, ++index); + prevItem = nextItem; + } + } + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Index.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Index.cs new file mode 100644 index 0000000..e6abdb3 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Index.cs @@ -0,0 +1,58 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2012 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System.Collections.Generic; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Returns a sequence of + /// where the key is the zero-based index of the value in the source + /// sequence. + /// + /// Type of elements in sequence. + /// The source sequence. + /// A sequence of . + /// This operator uses deferred execution and streams its + /// results. + + public static IEnumerable> Index(this IEnumerable source) + { + return source.Index(0); + } + + /// + /// Returns a sequence of + /// where the key is the index of the value in the source sequence. + /// An additional parameter specifies the starting index. + /// + /// Type of elements in sequence. + /// The source sequence. + /// + /// A sequence of . + /// This operator uses deferred execution and streams its + /// results. + + public static IEnumerable> Index(this IEnumerable source, int startIndex) + { + return source.Select((item, index) => new KeyValuePair(startIndex + index, item)); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Interleave.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Interleave.cs new file mode 100644 index 0000000..1caa859 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Interleave.cs @@ -0,0 +1,171 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Interleaves the elements of two or more sequences into a single sequence, skipping sequences as they are consumed + /// + /// + /// Interleave combines sequences by visiting each in turn, and returning the first element of each, followed + /// by the second, then the third, and so on. So, for example:
+ /// + /// {1,1,1}.Interleave( {2,2,2}, {3,3,3} ) => { 1,2,3,1,2,3,1,2,3 } + /// + /// This operator behaves in a deferred and streaming manner.
+ /// When sequences are of unequal length, this method will skip those sequences that have been fully consumed + /// and continue interleaving the remaining sequences.
+ /// The sequences are interleaved in the order that they appear in the + /// collection, with as the first sequence. + ///
+ /// The type of the elements of the source sequences + /// The first sequence in the interleave group + /// The other sequences in the interleave group + /// A sequence of interleaved elements from all of the source sequences + + public static IEnumerable Interleave(this IEnumerable sequence, params IEnumerable[] otherSequences) + { + return Interleave(sequence, ImbalancedInterleaveStrategy.Skip, otherSequences); + } + + /// + /// Interleaves the elements of two or more sequences into a single sequence, applying the specified strategy when sequences are of unequal length + /// + /// + /// Interleave combines sequences by visiting each in turn, and returning the first element of each, followed + /// by the second, then the third, and so on. So, for example:
+ /// + /// {1,1,1}.Interleave( {2,2,2}, {3,3,3} ) => { 1,2,3,1,2,3,1,2,3 } + /// + /// This operator behaves in a deferred and streaming manner.
+ /// When sequences are of unequal length, this method will use the imbalance strategy specified to + /// decide how to continue interleaving the remaining sequences. See + /// for more information.
+ /// The sequences are interleaved in the order that they appear in the + /// collection, with as the first sequence. + ///
+ /// The type of the elements of the source sequences + /// The first sequence in the interleave group + /// Defines the behavior of the operator when sequences are of unequal length + /// The other sequences in the interleave group + /// A sequence of interleaved elements from all of the source sequences + + private static IEnumerable Interleave(this IEnumerable sequence, ImbalancedInterleaveStrategy imbalanceStrategy, params IEnumerable[] otherSequences) + { + if (sequence == null) throw new ArgumentNullException("sequence"); + if (otherSequences == null) throw new ArgumentNullException("otherSequences"); + if (otherSequences.Any(s => s == null)) + throw new ArgumentNullException("otherSequences", "One or more sequences passed to Interleave was null."); + + return InterleaveImpl(new[] { sequence }.Concat(otherSequences), imbalanceStrategy); + } + + private static IEnumerable InterleaveImpl(IEnumerable> sequences, ImbalancedInterleaveStrategy imbalanceStrategy) + { + // produce an iterator collection for all IEnumerable instancess passed to us + var iterators = sequences.Select(e => e.GetEnumerator()).Acquire(); + List> iteratorList = null; + + try + { + iteratorList = new List>(iterators); + iterators = null; + var shouldContinue = true; + var consumedIterators = 0; + var iterCount = iteratorList.Count; + + while (shouldContinue) + { + // advance every iterator and verify a value exists to be yielded + for (var index = 0; index < iterCount; index++) + { + if (!iteratorList[index].MoveNext()) + { + // check if all iterators have been consumed and we can terminate + // or if the imbalance strategy informs us that we MUST terminate + if (++consumedIterators == iterCount || imbalanceStrategy == ImbalancedInterleaveStrategy.Stop) + { + shouldContinue = false; + break; + } + + iteratorList[index].Dispose(); // dispose the iterator sice we no longer need it + + // otherwise, apply the imbalance strategy + switch (imbalanceStrategy) + { + case ImbalancedInterleaveStrategy.Pad: + var newIter = iteratorList[index] = Generate(default(T), x => default(T)).GetEnumerator(); + newIter.MoveNext(); + break; + + case ImbalancedInterleaveStrategy.Skip: + iteratorList.RemoveAt(index); // no longer visit this particular iterator + --iterCount; // reduce the expected number of iterators to visit + --index; // decrement iterator index to compensate for index shifting + --consumedIterators; // decrement consumer iterator count to stay in balance + break; + } + + } + } + + if (shouldContinue) // only if all iterators could be advanced + { + // yield the values of each iterator's current position + for (var index = 0; index < iterCount; index++) + { + yield return iteratorList[index].Current; + } + } + } + } + finally + { + Debug.Assert(iteratorList != null || iterators != null); + foreach (var iter in (iteratorList ?? (IList>) iterators)) + iter.Dispose(); + } + } + + /// + /// Defines the strategies available when Interleave is passed sequences of unequal length + /// + enum ImbalancedInterleaveStrategy + { + /// + /// Extends a sequence by padding its tail with default(T) + /// + Pad, + /// + /// Removes the sequence from the interleave set, and continues interleaving remaining sequences. + /// + Skip, + /// + /// Stops the interleave operation. + /// + Stop, + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Lag.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Lag.cs new file mode 100644 index 0000000..41c89f9 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Lag.cs @@ -0,0 +1,100 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + public static partial class MoreEnumerable + { + /// + /// Produces a projection of a sequence by evaluating pairs of elements separated by a negative offset. + /// + /// + /// This operator evaluates in a deferred and streaming manner.
+ /// For elements prior to the lag offset, default(T) is used as the lagged value.
+ ///
+ /// The type of the elements of the source sequence + /// The type of the elements of the result sequence + /// The sequence over which to evaluate lag + /// The offset (expressed as a positive number) by which to lag each value of the sequence + /// A projection function which accepts the current and lagged items (in that order) and returns a result + /// A sequence produced by projecting each element of the sequence with its lagged pairing + + public static IEnumerable Lag(this IEnumerable source, int offset, Func resultSelector) + { + return Lag(source, offset, default(TSource), resultSelector); + } + + /// + /// Produces a projection of a sequence by evaluating pairs of elements separated by a negative offset. + /// + /// + /// This operator evaluates in a deferred and streaming manner.
+ ///
+ /// The type of the elements of the source sequence + /// The type of the elements of the result sequence + /// The sequence over which to evaluate lag + /// The offset (expressed as a positive number) by which to lag each value of the sequence + /// A default value supplied for the lagged value prior to the lag offset + /// A projection function which accepts the current and lagged items (in that order) and returns a result + /// A sequence produced by projecting each element of the sequence with its lagged pairing + + public static IEnumerable Lag(this IEnumerable source, int offset, TSource defaultLagValue, Func resultSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + // NOTE: Theoretically, we could assume that negative (or zero-offset) lags could be + // re-written as: sequence.Lead( -lagBy, resultSelector ). However, I'm not sure + // that it's an intuitive - or even desirable - behavior. So it's being omitted. + if (offset <= 0) throw new ArgumentOutOfRangeException("offset"); + + return LagImpl(source, offset, defaultLagValue, resultSelector); + } + + private static IEnumerable LagImpl(IEnumerable source, int offset, TSource defaultLagValue, Func resultSelector) + { + using (var iter = source.GetEnumerator()) + { + var lagQueue = new Queue(offset); + // until we progress far enough, the lagged value is defaultLagValue + var hasMore = true; + // NOTE: The if statement below takes advantage of short-circuit evaluation + // to ensure we don't advance the iterator when we reach the lag offset. + // Do not reorder the terms in the condition! + while (offset-- > 0 && (hasMore = iter.MoveNext())) + { + lagQueue.Enqueue(iter.Current); + // until we reach the lag offset, the lagged value is the defaultLagValue + yield return resultSelector(iter.Current, defaultLagValue); + } + + if (hasMore) // check that we didn't consume the sequence yet + { + // now the lagged value is derived from the sequence + while (iter.MoveNext()) + { + var lagValue = lagQueue.Dequeue(); + yield return resultSelector(iter.Current, lagValue); + lagQueue.Enqueue(iter.Current); + } + } + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Lead.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Lead.cs new file mode 100644 index 0000000..04e21bd --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Lead.cs @@ -0,0 +1,94 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + public static partial class MoreEnumerable + { + /// + /// Produces a projection of a sequence by evaluating pairs of elements separated by a positive offset. + /// + /// + /// This operator evaluates in a deferred and streaming manner.
+ /// For elements of the sequence that are less than items from the end, + /// default(T) is used as the lead value.
+ ///
+ /// The type of the elements in the source sequence + /// The type of the elements in the result sequence + /// The sequence over which to evaluate Lead + /// The offset (expressed as a positive number) by which to lead each element of the sequence + /// A projection function which accepts the current and subsequent (lead) element (in that order) and produces a result + /// A sequence produced by projecting each element of the sequence with its lead pairing + + public static IEnumerable Lead(this IEnumerable source, int offset, Func resultSelector) + { + return Lead(source, offset, default(TSource), resultSelector); + } + + /// + /// Produces a projection of a sequence by evaluating pairs of elements separated by a positive offset. + /// + /// + /// This operator evaluates in a deferred and streaming manner.
+ ///
+ /// The type of the elements in the source sequence + /// The type of the elements in the result sequence + /// The sequence over which to evaluate Lead + /// The offset (expressed as a positive number) by which to lead each element of the sequence + /// A default value supplied for the leading element when none is available + /// A projection function which accepts the current and subsequent (lead) element (in that order) and produces a result + /// A sequence produced by projecting each element of the sequence with its lead pairing + + public static IEnumerable Lead(this IEnumerable source, int offset, TSource defaultLeadValue, Func resultSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + if (offset <= 0) throw new ArgumentOutOfRangeException("offset"); + + return LeadImpl(source, offset, defaultLeadValue, resultSelector); + } + + private static IEnumerable LeadImpl(IEnumerable source, int offset, TSource defaultLeadValue, Func resultSelector) + { + var leadQueue = new Queue(); + using (var iter = source.GetEnumerator()) + { + bool hasMore; + // first, prefetch and populate the lead queue with the next step of + // items to be streamed out to the consumer of the sequence + while ((hasMore = iter.MoveNext()) && leadQueue.Count < offset) + leadQueue.Enqueue(iter.Current); + // next, while the source sequence has items, yield the result of + // the projection function applied to the top of queue and current item + while (hasMore) + { + yield return resultSelector(leadQueue.Dequeue(), iter.Current); + leadQueue.Enqueue(iter.Current); + hasMore = iter.MoveNext(); + } + // yield the remaining values in the lead queue with the default lead value + while (leadQueue.Count > 0) + { + yield return resultSelector(leadQueue.Dequeue(), defaultLeadValue); + } + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Lookup.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Lookup.cs new file mode 100644 index 0000000..ebbed34 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Lookup.cs @@ -0,0 +1,278 @@ +#region License and Terms +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. +// The MIT License (MIT) +// +// Copyright(c) Microsoft Corporation +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + + /// + /// A implementation that preserves insertion order + /// + /// The type of the keys in the + /// The type of the elements in the sequences that make up the values in the + /// + /// This implementation preserves insertion order of keys and elements within each + /// Copied over from CoreFX on 2015-10-27 + /// https://github.com/dotnet/corefx/blob/6f1c2a86fb8fa1bdaee7c6e70a684d27842d804c/src/System.Linq/src/System/Linq/Enumerable.cs#L3230-L3403 + /// Modified to remove internal interfaces + /// + internal class Lookup : IEnumerable>, ILookup + { + private IEqualityComparer _comparer; + private Grouping[] _groupings; + private Grouping _lastGrouping; + private int _count; + + internal static Lookup Create(IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + if (elementSelector == null) throw new ArgumentNullException("elementSelector"); + Lookup lookup = new Lookup(comparer); + foreach (TSource item in source) { + lookup.GetGrouping(keySelector(item), true).Add(elementSelector(item)); + } + return lookup; + } + + internal static Lookup CreateForJoin(IEnumerable source, Func keySelector, IEqualityComparer comparer) + { + Lookup lookup = new Lookup(comparer); + foreach (TElement item in source) { + TKey key = keySelector(item); + if (key != null) lookup.GetGrouping(key, true).Add(item); + } + return lookup; + } + + private Lookup(IEqualityComparer comparer) + { + if (comparer == null) comparer = EqualityComparer.Default; + _comparer = comparer; + _groupings = new Grouping[7]; + } + + public int Count + { + get { return _count; } + } + + public IEnumerable this[TKey key] + { + get + { + Grouping grouping = GetGrouping(key, false); + if (grouping != null) return grouping; + return Enumerable.Empty(); + } + } + + public bool Contains(TKey key) + { + return _count > 0 && GetGrouping(key, false) != null; + } + + public IEnumerator> GetEnumerator() + { + Grouping g = _lastGrouping; + if (g != null) { + do { + g = g.next; + yield return g; + } while (g != _lastGrouping); + } + } + + public IEnumerable ApplyResultSelector(Func, TResult> resultSelector) + { + Grouping g = _lastGrouping; + if (g != null) { + do { + g = g.next; + if (g.count != g.elements.Length) { Array.Resize(ref g.elements, g.count); } + yield return resultSelector(g.key, g.elements); + } while (g != _lastGrouping); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + internal int InternalGetHashCode(TKey key) + { + // Handle comparer implementations that throw when passed null + return (key == null) ? 0 : _comparer.GetHashCode(key) & 0x7FFFFFFF; + } + + internal Grouping GetGrouping(TKey key, bool create) + { + int hashCode = InternalGetHashCode(key); + for (Grouping g = _groupings[hashCode % _groupings.Length]; g != null; g = g.hashNext) + if (g.hashCode == hashCode && _comparer.Equals(g.key, key)) return g; + if (create) { + if (_count == _groupings.Length) Resize(); + int index = hashCode % _groupings.Length; + Grouping g = new Grouping(); + g.key = key; + g.hashCode = hashCode; + g.elements = new TElement[1]; + g.hashNext = _groupings[index]; + _groupings[index] = g; + if (_lastGrouping == null) { + g.next = g; + } + else { + g.next = _lastGrouping.next; + _lastGrouping.next = g; + } + _lastGrouping = g; + _count++; + return g; + } + return null; + } + + private void Resize() + { + int newSize = checked(_count * 2 + 1); + Grouping[] newGroupings = new Grouping[newSize]; + Grouping g = _lastGrouping; + do { + g = g.next; + int index = g.hashCode % newSize; + g.hashNext = newGroupings[index]; + newGroupings[index] = g; + } while (g != _lastGrouping); + _groupings = newGroupings; + } + } + + internal class Grouping : IGrouping, IList + { + internal TKey key; + internal int hashCode; + internal TElement[] elements; + internal int count; + internal Grouping hashNext; + internal Grouping next; + + internal Grouping() + { + } + + internal void Add(TElement element) + { + if (elements.Length == count) Array.Resize(ref elements, checked(count * 2)); + elements[count] = element; + count++; + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < count; i++) yield return elements[i]; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + // DDB195907: implement IGrouping<>.Key implicitly + // so that WPF binding works on this property. + public TKey Key + { + get { return key; } + } + + int ICollection.Count + { + get { return count; } + } + + bool ICollection.IsReadOnly + { + get { return true; } + } + + void ICollection.Add(TElement item) + { + throw new NotSupportedException("Lookup is immutable"); + } + + void ICollection.Clear() + { + throw new NotSupportedException("Lookup is immutable"); + } + + bool ICollection.Contains(TElement item) + { + return Array.IndexOf(elements, item, 0, count) >= 0; + } + + void ICollection.CopyTo(TElement[] array, int arrayIndex) + { + Array.Copy(elements, 0, array, arrayIndex, count); + } + + bool ICollection.Remove(TElement item) + { + throw new NotSupportedException("Lookup is immutable"); + } + + int IList.IndexOf(TElement item) + { + return Array.IndexOf(elements, item, 0, count); + } + + void IList.Insert(int index, TElement item) + { + throw new NotSupportedException("Lookup is immutable"); + } + + void IList.RemoveAt(int index) + { + throw new NotSupportedException("Lookup is immutable"); + } + + TElement IList.this[int index] + { + get + { + if (index < 0 || index >= count) throw new ArgumentOutOfRangeException("index"); + return elements[index]; + } + set + { + throw new NotSupportedException("Lookup is immutable"); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/MaxBy.cs b/VolvoWrench/ExtensionMethods/MoreLinq/MaxBy.cs new file mode 100644 index 0000000..fbd02db --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/MaxBy.cs @@ -0,0 +1,97 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns the maximal element of the given sequence, based on + /// the given projection. + /// + /// + /// If more than one element has the maximal projected value, the first + /// one encountered will be returned. This overload uses the default comparer + /// for the projected type. This operator uses immediate execution, but + /// only buffers a single result (the current maximal element). + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// The maximal element, according to the projection. + /// or is null + /// is empty + + public static TSource MaxBy(this IEnumerable source, + Func selector) + { + return source.MaxBy(selector, null); + } + + /// + /// Returns the maximal element of the given sequence, based on + /// the given projection and the specified comparer for projected values. + /// + /// + /// If more than one element has the maximal projected value, the first + /// one encountered will be returned. This operator uses immediate execution, but + /// only buffers a single result (the current maximal element). + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// Comparer to use to compare projected values + /// The maximal element, according to the projection. + /// , + /// or is null + /// is empty + + public static TSource MaxBy(this IEnumerable source, + Func selector, IComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (selector == null) throw new ArgumentNullException("selector"); + comparer = comparer ?? Comparer.Default; + + using (var sourceIterator = source.GetEnumerator()) + { + if (!sourceIterator.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + var max = sourceIterator.Current; + var maxKey = selector(max); + while (sourceIterator.MoveNext()) + { + var candidate = sourceIterator.Current; + var candidateProjected = selector(candidate); + if (comparer.Compare(candidateProjected, maxKey) > 0) + { + max = candidate; + maxKey = candidateProjected; + } + } + return max; + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/MinBy.cs b/VolvoWrench/ExtensionMethods/MoreLinq/MinBy.cs new file mode 100644 index 0000000..aa8e218 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/MinBy.cs @@ -0,0 +1,97 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns the minimal element of the given sequence, based on + /// the given projection. + /// + /// + /// If more than one element has the minimal projected value, the first + /// one encountered will be returned. This overload uses the default comparer + /// for the projected type. This operator uses immediate execution, but + /// only buffers a single result (the current minimal element). + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// The minimal element, according to the projection. + /// or is null + /// is empty + + public static TSource MinBy(this IEnumerable source, + Func selector) + { + return source.MinBy(selector, null); + } + + /// + /// Returns the minimal element of the given sequence, based on + /// the given projection and the specified comparer for projected values. + /// + /// + /// If more than one element has the minimal projected value, the first + /// one encountered will be returned. This operator uses immediate execution, but + /// only buffers a single result (the current minimal element). + /// + /// Type of the source sequence + /// Type of the projected element + /// Source sequence + /// Selector to use to pick the results to compare + /// Comparer to use to compare projected values + /// The minimal element, according to the projection. + /// , + /// or is null + /// is empty + + public static TSource MinBy(this IEnumerable source, + Func selector, IComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (selector == null) throw new ArgumentNullException("selector"); + comparer = comparer ?? Comparer.Default; + + using (var sourceIterator = source.GetEnumerator()) + { + if (!sourceIterator.MoveNext()) + { + throw new InvalidOperationException("Sequence contains no elements"); + } + var min = sourceIterator.Current; + var minKey = selector(min); + while (sourceIterator.MoveNext()) + { + var candidate = sourceIterator.Current; + var candidateProjected = selector(candidate); + if (comparer.Compare(candidateProjected, minKey) < 0) + { + min = candidate; + minKey = candidateProjected; + } + } + return min; + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/MoreEnumerable.cs b/VolvoWrench/ExtensionMethods/MoreLinq/MoreEnumerable.cs new file mode 100644 index 0000000..6d3c639 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/MoreEnumerable.cs @@ -0,0 +1,31 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System.Collections.Generic; + + /// + /// Provides a set of static methods for querying objects that + /// implement . The actual methods + /// are implemented in files reflecting the method name. + /// + + public static partial class MoreEnumerable + { + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/NestedLoops.cs b/VolvoWrench/ExtensionMethods/MoreLinq/NestedLoops.cs new file mode 100644 index 0000000..34bd68d --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/NestedLoops.cs @@ -0,0 +1,65 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + // This extension method was developed (primarily) to support the + // implementation of the Permutations() extension methods. However, + // it is of sufficient generality and usefulness to be elevated to + // a public extension method in its own right. + + /// + /// Produces a sequence from an action based on the dynamic generation of N nested loops + /// who iteration counts are defined by . + /// + /// Action delegate for which to produce a nested loop sequence + /// A sequence of loop repetition counts + /// A sequence of Action representing the expansion of a set of nested loops + + public static IEnumerable NestedLoops(this Action action, IEnumerable loopCounts) + { + if (action == null) throw new ArgumentNullException("action"); + if (loopCounts == null) throw new ArgumentNullException("loopCounts"); + + using (var iter = loopCounts.GetEnumerator()) + { + var loopCount = NextLoopCount(iter); + if (loopCount == null) + return Enumerable.Empty(); // null loop + var loop = Enumerable.Repeat(action, loopCount.Value); + while ((loopCount = NextLoopCount(iter)) != null) + loop = loop.Repeat(loopCount.Value); + return loop; + } + } + + private static int? NextLoopCount(IEnumerator iter) + { + if (!iter.MoveNext()) + return null; + if (iter.Current < 0) + throw new ArgumentException("All loop counts must be greater than or equal to zero.", "loopCounts"); + return iter.Current; + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/OrderBy.cs b/VolvoWrench/ExtensionMethods/MoreLinq/OrderBy.cs new file mode 100644 index 0000000..6232758 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/OrderBy.cs @@ -0,0 +1,92 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Sorts the elements of a sequence in a particular direction (ascending, descending) according to a key + /// + /// The type of the elements in the source sequence + /// The type of the key used to order elements + /// The sequence to order + /// A key selector function + /// A direction in which to order the elements (ascending, descending) + /// An ordered copy of the source sequence + + public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, OrderByDirection direction) + { + return OrderBy(source, keySelector, null, direction); + } + + /// + /// Sorts the elements of a sequence in a particular direction (ascending, descending) according to a key + /// + /// The type of the elements in the source sequence + /// The type of the key used to order elements + /// The sequence to order + /// A key selector function + /// A direction in which to order the elements (ascending, descending) + /// A comparer used to define the semantics of element comparison + /// An ordered copy of the source sequence + + public static IOrderedEnumerable OrderBy(this IEnumerable source, Func keySelector, IComparer comparer, OrderByDirection direction) + { + return direction == OrderByDirection.Ascending + ? source.OrderBy(keySelector, comparer) + : source.OrderByDescending(keySelector, comparer); + } + + /// + /// Performs a subsequent ordering of elements in a sequence in a particular direction (ascending, descending) according to a key + /// + /// The type of the elements in the source sequence + /// The type of the key used to order elements + /// The sequence to order + /// A key selector function + /// A direction in which to order the elements (ascending, descending) + /// An ordered copy of the source sequence + + public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, OrderByDirection direction) + { + return ThenBy(source, keySelector, null, direction); + } + + /// + /// Performs a subsequent ordering of elements in a sequence in a particular direction (ascending, descending) according to a key + /// + /// The type of the elements in the source sequence + /// The type of the key used to order elements + /// The sequence to order + /// A key selector function + /// A direction in which to order the elements (ascending, descending) + /// A comparer used to define the semantics of element comparison + /// An ordered copy of the source sequence + + public static IOrderedEnumerable ThenBy(this IOrderedEnumerable source, Func keySelector, IComparer comparer, OrderByDirection direction) + { + return direction == OrderByDirection.Ascending + ? source.ThenBy(keySelector, comparer) + : source.ThenByDescending(keySelector, comparer); + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/OrderByDirection.cs b/VolvoWrench/ExtensionMethods/MoreLinq/OrderByDirection.cs new file mode 100644 index 0000000..4bd2fb8 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/OrderByDirection.cs @@ -0,0 +1,35 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + /// + /// Enumeration that defines values representing valid ordering directions for a sequence. + /// + + public enum OrderByDirection + { + /// + /// Elements are ordered by increasing value + /// + Ascending = 0, + /// + /// Elements are ordered by decreasing value + /// + Descending = 1, + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/OrderedMerge.cs b/VolvoWrench/ExtensionMethods/MoreLinq/OrderedMerge.cs new file mode 100644 index 0000000..e9c4d91 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/OrderedMerge.cs @@ -0,0 +1,361 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2013 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + #region Imports + + using System; + using System.Collections.Generic; + using System.Diagnostics; + + #endregion + + static partial class MoreEnumerable + { + /// + /// Merges two ordered sequences into one. Where the elements equal + /// in both sequences, the element from the first sequence is + /// returned in the resulting sequence. + /// + /// Type of elements in input and output sequences. + /// The first input sequence. + /// The second input sequence. + /// + /// A sequence with elements from the two input sequences merged, as + /// in a full outer join. + /// + /// This method uses deferred execution. The behavior is undefined + /// if the sequences are unordered as inputs. + /// + + + public static IEnumerable OrderedMerge( + this IEnumerable first, + IEnumerable second) + { + return OrderedMerge(first, second, null); + } + + /// + /// Merges two ordered sequences into one with an additional + /// parameter specifying how to compare the elements of the + /// sequences. Where the elements equal in both sequences, the + /// element from the first sequence is returned in the resulting + /// sequence. + /// + /// Type of elements in input and output sequences. + /// The first input sequence. + /// The second input sequence. + /// An to compare elements. + /// + /// A sequence with elements from the two input sequences merged, as + /// in a full outer join. + /// + /// This method uses deferred execution. The behavior is undefined + /// if the sequences are unordered as inputs. + /// + + public static IEnumerable OrderedMerge( + this IEnumerable first, + IEnumerable second, + IComparer comparer) + { + return OrderedMerge(first, second, e => e, f => f, s => s, (a, _) => a, comparer); + } + + /// + /// Merges two ordered sequences into one with an additional + /// parameter specifying the element key by which the sequences are + /// ordered. Where the keys equal in both sequences, the + /// element from the first sequence is returned in the resulting + /// sequence. + /// + /// Type of elements in input and output sequences. + /// Type of keys used for merging. + /// The first input sequence. + /// The second input sequence. + /// Function to extract a key given an element. + /// + /// A sequence with elements from the two input sequences merged + /// according to a key, as in a full outer join. + /// + /// This method uses deferred execution. The behavior is undefined + /// if the sequences are unordered (by key) as inputs. + /// + public static IEnumerable OrderedMerge( + this IEnumerable first, + IEnumerable second, + Func keySelector) + { + return OrderedMerge(first, second, keySelector, a => a, b => b, (a, _) => a, null); + } + + /// + /// Merges two ordered sequences into one. Additional parameters + /// specify the element key by which the sequences are ordered, + /// the result when element is found in first sequence but not in + /// the second, the result when element is found in second sequence + /// but not in the first and the result when elements are found in + /// both sequences. + /// + /// Type of elements in source sequences. + /// Type of keys used for merging. + /// Type of elements in the returned sequence. + /// The first input sequence. + /// The second input sequence. + /// Function to extract a key given an element. + /// Function to project the result element + /// when only the first sequence yields a source element. + /// Function to project the result element + /// when only the second sequence yields a source element. + /// Function to project the result element + /// when only both sequences yield a source element whose keys are + /// equal. + /// + /// A sequence with projections from the two input sequences merged + /// according to a key, as in a full outer join. + /// + /// This method uses deferred execution. The behavior is undefined + /// if the sequences are unordered (by key) as inputs. + /// + + public static IEnumerable OrderedMerge( + this IEnumerable first, + IEnumerable second, + Func keySelector, + Func firstSelector, + Func secondSelector, + Func bothSelector) + { + return OrderedMerge(first, second, keySelector, firstSelector, secondSelector, bothSelector, null); + } + + /// + /// Merges two ordered sequences into one. Additional parameters + /// specify the element key by which the sequences are ordered, + /// the result when element is found in first sequence but not in + /// the second, the result when element is found in second sequence + /// but not in the first, the result when elements are found in + /// both sequences and a method for comparing keys. + /// + /// Type of elements in source sequences. + /// Type of keys used for merging. + /// Type of elements in the returned sequence. + /// The first input sequence. + /// The second input sequence. + /// Function to extract a key given an element. + /// Function to project the result element + /// when only the first sequence yields a source element. + /// Function to project the result element + /// when only the second sequence yields a source element. + /// Function to project the result element + /// when only both sequences yield a source element whose keys are + /// equal. + /// An to compare keys. + /// + /// A sequence with projections from the two input sequences merged + /// according to a key, as in a full outer join. + /// + /// This method uses deferred execution. The behavior is undefined + /// if the sequences are unordered (by key) as inputs. + /// + + public static IEnumerable OrderedMerge( + this IEnumerable first, + IEnumerable second, + Func keySelector, + Func firstSelector, + Func secondSelector, + Func bothSelector, + IComparer comparer) + { + if (keySelector == null) throw new ArgumentNullException("keySelector"); // Argument name changes to 'firstKeySelector' + return OrderedMerge(first, second, keySelector, keySelector, firstSelector, secondSelector, bothSelector, comparer); + } + + /// + /// Merges two heterogeneous sequences ordered by a common key type + /// into a homogeneous one. Additional parameters specify the + /// element key by which the sequences are ordered, the result when + /// element is found in first sequence but not in the second and + /// the result when element is found in second sequence but not in + /// the first, the result when elements are found in both sequences. + /// + /// Type of elements in the first sequence. + /// Type of elements in the second sequence. + /// Type of keys used for merging. + /// Type of elements in the returned sequence. + /// The first input sequence. + /// The second input sequence. + /// Function to extract a key given an + /// element from the first sequence. + /// Function to extract a key given an + /// element from the second sequence. + /// Function to project the result element + /// when only the first sequence yields a source element. + /// Function to project the result element + /// when only the second sequence yields a source element. + /// Function to project the result element + /// when only both sequences yield a source element whose keys are + /// equal. + /// + /// A sequence with projections from the two input sequences merged + /// according to a key, as in a full outer join. + /// + /// This method uses deferred execution. The behavior is undefined + /// if the sequences are unordered (by key) as inputs. + /// + + public static IEnumerable OrderedMerge( + this IEnumerable first, + IEnumerable second, + Func firstKeySelector, + Func secondKeySelector, + Func firstSelector, + Func secondSelector, + Func bothSelector) + { + return OrderedMerge(first, second, firstKeySelector, secondKeySelector, firstSelector, secondSelector, bothSelector, null); + } + + /// + /// Merges two heterogeneous sequences ordered by a common key type + /// into a homogeneous one. Additional parameters specify the + /// element key by which the sequences are ordered, the result when + /// element is found in first sequence but not in the second, + /// the result when element is found in second sequence but not in + /// the first, the result when elements are found in both sequences + /// and a method for comparing keys. + /// + /// Type of elements in the first sequence. + /// Type of elements in the second sequence. + /// Type of keys used for merging. + /// Type of elements in the returned sequence. + /// The first input sequence. + /// The second input sequence. + /// Function to extract a key given an + /// element from the first sequence. + /// Function to extract a key given an + /// element from the second sequence. + /// Function to project the result element + /// when only the first sequence yields a source element. + /// Function to project the result element + /// when only the second sequence yields a source element. + /// Function to project the result element + /// when only both sequences yield a source element whose keys are + /// equal. + /// An to compare keys. + /// + /// A sequence with projections from the two input sequences merged + /// according to a key, as in a full outer join. + /// + /// This method uses deferred execution. The behavior is undefined + /// if the sequences are unordered (by key) as inputs. + /// + + public static IEnumerable OrderedMerge( + this IEnumerable first, + IEnumerable second, + Func firstKeySelector, + Func secondKeySelector, + Func firstSelector, + Func secondSelector, + Func bothSelector, + IComparer comparer) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (firstKeySelector == null) throw new ArgumentNullException("firstKeySelector"); + if (secondKeySelector == null) throw new ArgumentNullException("secondKeySelector"); + if (firstSelector == null) throw new ArgumentNullException("firstSelector"); + if (bothSelector == null) throw new ArgumentNullException("bothSelector"); + if (secondSelector == null) throw new ArgumentNullException("secondSelector"); + + return OrderedMergeImpl(first, second, + firstKeySelector, secondKeySelector, + firstSelector, secondSelector, bothSelector, + comparer ?? Comparer.Default); + } + + static IEnumerable OrderedMergeImpl( + IEnumerable first, + IEnumerable second, + Func firstKeySelector, + Func secondKeySelector, + Func firstSelector, + Func secondSelector, + Func bothSelector, + IComparer comparer) + { + Debug.Assert(first != null); + Debug.Assert(second != null); + Debug.Assert(firstKeySelector != null); + Debug.Assert(secondKeySelector != null); + Debug.Assert(firstSelector != null); + Debug.Assert(secondSelector != null); + Debug.Assert(bothSelector != null); + Debug.Assert(comparer != null); + + using (var e1 = first.GetEnumerator()) + using (var e2 = second.GetEnumerator()) + { + var gotFirst = e1.MoveNext(); + var gotSecond = e2.MoveNext(); + + while (gotFirst || gotSecond) + { + if (gotFirst && gotSecond) + { + var element1 = e1.Current; + var key1 = firstKeySelector(element1); + var element2 = e2.Current; + var key2 = secondKeySelector(element2); + var comparison = comparer.Compare(key1, key2); + + if (comparison < 0) + { + yield return firstSelector(element1); + gotFirst = e1.MoveNext(); + } + else if (comparison > 0) + { + yield return secondSelector(element2); + gotSecond = e2.MoveNext(); + } + else + { + yield return bothSelector(element1, element2); + gotFirst = e1.MoveNext(); + gotSecond = e2.MoveNext(); + } + } + else if (gotSecond) + { + yield return secondSelector(e2.Current); + gotSecond = e2.MoveNext(); + } + else // (gotFirst) + { + yield return firstSelector(e1.Current); + gotFirst = e1.MoveNext(); + } + } + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Pad.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Pad.cs new file mode 100644 index 0000000..4ecf6be --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Pad.cs @@ -0,0 +1,136 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + static partial class MoreEnumerable + { + /// + /// Pads a sequence with default values if it is narrower (shorter + /// in length) than a given width. + /// + /// The type of the elements of . + /// The sequence to pad. + /// The width/length below which to pad. + /// + /// Returns a sequence that is at least as wide/long as the width/length + /// specified by the parameter. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// int[] numbers = { 123, 456, 789 }; + /// IEnumerable<int> result = numbers.Pad(5); + /// + /// The result variable, when iterated over, will yield + /// 123, 456, 789 and two zeroes, in turn. + /// + + public static IEnumerable Pad(this IEnumerable source, int width) + { + return Pad(source, width, default(TSource)); + } + + /// + /// Pads a sequence with a given filler value if it is narrower (shorter + /// in length) than a given width. + /// + /// The type of the elements of . + /// The sequence to pad. + /// The width/length below which to pad. + /// The value to use for padding. + /// + /// Returns a sequence that is at least as wide/long as the width/length + /// specified by the parameter. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// int[] numbers = { 123, 456, 789 }; + /// IEnumerable<int> result = numbers.Pad(5, -1); + /// + /// The result variable, when iterated over, will yield + /// 123, 456, and 789 followed by two occurrences of -1, in turn. + /// + + public static IEnumerable Pad(this IEnumerable source, int width, TSource padding) + { + if (source == null) throw new ArgumentNullException("source"); + if (width < 0) throw new ArgumentException(null, "width"); + return PadImpl(source, width, padding, null); + } + + /// + /// Pads a sequence with a dynamic filler value if it is narrower (shorter + /// in length) than a given width. + /// + /// The type of the elements of . + /// The sequence to pad. + /// The width/length below which to pad. + /// Function to calculate padding. + /// + /// Returns a sequence that is at least as wide/long as the width/length + /// specified by the parameter. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// int[] numbers = { 0, 1, 2 }; + /// IEnumerable<int> result = numbers.Pad(5, i => -i); + /// + /// The result variable, when iterated over, will yield + /// 0, 1, 2, -3 and -4, in turn. + /// + + public static IEnumerable Pad(this IEnumerable source, int width, Func paddingSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (paddingSelector == null) throw new ArgumentNullException("paddingSelector"); + if (width < 0) throw new ArgumentException(null, "width"); + return PadImpl(source, width, default(TSource), paddingSelector); + } + + private static IEnumerable PadImpl(IEnumerable source, + int width, T padding, Func paddingSelector) + { + Debug.Assert(source != null); + Debug.Assert(width >= 0); + + var count = 0; + foreach (var item in source) + { + yield return item; + count++; + } + while (count < width) + { + yield return paddingSelector != null ? paddingSelector(count) : padding; + count++; + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Pairwise.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Pairwise.cs new file mode 100644 index 0000000..4146dad --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Pairwise.cs @@ -0,0 +1,78 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2012 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + static partial class MoreEnumerable + { + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence and its + /// predecessor, with the exception of the first element which is + /// only returned as the predecessor of the second element. + /// + /// The type of the elements of . + /// The type of the element of the returned sequence. + /// The source sequence. + /// A transform function to apply to + /// each pair of sequence. + /// + /// Returns the resulting sequence. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// int[] numbers = { 123, 456, 789 }; + /// IEnumerable<int> result = numbers.Pairwise((a, b) => a + b); + /// + /// The result variable, when iterated over, will yield + /// 579 and 1245, in turn. + /// + + public static IEnumerable Pairwise(this IEnumerable source, Func resultSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + return PairwiseImpl(source, resultSelector); + } + + private static IEnumerable PairwiseImpl(this IEnumerable source, Func resultSelector) + { + Debug.Assert(source != null); + Debug.Assert(resultSelector != null); + + using (var e = source.GetEnumerator()) + { + if (!e.MoveNext()) + yield break; + + var previous = e.Current; + while (e.MoveNext()) + { + yield return resultSelector(previous, e.Current); + previous = e.Current; + } + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/PartialSort.cs b/VolvoWrench/ExtensionMethods/MoreLinq/PartialSort.cs new file mode 100644 index 0000000..0adabc2 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/PartialSort.cs @@ -0,0 +1,273 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2016 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Combines , + /// where each element is its key, and + /// in a single operation. + /// + /// Type of elements in the sequence. + /// The source sequence. + /// Number of (maximum) elements to return. + /// A sequence containing at most top + /// elements from source, in their ascending order. + /// + /// This operator uses deferred execution and streams it results. + /// + + public static IEnumerable PartialSort(this IEnumerable source, int count) + { + return source.PartialSort(count, null); + } + + /// + /// Combines , + /// where each element is its key, and + /// in a single operation. + /// An additional parameter specifies the direction of the sort + /// + /// Type of elements in the sequence. + /// The source sequence. + /// Number of (maximum) elements to return. + /// The direction in which to sort the elements + /// A sequence containing at most top + /// elements from source, in the specified order. + /// + /// This operator uses deferred execution and streams it results. + /// + + public static IEnumerable PartialSort(this IEnumerable source, + int count, OrderByDirection direction) + { + return source.PartialSort(count, null, direction); + } + + /// + /// Combines , + /// where each element is its key, and + /// in a single operation. An additional parameter specifies how the + /// elements compare to each other. + /// + /// Type of elements in the sequence. + /// The source sequence. + /// Number of (maximum) elements to return. + /// A to compare elements. + /// A sequence containing at most top + /// elements from source, in their ascending order. + /// + /// This operator uses deferred execution and streams it results. + /// + + public static IEnumerable PartialSort(this IEnumerable source, + int count, IComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + return PartialSortByImpl(source, count, null, null, comparer); + } + + /// + /// Combines , + /// where each element is its key, and + /// in a single operation. + /// Additional parameters specify how the elements compare to each other and + /// the direction of the sort. + /// + /// Type of elements in the sequence. + /// The source sequence. + /// Number of (maximum) elements to return. + /// A to compare elements. + /// The direction in which to sort the elements + /// A sequence containing at most top + /// elements from source, in the specified order. + /// + /// This operator uses deferred execution and streams it results. + /// + + public static IEnumerable PartialSort(this IEnumerable source, + int count, IComparer comparer, OrderByDirection direction) + { + comparer = comparer ?? Comparer.Default; + if (direction == OrderByDirection.Descending) { + comparer = new ReverseComparer(comparer); + } + return source.PartialSort(count, comparer); + } + + /// + /// Combines , + /// and in a single operation. + /// + /// Type of elements in the sequence. + /// Type of keys. + /// The source sequence. + /// A function to extract a key from an element. + /// Number of (maximum) elements to return. + /// A sequence containing at most top + /// elements from source, in ascending order of their keys. + /// + /// This operator uses deferred execution and streams it results. + /// + + public static IEnumerable PartialSortBy( + this IEnumerable source, int count, + Func keySelector) + { + return source.PartialSortBy(count, keySelector, null); + } + + /// + /// Combines , + /// and in a single operation. + /// An additional parameter specifies the direction of the sort + /// + /// Type of elements in the sequence. + /// Type of keys. + /// The source sequence. + /// A function to extract a key from an element. + /// Number of (maximum) elements to return. + /// The direction in which to sort the elements + /// A sequence containing at most top + /// elements from source, in the specified order of their keys. + /// + /// This operator uses deferred execution and streams it results. + /// + + public static IEnumerable PartialSortBy( + this IEnumerable source, int count, + Func keySelector, OrderByDirection direction) + { + return source.PartialSortBy(count, keySelector, null, direction); + } + + /// + /// Combines , + /// and in a single operation. + /// An additional parameter specifies how the keys compare to each other. + /// + /// Type of elements in the sequence. + /// Type of keys. + /// The source sequence. + /// A function to extract a key from an element. + /// Number of (maximum) elements to return. + /// A to compare elements. + /// A sequence containing at most top + /// elements from source, in ascending order of their keys. + /// + /// This operator uses deferred execution and streams it results. + /// + + public static IEnumerable PartialSortBy( + this IEnumerable source, int count, + Func keySelector, + IComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + return PartialSortByImpl(source, count, keySelector, comparer, null); + } + + /// + /// Combines , + /// and in a single operation. + /// Additional parameters specify how the elements compare to each other and + /// the direction of the sort. + /// + /// Type of elements in the sequence. + /// Type of keys. + /// The source sequence. + /// A function to extract a key from an element. + /// Number of (maximum) elements to return. + /// A to compare elements. + /// The direction in which to sort the elements + /// A sequence containing at most top + /// elements from source, in the specified order of their keys. + /// + /// This operator uses deferred execution and streams it results. + /// + + public static IEnumerable PartialSortBy( + this IEnumerable source, int count, + Func keySelector, + IComparer comparer, + OrderByDirection direction) + { + comparer = comparer ?? Comparer.Default; + if (direction == OrderByDirection.Descending) { + comparer = new ReverseComparer(comparer); + } + return source.PartialSortBy(count, keySelector, comparer); + } + + static IEnumerable PartialSortByImpl( + IEnumerable source, int count, + Func keySelector, + IComparer keyComparer, IComparer comparer) + { + Debug.Assert(source != null); + + var keys = keySelector != null ? new List(count) : null; + var top = new List(count); + + foreach (var item in source) + { + int i; + var key = default(TKey); + if (keys != null) + { + key = keySelector(item); + i = keys.BinarySearch(key, keyComparer); + } + else + { + i = top.BinarySearch(item, comparer); + } + + if (i < 0 && (i = ~i) >= count) + continue; + + if (top.Count == count) + { + if (keys != null) + keys.RemoveAt(top.Count - 1); + + top.RemoveAt(top.Count - 1); + } + + // TODO Stable sorting + + if (keys != null) + keys.Insert(i, key); + + top.Insert(i, item); + } + + // ReSharper disable once LoopCanBeConvertedToQuery + + foreach (var item in top) + yield return item; + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Partition.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Partition.cs new file mode 100644 index 0000000..b81b2c8 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Partition.cs @@ -0,0 +1,148 @@ +#region License and Terms +// +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008-9 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Indicates what to do with the current element when partitioning a sequence. + /// + private enum PartitionInstruction + { + /// + /// Adds the item to the current partition and then yields the partition. + /// A new partition is opened afterwards. + /// + Yield, + /// + /// Adds the item to the current partition. + /// + Fill + } + + + /// + /// Partitions a sequence into equal-sized partitions. + /// + /// Type of elements in sequence. + /// The source sequence to partition. + /// Size of partitions. + /// A sequence of equal-sized partitions containing elements of the source collection. + /// + /// the source sequence is exhausted before a complete partition could be filled, the partly filled partition is yielded. + /// This operator uses deferred execution and streams its results (partitions and partition content). + /// Each partition is fully filled before it's yielded. + /// + + public static IEnumerable> Partition(this IEnumerable source, int size) + { + if (source == null) throw new ArgumentNullException("source"); + if (size <= 0) throw new ArgumentOutOfRangeException("size"); + + var splitInstructions = GenerateByIndex(i => i % size == size - 1 ? PartitionInstruction.Yield : PartitionInstruction.Fill); + return source.PartitionImpl(splitInstructions); + } + + /// + /// Partitions a sequence into a series of partitions. Their size is defined by . + /// + /// Type of elements in sequence. + /// The source sequence to partition. + /// A sequence of partition sizes, defining how many elements to place in each partition. + /// A sequence of sized partitions containing elements of the source collection. + /// + /// If the source sequence is exhausted before a complete partition could be filled, the partly filled partition is yielded. + /// This operator uses deferred execution and streams its results (partitions and partition content). + /// Each partition is fully filled before yielded. + /// + + public static IEnumerable> Partition(this IEnumerable source, + IEnumerable partitions) + { + if (source == null) throw new ArgumentNullException("source"); + if (partitions == null) throw new ArgumentNullException("partitions"); + + var splitInstructions = Enumerable.Empty(); + + // Each partition shall be filled and then splitted + foreach (var partitionSize in partitions) + { + if (partitionSize < 0) + throw new ArgumentException("Partition sizes may not be negative."); + + splitInstructions = splitInstructions.Concat(Enumerable.Repeat(PartitionInstruction.Fill, partitionSize - 1)); + splitInstructions = splitInstructions.Concat(PartitionInstruction.Yield); + } + + return PartitionImpl(source, splitInstructions); + } + + + /// + /// Zips the source and instruction sequence, partitioning the source sequence according to the corresponding instruction. + /// A partition is buffered before it's yielded element by element. + /// If either input sequence is exhausted and a partition has been partly filled, it is yielded too. + /// + + private static IEnumerable> PartitionImpl(this IEnumerable source, + IEnumerable splitInstructions) + { + Debug.Assert(source != null); + Debug.Assert(splitInstructions != null); + + var collector = (IList) new List(); + var collectorFilled = false; + + // Zip shortest + foreach (var itemInstructionPair in source.ZipShortest(splitInstructions, (x, y) => new { Item = x, Instruction = y })) + { + switch (itemInstructionPair.Instruction) + { + case PartitionInstruction.Yield: + // add and yield afterwards + collector.Add(itemInstructionPair.Item); + // partition contents are streamed too + yield return collector.Select(x => x); + + // advance to the next partition, reset the collector + collector = new List(); + collectorFilled = false; + break; + case PartitionInstruction.Fill: + // add item to the collector and indicate the collector is filled + collectorFilled = true; + collector.Add(itemInstructionPair.Item); + break; + } + } + + // The source or instruction sequence is exhausted, yield the partly filled collector + if (collectorFilled) + { + yield return collector.Select(x => x); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Permutations.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Permutations.cs new file mode 100644 index 0000000..80cae2b --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Permutations.cs @@ -0,0 +1,225 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + #region Nested Classes + /// + /// The private implementation class that produces permutations of a sequence. + /// + /// + private class PermutationEnumerator : IEnumerator> + { + // NOTE: The algorithm used to generate permutations uses the fact that any set + // can be put into 1-to-1 correspondence with the set of ordinals number (0..n). + // The implementation here is based on the algorithm described by Kenneth H. Rosen, + // in Discrete Mathematics and Its Applications, 2nd edition, pp. 282-284. + // + // There are two significant changes from the original implementation. + // First, the algorithm uses lazy evaluation and streaming to fit well into the + // nature of most LINQ evaluations. + // + // Second, the algorithm has been modified to use dynamically generated nested loop + // state machines, rather than an integral computation of the factorial function + // to determine when to terminate. The original algorithm required a priori knowledge + // of the number of iterations necessary to produce all permutations. This is a + // necessary step to avoid overflowing the range of the permutation arrays used. + // The number of permutation iterations is defined as the factorial of the original + // set size minus 1. + // + // However, there's a fly in the ointment. The factorial function grows VERY rapidly. + // 13! overflows the range of a Int32; while 28! overflows the range of decimal. + // To overcome these limitations, the algorithm relies on the fact that the factorial + // of N is equivalent to the evaluation of N-1 nested loops. Unfortunatley, you can't + // just code up a variable number of nested loops ... this is where .NET generators + // with their elegant 'yield return' syntax come to the rescue. + // + // The methods of the Loop extension class (For and NestedLoops) provide the implementation + // of dynamic nested loops using generators and sequence composition. In a nutshell, + // the two Repeat() functions are the constructor of loops and nested loops, respectively. + // The NestedLoops() function produces a composition of loops where the loop counter + // for each nesting level is defined in a separate sequence passed in the call. + // + // For example: NestedLoops( () => DoSomething(), new[] { 6, 8 } ) + // + // is equivalent to: for( int i = 0; i < 6; i++ ) + // for( int j = 0; j < 8; j++ ) + // DoSomething(); + + #region Private Fields + private readonly IList m_ValueSet; + private readonly int[] m_Permutation; + private readonly IEnumerable m_Generator; + + private IEnumerator m_GeneratorIterator; + private bool m_HasMoreResults; + #endregion + + #region Constructors + public PermutationEnumerator(IEnumerable valueSet) + { + m_ValueSet = valueSet.ToArray(); + m_Permutation = new int[m_ValueSet.Count]; + // The nested loop construction below takes into account the fact that: + // 1) for empty sets and sets of cardinality 1, there exists only a single permutation. + // 2) for sets larger than 1 element, the number of nested loops needed is: set.Count-1 + m_Generator = NestedLoops(NextPermutation, Enumerable.Range(2, Math.Max(0, m_ValueSet.Count - 1))); + Reset(); + } + #endregion + + #region IEnumerator Members + public void Reset() + { + if (m_GeneratorIterator != null) + m_GeneratorIterator.Dispose(); + // restore lexographic ordering of the permutation indexes + for (var i = 0; i < m_Permutation.Length; i++) + m_Permutation[i] = i; + // start a newiteration over the nested loop generator + m_GeneratorIterator = m_Generator.GetEnumerator(); + // we must advance the nestedloop iterator to the initial element, + // this ensures that we only ever produce N!-1 calls to NextPermutation() + m_GeneratorIterator.MoveNext(); + m_HasMoreResults = true; // there's always at least one permutation: the original set itself + } + + public IList Current { get; private set; } + + object IEnumerator.Current + { + get { return Current; } + } + + public bool MoveNext() + { + Current = PermuteValueSet(); + // check if more permutation left to enumerate + var prevResult = m_HasMoreResults; + m_HasMoreResults = m_GeneratorIterator.MoveNext(); + if (m_HasMoreResults) + m_GeneratorIterator.Current(); // produce the next permutation ordering + // we return prevResult rather than m_HasMoreResults because there is always + // at least one permtuation: the original set. Also, this provides a simple way + // to deal with the disparity between sets that have only one loop level (size 0-2) + // and those that have two or more (size > 2). + return prevResult; + } + + void IDisposable.Dispose() { } + #endregion + + #region Private Methods + /// + /// Transposes elements in the cached permutation array to produce the next permutation + /// + private void NextPermutation() + { + // find the largest index j with m_Permutation[j] < m_Permutation[j+1] + var j = m_Permutation.Length - 2; + while (m_Permutation[j] > m_Permutation[j + 1]) + j--; + + // find index k such that m_Permutation[k] is the smallest integer + // greater than m_Permutation[j] to the right of m_Permutation[j] + var k = m_Permutation.Length - 1; + while (m_Permutation[j] > m_Permutation[k]) + k--; + + // interchange m_Permutation[j] and m_Permutation[k] + var oldValue = m_Permutation[k]; + m_Permutation[k] = m_Permutation[j]; + m_Permutation[j] = oldValue; + + // move the tail of the permutation after the jth position in increasing order + var x = m_Permutation.Length - 1; + var y = j + 1; + + while (x > y) + { + oldValue = m_Permutation[y]; + m_Permutation[y] = m_Permutation[x]; + m_Permutation[x] = oldValue; + x--; + y++; + } + } + + /// + /// Creates a new list containing the values from the original + /// set in their new permuted order. + /// + /// + /// The reason we return a new permuted value set, rather than reuse + /// an existing collection, is that we have no control over what the + /// consumer will do with the results produced. They could very easily + /// generate and store a set of permutations and only then begin to + /// process them. If we reused the same collection, the caller would + /// be surprised to discover that all of the permutations looked the + /// same. + /// + /// List of permuted source sequence values + private IList PermuteValueSet() + { + var permutedSet = new T[m_Permutation.Length]; + for (var i = 0; i < m_Permutation.Length; i++) + permutedSet[i] = m_ValueSet[m_Permutation[i]]; + return permutedSet; + } + #endregion + } + #endregion + + /// + /// Generates a sequence of lists that represent the permutations of the original sequence. + /// + /// + /// A permutation is a unique re-ordering of the elements of the sequence.
+ /// This operator returns permutations in a deferred, streaming fashion; however, each + /// permutation is materialized into a new list. There are N! permutations of a sequence, + /// where N => sequence.Count().
+ /// Be aware that the original sequence is considered one of the permutations and will be + /// returned as one of the results. + ///
+ /// The type of the elements in the sequence + /// The original sequence to permute + /// A sequence of lists representing permutations of the original sequence + + public static IEnumerable> Permutations(this IEnumerable sequence) + { + if (sequence == null) throw new ArgumentNullException("sequence"); + + return PermutationsImpl(sequence); + } + + private static IEnumerable> PermutationsImpl(IEnumerable sequence) + { + using (var iter = new PermutationEnumerator(sequence)) + { + while (iter.MoveNext()) + yield return iter.Current; + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Pipe.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Pipe.cs new file mode 100644 index 0000000..bd26da7 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Pipe.cs @@ -0,0 +1,57 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Executes the given action on each element in the source sequence + /// and yields it. + /// + /// The type of the elements in the sequence + /// The sequence of elements + /// The action to execute on each element + /// A sequence with source elements in their original order. + /// + /// The returned sequence is essentially a duplicate of + /// the original, but with the extra action being executed while the + /// sequence is evaluated. The action is always taken before the element + /// is yielded, so any changes made by the action will be visible in the + /// returned sequence. This operator uses deferred execution and streams it results. + /// + + public static IEnumerable Pipe(this IEnumerable source, Action action) + { + if (source == null) throw new ArgumentNullException("source"); + if (action == null) throw new ArgumentNullException("action"); + return PipeImpl(source, action); + } + + private static IEnumerable PipeImpl(this IEnumerable source, Action action) + { + foreach (var element in source) + { + action(element); + yield return element; + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/PreScan.cs b/VolvoWrench/ExtensionMethods/MoreLinq/PreScan.cs new file mode 100644 index 0000000..26b92e7 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/PreScan.cs @@ -0,0 +1,77 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Konrad Rudolph. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Performs a pre-scan (exclusive prefix sum) on a sequence of elements. + /// + /// + /// An exclusive prefix sum returns an equal-length sequence where the + /// N-th element is the sum of the first N-1 input elements (the first + /// element is a special case, it is set to the identity). More + /// generally, the pre-scan allows any commutative binary operation, + /// not just a sum. + /// The inclusive version of PreScan is . + /// This operator uses deferred execution and streams its result. + /// + /// + /// + /// Func<int, int, int> plus = (a, b) => a + b; + /// int[] values = { 1, 2, 3, 4 }; + /// IEnumerable<int> prescan = values.PreScan(plus, 0); + /// IEnumerable<int> scan = values.Scan(plus; a + b); + /// IEnumerable<int> result = values.ZipShortest(prescan, plus); + /// + /// prescan will yield { 0, 1, 3, 6 }, while scan + /// and result will both yield { 1, 3, 6, 10 }. This + /// shows the relationship between the inclusive and exclusive prefix sum. + /// + /// Type of elements in source sequence + /// Source sequence + /// Transformation operation + /// Identity element (see remarks) + /// The scanned sequence + + public static IEnumerable PreScan(this IEnumerable source, + Func transformation, TSource identity) + { + if (source == null) throw new ArgumentNullException("source"); + if (transformation == null) throw new ArgumentNullException("transformation"); + return PreScanImpl(source, transformation, identity); + } + + private static IEnumerable PreScanImpl(IEnumerable source, Func f, T id) + { + // special case, the first element is set to the identity + var aggregator = id; + + foreach (var i in source) + { + yield return aggregator; + + // aggregate the next element in the sequence + aggregator = f(aggregator, i); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Prepend.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Prepend.cs new file mode 100644 index 0000000..534af94 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Prepend.cs @@ -0,0 +1,51 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using LinqEnumerable = System.Linq.Enumerable; + + static partial class MoreEnumerable + { + /// + /// Prepends a single value to a sequence. + /// + /// The type of the elements of . + /// The sequence to prepend to. + /// The value to prepend. + /// + /// Returns a sequence where a value is prepended to it. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + /// + /// int[] numbers = { 1, 2, 3 }; + /// IEnumerable<int> result = numbers.Prepend(0); + /// + /// The result variable, when iterated over, will yield + /// 0, 1, 2 and 3, in turn. + + public static IEnumerable Prepend(this IEnumerable source, TSource value) + { + if (source == null) throw new ArgumentNullException("source"); + return LinqEnumerable.Concat(LinqEnumerable.Repeat(value, 1), source); + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Random.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Random.cs new file mode 100644 index 0000000..dc3688d --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Random.cs @@ -0,0 +1,154 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + public static partial class MoreEnumerable + { + /// + /// Returns an infinite sequence of random integers using the standard + /// .NET random number generator. + /// + /// An infinite sequence of random integers + + public static IEnumerable Random() + { + return Random(new Random()); + } + + /// + /// Returns an infinite sequence of random integers using the supplied + /// random number generator. + /// + /// Random generator used to produce random numbers + /// An infinite sequence of random integers + /// Thrown if is . + + public static IEnumerable Random(Random rand) + { + if (rand == null) throw new ArgumentNullException("rand"); + + return RandomImpl(rand, r => r.Next()); + } + + /// + /// Returns an infinite sequence of random integers between 0 and />. + /// + /// exclusive upper bound for the random values returned + /// An infinite sequence of random integers + + public static IEnumerable Random(int maxValue) + { + if (maxValue < 0) throw new ArgumentOutOfRangeException("maxValue"); + + return Random(new Random(), maxValue); + } + + /// + /// Returns an infinite sequence of random integers between 0 and /> + /// using the supplied random number generator. + /// + /// Random generator used to produce values + /// Exclusive upper bound for random values returned + /// An infinite sequence of random integers + /// Thrown if is . + + public static IEnumerable Random(Random rand, int maxValue) + { + if (rand == null) throw new ArgumentNullException("rand"); + if (maxValue < 0) throw new ArgumentOutOfRangeException("maxValue"); + + return RandomImpl(rand, r => r.Next(maxValue)); + } + + /// + /// Returns an infinite sequence of random integers between and + /// . + /// + /// Inclusive lower bound of the values returned + /// Exclusive upper bound of the values returned + /// An infinite sequence of random integers + + public static IEnumerable Random(int minValue, int maxValue) + { + return Random(new Random(), minValue, maxValue); + } + + /// + /// Returns an infinite sequence of random integers between and + /// using the supplied random number generator. + /// + /// Generator used to produce random numbers + /// Inclusive lower bound of the values returned + /// Exclusive upper bound of the values returned + /// An infinite sequence of random integers + /// Thrown if is . + + public static IEnumerable Random(Random rand, int minValue, int maxValue) + { + if (rand == null) throw new ArgumentNullException("rand"); + if (minValue > maxValue) + throw new ArgumentOutOfRangeException( "minValue", + string.Format("The argument minValue ({0}) is greater than maxValue ({1})", minValue, maxValue) ); + + return RandomImpl(rand, r => r.Next(minValue, maxValue)); + } + + /// + /// Returns an infinite sequence of random double values between 0.0 and 1.0 + /// + /// An infinite sequence of random doubles + + public static IEnumerable RandomDouble() + { + return RandomDouble(new Random()); + } + + /// + /// Returns an infinite sequence of random double values between 0.0 and 1.0 + /// using the supplied random number generator. + /// + /// Generator used to produce random numbers + /// An infinite sequence of random doubles + /// Thrown if is . + + public static IEnumerable RandomDouble(Random rand) + { + if (rand == null) throw new ArgumentNullException("rand"); + + return RandomImpl(rand, r => r.NextDouble()); + } + + /// + /// This is the underlying implementation that all random operators use to + /// produce a sequence of random values. + /// + /// The type of value returned (either Int32 or Double) + /// Random generators used to produce the sequence + /// Generator function that actually produces the next value - specific to T + /// An infinite sequence of random numbers of type T + + private static IEnumerable RandomImpl(Random rand, Func nextValue) + { + while (true) + yield return nextValue(rand); + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/RandomSubset.cs b/VolvoWrench/ExtensionMethods/MoreLinq/RandomSubset.cs new file mode 100644 index 0000000..f18e5a8 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/RandomSubset.cs @@ -0,0 +1,89 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Returns a sequence of a specified size of random elements from the original sequence + /// + /// The type of elements in the sequence + /// The sequence from which to return random elements + /// The size of the random subset to return + /// A random sequence of elements in random order from the original sequence + + public static IEnumerable RandomSubset(this IEnumerable sequence, int subsetSize) + { + return RandomSubset(sequence, subsetSize, new Random()); + } + + /// + /// Returns a sequence of a specified size of random elements from the original sequence + /// + /// The type of elements in the sequence + /// The sequence from which to return random elements + /// The size of the random subset to return + /// A random generator used as part of the selection algorithm + /// A random sequence of elements in random order from the original sequence + + public static IEnumerable RandomSubset(this IEnumerable sequence, int subsetSize, Random rand) + { + if (rand == null) throw new ArgumentNullException("rand"); + if (sequence == null) throw new ArgumentNullException("sequence"); + if (subsetSize < 0) throw new ArgumentOutOfRangeException("subsetSize"); + + return RandomSubsetImpl(sequence, subsetSize, rand); + } + + private static IEnumerable RandomSubsetImpl(IEnumerable sequence, int subsetSize, Random rand) + { + // The simplest and most efficient way to return a random subet is to perform + // an in-place, partial Fisher-Yates shuffle of the sequence. While we could do + // a full shuffle, it would be wasteful in the cases where subsetSize is shorter + // than the length of the sequence. + // See: http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle + + var seqArray = sequence.ToArray(); + if (seqArray.Length < subsetSize) + throw new ArgumentOutOfRangeException("subsetSize", "Subset size must be <= sequence.Count()"); + + var m = 0; // keeps track of count items shuffled + var w = seqArray.Length; // upper bound of shrinking swap range + var g = w - 1; // used to compute the second swap index + + // perform in-place, partial Fisher-Yates shuffle + while (m < subsetSize) + { + var k = g - rand.Next(w); + var tmp = seqArray[k]; + seqArray[k] = seqArray[m]; + seqArray[m] = tmp; + ++m; + --w; + } + + // yield the random subet as a new sequence + for (var i = 0; i < subsetSize; i++) + yield return seqArray[i]; + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Rank.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Rank.cs new file mode 100644 index 0000000..da8d7b4 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Rank.cs @@ -0,0 +1,104 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Ranks each item in the sequence in descending ordering using a default comparer. + /// + /// Type of item in the sequence + /// The sequence whose items will be ranked + /// A sequence of position integers representing the ranks of the corresponding items in the sequence + + public static IEnumerable Rank(this IEnumerable source) + { + return source.RankBy(x => x); + } + + /// + /// Rank each item in the sequence using a caller-supplied comparer. + /// + /// The type of the elements in the source sequence + /// The sequence of items to rank + /// A object that defines comparison semantics for the elements in the sequence + /// A sequence of position integers representing the ranks of the corresponding items in the sequence + + public static IEnumerable Rank(this IEnumerable source, IComparer comparer) + { + return source.RankBy(x => x, comparer); + } + + /// + /// Ranks each item in the sequence in descending ordering by a specified key using a default comparer + /// + /// The type of the elements in the source sequence + /// The type of the key used to rank items in the sequence + /// The sequence of items to rank + /// A key selector function which returns the value by which to rank items in the sequence + /// A sequence of position integers representing the ranks of the corresponding items in the sequence + + public static IEnumerable RankBy(this IEnumerable source, Func keySelector) + { + return RankBy(source, keySelector, null); + } + + /// + /// Ranks each item in a sequence using a specified key and a caller-supplied comparer + /// + /// The type of the elements in the source sequence + /// The type of the key used to rank items in the sequence + /// The sequence of items to rank + /// A key selector function which returns the value by which to rank items in the sequence + /// An object that defines the comparison semantics for keys used to rank items + /// A sequence of position integers representing the ranks of the corresponding items in the sequence + + public static IEnumerable RankBy(this IEnumerable source, Func keySelector, IComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + + return RankByImpl(source, keySelector, comparer ?? Comparer.Default); + } + + private static IEnumerable RankByImpl(IEnumerable source, Func keySelector, IComparer comparer) + { + source = source.ToArray(); // avoid enumerating source twice + + var rankDictionary = source.Distinct() + .OrderByDescending(keySelector, comparer) + .Index(1) + .ToDictionary(item => item.Value, + item => item.Key); + + // The following loop should not be be converted to a query to + // keep this RankBy lazy. + + // ReSharper disable LoopCanBeConvertedToQuery + + foreach (var item in source) + yield return rankDictionary[item]; + + // ReSharper restore LoopCanBeConvertedToQuery + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Repeat.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Repeat.cs new file mode 100644 index 0000000..aecd116 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Repeat.cs @@ -0,0 +1,50 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + public static partial class MoreEnumerable + { + /// + /// Repeats the specific sequences times. + /// + /// Type of elements in sequence + /// The sequence to repeat + /// Number of times to repeat the sequence + /// A sequence produced from the repetition of the original source sequence + + public static IEnumerable Repeat(this IEnumerable sequence, int count) + { + if (sequence == null) throw new ArgumentNullException("sequence"); + if (count < 0) throw new ArgumentOutOfRangeException("count", "Repeat count must be greater than or equal to zero."); + return RepeatImpl(sequence, count); + } + + private static IEnumerable RepeatImpl(this IEnumerable sequence, int count) + { + while (count-- > 0) + { + // TODO buffer to avoid multiple enumerations + foreach (var item in sequence) + yield return item; + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/ReverseComparer.cs b/VolvoWrench/ExtensionMethods/MoreLinq/ReverseComparer.cs new file mode 100644 index 0000000..859a1c6 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/ReverseComparer.cs @@ -0,0 +1,37 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2016 Felipe Sateler. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System.Collections.Generic; + + sealed class ReverseComparer : IComparer + { + readonly IComparer _underlying; + + public ReverseComparer(IComparer underlying) + { + _underlying = underlying ?? Comparer.Default; + } + + public int Compare(T x, T y) + { + var result = _underlying.Compare(x, y); + return result < 0 ? 1 : result > 0 ? -1 : 0; + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/RunLengthEncode.cs b/VolvoWrench/ExtensionMethods/MoreLinq/RunLengthEncode.cs new file mode 100644 index 0000000..95f5bae --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/RunLengthEncode.cs @@ -0,0 +1,88 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + public static partial class MoreEnumerable + { + /// + /// Run-length encodes a sequence by converting consecutive instances of the same element into + /// a KeyValuePair{T,int} representing the item and its occurrence count. + /// + /// The type of the elements in the sequence + /// The sequence to run length encode + /// A sequence of KeyValuePair{T,int} where the key is the element and the value is the occurrence count + + public static IEnumerable> RunLengthEncode(this IEnumerable sequence) + { + return RunLengthEncode(sequence, null); + } + + /// + /// Run-length encodes a sequence by converting consecutive instances of the same element into + /// a KeyValuePair{T,int} representing the item and its occurrence count. This overload + /// uses a custom equality comparer to identify equivalent items. + /// + /// The type of the elements in the sequence + /// The sequence to run length encode + /// The comparer used to identify equivalent items + /// A sequence of KeyValuePair{T,int} where they key is the element and the value is the occurrence count + + public static IEnumerable> RunLengthEncode(this IEnumerable sequence, IEqualityComparer comparer) + { + if (sequence == null) + throw new ArgumentNullException("sequence"); + + return RunLengthEncodeImpl(sequence, comparer ?? EqualityComparer.Default); + } + + private static IEnumerable> RunLengthEncodeImpl(IEnumerable sequence, IEqualityComparer comparer) + { + // This implementation could also have been written using a foreach loop, + // but it proved to be easier to deal with edge certain cases that occur + // (such as empty sequences) using an explicit iterator and a while loop. + + using (var iter = sequence.GetEnumerator()) + { + if (iter.MoveNext()) + { + var prevItem = iter.Current; + var runCount = 1; + + while (iter.MoveNext()) + { + if (comparer.Equals(prevItem, iter.Current)) + { + ++runCount; + } + else + { + yield return new KeyValuePair(prevItem, runCount); + prevItem = iter.Current; + runCount = 1; + } + } + + yield return new KeyValuePair(prevItem, runCount); + } + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Scan.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Scan.cs new file mode 100644 index 0000000..41e84c2 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Scan.cs @@ -0,0 +1,125 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Konrad Rudolph. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Peforms a scan (inclusive prefix sum) on a sequence of elements. + /// + /// + /// An inclusive prefix sum returns an equal-length sequence where the + /// N-th element is the sum of the first N input elements. More + /// generally, the scan allows any commutative binary operation, not + /// just a sum. + /// The exclusive version of Scan is . + /// This operator uses deferred execution and streams its result. + /// + /// + /// + /// Func<int, int, int> plus = (a, b) => a + b; + /// int[] values = { 1, 2, 3, 4 }; + /// IEnumerable<int> prescan = values.PreScan(plus, 0); + /// IEnumerable<int> scan = values.Scan(plus; a + b); + /// IEnumerable<int> result = values.ZipShortest(prescan, plus); + /// + /// prescan will yield { 0, 1, 3, 6 }, while scan + /// and result will both yield { 1, 3, 6, 10 }. This + /// shows the relationship between the inclusive and exclusive prefix sum. + /// + /// Type of elements in source sequence + /// Source sequence + /// Transformation operation + /// The scanned sequence + /// If is empty. + + public static IEnumerable Scan(this IEnumerable source, + Func transformation) + { + if (source == null) throw new ArgumentNullException("source"); + if (transformation == null) throw new ArgumentNullException("transformation"); + return ScanImpl(source, transformation); + } + + private static IEnumerable ScanImpl(IEnumerable source, Func f) + { + using (var i = source.GetEnumerator()) + { + if (!i.MoveNext()) + yield break; + + var aggregator = i.Current; + + while (i.MoveNext()) + { + yield return aggregator; + aggregator = f(aggregator, i.Current); + } + yield return aggregator; + } + } + + /// + /// Like except returns + /// the sequence of intermediate results as well as the final one. + /// An additional parameter specifies a seed. + /// + /// + /// This operator uses deferred execution and streams its result. + /// + /// + /// + /// var result = Enumerable.Range(1, 5).Scan(0, (a, b) => a + b); + /// + /// When iterated, result will yield { 0, 1, 3, 6, 10, 15 }. + /// + /// Type of elements in source sequence + /// Type of state + /// Source sequence + /// Initial state to seed + /// Transformation operation + /// The scanned sequence + + public static IEnumerable Scan(this IEnumerable source, + TState seed, Func transformation) + { + if (source == null) throw new ArgumentNullException("source"); + if (transformation == null) throw new ArgumentNullException("transformation"); + return ScanImpl(source, seed, transformation); + } + + private static IEnumerable ScanImpl(IEnumerable source, TState seed, Func f) + { + using (var i = source.GetEnumerator()) + { + var aggregator = seed; + + while (i.MoveNext()) + { + yield return aggregator; + aggregator = f(aggregator, i.Current); + } + yield return aggregator; + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Segment.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Segment.cs new file mode 100644 index 0000000..b037bf1 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Segment.cs @@ -0,0 +1,123 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + public static partial class MoreEnumerable + { + /// + /// Divides a sequence into multiple sequences by using a segment detector based on the original sequence + /// + /// The type of the elements in the sequence + /// The sequence to segment + /// A function, which returns true if the given element begins a new segment, and false otherwise + /// A sequence of segment, each of which is a portion of the original sequence + /// + /// Thrown if either or are . + /// + + public static IEnumerable> Segment(this IEnumerable source, Func newSegmentPredicate) + { + if (newSegmentPredicate == null) throw new ArgumentNullException("newSegmentPredicate"); + + return Segment(source, (curr, prev, index) => newSegmentPredicate(curr)); + } + + /// + /// Divides a sequence into multiple sequences by using a segment detector based on the original sequence + /// + /// The type of the elements in the sequence + /// The sequence to segment + /// A function, which returns true if the given element or index indicate a new segment, and false otherwise + /// A sequence of segment, each of which is a portion of the original sequence + /// + /// Thrown if either or are . + /// + + public static IEnumerable> Segment(this IEnumerable source, Func newSegmentPredicate) + { + if (newSegmentPredicate == null) throw new ArgumentNullException("newSegmentPredicate"); + + return Segment(source, (curr, prev, index) => newSegmentPredicate(curr, index)); + } + + /// + /// Divides a sequence into multiple sequences by using a segment detector based on the original sequence + /// + /// The type of the elements in the sequence + /// The sequence to segment + /// A function, which returns true if the given current element, previous element or index indicate a new segment, and false otherwise + /// A sequence of segment, each of which is a portion of the original sequence + /// + /// Thrown if either or are . + /// + + public static IEnumerable> Segment(this IEnumerable source, Func newSegmentPredicate) + { + if (source == null) throw new ArgumentNullException("source"); + if (newSegmentPredicate == null) throw new ArgumentNullException("newSegmentPredicate"); + + return SegmentImpl(source, newSegmentPredicate); + } + + private static IEnumerable> SegmentImpl(IEnumerable source, Func newSegmentPredicate) + { + var index = -1; + using (var iter = source.GetEnumerator()) + { + var segment = new List(); + var prevItem = default(T); + + // ensure that the first item is always part + // of the first segment. This is an intentional + // behavior. Segmentation always begins with + // the second element in the sequence. + if (iter.MoveNext()) + { + ++index; + segment.Add(iter.Current); + prevItem = iter.Current; + } + + while (iter.MoveNext()) + { + ++index; + // check if the item represents the start of a new segment + var isNewSegment = newSegmentPredicate(iter.Current, prevItem, index); + prevItem = iter.Current; + + if (!isNewSegment) + { + // if not a new segment, append and continue + segment.Add(iter.Current); + continue; + } + yield return segment; // yield the completed segment + + // start a new segment... + segment = new List { iter.Current }; + } + // handle the case of the sequence ending before new segment is detected + if (segment.Count > 0) + yield return segment; + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/SequenceException.cs b/VolvoWrench/ExtensionMethods/MoreLinq/SequenceException.cs new file mode 100644 index 0000000..4474c87 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/SequenceException.cs @@ -0,0 +1,75 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; +#if !NO_EXCEPTION_SERIALIZATION + using System.Runtime.Serialization; +#endif + + /// + /// The exception that is thrown for a sequence that fails a condition. + /// + +#if !NO_EXCEPTION_SERIALIZATION + [ Serializable ] +#endif + public class SequenceException : Exception + { + private const string defaultMessage = "Error in sequence."; + + /// + /// Initializes a new instance of the class. + /// + + public SequenceException() : + this(null) {} + + /// + /// Initializes a new instance of the class + /// with a given error message. + /// + /// A message that describes the error. + + public SequenceException(string message) : + this(message, null) { } + + /// + /// Initializes a new instance of the class + /// with a given error message and a reference to the inner exception + /// that is the cause of the exception. + /// + /// A message that describes the error. + /// The exception that is the cause of the current exception. + + public SequenceException(string message, Exception innerException) : + base(string.IsNullOrEmpty(message) ? defaultMessage : message, innerException) { } + +#if !NO_EXCEPTION_SERIALIZATION + /// + /// Initializes a new instance of the class + /// with serialized data. + /// + /// The object that holds the serialized object data. + /// The contextual information about the source or destination. + + protected SequenceException(SerializationInfo info, StreamingContext context) : + base(info, context) {} +#endif + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/SingleOrFallback.cs b/VolvoWrench/ExtensionMethods/MoreLinq/SingleOrFallback.cs new file mode 100644 index 0000000..7881ceb --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/SingleOrFallback.cs @@ -0,0 +1,92 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns the single element in the given sequence, or the result + /// of executing a fallback delegate if the sequence is empty. + /// This method throws an exception if there is more than one element in the sequence. + /// + /// + /// The fallback delegate is not executed if the sequence is non-empty. + /// This operator uses immediate execution and has optimizations for sources. + /// + /// Element type of sequence + /// The source sequence + /// The fallback delegate to execute if the sequence is empty + /// source or fallback is null + /// The sequence has more than one element + /// The single element in the sequence, or the result of calling the + /// fallback delegate if the sequence is empty. + /// + /// + /// var numbers = { 123, 456, 789 }; + /// var result = numbers.Where(x => x == 100).SingleOrFallback(() => -1); + /// + /// The result variable will contain -1. + /// + + [Obsolete("Consider using FallbackIfEmpty instead. SingleOrFallback may be removed in a future version. " + + "For more information, see https://github.com/morelinq/MoreLINQ/issues/122.")] + public static TSource SingleOrFallback(this IEnumerable source, Func fallback) + { + if (source == null) throw new ArgumentNullException("source"); + if (fallback == null) throw new ArgumentNullException("fallback"); + + var list = source as IList; + if (list != null) + { + switch (list.Count) + { + case 0: + return fallback(); + + case 1: + return list[0]; + + // anything but 0 and 1 is not handled + } + } + else + { + using (var iterator = source.GetEnumerator()) + { + if (!iterator.MoveNext()) + { + return fallback(); + } + var first = iterator.Current; + + // Return if there's no next element + if (!iterator.MoveNext()) + { + return first; + } + } + } + + // We should have checked the sequence length and returned by now + throw new InvalidOperationException("Sequence contains more than one element"); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/SkipUntil.cs b/VolvoWrench/ExtensionMethods/MoreLinq/SkipUntil.cs new file mode 100644 index 0000000..334d0e2 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/SkipUntil.cs @@ -0,0 +1,80 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Skips items from the input sequence until the given predicate returns true + /// when applied to the current source item; that item will be the last skipped. + /// + /// + /// + /// SkipUntil differs from Enumerable.SkipWhile in two respects. Firstly, the sense + /// of the predicate is reversed: it is expected that the predicate will return false + /// to start with, and then return true - for example, when trying to find a matching + /// item in a sequence. + /// + /// + /// Secondly, SkipUntil skips the element which causes the predicate to return true. For + /// example, in a sequence { 1, 2, 3, 4, 5 } and with a predicate of + /// x => x == 3, the result would be { 4, 5 }. + /// + /// + /// SkipUntil is as lazy as possible: it will not iterate over the source sequence + /// until it has to, it won't iterate further than it has to, and it won't evaluate + /// the predicate until it has to. (This means that an item may be returned which would + /// actually cause the predicate to throw an exception if it were evaluated, so long as + /// it comes after the first item causing the predicate to return true.) + /// + /// + /// Type of the source sequence + /// Source sequence + /// Predicate used to determine when to stop yielding results from the source. + /// Items from the source sequence after the predicate first returns true when applied to the item. + /// or is null + + public static IEnumerable SkipUntil(this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException("source"); + if (predicate == null) throw new ArgumentNullException("predicate"); + return SkipUntilImpl(source, predicate); + } + + private static IEnumerable SkipUntilImpl(this IEnumerable source, Func predicate) + { + using (var iterator = source.GetEnumerator()) + { + while (iterator.MoveNext()) + { + if (predicate(iterator.Current)) + { + break; + } + } + while (iterator.MoveNext()) + { + yield return iterator.Current; + } + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Slice.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Slice.cs new file mode 100644 index 0000000..650e2d2 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Slice.cs @@ -0,0 +1,68 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Extracts elements from a sequence at a particular zero-based starting index + /// + /// + /// If the starting position or count specified result in slice extending past the end of the sequence, + /// it will return all elements up to that point. There is no guarantee that the resulting sequence will + /// contain the number of elements requested - it may have anywhere from 0 to .
+ /// This method is implemented in an optimized manner for any sequence implementing IList{T}.
+ /// The result of Slice() is identical to: sequence.Skip(startIndex).Take(count) + ///
+ /// The type of the elements in the source sequence + /// The sequence from which to extract elements + /// The zero-based index at which to begin slicing + /// The number of items to slice out of the index + /// A new sequence containing any elements sliced out from the source sequence + + public static IEnumerable Slice(this IEnumerable sequence, int startIndex, int count) + { + if (sequence == null) throw new ArgumentNullException("sequence"); + if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex"); + if (count < 0) throw new ArgumentOutOfRangeException("count"); + + // optimization for anything implementing IList + var asList = sequence as IList; + return asList != null + ? SliceImpl(asList, startIndex, count) + : SliceImpl(sequence, startIndex, count); + } + + private static IEnumerable SliceImpl(IEnumerable sequence, int startIndex, int count) + { + return sequence.Skip(startIndex).Take(count); + } + + private static IEnumerable SliceImpl(IList list, int startIndex, int count) + { + var listCount = list.Count; + var index = startIndex; + while (index < listCount && count-- > 0) + yield return list[index++]; + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/SortedMerge.cs b/VolvoWrench/ExtensionMethods/MoreLinq/SortedMerge.cs new file mode 100644 index 0000000..3507b1e --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/SortedMerge.cs @@ -0,0 +1,173 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Merges two or more sequences that are in a common order (either ascending or descending) into + /// a single sequence that preserves that order. + /// + /// + /// Using SortedMerge on sequences that are not ordered or are not in the same order produces + /// undefined results.
+ /// SortedMerge uses performs the merge in a deferred, streaming manner.
+ /// + /// Here is an example of a merge, as well as the produced result: + /// + /// var s1 = new[] { 3, 7, 11 }; + /// var s2 = new[] { 2, 4, 20 }; + /// var s3 = new[] { 17, 19, 25 }; + /// var merged = s1.SortedMerge( OrderByDirection.Ascending, s2, s3 ); + /// var result = merged.ToArray(); + /// // result will be: + /// // { 2, 3, 4, 7, 11, 17, 19, 20, 25 } + /// + ///
+ /// The type of the elements of the sequence + /// The primary sequence with which to merge + /// The ordering that all sequences must already exhibit + /// A variable argument array of zero or more other sequences to merge with + /// A merged, order-preserving sequence containing all of the elements of the original sequences + + public static IEnumerable SortedMerge(this IEnumerable source, OrderByDirection direction, params IEnumerable[] otherSequences) + { + return SortedMerge(source, direction, null, otherSequences); + } + + /// + /// Merges two or more sequences that are in a common order (either ascending or descending) into + /// a single sequence that preserves that order. + /// + /// The type of the elements in the sequence + /// The primary sequence with which to merge + /// The ordering that all sequences must already exhibit + /// The comparer used to evaluate the relative order between elements + /// A variable argument array of zero or more other sequences to merge with + /// A merged, order-preserving sequence containing al of the elements of the original sequences + + public static IEnumerable SortedMerge(this IEnumerable source, OrderByDirection direction, IComparer comparer, params IEnumerable[] otherSequences) + { + if (source == null) throw new ArgumentNullException("source"); + if (otherSequences == null) throw new ArgumentNullException("otherSequences"); + + if (otherSequences.Length == 0) + return source; // optimization for when otherSequences is empty + + comparer = comparer ?? Comparer.Default; + + // define an precedence function based on the comparer and direction + // this is a function that will return True if (b) should precede (a) + var precedenceFunc = + direction == OrderByDirection.Ascending + ? (Func)((a, b) => comparer.Compare(b, a) < 0) + : (a, b) => comparer.Compare(b, a) > 0; + + // return the sorted merge result + return SortedMergeImpl(precedenceFunc, new[] { source }.Concat(otherSequences)); + } + + /// + /// Private implementation method that performs a merge of multiple, ordered sequences using + /// a precedence function which encodes order-sensitive comparison logic based on the caller's arguments. + /// + /// + /// The algorithm employed in this implementation is not necessarily the most optimal way to merge + /// two sequences. A swap-compare version would probably be somewhat more efficient - but at the + /// expense of considerably more complexity. One possible optimization would be to detect that only + /// a single sequence remains (all other being consumed) and break out of the main while-loop and + /// simply yield the items that are part of the final sequence. + /// + /// The algorithm used here will perform N*(K1+K2+...Kn-1) comparisons, where N => otherSequences.Count()+1. + /// + + private static IEnumerable SortedMergeImpl(Func precedenceFunc, IEnumerable> otherSequences) + { + using (var disposables = new DisposableGroup(otherSequences.Select(e => e.GetEnumerator()).Acquire())) + { + var iterators = disposables.Iterators; + + // prime all of the iterators by advancing them to their first element (if any) + // NOTE: We start with the last index to simplify the removal of an iterator if + // it happens to be terminal (no items) before we start merging + for (var i = iterators.Count - 1; i >= 0; i--) + { + if (!iterators[i].MoveNext()) + disposables.Exclude(i); + } + + // while all iterators have not yet been consumed... + while (iterators.Count > 0) + { + var nextIndex = 0; + var nextValue = disposables[0].Current; + + // find the next least element to return + for (var i = 1; i < iterators.Count; i++) + { + var anotherElement = disposables[i].Current; + // determine which element follows based on ordering function + if (precedenceFunc(nextValue, anotherElement)) + { + nextIndex = i; + nextValue = anotherElement; + } + } + + yield return nextValue; // next value in precedence order + + // advance iterator that yielded element, excluding it when consumed + if (!iterators[nextIndex].MoveNext()) + disposables.Exclude(nextIndex); + } + } + } + + /// + /// Class used to assist in ensuring that groups of disposable iterators + /// are disposed - either when Excluded or when the DisposableGroup is disposed. + /// + + private sealed class DisposableGroup : IDisposable + { + public DisposableGroup(IEnumerable> iterators) + { + Iterators = new List>(iterators); + } + + public List> Iterators { get; private set; } + + public IEnumerator this[int index] { get { return Iterators[index]; } } + + public void Exclude(int index) + { + Iterators[index].Dispose(); + Iterators.RemoveAt(index); + } + + public void Dispose() + { + Iterators.ForEach(iter => iter.Dispose()); + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Split.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Split.cs new file mode 100644 index 0000000..65b6a03 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Split.cs @@ -0,0 +1,322 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2012 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Linq; + + static partial class MoreEnumerable + { + + /// + /// Splits the source sequence by a separator. + /// + /// Type of element in the source sequence. + /// The source sequence. + /// Separator element. + /// A sequence of splits of elements. + + public static IEnumerable> Split(this IEnumerable source, + TSource separator) + { + return Split(source, separator, int.MaxValue); + } + + /// + /// Splits the source sequence by a separator given a maximum count of splits. + /// + /// Type of element in the source sequence. + /// The source sequence. + /// Separator element. + /// Maximum number of splits. + /// A sequence of splits of elements. + + public static IEnumerable> Split(this IEnumerable source, + TSource separator, int count) + { + return Split(source, separator, count, s => s); + } + + /// + /// Splits the source sequence by a separator and then transforms + /// the splits into results. + /// + /// Type of element in the source sequence. + /// Type of the result sequence elements. + /// The source sequence. + /// Separator element. + /// Function used to project splits + /// of source elements into elements of the resulting sequence. + /// + /// A sequence of values typed as . + /// + + public static IEnumerable Split(this IEnumerable source, + TSource separator, + Func, TResult> resultSelector) + { + return Split(source, separator, int.MaxValue, resultSelector); + } + + /// + /// Splits the source sequence by a separator, given a maximum count + /// of splits, and then transforms the splits into results. + /// + /// Type of element in the source sequence. + /// Type of the result sequence elements. + /// The source sequence. + /// Separator element. + /// Maximum number of splits. + /// Function used to project splits + /// of source elements into elements of the resulting sequence. + /// + /// A sequence of values typed as . + /// + + public static IEnumerable Split(this IEnumerable source, + TSource separator, int count, + Func, TResult> resultSelector) + { + return Split(source, separator, null, count, resultSelector); + } + + /// + /// Splits the source sequence by a separator and then transforms the + /// splits into results. + /// + /// Type of element in the source sequence. + /// The source sequence. + /// Separator element. + /// Comparer used to determine separator + /// element equality. + /// A sequence of splits of elements. + + public static IEnumerable> Split(this IEnumerable source, + TSource separator, IEqualityComparer comparer) + { + return Split(source, separator, comparer, int.MaxValue); + } + + /// + /// Splits the source sequence by a separator, given a maximum count + /// of splits. A parameter specifies how the separator is compared + /// for equality. + /// + /// Type of element in the source sequence. + /// The source sequence. + /// Separator element. + /// Comparer used to determine separator + /// element equality. + /// Maximum number of splits. + /// A sequence of splits of elements. + + public static IEnumerable> Split(this IEnumerable source, + TSource separator, IEqualityComparer comparer, int count) + { + return Split(source, separator, comparer, count, s => s); + } + + /// + /// Splits the source sequence by a separator and then transforms the + /// splits into results. A parameter specifies how the separator is + /// compared for equality. + /// + /// Type of element in the source sequence. + /// Type of the result sequence elements. + /// The source sequence. + /// Separator element. + /// Comparer used to determine separator + /// element equality. + /// Function used to project splits + /// of source elements into elements of the resulting sequence. + /// + /// A sequence of values typed as . + /// + + public static IEnumerable Split(this IEnumerable source, + TSource separator, IEqualityComparer comparer, + Func, TResult> resultSelector) + { + return Split(source, separator, comparer, int.MaxValue, resultSelector); + } + + /// + /// Splits the source sequence by a separator, given a maximum count + /// of splits, and then transforms the splits into results. A + /// parameter specifies how the separator is compared for equality. + /// + /// Type of element in the source sequence. + /// Type of the result sequence elements. + /// The source sequence. + /// Separator element. + /// Comparer used to determine separator + /// element equality. + /// Maximum number of splits. + /// Function used to project splits + /// of source elements into elements of the resulting sequence. + /// + /// A sequence of values typed as . + /// + + public static IEnumerable Split(this IEnumerable source, + TSource separator, IEqualityComparer comparer, int count, + Func, TResult> resultSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (count <= 0) throw new ArgumentOutOfRangeException("count"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + return SplitImpl(source, separator, comparer ?? EqualityComparer.Default, count, resultSelector); + } + + private static IEnumerable SplitImpl(IEnumerable source, + TSource separator, IEqualityComparer comparer, int count, + Func, TResult> resultSelector) + { + Debug.Assert(source != null); + Debug.Assert(comparer != null); + Debug.Assert(count >= 0); + Debug.Assert(resultSelector != null); + + return Split(source, item => comparer.Equals(item, separator), count, resultSelector); + } + + /// + /// Splits the source sequence by separator elements identified by a + /// function. + /// + /// Type of element in the source sequence. + /// The source sequence. + /// Predicate function used to determine + /// the splitter elements in the source sequence. + /// A sequence of splits of elements. + + public static IEnumerable> Split(this IEnumerable source, + Func separatorFunc) + { + return Split(source, separatorFunc, int.MaxValue); + } + + /// + /// Splits the source sequence by separator elements identified by a + /// function, given a maximum count of splits. + /// + /// Type of element in the source sequence. + /// The source sequence. + /// Predicate function used to determine + /// the splitter elements in the source sequence. + /// Maximum number of splits. + /// A sequence of splits of elements. + + public static IEnumerable> Split(this IEnumerable source, + Func separatorFunc, int count) + { + return Split(source, separatorFunc, count, s => s); + } + + /// + /// Splits the source sequence by separator elements identified by + /// a function and then transforms the splits into results. + /// + /// Type of element in the source sequence. + /// Type of the result sequence elements. + /// The source sequence. + /// Predicate function used to determine + /// the splitter elements in the source sequence. + /// Function used to project splits + /// of source elements into elements of the resulting sequence. + /// + /// A sequence of values typed as . + /// + + public static IEnumerable Split(this IEnumerable source, + Func separatorFunc, + Func, TResult> resultSelector) + { + return Split(source, separatorFunc, int.MaxValue, resultSelector); + } + + /// + /// Splits the source sequence by separator elements identified by + /// a function, given a maximum count of splits, and then transforms + /// the splits into results. + /// + /// Type of element in the source sequence. + /// Type of the result sequence elements. + /// The source sequence. + /// Predicate function used to determine + /// the splitter elements in the source sequence. + /// Maximum number of splits. + /// Function used to project a split + /// group of source elements into an element of the resulting sequence. + /// + /// A sequence of values typed as . + /// + + public static IEnumerable Split(this IEnumerable source, + Func separatorFunc, int count, + Func, TResult> resultSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (separatorFunc == null) throw new ArgumentNullException("separatorFunc"); + if (count <= 0) throw new ArgumentOutOfRangeException("count"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + return SplitImpl(source, separatorFunc, count, resultSelector); + } + + private static IEnumerable SplitImpl(IEnumerable source, + Func separatorFunc, int count, + Func, TResult> resultSelector) + { + Debug.Assert(source != null); + Debug.Assert(separatorFunc != null); + Debug.Assert(count >= 0); + Debug.Assert(resultSelector != null); + + if (count == 0) // No splits? + { + yield return resultSelector(source); + } + else + { + List items = null; + + foreach (var item in source) + { + if (count > 0 && separatorFunc(item)) + { + yield return resultSelector(items ?? Enumerable.Empty()); + count--; + items = null; + } + else + { + if (items == null) + items = new List(); + + items.Add(item); + } + } + + if (items != null && items.Count > 0) + yield return resultSelector(items); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/StartsWith.cs b/VolvoWrench/ExtensionMethods/MoreLinq/StartsWith.cs new file mode 100644 index 0000000..bdf26d8 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/StartsWith.cs @@ -0,0 +1,82 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2016 Andreas Gullberg Larsen. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Determines whether the beginning of the first sequence is + /// equivalent to the second sequence, using the default equality + /// comparer. + /// + /// Type of elements. + /// The sequence to check. + /// The sequence to compare to. + /// + /// true if begins with elements + /// equivalent to . + /// + /// + /// This is the equivalent of + /// and it calls + /// using + /// on pairs of elements at + /// the same index. + /// + public static bool StartsWith(this IEnumerable first, IEnumerable second) + { + return StartsWith(first, second, null); + } + + /// + /// Determines whether the beginning of the first sequence is + /// equivalent to the second sequence, using the specified element + /// equality comparer. + /// + /// Type of elements. + /// The sequence to check. + /// The sequence to compare to. + /// Equality comparer to use. + /// + /// true if begins with elements + /// equivalent to . + /// + /// + /// This is the equivalent of + /// and + /// it calls on pairs + /// of elements at the same index. + /// + public static bool StartsWith(this IEnumerable first, IEnumerable second, IEqualityComparer comparer) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + + comparer = comparer ?? EqualityComparer.Default; + + using (var firstIter = first.GetEnumerator()) + { + return second.All(item => firstIter.MoveNext() && comparer.Equals(firstIter.Current, item)); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Subsets.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Subsets.cs new file mode 100644 index 0000000..2271028 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Subsets.cs @@ -0,0 +1,246 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Returns a sequence of representing all of the subsets + /// of any size that are part of the original sequence. + /// + /// + /// This operator produces all of the subsets of a given sequence. Subsets are returned + /// in increasing cardinality, starting with the empty set and terminating with the + /// entire original sequence.
+ /// Subsets are produced in a deferred, streaming manner; however, each subset is returned + /// as a materialized list.
+ /// There are 2^N subsets of a given sequence, where N => sequence.Count(). + ///
+ /// Sequence for which to produce subsets + /// The type of the elements in the sequence + /// A sequence of lists that represent the all subsets of the original sequence + /// Thrown if is + + public static IEnumerable> Subsets(this IEnumerable sequence) + { + if (sequence == null) + throw new ArgumentNullException("sequence"); + return SubsetsImpl(sequence); + } + + /// + /// Returns a sequence of representing all subsets of the + /// specified size that are part of the original sequence. + /// + /// Sequence for which to produce subsets + /// The size of the subsets to produce + /// The type of the elements in the sequence + /// A sequence of lists that represents of K-sized subsets of the original sequence + /// + /// Thrown if is + /// + /// + /// Thrown if is less than zero. + /// + + public static IEnumerable> Subsets(this IEnumerable sequence, int subsetSize) + { + if (sequence == null) + throw new ArgumentNullException("sequence"); + if (subsetSize < 0) + throw new ArgumentOutOfRangeException("subsetSize", "Subset size must be >= 0"); + + // NOTE: Theres an interesting trade-off that we have to make in this operator. + // Ideally, we would throw an exception here if the {subsetSize} parameter is + // greater than the sequence length. Unforunately, determining the length of a + // sequence is not always possible without enumerating it. Herein lies the rub. + // We want Subsets() to be a deferred operation that only iterates the sequence + // when the caller is ready to consume the results. However, this forces us to + // defer the precondition check on the {subsetSize} upper bound. This can result + // in an exception that is far removed from the point of failure - an unfortunate + // and undesirable outcome. + // At the moment, this operator prioritizes deferred execution over fail-fast + // preconditions. This however, needs to be carefully considered - and perhaps + // may change after further thought and review. + + return new SubsetGenerator(sequence, subsetSize); + } + + /// + /// Underlying implementation for Subsets() overload. + /// + /// The type of the elements in the sequence + /// Sequence for which to produce subsets + /// Sequence of lists representing all subsets of a sequence + + private static IEnumerable> SubsetsImpl(IEnumerable sequence) + { + var sequenceAsList = sequence.ToList(); + var sequenceLength = sequenceAsList.Count; + + // the first subset is the empty set + yield return new List(); + + // all other subsets are computed using the subset generator + // this check also resolves the case of permuting empty sets + if (sequenceLength > 0) + { + for (var i = 1; i < sequenceLength; i++) + { + // each intermediate subset is a lexographically ordered K-subset + var subsetGenerator = new SubsetGenerator(sequenceAsList, i); + foreach (var subset in subsetGenerator) + yield return subset; + } + + yield return sequenceAsList; // the last subet is the original set itself + } + } + + /// + /// This class is responsible for producing the lexographically ordered k-subsets + /// + + private sealed class SubsetGenerator : IEnumerable> + { + /// + /// SubsetEnumerator uses a snapshot of the original sequence, and an + /// iterative, reductive swap algorithm to produce all subsets of a + /// predetermined size less than or equal to the original set size. + /// + + private class SubsetEnumerator : IEnumerator> + { + private readonly IList _set; // the original set of elements + private readonly T[] _subset; // the current subset to return + private readonly int[] _indices; // indices into the original set + + // TODO: It would be desirable to give these index members clearer names + private bool _continue; // termination indicator, set when all subsets have been produced + private int _m; // previous swap index (upper index) + private int _m2; // current swap index (lower index) + private int _k; // size of the subset being produced + private int _n; // size of the original set (sequence) + private int _z; // count of items excluded from the subet + + public SubsetEnumerator(IList set, int subsetSize) + { + // precondition: subsetSize <= set.Count + if (subsetSize > set.Count) + throw new ArgumentOutOfRangeException("subsetSize", "Subset size must be <= sequence.Count()"); + + // initialize set arrays... + _set = set; + _subset = new T[subsetSize]; + _indices = new int[subsetSize]; + // initialize index counters... + Reset(); + } + + public void Reset() + { + _m = _subset.Length; + _m2 = -1; + _k = _subset.Length; + _n = _set.Count; + _z = _n - _k + 1; + _continue = _subset.Length > 0; + } + + public IList Current + { + get { return (IList)_subset.Clone(); } + } + + object IEnumerator.Current + { + get { return Current; } + } + + public bool MoveNext() + { + if (!_continue) + return false; + + if (_m2 == -1) + { + _m2 = 0; + _m = _k; + } + else + { + if (_m2 < _n - _m) + { + _m = 0; + } + _m++; + _m2 = _indices[_k - _m]; + } + + for (var j = 1; j <= _m; j++) + _indices[_k + j - _m - 1] = _m2 + j; + + ExtractSubset(); + + _continue = (_indices[0] != _z); + return true; + } + + void IDisposable.Dispose() { } + + private void ExtractSubset() + { + for (var i = 0; i < _k; i++) + _subset[i] = _set[_indices[i] - 1]; + } + } + + private readonly IEnumerable _sequence; + private readonly int _subsetSize; + + public SubsetGenerator(IEnumerable sequence, int subsetSize) + { + if (sequence == null) + throw new ArgumentNullException("sequence"); + if (subsetSize < 0) + throw new ArgumentOutOfRangeException("subsetSize", "{subsetSize} must be between 0 and set.Count()"); + _subsetSize = subsetSize; + _sequence = sequence; + } + + /// + /// Returns an enumerator that produces all of the k-sized + /// subsets of the initial value set. The enumerator returns + /// and for each subset. + /// + /// an that enumerates all k-sized subsets + + public IEnumerator> GetEnumerator() + { + return new SubsetEnumerator(_sequence.ToList(), _subsetSize); + } + + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/TagFirstLast.cs b/VolvoWrench/ExtensionMethods/MoreLinq/TagFirstLast.cs new file mode 100644 index 0000000..c3aa2d0 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/TagFirstLast.cs @@ -0,0 +1,75 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2013 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + partial class MoreEnumerable + { + /// + /// Returns a sequence resulting from applying a function to each + /// element in the source sequence with additional parameters + /// indicating whether the element is the first and/or last of the + /// sequence. + /// + /// The type of the elements of . + /// The type of the element of the returned sequence. + /// The source sequence. + /// A function that determines how to + /// project the each element along with its first or last tag. + /// + /// Returns the resulting sequence. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// var numbers = new[] { 123, 456, 789 }; + /// var result = numbers.TagFirstLast((num, fst, lst) => new + /// { + /// Number = num, + /// IsFirst = fst, IsLast = lst + /// }); + /// + /// The result variable, when iterated over, will yield + /// { Number = 123, IsFirst = True, IsLast = False }, + /// { Number = 456, IsFirst = False, IsLast = False } and + /// { Number = 789, IsFirst = False, IsLast = True } in turn. + /// + + public static IEnumerable TagFirstLast(this IEnumerable source, Func resultSelector) + { + if (source == null) throw new ArgumentNullException("source"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + return TagFirsLastImpl(source, resultSelector); + } + + static IEnumerable TagFirsLastImpl(IEnumerable source, Func resultSelector) + { + var edge = new[] { new KeyValuePair(false, default(TSource)) }; + return edge.Concat(source.Select(e => new KeyValuePair(true, e))) + .Concat(edge) + .Pairwise((a, b) => new { Prev = a, Curr = b }) + .Pairwise((a, b) => new { a.Prev, a.Curr, Next = b.Curr }) + .Select(e => resultSelector(e.Curr.Value, !e.Prev.Key, !e.Next.Key)); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/TakeEvery.cs b/VolvoWrench/ExtensionMethods/MoreLinq/TakeEvery.cs new file mode 100644 index 0000000..764e66b --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/TakeEvery.cs @@ -0,0 +1,53 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + static partial class MoreEnumerable + { + /// + /// Returns every N-th element of a sequence. + /// + /// Type of the source sequence + /// Source sequence + /// Number of elements to bypass before returning the next element. + /// + /// A sequence with every N-th element of the input sequence. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// int[] numbers = { 1, 2, 3, 4, 5 }; + /// IEnumerable<int> result = numbers.TakeEvery(2); + /// + /// The result variable, when iterated over, will yield 1, 3 and 5, in turn. + /// + + public static IEnumerable TakeEvery(this IEnumerable source, int step) + { + if (source == null) throw new ArgumentNullException("source"); + if (step <= 0) throw new ArgumentOutOfRangeException("step"); + return source.Where((e, i) => i % step == 0); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/TakeLast.cs b/VolvoWrench/ExtensionMethods/MoreLinq/TakeLast.cs new file mode 100644 index 0000000..25fd89b --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/TakeLast.cs @@ -0,0 +1,75 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + static partial class MoreEnumerable + { + /// + /// Returns a specified number of contiguous elements from the end of + /// a sequence. + /// + /// The type of the elements of . + /// The sequence to return the last element of. + /// The number of elements to return. + /// + /// An that contains the specified number of + /// elements from the end of the input sequence. + /// + /// + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// int[] numbers = { 12, 34, 56, 78 }; + /// IEnumerable<int> result = numbers.TakeLast(2); + /// + /// The result variable, when iterated over, will yield + /// 56 and 78 in turn. + /// + + public static IEnumerable TakeLast(this IEnumerable source, int count) + { + if (source == null) throw new ArgumentNullException("source"); + return TakeLastImpl(source, count); + } + + private static IEnumerable TakeLastImpl(IEnumerable source, int count) + { + Debug.Assert(source != null); + + if (count <= 0) + yield break; + + var q = new Queue(count); + + foreach (var item in source) + { + if (q.Count == count) + q.Dequeue(); + q.Enqueue(item); + } + + foreach (var item in q) + yield return item; + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/TakeUntil.cs b/VolvoWrench/ExtensionMethods/MoreLinq/TakeUntil.cs new file mode 100644 index 0000000..7db00c8 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/TakeUntil.cs @@ -0,0 +1,74 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns items from the input sequence until the given predicate returns true + /// when applied to the current source item; that item will be the last returned. + /// + /// + /// + /// TakeUntil differs from Enumerable.TakeWhile in two respects. Firstly, the sense + /// of the predicate is reversed: it is expected that the predicate will return false + /// to start with, and then return true - for example, when trying to find a matching + /// item in a sequence. + /// + /// + /// Secondly, TakeUntil yields the element which causes the predicate to return true. For + /// example, in a sequence { 1, 2, 3, 4, 5 } and with a predicate of + /// x => x == 3, the result would be { 1, 2, 3 }. + /// + /// + /// TakeUntil is as lazy as possible: it will not iterate over the source sequence + /// until it has to, it won't iterate further than it has to, and it won't evaluate + /// the predicate until it has to. (This means that an item may be returned which would + /// actually cause the predicate to throw an exception if it were evaluated, so long as + /// no more items of data are requested.) + /// + /// + /// Type of the source sequence + /// Source sequence + /// Predicate used to determine when to stop yielding results from the source. + /// Items from the source sequence, until the predicate returns true when applied to the item. + /// or is null + + public static IEnumerable TakeUntil(this IEnumerable source, Func predicate) + { + if (source == null) throw new ArgumentNullException("source"); + if (predicate == null) throw new ArgumentNullException("predicate"); + return TakeUntilImpl(source, predicate); + } + + private static IEnumerable TakeUntilImpl(this IEnumerable source, Func predicate) + { + foreach (var item in source) + { + yield return item; + if (predicate(item)) + { + yield break; + } + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/ToDataTable.cs b/VolvoWrench/ExtensionMethods/MoreLinq/ToDataTable.cs new file mode 100644 index 0000000..943f063 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/ToDataTable.cs @@ -0,0 +1,267 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Johannes Rudolph. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Data; + using System.Linq; + using System.Linq.Expressions; + using System.Reflection; + + static partial class MoreEnumerable + { + private static MemberInfo GetAccessedMember(LambdaExpression lambda) + { + var body = lambda.Body; + + // If it's a field access, boxing was used, we need the field + if ((body.NodeType == ExpressionType.Convert) || (body.NodeType == ExpressionType.ConvertChecked)) + { + body = ((UnaryExpression)body).Operand; + } + + // Check if the MemberExpression is valid and is a "first level" member access e.g. not a.b.c + var memberExpression = body as MemberExpression; + if ((memberExpression == null) || (memberExpression.Expression.NodeType != ExpressionType.Parameter)) + { + throw new ArgumentException(string.Format("Illegal expression: {0}", lambda), "lambda"); + } + + return memberExpression.Member; + } + + private static IEnumerable PrepareMemberInfos(ICollection>> expressions) + { + // + // If no lambda expressions supplied then reflect them off the source element type. + // + + if (expressions == null || expressions.Count == 0) + { + return from m in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance) + where m.MemberType == MemberTypes.Field + || (m.MemberType == MemberTypes.Property && ((PropertyInfo) m).GetIndexParameters().Length == 0) + select m; + } + + // + // Ensure none of the expressions is null. + // + + if (expressions.Any(e => e == null)) + throw new ArgumentException("One of the supplied expressions was null.", "expressions"); + + try + { + return expressions.Select(GetAccessedMember); + } + catch (ArgumentException e) + { + throw new ArgumentException("One of the supplied expressions is not allowed.", "expressions", e); + } + } + + /// + /// The resulting array may contain null entries and those represent + /// columns for which there is no source member supplying a value. + /// + + private static MemberInfo[] BuildOrBindSchema(DataTable table, MemberInfo[] members) + { + // + // Retrieve member information needed to + // build or validate the table schema. + // + + var columns = table.Columns; + + var schemas = from m in members + let type = m.MemberType == MemberTypes.Property + ? ((PropertyInfo) m).PropertyType + : ((FieldInfo) m).FieldType + select new + { + Member = m, + Type = type.IsGenericType + && typeof(Nullable<>) == type.GetGenericTypeDefinition() + ? type.GetGenericArguments()[0] + : type, + Column = columns[m.Name], + }; + + // + // If the table has no columns then build the schema. + // If it has columns then validate members against the columns + // and re-order members to be aligned with column ordering. + // + + if (columns.Count == 0) + { + columns.AddRange(schemas.Select(m => new DataColumn(m.Member.Name, m.Type)).ToArray()); + } + else + { + members = new MemberInfo[columns.Count]; + + foreach (var info in schemas) + { + var member = info.Member; + var column = info.Column; + + if (column == null) + throw new ArgumentException(string.Format("Column named '{0}' is missing.", member.Name), "table"); + + if (info.Type != column.DataType) + throw new ArgumentException(string.Format("Column named '{0}' has wrong data type. It should be {1} when it is {2}.", member.Name, info.Type, column.DataType), "table"); + + members[column.Ordinal] = member; + } + } + + return members; + } + + private static UnaryExpression CreateMemberAccessor(Expression parameter, MemberInfo member) + { + var access = Expression.MakeMemberAccess(parameter, member); + return Expression.Convert(access, typeof(object)); + } + + private static Func CreateShredder(IEnumerable members) + { + var parameter = Expression.Parameter(typeof(T), "e"); + + // + // It is valid for members sequence to have null entries, in + // which case a null constant is emitted into the corresponding + // row values array. + // + + var initializers = members.Select(m => m != null + ? (Expression)CreateMemberAccessor(parameter, m) + : Expression.Constant(null, typeof(object))); + + var array = Expression.NewArrayInit(typeof(object), initializers); + + var lambda = Expression.Lambda>(array, parameter); + + return lambda.Compile(); + } + + /// + /// Appends elements in the sequence as rows of a given + /// object with a set of lambda expressions specifying which members (property + /// or field) of each element in the sequence will supply the column values. + /// + /// The type of the elements of . + /// The type of the input and resulting object. + /// The source. + /// The type of object where to add rows + /// Expressions providing access to element members. + /// + /// A or subclass representing the source. + /// + /// This operator uses immediate execution. + + public static TTable ToDataTable(this IEnumerable source, TTable table, params Expression>[] expressions) + where TTable : DataTable + { + if (source == null) throw new ArgumentNullException("source"); + if (table == null) throw new ArgumentNullException("table"); + + var members = PrepareMemberInfos(expressions).ToArray(); + members = BuildOrBindSchema(table, members); + var shredder = CreateShredder(members); + + // + // Builds rows out of elements in the sequence and + // add them to the table. + // + + table.BeginLoadData(); + + try + { + foreach (var element in source) + { + var row = table.NewRow(); + row.ItemArray = shredder(element); + table.Rows.Add(row); + } + } + finally + { + table.EndLoadData(); + } + + return table; + } + + /// + /// Appends elements in the sequence as rows of a given object. + /// + /// The type of the elements of . + /// + /// The source. + /// + /// + /// A or subclass representing the source. + /// + /// This operator uses immediate execution. + + public static TTable ToDataTable(this IEnumerable source, TTable table) + where TTable : DataTable + { + return ToDataTable(source, table, null); + } + + /// + /// Appends elements in the sequence as rows of a given + /// object with a set of lambda expressions specifying which members (property + /// or field) of each element in the sequence will supply the column values. + /// + /// The type of the elements of . + /// The source. + /// Expressions providing access to element members. + /// + /// A representing the source. + /// + /// This operator uses immediate execution. + + public static DataTable ToDataTable(this IEnumerable source, params Expression>[] expressions) + { + return ToDataTable(source, new DataTable(), expressions); + } + + /// + /// Converts a sequence to a object. + /// + /// The type of the elements of . + /// The source. + /// + /// A representing the source. + /// + /// This operator uses immediate execution. + + public static DataTable ToDataTable(this IEnumerable source) + { + return ToDataTable(source, new DataTable()); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/ToDelimitedString.cs b/VolvoWrench/ExtensionMethods/MoreLinq/ToDelimitedString.cs new file mode 100644 index 0000000..b83f933 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/ToDelimitedString.cs @@ -0,0 +1,91 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.Globalization; + using System.Text; + + static partial class MoreEnumerable + { + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// Type of element in the source sequence + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// Type of element in the source sequence + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, (sb, e) => sb.Append(e)); + } + + static string ToDelimitedStringImpl(IEnumerable source, string delimiter, Func append) + { + Debug.Assert(source != null); + Debug.Assert(append != null); + + delimiter = delimiter ?? CultureInfo.CurrentCulture.TextInfo.ListSeparator; + var sb = new StringBuilder(); + var i = 0; + + foreach (var value in source) + { + if (i++ > 0) sb.Append(delimiter); + append(sb, value); + } + + return sb.ToString(); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/ToDelimitedString.g.cs b/VolvoWrench/ExtensionMethods/MoreLinq/ToDelimitedString.g.cs new file mode 100644 index 0000000..b733522 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/ToDelimitedString.g.cs @@ -0,0 +1,702 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2013 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Text; + + partial class MoreEnumerable + { + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func Boolean = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.Boolean); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func Byte = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.Byte); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func Char = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.Char); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func Decimal = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.Decimal); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func Double = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.Double); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func Single = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.Single); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func Int32 = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.Int32); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func Int64 = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.Int64); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + [CLSCompliant(false)] + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func SByte = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + [CLSCompliant(false)] + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.SByte); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func Int16 = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.Int16); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func String = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.String); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + [CLSCompliant(false)] + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func UInt32 = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + [CLSCompliant(false)] + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.UInt32); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + [CLSCompliant(false)] + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func UInt64 = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + [CLSCompliant(false)] + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.UInt64); + } + + /// + /// Creates a delimited string from a sequence of values. The + /// delimiter used depends on the current culture of the executing thread. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// + /// A string that consists of the elements in + /// delimited by . If the source + /// sequence is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + [CLSCompliant(false)] + public static string ToDelimitedString(this IEnumerable source) + { + return ToDelimitedString(source, null); + } + + static partial class StringBuilderAppenders + { + public static readonly Func UInt16 = (sb, e) => sb.Append(e); + } + + /// + /// Creates a delimited string from a sequence of values and + /// a given delimiter. + /// + /// The sequence of items to delimit. Each is converted to a string using the + /// simple ToString() conversion. + /// The delimiter to inject between elements. May be null, in which case + /// the executing thread's current culture's list separator is used. + /// + /// A string that consists of the elements in + /// delimited by . If the source sequence + /// is empty, the method returns an empty string. + /// + /// + /// This operator uses immediate execution and effectively buffers the sequence. + /// + [CLSCompliant(false)] + public static string ToDelimitedString(this IEnumerable source, string delimiter) + { + if (source == null) throw new ArgumentNullException("source"); + return ToDelimitedStringImpl(source, delimiter, StringBuilderAppenders.UInt16); + } + + + static partial class StringBuilderAppenders {} + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/ToHashSet.cs b/VolvoWrench/ExtensionMethods/MoreLinq/ToHashSet.cs new file mode 100644 index 0000000..6e5b202 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/ToHashSet.cs @@ -0,0 +1,62 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + // TODO: Tests! (The code is simple enough I trust it not to fail, mind you...) + static partial class MoreEnumerable + { + /// + /// Returns a of the source items using the default equality + /// comparer for the type. + /// + /// Type of elements in source sequence. + /// Source sequence + /// A hash set of the items in the sequence, using the default equality comparer. + /// is null + /// + /// This evaluates the input sequence completely. + /// + + public static HashSet ToHashSet(this IEnumerable source) + { + return source.ToHashSet(null); + } + + /// + /// Returns a of the source items using the specified equality + /// comparer for the type. + /// + /// Type of elements in source sequence. + /// Source sequence + /// Equality comparer to use; a value of null will cause the type's default equality comparer to be used + /// A hash set of the items in the sequence, using the default equality comparer. + /// is null + /// + /// This evaluates the input sequence completely. + /// + + public static HashSet ToHashSet(this IEnumerable source, IEqualityComparer comparer) + { + if (source == null) throw new ArgumentNullException("source"); + return new HashSet(source, comparer); + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Trace.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Trace.cs new file mode 100644 index 0000000..305c27f --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Trace.cs @@ -0,0 +1,106 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + static partial class MoreEnumerable + { + /// + /// Traces the elements of a source sequence for diagnostics. + /// + /// Type of element in the source sequence + /// Source sequence whose elements to trace. + /// + /// Return the source sequence unmodified. + /// + /// + /// This a pass-through operator that uses deferred execution and + /// streams the results. + /// + + public static IEnumerable Trace(this IEnumerable source) + { + return Trace(source, (string) null); + } + + /// + /// Traces the elements of a source sequence for diagnostics using + /// custom formatting. + /// + /// Type of element in the source sequence + /// Source sequence whose elements to trace. + /// + /// String to use to format the trace message. If null then the + /// element value becomes the traced message. + /// + /// + /// Return the source sequence unmodified. + /// + /// + /// This a pass-through operator that uses deferred execution and + /// streams the results. + /// + + public static IEnumerable Trace(this IEnumerable source, string format) + { + if (source == null) throw new ArgumentNullException("source"); + + return TraceImpl(source, + string.IsNullOrEmpty(format) + ? (Func) (x => x == null ? string.Empty : x.ToString()) + : (x => string.Format(format, x))); + } + + /// + /// Traces the elements of a source sequence for diagnostics using + /// a custom formatter. + /// + /// Type of element in the source sequence + /// Source sequence whose elements to trace. + /// Function used to format each source element into a string. + /// + /// Return the source sequence unmodified. + /// + /// + /// This a pass-through operator that uses deferred execution and + /// streams the results. + /// + + public static IEnumerable Trace(this IEnumerable source, Func formatter) + { + if (source == null) throw new ArgumentNullException("source"); + if (formatter == null) throw new ArgumentNullException("formatter"); + return TraceImpl(source, formatter); + } + + private static IEnumerable TraceImpl(IEnumerable source, Func formatter) + { + Debug.Assert(source != null); + Debug.Assert(formatter != null); + + return source +#if !NO_TRACING + .Pipe(x => System.Diagnostics.Trace.WriteLine(formatter(x))) +#endif + ; + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Traverse.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Traverse.cs new file mode 100644 index 0000000..a9955dc --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Traverse.cs @@ -0,0 +1,92 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2016 Felipe Sateler. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public partial class MoreEnumerable + { + /// + /// Traverses a tree in a breadth-first fashion, starting at a root node and using a user-defined + /// function to get the children at each node of the tree. + /// + /// The tree node type + /// The root of the tree to traverse + /// The function that produces the children of each element + /// A sequence containing the traversed values + /// + /// This function defers traversal until needed and streams the results. + /// The tree is not checked for loops. If the resulting sequence needs to be finite then it is the + /// responsibility of to ensure that loops are not produced. + /// + public static IEnumerable TraverseBreadthFirst(T root, Func> childrenSelector) + { + if (childrenSelector == null) throw new ArgumentNullException("childrenSelector"); + return TraverseBreadthFirstImpl(root, childrenSelector); + } + + private static IEnumerable TraverseBreadthFirstImpl(T root, Func> childrenSelector) + { + var queue = new Queue(); + queue.Enqueue(root); + + while (queue.Count != 0) { + var current = queue.Dequeue(); + yield return current; + foreach (var child in childrenSelector(current)) + queue.Enqueue(child); + } + } + + /// + /// Traverses a tree in a depth-first fashion, starting at a root node and using a user-defined + /// function to get the children at each node of the tree. + /// + /// The tree node type + /// The root of the tree to traverse + /// The function that produces the children of each element + /// A sequence containing the traversed values + /// + /// This function defers traversal until needed and streams the results. + /// The tree is not checked for loops. If the resulting sequence needs to be finite then it is the + /// responsibility of to ensure that loops are not produced. + /// + public static IEnumerable TraverseDepthFirst(T root, Func> childrenSelector) + { + if (childrenSelector == null) throw new ArgumentNullException("childrenSelector"); + return TraverseDepthFirstImpl(root, childrenSelector); + } + + private static IEnumerable TraverseDepthFirstImpl(T root, Func> childrenSelector) + { + var stack = new Stack(); + stack.Push(root); + + while (stack.Count != 0) { + var current = stack.Pop(); + yield return current; + // because a stack pops the elements out in LIFO order, we need to push them in reverse + // if we want to traverse the returned list in the same order as was returned to us + foreach (var child in childrenSelector(current).Reverse()) + stack.Push(child); + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Windowed.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Windowed.cs new file mode 100644 index 0000000..7e2f90b --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Windowed.cs @@ -0,0 +1,78 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2010 Leopold Bushkin. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public static partial class MoreEnumerable + { + /// + /// Processes a sequence into a series of subsequences representing a windowed subset of the original + /// + /// + /// This operator is guaranteed to return at least one result, even if the source sequence is smaller + /// than the window size.
+ /// The number of sequences returned is: Max(0, sequence.Count() - windowSize) + 1
+ /// Returned subsequences are buffered, but the overall operation is streamed.
+ ///
+ /// The type of the elements of the source sequence + /// The sequence to evaluate a sliding window over + /// The size (number of elements) in each window + /// A series of sequences representing each sliding window subsequence + + public static IEnumerable> Windowed(this IEnumerable source, int size) + { + if (source == null) throw new ArgumentNullException("source"); + if (size <= 0) throw new ArgumentOutOfRangeException("size"); + + return WindowedImpl(source, size); + } + + private static IEnumerable> WindowedImpl(this IEnumerable source, int size) + { + using (var iter = source.GetEnumerator()) + { + // generate the first window of items + var countLeft = size; + var window = new List(); + // NOTE: The order of evaluation in the if() below is important + // because it relies on short-circuit behavior to ensure + // we don't move the iterator once the window is complete + while (countLeft-- > 0 && iter.MoveNext()) + { + window.Add(iter.Current); + } + + // return the first window (whatever size it may be) + yield return window; + + // generate the next window by shifting forward by one item + while (iter.MoveNext()) + { + // NOTE: If we used a circular queue rather than a list, + // we could make this quite a bit more efficient. + // Sadly the BCL does not offer such a collection. + window = new List(window.Skip(1)) { iter.Current }; + yield return window; + } + } + } + } +} \ No newline at end of file diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/Zip.stub.cs b/VolvoWrench/ExtensionMethods/MoreLinq/Zip.stub.cs new file mode 100644 index 0000000..a6616c0 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/Zip.stub.cs @@ -0,0 +1,4 @@ +// An implementation of Zip for sequences is included in Microsoft .NET +// Framework starting with version 4.0 and is identical to Zip in MoreLINQ. +// For more information on Zip, see the following reference on MSDN: +// http://msdn.microsoft.com/en-us/library/dd267698.aspx diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/ZipLongest.cs b/VolvoWrench/ExtensionMethods/MoreLinq/ZipLongest.cs new file mode 100644 index 0000000..71bbc93 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/ZipLongest.cs @@ -0,0 +1,94 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns a projection of tuples, where each tuple contains the N-th element + /// from each of the argument sequences. + /// + /// + /// If the two input sequences are of different lengths then the result + /// sequence will always be as long as the longer of the two input sequences. + /// The default value of the shorter sequence element type is used for padding. + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// int[] numbers = { 1, 2, 3 }; + /// string[] letters = { "A", "B", "C", "D" }; + /// var zipped = numbers.EquiZip(letters, (n, l) => n + l); + /// + /// The zipped variable, when iterated over, will yield "1A", "2B", "3C", "0D" in turn. + /// + /// Type of elements in first sequence + /// Type of elements in second sequence + /// Type of elements in result sequence + /// First sequence + /// Second sequence + /// Function to apply to each pair of elements + /// + /// A sequence that contains elements of the two input sequences, + /// combined by . + /// + + public static IEnumerable ZipLongest(this IEnumerable first, + IEnumerable second, + Func resultSelector) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return ZipLongestImpl(first, second, resultSelector); + } + + static IEnumerable ZipLongestImpl( + IEnumerable first, + IEnumerable second, + Func resultSelector) + { + using (var e1 = first.GetEnumerator()) + using (var e2 = second.GetEnumerator()) + { + while (e1.MoveNext()) + { + if (e2.MoveNext()) + { + yield return resultSelector(e1.Current, e2.Current); + } + else + { + do { yield return resultSelector(e1.Current, default(TSecond)); } + while (e1.MoveNext()); + yield break; + } + } + if (e2.MoveNext()) + { + do { yield return resultSelector(default(TFirst), e2.Current); } + while (e2.MoveNext()); + } + } + } + } +} diff --git a/VolvoWrench/ExtensionMethods/MoreLinq/ZipShortest.cs b/VolvoWrench/ExtensionMethods/MoreLinq/ZipShortest.cs new file mode 100644 index 0000000..0989203 --- /dev/null +++ b/VolvoWrench/ExtensionMethods/MoreLinq/ZipShortest.cs @@ -0,0 +1,167 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2008 Jonathan Skeet. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Returns a projection of tuples, where each tuple contains the N-th element + /// from each of the argument sequences. + /// + /// + /// If the input sequences are of different lengths, the result sequence + /// is terminated as soon as the shortest input sequence is exhausted. + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// var numbers = new[] { 1, 2, 3 }; + /// var letters = new[] { "A", "B", "C", "D" }; + /// var chars = new[] { 'a', 'b', 'c', 'd', 'e' }; + /// var zipped = numbers.ZipShortest(letters, chars, (n, l, c) => c + n + l); + /// + /// The zipped variable, when iterated over, will yield + /// "98A", "100B", "102C", in turn. + /// + /// Type of elements in first sequence + /// Type of elements in second sequence + /// Type of elements in third sequence + /// Type of elements in result sequence + /// First sequence + /// Second sequence + /// Third sequence + /// Function to apply to each triplet of elements + /// A projection of tuples, where each tuple contains the N-th element from each of the argument sequences. + + public static IEnumerable ZipShortest(this IEnumerable first, + IEnumerable second, IEnumerable third, Func resultSelector) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (third == null) throw new ArgumentNullException("third"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return ZipImpl(first, second, third, null, (a, b, c, _) => resultSelector(a, b, c)); + } + + /// + /// Returns a projection of tuples, where each tuple contains the N-th element + /// from each of the argument sequences. + /// + /// + /// If the input sequences are of different lengths, the result sequence + /// is terminated as soon as the shortest input sequence is exhausted. + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// var numbers = new[] { 1, 2, 3 }; + /// var letters = new[] { "A", "B", "C", "D" }; + /// var chars = new[] { 'a', 'b', 'c', 'd', 'e' }; + /// var flags = new[] { true, false }; + /// var zipped = numbers.ZipShortest(letters, chars, flags (n, l, c, f) => n + l + c + f); + /// + /// The zipped variable, when iterated over, will yield + /// "1AaTrue", "2BbFalse" in turn. + /// + /// Type of elements in first sequence + /// Type of elements in second sequence + /// Type of elements in third sequence + /// Type of elements in fourth sequence + /// Type of elements in result sequence + /// First sequence + /// Second sequence + /// Third sequence + /// Fourth sequence + /// Function to apply to each quadruplet of elements + /// A projection of tuples, where each tuple contains the N-th element from each of the argument sequences. + + public static IEnumerable ZipShortest(this IEnumerable first, + IEnumerable second, IEnumerable third, IEnumerable fourth, Func resultSelector) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (third == null) throw new ArgumentNullException("third"); + if (fourth == null) throw new ArgumentNullException("fourth"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return ZipImpl(first, second, third, fourth, resultSelector); + } + + /// + /// Returns a projection of tuples, where each tuple contains the N-th element + /// from each of the argument sequences. + /// + /// + /// If the two input sequences are of different lengths, the result sequence + /// is terminated as soon as the shortest input sequence is exhausted. + /// This operator uses deferred execution and streams its results. + /// + /// + /// + /// var numbers = new[] { 1, 2, 3 }; + /// var letters = new[] { "A", "B", "C", "D" }; + /// var zipped = numbers.ZipShortest(letters, (n, l) => n + l); + /// + /// The zipped variable, when iterated over, will yield "1A", "2B", "3C", in turn. + /// + /// Type of elements in first sequence + /// Type of elements in second sequence + /// Type of elements in result sequence + /// First sequence + /// Second sequence + /// Function to apply to each pair of elements + /// A projection of tuples, where each tuple contains the N-th element from each of the argument sequences + + public static IEnumerable ZipShortest(this IEnumerable first, + IEnumerable second, Func resultSelector) + { + if (first == null) throw new ArgumentNullException("first"); + if (second == null) throw new ArgumentNullException("second"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + return ZipImpl(first, second, null, null, (a, b, c, d) => resultSelector(a, b)); + } + + static IEnumerable ZipImpl( + IEnumerable s1, IEnumerable s2, + IEnumerable s3, IEnumerable s4, + Func resultSelector) + { + using (var e1 = s1.GetEnumerator()) + using (var e2 = s2.GetEnumerator()) + using (var e3 = s3 != null ? s3.GetEnumerator() : null) + using (var e4 = s4 != null ? s4.GetEnumerator() : null) + { + while (e1.MoveNext()) + { + if (e2.MoveNext() && (e3 == null || e3.MoveNext()) + && (e4 == null || e4.MoveNext())) + { + yield return resultSelector(e1.Current, e2.Current, + e3 != null ? e3.Current : default(T3), + e4 != null ? e4.Current : default(T4)); + } + } + } + } + } +} diff --git a/VolvoWrench/Main.cs b/VolvoWrench/Main.cs index 5b13a44..82d9c59 100644 --- a/VolvoWrench/Main.cs +++ b/VolvoWrench/Main.cs @@ -9,6 +9,7 @@ using System.Windows.Forms; using IniParser; using IniParser.Model; +using Ookii.Dialogs; using VolvoWrench.Demo_stuff; using VolvoWrench.Demo_stuff.GoldSource; using VolvoWrench.Demo_stuff.L4D2Branch.PortalStuff; @@ -276,8 +277,10 @@ private void statisticsToolStripMenuItem_Click(object sender, EventArgs e) private void demoVerificationToolToolStripMenuItem_Click(object sender, EventArgs e) { + AllowDrop = false; using (var a = new Verification()) a.ShowDialog(); + AllowDrop = true; } #endregion @@ -472,7 +475,9 @@ public static void SettingsManager(bool reset) average_fps=0 lowest_msec=0 highest_msec=0 -average_msec=0"}); +average_msec=0 +[DIALOG_PREFERENCES] +exit_dialog=1"}); #endregion DemoPopupKey = 0x70; //F1 OverLayExitKey = 0x71; @@ -803,11 +808,43 @@ static async Task WriteTextAsync(string filePath, string text) private void Main_FormClosing(object sender, FormClosingEventArgs e) { - SettingsManager(false); - if (MessageBox.Show(@"Are you sure you would like to close the program?",@"Confirm!",MessageBoxButtons.YesNo,MessageBoxIcon.Question) == DialogResult.No) + var parser = new FileIniDataParser(); + var iniD = parser.ReadFile(SettingsPath); + var showexitdialog = ToBoolean(int.Parse(iniD["DIALOG_PREFERENCES"]["exit_dialog"])); + if (showexitdialog) { - e.Cancel = true; + var td = new TaskDialog + { + WindowTitle = @"Warning", + Content = @"Are you sure you would like to exit?" + }; + td.Buttons.Add(new TaskDialogButton() + { + ButtonType = ButtonType.No + + }); + td.Buttons.Add(new TaskDialogButton() + { + ButtonType = ButtonType.Yes, + Default = true + }); + var checkbox = new TaskDialogRadioButton + { + Text = @"Never show this again!", + Checked = false + }; + td.RadioButtons.Add(checkbox); + if (td.ShowDialog().ButtonType == ButtonType.No) + { + e.Cancel = true; + } + if (td.RadioButtons[0].Checked) + { + iniD["DIALOG_PREFERENCES"]["exit_dialog"] = "0"; + } + } + parser.WriteFile(SettingsPath, iniD); } public void StripEnabler(CrossParseResult cpr) diff --git a/VolvoWrench/VolvoWrench.csproj b/VolvoWrench/VolvoWrench.csproj index 00996da..82b0e4e 100644 --- a/VolvoWrench/VolvoWrench.csproj +++ b/VolvoWrench/VolvoWrench.csproj @@ -94,6 +94,12 @@ True + + C:\Users\Traderai\Desktop\ooki\Ookii.Dialogs.dll + + + C:\Users\Traderai\Desktop\ooki\Ookii.Dialogs.Wpf.dll + ..\packages\SharpDX.3.1.1\lib\net45\SharpDX.dll @@ -207,6 +213,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form