Problem: Another feature of Shakespeare language is that the variables are named after characters of plays by Shakespeare, and all operations on them (value assignment, output etc.) look like a dialog with other characters. New values of variables are defined in a rather lengthy way, so a programmer should try to minimize their usage.

You have to print the given sequence of n integers. To do this, you have m variables and two types of operations on them:

  * variable=integer
  * print(variable)



Any of the m variables can be used as variable. Variables are denoted by lowercase letters between "a" and "z", inclusive. Any integer number can be used as integer.

Let's say that the penalty for using first type of operations equals to the number of set bits in the number integer. There is no penalty on using second type of operations. Find and output the program which minimizes the penalty for printing the given sequence of numbers.

Input

The first line of input contains integers n and m (1 ≤ n ≤ 250, 1 ≤ m ≤ 26). The second line contains the sequence to be printed. Each element of the sequence is an integer between 1 and 109, inclusive. The sequence has to be printed in the given order (from left to right).

Output

Output the number of lines in the optimal program and the optimal penalty. Next, output the program itself, one command per line. If there are several programs with minimal penalty, output any of them (you have only to minimize the penalty).

Examples

Input

7 2
1 2 2 4 2 1 2


Output

11 4
b=1
print(b)
a=2
print(a)
print(a)
b=4
print(b)
print(a)
b=1
print(b)
print(a)


Input

6 3
1 2 3 1 2 3


Output

9 4
c=1
print(c)
b=2
print(b)
a=3
print(a)
print(c)
print(b)
print(a)

Solution (in JAVA):

Solution:
import java.util.Comparator;
import java.io.BufferedReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.List;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.math.BigInteger;
import java.util.Collections;
import java.io.InputStream;

/**
 * Built using CHelper plug-in
 * Actual solution is at the top
 * @author AlexFetisov
 */
public class Main {
	public static void main(String[] args) {
		InputStream inputStream = System.in;
		OutputStream outputStream = System.out;
		InputReader in = new InputReader(inputStream);
		PrintWriter out = new PrintWriter(outputStream);
		TaskE solver = new TaskE();
		solver.solve(1, in, out);
		out.close();
	}
}

class TaskE {

    private int N;
    private int source;
    private int dest;

    public void solve(int testNumber, InputReader in, PrintWriter out) {
        int n = in.nextInt();
        int m = in.nextInt();
        int[] a = Utils.readIntArray(in, n);
        N = 2 * n + m + 2;
        Graph g = new Graph();
        g.initGraphWithFlowCost(N, (n * n + m * n + n + m + n) * 2);
        source = 0;
        dest = N -1;
        for (int i = 1; i <= n + m; ++i) {
            g.addFlowCostEdge(source, i, 1, 0);
        }
        for (int i = 1; i <= n; ++i) {
            for (int j = i + 1; j <= n; ++j) {
                int jj = j + n + m;
                g.addFlowCostEdge(i, jj, 1, getCost(i - 1, j - 1, a));
            }
        }
        for (int i = n + 1; i <= n + m; ++i) {
            for (int j = n + m + 1; j <= n + n + m; ++j) {
                g.addFlowCostEdge(i, j, 1, Integer.bitCount(a[j - n - m - 1]));
            }
        }
        for (int j = n + m + 1; j <= n + n + m; ++j) {
            g.addFlowCostEdge(j, dest, 1, 0);
        }
        MinCostMaxFlow.run(g, source, dest, false, n + m);
        DisjointSet dsu = new DisjointSet(n);
        for (int v = 1; v <= n; ++v) {
            for (int eid = g.first[v]; eid != -1; eid = g.next[eid]) {
                if (g.to[eid] >= n + m + 1 && g.to[eid] <= n + n + m) {
                    if (g.flow[eid] > 0) {
                        dsu.union(v - 1, g.to[eid] - n - m - 1);
                    }
                }
            }
        }
        int[] vars = new int[m];
        int[] value = new int[m];
        Arrays.fill(vars, -1);
        List<String> result = new ArrayList<String>();
        int res = 0;
        for (int i = 0; i < n; ++i) {
            int cur = dsu.findSet(i);
            boolean found = false;
            for (int j = 0; j < m; ++j) {
                if (vars[j] == cur) {
                    found = true;
                    if (value[j] == a[i]) {
                        result.add("print(" + ((char)(j + 'a')) + ")");
                    } else {
                        result.add(((char)(j + 'a')) + "=" + a[i]);
                        result.add("print(" + ((char)(j + 'a')) + ")");
                        value[j] = a[i];
                        res += Integer.bitCount(a[i]);
                    }
                    break;
                }
            }
            if (!found) {
                int id = 0;
                for (int j = 0; j < m; ++j) {
                    if (vars[j] == -1) {
                        id = j;
                        break;
                    }
                }
                vars[id] = cur;
                value[id] = a[i];
                res += Integer.bitCount(a[i]);
                result.add(((char)(id + 'a')) + "=" + a[i]);
                result.add("print(" + ((char)(id + 'a')) + ")");
            }
        }
        out.println(result.size() + " " + res);
        for (String s : result) {
            out.println(s);
        }
    }

    private int getCost(int i, int j, int[] a) {
        if (a[i] != a[j]) {
            return Integer.bitCount(a[j]);
        } else {
            return 0;
        }
    }
}

class InputReader {
    private BufferedReader reader;
    private StringTokenizer stt;

    public InputReader(InputStream stream) {
        reader = new BufferedReader(new InputStreamReader(stream));
    }

    public String nextLine() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            return null;
        }
    }

    public String nextString() {
        while (stt == null || !stt.hasMoreTokens()) {
            stt = new StringTokenizer(nextLine());
        }
        return stt.nextToken();
    }

    public int nextInt() {
        return Integer.parseInt(nextString());
    }

}

class Utils {
    public static int[] readIntArray(InputReader in, int n) {
        int[] a = new int[n];
        for (int i = 0; i < n; ++i) {
            a[i] = in.nextInt();
        }
        return a;
    }

}

class Graph {
    public int[] from;
    public int[] to;
    public int[] first;
    public int[] next;
    public int[] cost;
    public int[] capacity;
    public long[] flow;
    public int[] last;
    public int nVertex;
    public int nEdges;
    int[] reverseEdge;
    public int curEdge;

    public Graph() {}

    public void initGraphWithFlowCost(int n, int m) {
        curEdge = 0;
        nVertex = n;
        nEdges = m;
        from = new int[m * 2];
        to = new int[m * 2];
        first = new int[n];
        next = new int[m * 2];
        cost = new int[m * 2];
        capacity = new int[m * 2];
        flow = new long[m * 2];
        reverseEdge = new int[m * 2];
        last = new int[m * 2];
        Arrays.fill(first, -1);
        Arrays.fill(last, -1);
    }

    public void addFlowCostEdge(int a, int b, int f, int c) {
//        System.err.println("Added edge " + a + " - " + b + " with flow: " + f);
        next[curEdge] = first[a];
        first[a] = curEdge;
        to[curEdge] = b;
        from[curEdge] = a;
        cost[curEdge] = c;
        capacity[curEdge] = f;
        flow[curEdge] = 0;
        reverseEdge[curEdge] = curEdge + 1;
        ++curEdge;

        next[curEdge] = first[b];
        first[b] = curEdge;
        to[curEdge] = a;
        from[curEdge] = b;
        cost[curEdge] = -c;
        capacity[curEdge] = 0;
        flow[curEdge] = 0;
        reverseEdge[curEdge] = curEdge - 1;
        ++curEdge;
    }

    public boolean isSparse() {
        return nVertex == 0 || nEdges * 20 / nVertex <= nVertex;
    }
}

class MinCostMaxFlow {

    private final static long INF = Long.MAX_VALUE / 2;

    Graph g;
    int source;
    int dest;
    long[] phi;
    long[] dst;
    int n;
    Heap heap;
    boolean[] visited;
    long flow;
    int[] parent;
    long cost;

    public MinCostMaxFlow(Graph g, int source, int dest, boolean hasNegativeEdges) {
        this.flow = 0;
        this.cost = 0;
        this.g = g;
        this.source = source;
        this.dest = dest;
        this.n = g.nVertex;
        dst = new long[n];
        parent = new int[n];
        phi = new long[n];
        if (hasNegativeEdges) {
            runFordBellman();
        }
        if (g.isSparse()) {
            heap = new Heap(n, new IntComparator() {
                public int compare(int first, int second) {
                    return IntegerUtils.longCompare(dst[first], dst[second]);
                }
            }, n);
            visited = null;
        } else {
            heap = null;
            visited = new boolean[n];
        }
    }

    private void runFordBellman() {
        Arrays.fill(phi, Long.MAX_VALUE);
        phi[source] = 0;
        boolean finished = false;
        for (int i = 0; i < n && !finished; ++i) {
            finished = true;
            for (int v = 0; v < n; ++v) {
                for (int j = g.first[v]; j != -1; j = g.next[j]) {
                    int to = g.to[j];
                    long total = phi[v] + g.cost[j];
                    if (g.capacity[j] != 0 && phi[to] > total) {
                        phi[to] = total;
                        finished = false;
                    }
                }
            }
        }
    }

    void dijkstra() {
        Arrays.fill(parent, -1);
        Arrays.fill(dst, INF);
        dst[source] = 0;
        heap.add(source);
        while (!heap.isEmpty()) {
            int v = heap.poll();
            for (int eid = g.first[v]; eid != -1; eid = g.next[eid]) {
                int to = g.to[eid];
                if (g.capacity[eid] > g.flow[eid]) {
                    long total = g.cost[eid] + phi[v] - phi[to] + dst[v];
                    if (dst[to] > total) {
                        dst[to] = total;
                        if (heap.getIndex(to) == -1) {
                            heap.add(to);
                        } else {
                            heap.shiftUp(heap.getIndex(to));
                        }
                        parent[to] = eid;
                    }
                }
            }

        }
    }

    void simpleDijkstra() {
        Arrays.fill(parent, -1);
        Arrays.fill(dst, INF);
        Arrays.fill(visited, false);
        dst[source] = 0;
        for (int i = 0; i < n; ++i) {
            int v = -1;
            for (int j = 0; j < n; ++j) {
                if (!visited[j] && (v == -1 || dst[v] > dst[j])) {
                    v = j;
                }
            }
            if (v == -1 || dst[v] >= INF) {
                break;
            }
            visited[v] = true;
            for (int eid = g.first[v]; eid != -1; eid = g.next[eid]) {
                int to = g.to[eid];
                if (g.capacity[eid] > g.flow[eid]) {
                    if (!visited[to]) {
                        long total = g.cost[eid] + phi[v] - phi[to] + dst[v];
                        if (dst[to] > total) {
                            dst[to] = total;
                            parent[to] = eid;
                        }
                    }
                }
            }
        }
    }

    public static Pair<Long, Long> run(Graph g, int source, int dest, boolean hasNegativeEdges, long maxFlow) {
        return new MinCostMaxFlow(g, source, dest, hasNegativeEdges).minCostMaxFlow(maxFlow);
    }

    public Pair<Long, Long> minCostMaxFlow(long maxFlow) {
        while (maxFlow != 0) {
            if (g.isSparse()) {
                dijkstra();
            } else {
                simpleDijkstra();
            }
            if (parent[dest] == -1) {
                return Pair.makePair(flow, cost);
            }
            for (int i = 0; i < n; ++i) {
                if (dst[i] != INF) {
                    phi[i] += dst[i];
                }
            }
            int cur = dest;
            long curCost = 0;
            long min = INF;
            while (cur != source) {
                int id = parent[cur];
                min = Math.min(min, g.capacity[id] - g.flow[id]);
                curCost += g.cost[id];
                cur = g.from[id];
            }
            maxFlow -= min;
            flow += min;
            cost += min * curCost;
            cur = dest;
            while (cur != source) {
                int id = parent[cur];
                g.flow[id] += min;
                g.flow[g.reverseEdge[id]] -= min;
                cur = g.from[id];
            }
        }
        return Pair.makePair(flow, cost);
    }
}

class Pair<U, V> implements Comparable<Pair<U, V>> {
    public final U first;
    public final V second;

    public static<U, V> Pair<U, V> makePair(U first, V second) {
        return new Pair<U, V>(first, second);
    }

    public Pair(U first, V second) {
        this.first = first;
        this.second = second;
    }

    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Pair pair = (Pair) o;

        return !(first != null ? !first.equals(pair.first) : pair.first != null) && !(second != null ? !second.equals(pair.second) : pair.second != null);

    }

    public int hashCode() {
        int result = first != null ? first.hashCode() : 0;
        result = 31 * result + (second != null ? second.hashCode() : 0);
        return result;
    }

    public String toString() {
        return "(" + first + "," + second + ")";
    }

    public int compareTo(Pair<U, V> o) {
        int value = ((Comparable<U>)first).compareTo(o.first);
        if (value != 0)
            return value;
        return ((Comparable<V>)second).compareTo(o.second);
    }
}

class DisjointSet {
    int[] parent;
    int[] rank;

    public DisjointSet(int size) {
        parent = new int[size];
        rank = new int[size];
        for (int i = 0; i < size; ++i) {
            parent[i] = i;
            rank[i] = 0;
        }
    }

    public int findSet(int x) {
        if (parent[x] != x) {
            parent[x] = findSet(parent[x]);
        }
        return parent[x];
    }

    public boolean union(int x, int y) {
        x = findSet(x);
        y = findSet(y);
        if (x != y) {
            if (rank[x] < rank[y]) {
                parent[x] = y;
            } else {
                parent[y] = x;
                if (rank[x] == rank[y]) {
                    ++rank[x];
                }
            }
            return true;
        }
        return false;
    }
}

class Heap {
    private IntComparator comparator;
    private int size = 0;
    private int[] elements;
    private int[] at;

    public Heap(int capacity, IntComparator comparator, int maxElement) {
        this.comparator = comparator;
        elements = new int[capacity];
        at = new int[maxElement];
        Arrays.fill(at, -1);
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public int add(int element) {
        ensureCapacity(size + 1);
        elements[size] = element;
        at[element] = size;
        shiftUp(size++);
        return at[element];
    }

    public void shiftUp(int index) {
        int value = elements[index];
        while (index != 0) {
            int parent = (index - 1) >>> 1;
            int parentValue = elements[parent];
            if (comparator.compare(parentValue, value) <= 0) {
                elements[index] = value;
                at[value] = index;
                return;
            }
            elements[index] = parentValue;
            at[parentValue] = index;
            index = parent;
        }
        elements[0] = value;
        at[value] = 0;
    }

    public void shiftDown(int index) {
        while (true) {
            int child = (index << 1) + 1;
            if (child >= size) {
                return;
            }
            if (child + 1 < size && comparator.compare(elements[child], elements[child + 1]) > 0) {
                child++;
            }
            if (comparator.compare(elements[index], elements[child]) <= 0) {
                return;
            }
            swap(index, child);
            index = child;
        }
    }

    public int getIndex(int element) {
        return at[element];
    }

    private void swap(int first, int second) {
        int temp = elements[first];
        elements[first] = elements[second];
        elements[second] = temp;
        at[elements[first]] = first;
        at[elements[second]] = second;
    }

    private void ensureCapacity(int size) {
        if (elements.length < size) {
            int[] oldElements = elements;
            elements = new int[Math.max(2 * elements.length, size)];
            System.arraycopy(oldElements, 0, elements, 0, this.size);
        }
    }

    public int poll() {
        if (isEmpty())
            throw new IndexOutOfBoundsException();
        int result = elements[0];
        at[result] = -1;
        if (size == 1) {
            size = 0;
            return result;
        }
        elements[0] = elements[--size];
        at[elements[0]] = 0;
        shiftDown(0);
        return result;
    }

}

interface IntComparator {

    public int compare(int first, int second);
}

class IntegerUtils {

    public static int longCompare(long a, long b) {
        if (a < b) return -1;
        if (a > b) return 1;
        return 0;
    }

}