changeset 691:527a7b954f26 feature/quantumTriangles

Merge with default
author Ylva Rydin <ylva.rydin@telia.com>
date Thu, 14 Sep 2017 16:00:36 +0200
parents f235284e2eb1 (current diff) bcddbc2beef4 (diff)
children dd3e9a0f5032
files textTable.m
diffstat 20 files changed, 759 insertions(+), 89 deletions(-) [+]
line wrap: on
line diff
diff -r f235284e2eb1 -r 527a7b954f26 Cell.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Cell.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,88 @@
+% Cell is a reimplementation of matlabs cell array with the benefit that it is subclassable
+% It might be used for giving a typename to a cellarray to increase readability of the code.
+classdef Cell
+    properties
+        data
+    end
+
+    methods
+        function obj = Cell(data)
+            default_arg('data', {});
+            if ~iscell(data)
+                error('Input argument to Cell must be a cell array');
+            end
+
+            obj.data = data;
+        end
+
+        function str = toString(obj)
+            str = sprintf('%s%s', class(obj), toString(obj.data));
+        end
+
+        function s = size(A)
+            s = size(A.data);
+        end
+
+        function l = length(A)
+            l = length(A.data);
+        end
+
+        function ind = end(A,k,n)
+            ind = builtin('end',A.data, k, n);
+        end
+
+        function B = transpose(A)
+            b = A.data.';
+            B = callConstructor(A, b);
+        end
+
+        function B = ctranspose(A)
+            b = A.data';
+            B = callConstructor(A, b);
+        end
+
+        function A = subsasgn(A, S, B)
+            a = subsasgn(A.data, S, B);
+            A = callConstructor(A, a);
+        end
+
+        function B = subsref(A, S)
+            switch S(1).type
+                case '()'
+                    b = subsref(A.data, S(1));
+                    B = callConstructor(A, b);
+                    if length(S) > 1
+                        B = subsref(B,S(2:end));
+                    end
+                case '{}'
+                    B = subsref(A.data, S);
+                case '.'
+                    B = builtin('subsref',A, S);
+                otherwise
+                    error('unreachable');
+            end
+        end
+
+        function C = horzcat(varargin)
+            dataArray = cell(1, length(varargin));
+
+            for i = 1:length(varargin)
+                dataArray{i} = varargin{i}.data;
+            end
+
+            c = horzcat(dataArray{:});
+            C = callConstructor(varargin{1}, c);
+        end
+
+        function C = vertcat(varargin)
+            dataArray = cell(1, length(varargin));
+
+            for i = 1:length(varargin)
+                dataArray{i} = varargin{i}.data;
+            end
+
+            c = vertcat(dataArray{:});
+            C = callConstructor(varargin{1}, c);
+        end
+    end
+end
diff -r f235284e2eb1 -r 527a7b954f26 CellTest.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CellTest.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,264 @@
+function tests = CellTest()
+    tests = functiontests(localfunctions);
+end
+
+function testSize(testCase)
+    cases = {
+        {{}, [0, 0]},
+        {{1}, [1, 1]},
+        {{1, 2}, [1, 2]},
+        {{1; 2}, [2, 1]},
+        {{1, 2; 3, 4}, [2,2]},
+    };
+
+    for i = 1:length(cases)
+        A = Cell(cases{i}{1});
+        expected = cases{i}{2};
+
+        testCase.verifyEqual(size(A),expected);
+    end
+end
+
+function testLength(testCase)
+    cases = {
+        {{}, 0},
+        {{1}, 1},
+        {{1, 2}, 2},
+        {{1; 2}, 2},
+        {{1, 2; 3, 4}, 2},
+    };
+
+    for i = 1:length(cases)
+        A = Cell(cases{i}{1});
+        expected = cases{i}{2};
+
+        testCase.verifyEqual(length(A),expected);
+    end
+end
+
+function testTranspose(testCase)
+    testCase.verifyEqual(Cell({1i, 2}).', Cell({1i; 2}));
+    testCase.verifyEqual(Cell({1i; 2}).', Cell({1i, 2}));
+end
+
+function testCtranspose(testCase)
+    testCase.verifyEqual(Cell({1i, 2})', Cell({1i; 2}));
+    testCase.verifyEqual(Cell({1i; 2})', Cell({1i, 2}));
+end
+
+function testRoundIndexWithProperty(testCase)
+    A = Cell({3,2,1});
+
+    result = A([1,3]).data;
+    testCase.verifyEqual(result, {3, 1});
+end
+
+function testSubAssignmentRound(testCase)
+    cases = {
+        % {
+        %     lArray,
+        %     index,
+        %     rhs,
+        %     expectedResult
+        % },
+        {
+            {},
+            1,
+            {'a'},
+            {'a'},
+        },
+        {
+            {1},
+            1,
+            {'a'},
+            {'a'},
+        },
+        {
+            {1,2,3},
+            2,
+            {'a'},
+            {1,'a',3},
+        },
+        {
+            {1,2,3},
+            2,
+            [],
+            {1,3},
+        },
+    };
+
+    for i = 1:length(cases)
+        lArray         = Cell(cases{i}{1});
+        index          = cases{i}{2};
+        rhs            = cases{i}{3};
+        expectedResult = cases{i}{4};
+
+        lArray(index) = rhs;
+
+        testCase.verifyEqual(lArray.data, expectedResult)
+    end
+end
+
+function testSubAssignmentCurly(testCase)
+    cases = {
+        % {
+        %     lArray,
+        %     index,
+        %     rhs,
+        %     expectedResult
+        % },
+        {
+            {},
+            1,
+            'a',
+            {'a'},
+        },
+        {
+            {1},
+            1,
+            'a',
+            {'a'},
+        },
+        {
+            {1,2,3},
+            2,
+            'a',
+            {1,'a',3},
+        },
+    };
+
+    for i = 1:length(cases)
+        lArray         = Cell(cases{i}{1});
+        index          = cases{i}{2};
+        rhs            = cases{i}{3};
+        expectedResult = cases{i}{4};
+
+        lArray{index} = rhs;
+
+        testCase.verifyEqual(lArray.data, expectedResult)
+    end
+end
+
+function testIndexreferenceRound(testCase)
+    cases = {
+        % {
+        %     array,
+        %     index,
+        %     roundResult
+        % },
+        {
+            {1,2,3},
+            1,
+            {1},
+        },
+        {
+            {1,3,2},
+            2,
+            {3},
+        },
+        {
+            {1,3,2},
+            [1 3],
+            {1, 2},
+        },
+    };
+
+
+    for i = 1:length(cases)
+        array = Cell(cases{i}{1});
+        index = cases{i}{2};
+        expected = cases{i}{3};
+
+        result = array(index);
+
+        testCase.verifyTrue(isa(result, 'Cell'));
+        testCase.verifyEqual(result.data, expected);
+    end
+end
+
+function testEndIndexing(testCase)
+    C = Cell({1,2,3});
+
+    testCase.verifyEqual(C(end), Cell({3}));
+    testCase.verifyEqual(C{end}, 3);
+end
+
+function testColonIndexing(testCase)
+    C = Cell({1, 2, 3});
+    D = Cell({1; 2; 3});
+
+    testCase.verifyEqual(C(:), D);
+    testCase.verifyEqual(D(:), D);
+end
+
+function testIndexreferenceCurly(testCase)
+    cases = {
+        % {
+        %     array,
+        %     index,
+        %     curlyResult
+        % },
+        {
+            {1,2,3},
+            1,
+            1
+        },
+        {
+            {1,3,2},
+            2,
+            3
+        },
+    };
+
+
+    for i = 1:length(cases)
+        array = Cell(cases{i}{1});
+        index = cases{i}{2};
+        expected = cases{i}{3};
+
+        result = array{index};
+
+        testCase.verifyEqual(result, expected);
+    end
+end
+
+function testConcat(testCase)
+    cases = {
+        {{},{}},
+        {{1},{}},
+        {{},{1}},
+        {{1},{2}},
+        {{1, 2},{3, 4}},
+        {{1; 2},{3; 4}},
+    };
+
+    horzCat = {
+        {},
+        {1},
+        {1},
+        {1,2},
+        {1, 2, 3, 4},
+        {1, 3; 2, 4},
+    };
+
+    vertCat = {
+        {},
+        {1},
+        {1},
+        {1; 2},
+        {1, 2; 3, 4},
+        {1; 2; 3; 4},
+    };
+
+    for i = 1:length(cases)
+        A = Cell(cases{i}{1});
+        B = Cell(cases{i}{2});
+
+        C_horz = [A, B];
+        C_vert = [A; B];
+
+        testCase.verifyEqual(C_horz.data, horzCat{i});
+        testCase.verifyEqual(C_vert.data, vertCat{i});
+
+    end
+end
diff -r f235284e2eb1 -r 527a7b954f26 Color.m
--- a/Color.m	Mon Aug 07 13:20:48 2017 +0200
+++ b/Color.m	Thu Sep 14 16:00:36 2017 +0200
@@ -10,6 +10,26 @@
         black     = [0.000 0.000 0.000];
         white     = [1.000 1.000 1.000];
         colors = { Color.blue, Color.red, Color.yellow, Color.green, Color.purple, Color.lightblue, Color.darkred, Color.black, Color.white};
+
+        notabilityYellow     = [100.0   99.0    22.0    ]/100;
+        notabilityOrange     = [97.0    61.0    15.0    ]/100;
+        notabilityRed        = [92.0    22.0    18.0    ]/100;
+
+        notabilityLightGreen = [67.0    99.0    22.0    ]/100;
+        notabilityGreen      = [39.0    72.0    30.0    ]/100;
+        notabilityDarkGreen  = [5.0     45.0    24.0    ]/100;
+
+        notabilityLightBlue  = [8.0     45.0    98.0    ]/100;
+        notabilityBlue       = [11.0    30.0    69.0    ]/100;
+        notabilityDarkBlue   = [7.0     5.0     46.0    ]/100;
+
+        notabilityPink       = [99.0    13.0    82.0    ]/100;
+        notabilityPurple     = [64.0    11.0    77.0    ]/100;
+        notabilityDarkPurple = [38.0    19.0    56.0    ]/100;
+
+        notabilityBrown      = [58.0    40.0    22.0    ]/100;
+        notabilityGrey       = [59.0    59.0    59.0    ]/100;
+        notabilityBlack      = [0.0     0.0     0.0     ]/100;
     end
 
     methods(Static)
diff -r f235284e2eb1 -r 527a7b954f26 TextTable.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TextTable.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,122 @@
+classdef TextTable < handle
+    properties
+        data
+        fmtArray
+        vertDiv
+        horzDiv
+
+        nCols
+        nRows
+    end
+
+    methods
+        function obj = TextTable(data, vertDiv, horzDiv);
+            default_arg('vertDiv', []);
+            default_arg('horzDiv', []);
+
+
+            obj.data = data;
+            obj.vertDiv = vertDiv;
+            obj.horzDiv = horzDiv;
+
+            [obj.nRows, obj.nCols] = size(data);
+            obj.fmtArray = cell(size(data));
+            obj.formatAll('%s');
+
+        end
+
+        function formatAll(obj, fmt)
+            obj.fmtArray(:,:) = {fmt};
+        end
+
+        function formatCell(obj, i, j, fmt)
+            obj.fmtArray{i,j} = fmt;
+        end
+
+        function formatRow(obj, i, fmt)
+            obj.fmtArray(i,:) = {fmt};
+        end
+
+        function formatColumn(obj, j, fmt)
+            obj.fmtArray(:,j) = {fmt};
+        end
+
+        function widths = getWidths(obj)
+            strArray = obj.getStringArray();
+
+            widths = zeros(1,obj.nCols);
+            for j = 1:obj.nCols
+                for i = 1:obj.nRows
+                    widths(j) = max(widths(j), length(strArray{i,j}));
+                end
+            end
+        end
+
+        function str = toString(obj)
+            strArray = obj.getStringArray();
+            widths = obj.getWidths();
+
+            str = '';
+
+            % First horzDiv
+            if ismember(0, obj.horzDiv)
+                str = [str, obj.getHorzDiv(widths)];
+            end
+
+            for i = 1:obj.nRows
+                str = [str, TextTable.rowToString(strArray(i,:), widths, obj.vertDiv, obj.horzDiv)];
+
+                % Interior horzDiv
+                if ismember(i, obj.horzDiv)
+                    str = [str, obj.getHorzDiv(widths)];
+                end
+            end
+        end
+
+        function str = getHorzDiv(obj, widths)
+            str = TextTable.rowToString(cell(1,obj.nCols), widths, obj.vertDiv, obj.horzDiv);
+            str(find(' ' == str)) = '-';
+            str(find('|' == str)) = '+';
+        end
+
+        function strArray = getStringArray(obj)
+            strArray = cell(size(obj.data));
+
+            for i = 1:obj.nRows
+                for j = 1:obj.nCols
+                    strArray{i,j} = sprintf(obj.fmtArray{i,j}, obj.data{i,j});
+                end
+            end
+        end
+    end
+
+    methods (Static)
+        function str = rowToString(strs, widths, vertDiv, horzDiv)
+            % First vertDiv
+            if ismember(0, vertDiv)
+                str = '| ';
+            else
+                str = ' ';
+            end
+
+            % Interior cols
+            for j = 1:length(strs) - 1
+                str = [str, sprintf('%*s ', widths(j), strs{j})];
+
+                % Interior vertDiv
+                if ismember(j, vertDiv)
+                    str = [str, '| '];
+                end
+            end
+
+            % Last col
+            str = [str, sprintf('%*s ', widths(end), strs{end})];
+
+            if ismember(length(strs), vertDiv)
+                str = [str, '|'];
+            end
+
+            str = [str, sprintf('\n')];
+        end
+    end
+end
\ No newline at end of file
diff -r f235284e2eb1 -r 527a7b954f26 assertType.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/assertType.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,5 @@
+function assertType(obj, type)
+    if ~isa(obj, type)
+        error('sbplib:assertType:wrongType', '"%s" must have type "%s", found "%s"', inputname(1), type, class(obj));
+    end
+end
diff -r f235284e2eb1 -r 527a7b954f26 callConstructor.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/callConstructor.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,6 @@
+% Calls the constructor of an object.
+% Might be usefull to call the constructor of a subclass object in the superclass
+function obj = callConstructor(subclassObj, varargin)
+    fun = str2func(class(subclassObj));
+    obj = fun(varargin{:});
+end
diff -r f235284e2eb1 -r 527a7b954f26 checkAllCode.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/checkAllCode.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,6 @@
+% Run matlabs checkcode on all files in current folder including subfolders
+function checkAllCode(d)
+    default_arg('d','')
+    files = findMfiles(d);
+    checkcode(files)
+end
diff -r f235284e2eb1 -r 527a7b954f26 convergenceTable.m
--- a/convergenceTable.m	Mon Aug 07 13:20:48 2017 +0200
+++ b/convergenceTable.m	Thu Sep 14 16:00:36 2017 +0200
@@ -1,15 +1,15 @@
-function convergenceTable(methodName, T, orders, m, e, q, tableType)
+function convergenceTable(caption, orders, m, e, q, tableType)
     default_arg('tableType','plaintext')
 
     switch tableType
         case {'plaintext','text','plain'}
-            plainTextTable(methodName, T, orders, m, e, q);
+            plainTextTable(caption, orders, m, e, q);
         case {'tex', 'latex'}
-            latexTable(methodName, T, orders, m, e, q);
+            latexTable(caption, orders, m, e, q);
     end
 end
 
-function plainTextTable(methodName, T, orders, m, e, q)
+function plainTextTable(caption, orders, m, e, q)
 
 
     eW = 0;
@@ -23,7 +23,7 @@
     mW = findFieldWidth('%d',m);
     orderHeaderWidth = eW + qW + 1;
 
-    fprintf('method: %s\nT: %d\n',methodName, T);
+    fprintf('%s\n',caption);
 
     % Print order headers
     fprintf(' %*s |',mW,'')
@@ -69,7 +69,7 @@
 
 end
 
-function latexTable(methodName, T, orders, m, e, q)
+function latexTable(caption, orders, m, e, q)
 
     nOrders = length(orders);
 
@@ -85,7 +85,7 @@
 
     footer = {
         '\end{tabular}'
-        '\caption{Error $l_2$, and convergence rate, $q$, for SBP operators of orders 4 and 6 at different grid densities $N$. PROBLEM DESCRIPTION.}'
+        ['\caption{' caption '}']
         '\label{table:LABEL}'
         '\end{table}'
     };
diff -r f235284e2eb1 -r 527a7b954f26 findMfiles.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/findMfiles.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,14 @@
+% Find all m files reachable from folder d
+function files = findMfiles(d)
+    contents = what(d);
+
+    files = {};
+    for i = 1:length(contents.m)
+        files{end+1} = fullfile(d, contents.m{i});
+    end
+
+    for i = 1:length(contents.packages)
+        packagePath = fullfile(d,['+' contents.packages{i}]);
+        files = [files, findMfiles(packagePath)];
+    end
+end
\ No newline at end of file
diff -r f235284e2eb1 -r 527a7b954f26 isEquidistant.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/isEquidistant.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,19 @@
+% Tests if consecutive elements of vector v are euidistant
+function b = isEquidistant(v)
+    if length(v) < 2
+        error('sbplib:isEquidistant:inputTooShort', 'Input vector is too short');
+    end
+
+    tol = 1e-8;
+
+    d = v(2:end) - v(1:end-1);
+    err = abs(d - d(1));
+
+    relErr = err./abs(d);
+
+    I_zero = find(d < tol);
+
+    relErr(I_zero) = err(I_zero);
+
+    b = all(relErr < tol);
+end
diff -r f235284e2eb1 -r 527a7b954f26 isEquidistantTest.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/isEquidistantTest.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,31 @@
+function tests = isEquidistantTest()
+    tests = functiontests(localfunctions);
+end
+
+function testTooShortInput(testCase)
+    testCase.verifyError(@()isEquidistant([]), 'sbplib:isEquidistant:inputTooShort')
+end
+
+function testCorrectOutput(testCase)
+    cases = {
+        % {input, expected},
+        {[0,0,0,0,0], true},
+        {[1,1,1,1,1], true},
+        {[1,2,3,4,5], true},
+        {[1,3,4,5], false},
+        {[1,2,3,5], false},
+        {[1,2,4,5], false},
+        {linspace(0,pi, 3), true},
+        {linspace(0,1, 4), true},
+        {linspace(0,1, 4123), true},
+        {linspace(0,sin(1), 123), true},
+    };
+
+    for i = 1:length(cases)
+        input = cases{i}{1};
+        expected = cases{i}{2};
+        result = isEquidistant(input);
+
+        testCase.verifyEqual(result,expected);
+    end
+end
diff -r f235284e2eb1 -r 527a7b954f26 logsurf.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/logsurf.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,19 @@
+function [srfHandle, cbHandle] = logsurf(X,Y,Z, lim)
+    absLogZ = log10(abs(Z));
+    srfHandle = surf(X,Y,absLogZ);
+
+    cbHandle = colorbar();
+    colormap(hot(256));
+    ah = gca();
+    ah.CLim = lim;
+
+    oldTickLabels = cbHandle.TickLabels;
+
+    newTickLabels = {};
+
+    for i = 1:length(oldTickLabels)
+        newTickLabels{i} = sprintf('10^{%s}',oldTickLabels{i});
+    end
+
+    cbHandle.TickLabels = newTickLabels;
+end
\ No newline at end of file
diff -r f235284e2eb1 -r 527a7b954f26 plotConvergenceFit.m
--- a/plotConvergenceFit.m	Mon Aug 07 13:20:48 2017 +0200
+++ b/plotConvergenceFit.m	Thu Sep 14 16:00:36 2017 +0200
@@ -1,16 +1,45 @@
-% Draws a line in a loglog plot with slope `slope` and position `pos`
-% and `width` orders of magnitude wide.
-%    ex: pos = [1e-1 1e-4]
+% Draws a line in a loglog plot with slope `slope` fitted to the data in `x`
+% and `y`. `xlength` scales how much of the interval [x(1) x(end)] is coverd
+% by the line. `offset` is a multiplicative offset to where the line is drawn
+% relative to the data.
+function hand = plotConvergenceFit(slope, x, y, xlength, offset)
+    default_arg('xlength', 0.8)
+    default_arg('offset', 1);
+
+    % Optimise for log(y) = p*log(x) + q
+
+    p = slope;
 
-function hand = plotConvergenceFit(slope, pos, width)
-    x0 = pos(1);
-    y0 = pos(2);
+    logx = log(x);
+    logy = log(y);
+
+    N = length(logx);
+
+    q = 1/N*sum(logy-p*logx);
 
-    x = [x0*10^-(width/2) x0*10^(width/2)];
-    y = x.^slope * x0^-slope * y0;
+    logxlength = xlength * abs(logx(end)-logx(1));
+    logxends = (logx(1)+logx(end))/2 + [-logxlength/2, logxlength/2];
 
-    hand = line(x,y);
+    xends = exp(logxends);
+    yends = exp(q)*xends.^p;
+
+    hand = line(xends, yends);
     hand.Color = Color.black;
     hand.LineStyle = '--';
     hand.LineWidth = 2;
-end
\ No newline at end of file
+end
+
+
+
+% function hand = plotConvergenceFit(slope, pos, width)
+%     x0 = pos(1);
+%     y0 = pos(2);
+
+%     x = [x0*10^-(width/2) x0*10^(width/2)];
+%     y = x.^slope * x0^-slope * y0;
+
+%     hand = line(x,y);
+%     hand.Color = Color.black;
+%     hand.LineStyle = '--';
+%     hand.LineWidth = 2;
+% end
\ No newline at end of file
diff -r f235284e2eb1 -r 527a7b954f26 runtestsAll.m
--- a/runtestsAll.m	Mon Aug 07 13:20:48 2017 +0200
+++ b/runtestsAll.m	Thu Sep 14 16:00:36 2017 +0200
@@ -1,6 +1,8 @@
 function res = runtestsAll()
     l = dir();
 
+    warning('Simplify using the ''what'' command')
+
     dirNames = {l([l.isdir]).name};
 
     packages = {};
diff -r f235284e2eb1 -r 527a7b954f26 savepng.m
--- a/savepng.m	Mon Aug 07 13:20:48 2017 +0200
+++ b/savepng.m	Thu Sep 14 16:00:36 2017 +0200
@@ -11,11 +11,7 @@
     h.PaperUnits = 'centimeters';
     h.PaperPosition(3:4) = h.Position(3:4);
 
-    % Save as a bugged eps file.
     print(h,filename,'-dpng',sprintf('-r%d',dpi));
 
     h.Units = handle_units; % Restore the old units
-
-
-
 end
diff -r f235284e2eb1 -r 527a7b954f26 spyh.m
--- a/spyh.m	Mon Aug 07 13:20:48 2017 +0200
+++ b/spyh.m	Thu Sep 14 16:00:36 2017 +0200
@@ -3,10 +3,14 @@
     [n,m] = size(A);
     [I,J] = find(A);
 
-    h = plot(J,I);
-    h.LineStyle = 'none';
-    h.Marker = '.';
-    h.MarkerSize = 14;
+    if ~isempty(J)
+        h = plot(J,I);
+        h.LineStyle = 'none';
+        h.Marker = '.';
+        h.MarkerSize = 14;
+    else
+        h = [];
+    end
 
     a = gca;
     xlim([0 m+1]);
diff -r f235284e2eb1 -r 527a7b954f26 spzeros.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/spzeros.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,18 @@
+function S = spzeros(varargin)
+    switch length(varargin)
+        case 2
+            S = sparse(varargin{1}, varargin{2});
+        case 1
+            v = varargin{1};
+            switch length(v)
+                case 1
+                    S = sparse(v,v);
+                case 2
+                    S = sparse(v(1), v(2));
+                otherwise
+                    error('Input must be either one integer, two integers or a vector with two integers');
+            end
+        otherwise
+            error('Too many input arguments.');
+    end
+end
diff -r f235284e2eb1 -r 527a7b954f26 surfSym.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/surfSym.m	Thu Sep 14 16:00:36 2017 +0200
@@ -0,0 +1,54 @@
+function h = surfSym(X,Y,Z)
+    if isvector(X) && isvector(Y)
+        [X,Y] = meshgrid(X,Y);
+    end
+
+    V_nodes = [X(:), Y(:), Z(:)];
+
+    X_centers = neighbourMean(X);
+    Y_centers = neighbourMean(Y);
+    Z_centers = neighbourMean(Z);
+    V_centers = [X_centers(:), Y_centers(:), Z_centers(:)];
+
+
+    N = prod(size(X));
+    nodeIndecies = reshape(1:N, size(X));
+    centerIndecies = reshape(N+(1:prod(size(X)-[1,1])), size(X) - [1,1]);
+
+    % figure()
+    % h = line(V_nodes(:,1),V_nodes(:,2),V_nodes(:,3));
+    % h.LineStyle = 'none';
+    % h.Marker = '.';
+    % h = line(V_centers(:,1),V_centers(:,2),V_centers(:,3));
+    % h.LineStyle = 'none';
+    % h.Marker = '.';
+    % h.Color = Color.red;
+    % axis equal
+
+
+    I_0 = nodeIndecies(1:end-1, 1:end-1);
+    I_1 = nodeIndecies(2:end, 1:end-1);
+    I_2 = nodeIndecies(2:end, 2:end);
+    I_3 = nodeIndecies(1:end-1, 2:end);
+    I_C = centerIndecies;
+
+    S.Vertices = [
+        V_nodes;
+        V_centers;
+    ];
+
+    S.Faces = [
+        I_0(:), I_1(:), I_C(:);
+        I_1(:), I_2(:), I_C(:);
+        I_2(:), I_3(:), I_C(:);
+        I_3(:), I_0(:), I_C(:);
+    ];
+
+    % figure()
+    h = patch(S, 'FaceVertexCData',  S.Vertices(:,3),'FaceColor','flat');
+end
+
+% Calculate the mean of four neighbours around a patch
+function M = neighbourMean(A)
+    M = (A(1:end-1, 1:end-1) + A(2:end, 1:end-1) + A(1:end-1, 2:end) + A(2:end, 2:end))/4;
+end
diff -r f235284e2eb1 -r 527a7b954f26 textTable.m
--- a/textTable.m	Mon Aug 07 13:20:48 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-
-% data           -- cell array of numbers
-% leftColstrings -- cell array of strings, for left column
-% topRowStrings  -- cell array of strings, for top row
-% dataFormat     -- (optional) format specifier, e.g. '%.2f' 
-function textTable(data, leftColStrings, topRowStrings, dataFormat)
-
-    default_arg('dataFormat','%.2f')
-    
-    nRows = length(leftColStrings);
-    nCols = length(topRowStrings);
-    [m,n] = size(data);
-    
-    if(m ~= nRows || n ~=nCols)
-        error('Data dimensions must match labels');
-    end
-
-    % Find column widths
-    headerLength = 0;
-    for i = 1:nCols
-        headerLength = max(headerLength, length(topRowStrings{i} ));
-    end
- 
-    dataLength = 0;
-    for i = 1:nRows
-        for j = 1:nCols
-            temp = length(sprintf(dataFormat, data{i,j}));
-            dataLength = max(dataLength, temp);
-        end
-    end
-    dataLength = length(sprintf(dataFormat, data{1,1}));
-    
-    colWidth = max(headerLength,dataLength);
-
-    % Print headers
-    fprintf(' %*s |',colWidth,'')
-    for i = 1:nCols
-        fprintf(' %-*s |', colWidth, topRowStrings{i});
-    end
-    fprintf('\n');
-
-    % Print divider
-    m_dev = repmat('-',1,colWidth);
-    column_dev = repmat('-',1,colWidth);
-    fprintf('-%s-+',m_dev);
-    for i = 1:nCols
-        fprintf('-%s-+', column_dev);
-    end
-    fprintf('\n');
-    
-
-    % Print each row
-    dataFormat = ['%*' dataFormat(2:end)];
-    for i = 1:nRows
-        fprintf(' %*s |',colWidth,leftColStrings{i});
-        for j = 1:nCols
-            fprintf([' ' dataFormat ' |'], colWidth, data{i,j});
-        end
-        fprintf('\n');
-    end
-
-    fprintf('\n');
-
-end
\ No newline at end of file
diff -r f235284e2eb1 -r 527a7b954f26 toString.m
--- a/toString.m	Mon Aug 07 13:20:48 2017 +0200
+++ b/toString.m	Thu Sep 14 16:00:36 2017 +0200
@@ -51,6 +51,14 @@
 end
 
 function str = struct2string(s)
+    if isscalar(s)
+        str = structScalar2string(s);
+    else
+        str = structArray2string(s);
+    end
+end
+
+function str = structScalar2string(s)
     fn = fieldnames(s);
 
     if length(fn) == 0
@@ -68,3 +76,32 @@
     str = [str sprintf('%s: %s}',fn{end}, value2string(value))];
 end
 
+function str = structArray2string(s)
+    fn = fieldnames(s);
+
+    if length(fn) == 0
+        str = '{}';
+        return
+    end
+
+    stringArray = cell(length(s)+1, length(fn)+1);
+
+    stringArray(1,2:end) = fn;
+
+    for i = 1:length(s)
+        stringArray{i+1,1} = i;
+        for j = 1:length(fn)
+            valueStr = value2string(s(i).(fn{j}));
+            stringArray{i+1,j+1} = valueStr;
+        end
+    end
+
+    tt = TextTable(stringArray);
+    tt.fmtArray(2:end, 1) = {'%d'};
+    tt.vertDiv = [1];
+    tt.horzDiv = [1];
+    str = tt.toString();
+end
+
+
+