import java.util.ArrayList;

// Exception [unchecked] : peut être utilisée n'importe où.
class HoraireInvalide extends IllegalArgumentException{}

// Exceptions [checked] : doit être déclarée avec [throws] quand elle
//                        peut être levée directement ou indirectement.
class CreneauInvalide extends Exception {}

class CreneauVide extends CreneauInvalide {}

class Depassement extends CreneauInvalide {
    public Horaire hor;
    public Depassement(Horaire hor) { this.hor = hor; }
}

class NonOuvrable extends CreneauInvalide {
    public Creneau c;
    public NonOuvrable(Creneau c) { this.c = c; }
}

class Conflit extends Exception {
    public Creneau c;
    public int i;
    public Conflit(Creneau c, int i) { this.c = c; this.i = i; }
}

// Horaires (parties 1 et 2)
class Horaire implements Comparable<Horaire> {
    
    private int jour, heure;
    public int getJour() { return jour; }
    public int getHeure() { return heure; }
    
    public Horaire(int j, int h)
    {
        if (0 <= j && j < 7 && 0 <= h && h < 24) {
            this.jour = j; this.heure = h;
        } else {
            // Simplement faire
            //   throw new IllegalArgumentException();
            // fonctionne aussi, mais on peut faire remarquer
            // au passage qu'une exception qui étent celle-ci
            // n'a pas besoin d'être déclarée non plus.
            throw new HoraireInvalide();
        }
    }
    
    public int compareTo(Horaire other)
    {
        if (this.jour == other.jour) {
            return other.heure - this.heure;
        } else {
            return other.jour - this.jour;
        }
    }
    
    public Horaire ajoute(int duree) throws Depassement
    {
        if (this.heure + duree < 24) {
            return new Horaire(this.jour, this.heure + duree);
        } else {
            try {
                int d = duree - (24 - this.heure);
                return new Horaire(this.jour + d/24, d%24);
            }
            // Avantage d'avoir utilisé une exception "maison" :
            // on ne rattrape pas tout et n'importe quoi dans ce [catch].
            catch (HoraireInvalide e) { throw new Depassement(this); }
        }
    }

    // public Horaire ajouteUnsafe(int duree)
    // {
    //     if (this.heure + duree < 24) {
    //         return new Horaire(this.jour, this.heure + duree);
    //     } else {
    //         int d = duree - (24 - this.heure);
    //         return new Horaire(this.jour + d/24, d%24);
    //     }
    // }
}

// Créneaux (parties 2 et 3)
class Creneau {
    
    private Horaire hord, horf;
    public Horaire getDebut() { return hord; }
    public Horaire getFin()   { return horf; }

    // Constructeurs normaux
    public Creneau(Horaire hd, Horaire hf) throws CreneauVide
    {
        if (hd.compareTo(hf) > 1) { throw new CreneauVide(); }
        else { this.hord = hd; this.horf = hf; }
    }
    
    private Creneau(Horaire hd, int duree) throws CreneauVide, Depassement
    {
        this(hd, hd.ajoute(duree));
    }
    
    // Constructeur ne déclarant pas d'exception [CreneauVide].
    // Précondition : hd < hf
    // Le booléen ne sert qu'à assurer la gestion de la surcharge (gore)
    private Creneau(Horaire hd, Horaire hf, boolean t)
    {
        this.hord = hd;
        this.horf = hf;
    }
    
    public boolean verifOuvrable()
    {
        return (hord.getJour() == horf.getJour() &&
                hord.getJour() < 6 &&
                hord.getHeure() > 7 &&
                horf.getHeure() < 19);
    }

    // Comme lors du rattrapage on réutilise le constructeur, les
    // déclarations d'exceptions doivent rester même si elles ne peuvent
    // pas être levées dans les faits.
    public static Creneau creneauNonVide(Horaire hd, Horaire hf)
        throws CreneauVide, Depassement
    {
        try { return new Creneau(hd, hf); }
        catch (CreneauVide e) { return new Creneau(hd, 1); }
    }

    // Cette version utilise le constructeur qui ne déclare pas d'exceptions,
    // mais il y a toujours la possibilité d'un dépassement.
    // Pour ne pas déclarer [Depassement] il faudrait utiliser [ajouteUnsafe]
    // mais ce n'est pas une bonne idée car notre méthode ne vérifie pas
    // qu'il n'y a pas de dépassement.
    public static Creneau creneauNonVideBis(Horaire hd, Horaire hf)
        throws Depassement
    {
        try { return new Creneau(hd, hf); }
        catch (CreneauVide e) { return new Creneau(hd, hd.ajoute(1), true); }
    }

    public CreneauCours creneauCours(Horaire hd, Horaire hf)
        throws CreneauVide, NonOuvrable
    {
        try {
            return new CreneauCours(hd, hf);
        }
        catch (NonOuvrable e) {
            int j   = e.c.getDebut().getJour();
            int hhd = Math.max(8, e.c.getDebut().getHeure());
            int hhf =
                (e.c.getDebut().getJour() == e.c.getFin().getJour())?
                Math.min(18, e.c.getFin().getHeure()):
                18;
            return new CreneauCours(new Horaire(j, hhd), new Horaire(j, hhf));
        }
    }

    public boolean precede(Creneau c)
    {
        return this.horf.compareTo(c.getDebut()) < 0;
    }
    
    public boolean suit(Creneau c)
    {
        return this.hord.compareTo(c.getFin()) > 0;
    }
    
    // public boolean contientHoraire(Horaire h) {
    //  return this.hd.compareTo(h) < 0 && this.hf.compareTo(h) > 0;
    // }
    // public boolean precede(Horaire h) {
    //  return this.hf.compareTo(h) < 0;
    // }
    // public boolean suit(Horaire h) {
    //  return this.hd.compareTo(h) > 0;
    // }
    // public boolean conflit(Creneau c) {
    //  return this.contientHoraire(c.getDebut()) || c.contientHoraire(hd);
    // }

    public boolean estPrioritaire() { return false; }

}

class CreneauCours extends Creneau {
    
    public CreneauCours(Horaire hd, Horaire hf)
        throws CreneauVide, NonOuvrable
    {
        super(hd, hf);
        if (!this.verifOuvrable()) { throw new NonOuvrable(this); }
    }
    
    public CreneauCours(Horaire hd, int duree)
        throws CreneauVide, NonOuvrable, Depassement
    {
        this(hd, hd.ajoute(duree));
    }
    
    public boolean verifOuvrable()
    {
        return (getDebut().getJour() == getFin().getJour() &&
                getDebut().getJour() < 6 &&
                getDebut().getHeure() > 7 &&
                getFin().getHeure() < 19);
    }
    
    public boolean estPrioritaire() { return true; }
}

class AjouteCreneauOK extends Exception {}

// EdT (partie 3)
public class EdT {
    private ArrayList<Creneau> liste;
    public EdT() { liste = new ArrayList<Creneau>(); }
    
    public void ajouteCreneau(Creneau c) throws Conflit
    {
        int i=0;
        try {
            while (this.liste.get(i).precede(c)) {
                i++;
            }
            if (this.liste.get(i).suit(c)) {
                this.liste.add(i, c);
            } else {
                throw new Conflit(c, i);
            }
        }
        // Remarque : dans la boucle précédente, je ne regarde jamais si [i]
        // reste dans le tableau. Ce rattrapage d'exception intercepte
        // l'éventuelle sortie, qui correspond au cas où le créneau qu'on veut
        // ajouter est postérieur à tous les créneaux déjà présents.
        catch (IndexOutOfBoundsException e) {
            this.liste.add(i, c);
        }
    }
    
    public void ajouteCreneauAvecPriorite(Creneau c) throws Conflit
    {
        try { this.ajouteCreneau(c); }
        catch (Conflit e) {
            if (!c.estPrioritaire()) { throw e; }
            boolean conflit = false;
            int j = e.i;
            while (!this.liste.get(j).suit(c)) {
                conflit = conflit || this.liste.get(j).estPrioritaire();
                j++;
            }
            if (conflit) { throw e; }
            else {
                for (int i=e.i; i<j; i++) { this.liste.remove(i); }
                this.liste.add(e.i, c);
            }
        }
    }
}