classdef sydney
    % SYDNEY  [Not a public class] Automatic first-order differentiator.
    %
    % Backend IRIS class.
    % No help provided.
    
    % -IRIS Toolbox.
    % -Copyright (c) 2007-2012 Jaromir Benes.
    
    properties
        func = '';
        args = {};
        lookahead = {};
    end
    
    methods
        function this = sydney(varargin)
            if isempty(varargin)
                return
            elseif length(varargin) == 1
                if isa(varargin{1},'sydney')
                    this = varargin{1};
                elseif isnumeric(varargin{1})
                    this.func = '';
                    this.args = varargin{1};
                elseif ischar(varargin{1})
                    if isvarname(varargin{1})
                        this.func = '';
                        this.args = varargin{1};
                    else
                        template = sydney();
                        eq = strtrim(varargin{1});
                        if isempty(eq)
                            this.func = '';
                            this.args = 0;
                            return
                        end
                        % Remove anonymous function header @(...) if present.
                        if strncmp(eq,'@(',2);
                            eq = regexprep(eq,'@\(.*?\)','');
                        end
                        % Find all variables names.
                        varlist = regexp(eq, ...
                            '(?<!@)(\<[a-zA-Z]\w*\>)(?!\()','tokens');
                        % Validate function names in the equation. Function
                        % not handled by the sydney class will be replaced
                        % with a call to sydney.xf().
                        eq = sydney.callfunc(eq);
                        if ~isempty(varlist)
                            varlist = unique([varlist{:}]);
                        end
                        % Create a sydney object for each variables name.
                        nvar = length(varlist);
                        z = cell(1,nvar);
                        for i = 1 : nvar
                            z{i} = template;
                            z{i}.func = '';
                            z{i}.args = varlist{i};
                        end
                        % Create an anonymous function for the equation.
                        % The function's preamble includes all variable
                        % names found in the equation.
                        preamble = sprintf('%s,',varlist{:});
                        preamble = ['@(',preamble(1:end-1),')'];
                        tempfunc = str2func([preamble,eq]);
                        % Evaluate the equation's function handle on the
                        % sydney objects.
                        tempeval = tempfunc(z{:});
                        if isa(tempeval,'sydney')
                            this = tempeval;
                        elseif isnumeric(tempeval)
                            this.func = '';
                            this.args = tempeval;
                        else
                            error('sydney:construct', ...
                                'Cannot create a SYDNEY object');
                        end
                    end
                end
            else
                this.func = varargin{1};
                this.args = varargin{2};
            end
        end
        
        % Functional forms of unary and binary operators.
        
        function this = uplus(varargin)
            this = sydney.parse('uplus',varargin{:});
        end
        function this = uminus(varargin)
            this = sydney.parse('uminus',varargin{:});
        end
        function this = plus(varargin)
            this = sydney.parse('plus',varargin{:});
        end
        function this = minus(varargin)
            this = sydney.parse('minus',varargin{:});
        end
        function this = times(varargin)
            this = sydney.parse('times',varargin{:});
        end
        function this = mtimes(varargin)
            this = sydney.parse('times',varargin{:});
        end
        function this = rdivide(varargin)
            this = sydney.parse('rdivide',varargin{:});
        end
        function this = mrdivide(varargin)
            this = sydney.parse('rdivide',varargin{:});
        end
        function this = ldivide(varargin)
            this = sydney.parse('rdivide',varargin{[2,1]});
        end
        function this = mldivide(varargin)
            this = sydney.parse('rdivide',varargin{[2,1]});
        end
        function this = power(varargin)
            this = sydney.parse('power',varargin{:});
        end
        function this = mpower(varargin)
            this = sydney.parse('power',varargin{:});
        end
        
        function flag = isatom(z)
            flag = isempty(z.func) || isequal(z.func,'sydney.d');
        end
        
        function flag = isnumber(z)
            flag = isempty(z.func) && isnumericscalar(z.args);
        end
        
        varargout = diff(varargin)
        varargout = reduce(varargin)
        varargout = char(varargin)
        
    end
    
    methods (Access=protected)
        varargout = mydiff(varargin)
    end
    
    methods (Static)
        
        varargout = d(varargin)
        varargout = mydiffeqtn(varargin)
        varargout = mysymb2eqtn(varargin)
        varargout = myeqtn2symb(varargin)
        varargout = parse(varargin)

        varargout = testme(varargin)        
        
        function eq = callfunc(eq)
            % Find all function names. Function names may also include dots to allow
            % for methods and packages. Functions with no input arguments are not
            % parsed and remain unchanged.
            funclist = regexp(eq, ...
                '\<[a-zA-Z][\w\.]*\>\((?!\))','match');
            funclist = unique(funclist);
            % Find function names that are not handled by the sydney
            % class.
            for i = 1 : length(funclist)
                funcname = funclist{i}(1:end-1);
                eq = regexprep(eq,['\<',funcname,'\>('], ...
                    ['sydney.parse(''',funcname,''',']);
            end
        end
        
        % For bkw compatibility.
        
        function varargout = diffxf(varargin)
            [varargout{1:nargout}] = sydney.d(varargin{:});
        end
        
        function varargout = numdiff(varargin)
            [varargout{1:nargout}] = sydney.d(varargin{:});
        end
        
    end
    
end