import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

/**
* Das Sieb des Eratosthenes implementiert mit HashSet
*
* @author neubauer 14.10.2022 create
*/
public class HashSieb
{
   private static final int N = Integer.MAX_VALUE;
   private static final int START = 2;
   private static Map<Integer, HashSet<Integer>> zahlenZuTeilern = new HashMap<Integer, HashSet<Integer>>();
   private static Map<Integer, Integer> redundanteZugriffe = new HashMap<Integer, Integer>();

   public static void main(final String[] args)
   {
       initMaps();
       fillMapsNotRedundant();
       outputResults();
       initMaps();
       fillMapsRedundant();
       outputResults();
   }

   private static void outputResults()
   {
       if (!redundanteZugriffeVorhanden())
       {
           zahlenZuTeilern.forEach((zahl, teilerSet) -> {
               System.out.print(zahl + " hat folgende Teiler:\t");
               teilerSet.forEach((teiler) -> {
                   System.out.print(teiler + "\t");
               });
               System.out.println("");
           });
           System.out.println(". . . . . . . . . ");
           System.out.println("Es gab keine redundanten Schreiboperationen.");
       }
       else
       {
           System.out.println("Es wurden folgende Primzahlen ermittelt:");
           zahlenZuTeilern.forEach((zahl, teilerSet) -> {
               if (teilerSet.contains(1))
               {
                   System.out.print(zahl + " ");
               }
           });
           System.out.println("");
       }
   }

   private static boolean redundanteZugriffeVorhanden()
   {
       boolean result = false;
       for (int i = START; i < redundanteZugriffe.size(); i++)
       {
           if (redundanteZugriffe.get(i) > 0)
           {
               result = true;
               break;
           }
       }
       return result;
   }

   private static void fillMapsNotRedundant()
   {
       for (int i = START; i < N; i++)
       {
           for (int j = START; j < (i / 2); j++)
           {
               if (i % j == 0)
               {
                   if (zahlenZuTeilern.get(i).contains(j))
                   {
                       int r = redundanteZugriffe.get(i);
                       redundanteZugriffe.put(i, r++);
                   }
                   else
                   {
                       zahlenZuTeilern.get(i).add(j);
                   }
               }
           }
       }
   }

   private static void fillMapsRedundant()
   {
       for (int h = START; h < N; h++)
       {
           zahlenZuTeilern.get(h).add(1); // "true"
       }
       for (int i = START; i < N; i++)
       {
           // die Auskommentierung erzeugt die redundanten Aufrufe:
//            if (zahlenZuTeilern.get(i).contains(1))
//            {
           int temp = i;
           do
           {
               temp += i;
               if (temp < N)
               {
                   if (zahlenZuTeilern.get(i).contains(0))
                   {
                       redundanteZugriffe.put(i, redundanteZugriffe.get(i) + 1);
                   }
                   zahlenZuTeilern.get(temp).clear();
                   zahlenZuTeilern.get(temp).add(0); // ist keine Primzahl ("false")
               }
           }
           while (temp < N);
//            }
       }
   }

   private static void initMaps()
   {
       zahlenZuTeilern.clear();
       redundanteZugriffe.clear();
       for (int h = 2; h < N; h++)
       {
           zahlenZuTeilern.put(h, new HashSet<Integer>());
           redundanteZugriffe.put(h, 0);
       }
   }

}