private boolean match(Value[] matches, Value match, Value value) {
    if(match instanceof Symbol) {
      int index = args.indexOf((Symbol)match);
      if(index != -1) {
        if(matches[index] == null) {
          matches[index] = value;
          return true;
        }
        return matches[index].equals(value);
      } else {
        return match.equals(value);
      }
    } else if(match instanceof Rational) {
      return match.equals(value);
    } else { // match instanceof Call
      Call mc = (Call)match;
      if(value instanceof Call) {
        Call vc = (Call)value;
        if(!mc.func.equals(vc.func)) {
          return false;
        }
        Value[] tmpMatches = Arrays.copyOf(matches, matches.length);
        for(int i = 0; i < mc.args.size(); i++) {
          if(!match(tmpMatches, mc.args.get(i), vc.args.get(i))) {
            return false;
          }
        }
        for(int i = 0; i < matches.length; i++) {
          matches[i] = tmpMatches[i];
        }
        return true;
      } else {
        return false;
      }
    }
  }