This night wasn't easy on Vasya. His favorite team lost, and he didn't find himself victorious either — although he played perfectly, his teammates let him down every time. He had to win at least one more time, but the losestreak only grew longer and longer... It's no wonder he didn't get any sleep this night at all.

In the morning, Vasya was waiting the bus to the university on the bus stop. Vasya's thoughts were hazy and so he couldn't remember the right bus' number quite right and got onto the bus with the number n.

In the bus, Vasya thought that he could get the order of the digits in the number of the bus wrong. Futhermore, he could "see" some digits several times, but the digits he saw were definitely in the real number of the bus. For example, if Vasya saw the number 2028, it could mean that the real bus number could be 2028, 8022, 2820 or just 820. However, numbers 80, 22208, 52 definitely couldn't be the number of the bus. Also, real bus number couldn't start with the digit 0, this meaning that, for example, number 082 couldn't be the real bus number too.

Given n, determine the total number of possible bus number variants.

Input

The first line contains one integer n (1 ≤ n ≤ 10^{18}) — the number of the bus that was seen by Vasya. It is guaranteed that this number does not start with 0.

Output

Output a single integer — the amount of possible variants of the real bus number.

Examples

Input

97


Output

2


Input

2028


Output

13

Note

In the first sample, only variants 97 and 79 are possible.

In the second sample, the variants (in the increasing order) are the following: 208, 280, 802, 820, 2028, 2082, 2208, 2280, 2802, 2820, 8022, 8202, 8220.
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.StringTokenizer;

/**
 *
 * @author is2ac
 */
public class D_CF {

    public static void main(String[] args) throws IOException {
        FastScanner58 fs = new FastScanner58();
        //Reader fs = new Reader();
        PrintWriter pw = new PrintWriter(System.out);
        //int t = fs.ni();
        int t = 1;
        for (int tc = 0; tc < t; tc++) {
            String s = fs.next();
            int[] d = new int[10];
            int mask = 0;
            for (int i = 0; i < s.length(); i++) {
                d[s.charAt(i)-'0']++;
                mask |= (1<<s.charAt(i)-'0');
            }
            int org = mask;
            Long[][][][][][][][][][][] dp = new Long[d[0]+1][d[1]+1][d[2]+1][d[3]+1][d[4]+1][d[5]+1][d[6]+1][d[7]+1][d[8]+1][d[9]+1][1<<11];
            pw.println(recur(d,mask,dp,org));
        }
  
        pw.close();
    }
    public static long recur(int[] d, int mask, Long[][][][][][][][][][][] dp,int org) {
        long n = 0L;
        if (dp[d[0]][d[1]][d[2]][d[3]][d[4]][d[5]][d[6]][d[7]][d[8]][d[9]][mask] != null) return dp[d[0]][d[1]][d[2]][d[3]][d[4]][d[5]][d[6]][d[7]][d[8]][d[9]][mask];
        if (mask==0) n++;
        int[] e = d.clone();
        for (int i = 0; i < 10; i++) {
            if (d[i]==0) continue;
            if (i==0 && mask==org) continue;
            int nm = mask;
            e[i]--;
            if ((nm&(1<<i))>0) {
                nm -= (1<<i);
            }
            n += recur(e,nm,dp,org);
            e[i]++;
            nm |= (1<<i);
        }
        return dp[d[0]][d[1]][d[2]][d[3]][d[4]][d[5]][d[6]][d[7]][d[8]][d[9]][mask] = n;
    }
    public static long gcd(long n1, long n2) {
        if (n2 == 0) {
            return n1;
        }
        return gcd(n2, n1 % n2);
    }
}
class GFG {
 
    // A utility function to get the
    // middle index of given range.
    static int getMid(int s, int e)
    {
        return s + (e - s) / 2;
    }
 
    /*
    * A recursive function to get the sum
    of values in given range of the array.
    * The following are parameters
      for this function.
    *
    * st -> Pointer to segment tree
    * node -> Index of current node in
    *         the segment tree.
    * ss & se -> Starting and ending indexes
    *         of the segment represented
    *         by current node, i.e., st[node]
    * l & r -> Starting and ending indexes
    *         of range query
    */
    static long MinUtil(long[] st, int ss,
                       int se, int l,
                       int r, int node)
    {
 
        // If segment of this node is completely
        // part of given range, then return
        // the max of segment
        if (l <= ss && r >= se)
            return st[node];
 
        // If segment of this node does not
        // belong to given range
        if (se < l || ss > r)
            return (long)(1e11);
 
        // If segment of this node is partially
        // the part of given range
        int mid = getMid(ss, se);
 
        return Math.min(
            MinUtil(st, ss, mid, l, r,
                     2 * node + 1),
            MinUtil(st, mid + 1, se, l, r,
                    2 * node + 2));
    }
 
    /*
    * A recursive function to update the
    nodes which have the given index in their
    * range. The following are parameters
    st, ss and se are same as defined above
    * index -> index of the element to be updated.
    */
    static void updateValue(long arr[], long[]
                            st, int ss,
                            int se, int index,
                            long value,
                            int node)
    {
        if (index < ss || index > se) {
            //System.out.println("Invalid Input");
            return;
        }
 
        if (ss == se) {
 
            // update value in array and in
            // segment tree
            arr[index] = value;
            st[node] = value;
        }
        else {
            int mid = getMid(ss, se);
 
            if (index >= ss && index <= mid)
                updateValue(arr, st, ss, mid,
                            index, value,
                            2 * node + 1);
            else
                updateValue(arr, st, mid + 1, se, index,
                            value, 2 * node + 2);
 
            st[node] = Math.min(st[2 * node + 1],
                                st[2 * node + 2]);
        }
        return;
    }
 
    // Return max of elements in range from
    // index l (query start) to r (query end).
    static long getMin(long[] st, int n, int l, int r)
    {
 
        // Check for erroneous input values
        if (l < 0 || r > n - 1 || l > r) {
            //System.out.printf("Invalid Input\n");
            return (long)(1e11);
        }
 
        return MinUtil(st, 0, n - 1, l, r, 0);
    }
 
    // A recursive function that constructs Segment
    // Tree for array[ss..se]. si is index of
    // current node in segment tree st
    static long constructSTUtil(long arr[],
                               int ss, int se,
                               long[] st, int si)
    {
 
        // If there is one element in array, store
        // it in current node of segment tree and return
        if (ss == se) {
            st[si] = arr[ss];
            return arr[ss];
        }
 
        // If there are more than one elements, then
        // recur for left and right subtrees and
        // store the max of values in this node
        int mid = getMid(ss, se);
 
        st[si] = Math.min(
            constructSTUtil(arr, ss, mid,
                            st, si * 2 + 1),
            constructSTUtil(arr, mid + 1,
                            se, st,
                            si * 2 + 2));
 
        return st[si];
    }
 
    /*
    * Function to construct segment tree from
    given array. This function allocates
    * memory for segment tree.
    */
    static long[] constructST(long arr[], int n)
    {
 
        // Height of segment tree
        int x = (int)Math.ceil(Math.log(n) / Math.log(2));
 
        // Maximum size of segment tree
        int max_size = 2 * (int)Math.pow(2, x) - 1;
 
        // Allocate memory
        long[] st = new long[max_size];
 
        // Fill the allocated memory st
        constructSTUtil(arr, 0, n - 1, st, 0);
 
        // Return the constructed segment tree
        return st;
    }
}
class BIT16 {

    int[] bit;

    public BIT16(int size) {
        bit = new int[size];
    }

    public void update(int ind, int delta) {
        while (ind < bit.length) {
            bit[ind] += delta;
            ind = ind + (ind & (-1 * ind));
        }
    }

    public int sum(int ind) {
        int s = 0;
        while (ind > 0) {
            s += bit[ind];
            ind = ind - (ind & (-1 * ind));
        }
        return s;
    }

    public int query(int l, int r) {
        return sum(r) - sum(l);
    }
}

class UnionFind18 {

    int[] id;

    public UnionFind18(int size) {
        id = new int[size];
        for (int i = 0; i < size; i++) {
            id[i] = i;
        }
    }

    public int find(int p) {
        int root = p;
        while (root != id[root]) {
            root = id[root];
        }
        while (p != root) {
            int next = id[p];
            id[p] = root;
            p = next;
        }
        return root;
    }

    public void union(int p, int q) {
        int a = find(p), b = find(q);
        if (a == b) {
            return;
        }
        id[b] = a;
    }
}

class Reader {

    final private int BUFFER_SIZE = 1 << 16;
    private DataInputStream din;
    private byte[] buffer;
    private int bufferPointer, bytesRead;

    public Reader() {
        din = new DataInputStream(System.in);
        buffer = new byte[BUFFER_SIZE];
        bufferPointer = bytesRead = 0;
    }

    public Reader(String file_name) throws IOException {
        din = new DataInputStream(new FileInputStream(file_name));
        buffer = new byte[BUFFER_SIZE];
        bufferPointer = bytesRead = 0;
    }

    public String readLine() throws IOException {
        byte[] buf = new byte[64];
        int cnt = 0, c;
        while ((c = read()) != -1) {
            if (c == '\n') {
                break;
            }
            buf[cnt++] = (byte) c;
        }
        return new String(buf, 0, cnt);
    }

    public int ni() throws IOException {
        int ret = 0;
        byte c = read();
        while (c <= ' ') {
            c = read();
        }
        boolean neg = (c == '-');
        if (neg) {
            c = read();
        }
        do {
            ret = ret * 10 + c - '0';
        } while ((c = read()) >= '0' && c <= '9');
        if (neg) {
            return -ret;
        }
        return ret;
    }

    public long nextLong() throws IOException {
        long ret = 0;
        byte c = read();
        while (c <= ' ') {
            c = read();
        }
        boolean neg = (c == '-');
        if (neg) {
            c = read();
        }
        do {
            ret = ret * 10 + c - '0';
        } while ((c = read()) >= '0' && c <= '9');
        if (neg) {
            return -ret;
        }
        return ret;
    }

    public double nextDouble() throws IOException {
        double ret = 0, div = 1;
        byte c = read();
        while (c <= ' ') {
            c = read();
        }
        boolean neg = (c == '-');
        if (neg) {
            c = read();
        }
        do {
            ret = ret * 10 + c - '0';
        } while ((c = read()) >= '0' && c <= '9');
        if (c == '.') {
            while ((c = read()) >= '0' && c <= '9') {
                ret += (c - '0') / (div *= 10);
            }
        }
        if (neg) {
            return -ret;
        }
        return ret;
    }

    private void fillBuffer() throws IOException {
        bytesRead = din.read(buffer, bufferPointer = 0, BUFFER_SIZE);
        if (bytesRead == -1) {
            buffer[0] = -1;
        }
    }

    private byte read() throws IOException {
        if (bufferPointer == bytesRead) {
            fillBuffer();
        }
        return buffer[bufferPointer++];
    }

    public void close() throws IOException {
        if (din == null) {
            return;
        }
        din.close();
    }
}

class FastScanner58 {

    BufferedReader br;
    StringTokenizer st;

    public FastScanner58() {
        br = new BufferedReader(new InputStreamReader(System.in), 32768);
        st = null;
    }

    String next() {
        while (st == null || !st.hasMoreElements()) {
            try {
                st = new StringTokenizer(br.readLine());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return st.nextToken();
    }

    int ni() {
        return Integer.parseInt(next());
    }

    int[] intArray(int N) {
        int[] ret = new int[N];
        for (int i = 0; i < N; i++) {
            ret[i] = ni();
        }
        return ret;
    }

    long nl() {
        return Long.parseLong(next());
    }

    long[] longArray(int N) {
        long[] ret = new long[N];
        for (int i = 0; i < N; i++) {
            ret[i] = nl();
        }
        return ret;
    }

    double nd() {
        return Double.parseDouble(next());
    }

    String nextLine() {
        String str = "";
        try {
            str = br.readLine();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return str;
    }
}
