⚠️ Warning: This is a draft ⚠️

This means it might contain formatting issues, incorrect code, conceptual problems, or other severe issues.

If you want to help to improve and eventually enable this page, please fork RosettaGit's repository and open a merge request on GitHub.

/**
 * @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);
        }
    }
}