/**
* @file GeneralFizzBuzz.java
*/
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Scanner;
/**
* This class follows the "God class" pattern.
* See {@link #main(String[])} for example usage.
*/
class FizzBuzzer
implements
Iterable<FizzBuzzer.Entry>,
Iterator<FizzBuzzer.Entry>
{
private static final long DEFAULT_START_NUM = 1;
private static final long DEFAULT_MAX_NUM = Long.MAX_VALUE;
public class Entry {
public final long num;
public final List<String> names;
private Entry(Long num, List<String> names) {
this.num = num;
this.names = names;
}
}
private Map<Integer, String> modToName = new HashMap<>();
private long currNum = DEFAULT_START_NUM;
private long maxNum = DEFAULT_MAX_NUM;
private Map<Long, List<Integer>> schedule = new HashMap<>();
public long getCurrNum() { return currNum; }
public void setCurrNum(long currNum) { this.currNum = currNum; }
public long getMaxNum() { return maxNum; }
public void setMaxNum(long maxNum) { this.maxNum = maxNum; }
private List<Integer> scheduledAt(long num) {
return schedule.computeIfAbsent(num, num_ -> new ArrayList<>());
}
private void scheduleNearest(Integer mod) {
long nearestNum = currNum + (mod - currNum % mod);
scheduledAt(nearestNum).add(mod);
}
public boolean add(Integer mod, String name) {
if (modToName.containsKey(mod)) {
return false;
}
modToName.put(mod, name);
scheduleNearest(mod);
return true;
}
public void addAll(Map<Integer, String> modToName) {
for (Map.Entry<Integer, String> modAndName : modToName.entrySet()) {
add(modAndName.getKey(), modAndName.getValue());
}
}
@Override
public Entry next() {
List<Integer> currMods = scheduledAt(currNum);
List<String> currNames = new ArrayList<>();
for (Integer m : currMods) {
String name = modToName.get(m);
currNames.add(name);
long newNum = currNum + m;
scheduledAt(newNum).add(m);
}
Entry result = new Entry(currNum, currNames);
currNum++;
return result;
}
@Override
public void remove() {
schedule.remove(currNum - 1);
}
@Override
public boolean hasNext() {
return (maxNum < 0) || (currNum <= maxNum);
}
@Override
public Iterator<Entry> iterator() {
return this;
}
public static FizzBuzzer readFrom(InputStream in) {
FizzBuzzer fizzBuzzer = new FizzBuzzer();
try (Scanner scanner = new Scanner(in)) {
long maxNum = scanner.nextLong();
fizzBuzzer.setMaxNum(maxNum);
while (scanner.hasNext()) {
Integer mod = scanner.nextInt();
scanner.skip("[\t ]*");
String name = scanner.nextLine();
fizzBuzzer.add(mod, name);
}
}
return fizzBuzzer;
}
public void printTo(OutputStream out) {
try (PrintWriter writer = new PrintWriter(out)) {
for (FizzBuzzer.Entry e : this) {
String strNames = String.join(" ", e.names);
String line = String.format("%d %s", e.num, strNames);
writer.println(line);
writer.flush();
remove();
}
}
}
@Override
public String toString() {
return String.format("%s; current %d; max %d",
modToName, currNum, maxNum);
}
}
class GeneralFizzBuzz {
public static void main(String[] args) {
try {
FizzBuzzer.readFrom(System.in).printTo(System.out);
}
catch (NoSuchElementException | IllegalStateException e) {
System.err.format("Error: %s\n", e.toString());
System.exit(1);
}
}
}