From 14bab8de4caea55683c76ad6f0722f17638f92b4 Mon Sep 17 00:00:00 2001
From: Ferdinand Fischer <ferdinand.fischer@fau.de>
Date: Thu, 23 May 2019 17:59:15 +0200
Subject: [PATCH] Some changes: *) Allow to set empty quantity.Discrete objects
 with certain size *) added the isConstant property for the quantity.Discrete
 *) Implemented a cast to the quantity.Operator object *) Implemented a cast
 of empty quantity.Discretes to quantity.Symbolics *) Fixed bug in "on()" for
 constant quantities *) Fixed some functions to work with empty quantities *)
 Added a settings object to use global settings *) plot function returns now
 an array of the figure handles *) Re-implementation of the quantity.Operator
 object

---
 +quantity/BasicVariable.m           | 394 +++++++++++++-------------
 +quantity/Discrete.m                | 130 +++++++--
 +quantity/Operator.m                | 412 ++++++++++++++++++++--------
 +quantity/Settings.m                |  27 ++
 +quantity/Symbolic.m                |  43 +--
 +unittests/+quantity/testDiscrete.m |  11 +
 +unittests/+quantity/testOperator.m | 275 ++++++++++---------
 +unittests/+quantity/testSymbolic.m |   6 +-
 8 files changed, 818 insertions(+), 480 deletions(-)
 create mode 100644 +quantity/Settings.m

diff --git a/+quantity/BasicVariable.m b/+quantity/BasicVariable.m
index 30cb439..8069312 100644
--- a/+quantity/BasicVariable.m
+++ b/+quantity/BasicVariable.m
@@ -1,199 +1,199 @@
 classdef BasicVariable < quantity.Function
-    %GEVREYFUNCTION class for desription of basic variables
-    %   The basic variable can be of the form 
-    %       g(t) = [phi_1(t); phi(2); ... ]
-    %
-    properties
-        % The constant gain of the Gevrey-function
-        K double = 1;
-        % Shifts the evaluation of the derivative of the Gevrey-function.
-        % Can be used if the series, computed using the gevrey function,
-        % should not start at the 0-th derivative.
-        diffShift double {mustBeNonnegative, mustBeInteger};
-        % Offset to raise the whole Gevrey-function g_ = g + offset.
-        offset double; 
-        % Number of discretization points for the time variable at which
-        % the Gevrey-function is evaluated.
-        N_t double;                         
-        % Number of derivatives that need to be considered for this gevrey
-        % function
-        N_diff double;
-        % Length of the time interval the Gevrey-function is defined on.
-        T double = -1;                 
-        % Gevrey-order
-        order double = -1;   
-    end
-    
-    properties (Dependent)
-        sigma;
-        dt; 
-    end
-    
-    methods
-        %% CONSTRUCTOR
-        function obj = BasicVariable(valueContinuous, varargin)
-            
-            parentVarargin = {};
-            
-            if nargin > 0 
-
-                % make default grid:
-                preParser = misc.Parser();
-                preParser.addParameter('T', 1);
-                preParser.addParameter('N_t', 101);
-                preParser.addParameter('dt', []);
-                preParser.addParameter('N_diff', 1);
-                preParser.parse(varargin{:});
-
-                if ~isempty(preParser.Results.dt)
-                    N_t = quantity.BasicVariable.setDt(preParser.Results.T, preParser.Results.dt);
-                    preResults.T = preParser.Results.T;
-                    preResults.dt = preParser.Results.dt;
-                    preResults.N_diff = preParser.Results.N_diff;
-                else
-                    N_t = preParser.Results.N_t;
-
-                    preResults.T = preParser.Results.T;
-                    preResults.N_t = preParser.Results.N_t;
-                    preResults.N_diff = preParser.Results.N_diff;                
-                end
-
-                grid = {linspace(0, preParser.Results.T, N_t)'}; 
-                varargin = [varargin{:}, 'grid', {grid}];
-                parentVarargin = {valueContinuous, varargin{:}, 'gridName', {'t'}};
-            end
-            
-            obj@quantity.Function(parentVarargin{:});
-            
-            if nargin > 0
-                % first parser
-                p1 = misc.Parser();
-                p1.addParameter('K', 1);
-                p1.addParameter('diffShift', 0);
-                p1.addParameter('offset', 0);
-                p1.addParameter('order', 1.5);
-                p1.parse(varargin{:});
-                for parameter = fieldnames(p1.Results)'
-                  obj.(parameter{1}) = p1.Results.(parameter{1}); 
-                end
-                for parameter = fieldnames(preResults)'
-                  obj.(parameter{1}) = preParser.Results.(parameter{1}); 
-                end
-            end
-        end
-               
-        function n = nargin(obj)
-           n = 1; 
-        end
-                
-        function D = diff(obj, K)
-            if nargin == 1
-                i = 1;
-            end
-            
-            for i = 1:numel(K)
-                % create a new object for the derivative of obj:
-                D(:,i) = obj.copy();
-                [D.name] = deal(['d/dt (' num2str(K(i)) ') ']);
-                [D.valueDiscrete] = deal([]);
-                for l = 1:numel(obj)
-                   D(l,i).diffShift = D(l, 1).diffShift + K(i);
-                end
-            end
-        end
-                
-        %% PROPERTIES
-        function dt = get.dt(obj)
-           dt = obj.T / obj.N_t; 
-        end
-        function set.dt(obj, dt)
-           obj.N_t = quantity.BasicVariable.setDt(obj.T, dt);
-        end
-                
-        function obj = set.diffShift(obj, n)
-            obj.diffShift = n;
-            obj.valueDiscrete = [];
-        end
-        
-        function s = get.sigma(obj)
-            s = 1 / ( obj.order - 1);
-        end
-        function obj = set.sigma(obj, s)
-           obj.order = 1 + 1/s; 
-        end  
-        function obj = set.order(obj, order)
-           obj.order = obj.set_order(order);
-        end
-        function order = get.order(obj)
-            order = obj.get_order(obj.order);
-        end
-        function obj = set.N_t(obj, value)
-           obj.N_t = obj.set_N_t(value);
-        end
-        function N = get.N_t(obj)
-           N = obj.get_N_t(obj.N_t); 
-        end        
-        function obj = set.T(obj, value)
-            obj.T = obj.set_T(value);
-        end
-        function T = get.T(obj)
-           T = obj.get_T(obj.T); 
-        end 
-        
-        function b = copy_K(obj, K)
-            b = obj.copy();
-            b.valueDiscrete = K / obj.K * obj.valueDiscrete();
-            b.K = K;
-        end
-        
-    end
-   
-    methods (Access = protected)
-        
-        function f = getValueContinuous(obj, f)
-            f = @(z) obj.K * f(z, obj.diffShift, obj.T, obj.sigma);
-        end
-        
-        function order = set_order(obj, order)
-        end 
-        function order = get_order(obj, order)
-        end        
-        function T = get_T(obj, T)
-        end
-        function T = set_T(obj, T)
-        end
-        function n = get_n(obj, n)
-        end
-        function n = set_n(obj, n)
-        end
-        function N = get_N_t(obj, N)
-        end
-        function N = set_N_t(obj, N)
-        end
-    end    
-    
-    methods (Access = protected, Static)
-       function Nt = setDt(T, dt)
-           t = 0:dt:T;
-           Nt = length(t);
-           if ~numeric.near(t(end), T)
-               warning(['Sampling with dt=' num2str(dt) ' leads to t(end)= ' num2str(t(end))]);
-           end
-        end 
-    end
-    
-    methods (Static)
-       
-        
-        function b = makeBasicVariable(b0, K)
-            
-            for k = 1:numel(K)
-                for l = 1:size(b0, 2)
-                    b(k, l) = b0(l).copy_K(K(k));
-                end
-            end
-            
-        end
-        
-    end
+	%BasicVariable class for desription of basic variables
+	%   The basic variable can be of the form
+	%       g(t) = [phi_1(t); phi(2); ... ]
+	%
+	properties
+		% The constant gain of the Gevrey-function
+		K double = 1;
+		% Shifts the evaluation of the derivative of the Gevrey-function.
+		% Can be used if the series, computed using the gevrey function,
+		% should not start at the 0-th derivative.
+		diffShift double {mustBeNonnegative, mustBeInteger};
+		% Offset to raise the whole Gevrey-function g_ = g + offset.
+		offset double;
+		% Number of discretization points for the time variable at which
+		% the Gevrey-function is evaluated.
+		N_t double;
+		% Number of derivatives that need to be considered for this gevrey
+		% function
+		N_diff double;
+		% Length of the time interval the Gevrey-function is defined on.
+		T double = -1;
+		% Gevrey-order
+		order double = -1;
+	end
+	
+	properties (Dependent)
+		sigma;
+		dt;
+	end
+	
+	methods
+		%% CONSTRUCTOR
+		function obj = BasicVariable(valueContinuous, varargin)
+			
+			parentVarargin = {};
+			
+			if nargin > 0
+				
+				% make default grid:
+				preParser = misc.Parser();
+				preParser.addParameter('T', 1);
+				preParser.addParameter('N_t', 101);
+				preParser.addParameter('dt', []);
+				preParser.addParameter('N_diff', 1);
+				preParser.parse(varargin{:});
+				
+				if ~isempty(preParser.Results.dt)
+					N_t = quantity.BasicVariable.setDt(preParser.Results.T, preParser.Results.dt);
+					preResults.T = preParser.Results.T;
+					preResults.dt = preParser.Results.dt;
+					preResults.N_diff = preParser.Results.N_diff;
+				else
+					N_t = preParser.Results.N_t;
+					
+					preResults.T = preParser.Results.T;
+					preResults.N_t = preParser.Results.N_t;
+					preResults.N_diff = preParser.Results.N_diff;
+				end
+				
+				grid = {linspace(0, preParser.Results.T, N_t)'};
+				varargin = [varargin{:}, 'grid', {grid}];
+				parentVarargin = {valueContinuous, varargin{:}, 'gridName', {'t'}};
+			end
+			
+			obj@quantity.Function(parentVarargin{:});
+			
+			if nargin > 0
+				% first parser
+				p1 = misc.Parser();
+				p1.addParameter('K', 1);
+				p1.addParameter('diffShift', 0);
+				p1.addParameter('offset', 0);
+				p1.addParameter('order', 1.5);
+				p1.parse(varargin{:});
+				for parameter = fieldnames(p1.Results)'
+					obj.(parameter{1}) = p1.Results.(parameter{1});
+				end
+				for parameter = fieldnames(preResults)'
+					obj.(parameter{1}) = preParser.Results.(parameter{1});
+				end
+			end
+		end
+		
+		function n = nargin(obj)
+			n = 1;
+		end
+		
+		function D = diff(obj, K)
+			if nargin == 1
+				i = 1;
+			end
+			
+			for i = 1:numel(K)
+				% create a new object for the derivative of obj:
+				D(:,i) = obj.copy();
+				[D.name] = deal(['d/dt (' num2str(K(i)) ') ']);
+				[D.valueDiscrete] = deal([]);
+				for l = 1:numel(obj)
+					D(l,i).diffShift = D(l, 1).diffShift + K(i);
+				end
+			end
+		end
+		
+		%% PROPERTIES
+		function dt = get.dt(obj)
+			dt = obj.T / obj.N_t;
+		end
+		function set.dt(obj, dt)
+			obj.N_t = quantity.BasicVariable.setDt(obj.T, dt);
+		end
+		
+		function obj = set.diffShift(obj, n)
+			obj.diffShift = n;
+			obj.valueDiscrete = [];
+		end
+		
+		function s = get.sigma(obj)
+			s = 1 / ( obj.order - 1);
+		end
+		function obj = set.sigma(obj, s)
+			obj.order = 1 + 1/s;
+		end
+		function obj = set.order(obj, order)
+			obj.order = obj.set_order(order);
+		end
+		function order = get.order(obj)
+			order = obj.get_order(obj.order);
+		end
+		function obj = set.N_t(obj, value)
+			obj.N_t = obj.set_N_t(value);
+		end
+		function N = get.N_t(obj)
+			N = obj.get_N_t(obj.N_t);
+		end
+		function obj = set.T(obj, value)
+			obj.T = obj.set_T(value);
+		end
+		function T = get.T(obj)
+			T = obj.get_T(obj.T);
+		end
+		
+		function b = copy_K(obj, K)
+			b = obj.copy();
+			b.valueDiscrete = K / obj.K * obj.valueDiscrete();
+			b.K = K;
+		end
+		
+	end
+	
+	methods (Access = protected)
+		
+		function f = getValueContinuous(obj, f)
+			f = @(z) obj.K * f(z, obj.diffShift, obj.T, obj.sigma);
+		end
+		
+		function order = set_order(obj, order)
+		end
+		function order = get_order(obj, order)
+		end
+		function T = get_T(obj, T)
+		end
+		function T = set_T(obj, T)
+		end
+		function n = get_n(obj, n)
+		end
+		function n = set_n(obj, n)
+		end
+		function N = get_N_t(obj, N)
+		end
+		function N = set_N_t(obj, N)
+		end
+	end
+	
+	methods (Access = protected, Static)
+		function Nt = setDt(T, dt)
+			t = 0:dt:T;
+			Nt = length(t);
+			if ~numeric.near(t(end), T)
+				warning(['Sampling with dt=' num2str(dt) ' leads to t(end)= ' num2str(t(end))]);
+			end
+		end
+	end
+	
+	methods (Static)
+		
+		
+		function b = makeBasicVariable(b0, K)
+			
+			for k = 1:numel(K)
+				for l = 1:size(b0, 2)
+					b(k, l) = b0(l).copy_K(K(k));
+				end
+			end
+			
+		end
+		
+	end
 end
\ No newline at end of file
diff --git a/+quantity/Discrete.m b/+quantity/Discrete.m
index 9973f3d..9e12a59 100644
--- a/+quantity/Discrete.m
+++ b/+quantity/Discrete.m
@@ -59,7 +59,13 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 				valueOriginalSize = size(valueOriginal);
 				S = num2cell(valueOriginalSize);
 				if any(valueOriginalSize == 0)
-					obj = quantity.Discrete.empty(S{:});
+					% If the size is specified in the arguements, it should
+					% be chosen instead of the default size from the
+					% valueOriginal.
+					myParser = misc.Parser();
+					myParser.addParameter('size', [S{:}]);
+					myParser.parse(varargin{:});
+					obj = quantity.Discrete.empty(myParser.Results.size);
 					return;
 				end
 				
@@ -151,7 +157,12 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 
 		%---------------------------
 		% --- getter and setters ---
-		%---------------------------
+ 		%---------------------------
+		function i = isConstant(obj)
+			% the quantity is interpreted as constant if it has no grid or
+			% it has a grid that is only one point.
+			i = isempty(obj.gridSize) || prod(obj.gridSize) == 1;
+		end
 		function doNotCopy = get.doNotCopy(obj)
 			doNotCopy = obj.doNotCopyPropertiesName();
 		end
@@ -178,7 +189,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			end
 			obj.gridName = name;
 		end
-		
+	
 		function set.grid(obj, grid)
 			if ~iscell(grid)
 				grid = {grid};
@@ -203,6 +214,21 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 		function d = double(obj)
 			d = obj.on();
 		end
+		
+		function o = quantity.Operator(obj)
+			for k = 1:size(obj, 3)
+				A{k} = obj(:,:,k);
+			end
+			o = quantity.Operator(A, 'grid', obj(1).grid);
+		end
+		
+		function o = quantity.Symbolic(obj)
+			if isempty(obj)
+				o = quantity.Symbolic.empty(size(obj));
+			else
+				error('Not yet implemented')
+			end
+		end
 	end
 	
 	methods (Access = public)
@@ -241,6 +267,9 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 					tempInterpolant = numeric.interpolant(...
 						[obj(1).grid, indexGrid{:}], value);
 					value = tempInterpolant.evaluate(myGrid{:}, indexGrid{:});
+				elseif obj.isConstant
+					value = repmat(value, [cellfun(@(v) numel(v), myGrid), ones(1, length(size(obj)))]);
+					gridPermuteIdx = 1:numel(myGrid);
 				end
 				value = permute(reshape(value, [cellfun(@(v) numel(v), myGrid), size(obj)]), ...
 					[gridPermuteIdx, numel(gridPermuteIdx)+(1:ndims(obj))]);
@@ -252,8 +281,13 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			% further quantites are inputs via varargin, it is verified if
 			% that quantity has same grid and gridName as quantity a
 			% as well.
-			referenceGridName = a(1).gridName;
-			referenceGrid= a(1).grid;
+			if isempty(a)
+				referenceGridName = '';
+				referenceGrid = {};
+			else
+				referenceGridName = a(1).gridName;
+				referenceGrid= a(1).grid;
+			end
 			for it = 1 : numel(a)
 				assert(isequal(referenceGridName, a(it).gridName), ...
 					'All elements of a quantity must have same gridNames');
@@ -279,10 +313,21 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 		function [referenceGrid, referenceGridName] = getFinestGrid(a, varargin)
 			% find the finest grid of all input quantities by comparing
 			% gridSize for each iteratively.
-			referenceGridName = a(1).gridName;
-			referenceGrid = a(1).grid;
-			referenceGridSize = a(1).gridSize(referenceGridName);
+			
+			if isempty(a)
+				referenceGridName = '';
+				referenceGrid = {};
+				referenceGridSize = [];
+			else
+				referenceGridName = a(1).gridName;
+				referenceGrid = a(1).grid;
+				referenceGridSize = a(1).gridSize(referenceGridName);				
+			end
+
 			for it = 1 : numel(varargin)
+				if isempty(varargin{it})
+					continue;
+				end
 				assert(numel(referenceGridName) == numel(varargin{it}(1).gridName), ...
 					['For getFinestGrid, the gridName of all objects must be equal', ...
 					'. Maybe gridJoin() does what you want?']);
@@ -416,7 +461,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			%   Copyright 1984-2005 The MathWorks, Inc.
 			%   Built-in function.
 			if nargin == 1
-				objCell = {a};
+				objCell = {a};			
 			else
 				objCell = [{a}, varargin(:)'];
 				
@@ -599,6 +644,9 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			if nargin == 1 || isempty(gridName2Replace)
 				% if gridName2Replace is empty, then nothing must be done.
 				solution = obj;
+			elseif isempty(obj);
+				% if the object is empty, nothing must be done.
+				solution = obj;
 			else
 				% input checks
 				assert(nargin == 3, ['Wrong number of input arguments. ', ...
@@ -776,21 +824,23 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			end
 		end
 		
-		function h = plot(obj, varargin)
-			
+		function H = plot(obj, varargin)
+			H = [];
 			p = misc.Parser();
 			p.addParameter('fig', []);
-			p.addParameter('dock', false);
+			p.addParameter('dock', quantity.Settings.instance().dockThePlot);
 			p.parse(varargin{:});
-			%misc.struct2ws(p.Results);
+			additionalPlotOptions = misc.struct2namevaluepair(p.Unmatched);
+			
 			fig = p.Results.fig;
 			dock = p.Results.dock;
 			for figureIdx = 1:size(obj, 3)
 				if isempty(p.Results.fig)
 					h = figure();
 				else
-				h = figure(fig + figureIdx - 1);
+					h = figure(fig + figureIdx - 1);
 				end
+				H = [H, h];
 				
 				if dock
 					set(h, 'WindowStyle', 'Docked');
@@ -805,6 +855,11 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 				i = 1: numel(obj(:,:,figureIdx));
 				i = reshape(i, size(obj, 2), size(obj, 1))';
 				
+				if obj.gridSize == 1
+					additionalPlotOptions = [additionalPlotOptions(:)', ...
+						{'x'}];
+				end
+				
 				for rowIdx = subplotRowIdx
 					for columnIdx = subpotColumnIdx
 						subplot(size(obj, 1), size(obj, 2), i(rowIdx, columnIdx));
@@ -813,11 +868,13 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 							
 							plot(...
 								obj(rowIdx, columnIdx, figureIdx).grid{1}(:), ...
-								obj(rowIdx, columnIdx, figureIdx).valueDiscrete );
+								obj(rowIdx, columnIdx, figureIdx).valueDiscrete, ...
+								additionalPlotOptions{:});
 						elseif obj.nargin() == 2
 							misc.isurf(obj(rowIdx, columnIdx, figureIdx).grid{1}(:), ...
 								obj(rowIdx, columnIdx, figureIdx).grid{2}(:), ...
-								obj(rowIdx, columnIdx, figureIdx).valueDiscrete);
+								obj(rowIdx, columnIdx, figureIdx).valueDiscrete, ...
+								additionalPlotOptions{:});
 							ylabel(labelHelper(2), 'Interpreter','latex');
 						else
 							error('number inputs not supported');
@@ -854,7 +911,6 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 					myText = strrep(myText, 'gamma', '\gamma');
 					myText = strrep(myText, 'Delta', '\Delta');
 					myText = strrep(myText, 'delta', '\delta');
-					myText = strrep(myText, 'int', '\int');
 					if ~contains(myText, '\zeta') && ~contains(myText, '\Zeta')
 						myText = strrep(myText, 'eta', '\eta');
 					end
@@ -900,6 +956,9 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			% change the grid of the obj quantity. The order of grid and
 			% gridName in the obj properties remains unchanged, only the
 			% data points are exchanged.
+			if isempty(obj)
+				return;
+			end
 			gridIndexNew = obj(1).gridIndex(gridNameNew);
 			myGrid = cell(1, numel(obj(1).grid));
 			myGridName = cell(1, numel(obj(1).grid));
@@ -966,6 +1025,18 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			
 			ftr = false; % flag for the special case a == const.
 			
+			if isempty(b) || isempty(a)
+				% TODO: actually this only holds for multiplication of
+				% matrices. If higher dimensional arrays are multiplied
+				% this can lead to wrong results.
+				sa = size(a);
+				sb = size(b);
+				
+				P = quantity.Discrete.empty([sa(1:end-1) sb(2:end)]);
+				return
+			end
+			
+			
 			if isa(a, 'double')
 				if numel(a) == 1
 					% simple multiplication in scalar case
@@ -979,7 +1050,11 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			end
 			
 			if a.nargin() == 0
-				% TODO: Whats the use of this?
+				% If the first argument a is constant value, then bad
+				% things will happen. To avoid this, we calculate
+				%	a * b = (b' * a')'
+				% instead. Thus we have to exchange both values and set the
+				% flag, that we have to transpose the result in the end.
 				a_ = a;
 				a = b';
 				b = a_';
@@ -1362,6 +1437,11 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			%% PLUS is the sum of two quantities.
 			assert(isequal(size(A), size(B)), 'plus() not supports mismatching sizes')
 			
+			if isempty(A) || isempty(B)
+				C = quantity.Discrete.empty(size(A));
+				return
+			end
+			
 			% combine both domains with finest grid
 			[gridJoined, gridNameJoined] = gridJoin(A, B);
 			gridJoinedLength = cellfun(@(v) numel(v), gridJoined);
@@ -1413,7 +1493,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			
 			P = A.copy();
 			
-			if ~isa(B, 'quantity.quantiy')
+			if ~isa(B, 'quantity.Discrete')
 				B = quantity.Discrete(B);
 			end
 			supremum = nan(size(A));
@@ -1431,6 +1511,11 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			maxValue = reshape(max(obj.on(), [], 1:obj(1).nargin), [size(obj)]);			
 		end
 		
+		function maxValue = MAX(obj)
+			maxValue = obj.max();
+			maxValue = max(maxValue(:));
+		end
+		
 		function minValue = min(obj)
 			% max returns the minimum value of all elements of A over all
 			% variables as a double array.
@@ -1465,6 +1550,11 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 			%	variable, this one is chosen. The 'fixedLowerBound' option
 			%	defines if a = 0 or a = s.
 			
+			if isempty(x)
+				f = quantity.Discrete.empty(size(K, 1), 0);
+				return
+			end
+			
 			ip = misc.Parser();
 			ip.addParameter('variable', x(1).gridName{1});
 			ip.addParameter('fixedLowerBound', true);
@@ -1673,7 +1763,7 @@ classdef  (InferiorClasses = {?quantity.Symbolic, ?quantity.Operator}) Discrete
 		function [idx, permuteGrid] = computePermutationVectors(a, b)
 			% Computes the required permutation vectors to use
 			% misc.multArray for multiplication of matrices
-			
+						
 			uA = unique(a(1).gridName);
 			assert(numel(uA) == numel(a(1).gridName), 'Gridnames have to be unique!');
 			uB = unique(b(1).gridName);
diff --git a/+quantity/Operator.m b/+quantity/Operator.m
index ceae683..976dafa 100644
--- a/+quantity/Operator.m
+++ b/+quantity/Operator.m
@@ -1,127 +1,305 @@
-classdef Operator < quantity.Symbolic
-	%OPERATOR system operator of the form A[x(t)](z)
-	%	This class describes system opertors of a system of first order
-	%	PDE wrt. space of the form
-	%		dz x(z,t) = A[x(t)](z)
-	%	where A can be written as
-	%		A[x(t)](z) = A0(z) x(z,t) + A1(z) dz x(z,t) + A2(z) dz^2 x(z,t)
-	%			... + Ak dz^k x(z,t)
-    
-    properties
-       operatorVar;
-    end
-	properties (SetAccess = protected)
+classdef Operator < handle
+%OPERATOR define operator matrices
+% This class describes operators of the form 
+%		A[x(t)](z) = A0(z) x(z,t) + A1(z) dz x(z,t) + A2(z) dz^2 x(z,t)
+%			... + Ak dz^k x(z,t)
+	
+	properties
+		% The variable that is used for the algebraic representation of the
+		% operator
+		s;
+		% continuous variable
+		variable;
+		% The values of the operator matrices in form of a
+		% quantity.Discrete object
+		coefficient quantity.Discrete;
+		% The grid, on which the operator is defined
+		grid;
+	end
+	properties (Hidden, SetAccess = protected)
 		% Fundamental matrix of this operator, that is the solution of
 		%	dz phi(z,z0) = A(z,s) phi(z,z0)
-		%	   phi(z0, z0) = I
-		%	   A(z,s) = sum_k Ak(z) * s^k
+		%	   phi(z0, z0) = I A(z,s) = sum_k Ak(z) * s^k
 		Phi0;
 	end
-    
-    methods
-        function obj = Operator(A, varargin)
-            
-            parentVarargin = {};
-            if nargin > 0
-                parentVarargin = {A, varargin{:}};
-            end
-            
-            %OPERATOR 
-			obj@quantity.Symbolic(parentVarargin{:});           
-            
-            if nargin > 0
-                p = misc.Parser();
-                p.addParameter('operatorVar', sym('s'));
-                p.parse(varargin{:});
-                misc.struct2ws(p.Results);
+	
+	methods
+		function obj = Operator(A, varargin)
+			
+			if nargin > 0
+				
+				gridParser = misc.Parser();
+				gridParser.addOptional('grid', linspace(0,1)');
+				gridParser.addOptional('variable', sym('z', 'real'));
+				gridParser.addOptional('s', sym('s'));
+				gridParser.parse(varargin{:});
+				
+				grid = gridParser.Results.grid;
+				variable = gridParser.Results.variable;
+				s = gridParser.Results.s;		
+
+				%TODO assert that A has entries with the same size
+				for k = 1 : numel(A)
+					obj(k).coefficient = A{k}; %#ok<AGROW>
+				end
+				
+				[obj.grid] = deal(grid);
+				[obj.variable] = deal(variable);
+				[obj.s] = deal(s);
+			end
+		end
+		
+		function set.grid(obj, grid)
+			if ~iscell(grid)
+				grid = {grid};
+			end
+			
+			isV = cellfun(@(v) (sum(size(v)>1) == 1) || (numel(v) == 1), grid); % also allow 1x1x1x41 grids
+			assert(all(isV(:)), 'Please use vectors for the grid entries!');
+			
+			[obj.grid] = deal(grid);
+		end		
+		
+	end
+	
+ 	methods (Access = public)
+		
+		function c = copy(obj, W)
+			c = quantity.Operator(W, 'grid', obj(1).grid, 'variable', obj(1).variable, 's', obj(1).s);
+		end
+		
+		function m = M(obj) % TODO: This function should be protected actually.
+			m = builtin('cat', 3, obj.coefficient);
+		end
+
+		function h = plot(obj, varargin)
+			h = obj.M.plot(varargin{:});
+		end
+		
+		function S = powerSeries(obj)
+			S = misc.powerSeries2Sum(obj.M.sym, obj(1).s);
+		end
+ 		
+		function A = ctranspose(obj)
+			for k = 1:length(obj)
+				M{k} = (-1)^(k-1) * obj(k).coefficient'; %#ok<AGROW>
+			end
+			A = obj.copy(M);
+		end
+		
+		function [Phi, Psi] = stateTransitionByOde(A, varargin)
+			% STATETRANSTITIONBYODE 
+			% [Phi, Psi] = stateTransitionByOde(A, varargin) computes the
+			% state-transition matrix of the boundary-value-problem 
+			%	dz x(z,s) = A(z,s) x(xi,s) + B(z,s) * u(s)
+			% so that Phi is the solution of
+			%	dz Phi(z,xi,s) = A(z,s) Phi(z,xi,s)
+			% and Psi is given by
+			%	Psi(z,xi,s) = int_xi_z Phi(z,zeta,s) B(zeta,s) d zeta
+			% if B(z,s) is defined as quantity.Operator object by
+			% name-value-pair.
+			% To solve this problem the recursion formula from [1] 
+			%	dz w_k(z) = sum_j=0 ^d A_j(z) * w_{k-j}(z) + B_k nu
+			% is used. This recursion of odes can be reformulated to a
+			% large ode:
+			%
+			%	dz w_0 = A_0 w_0 + B_0 nu
+			%	dz w_1 = A_1 w_0 + A_0 w_0 + B_1 nu
+			%	dz w_2 = A_2 w_0 + A_1 w_1 + A_0 w_2 + B_2 nu
+			%	...
+			%	
+			%	[dz w_0]   [ A_0  0   0   0  ... ][w_0]   [B_0]
+			%	[dz w_1]   [ A_1 A_0  0   0  ... ][w_1]   [B_1]
+			%	[dz w_2] = [ A_2 A_1 A_0  0  ... ][w_2] + [B_2] nu
+			%	[ ...  ]   [  ...                [[...]   [...]
+			%
+			% By some sorting of the terms, as shown in [2], the solution
+			% of the recursion can be described by
+			%	w_k(z) = Phi_k(z, xi) gamma + Psi_k(z,xi) nu
+			% with w_0(xi) = gamma and w_k(xi) = 0, k > 0. From the
+			% solution for the ode of the recursion:
+			%	W(z) = PHI(z, xi) W(0) + PSI(z,xi) nu
+			% and W(z) = [w_0'(z), w_1'(z), w_2'(z), ... ]' it is obvious,
+			% that the required Phi_k(z,xi) and Psi_k(z,xi) can be
+			% determined by appropriate partitioning of PHI(z,xi),
+			% PSI(z,xi):
+			% 
+			%	[w_0]   [ Phi_0  *   *  ... ][gamma]   [Psi_0]
+			%	[w_1]   [ Phi_1  *   *  ... ][  0  ]   [Psi_1]
+			%	[w_2] = [ Phi_2  *   *  ... ][  0  ] + [Psi_2] nu
+			%	[...]   [  ...              ][ ... ]   [ ... ]
+			% 
+			
+			n = size(A(1).coefficient, 2);
+			
+			ip = misc.Parser();
+			ip.addOptional('N', 1);
+			ip.addOptional('B', quantity.Operator.empty(1, 0));
+			ip.parse(varargin{:});
+			B = ip.Results.B;
+			N = ip.Results.N;	
+			
+			if isempty(B)
+				m = 0;
+			else
+				m = size(B(1).coefficient, 2); 
+			end
+			
+			n = size(A(1).coefficient, 1);
+			d = length(A);
+			spatialDomain = A(1).coefficient(1).grid{:};
+			
+			% build the large system of odes from the recursion
+			A_full = zeros(length(spatialDomain), d*n, d*n);
+			B_full = zeros(length(spatialDomain), d*n, m);
+			
+			idx = 1:n;
+			for k = 1:N
+				idxk = idx + (k-1)*n;
+				if length(B) >= k
+					B_full(:, idxk, :) = B(k).coefficient.on();
+				end
+				for j = 1:d
+					idxj = idx + (j-1)*n;
+					if k - (j-1) > 0
+						A_full(:, idxk, idxj) = A(k - (j-1)).coefficient.on();
+					end
+				end
+			end
+			f = quantity.Discrete(...
+				misc.fundamentalMatrix.odeSolver_par( A_full, spatialDomain ), ...
+				'grid', {spatialDomain, spatialDomain}, 'gridName', {'z', 'zeta'});
+			
+			p = f.volterra(quantity.Discrete(B_full, 'grid', spatialDomain, ...
+				'gridName', 'zeta'));
+			
+			for k = 1:N
+				idxk = idx + (k-1)*n;
+				Phi(:,:,k) = f(idxk, idx).subs('zeta', 0);
+				Psi(:,:,k) = p(idxk,:);
+			end
+			
+		end
+
+		function [Phi, Psi] = stateTransitionMatrix(A, varargin)
+			% STATETRANSITIONMATRIX computation of the
+			% state-transition-matrix
+			%	[Phi, Psi] = state-transition-matrix(obj, varargin) computes the
+			% state-transition-matrix of the boundary value problem
+			%	dz x(z,s) = A(z,s) x(xi,s) + B(z,s) * u(s)
+			% so that Phi is the solution of
+			%	dz Phi(z,xi,s) = A(z,s) Phi(z,xi,s)
+			% and Psi is given by
+			%	Psi(z,xi,s) = int_xi_z Phi(z,zeta,s) B(zeta,s) d zeta
+			% if B(z,s) is defined as quantity.Operator object by
+			% name-value-pair.
+			
+			n = size(A(1).coefficient, 2);
+			
+			ip = misc.Parser();
+			ip.addOptional('N', 1);
+			ip.addOptional('B', quantity.Operator.empty(1, 0));
+			ip.parse(varargin{:});
+			B = ip.Results.B;
+			N = ip.Results.N;
+
+			if isempty(B)
+				m = 0;
+			else
+				m = size(B(1).coefficient, 2); 
+			end
+			d = length(A); % As order of a polynom is zero based
+			z = A(1).grid{1};
 
-                for par = fieldnames(p.Results)'
-                   [obj.(par{1})] = deal(p.Results.(par{1})); 
-                end
-            end
-        end        
-    end
-    
-    methods (Access = public)
-        function S = powerSeries(obj)
-            S = misc.powerSeries2Sum(obj.valueSymbolic, obj.operatorVar);
-        end  
-        
-              
-      function A = ctranspose(obj)
-          
-          argin = struct(obj(1));
-          argin.name = [argin.name, ''''];
-          argin = misc.struct2namevaluepair(argin);
-          
-          a = obj.sym;
-          for k = 1:size(obj,3)
-              a(:,:,k) = (-1)^(k-1) * a(:,:,k)';
-          end
-          A = quantity.Operator(a, argin{:});
-      end
-      
-        function [Phi, F, Psi, P] = fundamentalMatrix(obj, varargin)
-            
-            ip = misc.Parser();
-            ip.addOptional('N', 5);
-            ip.addOptional('B', []);
-            ip.parse(varargin{:});
-            misc.struct2ws(ip.Results)
-            
-             [Phi, F, Psi, P] = ...
-                 misc.fundamentalMatrix.rudolphWoittennek(obj.sym, ...
-                 'N', N, 'zeta', obj(1).grid{1}(1), 's', obj(1).operatorVar, ...
-                 'B', B.sym);
-             
-             if isempty(B)
-                 Psi = zeros(size(B));
-                 P = zeros([size(B), N]);
-             end
-             
-        end
-        
-        function [Phi, Psi] = fundamentalMatrixNumeric(obj, varargin)
-            ip = misc.Parser();
-            ip.addOptional('N', 5);
-			ip.addOptional('B', quantity.Discrete.empty([size(obj,1), 0]));
-			ip.addOptional('spatialDomain', obj(1).grid{:});
-            ip.parse(varargin{:});
-            misc.struct2ws(ip.Results)
-            
-             [Phi, Psi] = ...
-				misc.fundamentalMatrix.rudolphWoittennekNumeric(...
-				obj, N, B, spatialDomain);
+			% initialization of the ProgressBar
+			counter = N - 1;
+			pbar = misc.ProgressBar(...
+				'name', 'Trajectory Planning (Woittennek)', ...
+				'terminalValue', counter, ...
+				'steps', counter, ...
+				'initialStart', true);
+
+			%% computation of transition matrix as power series
+			% The fundamental matrix is considered in the laplace space as the state
+			% transition matrix of the ODE with complex variable s:
+			%   dz w(z,s) = A(z, s) w(z,s) * B(z) v(s),
+			% Hence, fundamental matrix is a solution of the ODE:
+			%   dz Phi(z,zeta) = A(Z) Phi(z,zeta)
+			%   Phi(zeta,zeta) = I
+			% 
+			%   Psi(z,xi,s) = int_zeta^z Phi(z, xi, s) B(xi) d_xi
+			% 
+			% At this, A(z, s) can be described by
+			%   A(z,s) = A_0(z) + A_1(z) s + A_2(z) s^2 + ... + A_d(z) s^d
+			% 
+			% With this, the recursion formula 
+			%   Phi_k(z, zeta) = int_zeta^z Phi_0(z, xi) * sum_i^d A_i(xi) *
+			%   Phi_{k-i}(z, xi) d_xi
+			%   Psi_k(z, zeta) = int_zeta^z Phi_0(z, xi) * sum_i^d A_i(xi) *
+			%   Psi_{k-i}(z, xi) + B_k(xi) d_xi
+
+
+			F0 = A.computePhi0();
+			Phi(:,:,1) = F0.subs('zeta', 0);
+			Psi(:,:,1) = F0.volterra(B(1).coefficient.subs('z', 'zeta'));
+						
+			On = quantity.Discrete.zeros([n n], z, 'gridName', 'z');
+			Om = quantity.Discrete.zeros([n m], z, 'gridName', 'z');
+			for k = 2 : N	
+				pbar.raise();
+				dA = min(d, k);
+				% compute the temporal matrices:
+				M = On;
+				N = Om;
+				for l = 2 : dA
+				   M = M + A(l).coefficient * Phi(:, :, k-l+1);
+				   N = N + A(l).coefficient * Psi(:, :, k-l+1);
+				end
+			   if size(B, 3) >= k
+				  N = N + B(k).coefficient(:, :); 
+			   end
+
+			   % compute the integration of the fundamental matrix with the temporal
+			   % values:
+			   %   Phi_k(z) = int_0_z Phi_0(z, xi) * M(xi) d_xi   
+			   Phi(:, :, k) = F0.volterra( M.subs('z', 'zeta') );	
+			   Psi(:, :, k) = F0.volterra( N.subs('z', 'zeta') );
+			end
+			%
+			pbar.stop();
+
+			
 		end
 		
-		function [F0] = fundamentalMatrix0(obj, spatialDomain)
-			% Fundamental matrix of this operator, that is the solution of
-			%	dz phi(z,z0) = A(z,s) phi(z,z0)
-			%	   phi(z0, z0) = I
-			%	   A(z,s) = sum_k Ak(z) * s^k
+		function [F0] = computePhi0(obj, spatialDomain)
+			% COMPUTEPHI0 compute the static state-transition-matrix
+			%	[F0] = computePhi0(obj, spatialDomain) computation of the
+			% state-transition matrix for this operator for the
+			% steady-state case, i.e., s=0.
+			%	dz phi(z, z0) = A(z, s=0) phi(z,z0)
+			%	   phi(z0, z0) = I 
 			
 			% todo@ferdinand: Es sollte die möglichkeit geben den Operator
 			% mit neuen Eigenschaften (wie z.B. einem neuen grid) zu
 			% aktualisieren. Momentan wird er nur einmal berechnet.
 			
-			if isempty(obj(1).Phi0) 
-			if nargin == 1
-				assert(obj(1).nargin == 1);
-				spatialDomain = obj(1).grid{1};
-				Adisc = obj(:,:,1).on();
-			else
-				Adisc = obj(:,:,1).on(spatialDomain);
-			end
+			if isempty(obj(1).Phi0)
+				if nargin == 1
+					assert(numel(obj(1).variable) == 1);
+					spatialDomain = obj(1).grid{1};
+				end
 				
-				if all([obj.isConstant])
+				if all(cellfun(@(e) e.isConstant, {obj.coefficient}))
 					% for a constant system operator we can use the matrix
 					% exponential function:
 					z = sym('z', 'real');
 					zeta = sym('zeta', 'real');
-					f0 = expm(obj(:,:,1).on()*(z - zeta));
+					f0 = expm(squeeze(obj(1).coefficient.on())*(z - zeta));
 					F0 = quantity.Symbolic(f0, 'grid', {spatialDomain, spatialDomain});
 				else
-					f0 = misc.fundamentalMatrix.odeSolver_par( Adisc, spatialDomain );
+					f0 = misc.fundamentalMatrix.odeSolver_par( ...
+						obj(1).coefficient.on(spatialDomain), ...
+						spatialDomain );
 					F0 = quantity.Discrete(f0, ...
 						'size', [size(obj, 1), size(obj, 2)], ...
 						'gridName', {'z', 'zeta'}, ...
@@ -132,17 +310,17 @@ classdef Operator < quantity.Symbolic
 			
 			F0 = obj(1).Phi0;
 			
-        end
-      
-    end
-    
-    methods (Static, Access = protected)
-        function var = getVariable(symbolicFunction, obj)           
-            var = getVariable@quantity.Symbolic(symbolicFunction);
-            idxS = arrayfun(@(s) isequal(s, obj.operatorVar), var);
-            var = var(~idxS);
-        end
-    end
-    
+		end
+		
+	end
+	
+	methods (Static, Access = protected)
+		function var = getVariable(symbolicFunction, obj)
+			var = getVariable@quantity.Symbolic(symbolicFunction);
+			idxS = arrayfun(@(s) isequal(s, obj.operatorVar), var);
+			var = var(~idxS);
+		end
+	end
+	
 end
 
diff --git a/+quantity/Settings.m b/+quantity/Settings.m
new file mode 100644
index 0000000..1093580
--- /dev/null
+++ b/+quantity/Settings.m
@@ -0,0 +1,27 @@
+classdef Settings < handle
+	%SETTINGS object to save some settings for the quantity class
+	%   
+	
+	properties (Access = public)
+		dockThePlot = false
+	end
+	
+	methods (Access = private)
+		function obj = Settings()
+		end
+	end
+
+	methods (Static)
+		function obj = instance()
+			persistent uniqueObj
+			if isempty(uniqueObj)
+				obj = quantity.Settings();
+				uniqueObj = obj;
+			else
+				obj = uniqueObj;
+			end
+		end
+	end
+	
+end
+
diff --git a/+quantity/Symbolic.m b/+quantity/Symbolic.m
index ace9702..33d381a 100644
--- a/+quantity/Symbolic.m
+++ b/+quantity/Symbolic.m
@@ -4,11 +4,6 @@ classdef Symbolic < quantity.Function
 		valueSymbolic sym;
 		variable sym;
 	end
-	
-	properties (Dependent)
-		isConstant logical;
-	end
-	
 	properties (Constant)
 		defaultSymVar = sym('z', 'real');
 	end
@@ -98,8 +93,14 @@ classdef Symbolic < quantity.Function
 			end
 		end
 				
-		function i = get.isConstant(obj)
-			i = isempty(symvar(obj.valueSymbolic));
+		function i = isConstant(obj)
+			i = true;
+			for k = 1:numel(obj)
+				i = i && isempty(symvar(obj(k).sym));
+				if ~i 
+					break
+				end
+			end
 		end
 		
 		function i = eq(A, B)
@@ -128,11 +129,6 @@ classdef Symbolic < quantity.Function
 			
 		end
 		
-		function operator = quantity.Operator(obj)
-			argin = getCopyParameters(obj(1));
-			operator = quantity.Operator(obj.sym, argin{:});
-		end
-		
 		function Q = quantity.Discrete(obj, varargin)
 			% Cast of a quantity.Symbolic object into a quantity.Discrete
 			% object.
@@ -246,14 +242,22 @@ classdef Symbolic < quantity.Function
 				objIdx = find(isAquantityDiscrete, 1);
 				obj = objCell{objIdx};
 				
+				if ~isempty(obj)
+					myVariable = obj(1).variable;
+					myGrid = obj(1).grid;
+				else
+					myVariable = '';
+					myGrid = {};
+				end
+				
 				for k = 1:numel(objCell)
 					
 					if isa(objCell{k}, 'quantity.Symbolic')
 						o = objCell{k};
 					elseif isa(objCell{k}, 'double') || isa(objCell{k}, 'sym')
 						o = quantity.Symbolic( objCell{k}, ...
-							'variable', obj(1).variable, ...
-							'grid', obj(1).grid);
+							'variable', myVariable, ...
+							'grid', myGrid);
 					end
 					objCell{k} = o;
 				end
@@ -493,9 +497,7 @@ classdef Symbolic < quantity.Function
 			if nargin == 1
 				k = 1;
 			end
-			% TODO: if result is constant, delete gridName
 			% TODO: specify for which variable it should be differentiated
-			% create a new object for the derivative of obj:
 			D = obj.copy();
 			[D.name] = deal(['(d_{' char(obj(1).variable) '} ' D(1).name, ')']);
 			[D.valueDiscrete] = deal([]);
@@ -509,12 +511,19 @@ classdef Symbolic < quantity.Function
 			mObj = uminus@quantity.Function(obj);
 			for k = 1:numel(obj)
 				mObj(k).valueSymbolic = - obj(k).valueSymbolic;
+				mObj(k).name = ['-', obj(k).name];
 			end
-			[mObj.name] = deal(['-', obj(1).name]);
 		end
 		
 		function C = mtimes(A, B)
 			% if one input is ordinary matrix, this is very simple.
+			if isempty(B) ||  isempty(A)
+				% TODO: actually this only holds for multiplication of
+				% matrices. If higher dimensional arrays are multiplied
+				% this can lead to wrong results.
+				C = quantity.Symbolic( mtimes@quantity.Discrete(A, B) );
+				return
+			end			
 			if isnumeric(B)
 				C = quantity.Symbolic(A.sym() * B, ...
 					'grid', A(1).grid, 'variable', A(1).variable, ...
diff --git a/+unittests/+quantity/testDiscrete.m b/+unittests/+quantity/testDiscrete.m
index cd0a07a..0a75b91 100644
--- a/+unittests/+quantity/testDiscrete.m
+++ b/+unittests/+quantity/testDiscrete.m
@@ -684,6 +684,17 @@ testCase.verifyTrue(numeric.near(bc, BC.on));
 
 end
 
+function testIsConstant(testCase)
+
+C = quantity.Discrete(rand(3,7), 'gridName', {});
+testCase.verifyTrue(C.isConstant());
+
+z = linspace(0, pi)';
+A = quantity.Discrete(sin(z), 'grid', z, 'gridName', 'z');
+testCase.verifyFalse(A.isConstant());
+
+end
+
 function testMTimesConstant(testCase)
 
 myGrid = linspace(0, 2*pi)';
diff --git a/+unittests/+quantity/testOperator.m b/+unittests/+quantity/testOperator.m
index 46b9acb..7ba6994 100644
--- a/+unittests/+quantity/testOperator.m
+++ b/+unittests/+quantity/testOperator.m
@@ -2,149 +2,168 @@ function [tests] = testOperator()
 %TESTGEVREY Summary of this function goes here
 %   Detailed explanation goes here
 tests = functiontests(localfunctions);
-
-% FIXME write useful unittests
-
 end
 
-function testCastToQuantity(testCase)
+function setupOnce(testCase)
 %%
-N = 8;
 z = sym('z', 'real');
-Z = linspace(0, 1, 500)';
-A = quantity.Operator(cat(3, ...
-   [ 0, 0, 0, 0; ...
-     1   0  0  0; ...
-     0   1  0  0; ...
-     0   0  1  0], ...
-   [0 0 0, -3 + z; ...
-    0 0 0   0; ...
-    0 0 0   0; ...
-    0 0 0   0], ...
-   [0 0 0, -4; ...
-    0 0 0 0; ...
-    0 0 0 0; ...
-    0 0 0 0]...
-   ), 'name', 'A', 'grid', Z);
-
-%%
-X1 = quantity.Discrete(A);
-X2 = quantity.Discrete(A);
-
-verifyEqual(testCase, X1.on, A.on);
-verifyEqual(testCase, X2, X1);
+s = sym('s');
+Z = linspace(0, 1, 11)';
+
+% init with symbolic values
+A = cat(3, [ 0, 0, 0, 0; ...
+		     1, 0, 0, 0; ...
+			 0, 1, 0, 0; ...
+			 0, 0, 1, 0], ...
+            [0, 0, 0, -3 + z; ...
+			 0, 0, 0,   0; ...
+			 0, 0, 0,   0; ...
+			 0, 0, 0,   0], ...
+			[0, 0, 0, -4; ...
+			 0, 0, 0, 0; ...
+			 0, 0, 0, 0; ...
+			 0, 0, 0, 0]);
+
+B = cat(3, [1, 0, 0, 0]', zeros(4, 1), zeros(4, 1));
+		 
+for k = 1:3
+	a{k} = quantity.Symbolic(A(:,:,k), 'grid', Z, 'variable', z);
 end
+		 
+A = quantity.Operator(a, 'grid', Z, 'variable', z, 's', s);
 
-function testPhiPsiSpaceDependent(testCase)
-%%
-a = 20;
-N = 1;
-z = sym('z', 'real');
-Z = linspace(0, 1, 12)';
-A = quantity.Operator([1, z; 0, a], 'name', 'A', 'grid', Z);
-B = quantity.Operator([0; 0], 'name', 'B', 'grid', Z, 'variable', z);
-
-[PhiNum, PsiNum] = A.fundamentalMatrixNumeric(N, B);
-[~, F, ~, P] = A.fundamentalMatrix(N, B);
-
-PhiSym = quantity.Operator(F, 'grid', Z, 'figureID', N + 1);
-PsiSym = quantity.Operator(P, 'grid', Z, 'variable', z);
-
-% plot(PhiSym, 'fig', 4)
-
-% use the exact solution from the peano-baker series paper.
-if numeric.near(a, 1)
-    f = 0.5 * z^2 * exp(z);
-else
-    f = (exp(z) - exp(a * z) - (1 - a) * z * exp( a * z ) ) / (1 - a)^2; 
-end   
-
-PhiExact = quantity.Symbolic([exp(z), f; 0, exp(a*z)], 'grid', Z, 'name', 'Phi_A');
-
-Dnum = relativeErrorSupremum(PhiNum, PhiExact);
-Dsym = relativeErrorSupremum(PhiSym, PhiExact);
-deltaNum = Dnum.on();
-deltaSym = Dsym.on();
-testCase.verifyLessThan(max(deltaNum(:)), 1e-6);
-testCase.verifyLessThan(max(deltaSym(:)), 1e-6);
+ testCase.TestData.A = A;
 
 end
 
-function testPhiPsi(testCase)
-
-%%
-N = 3;
-z = sym('z', 'real');
-Z = linspace(0, 1, 15)';
-A = quantity.Operator(cat(3, ...
-   [ 0, 0, 0, 0; ...
-     1   0  0  0; ...
-     0   1  0  0; ...
-     0   0  1  0], ...
-   [0 0 0, -3 + z; ...
-    0 0 0   0; ...
-    0 0 0   0; ...
-    0 0 0   0], ...
-   [0 0 0, -4; ...
-    0 0 0 0; ...
-    0 0 0 0; ...
-    0 0 0 0]...
-   ), 'name', 'A', 'grid', Z);
-
-B = quantity.Operator([1 1; zeros(3,2)], 'name', 'B', 'variable', z, 'grid', Z);
-
-[PhiNum, PsiNum] = A.fundamentalMatrixNumeric(N, B, 'spatialDomain', Z);
-
-[PhiNum.figureID] = deal(6);
-[~, F, ~, P] = A.fundamentalMatrix(N, B);
-
-PhiSym = quantity.Operator(F, 'grid', Z);
-PsiSym = quantity.Operator(P, 'grid', Z);
-
-[D, sup] = relativeErrorSupremum(PhiNum, PhiSym);
-delta = D.on();
-testCase.verifyLessThan(max(delta(:)), 1e-2);
-
-D = relativeErrorSupremum(PsiNum, PsiSym);
-delta = D.on();
-testCase.verifyLessThan(max(delta(:)), 1e-2);
+function testCopy(testCase)
+	B = {quantity.Discrete(0, 'gridName', ''), ...
+		 quantity.Discrete(1, 'gridName', ''), ...
+		 quantity.Discrete(2, 'gridName', '')};
+	A = testCase.TestData.A;
+	C = A.copy(B);
+	
+	testCase.verifyEqual(C(1).grid, A(1).grid);
+	testCase.verifyEqual(C(1).variable, A(1).variable);
+end
 
+function testPlot(testCase)
+	f = testCase.TestData.A.plot();
+	close(f);
+end
 
+function testPowerSeries(testCase)
+	syms z s
+	P = testCase.TestData.A.powerSeries();
+	testCase.verifyEqual(P(1, 4), - 4*s^2 + (z - 3)*s);
+end
 
+function testCTranspose(testCase)
+	At = testCase.TestData.A';
+	testCase.verifyTrue(At(3).coefficient == testCase.TestData.A(3).coefficient');	
 end
 
+% 
+function testFundamentalMatrixSpaceDependent(testCase)
+%
+	a = 20;
+	z = sym('z', 'real');
+	Z = linspace(0, 1, 32)';
+	A = quantity.Operator(...
+		{quantity.Symbolic([1, z; 0, a], 'variable', 'z', 'grid', Z)}, ...
+		'grid', Z);
+	
+	F = A.computePhi0();
+	F0 = quantity.Discrete(squeeze(F.on({Z, 0})), 'gridName', 'z');
+	
+	% compute the exact solution from the peano-baker series paper.
+	if numeric.near(a, 1)
+		f = 0.5 * z^2 * exp(z);
+	else
+		f = (exp(z) - exp(a * z) - (1 - a) * z * exp( a * z ) ) / (1 - a)^2; 
+	end   
+
+	PhiExact = quantity.Symbolic([exp(z), f; 0, exp(a*z)], 'grid', Z, 'name', 'Phi_A');
+		
+	testCase.verifyTrue(numeric.near(F0.on() ./ F0.MAX(), PhiExact.on() ./ F0.MAX(), 1e-6));
+	
+end
+% 
+% function testPhiPsi(testCase)
+% N = 3;
+% z = sym('z', 'real');
+% Z = linspace(0, 1, 51)';
+% a = quantity.Symbolic(cat(3, ...
+%    [ 0, 0, 0, 0; ...
+%      1   0  0  0; ...
+%      0   1  0  0; ...
+%      0   0  1  0], ...
+%    [0 0 0, -3 + z; ...
+%     0 0 0   0; ...
+%     0 0 0   0; ...
+%     0 0 0   0], ...
+%    [0 0 0, -4; ...
+%     0 0 0 0; ...
+%     0 0 0 0; ...
+%     0 0 0 0]...
+%    ), 'name', 'A', 'grid', Z);
+% A = quantity.Operator(a);
+% B = quantity.Symbolic([1 1; zeros(3,2)], 'name', 'B', 'variable', z, 'grid', Z);
+% B = quantity.Operator(B);
+% 
+% [Phi1, Psi1] = A.stateTransitionMatrix(N, B);
+% [Phi2, Psi2] = A.stateTransitionByOde(N, B);
+% 
+% deltaPhi = Phi1.relativeErrorSupremum(Phi2);
+% deltaPsi = Psi1.relativeErrorSupremum(Psi2);
+% 
+% %% TODO: write a test for the fundamental matrices.
+% gamma = ones(4,1);
+% nu = ones(2,1);
+% 
+% for k = 1:N
+% 	wk_num(k,:) = Phi(:,:,k) * gamma + Psi(:,:,k) * nu;
+% end
+% 	
+% wk_ode = quantity.Discrete.empty(0,4);
+% 
+% for k = 1:N
+% % odeStep = @(z, w, A, B, v) (A.on(z) * w + B.on(z) * v);
+% 	[~, tmp] = ode15s(@(z,w) odeStep(z, w, A, B, nu, wk_ode), Z, gamma * (k==1));
+% 	wk_ode(k, :) = quantity.Discrete(tmp, 'grid', Z, 'gridName', 'z');
+% end
+% 
+% P = wk_num.relativeErrorSupremum(wk_ode);
+% P.plot
+% 
+% 
+% end
+
+% 
 function testFundamental(testCase)
 
-%%
-Z = sym('z', 'real');
-zeta = sym('zeta', 'real');
-A = quantity.Operator(cat(3, ...
-   [ 0, 0, 0, 0; ...
-     1   0  0  0; ...
-     0   1  0  0; ...
-     0   0  1  0], ...
-   [0 0 0, -3; ...
-    0 0 0   0; ...
-    0 0 0   0; ...
-    0 0 0   0], ...
-   [0 0 0, -4; ...
-    0 0 0 0; ...
-    0 0 0 0; ...
-    0 0 0 0]...
-   ), 'name', 'A');
-
-%%
-Nz = A.gridSize;
-n = size(A, 2);
-d = size(A, 3); % As order of a polynom is zero based
-zz = linspace(0, 1, 23)';
-
-F = misc.fundamentalMatrix.odeSolver( A(:,:,1).on(zz), zz);
-phi = quantity.Discrete( squeeze(F(:,1,:,:)), 'grid', {zz}, 'gridName', 'z', 'name', 'Phi');
-
-%%
-P = expm( A(:,:,1).at(1) * (Z - zeta) ) ;
-PHI0 = subs(P, zeta, 0);
-PHI = quantity.Symbolic(PHI0, 'grid', {zz}, 'figureID', 3);
+	Z = sym('z', 'real');
+	zeta = sym('zeta', 'real');
+	a0 = [ 0, 0, 0, 0; ...
+		   1, 0, 0,  0; ...
+		   0, 1, 0,  0; ...
+		   0, 0, 1,  0];
+	A0 = quantity.Discrete( reshape(a0, 1, 4, 4), 'gridName', '', 'grid', 0);
+
+	zz = linspace(0, 1, 51)';
+	A = quantity.Operator({A0}, 'grid', zz);
+	F0 = quantity.Discrete(squeeze(A.computePhi0().on({zz, 0})), 'grid', zz, 'gridName', 'z');
+
+	F = misc.fundamentalMatrix.odeSolver_par( A0.on(zz) , zz);
+	FF = quantity.Discrete(F, 'grid', {zz, zz}, 'gridName', {'z', 'zeta'});
+	
+	F1 = quantity.Discrete(squeeze( F(:,1,:,:) ), 'grid', {zz}, 'gridName', {'z'}, 'name', 'Phi');
+
+	P = expm( squeeze(A0.on()) * (Z - zeta) ) ;
+	PHI0 = subs(P, zeta, 0);
+	F2 = quantity.Symbolic(PHI0, 'grid', {zz}, 'figureID', 3);
+
+	testCase.verifyTrue(numeric.near(F0.on(), F1.on(), 1e-7));
+	testCase.verifyTrue(numeric.near(F0.on(), F2.on(), 1e-7));
 
 end
\ No newline at end of file
diff --git a/+unittests/+quantity/testSymbolic.m b/+unittests/+quantity/testSymbolic.m
index 4dc30e1..3320fc0 100644
--- a/+unittests/+quantity/testSymbolic.m
+++ b/+unittests/+quantity/testSymbolic.m
@@ -442,7 +442,11 @@ for k = 1:q.gridSize()
 end
 
 %%
-verifyEqual(testCase, magic700, q.on());
+testCase.verifyEqual(magic700, q.on());
+testCase.verifyTrue(q.isConstant());
+
+p = quantity.Symbolic([0 0 0 0 z], 'grid', linspace(0,1)', 'variable', z);
+testCase.verifyFalse(p.isConstant());
 
 end
 
-- 
GitLab