All Downloads are FREE. Search and download functionalities are using the official Maven repository.

shz.Help Maven / Gradle / Ivy

package shz;

import shz.constant.ArrayConstant;
import shz.msg.ServerFailure;
import shz.stack.LArrayStack;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;

public final class Help {
    private Help() {
        throw new IllegalStateException();
    }

    public static String uuid(boolean replace) {
        String uuid = UUID.randomUUID().toString();
        return replace ? uuid.replaceAll("-", "") : uuid;
    }

    public static String uuid() {
        return uuid(true);
    }

    public static String randomString(char[] array, int len) {
        Validator.requireNon(len < 1 || Validator.isEmpty(array));
        StringBuilder sb = new StringBuilder(len);
        ThreadLocalRandom.current().ints(len, 0, array.length).forEach(i -> sb.append(array[i]));
        return sb.toString();
    }

    public static String randomString(int len) {
        return randomString(ArrayConstant.CHAR_ARRAY_62, len);
    }

    public static String randomNumber(int len) {
        Validator.requireNon(len < 1);
        StringBuilder sb = new StringBuilder(len);
        ThreadLocalRandom.current().ints(len, 0, 10).forEach(sb::append);
        return sb.toString();
    }

    public static double setScale(double val, int scale, boolean align) {
        BigDecimal result = new BigDecimal(Double.toString(val));
        if (align || result.scale() > scale) result = result.setScale(scale, BigDecimal.ROUND_HALF_UP);
        return result.doubleValue();
    }

    public static double setScale(double val, int scale) {
        return setScale(val, scale, false);
    }

    /**
     * 指定字符集 字符串递增
     */
    public static String increment(String s, char[] arrayX) {
        Validator.requireNonAnyEmpty(s, arrayX);
        char[] array = s.toCharArray();
        if (array.length == 0) return s;
        return increment0(array, array.length - 1, arrayX);
    }

    private static String increment0(char[] array, int idx, char[] arrayX) {
        int index = indexOf(array[idx], arrayX);
        Validator.requireNon(index == -1);
        if (index != arrayX.length - 1) {
            array[idx] = arrayX[index + 1];
            return new String(array);
        } else if (idx == 0) {
            array[0] = arrayX[0];
            return arrayX[1] + new String(array);
        } else {
            array[idx] = arrayX[0];
            return increment0(array, idx - 1, arrayX);
        }
    }

    public static int indexOf(char c, char[] array) {
        for (int i = 0; i < array.length; ++i) if (array[i] == c) return i;
        return -1;
    }

    public static  int indexOf(T t, T[] array) {
        for (int i = 0; i < array.length; ++i) if (Objects.equals(array[i], t)) return i;
        return -1;
    }

    public static String increment(String s) {
        return increment(s, ArrayConstant.CHAR_ARRAY_62);
    }

    /**
     * 指定字符集 字符串递减
     */
    public static String decrement(String s, char[] arrayX) {
        Validator.requireNonAnyEmpty(s, arrayX);
        char[] array = s.toCharArray();
        if (array.length == 0) return s;
        return decrement0(array, array.length - 1, arrayX);
    }

    private static String decrement0(char[] array, int idx, char[] arrayX) {
        int index = indexOf(array[idx], arrayX);
        Validator.requireNon(index == -1);
        if (index != 0) {
            array[idx] = arrayX[index - 1];
            return new String(array);
        } else if (idx == 0) return new String(array, 1, array.length - 1);
        else {
            array[idx] = arrayX[arrayX.length - 1];
            return decrement0(array, idx - 1, arrayX);
        }
    }

    public static String decrement(String s) {
        return decrement(s, ArrayConstant.CHAR_ARRAY_62);
    }

    private static int compare(String s1, String s2, char[] arrayX) {
        Validator.requireNonAnyEmpty(s1, s2, arrayX);
        int len = s1.length();
        if (len - s2.length() != 0) return len - s2.length();
        char[] array1 = s1.toCharArray();
        char[] array2 = s2.toCharArray();
        for (int i = 0; i < len; ++i) {
            int index1 = indexOf(array1[i], arrayX);
            if (index1 == -1) throw new IllegalArgumentException();
            int index2 = indexOf(array2[i], arrayX);
            if (index2 == -1) throw new IllegalArgumentException();
            if (index1 - index2 != 0) return index1 - index2;
        }
        return 0;
    }

    /**
     * 指定字符集 字符串比较
     */
    public static boolean gt(String s1, String s2, char[] arrayX) {
        return compare(s1, s2, arrayX) > 0;
    }

    public static boolean gt(String s1, String s2) {
        return compare(s1, s2, ArrayConstant.CHAR_ARRAY_62) > 0;
    }

    public static boolean ge(String s1, String s2, char[] arrayX) {
        return compare(s1, s2, arrayX) >= 0;
    }

    public static boolean ge(String s1, String s2) {
        return compare(s1, s2, ArrayConstant.CHAR_ARRAY_62) >= 0;
    }

    /**
     * 指定字符集 字符串转long
     */
    public static long stringToLong(String s, char[] arrayX) {
        Validator.requireNonAnyEmpty(s, arrayX);
        char[] array = s.toCharArray();
        if (array.length == 0) return -1L;
        return stringToLong0(array, array.length - 1, arrayX);
    }

    private static long stringToLong0(char[] array, int idx, char[] arrayX) {
        int index = indexOf(array[idx], arrayX);
        Validator.requireNon(index == -1);
        if (idx == 0) return (long) (index * Math.pow(arrayX.length, array.length - 1));
        return (long) (index * Math.pow(arrayX.length, array.length - 1 - idx)) + stringToLong0(array, --idx, arrayX);
    }

    public static long stringToLong(String s) {
        return stringToLong(s, ArrayConstant.CHAR_ARRAY_62);
    }

    /**
     * 指定字符集 long转字符串
     */
    public static String longToString(long value, char[] arrayX) {
        Validator.requireNon(value < 0L || Validator.isEmpty(arrayX));
        if (value < arrayX.length) return arrayX[(int) value] + "";
        int idx = 2;
        while (Math.pow(arrayX.length, idx) < value) ++idx;
        StringBuilder sb = new StringBuilder(idx);
        longToString0(value, arrayX, idx - 1, sb);
        return sb.toString();
    }

    private static void longToString0(long value, char[] arrayX, int idx, StringBuilder sb) {
        if (idx == 0) {
            sb.append(arrayX[(int) value]);
            return;
        }
        int index = 0;
        long current = 0;
        long next;
        while ((next = (long) (index * Math.pow(arrayX.length, idx))) < value) {
            current = next;
            ++index;
        }
        if (index == 0) for (int i = 0; i <= idx; ++i) sb.append(arrayX[0]);
        else {
            sb.append(arrayX[index - 1]);
            longToString0(value - current, arrayX, idx - 1, sb);
        }
    }

    public static String longToString(long value) {
        return longToString(value, ArrayConstant.CHAR_ARRAY_62);
    }

    /**
     * 指定字符集 字符串相加
     */
    public static String add(String s1, String s2, char[] arrayX) {
        Validator.requireNonAnyEmpty(s1, s2, arrayX);
        if (s1.length() == 0) return s2;
        if (s2.length() == 0) return s1;
        if (s1.length() < s2.length()) {
            String temp = s1;
            s1 = s2;
            s2 = temp;
        }
        char[] lmax = s1.toCharArray();
        char[] lmin = s2.toCharArray();
        int i = lmax.length - 1, j = lmin.length - 1;
        int[] carry = new int[1];
        while (j >= 0) {
            add0(lmax, i, lmin[j], arrayX, carry);
            --i;
            --j;
        }
        if (carry[0] == 1) return arrayX[1] + new String(lmax);
        return new String(lmax);
    }

    private static void add0(char[] lmax, int i, char c, char[] arrayX, int[] carry) {
        if (i == -1) {
            carry[0] = 1;
            return;
        }
        int idx1 = indexOf(lmax[i], arrayX);
        Validator.requireNon(idx1 == -1);
        int idx2 = indexOf(c, arrayX);
        Validator.requireNon(idx2 == -1);
        int idx = idx1 + idx2;
        if (idx <= arrayX.length - 1) lmax[i] = arrayX[idx];
        else {
            lmax[i] = arrayX[idx - arrayX.length];
            add0(lmax, --i, arrayX[1], arrayX, carry);
        }
    }

    public static String add(String s1, String s2) {
        return add(s1, s2, ArrayConstant.CHAR_ARRAY_62);
    }

    /**
     * 指定字符集 字符串相减
     */
    public static String minus(String s1, String s2, char[] arrayX) {
        Validator.requireNonAnyEmpty(s1, s2, arrayX);
        if (s1.length() == 0) return s2;
        if (s2.length() == 0) return s1;
        if (s1.length() < s2.length()) {
            String temp = s1;
            s1 = s2;
            s2 = temp;
        } else if (s1.length() == s2.length() && stringToLong(s1, arrayX) < stringToLong(s2, arrayX)) {
            String temp = s1;
            s1 = s2;
            s2 = temp;
        }
        char[] lmax = s1.toCharArray();
        char[] lmin = s2.toCharArray();
        int i = lmax.length - 1, j = lmin.length - 1;
        while (j >= 0) {
            minus0(lmax, i, lmin[j], arrayX);
            --i;
            --j;
        }
        int L = 0;
        for (i = 0; i < lmax.length; ++i)
            if (indexOf(lmax[i], arrayX) == 0) ++L;
            else break;

        if (L == 0) return new String(lmax);
        if (L == lmax.length) return arrayX[0] + "";
        return new String(lmax, L, lmax.length - L);
    }

    private static void minus0(char[] lmax, int i, char c, char[] arrayX) {
        int idx1 = indexOf(lmax[i], arrayX);
        Validator.requireNon(idx1 == -1);
        int idx2 = indexOf(c, arrayX);
        Validator.requireNon(idx2 == -1);
        int idx = idx1 - idx2;
        if (idx >= 0) lmax[i] = arrayX[idx];
        else {
            lmax[i] = arrayX[idx + arrayX.length];
            minus0(lmax, --i, arrayX[1], arrayX);
        }
    }

    public static String minus(String s1, String s2) {
        return minus(s1, s2, ArrayConstant.CHAR_ARRAY_62);
    }

    /**
     * 字符串编辑距离
     */
    public static int distance(String s1, String s2) {
        if (Validator.isAnyBlank(s1, s2)) return -1;
        char[] a1 = s1.toCharArray();
        char[] a2 = s2.toCharArray();
        int[][] dp = new int[a1.length][a2.length];
        dp[0][0] = a1[0] == a2[0] ? 0 : 1;
        for (int i = 1; i < a1.length; ++i)
            if (a1[i] == a2[0]) dp[i][0] = i;
            else dp[i][0] = dp[i - 1][0] + 1;

        for (int i = 1; i < a2.length; ++i)
            if (a2[i] == a1[0]) dp[0][i] = i;
            else dp[0][i] = dp[0][i - 1] + 1;

        for (int i = 1; i < a1.length; ++i)
            for (int j = 1; j < a2.length; ++j)
                if (a1[i] == a2[j]) dp[i][j] = dp[i - 1][j - 1];
                else dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])) + 1;

        return dp[a1.length - 1][a2.length - 1];
    }

    public static int[] toIntArray(CharSequence cs, int len) {
        if (cs == null) return ArrayConstant.EMPTY_INT_ARRAY;
        return cs.chars().limit(len < 0 ? cs.length() : Math.min(cs.length(), len)).toArray();
    }

    public static int[] toIntArray(CharSequence cs) {
        return toIntArray(cs, -1);
    }

    public static char[] toCharArray(int[] array) {
        if (Validator.isEmpty(array)) return ArrayConstant.EMPTY_CHAR_ARRAY;
        char[] charArray = new char[array.length];
        for (int i = 0; i < array.length; ++i) charArray[i] = (char) array[i];
        return charArray;
    }

    public static char[] toCharArray(List list) {
        if (Validator.isEmpty(list)) return ArrayConstant.EMPTY_CHAR_ARRAY;
        char[] charArray = new char[list.size()];
        for (int i = 0; i < list.size(); ++i) charArray[i] = list.get(i);
        return charArray;
    }

    /**
     * 转换科学计数法字符串
     */
    public static String fromScientific(String numeric, int idx) {
        int dot = numeric.indexOf(".");
        if (idx == 0) return numeric;
        int len = numeric.length();
        if (idx > 0) {
            if (dot == -1) {
                char[] array = new char[len + idx];
                numeric.getChars(0, len, array, 0);
                for (int i = len; i < array.length; ++i) array[i] = '0';
                return new String(array);
            }
            int len0 = dot + 1 + idx;
            if (len <= len0) {
                char[] array = new char[len0 - 1];
                numeric.getChars(0, dot, array, 0);
                numeric.getChars(dot + 1, len, array, dot);
                if (len < len0) for (int i = len - 1; i < dot + idx; ++i) array[i] = '0';
                return new String(array);
            }
            char[] array = new char[len];
            numeric.getChars(0, dot, array, 0);
            numeric.getChars(dot + 1, len0, array, dot);
            array[dot + idx] = '.';
            numeric.getChars(len0, len, array, len0);
            return new String(array);
        }
        idx = -idx;
        if (dot == -1) {
            if (idx < len) {
                char[] array = new char[len + 1];
                numeric.getChars(0, len - idx, array, 0);
                array[len - idx] = '.';
                numeric.getChars(len - idx, len, array, len - idx + 1);
                return new String(array);
            }
            char[] array = new char[idx + 2];
            numeric.getChars(0, len, array, idx - len + 2);
            array[0] = '0';
            array[1] = '.';
            for (int i = 2; i < idx - len + 2; ++i) array[i] = '0';
            return new String(array);
        }
        if (idx < dot) {
            char[] array = new char[len];
            numeric.getChars(0, dot - idx, array, 0);
            array[dot - idx] = '.';
            numeric.getChars(dot - idx, dot, array, dot - idx + 1);
            numeric.getChars(dot + 1, len, array, dot + 1);
            return new String(array);
        }
        char[] array = new char[len + idx - dot + 1];
        numeric.getChars(0, dot, array, idx - dot + 2);
        numeric.getChars(dot + 1, len, array, idx + 2);
        array[0] = '0';
        array[1] = '.';
        for (int i = 2; i < idx - dot + 2; ++i) array[i] = '0';
        return new String(array);
    }

    public static String fromScientific(String s) {
        Validator.requireNon(Validator.isBlank(s) || (!s.contains("E") && !s.contains("e")));
        String[] split = !s.contains("E") ? s.split("e") : s.split("E");
        Validator.requireNon(split.length != 2);
        return fromScientific(split[0], Integer.parseInt(split[1]));
    }

    public static int pwdScore(String s) {
        char[] array = s.toCharArray();
        int socre, len;
        if ((len = array.length) <= 4) socre = 5;
        else if (len <= 7) socre = 10;
        else if (len <= 12) socre = 25;
        else socre = 29;
        boolean L = false, U = false;
        int D = 0, S = 0;
        for (int i = 0; i < len; ++i) {
            if (indexOf(array[i], ArrayConstant.CHAR_LOWERCASE) != -1) L = true;
            else if (indexOf(array[i], ArrayConstant.CHAR_UPPERCASE) != -1) U = true;
            else if (indexOf(array[i], ArrayConstant.CHAR_ARRAY_10) != -1) ++D;
            else if (indexOf(array[i], ArrayConstant.CHAR_SYMBOL) != -1) ++S;
            if (L && U && D > 1 && S > 1) return socre + 70;
        }

        if (L && U) socre += 20;
        else if (L || U) socre += 10;

        if (D > 1) socre += 20;
        else if (D == 1) socre += 10;

        if (S > 1) socre += 25;
        else if (S == 1) socre += 10;

        if (L && U && D > 0 && S > 0) socre += 5;
        else if ((L || U) && D > 0 && S > 0) socre += 3;
        else if ((L || U) && D > 0) socre += 2;

        return socre;
    }

    /**
     * 计算表达式
     */
    public static Number eval(String expression) {
        char[] a = expression.toCharArray();
        LArrayStack data = LArrayStack.of();
        LArrayStack calc = LArrayStack.of();
        for (char c : a) {
            if (c == ')' || c == ')') {
                while (!data.isEmpty()) {
                    Object pop = data.pop();
                    if (pop instanceof Character) {
                        char c1 = (Character) pop;
                        if (c1 == '(' || c1 == '(') break;
                    }
                    calc.push(pop);
                }
                data.push(eval0(calc));
            } else if (!Character.isWhitespace(c)) data.push(c);
        }
        while (!data.isEmpty()) calc.push(data.pop());
        return eval0(calc);
    }

    private static Number eval0(LArrayStack calc) {
        BigDecimal num;
        char op;
        BigDecimal nextNum;
        char nextOp;
        while (!calc.isEmpty()) {
            num = getNumber(calc);
            if (calc.isEmpty()) return num;
            op = (char) calc.pop();
            nextNum = getNumber(calc);
            if (calc.isEmpty()) return op(num, nextNum, op);
            if (op == '*' || op == '/' || (nextOp = (char) calc.peek()) == '+' || nextOp == '-')
                calc.push(op(num, nextNum, op));
            else {
                calc.pop();
                calc.push(op(nextNum, getNumber(calc), nextOp));
                calc.push(op);
                calc.push(num);
            }
        }
        throw PRException.impossible();
    }

    private static BigDecimal getNumber(LArrayStack calc) {
        if (calc.peek() instanceof BigDecimal) return (BigDecimal) calc.pop();
        StringBuilder sb = new StringBuilder();
        char c;
        while (!calc.isEmpty() && (((c = (char) calc.peek()) >= '0' && c <= '9') || c == '.')) {
            calc.pop();
            sb.append(c);
        }
        return new BigDecimal(sb.toString());
    }

    private static BigDecimal op(BigDecimal num1, BigDecimal num2, char op) {
        switch (op) {
            case '+':
                return num1.add(num2);
            case '-':
                return num1.subtract(num2);
            case '*':
                return num1.multiply(num2);
            case '/':
                return num1.divide(num2, Math.max(num1.scale(), num2.scale()), RoundingMode.HALF_UP);
        }
        throw PRException.of(ServerFailure.UnsupportedOperationException);
    }

    private static final char[] DIGIT = {'零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'};
    private static final char[] INDEX = {'拾', '佰', '仟', '万', '亿'};
    private static final char[] UNIT = {'元', '角', '分'};
    private static final char I = '整';

    /**
     * 数字转人民币格式字符串
     */
    public static String toRmb(double value) {
        String s = Double.toString(value);
        StringBuilder sb = new StringBuilder("人民币");
        char[] array = s.toCharArray();
        int dot = s.indexOf('.');
        dot = dot == -1 ? array.length : dot;
        boolean tail0 = false;
        if (dot != 1 || array[0] != '0') {
            toRmb0(array, 0, dot, sb);
            tail0 = sb.charAt(sb.length() - 1) == DIGIT[0];
            if (tail0) sb.deleteCharAt(sb.length() - 1);
            sb.append(UNIT[0]);
            if (dot == array.length) return sb.append(I).toString();
        }
        int end = dot + 1 + Math.min(2, array.length - 1 - dot);
        while (end > 0 && array[end - 1] == '0') --end;
        if (end == 0) return sb.append(I).toString();
        if (tail0) sb.append(DIGIT[0]);
        for (int i = dot + 1; i < end; ++i) if (array[i] != '0') sb.append(DIGIT[array[i] - '0']).append(UNIT[i - dot]);
        return sb.toString();
    }

    private static void toRmb0(char[] array, int L, int R, StringBuilder sb) {
        if (R - L >= 9) {
            toRmb0(array, L, R - 8, sb);
            dealRmb0(sb, INDEX[4]);
            toRmb0(array, R - 8, R, sb);
            return;
        }
        if (R - L >= 5) {
            toRmb0(array, L, R - 4, sb);
            dealRmb0(sb, INDEX[3]);
            toRmb0(array, R - 4, R, sb);
            return;
        }
        boolean mark0 = sb.charAt(sb.length() - 1) == DIGIT[0];
        for (int i = L; i < R; ++i) {
            if (array[i] == '0') {
                if (!mark0) {
                    sb.append(DIGIT[0]);
                    mark0 = true;
                }
            } else {
                if (R - 2 - i > 0) sb.append(DIGIT[array[i] - '0']).append(INDEX[R - 2 - i]);
                else if (R - 2 - i == 0)
                    if (array[i] - '0' != 1) sb.append(DIGIT[array[i] - '0']).append(INDEX[0]);
                    else sb.append(INDEX[0]);
                else sb.append(DIGIT[array[i] - '0']);
                mark0 = false;
            }
        }
    }

    private static void dealRmb0(StringBuilder sb, char c) {
        boolean tail0 = sb.charAt(sb.length() - 1) == DIGIT[0];
        if (tail0) {
            sb.deleteCharAt(sb.length() - 1);
            appendRmbIndex0(sb, c);
            sb.append(DIGIT[0]);
        } else appendRmbIndex0(sb, c);
    }

    private static void appendRmbIndex0(StringBuilder sb, char c) {
        char prev = sb.charAt(sb.length() - 1);
        int prev_idx = -1;
        int current_idx = -1;
        for (int i = 0; i < INDEX.length; ++i) {
            if (prev == INDEX[i]) prev_idx = i;
            if (c == INDEX[i]) current_idx = i;
        }
        if (prev_idx <= current_idx) sb.append(c);
    }

    public static long gcd(long x, long y) {
        if (x < y) {
            x ^= y;
            y ^= x;
            x ^= y;
        }
        long m;
        while ((m = x % y) > 0) {
            x = y;
            y = m;
        }
        return y;
    }

    public static long lcm(long x, long y) {
        return x * y / gcd(x, y);
    }

    /**
     * 拆分指定宽高为指定个数的相同宽高区域
     */
    public static int[] whAndGcd(int w, int h, int count, int gcdLimit, boolean cut) {
        long tw = w;
        long th = h;
        int gcd = (int) gcd(w, h);
        int state = 0;
        if (gcd < gcdLimit) {
            gcd <<= 1;
            state = 1;
        }
        while (tw * th / (gcd * gcd) < count) {
            if (state == 1) {
                tw <<= 1;
                th <<= 1;
                if (tw * th > Integer.MAX_VALUE) {
                    tw >>= 1;
                    th >>= 1;
                    state = 2;
                }
            } else {
                gcd >>= 1;
                if (state == 0) {
                    if (gcd < gcdLimit) {
                        gcd = gcd == 0 ? 1 : gcd << 1;
                        state = 1;
                    }
                } else if (gcd == 0) {
                    gcd = 1;
                    state = 3;
                    break;
                }
            }
        }
        while (tw * th / (gcd * gcd) > count) {
            if (state == 0) gcd <<= 1;
            else {
                tw >>= 1;
                th >>= 1;
            }
        }
        tw = tw == 0 ? 1 : tw;
        th = th == 0 ? 1 : th;
        int delta = count - (int) (tw * th / (gcd * gcd));
        int cn = (int) (tw / gcd);
        if (cn > 0) {
            th += (delta / cn) * gcd;
            if (!cut && delta % cn > 0) th += gcd;
        }
        return new int[]{(int) tw, (int) th, gcd};
    }

    public static boolean isPrime(long x) {
        if (x == 2L) return true;
        if (x < 2 || (x & 1) == 0) return false;
        double sqrt = Math.sqrt(x);
        for (int i = 3; i <= sqrt; i += 2) if (x % i == 0) return false;
        return true;
    }

    public static long arithmeticSequenceSum(long start, int step, int n) {
        if (n <= 0) return 0;
        return start + arithmeticSequenceSum(start + step, step, --n);
    }

    public static long permutation(int n, int m) {
        if (m == 1) return n;
        return n * permutation(n - 1, m - 1);
    }

    public static  void permutation(E[] es, int m, Consumer consumer) {
        permutation0(es, Arrays.copyOf(es, m), 0, new boolean[es.length], consumer);
    }

    private static  void permutation0(E[] es, E[] a, int idx, boolean[] book, Consumer consumer) {
        if (idx == a.length) {
            consumer.accept(a);
            return;
        }
        for (int i = 0; i < es.length; ++i) {
            if (!book[i]) {
                a[idx] = es[i];
                book[i] = true;
                permutation0(es, a, idx + 1, book, consumer);
                book[i] = false;
            }
        }
    }

    public static long combination(int n, int m) {
        return combination0(n, m > (n >> 1) ? n - m : m);
    }

    private static long combination0(int n, int m) {
        if (n == m) return 1;
        if (m == 1) return n;
        return combination0(n - 1, m) + combination0(n - 1, m - 1);
    }

    public static  void combination(E[] es, int m, Consumer consumer) {
        if (m == es.length) {
            consumer.accept(es);
            return;
        }
        combination0(es, Arrays.copyOf(es, m), 0, 0, consumer);
    }

    private static  void combination0(E[] es, E[] a, int idx, int L, Consumer consumer) {
        if (idx == a.length) {
            consumer.accept(a);
            return;
        }
        for (int i = L; i < es.length; ++i) {
            a[idx] = es[i];
            combination0(es, a, idx + 1, i + 1, consumer);
        }
    }

    public static int atoi(String s) {
        if (Validator.isBlank(s)) return 0;
        char[] array = s.toCharArray();
        char head = array[0];
        if ((head < '0' || head > '9') && head != '-' && head != '+') return 0;
        int flag = head == '-' ? -1 : 1;
        long unresult = head == '-' || head == '+' ? 0 : head - '0';
        long result = flag * unresult;
        for (int len = array.length, i = 1; i < len; ++i) {
            if (array[i] < '0' || array[i] > '9') break;
            unresult = unresult * 10 + (array[i] - '0');
            result = flag * unresult;
            if (result >= Integer.MAX_VALUE) return Integer.MAX_VALUE;
            else if (result <= Integer.MIN_VALUE) return Integer.MIN_VALUE;
        }
        return (int) result;
    }

    public static String format(int capacity, String format, Object... args) {
        if (Validator.isBlank(format) || Validator.isEmpty(args)) return format;
        return new Formatter(new StringBuilder(capacity)).format(format, args).toString();
    }

    public static String format(String format, Object... args) {
        return format(format.length() + 50, format, args);
    }

    public static int toIntIp(String ip) {
        String[] a = ip.split("\\.");
        return (int) (Long.parseLong(a[0]) << 24
                | Long.parseLong(a[1]) << 16
                | Long.parseLong(a[2]) << 8
                | Long.parseLong(a[3]));
    }

    public static String toStrIp(int ip) {
        return Integer.toString(ip >> 24 & 0xff) + '.' + (ip >> 16 & 0xff) + '.' + (ip >> 8 & 0xff) + '.' + (ip & 0xff);
    }

    public static boolean hasConfig(long config, int value) {
        Validator.requireNon(value < 0 || value >= Long.SIZE);
        return (config & 1L << value) != 0;
    }

    public static long addConfig(long config, int value) {
        Validator.requireNon(value < 0 || value >= Long.SIZE);
        return config | 1L << value;
    }

    public static long removeConfig(long config, int value) {
        Validator.requireNon(value < 0 || value >= Long.SIZE);
        return config & ~(1L << value);
    }

    public static boolean hasConfig(long config, int... values) {
        if (values.length == 0) return false;
        return Arrays.stream(values).parallel().allMatch(value -> hasConfig(config, value));
    }

    public static long addConfig(long config, int... values) {
        if (values.length == 0) return config;
        for (int value : values) config = addConfig(config, value);
        return config;
    }

    public static long removeConfig(long config, int... values) {
        if (values.length == 0) return config;
        for (int value : values) config = removeConfig(config, value);
        return config;
    }

    public static boolean hasConfig(long config, List values) {
        Validator.requireNonEmpty(values);
        return values.parallelStream().allMatch(value -> hasConfig(config, value));
    }

    public static long addConfig(long config, List values) {
        Validator.requireNonEmpty(values);
        for (int value : values) config = addConfig(config, value);
        return config;
    }

    public static long removeConfig(long config, List values) {
        Validator.requireNonEmpty(values);
        for (int value : values) config = removeConfig(config, value);
        return config;
    }

    public static boolean hasConfig(Map configs, int value) {
        Objects.requireNonNull(configs);
        Validator.requireNon(value < 0);
        Long config = configs.get(value / Long.SIZE);
        return config != null && hasConfig(config, value % Long.SIZE);
    }

    public static int addConfig(Map configs, int value) {
        Objects.requireNonNull(configs);
        Validator.requireNon(value < 0);
        int idx = value / Long.SIZE;
        Long config = configs.get(idx);
        configs.put(idx, addConfig(config == null ? 0L : config, value % Long.SIZE));
        return idx;
    }

    public static int removeConfig(Map configs, int value) {
        Objects.requireNonNull(configs);
        Validator.requireNon(value < 0);
        int idx = value / Long.SIZE;
        Long config;
        if ((config = configs.get(idx)) == null) return -1;
        configs.put(idx, removeConfig(config, value % Long.SIZE));
        return idx;
    }

    public static boolean hasConfig(Map configs, int... values) {
        Objects.requireNonNull(configs);
        return Arrays.stream(values).parallel().allMatch(value -> hasConfig(configs, value));
    }

    public static Map addConfig(Map configs, int... values) {
        Objects.requireNonNull(configs);
        int count = (int) Arrays.stream(values).map(i -> i / Long.SIZE).distinct().count();
        Map update = ToMap.get(count).build();
        for (int value : values) {
            int idx = value / Long.SIZE;
            Long config;
            if ((config = update.get(idx)) == null) config = configs.get(idx);
            update.put(idx, addConfig(config == null ? 0L : config, value % Long.SIZE));
        }
        return update;
    }

    public static Map removeConfig(Map configs, int... values) {
        Objects.requireNonNull(configs);
        int count = (int) Arrays.stream(values).map(i -> i / Long.SIZE).distinct().count();
        Map update = ToMap.get(count).build();
        for (int value : values) {
            int idx = value / Long.SIZE;
            Long config;
            if ((config = update.get(idx)) == null && (config = configs.get(idx)) == null) continue;
            update.put(idx, removeConfig(config, value % Long.SIZE));
        }
        return update;
    }

    public static boolean hasConfig(Map configs, List values) {
        Objects.requireNonNull(configs);
        Objects.requireNonNull(values);
        return values.parallelStream().allMatch(value -> hasConfig(configs, value));
    }

    public static Map addConfig(Map configs, List values) {
        Objects.requireNonNull(configs);
        Objects.requireNonNull(values);
        return addConfig(configs, values.stream().mapToInt(value -> value).toArray());
    }

    public static Map removeConfig(Map configs, List values) {
        Objects.requireNonNull(configs);
        Objects.requireNonNull(values);
        return removeConfig(configs, values.stream().mapToInt(value -> value).toArray());
    }

    public static boolean hasConfig(Map configs, Map others) {
        Objects.requireNonNull(configs);
        Objects.requireNonNull(others);
        if (others.size() > configs.size()) return false;
        for (Map.Entry kv : others.entrySet()) {
            Long config;
            if ((config = configs.get(kv.getKey())) == null) return false;
            if ((kv.getValue() ^ (config & kv.getValue())) != 0L) return false;
        }
        return true;
    }

    public static Map addConfig(Map configs, Map others) {
        Objects.requireNonNull(configs);
        Objects.requireNonNull(others);
        Map update = ToMap.get(others.size(), 1).build();
        others.forEach((idx, value) -> {
            Long config;
            if ((config = update.get(idx)) == null) config = configs.get(idx);
            update.put(idx, config == null ? value : config | value);
        });
        return update;
    }

    public static Map removeConfig(Map configs, Map others) {
        Objects.requireNonNull(configs);
        Objects.requireNonNull(others);
        Map update = ToMap.get(others.size(), 1).build();
        others.forEach((idx, value) -> {
            Long config;
            if ((config = update.get(idx)) == null && (config = configs.get(idx)) == null) return;
            update.put(idx, config ^ (config & value));
        });
        return update;
    }
}