diff --git a/ABR_insert.csv b/ABR_insert.csv index c2f7b2e5cc0a1d316b38f238fee556832496a913..3e13050e69d1d57711ddf3f3b715223daeca61a5 100644 --- a/ABR_insert.csv +++ b/ABR_insert.csv @@ -1,3 +1,3 @@ n,1000,2000,5000,10000,20000,50000 -ASCENDANT,1231700,00,4418640,00,30034160,00,116455020,00,474819140,00,3056061360,00 -ALEATOIRE,149580,00,167500,00,356340,00,830320,00,1814660,00,5609920,00 +ASCENDANT,2741266,60,4242239,60,25764592,60,103848038,60,427439882,20,2674167635,60 +ALEATOIRE,296387,40,450203,40,656077,20,1670061,80,3761467,60,11957840,20 diff --git a/ABR_search.csv b/ABR_search.csv index e12ff3ce2e7f9c39cc581601408dd2085649923a..69f62c11ef53a995557c10ae388cbb295705bbe2 100644 --- a/ABR_search.csv +++ b/ABR_search.csv @@ -1,3 +1,3 @@ n,1000,2000,5000,10000,20000,50000 -ASCENDANT,2658220,00,9699160,00,60452520,00,233926280,00,907753720,00,5800536280,00 -ALEATOIRE,128340,00,122680,00,310240,00,577920,00,1311820,00,3409080,00 +ASCENDANT,4158852,60,12248819,80,76968346,20,319434800,40,1309307052,20,8234666725,00 +ALEATOIRE,266154,80,235807,80,574324,40,1102771,40,2151257,40,5866763,00 diff --git a/ARN_insert.csv b/ARN_insert.csv index afb83d219a47527e8355b50b64508e86a098def9..5ff730214b8dfe382c89f70e4c924a3d079cc449 100644 --- a/ARN_insert.csv +++ b/ARN_insert.csv @@ -1,3 +1,3 @@ n,1000,2000,5000,10000,20000,50000 -ASCENDANT,221140,00,242880,00,405760,00,720540,00,761520,00,1803680,00 -ALEATOIRE,153900,00,219500,00,457300,00,938200,00,2106160,00,6222500,00 +ASCENDANT,429293,00,541214,60,1492209,80,1450807,80,1846437,80,4429069,40 +ALEATOIRE,402464,80,359252,00,1019308,20,1808727,00,3546869,20,11937927,00 diff --git a/ARN_search.csv b/ARN_search.csv index 107032e6f5526cf8a136cb5c961e0817f73d3c5d..5664606bf0d8cbb49d3e4eb151f084fc85430d92 100644 --- a/ARN_search.csv +++ b/ARN_search.csv @@ -1,3 +1,3 @@ n,1000,2000,5000,10000,20000,50000 -ASCENDANT,136940,00,128920,00,320120,00,733100,00,1475920,00,4872060,00 -ALEATOIRE,51780,00,101540,00,308340,00,616120,00,1244160,00,3500360,00 +ASCENDANT,353458,80,290777,00,621226,20,1336956,20,2635875,60,9406446,40 +ALEATOIRE,63701,80,132267,60,378176,00,815282,80,1745943,60,5169691,20 diff --git a/pom.xml b/pom.xml index ab64bfe978840e2955ed21870007ff90ef9cfb24..78300031874a529c20446e595cfa3ac3e5f860ba 100644 --- a/pom.xml +++ b/pom.xml @@ -61,13 +61,43 @@ maven-site-plugin 3.21.0 - + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + default-prepare-agent + + prepare-agent + + + + default-report + + report + + + + default-check + + check + + + + + org.apache.maven.plugins maven-checkstyle-plugin 3.6.0 + + + google_checks.xml + + com.puppycrawl.tools @@ -102,6 +132,19 @@ maven-javadoc-plugin 3.12.0 - - + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + + report + + + + + + diff --git a/src/main/java/collection/ArbreBinaireRecherche.java b/src/main/java/collection/ArbreBinaireRecherche.java index 5945dbfc05acc30c08ea2fcc33f7a2877a40edf2..236911adc269ab17bcb8ac3f177c5b97fa24137f 100644 --- a/src/main/java/collection/ArbreBinaireRecherche.java +++ b/src/main/java/collection/ArbreBinaireRecherche.java @@ -12,261 +12,282 @@ import java.util.Stack; */ public class ArbreBinaireRecherche> implements Collection { - /** Classe interne représentant un nœud de l'arbre */ - private class Noeud { - E cle; - Noeud gauche, droit; - - Noeud(E k) { - this.cle = k; - } + /** Classe interne représentant un nœud de l'arbre */ + private class Noeud { + E cle; + Noeud gauche; + Noeud droit; + + Noeud(E k) { + this.cle = k; } + } - private Noeud racine; - private int size = 0; - - @Override - public boolean add(E e) { - Objects.requireNonNull(e); - - // Si l’arbre est vide : racine = nouvel élément - if (racine == null) { - racine = new Noeud(e); - size++; - return true; - } - - Noeud cur = racine, parent = null; - int cmp = 0; - - // Recherche de la position correcte pour insérer - while (cur != null) { - parent = cur; - cmp = e.compareTo(cur.cle); - - if (cmp < 0) - cur = cur.gauche; // aller à gauche - else if (cmp > 0) - cur = cur.droit; // aller à droite - else - return false; // élément déjà présent - } - - // Insertion du nœud - if (cmp < 0) - parent.gauche = new Noeud(e); - else - parent.droit = new Noeud(e); - - size++; - return true; + private Noeud racine; + private int taille = 0; + + @Override + public boolean add(E e) { + Objects.requireNonNull(e); + + // Si l’arbre est vide : racine = nouvel élément + if (racine == null) { + racine = new Noeud(e); + taille++; + return true; + } + + Noeud actuel = racine; + Noeud parent = null; + int cmp = 0; + + // Recherche de la position correcte pour insérer + while (actuel != null) { + parent = actuel; + cmp = e.compareTo(actuel.cle); + + if (cmp < 0) { + actuel = actuel.gauche; // aller à gauche + } else if (cmp > 0) { + actuel = actuel.droit; // aller à droite + } else { + return false; // élément déjà présent + } + } + + // Insertion du nœud + if (cmp < 0) { + parent.gauche = new Noeud(e); + } else { + parent.droit = new Noeud(e); } - /** - * Recherche d'un nœud contenant la clé donnée - */ - private Noeud findNode(E e) { - Noeud cur = racine; - - while (cur != null) { - int cmp = e.compareTo(cur.cle); - - if (cmp == 0) - return cur; - if (cmp < 0) - cur = cur.gauche; - else - cur = cur.droit; - } - return null; + taille++; + return true; + } + + /** + * Recherche d'un nœud contenant la clé donnée + */ + private Noeud findNode(E e) { + Noeud actuel = racine; + + while (actuel != null) { + int cmp = e.compareTo(actuel.cle); + + if (cmp == 0) { + return actuel; + } + if (cmp < 0) { + actuel = actuel.gauche; + } else { + actuel = actuel.droit; + } } + return null; + } - @Override - public boolean contains(Object o) { - if (o == null) - return false; - - try { - @SuppressWarnings("unchecked") - E e = (E) o; - return findNode(e) != null; - } catch (ClassCastException ex) { - return false; - } + @Override + public boolean contains(Object o) { + if (o == null) { + return false; } - @Override - public boolean remove(Object o) { - if (o == null) - return false; - - try { - @SuppressWarnings("unchecked") - E e = (E) o; - } catch (ClassCastException ex) { - return false; - } - - // Suppression simple (remplacement par successeur) - // (Pour les benchmarks, remove n'est pas essentiel) - E key = (E) o; - Noeud parent = null, cur = racine; - - // Recherche du nœud à supprimer - while (cur != null && !cur.cle.equals(key)) { - parent = cur; - if (key.compareTo(cur.cle) < 0) - cur = cur.gauche; - else - cur = cur.droit; - } - - if (cur == null) - return false; - - // Cas où le nœud a deux enfants - if (cur.gauche != null && cur.droit != null) { - Noeud succParent = cur, succ = cur.droit; - - // Successeur = minimum du sous-arbre droit - while (succ.gauche != null) { - succParent = succ; - succ = succ.gauche; - } - - // Remplacer la clé - cur.cle = succ.cle; - parent = succParent; - cur = succ; - } - - // Cas 0 ou 1 enfant - Noeud child = (cur.gauche != null) ? cur.gauche : cur.droit; - - if (parent == null) - racine = child; - else if (parent.gauche == cur) - parent.gauche = child; - else - parent.droit = child; - - size--; - return true; + try { + @SuppressWarnings("unchecked") + E e = (E) o; + return findNode(e) != null; + } catch (ClassCastException ex) { + return false; } + } - @Override - public Iterator iterator() { - - // Parcours en ordre croissant (in-order) - return new Iterator() { - private final Stack st = init(racine); - - private Stack init(Noeud r) { - Stack s = new Stack<>(); - Noeud c = r; - - while (c != null) { - s.push(c); - c = c.gauche; - } - return s; - } - - @Override - public boolean hasNext() { - return !st.isEmpty(); - } - - @Override - public E next() { - Noeud n = st.pop(); - E res = n.cle; - Noeud c = n.droit; - - while (c != null) { - st.push(c); - c = c.gauche; - } - - return res; - } - }; + @Override + public boolean remove(Object o) { + if (o == null) { + return false; } - @Override - public int size() { - return size; + try { + @SuppressWarnings("unchecked") + E e = (E) o; + } catch (ClassCastException ex) { + return false; } - @Override - public boolean isEmpty() { - return size == 0; + // Suppression simple (remplacement par successeur) + // (Pour les benchmarks, remove n'est pas essentiel) + E key = (E) o; + Noeud parent = null; + Noeud actuel = racine; + + // Recherche du nœud à supprimer + while (actuel != null && !actuel.cle.equals(key)) { + parent = actuel; + if (key.compareTo(actuel.cle) < 0) { + actuel = actuel.gauche; + } else { + actuel = actuel.droit; + } } - @Override - public void clear() { - racine = null; - size = 0; + if (actuel == null) { + return false; } - @Override - public Object[] toArray() { - Object[] arr = new Object[size]; - int i = 0; + // Cas où le nœud a deux enfants + if (actuel.gauche != null && actuel.droit != null) { + Noeud succParent = actuel; + Noeud suivant = actuel.droit; + + // Successeur = minimum du sous-arbre droit + while (suivant.gauche != null) { + succParent = suivant; + suivant = suivant.gauche; + } + + // Remplacer la clé + actuel.cle = suivant.cle; + parent = succParent; + actuel = suivant; + } - for (E e : this) - arr[i++] = e; + // Cas 0 ou 1 enfant + Noeud child = (actuel.gauche != null) ? actuel.gauche : actuel.droit; - return arr; + if (parent == null) { + racine = child; + } else if (parent.gauche == actuel) { + parent.gauche = child; + } else { + parent.droit = child; } - @Override - public T[] toArray(T[] a) { - return null; // même logique que RBTree ; omis pour brièveté + taille--; + return true; + } + + @Override + public Iterator iterator() { + + // Parcours en ordre croissant (in-order) + return new Iterator() { + private final Stack st = init(racine); + + private Stack init(Noeud r) { + Stack s = new Stack<>(); + Noeud c = r; + + while (c != null) { + s.push(c); + c = c.gauche; + } + return s; + } + + @Override + public boolean hasNext() { + return !st.isEmpty(); + } + + @Override + public E next() { + Noeud n = st.pop(); + E res = n.cle; + Noeud c = n.droit; + + while (c != null) { + st.push(c); + c = c.gauche; + } + + return res; + } + }; + } + + @Override + public int size() { + return taille; + } + + @Override + public boolean isEmpty() { + return taille == 0; + } + + @Override + public void clear() { + racine = null; + taille = 0; + } + + @Override + public Object[] toArray() { + Object[] arr = new Object[taille]; + int i = 0; + + for (E e : this) { + arr[i++] = e; } - @Override - public boolean containsAll(Collection c) { - for (Object o : c) - if (!contains(o)) - return false; + return arr; + } + + @Override + public T[] toArray(T[] a) { + return null; // même logique que RBTree ; omis pour brièveté + } - return true; + @Override + public boolean containsAll(Collection c) { + for (Object o : c) { + if (!contains(o)) { + return false; + } } - @Override - public boolean addAll(Collection c) { - boolean ch = false; + return true; + } - for (E e : c) - ch |= add(e); + @Override + public boolean addAll(Collection c) { + boolean ch = false; - return ch; + for (E e : c) { + ch |= add(e); } - @Override - public boolean removeAll(Collection c) { - boolean ch = false; + return ch; + } - for (Object o : c) - ch |= remove(o); + @Override + public boolean removeAll(Collection c) { + boolean ch = false; - return ch; + for (Object o : c) { + ch |= remove(o); } - @Override - public boolean retainAll(Collection c) { - List del = new ArrayList<>(); + return ch; + } - // Construire la liste des éléments à supprimer - for (E e : this) - if (!c.contains(e)) - del.add(e); + @Override + public boolean retainAll(Collection c) { + List del = new ArrayList<>(); - // Supprimer les éléments - for (E e : del) - remove(e); + // Construire la liste des éléments à supprimer + for (E e : this) { + if (!c.contains(e)) { + del.add(e); + } + } - return !del.isEmpty(); + // Supprimer les éléments + for (E e : del) { + remove(e); } + + return !del.isEmpty(); + } } diff --git a/src/main/java/collection/Benchmark.java b/src/main/java/collection/Benchmark.java index 40c158cee98d1238aa087f938ae4bcf7a0b9b5a7..ef747c7c7d52f3be0ede4ae5a5d463906b79fc9a 100644 --- a/src/main/java/collection/Benchmark.java +++ b/src/main/java/collection/Benchmark.java @@ -11,128 +11,136 @@ import java.util.Random; public class Benchmark { - private static final Random RNG = new Random(12345); + private static final Random RNG = new Random(12345); - public static void main(final String[] args) throws Exception { + public static void main(final String[] args) throws Exception { - final int[] N_LIST = { 1000, 2000, 5000, 10000, 20000, 50000 }; - final int REP = 5; + final int[] nList = { 1000, 2000, 5000, 10000, 20000, 50000 }; + final int repetition = 5; - // --- Create the 4 CSV writers --- - PrintWriter abrInsert = new PrintWriter(new FileWriter("ABR_insert.csv")); - PrintWriter abrSearch = new PrintWriter(new FileWriter("ABR_search.csv")); - PrintWriter arnInsert = new PrintWriter(new FileWriter("ARN_insert.csv")); - PrintWriter arnSearch = new PrintWriter(new FileWriter("ARN_search.csv")); + // --- Creation des 4 CSV avec leur enTete --- + PrintWriter abrInsert = new PrintWriter(new FileWriter("ABR_insert.csv")); + writeHeader(abrInsert, nList); - // Header line : n,1000,2000,... - writeHeader(abrInsert, N_LIST); - writeHeader(abrSearch, N_LIST); - writeHeader(arnInsert, N_LIST); - writeHeader(arnSearch, N_LIST); + PrintWriter abrSearch = new PrintWriter(new FileWriter("ABR_search.csv")); + writeHeader(abrSearch, nList); - // Scenarios - List scenarios = Arrays.asList("ASCENDANT", "ALEATOIRE"); + PrintWriter arnInsert = new PrintWriter(new FileWriter("ARN_insert.csv")); + writeHeader(arnInsert, nList); - // Process each structure - for (String structure : Arrays.asList("ABR", "ARN")) { + PrintWriter arnSearch = new PrintWriter(new FileWriter("ARN_search.csv")); + writeHeader(arnSearch, nList); - for (String scenario : scenarios) { + // Scenarios + List scenarios = Arrays.asList("ASCENDANT", "ALEATOIRE"); - // Buffers for one line of insert & search results - List insertLine = new ArrayList<>(); - List searchLine = new ArrayList<>(); + // Process each structure + for (String structure : Arrays.asList("ABR", "ARN")) { - for (int n : N_LIST) { + for (String scenario : scenarios) { - long insertSum = 0; - long searchSum = 0; + // Buffers for one line of insert & search results + List insertLine = new ArrayList<>(); + List searchLine = new ArrayList<>(); - for (int r = 0; r < REP; r++) { + for (int n : nList) { - List keys = new ArrayList<>(); - for (int i = 0; i < n; i++) - keys.add(i); + long insertSum = 0; + long searchSum = 0; - if (scenario.equals("ALEATOIRE")) { - Collections.shuffle(keys, new Random(RNG.nextLong())); - } + for (int r = 0; r < repetition; r++) { - long[] result = structure.equals("ABR") ? benchmarkABR(keys, n) : benchmarkARN(keys, n); + List keys = new ArrayList<>(); + for (int i = 0; i < n; i++) { + keys.add(i); + } - insertSum += result[0]; - searchSum += result[1]; - } + if (scenario.equals("ALEATOIRE")) { + Collections.shuffle(keys, new Random(RNG.nextLong())); + } - insertLine.add(insertSum / (double) REP); - searchLine.add(searchSum / (double) REP); + long[] result = structure.equals("ABR") ? benchmarkAbr(keys, n) : benchmarkArn(keys, n); - System.out.printf("Done %s %s n=%d\n", structure, scenario, n); - } + insertSum += result[0]; + searchSum += result[1]; + } - // Write to correct CSV - if (structure.equals("ABR")) { - writeScenarioLine(abrInsert, scenario, insertLine); - writeScenarioLine(abrSearch, scenario, searchLine); - } else { - writeScenarioLine(arnInsert, scenario, insertLine); - writeScenarioLine(arnSearch, scenario, searchLine); - } - } - } + insertLine.add(insertSum / (double) repetition); + searchLine.add(searchSum / (double) repetition); - abrInsert.close(); - abrSearch.close(); - arnInsert.close(); - arnSearch.close(); + System.out.printf("Done %s %s n=%d\n", structure, scenario, n); + } - System.out.println("4 CSV créés au format tableau."); + // Write to correct CSV + if (structure.equals("ABR")) { + writeScenarioLine(abrInsert, scenario, insertLine); + writeScenarioLine(abrSearch, scenario, searchLine); + } else { + writeScenarioLine(arnInsert, scenario, insertLine); + writeScenarioLine(arnSearch, scenario, searchLine); + } + } } - // ------------------------------------------------------- - // Helper: header line - // ------------------------------------------------------- - private static void writeHeader(PrintWriter pw, int[] nList) { - pw.print("n"); - for (int n : nList) - pw.print("," + n); - pw.println(); + abrInsert.close(); + abrSearch.close(); + arnInsert.close(); + arnSearch.close(); + + System.out.println("4 CSV créés au format tableau."); + } + + // ------------------------------------------------------- + // Helper: header line + // ------------------------------------------------------- + private static void writeHeader(PrintWriter pw, int[] nlist) { + pw.print("n"); + for (int n : nlist) { + pw.print("," + n); } - // ------------------------------------------------------- - // Helper: write one scenario line - // ------------------------------------------------------- - private static void writeScenarioLine(PrintWriter pw, String scenario, List values) { - pw.print(scenario); - for (double v : values) - pw.printf(",%.2f", v); - pw.println(); + pw.println(); + } + + // ------------------------------------------------------- + // Helper: write one scenario line + // ------------------------------------------------------- + private static void writeScenarioLine(PrintWriter pw, String scenario, List values) { + pw.print(scenario); + for (double v : values) { + pw.printf(",%.2f", v); } - // ------------------------------------------------------- - // Benchmark ABR / ARN - // ------------------------------------------------------- + pw.println(); + } - private static long[] benchmarkABR(List keys, int n) { - Collection tree = new ArbreBinaireRecherche<>(); - return runBenchmark(tree, keys, n); - } + // ------------------------------------------------------- + // Benchmark ABR / ARN + // ------------------------------------------------------- - private static long[] benchmarkARN(List keys, int n) { - Collection tree = new RedBlackTree<>(); - return runBenchmark(tree, keys, n); - } + private static long[] benchmarkAbr(List keys, int n) { + Collection tree = new ArbreBinaireRecherche<>(); + return runBenchmark(tree, keys, n); + } - private static long[] runBenchmark(Collection tree, List keys, int n) { - long t0 = System.nanoTime(); - for (Integer k : keys) - tree.add(k); - long t1 = System.nanoTime(); + private static long[] benchmarkArn(List keys, int n) { + Collection tree = new RedBlackTree<>(); + return runBenchmark(tree, keys, n); + } - long t2 = System.nanoTime(); - for (int x = 0; x < 2 * n; x++) - tree.contains(x); - long t3 = System.nanoTime(); + private static long[] runBenchmark(Collection tree, List keys, int n) { + long t0 = System.nanoTime(); + for (Integer k : keys) { + tree.add(k); + } + long t1 = System.nanoTime(); - return new long[] { t1 - t0, t3 - t2 }; + long t2 = System.nanoTime(); + for (int x = 0; x < 2 * n; x++) { + tree.contains(x); } + long t3 = System.nanoTime(); + + return new long[] { t1 - t0, t3 - t2 }; + } } diff --git a/src/main/java/collection/RedBlackTree.java b/src/main/java/collection/RedBlackTree.java index 83047397cf03823988a0a9a4c819a25255eb832e..98c909367683644023e7df9cd54f88e1e6a40fcb 100644 --- a/src/main/java/collection/RedBlackTree.java +++ b/src/main/java/collection/RedBlackTree.java @@ -16,497 +16,543 @@ import java.util.Stack; * contiennent le même nombre de nœuds noirs */ public class RedBlackTree> implements Collection { - private static final boolean RED = true; - private static final boolean BLACK = false; - - /** Classe interne représentant un nœud de l'arbre */ - private class Node { - E key; - Node left, right, parent; - boolean color = RED; // par défaut, un nouveau nœud est rouge - - Node(E key, Node parent) { - this.key = key; - this.parent = parent; - } + private static final boolean ROUGE = true; + private static final boolean NOIR = false; + + /** Classe interne représentant un nœud de l'arbre */ + private class Noeud { + E cle; + Noeud gauche; + Noeud droit; + Noeud parent; + boolean color = ROUGE; // par défaut, un nouveau nœud est rouge + + Noeud(E cle, Noeud parent) { + this.cle = cle; + this.parent = parent; } + } - private Node root; - private int size = 0; + private Noeud racine; + private int taille = 0; - /** Rotation gauche standard */ - private void rotateLeft(Node x) { - Node y = x.right; - x.right = y.left; + /** Rotation gauche standard */ + private void rotateLeft(Noeud x) { + Noeud y = x.droit; + x.droit = y.gauche; - if (y.left != null) - y.left.parent = x; - - y.parent = x.parent; + if (y.gauche != null) { + y.gauche.parent = x; + } - if (x.parent == null) - root = y; - else if (x == x.parent.left) - x.parent.left = y; - else - x.parent.right = y; + y.parent = x.parent; - y.left = x; - x.parent = y; + if (x.parent == null) { + racine = y; + } else if (x == x.parent.gauche) { + x.parent.gauche = y; + } else { + x.parent.droit = y; } - /** Rotation droite standard */ - private void rotateRight(Node x) { - Node y = x.left; - x.left = y.right; + y.gauche = x; + x.parent = y; + } - if (y.right != null) - y.right.parent = x; + /** Rotation droite standard */ + private void rotateRight(Noeud x) { + Noeud y = x.gauche; + x.gauche = y.droit; - y.parent = x.parent; + if (y.droit != null) { + y.droit.parent = x; + } - if (x.parent == null) - root = y; - else if (x == x.parent.right) - x.parent.right = y; - else - x.parent.left = y; + y.parent = x.parent; - y.right = x; - x.parent = y; + if (x.parent == null) { + racine = y; + } else if (x == x.parent.droit) { + x.parent.droit = y; + } else { + x.parent.gauche = y; } - /** - * Correction après insertion. Assure que les propriétés RB sont conservées. - */ - private void fixAfterInsert(Node z) { - while (z.parent != null && z.parent.color == RED) { - if (z.parent == z.parent.parent.left) { - Node y = z.parent.parent.right; // oncle - - if (y != null && y.color == RED) { - // Cas 1 : parent rouge + oncle rouge -> recolorations - z.parent.color = BLACK; - y.color = BLACK; - z.parent.parent.color = RED; - z = z.parent.parent; - } else { - if (z == z.parent.right) { - // Cas 2 : triangle -> rotation gauche - z = z.parent; - rotateLeft(z); - } - - // Cas 3 : ligne -> rotation droite - z.parent.color = BLACK; - z.parent.parent.color = RED; - rotateRight(z.parent.parent); - } - } else { - // Symétrique : parent est enfant droit - Node y = z.parent.parent.left; - - if (y != null && y.color == RED) { - z.parent.color = BLACK; - y.color = BLACK; - z.parent.parent.color = RED; - z = z.parent.parent; - } else { - if (z == z.parent.left) { - z = z.parent; - rotateRight(z); - } - z.parent.color = BLACK; - z.parent.parent.color = RED; - rotateLeft(z.parent.parent); - } - } - } - - root.color = BLACK; + y.droit = x; + x.parent = y; + } + + /** + * Correction après insertion. Assure que les propriétés RB sont conservées. + */ + private void fixAfterInsert(Noeud z) { + while (z.parent != null && z.parent.color == ROUGE) { + if (z.parent == z.parent.parent.gauche) { + Noeud y = z.parent.parent.droit; // oncle + + if (y != null && y.color == ROUGE) { + // Cas 1 : parent rouge + oncle rouge -> recolorations + z.parent.color = NOIR; + y.color = NOIR; + z.parent.parent.color = ROUGE; + z = z.parent.parent; + } else { + if (z == z.parent.droit) { + // Cas 2 : triangle -> rotation gauche + z = z.parent; + rotateLeft(z); + } + + // Cas 3 : ligne -> rotation droite + z.parent.color = NOIR; + z.parent.parent.color = ROUGE; + rotateRight(z.parent.parent); + } + } else { + // Symétrique : parent est enfant droit + Noeud y = z.parent.parent.gauche; + + if (y != null && y.color == ROUGE) { + z.parent.color = NOIR; + y.color = NOIR; + z.parent.parent.color = ROUGE; + z = z.parent.parent; + } else { + if (z == z.parent.gauche) { + z = z.parent; + rotateRight(z); + } + z.parent.color = NOIR; + z.parent.parent.color = ROUGE; + rotateLeft(z.parent.parent); + } + } } - /** Remplace un nœud par un autre dans l’arbre */ - private void transplant(Node u, Node v) { - if (u.parent == null) - root = v; - else if (u == u.parent.left) - u.parent.left = v; - else - u.parent.right = v; - - if (v != null) - v.parent = u.parent; + racine.color = NOIR; + } + + /** Remplace un nœud par un autre dans l’arbre */ + private void transplant(Noeud u, Noeud v) { + if (u.parent == null) { + racine = v; + } else if (u == u.parent.gauche) { + u.parent.gauche = v; + } else { + u.parent.droit = v; } - /** Renvoie le minimum du sous-arbre */ - private Node minimum(Node x) { - while (x.left != null) - x = x.left; - return x; + if (v != null) { + v.parent = u.parent; } + } - /** - * Correction après suppression. Restaure les propriétés Rouge-Noir. - */ - private void fixAfterDelete(Node x, Node parent) { - while ((x != root) && (x == null || x.color == BLACK)) { - if (parent != null && x == parent.left) { - Node w = parent.right; - - if (w != null && w.color == RED) { - w.color = BLACK; - parent.color = RED; - rotateLeft(parent); - w = parent.right; - } - - if ((w == null) - || ((w.left == null || w.left.color == BLACK) && (w.right == null || w.right.color == BLACK))) { - if (w != null) - w.color = RED; - - x = parent; - parent = x.parent; - } else { - if (w.right == null || w.right.color == BLACK) { - if (w.left != null) - w.left.color = BLACK; - - w.color = RED; - rotateRight(w); - w = parent.right; - } - - if (w != null) - w.color = parent.color; - - parent.color = BLACK; - - if (w != null && w.right != null) - w.right.color = BLACK; - - rotateLeft(parent); - x = root; - break; - } - } else { - if (parent == null) - break; - - Node w = parent.left; - - if (w != null && w.color == RED) { - w.color = BLACK; - parent.color = RED; - rotateRight(parent); - w = parent.left; - } - - if ((w == null) - || ((w.right == null || w.right.color == BLACK) && (w.left == null || w.left.color == BLACK))) { - if (w != null) - w.color = RED; - - x = parent; - parent = x.parent; - } else { - if (w.left == null || w.left.color == BLACK) { - if (w.right != null) - w.right.color = BLACK; - - w.color = RED; - rotateLeft(w); - w = parent.left; - } - - if (w != null) - w.color = parent.color; - - parent.color = BLACK; - - if (w != null && w.left != null) - w.left.color = BLACK; - - rotateRight(parent); - x = root; - break; - } - } - } - - if (x != null) - x.color = BLACK; + /** Renvoie le minimum du sous-arbre */ + private Noeud minimum(Noeud x) { + while (x.gauche != null) { + x = x.gauche; } - @Override - public boolean add(E e) { - Objects.requireNonNull(e); - - // Arbre vide : nouvelle racine noire - if (root == null) { - root = new Node(e, null); - root.color = BLACK; - size = 1; - return true; - } - - Node cur = root, parent = null; - int cmp = 0; - - // Recherche de la position d'insertion - while (cur != null) { - parent = cur; - cmp = e.compareTo(cur.key); - - if (cmp < 0) - cur = cur.left; - else if (cmp > 0) - cur = cur.right; - else - return false; // pas de doublons - } - - Node node = new Node(e, parent); - - if (cmp < 0) - parent.left = node; - else - parent.right = node; - - // Correction RB après insertion - fixAfterInsert(node); - size++; - - return true; + return x; + } + + /** + * Correction après suppression. Restaure les propriétés Rouge-Noir. + */ + private void fixAfterDelete(Noeud x, Noeud parent) { + while ((x != racine) && (x == null || x.color == NOIR)) { + if (parent != null && x == parent.gauche) { + Noeud w = parent.droit; + + if (w != null && w.color == ROUGE) { + w.color = NOIR; + parent.color = ROUGE; + rotateLeft(parent); + w = parent.droit; + } + + if ((w == null) || ((w.gauche == null || w.gauche.color == NOIR) + && (w.droit == null || w.droit.color == NOIR))) { + if (w != null) { + w.color = ROUGE; + } + + x = parent; + parent = x.parent; + } else { + if (w.droit == null || w.droit.color == NOIR) { + if (w.gauche != null) { + w.gauche.color = NOIR; + } + + w.color = ROUGE; + rotateRight(w); + w = parent.droit; + } + + if (w != null) { + w.color = parent.color; + } + + parent.color = NOIR; + + if (w != null && w.droit != null) { + w.droit.color = NOIR; + } + + rotateLeft(parent); + x = racine; + break; + } + } else { + if (parent == null) { + break; + } + + Noeud w = parent.gauche; + + if (w != null && w.color == ROUGE) { + w.color = NOIR; + parent.color = ROUGE; + rotateRight(parent); + w = parent.gauche; + } + + if ((w == null) || ((w.droit == null || w.droit.color == NOIR) + && (w.gauche == null || w.gauche.color == NOIR))) { + if (w != null) { + w.color = ROUGE; + } + + x = parent; + parent = x.parent; + } else { + if (w.gauche == null || w.gauche.color == NOIR) { + if (w.droit != null) { + w.droit.color = NOIR; + } + + w.color = ROUGE; + rotateLeft(w); + w = parent.gauche; + } + + if (w != null) { + w.color = parent.color; + } + + parent.color = NOIR; + + if (w != null && w.gauche != null) { + w.gauche.color = NOIR; + } + + rotateRight(parent); + x = racine; + break; + } + } } - /** Recherche un nœud contenant la clé */ - private Node findNode(E e) { - Node cur = root; + if (x != null) { + x.color = NOIR; + } + } + + @Override + public boolean add(E e) { + Objects.requireNonNull(e); + + // Arbre vide : nouvelle racine noire + if (racine == null) { + racine = new Noeud(e, null); + racine.color = NOIR; + taille = 1; + return true; + } - while (cur != null) { - int cmp = e.compareTo(cur.key); + Noeud cur = racine; + Noeud parent = null; + int cmp = 0; + + // Recherche de la position d'insertion + while (cur != null) { + parent = cur; + cmp = e.compareTo(cur.cle); + + if (cmp < 0) { + cur = cur.gauche; + } else if (cmp > 0) { + cur = cur.droit; + } else { + return false; // pas de doublons + } + } - if (cmp == 0) - return cur; - if (cmp < 0) - cur = cur.left; - else - cur = cur.right; - } + Noeud node = new Noeud(e, parent); - return null; + if (cmp < 0) { + parent.gauche = node; + } else { + parent.droit = node; } - @Override - public boolean contains(Object o) { - if (o == null) - return false; - try { - @SuppressWarnings("unchecked") - E e = (E) o; - return findNode(e) != null; - } catch (ClassCastException ex) { - return false; - } + // Correction RB après insertion + fixAfterInsert(node); + taille++; + + return true; + } + + /** Recherche un nœud contenant la clé */ + private Noeud findNode(E e) { + Noeud cur = racine; + + while (cur != null) { + int cmp = e.compareTo(cur.cle); + + if (cmp == 0) { + return cur; + } + if (cmp < 0) { + cur = cur.gauche; + } else { + cur = cur.droit; + } } - @Override - public boolean remove(Object o) { - if (o == null) - return false; - - E e; - - try { - e = (E) o; - } catch (ClassCastException ex) { - return false; - } - - Node z = findNode(e); - - if (z == null) - return false; - - Node y = z; - boolean yOriginalColor = y.color; - Node x; - Node xParent; - - // Cas 1 : un seul enfant - if (z.left == null) { - x = z.right; - xParent = z.parent; - transplant(z, z.right); - } else if (z.right == null) { - x = z.left; - xParent = z.parent; - transplant(z, z.left); - } - - // Cas 2 : deux enfants → successeur - else { - y = minimum(z.right); - yOriginalColor = y.color; - x = y.right; - - if (y.parent == z) { - if (x != null) - x.parent = y; - xParent = y; - } else { - transplant(y, y.right); - y.right = z.right; - - if (y.right != null) - y.right.parent = y; - - xParent = y.parent; - } - - transplant(z, y); - y.left = z.left; - - if (y.left != null) - y.left.parent = y; - - y.color = z.color; - } - - if (yOriginalColor == BLACK) - fixAfterDelete(x, xParent); - - size--; - return true; + return null; + } + + @Override + public boolean contains(Object o) { + if (o == null) { + return false; } - @Override - public Iterator iterator() { - return new Iterator() { - private final Stack stack = initStack(root); - - private Stack initStack(Node r) { - Stack s = new Stack<>(); - Node cur = r; - - while (cur != null) { - s.push(cur); - cur = cur.left; - } - - return s; - } - - @Override - public boolean hasNext() { - return !stack.isEmpty(); - } - - @Override - public E next() { - if (!hasNext()) - throw new NoSuchElementException(); - - Node n = stack.pop(); - E res = n.key; - Node cur = n.right; - - // Descente maximale à gauche - while (cur != null) { - stack.push(cur); - cur = cur.left; - } - - return res; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; + try { + @SuppressWarnings("unchecked") + E e = (E) o; + return findNode(e) != null; + } catch (ClassCastException ex) { + return false; } + } - @Override - public int size() { - return size; + @Override + public boolean remove(Object o) { + if (o == null) { + return false; } - @Override - public boolean isEmpty() { - return size == 0; + E e; + + try { + e = (E) o; + } catch (ClassCastException ex) { + return false; } - @Override - public void clear() { - root = null; - size = 0; + Noeud z = findNode(e); + + if (z == null) { + return false; } - @Override - public Object[] toArray() { - Object[] arr = new Object[size]; - int i = 0; + Noeud y = z; + boolean yorigColor = y.color; + Noeud x; + Noeud xparent; + + // Cas 1 : un seul enfant + if (z.gauche == null) { + x = z.droit; + xparent = z.parent; + transplant(z, z.droit); + } else if (z.droit == null) { + x = z.gauche; + xparent = z.parent; + transplant(z, z.gauche); + } else { // Cas 2 : deux enfants → successeur + y = minimum(z.droit); + yorigColor = y.color; + x = y.droit; + + if (y.parent == z) { + if (x != null) { + x.parent = y; + } + + xparent = y; + } else { + transplant(y, y.droit); + y.droit = z.droit; + + if (y.droit != null) { + y.droit.parent = y; + } + + xparent = y.parent; + } + + transplant(z, y); + y.gauche = z.gauche; + + if (y.gauche != null) { + y.gauche.parent = y; + } + + y.color = z.color; + } - for (E e : this) - arr[i++] = e; + if (yorigColor == NOIR) { + fixAfterDelete(x, xparent); + } - return arr; + taille--; + return true; + } + + @Override + public Iterator iterator() { + return new Iterator() { + private final Stack stack = initStack(racine); + + private Stack initStack(Noeud r) { + Stack s = new Stack<>(); + Noeud cur = r; + + while (cur != null) { + s.push(cur); + cur = cur.gauche; + } + + return s; + } + + @Override + public boolean hasNext() { + return !stack.isEmpty(); + } + + @Override + public E next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + Noeud n = stack.pop(); + E res = n.cle; + Noeud cur = n.droit; + + // Descente maximale à gauche + while (cur != null) { + stack.push(cur); + cur = cur.gauche; + } + + return res; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override + public int size() { + return taille; + } + + @Override + public boolean isEmpty() { + return taille == 0; + } + + @Override + public void clear() { + racine = null; + taille = 0; + } + + @Override + public Object[] toArray() { + Object[] arr = new Object[taille]; + int i = 0; + + for (E e : this) { + arr[i++] = e; } - @Override - public T[] toArray(T[] a) { - if (a.length < size) - a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size); + return arr; + } - int i = 0; + @Override + public T[] toArray(T[] a) { + if (a.length < taille) { + a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), taille); + } - for (E e : this) - a[i++] = (T) e; + int i = 0; - if (a.length > size) - a[size] = null; + for (E e : this) { + a[i++] = (T) e; + } - return a; + if (a.length > taille) { + a[taille] = null; } - @Override - public boolean containsAll(Collection c) { - for (Object o : c) - if (!contains(o)) - return false; - return true; + return a; + } + + @Override + public boolean containsAll(Collection c) { + for (Object o : c) { + if (!contains(o)) { + return false; + } } - @Override - public boolean addAll(Collection c) { - boolean changed = false; - for (E e : c) - changed |= add(e); - return changed; + return true; + } + + @Override + public boolean addAll(Collection c) { + boolean changed = false; + for (E e : c) { + changed |= add(e); } - @Override - public boolean removeAll(Collection c) { - boolean changed = false; - for (Object o : c) - changed |= remove(o); - return changed; + return changed; + } + + @Override + public boolean removeAll(Collection c) { + boolean changed = false; + for (Object o : c) { + changed |= remove(o); } - @Override - public boolean retainAll(Collection c) { - List toRemove = new ArrayList<>(); + return changed; + } - for (E e : this) - if (!c.contains(e)) - toRemove.add(e); + @Override + public boolean retainAll(Collection c) { + List toRemove = new ArrayList<>(); - for (E e : toRemove) - remove(e); + for (E e : this) { + if (!c.contains(e)) { + toRemove.add(e); + } + } - return !toRemove.isEmpty(); + for (E e : toRemove) { + remove(e); } + + return !toRemove.isEmpty(); + } } diff --git a/src/test/java/collection/ArbreBinaireRechercheTest.java b/src/test/java/collection/ArbreBinaireRechercheTest.java new file mode 100644 index 0000000000000000000000000000000000000000..66c29952789cf189b5fd28e40bae56b8944d6474 --- /dev/null +++ b/src/test/java/collection/ArbreBinaireRechercheTest.java @@ -0,0 +1,308 @@ +package collection; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ArbreBinaireRechercheTest { + + private ArbreBinaireRecherche arbre; + + @BeforeEach + void setUp() { + arbre = new ArbreBinaireRecherche<>(); + } + + @Test + void testAdd() { + assertTrue(arbre.add(5)); + assertTrue(arbre.add(3)); + assertTrue(arbre.add(7)); + assertFalse(arbre.add(5)); // doublon + + assertEquals(3, arbre.size()); + } + + @Test + void testContains() { + arbre.add(5); + arbre.add(2); + arbre.add(8); + + assertTrue(arbre.contains(5)); + assertTrue(arbre.contains(2)); + assertFalse(arbre.contains(10)); + assertFalse(arbre.contains(null)); + } + + @Test + void testRemoveFeuille() { + arbre.add(5); + arbre.add(3); + arbre.add(8); + + assertTrue(arbre.remove(3)); + assertFalse(arbre.contains(3)); + assertEquals(2, arbre.size()); + } + + @Test + void testRemoveNoeudAvecUnEnfant() { + arbre.add(5); + arbre.add(3); + arbre.add(2); // enfant de 3 + + assertTrue(arbre.remove(3)); + assertTrue(arbre.contains(2)); + assertFalse(arbre.contains(3)); + assertEquals(2, arbre.size()); + } + + @Test + void testRemoveNoeudDeuxEnfants() { + arbre.add(5); + arbre.add(3); + arbre.add(7); + arbre.add(6); + arbre.add(8); + + assertTrue(arbre.remove(7)); + assertFalse(arbre.contains(7)); + assertEquals(4, arbre.size()); + } + + @Test + void testIteratorOrdreCroissant() { + arbre.add(5); + arbre.add(2); + arbre.add(8); + arbre.add(1); + arbre.add(3); + + Iterator it = arbre.iterator(); + + assertEquals(1, it.next()); + assertEquals(2, it.next()); + assertEquals(3, it.next()); + assertEquals(5, it.next()); + assertEquals(8, it.next()); + assertFalse(it.hasNext()); + } + + @Test + void testClear() { + arbre.add(4); + arbre.add(1); + arbre.add(9); + + arbre.clear(); + + assertTrue(arbre.isEmpty()); + assertEquals(0, arbre.size()); + assertFalse(arbre.contains(4)); + } + + @Test + void testToArray() { + arbre.add(3); + arbre.add(1); + arbre.add(2); + + Object[] arr = arbre.toArray(); + + assertEquals(3, arr.length); + assertArrayEquals(new Object[] { 1, 2, 3 }, arr); + } + + @Test + void testAddAll() { + List list = Arrays.asList(5, 2, 8); + + assertTrue(arbre.addAll(list)); + assertEquals(3, arbre.size()); + assertTrue(arbre.contains(8)); + } + + @Test + void testRemoveAll() { + arbre.add(5); + arbre.add(2); + arbre.add(8); + + List list = Arrays.asList(2, 8); + + assertTrue(arbre.removeAll(list)); + assertFalse(arbre.contains(2)); + assertFalse(arbre.contains(8)); + assertTrue(arbre.contains(5)); + } + + @Test + void testRetainAll() { + arbre.add(5); + arbre.add(2); + arbre.add(8); + + List list = Arrays.asList(5); + + assertTrue(arbre.retainAll(list)); + assertTrue(arbre.contains(5)); + assertFalse(arbre.contains(2)); + assertFalse(arbre.contains(8)); + assertEquals(1, arbre.size()); + } + + @Test + void testContainsAvecTypeIncorrect() { + arbre.add(5); + arbre.add(3); + + // Un type incompatible doit entraîner catch(ClassCastException) → false + assertFalse(arbre.contains("texte")); // String ≠ Integer + } + + @Test + void testRemoveNull() { + arbre.add(5); + + assertFalse(arbre.remove(null)); // ne doit jamais lancer d’exception + assertEquals(1, arbre.size()); + assertTrue(arbre.contains(5)); + } + + @Test + void testRemoveRacineNull() { + // racine = null + assertTrue(arbre.isEmpty()); + + assertFalse(arbre.remove(10)); // retirer un élément dans un arbre vide => false + assertTrue(arbre.isEmpty()); + } + + @Test + void testRemoveSansException() { + arbre.add(10); + arbre.add(5); + arbre.add(15); + + // Cas normaux + assertDoesNotThrow(() -> arbre.remove(5)); + assertDoesNotThrow(() -> arbre.remove(15)); + assertDoesNotThrow(() -> arbre.remove(10)); + + // Retrait sur arbre vide + assertDoesNotThrow(() -> arbre.remove(999)); + + // Retrait d’un null + assertDoesNotThrow(() -> arbre.remove(null)); + } + + @Test + void testRemoveAvecSuccesseurProfond() { + + arbre.add(10); + arbre.add(5); + arbre.add(20); + arbre.add(15); + arbre.add(13); // successeur profond + + // Suppression du nœud ayant deux enfants + assertTrue(arbre.remove(10)); + + // Le successeur (13) doit avoir remplacé la racine + Iterator it = arbre.iterator(); + assertEquals(5, it.next()); + assertEquals(13, it.next()); // remplace 10 + assertEquals(15, it.next()); + assertEquals(20, it.next()); + assertFalse(it.hasNext()); + } + + @Test + void testRemoveRacineAvecUnEnfant() { + arbre.add(10); + arbre.add(20); // enfant droit unique + + assertTrue(arbre.remove(10)); + + // La racine doit être remplacée par l’enfant (20) + assertEquals(20, arbre.iterator().next()); + assertEquals(1, arbre.size()); + } + + @Test + void testIsEmpty() { + ArbreBinaireRecherche a = new ArbreBinaireRecherche<>(); + + // L'arbre vide + assertTrue(a.isEmpty()); + + // Après insertion + a.add(1); + assertFalse(a.isEmpty()); + + // Après suppression + a.remove(1); + assertTrue(a.isEmpty()); + } + + @Test + void testToArrayComplet() { + arbre.add(4); + arbre.add(1); + arbre.add(3); + arbre.add(2); + + Object[] arr = arbre.toArray(); + + assertEquals(4, arr.length); + assertArrayEquals(new Object[] { 1, 2, 3, 4 }, arr); + + // Vérification cohérence avec iterator() + int index = 0; + for (int value : arbre) { + assertEquals(value, arr[index++]); + } + } + + @Test + void testContainsAll() { + arbre.add(5); + arbre.add(2); + arbre.add(8); + + // Tous présents + assertTrue(arbre.containsAll(Arrays.asList(2, 5))); + + // Un absent + assertFalse(arbre.containsAll(Arrays.asList(2, 10))); + + // Collection vide → doit être true + assertTrue(arbre.containsAll(Arrays.asList())); + + // Mauvais type → contains(o) doit retourner false + assertFalse(arbre.containsAll(Arrays.asList("test"))); + } + + @Test + void testToArrayGenericRetourNull() { + arbre.add(5); + arbre.add(2); + arbre.add(9); + + Integer[] cible = new Integer[3]; + + // L’implémentation actuelle doit renvoyer null + assertNull(arbre.toArray(cible)); + } +} diff --git a/src/test/java/collection/RedBlackTree.java b/src/test/java/collection/RedBlackTree.java new file mode 100644 index 0000000000000000000000000000000000000000..efc89c2d497288ecdc173fd80dfebf97bc642a2b --- /dev/null +++ b/src/test/java/collection/RedBlackTree.java @@ -0,0 +1,136 @@ +package collection; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class RedBlackTreeTest { + + private RedBlackTree tree; + + @BeforeEach + void setUp() { + tree = new RedBlackTree<>(); + } + + @Test + void testAddAndContains() { + assertTrue(tree.add(10)); + assertTrue(tree.contains(10)); + assertFalse(tree.add(10)); // pas de doublons + } + + @Test + void testRemove() { + tree.add(20); + tree.add(10); + tree.add(30); + + assertTrue(tree.remove(20)); + assertFalse(tree.contains(20)); + assertFalse(tree.remove(40)); // élément inexistant + } + + @Test + void testSizeAndIsEmpty() { + assertTrue(tree.isEmpty()); + tree.add(1); + tree.add(2); + tree.add(3); + + assertEquals(3, tree.size()); + assertFalse(tree.isEmpty()); + } + + @Test + void testClear() { + tree.add(5); + tree.add(15); + tree.clear(); + assertEquals(0, tree.size()); + assertTrue(tree.isEmpty()); + } + + @Test + void testIterator() { + tree.add(3); + tree.add(1); + tree.add(4); + tree.add(2); + + Iterator it = tree.iterator(); + int[] expectedOrder = { 1, 2, 3, 4 }; + int i = 0; + + while (it.hasNext()) { + assertEquals(expectedOrder[i++], it.next()); + } + + assertEquals(expectedOrder.length, i); + } + + @Test + void testToArray() { + tree.add(7); + tree.add(3); + tree.add(9); + + Object[] arr = tree.toArray(); + Arrays.sort(arr); // pour comparaison + assertArrayEquals(new Object[] { 3, 7, 9 }, arr); + } + + @Test + void testToArrayWithType() { + tree.add(5); + tree.add(1); + tree.add(8); + + Integer[] arr = new Integer[3]; + arr = tree.toArray(arr); + + Arrays.sort(arr); + assertArrayEquals(new Integer[] { 1, 5, 8 }, arr); + } + + @Test + void testAddAllAndContainsAll() { + List list = Arrays.asList(10, 20, 30); + assertTrue(tree.addAll(list)); + assertTrue(tree.containsAll(list)); + } + + @Test + void testRemoveAll() { + tree.add(1); + tree.add(2); + tree.add(3); + + List toRemove = Arrays.asList(2, 3, 4); + assertTrue(tree.removeAll(toRemove)); + assertFalse(tree.contains(2)); + assertFalse(tree.contains(3)); + assertTrue(tree.contains(1)); + } + + @Test + void testRetainAll() { + tree.add(1); + tree.add(2); + tree.add(3); + + List toRetain = Arrays.asList(2, 3, 4); + assertTrue(tree.retainAll(toRetain)); + assertFalse(tree.contains(1)); + assertTrue(tree.contains(2)); + assertTrue(tree.contains(3)); + } +}