In the days before the internet, email and cell phones:
My ice hockey team was in the locker room talking about skating when the topic of a video showing me roller staking in Boston in 1980.
https://www.youtube.com/watch?v=IxlV_lOLmrk
Below is the new version that uses a background thread to do the work and allows multiple length data.
In this screenshot is the QuickSort running across 3 monitors was interrupted when about halfway done. The top half has already been sorted (no bolded items)
I wrote some simple tests to verify the algorithms worked and output the performance results to an Excel pivot chart
The multiple sorting algorithms have huge performance differences. The better performing tests were skewed toward the ones with the fewest screen updates, so the test code can change the visibility of the items when run.
It’s interesting that several of the algorithms were invented since my first implementation in the 1980’s
Start Visual Studio. (most versions)
File->New->Project->c#->Windows->WPF Application
replace the Mainwindow.Xaml.cs code with the code below
<SortingCode>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;
namespace WpfApplication1
{
publicpartialclassMainWindow : Window
{
publicstaticstring[] _SortTypes =
{
"Bubble",
"Unknown",
"Stupid",
"Insertion",
"Selection",
"OddEven",
"Cycle",
"Merge",
"MergeIP",
"MergeIP2",
"Quick",
"Shell",
"Heap",
"Super",
};
public MainWindow()
{
InitializeComponent();
this.WindowState = WindowState.Maximized;
this.Title = "Visual Sorter";
this.Loaded += MainWindow_Loaded;
}
publicstaticCancellationTokenSource _cancelTokSrc;
publicstaticbool _ShowSort = true;
internalCanvas _canvas = newCanvas();
publicstaticint _spControlsHeight = 60;
publicint _nRows;
publicclassSortBox : Label, IComparable
{
publicstructStats
{
publicDateTime startTime;
publicint numItems;
publiclong numCompares;
publiclong numReads;
publiclong numWrites;
publicint MaxDepth;
publicoverridestring ToString()
{
var elapsed = (DateTime.Now - startTime).TotalSeconds;
var strDepth = string.Empty;
if (MaxDepth >= 0)
{
strDepth = $" MaxDepth= {MaxDepth}";
}
return$"Secs= {elapsed,9:n3} Items= {numItems,8} Compares= {numCompares,13:n0} Reads= {numReads,13:n0} Writes= {numWrites,13:n0}{strDepth}";
}
}
publicstaticStats stats;
internalstaticvoid InitStats(int nTotal)
{
stats = newStats()
{
numItems = nTotal,
startTime = DateTime.Now,
MaxDepth = -1
};
}
public SortBox()
{
// this.FontSize = 8;
}
publicvoid Swap(SortBox other)
{
if (this != other)
{
var temp = this.Data;
this.Data = other.Data;
other.Data = temp;
}
}
privatestring _data;
publicstring Data
{
get
{
stats.numReads++;
return _data;
}
set
{
_data = value;
stats.numWrites++;
this.Update();
}
}
publicstaticbooloperator<(SortBox a, SortBox b)
{
stats.numCompares++;
if (string.CompareOrdinal(a.Data, b.Data) < 0)
{
returntrue;
}
returnfalse;
}
publicstaticbooloperator>(SortBox a, SortBox b)
{
stats.numCompares++;
if (string.CompareOrdinal(a.Data, b.Data) > 0)
{
returntrue;
}
returnfalse;
}
publicvoid Update()
{
if (_ShowSort)
{
bool fCancel = false;
Dispatcher.Invoke(() =>
{
// set the content on the UI threadthis.Content = this.Data;
if (_cancelTokSrc != null)
{
// check input queue for messagesvar stat = GetQueueStatus(4);
if (stat != 0)
{
//need to throw on right thread
fCancel = true;
}
}
});
if (fCancel)
{
//need to throw on right thread
_cancelTokSrc.Cancel();
thrownewTaskCanceledException("User cancelled");
}
}
}
publicoverridestring ToString()
{
return$"{this.Data}";
}
publicint CompareTo(object obj)
{
stats.numCompares++;
var other = obj asSortBox;
if (other != null)
{
returnstring.CompareOrdinal(this.Data, other.Data);
}
var otherAsString = obj asstring;
returnstring.CompareOrdinal(this.Data, otherAsString);
}
}
privatevoid MainWindow_Loaded(object sender, RoutedEventArgs e)
{
try
{
_nRows = ((int)this.ActualHeight - _spControlsHeight - 50) / 10;
this.Content = _canvas;
var spControls = newStackPanel()
{
Orientation = Orientation.Horizontal,
MaxHeight = _spControlsHeight
};
_canvas.Children.Add(spControls);
var btnSort = newButton()
{
Content = "Do_Sort",
ToolTip = "Will generate data and sort. Click to cancel. LeftShift-Click to continue an aborted sort, possibly with a different algorithm",
Height = 20,
VerticalAlignment = VerticalAlignment.Top
};
spControls.Children.Add(btnSort);
var cboSortType = newComboBox()
{
ItemsSource = _SortTypes,
Height = 20,
VerticalAlignment = VerticalAlignment.Top,
Width = 150,
};
cboSortType.SelectedIndex = 8;
spControls.Children.Add(cboSortType);
var txtNumItems = newTextBox()
{
Text = "4000",
ToolTip = "Max Number of items to sort. (limited by display)",
Height = 20,
VerticalAlignment = VerticalAlignment.Top,
Width = 100
};
spControls.Children.Add(txtNumItems);
var spVertControls = newStackPanel()
{
Orientation = Orientation.Vertical
};
spControls.Children.Add(spVertControls);
var chkLettersOnly = newCheckBox()
{
Content = "_Letters only",
ToolTip = "Include just letters or other characters too",
IsChecked = true
};
spVertControls.Children.Add(chkLettersOnly);
var chkShowSort = newCheckBox()
{
Content = "Show ",
ToolTip = "Update display during sort. Turn this off to see performance without updaating",
IsChecked = true
};
chkShowSort.Checked += (os, es) => _ShowSort = true;
chkShowSort.Unchecked += (os, es) => _ShowSort = false;
spVertControls.Children.Add(chkShowSort);
var txtMaxDataLength = newTextBox()
{
Text = "0",
Width = 40,
ToolTip = "Max # of random chars per datum. 0 means use a dictionary of real words"
};
spVertControls.Children.Add(txtMaxDataLength);
var txtStatus = newTextBox()
{
Margin = newThickness(10, 0, 0, 0),
Width = 900,
Height = _spControlsHeight,
VerticalScrollBarVisibility = ScrollBarVisibility.Auto
};
Action<string> addStatusMsg = (str) =>
{
Dispatcher.BeginInvoke(newAction(
() =>
{
txtStatus.AppendText($"{str}rn");
txtStatus.ScrollToEnd();
}
));
};
spControls.Children.Add(txtStatus);
List<SortBox> arrData = null;
int nTotal = 0;
btnSort.Click += (ob, eb) =>
{
_cancelTokSrc = newCancellationTokenSource();
btnSort.IsEnabled = false;
var sortType = (string)cboSortType.SelectedValue;
if (arrData == null ||
!System.Windows.Input.Keyboard.IsKeyDown(System.Windows.Input.Key.LeftShift)
)
{
// lets create controls on main thread
_canvas.Children.Clear();
_canvas.Children.Add(spControls);
nTotal = int.Parse(txtNumItems.Text);
arrData = newList<SortBox>();
var maxDatalength = int.Parse(txtMaxDataLength.Text);
arrData = InitData(ref nTotal, maxDatalength, chkLettersOnly.IsChecked.Value);
addStatusMsg($"Starting {sortType} with {nTotal} items. Click anywhare to stop");
}
else
{// user left shift-click: continue sorting with a possible different algorithm// note: cancellation can result in slight data errors because exception thrownfor (int i = 1; i < nTotal; i++)
{
if (arrData[i] < arrData[i - 1])
{
arrData[i].FontWeight = FontWeights.Normal;
}
}
addStatusMsg($"Continuing with {sortType}{nTotal} items. Click anywhare to stop");
}
var tsk = Task.Run(() =>
{
// do the sorting on a background threadtry
{
DoTheSorting(arrData, sortType, nTotal);
}
catch (TaskCanceledException)
{
}
catch (Exception ex)
{
addStatusMsg($"Exception {ex.ToString()}");
}
txtStatus.Dispatcher.BeginInvoke(newAction(
() =>
{
// now that we're done, let's verifyvar stats = SortBox.stats;
var hasError = ValidateSorted(arrData, nTotal);
if (!_ShowSort)
{
// show sorted resultsfor (int i = 0; i < nTotal; i++)
{
arrData[i].Content = arrData[i].Data;
}
}
string donemsg = _cancelTokSrc.IsCancellationRequested ? "Aborted" : "Done ";
addStatusMsg($"{sortType,10}{donemsg}{stats}{hasError}");
btnSort.IsEnabled = true;
}));
});
};
btnSort.RaiseEvent(newRoutedEventArgs(Button.ClickEvent, this));
}
catch (Exception ex)
{
this.Content = ex.ToString();
}
}
publicstring ValidateSorted(List<SortBox> arrData, int nTotal)
{
var hasError = string.Empty;
int nErrors = 0;
for (int i = 1; i < nTotal; i++)
{
if (arrData[i] < arrData[i - 1])
{
nErrors++;
arrData[i].FontWeight = FontWeights.ExtraBold;
}
}
if (nErrors > 0)
{
hasError = $"Error! {nErrors} not sorted";
}
return hasError;
}
publicList<SortBox> InitData(refint nTotal, int maxDatalength, bool ltrsOnly)
{
var arrData = newList<SortBox>();
var rand = newRandom(1);
/* to use without the dictionary (using random letters) comment the current line and the new Dictionary line below
dynamic dict = null;
maxDatalength = 5;
/*/// get the dictionary from my OneDrive:// https://onedrive.live.com/redir?resid=D69F3552CEFC21!99083&authkey=!AFjyjUlZpH5sQy0&ithint=file%2cdll // then run the command RegSvr32 dictionary.dll// then add a reference to COM ->Dictionary 1.0 Type Library
Dictionary.CDict dict = null;
//*/int colWidth;
// we will try to fill the screen with sort data. // However, there are times when a particular # of items is desired,// such as debugging a list of 5 items.// so we limit by nTotal or screen capacity if (maxDatalength == 0)
{
dict = new Dictionary.CDict(); // comment out this line if no dictionary
dict.DictNum = 2;
maxDatalength = 11;
colWidth = 8 * (maxDatalength - 1);
}
else
{
colWidth = 20 + 8 * (maxDatalength - 1);
}
int nCols = (int)this.ActualWidth / colWidth;
if (nCols == 0) //tests
{
nCols = 80;
}
for (int i = 0; i < _nRows; i++)
{
for (int j = 0; j < nCols; j++)
{
if (arrData.Count < nTotal)
{
string dat = string.Empty;
switch (arrData.Count)
{
// set the first few items to const so easier to debug algorithmscase 0:
dat = "zero";
break;
case 1:
dat = "one";
break;
case 2:
dat = "two";
break;
case 3:
dat = "three";
break;
case 4:
dat = "four";
break;
case 5:
dat = "five";
break;
default:
var len = 1 + rand.Next(maxDatalength);
var datarray = newchar[len];
for (int k = 0; k < len; k++)
{
// "A" is 65,"!" is 33
datarray[k] = (char)(ltrsOnly == true ?
65 + rand.Next(26) :
33 + rand.Next(90));
}
dat = newstring(datarray);
break;
}
var box = newSortBox();
if (dict == null)
{
box.Data = dat.Substring(0, Math.Min(maxDatalength, dat.Length));
}
else
{
box.Data = dict.RandWord(0);
}
box.Content = box.Data;
arrData.Add(box);
if (_ShowSort)
{
Canvas.SetTop(box, 3 + _spControlsHeight + i * 10);
Canvas.SetLeft(box, j * colWidth);
_canvas.Children.Add(box);
}
}
}
}
nTotal = arrData.Count; // could be lessif (!_ShowSort)
{
// show initial valuesfor (int i = 0; i < nTotal; i++)
{
arrData[i].Content = arrData[i].Data;
}
}
SortBox.InitStats(nTotal);
return arrData;
}
publicvoid DoTheSorting(List<SortBox> arrData, string sortType, int nTotal)
{
switch (sortType)
{
case"Bubble":
var nEnd = nTotal;
var newEnd = 0;
do
{
newEnd = 0;
for (int i = 1; i < nEnd; i++)
{
if (arrData[i - 1] > arrData[i])
{
arrData[i - 1].Swap(arrData[i]);
newEnd = i;
}
}
nEnd = newEnd;
} while (newEnd != 0);
break;
case"Unknown":
for (int i = 1; i < nTotal; i++)
{
for (int j = 0; j < i; j++)
{
if (arrData[i] < arrData[j])
{
arrData[i].Swap(arrData[j]);
}
}
}
break;
case"Stupid": // gnomeSortfor (int i = 0; i < nTotal;)
{
if (i == 0 || !(arrData[i - 1] > arrData[i]))
{
i++;
}
else
{
arrData[i - 1].Swap(arrData[i]);
i--;
}
}
break;
case"Insertion":
for (int i = 1; i < nTotal; i++)
{
var t = arrData[i].Data;
int j = i - 1;
for (; j >= 0 && arrData[j].CompareTo(t) > 0; j--)
{
arrData[j + 1].Data = arrData[j].Data;
}
//int j = i - 1;//while (j >= 0 && arrData[j].CompareTo(t) > 0)//{// arrData[j + 1].Data = arrData[j].Data;// j--;//}
arrData[j + 1].Data = t;
}
break;
case"Selection":
// scan entire array for minimum, swap with 1st, then repeat for restfor (int i = 0; i < nTotal - 1; i++)
{
int minimum = i;
for (int j = i + 1; j < nTotal; j++)
{
if (arrData[j] < arrData[minimum])
{
minimum = j;
}
}
if (minimum != i)
{
arrData[i].Swap(arrData[minimum]);
}
}
break;
case"OddEven":
var sorted = false;
while (!sorted)
{
sorted = true;
for (int i = 1; i < nTotal - 1; i += 2)
{
if (arrData[i + 1] < arrData[i])
{
arrData[i + 1].Swap(arrData[i]);
sorted = false;
}
}
for (int i = 0; i < nTotal - 1; i += 2)
{
if (arrData[i + 1] < arrData[i])
{
arrData[i + 1].Swap(arrData[i]);
sorted = false;
}
}
}
break;
case"Cycle":
// deceptively fast because fewer updates, // which are expensive in this code because of thread// context switches and drawing updated data// note the # of updates <= ntotalfor (int cycleStart = 0; cycleStart < nTotal; cycleStart++)
{
// the item to placevar item = arrData[cycleStart].Data;
int pos = cycleStart;
do
{
int nextPos = 0;
// find it's position # in entire array// by finding how many should precede itfor (int i = 0; i < nTotal; i++)
{
if (i != cycleStart && arrData[i].CompareTo(item) < 0)
{
nextPos++;
}
}
// if it's not in the correct positionif (pos != nextPos)
{
// move past duplicateswhile (pos != nextPos && arrData[nextPos].CompareTo(item) == 0)
{
nextPos++;
}
// save the cur value at nextposvar temp = arrData[nextPos].Data;
// set the value at nextpos to the item to place
arrData[nextPos].Data = item;
// new value for which to seek position
item = temp;
pos = nextPos;
}
} while (pos != cycleStart);
}
break;
case"Merge":
// not in place: uses additional storage// lets make a recursive lambdaAction<int, int, int> MergeSort = null;
MergeSort = (left, right, depth) =>
{
SortBox.stats.MaxDepth = Math.Max(depth, SortBox.stats.MaxDepth);
if (right > left)
{
int mid = (right + left) / 2;
MergeSort(left, mid, depth + 1);
mid++;
MergeSort(mid, right, depth + 1);
// now we merge 2 sections that are already sortedint leftNdx = left;
// use extra storagevar temp = newList<string>();
int pivot = mid;
while (leftNdx < pivot && mid <= right)
{
// fill temp from left or rightif (arrData[mid] < arrData[leftNdx])
{
temp.Add(arrData[mid++].Data);
}
else
{
temp.Add(arrData[leftNdx++].Data);
}
}
// deal with leftovers on left or rightwhile (leftNdx < pivot)
{
temp.Add(arrData[leftNdx++].Data);
}
while (mid <= right)
{
temp.Add(arrData[mid++].Data);
}
// fill the elements with the sorted listfor (int i = 0; i < temp.Count; i++)
{
arrData[left + i].Data = temp[i];
}
}
};
MergeSort(0, nTotal - 1, 0);
break;
case"MergeIP":
Action<int, int, int> MergeSortIp = null;
MergeSortIp = (left, right, depth) =>
{
SortBox.stats.MaxDepth = Math.Max(depth, SortBox.stats.MaxDepth);
if (left >= right)
{
return;
}
int mid = (left + right) / 2;
MergeSortIp(left, mid, depth + 1);
mid++;
MergeSortIp(mid, right, depth + 1);
int leftNdx = left;
int pivot = mid;
int leftEnd = pivot - 1;
while (leftNdx <= leftEnd && mid <= right)
{
if (arrData[leftNdx] < arrData[mid])
{
// left already in place
leftNdx++;
}
else
{
// take from right: shift everyone over to make roomvar temp = arrData[mid].Data;
for (int j = mid - 1; j >= leftNdx; j--)
{
arrData[j + 1].Data = arrData[j].Data;
}
arrData[leftNdx].Data = temp;
leftNdx++;
leftEnd++;
mid++;
}
}
};
MergeSortIp(0, nTotal - 1, 0);
break;
case"MergeIP2":
//http://stackoverflow.com/questions/2571049/how-to-sort-in-place-using-the-merge-sort-algorithm/22839426#22839426Action<int, int> reverse = (a, b) =>
{
for (--b; a < b; a++, b--)
{
arrData[a].Swap(arrData[b]);
}
};
Func<int, int, int, int> rotate = (a, b, c) =>
{
//* swap the sequence [a,b) with [b,c). if (a != b && b != c)
{
reverse(a, b);
reverse(b, c);
reverse(a, c);
}
return a + c - b;
};
Func<int, int, SortBox, int> lower_bound = (a, b, key) =>
{
//* find first element not less than @p key in sorted sequence or end of// * sequence (@p b) if not found. for (int i = b - a; i != 0; i /= 2)
{
int mid = a + i / 2;
if (arrData[mid] < key)
{
a = mid + 1;
i--;
}
}
return a;
};
Func<int, int, SortBox, int> upper_bound = (a, b, key) =>
{
///* find first element greater than @p key in sorted sequence or end of//* sequence (@p b) if not found. for (int i = b - a; i != 0; i /= 2)
{
int mid = a + i / 2;
if (arrData[mid].CompareTo(key) <= 0)
{
a = mid + 1;
i--;
}
}
return a;
};
Action<int, int, int, int> mergeInPlace = null;
mergeInPlace = (left, mid, right, depth) =>
{
SortBox.stats.MaxDepth = Math.Max(depth, SortBox.stats.MaxDepth);
int n1 = mid - left;
int n2 = right - mid;
if (n1 == 0 || n2 == 0)
{
return;
}
if (n1 == 1 && n2 == 1)
{
if (arrData[mid] < arrData[left])
{
arrData[mid].Swap(arrData[left]);
}
}
else
{
int p, q;
if (n1 <= n2)
{
q = mid + n2 / 2;
p = upper_bound(left, mid, arrData[q]);
}
else
{
p = left + n1 / 2;
q = lower_bound(mid, right, arrData[p]);
}
mid = rotate(p, mid, q);
mergeInPlace(left, p, mid, depth + 1);
mergeInPlace(mid, q, right, depth + 1);
}
};
Action<int, int, int> inPlaceMergeSort = null;
inPlaceMergeSort = (left, nElem, depth) =>
{
if (nElem > 1)
{
int mid = nElem / 2;
inPlaceMergeSort(left, mid, depth + 1);
inPlaceMergeSort(left + mid, nElem - mid, depth + 1);
mergeInPlace(left, left + mid, left + nElem, depth + 1);
}
};
inPlaceMergeSort(0, nTotal, 0);
break;
case"Quick":
Action<int, int, int> quickSort = null;
quickSort = (left, right, depth) =>
{
SortBox.stats.MaxDepth = Math.Max(depth, SortBox.stats.MaxDepth);
if (left < right)
{
var pivot = arrData[left];
int i = left;
int j = right;
while (i < j)
{
// find the leftmost one that should be on the rightwhile (i < right && !(arrData[i] > pivot))
{
i++;
}
// set j to the rightmost one that should be on the leftwhile (arrData[j] > pivot)
{
j--;
}
if (i < j)
{
arrData[i].Swap(arrData[j]);
}
}
// now put pivot into place
pivot.Swap(arrData[j]);
// now recur to sort left, then right sides
quickSort(left, j - 1, depth + 1);
quickSort(j + 1, right, depth + 1);
}
};
// now do the actual sort
quickSort(0, nTotal - 1, 0);
break;
case"Shell":
for (int g = nTotal / 2; g > 0; g /= 2)
{
for (int i = g; i < nTotal; i++)
{
for (int j = i - g; j >= 0 && arrData[j] > arrData[j + g]; j -= g)
{
arrData[j].Swap(arrData[j + g]);
}
}
}
break;
case"Heap":
// https://simpledevcode.wordpress.com/2014/11/25/heapsort-c-tutorial/int heapSize = 0;
Action<int, int> Heapify = null;
Heapify = (index, depth) =>
{
SortBox.stats.MaxDepth = Math.Max(SortBox.stats.MaxDepth, depth);
int left = 2 * index;
int right = 2 * index + 1;
int largest = index;
if (left <= heapSize && arrData[left] > arrData[index])
{
largest = left;
}
if (right <= heapSize && arrData[right] > arrData[largest])
{
largest = right;
}
if (largest != index)
{
arrData[index].Swap(arrData[largest]);
Heapify(largest, depth + 1);
}
};
heapSize = nTotal - 1;
for (int i = nTotal / 2; i >= 0; i--)
{
Heapify(i, 0);
}
for (int i = nTotal - 1; i >= 0; i--)
{
arrData[0].Swap(arrData[i]);
heapSize--;
Heapify(0, 0);
}
break;
case"Super":
var data = (from dat in arrData
orderby dat
select dat.Data).ToArray();
for (int i = 0; i < nTotal; i++)
{
arrData[i].Data = data[i];
}
break;
}
}
[DllImport("user32.dll")]
staticexternuint GetQueueStatus(uint flags);
}
}
</SortingCode>
<TestCode>
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WpfApplication1;
using System.Linq;
namespace SorterTests
{
[TestClass]
publicclassUnitTest1
{
publicTestContext TestContext { get; set; }
[TestMethod]
publicvoid TestMethod1()
{
MainWindow._ShowSort = false;
TestContext.WriteLine("Start");
foreach (var sortType inMainWindow._SortTypes.Where(
/*
t => true
/*/
t =>
t != "Cycle"&& t != "Bubble"&& t != "Unknown"&& t != "Stupid"&& t != "Selection"&& t != "Insertion"&& t != "MergeIP"&& t != "OddEven"//*/
))
{
for (int pow = 0, nTotal = 1; pow < 7; pow++)
{
var mwindow = newMainWindow();
mwindow._nRows = 1000000; // don't limit data by # rowsvar arrData = mwindow.InitData(ref nTotal, maxDatalength: 4, ltrsOnly: false);
mwindow.DoTheSorting(arrData, sortType, nTotal);
var errs = mwindow.ValidateSorted(arrData, nTotal);
Assert.IsTrue(string.IsNullOrEmpty(errs), $"{sortType}{errs}");
TestContext.WriteLine($"{sortType,10}{MainWindow.SortBox.stats}");
nTotal *= 10;
}
}
}
/*
ActiveSheet.PasteSpecial Format:="Unicode Text", Link:=False, _
DisplayAsIcon:=False
Range("A1:B5").Select
Selection.EntireRow.Delete
Columns("B:P").Select
Columns("B:P").EntireColumn.AutoFit
Range("C:C,E:E,G:G,I:I,K:K,M:M,O:O").Select
Range("O1").Activate
Selection.EntireColumn.Hidden = True
Range("B1").Select
Selection.EntireRow.Insert , CopyOrigin:=xlFormatFromLeftOrAbove
ActiveCell.FormulaR1C1 = "Sort"
Range("D1").Select
ActiveCell.FormulaR1C1 = "Secs"
Range("F1").Select
ActiveCell.FormulaR1C1 = "Items"
Range("H1").Select
ActiveCell.FormulaR1C1 = "Compares"
Range("J1").Select
ActiveCell.FormulaR1C1 = "Updates"
Range("L1").Select
ActiveCell.FormulaR1C1 = "Reads"
Range("N1").Select
ActiveCell.FormulaR1C1 = "Writes"
Range("P1").Select
ActiveCell.FormulaR1C1 = "Depth"
Range("H10").Select
ActiveSheet.ListObjects.Add(xlSrcRange, Range("$B$1:$P$50"), , xlYes).Name = _
"Table1"
Range("Table1[#All]").Select
Sheets.Add
ActiveWorkbook.PivotCaches.Create(SourceType:=xlDatabase, SourceData:= _
"Table1", Version:=6).CreatePivotTable TableDestination:="Sheet2!R3C1", _
TableName:="PivotTable1", DefaultVersion:=6
Sheets("Sheet2").Select
Cells(3, 1).Select
With ActiveSheet.PivotTables("PivotTable1").PivotFields("Sort")
.Orientation = xlColumnField
.Position = 1
End With
With ActiveSheet.PivotTables("PivotTable1").PivotFields("Items")
.Orientation = xlRowField
.Position = 1
End With
ActiveSheet.PivotTables("PivotTable1").AddDataField ActiveSheet.PivotTables( _
"PivotTable1").PivotFields("Secs"), "Sum of Secs", xlSum
ActiveChart.ClearToMatchStyle
ActiveSheet.Shapes.AddChart2(227, xlLine).Select
ActiveChart.SetSourceData Source:=Range("Sheet2!$A$3:$I$12")
Application.Goto Reference:="Macro1"
*/
}
}
</TestCode>