public int maxHandler(TreeNode n) {
        //note: this helper function does 2 things
        // (1) it solve the same problem at node n, updating gMax
        // (2) it computes the best single path (from n to n's descendents) ends at n, and return this value
        if (n == null) {
            return 0; //no affect on gMax, and 0 for function (2)
        } else if (n.left == null && n.right == null) {//base case, a leaf node
            if (n.val > gMax) {
                gMax = n.val;
            }
            return n.val;
        } else {
            int leftVal = maxHandler(n.left);
            int rightVal = maxHandler(n.right);
            int myMaxSinglePath;
            if (leftVal <= 0 && rightVal <= 0) {
                myMaxSinglePath = n.val;
            } else {
                myMaxSinglePath = Math.max(leftVal, rightVal) + n.val;
            }
            if (myMaxSinglePath > gMax) {
                gMax = myMaxSinglePath;
            }

            int myMaxDoublePath = leftVal + rightVal + n.val;
            if (myMaxDoublePath > gMax) {
                gMax = myMaxDoublePath;
            }

            return myMaxSinglePath;
        }
    }