classdef Base 
    properties (Access=protected)
        obj;  % 存储sympy的表达式对象（Symbol/Add/Mul等）
    end

    methods(Access=public)

        % ===================== 构造函数（通用表达式构造） =====================
        function obj = Base(x)
            % 通用构造函数：接收sympy对象并存储
            obj.obj = x;
        end

        function no = setObj(o, x)
            o.obj = x;
            no = o;
        end

        function obj = getObj(o)
            obj = o.obj;
        end

        % ===================== 辅助方法 =====================
        function disp(a)
            % 美化打印表达式
            s = a.obj.__str__();
            s = strrep(s, '**', '^');
            disp(s);
        end

        function pprint(a)
            sympy__pprint(getObj(a));
        end

        function nexpr = subs(expr, varargin)
            % 符号替换
            if nargin ~= 2 && nargin ~= 3
                error("请输入正确的参数，支持：subs(expr, var, newval) 或 subs(expr, newval)");
            end

            expr_sp = expr.obj;
            
            if nargin == 3
                % 情况1：指定变量和替换值 subs(expr, var, newval)
                if iscell(varargin{1}) && iscell(varargin{2})
                    vars = varargin{1};
                    newvars = varargin{2};
                    nvars = numel(vars);
                    if nvars ~= numel(newvars); error('新旧替换符号的参数个数不一致'); end
                    vars = getObj(vars);
                    newvars = sympy.to_sp_object(newvars);
                    if nvars == 1
                        nexpr = sympy.Expr(expr_sp.subs(vars, newvars).simplify());
                        return;
                    end
                    subs_list = pylist;
                    for i=0:numel(vars)-1
                        subs_list.append(pytuple(vars{i}, newvars{i}));
                    end
                    nexpr = sympy.Expr(expr_sp.subs(subs_list).simplify());
                    return;
                else
                    var_sp = sympy.to_sp_object(varargin{1});
                    newval_sp = sympy.to_sp_object(varargin{2});
                    nexpr = sympy.Expr(expr_sp.subs(var_sp, newval_sp).simplify());
                    return;
                end
            end 
            
            if nargin == 2
                % 情况2：仅指定替换值，自动替换第一个自由符号 subs(expr, newval)
                py = pyroot;
                fs_list = py.list(expr_sp.free_symbols);
                fs_list.sort();
                if py.len(fs_list) == 0
                    error("表达式中无自由符号可替换！");
                end
                target_sym = fs_list{0};
                newval_sp = sympy.to_sp_object(varargin{1});
                nexpr = sympy.Expr(expr_sp.subs(target_sym, newval_sp).simplify());
                return;
            end 
        end

        function nexpr = expand(expr, varargin)
            % nexpr = expand(expr)
            % 展开符号表达式expr中的所有括号
            % nexpr = expand(expr, name, value)
            % nexpr = expand(expr, name, value) 可以增加选项控制表达式展开方式。例如：
            % 'ArithmeticOnly', true 表示只做代数展开，不涉及三角函数，指数等，默认是false。'IgnoreAnalyticConstraints', true 表示展开指数，默认false。
            
            % 例子：
            % Input [1] >> [x,y] = syms('x y');
            % Input [2] >> expr = (x + y)*cos(x-y);
            % Input [3] >> expand(expr)
            % x*sin(x)*sin(y) + x*cos(x)*cos(y) + y*sin(x)*sin(y) + y*cos(x)*cos(y)
            % Input [4] >> expand(expr,'ArithmeticOnly',true)
            % x*cos(x - y) + y*cos(x - y) 
            p = inputParser;
            p = addParameter(p, "ArithmeticOnly", false, @BoolCheck);
            p = addParameter(p, "IgnoreAnalyticConstraints", false, @BoolCheck);

            p = parse(p, varargin{:});

            sp = sympy__sp();
            expr = expr.getObj();
            if ~p.Results.ArithmeticOnly
                expr = sp.expand_trig(expr);
            end

            if ~p.Results.IgnoreAnalyticConstraints
                expr = sp.expand_log(expr, 'force', true);
            end
            
            nexpr = sp.expand(expr);
            nexpr = expr.setObj(nexpr);
        end 

        function s = simplify(o)
            s = o.setObj(o.getObj().simplify());
        end
    end
end