Merge pull request #819 from brendanzagaeski/patch-1
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Utils / ArrayUtils.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 using System;
17 using System.Collections.Generic;
18 using System.Diagnostics;
19 using System.Text;
20
21 namespace Microsoft.Scripting.Utils {
22     public static class ArrayUtils {
23         internal sealed class FunctorComparer<T> : IComparer<T> {
24             private readonly Comparison<T> _comparison;
25
26             public FunctorComparer(Comparison<T> comparison) {
27                 Assert.NotNull(comparison);
28                 _comparison = comparison;
29             }
30
31             public int Compare(T x, T y) {
32                 return _comparison(x, y);
33             }
34         }
35
36         // Emitted:
37         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
38         public static readonly string[] EmptyStrings = new string[0];
39
40         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2105:ArrayFieldsShouldNotBeReadOnly")]
41         public static readonly object[] EmptyObjects = new object[0];
42
43         public static IComparer<T> ToComparer<T>(Comparison<T> comparison) {
44             return new FunctorComparer<T>(comparison);
45         }
46
47         public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] input, Func<TInput, TOutput> conv) {
48             ContractUtils.RequiresNotNull(input, "input");
49             ContractUtils.RequiresNotNull(conv, "conv");
50
51             TOutput[] res = new TOutput[input.Length];
52             for (int i = 0; i < input.Length; i++) {
53                 res[i] = conv(input[i]);
54             }
55
56             return res;
57         }
58
59         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional", MessageId = "1#")] // TODO: fix
60         public static void PrintTable(StringBuilder output, string[,] table) {
61             ContractUtils.RequiresNotNull(output, "output");
62             ContractUtils.RequiresNotNull(table, "table");
63
64             int max_width = 0;
65             for (int i = 0; i < table.GetLength(0); i++) {
66                 if (table[i, 0].Length > max_width) {
67                     max_width = table[i, 0].Length;
68                 }
69             }
70
71             for (int i = 0; i < table.GetLength(0); i++) {
72                 output.Append(" ");
73                 output.Append(table[i, 0]);
74
75                 for (int j = table[i, 0].Length; j < max_width + 1; j++) {
76                     output.Append(' ');
77                 }
78
79                 output.AppendLine(table[i, 1]);
80             }
81         }
82
83         public static T[] Copy<T>(T[] array) {
84             return (array.Length > 0) ? (T[])array.Clone() : array;
85         }
86
87         /// <summary>
88         /// Converts a generic ICollection of T into an array of T.  
89         /// 
90         /// If the collection is already an  array of T the original collection is returned.
91         /// </summary>
92         public static T[] ToArray<T>(ICollection<T> list) {
93             return (list as T[]) ?? MakeArray(list);
94         }
95
96         /// <summary>
97         /// Converts a generic ICollection of T into an array of R using a given conversion.  
98         /// 
99         /// If the collection is already an array of R the original collection is returned.
100         /// </summary>
101         public static TResult[] ToArray<TElement, TResult>(ICollection<TElement> list, Func<TElement, TResult> convertor) {
102             TResult[] res = list as TResult[];
103             if (res == null) {
104                 res = new TResult[list.Count];
105                 int i = 0;
106                 foreach (TElement obj in list) {
107                     res[i++] = convertor(obj);
108                 }
109             }
110             return res;
111         }
112
113         public static T[] MakeArray<T>(ICollection<T> list) {
114             if (list.Count == 0) {
115                 return new T[0];
116             }
117
118             T[] res = new T[list.Count];
119             list.CopyTo(res, 0);
120             return res;
121         }
122
123         public static T[] MakeArray<T>(ICollection<T> elements, int reservedSlotsBefore, int reservedSlotsAfter) {
124             if (reservedSlotsAfter < 0) throw new ArgumentOutOfRangeException("reservedSlotsAfter");
125             if (reservedSlotsBefore < 0) throw new ArgumentOutOfRangeException("reservedSlotsBefore");
126
127             if (elements == null) {
128                 return new T[reservedSlotsBefore + reservedSlotsAfter];
129             }
130
131             T[] result = new T[reservedSlotsBefore + elements.Count + reservedSlotsAfter];
132             elements.CopyTo(result, reservedSlotsBefore);
133             return result;
134         }
135
136         public static T[] RotateRight<T>(T[] array, int count) {
137             ContractUtils.RequiresNotNull(array, "array");
138             if ((count < 0) || (count > array.Length)) throw new ArgumentOutOfRangeException("count");
139
140             T[] result = new T[array.Length];
141             // The head of the array is shifted, and the tail will be rotated to the head of the resulting array
142             int sizeOfShiftedArray = array.Length - count;
143             Array.Copy(array, 0, result, count, sizeOfShiftedArray);
144             Array.Copy(array, sizeOfShiftedArray, result, 0, count);
145             return result;
146         }
147
148         public static T[] ShiftRight<T>(T[] array, int count) {
149             ContractUtils.RequiresNotNull(array, "array");
150             if (count < 0) throw new ArgumentOutOfRangeException("count");
151
152             T[] result = new T[array.Length + count];
153             System.Array.Copy(array, 0, result, count, array.Length);
154             return result;
155         }
156
157         public static T[] ShiftLeft<T>(T[] array, int count) {
158             ContractUtils.RequiresNotNull(array, "array");
159             if (count < 0) throw new ArgumentOutOfRangeException("count");
160
161             T[] result = new T[array.Length - count];
162             System.Array.Copy(array, count, result, 0, result.Length);
163             return result;
164         }
165
166         public static T[] Insert<T>(T item, IList<T> list) {
167             T[] res = new T[list.Count + 1];
168             res[0] = item;
169             list.CopyTo(res, 1);
170             return res;
171         }
172
173         public static T[] Insert<T>(T item1, T item2, IList<T> list) {
174             T[] res = new T[list.Count + 2];
175             res[0] = item1;
176             res[1] = item2;
177             list.CopyTo(res, 2);
178             return res;
179         }
180
181         public static T[] Insert<T>(T item, T[] array) {
182             T[] result = ShiftRight(array, 1);
183             result[0] = item;
184             return result;
185         }
186
187         public static T[] Insert<T>(T item1, T item2, T[] array) {
188             T[] result = ShiftRight(array, 2);
189             result[0] = item1;
190             result[1] = item2;
191             return result;
192         }
193
194         public static T[] Append<T>(T[] array, T item) {
195             System.Array.Resize<T>(ref array, (array == null) ? 1 : array.Length + 1);
196             array[array.Length - 1] = item;
197             return array;
198         }
199
200         public static T[] AppendRange<T>(T[] array, IList<T> items) {
201             return AppendRange<T>(array, items, 0);
202         }
203
204         public static T[] AppendRange<T>(T[] array, IList<T> items, int additionalItemCount) {
205             if (additionalItemCount < 0) {
206                 throw new ArgumentOutOfRangeException("additionalItemCount");
207             }
208
209             int j = (array == null) ? 0 : array.Length;
210             System.Array.Resize<T>(ref array, j + items.Count + additionalItemCount);
211
212             for (int i = 0; i < items.Count; i++, j++) {
213                 array[j] = items[i];
214             }
215
216             return array;
217         }
218
219         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1814:PreferJaggedArraysOverMultidimensional")] // TODO: fix
220         public static T[,] Concatenate<T>(T[,] array1, T[,] array2) {
221             int columnsCount = array1.GetLength(1);
222             Debug.Assert(array2.GetLength(1) == columnsCount);
223
224             int row1Count = array1.GetLength(0);
225             int row2Count = array2.GetLength(0);
226             int totalRowsCount = row1Count + row2Count;
227             T[,] result = new T[totalRowsCount, columnsCount];
228
229             for (int i = 0; i < row1Count; i++) {
230                 for (int j = 0; j < columnsCount; j++) {
231                     result[i, j] = array1[i, j];
232                 }
233             }
234
235             for (int i = 0; i < row2Count; i++) {
236                 for (int j = 0; j < columnsCount; j++) {
237                     result[(i + row1Count), j] = array2[i, j];
238                 }
239             }
240
241             return result;
242         }
243
244         public static void SwapLastTwo<T>(T[] array) {
245             Debug.Assert(array != null && array.Length >= 2);
246
247             T temp = array[array.Length - 1];
248             array[array.Length - 1] = array[array.Length - 2];
249             array[array.Length - 2] = temp;
250         }
251
252         public static T[] RemoveFirst<T>(IList<T> list) {
253             return ShiftLeft(MakeArray(list), 1);
254         }
255
256         public static T[] RemoveFirst<T>(T[] array) {
257             return ShiftLeft(array, 1);
258         }
259
260         public static T[] RemoveLast<T>(T[] array) {
261             ContractUtils.RequiresNotNull(array, "array");
262
263             System.Array.Resize(ref array, array.Length - 1);
264             return array;
265         }
266
267         public static T[] RemoveAt<T>(IList<T> list, int indexToRemove) {
268             return RemoveAt(MakeArray(list), indexToRemove);
269         }
270
271         public static T[] RemoveAt<T>(T[] array, int indexToRemove) {
272             ContractUtils.RequiresNotNull(array, "array");
273             ContractUtils.Requires(indexToRemove >= 0 && indexToRemove < array.Length, "index");
274
275             T[] result = new T[array.Length - 1];
276             if (indexToRemove > 0) {
277                 Array.Copy(array, 0, result, 0, indexToRemove);
278             }
279             int remaining = array.Length - indexToRemove - 1;
280             if (remaining > 0) {
281                 Array.Copy(array, array.Length - remaining, result, result.Length - remaining, remaining);
282             }
283             return result;
284         }
285
286         public static T[] InsertAt<T>(IList<T> list, int index, params T[] items) {
287             return InsertAt(MakeArray(list), index, items);
288         }
289
290         public static T[] InsertAt<T>(T[] array, int index, params T[] items) {
291             ContractUtils.RequiresNotNull(array, "array");
292             ContractUtils.RequiresNotNull(items, "items");
293             ContractUtils.Requires(index >= 0 && index <= array.Length, "index");
294
295             if (items.Length == 0) {
296                 return Copy(array);
297             }
298
299             T[] result = new T[array.Length + items.Length];
300             if (index > 0) {
301                 Array.Copy(array, 0, result, 0, index);
302             }
303             Array.Copy(items, 0, result, index, items.Length);
304
305             int remaining = array.Length - index;
306             if (remaining > 0) {
307                 Array.Copy(array, array.Length - remaining, result, result.Length - remaining, remaining);
308             }
309             return result;
310         }
311
312         public static bool ValueEquals<T>(this T[] array, T[] other) {
313             if (other.Length != array.Length) {
314                 return false;
315             }
316
317             for (int i = 0; i < array.Length; i++) {
318                 if (!Object.Equals(array[i], other[i])) {
319                     return false;
320                 }
321             }
322
323             return true;
324         }
325
326         public static int GetValueHashCode<T>(this T[] array) {
327             return GetValueHashCode<T>(array, 0, array.Length);
328         }
329
330         public static int GetValueHashCode<T>(this T[] array, int start, int count) {
331             ContractUtils.RequiresNotNull(array, "array");
332             ContractUtils.RequiresArrayRange(array.Length, start, count, "start", "count");
333             
334             if (count == 0) {
335                 return 0;
336             }
337
338             int result = array[start].GetHashCode();
339             for (int i = 1; i < count; i++) {
340                 result = ((result << 5) | (result >> 27)) ^ array[start + i].GetHashCode();
341             }
342
343             return result;
344         }
345
346         public static T[] Reverse<T>(this T[] array) {
347             T[] res = new T[array.Length];
348             for (int i = 0; i < array.Length; i++) {
349                 res[array.Length - i - 1] = array[i];
350             }
351             return res;
352         }
353     }
354 }