From 7ecd1c4f5bd0784da6cfc94a3f80dfbe7de0d8e3 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 7 Feb 2026 00:11:18 +0300 Subject: [PATCH 01/23] feat: implement Cube interface for RubicsCube --- .../hse/java/practice/task1/EdgePosition.java | 2 +- .../hse/java/practice/task1/EdgeRotation.java | 104 ++++++++++++++++++ .../hse/java/practice/task1/PartsIndex.java | 5 + .../hse/java/practice/task1/RubiksCube.java | 57 +++++++++- 4 files changed, 165 insertions(+), 3 deletions(-) create mode 100644 src/main/java/hse/java/practice/task1/EdgeRotation.java create mode 100644 src/main/java/hse/java/practice/task1/PartsIndex.java diff --git a/src/main/java/hse/java/practice/task1/EdgePosition.java b/src/main/java/hse/java/practice/task1/EdgePosition.java index f96cdf3c..f7ff700d 100644 --- a/src/main/java/hse/java/practice/task1/EdgePosition.java +++ b/src/main/java/hse/java/practice/task1/EdgePosition.java @@ -14,5 +14,5 @@ public enum EdgePosition { LEFT, RIGHT, FRONT, - BACK + BACK; } diff --git a/src/main/java/hse/java/practice/task1/EdgeRotation.java b/src/main/java/hse/java/practice/task1/EdgeRotation.java new file mode 100644 index 00000000..00bfc51d --- /dev/null +++ b/src/main/java/hse/java/practice/task1/EdgeRotation.java @@ -0,0 +1,104 @@ +package hse.java.practice.task1; + +/** + * Рассматриваем кубик Рубика как подгруппу симметрической группы S48, + * которая действует на 48 стикерах (по 8 стикеров на каждой из 6 граней, центры неподвижны). + * Поворот грани = это перестановка, которая циклически меняет 4 стикера на грани и 12 стикеров на соседних гранях. + * Отсюда и получаем такие страшные массивы циклов...Подробнее на вики + */ +public class EdgeRotation { + private static final int STICKERS_ON_EDGE = 8; + + public static final int[][] LEFT_CLOCKWISE = { + {16, 18, 23, 21}, + {17, 20, 22, 19}, + {0, 32, 8, 47}, + {3, 35, 11, 44}, + {5, 37, 13, 42} + }; + public static final int[][] LEFT_COUNTERCLOCKWISE = reverseDirection(LEFT_CLOCKWISE); + + public static final int[][] FRONT_CLOCKWISE = { + {32, 34, 39, 37}, + {33, 36, 38, 35}, + {5, 24, 10, 23}, + {6, 27, 9, 20}, + {7, 29, 8, 18} + }; + public static final int[][] FRONT_COUNTERCLOCKWISE = reverseDirection(FRONT_CLOCKWISE); + + public static final int[][] RIGHT_CLOCKWISE = { + {24, 26, 31, 29}, + {25, 28, 30, 27}, + {2, 34, 10, 45}, + {4, 36, 12, 43}, + {7, 39, 15, 40} + }; + public static final int[][] RIGHT_COUNTERCLOCKWISE = reverseDirection(RIGHT_CLOCKWISE); + + public static final int[][] BACK_CLOCKWISE = { + {40, 42, 47, 45}, + {41, 44, 46, 43}, + {2, 16, 15, 31}, + {1, 19, 14, 28}, + {0, 21, 13, 26} + }; + public static final int[][] BACK_COUNTERCLOCKWISE = reverseDirection(BACK_CLOCKWISE); + + public static final int[][] UP_CLOCKWISE = { + {0, 2, 7, 5}, + {1, 4, 6, 3}, + {40, 24, 32, 16}, + {41, 25, 33, 17}, + {42, 26, 34, 18} + }; + public static final int[][] UP_COUNTERCLOCKWISE = reverseDirection(UP_CLOCKWISE); + + public static final int[][] DOWN_CLOCKWISE = { + {8, 10, 15, 13}, + {9, 12, 14, 11}, + {37, 29, 45, 21}, + {38, 30, 46, 22}, + {39, 31, 47, 23} + }; + public static final int[][] DOWN_COUNTERCLOCKWISE = reverseDirection(DOWN_CLOCKWISE); + + private EdgeRotation() { + } + + private static int[][] reverseDirection(int[][] rotation) { + var result = new int[rotation.length][]; + + for (int i = 0; i < rotation.length; i++) { + var row = rotation[i]; + var reversedRow = new int[row.length]; + for (int j = 0; j < row.length; j++) { + reversedRow[j] = row[(j + 3) % row.length]; + } + result[i] = reversedRow; + } + + return result; + } + + /** + * По индексу стикера возвращает индекс грани, на которой он находится. + * + * @param stickerIndex индекс стикера от 0 до 47 включительно + * @return индекс грани от 0 до 5 включительно + */ + public static int getEdgeIndexByStickerIndex(int stickerIndex) { + return stickerIndex / STICKERS_ON_EDGE; + } + + /** + * По индексу стикера возвращает индекс части грани, на которой он находится. + * + * @param stickerIndex индекс стикера от 0 до 47 включительно + * @return пара индексов, где первый индекс - это строка, а второй - это столбец + */ + public static PartsIndex stickerIndexToPartsIndex(int stickerIndex) { + int stickerIndexOnEdge = stickerIndex % STICKERS_ON_EDGE; + return new PartsIndex(stickerIndexOnEdge / 3, stickerIndex % 3); + } +} diff --git a/src/main/java/hse/java/practice/task1/PartsIndex.java b/src/main/java/hse/java/practice/task1/PartsIndex.java new file mode 100644 index 00000000..1ef0a303 --- /dev/null +++ b/src/main/java/hse/java/practice/task1/PartsIndex.java @@ -0,0 +1,5 @@ +package hse.java.practice.task1; + +/// Позиция внутри грани куба. +public record PartsIndex(int row, int column) { +} diff --git a/src/main/java/hse/java/practice/task1/RubiksCube.java b/src/main/java/hse/java/practice/task1/RubiksCube.java index 2091b657..a9f7d76b 100644 --- a/src/main/java/hse/java/practice/task1/RubiksCube.java +++ b/src/main/java/hse/java/practice/task1/RubiksCube.java @@ -2,11 +2,13 @@ import java.util.Arrays; +import static hse.java.practice.task1.EdgeRotation.*; + /** * Необходимо реализовать интерфейс Cube * При повороте передней грани, меняются верх низ право и лево */ -public class RubiksCube { +public class RubiksCube implements Cube { private static final int EDGES_COUNT = 6; @@ -26,10 +28,61 @@ public RubiksCube() { } } + private void applyCycle(int[] cycle) { + CubeColor temp = getSticker(cycle[cycle.length - 1]); + for (int i = cycle.length - 2; i > 0; i--) { + setSticker(cycle[i], getSticker(cycle[i - 1])); + } + setSticker(cycle[0], temp); + } + + private CubeColor getSticker(int stickerIndex) { + var index = stickerIndexToPartsIndex(stickerIndex); + return edges[getEdgeIndexByStickerIndex(stickerIndex)].getParts()[index.row()][index.column()]; + } + + private void setSticker(int stickerIndex, CubeColor color) { + var index = stickerIndexToPartsIndex(stickerIndex); + edges[getEdgeIndexByStickerIndex(stickerIndex)].getParts()[index.row()][index.column()] = color; + } + + private void rotate(RotateDirection direction, int[][] clockwiseRotation, int[][] counterClockwiseRotation) { + int[][] rotation = (direction == RotateDirection.CLOCKWISE) ? clockwiseRotation : counterClockwiseRotation; + for (int[] cycle : rotation) { + applyCycle(cycle); + } + } + + @Override + public void up(RotateDirection direction) { + rotate(direction, EdgeRotation.UP_CLOCKWISE, EdgeRotation.UP_COUNTERCLOCKWISE); + } + + @Override + public void down(RotateDirection direction) { + rotate(direction, EdgeRotation.DOWN_CLOCKWISE, EdgeRotation.DOWN_COUNTERCLOCKWISE); + } + + @Override + public void left(RotateDirection direction) { + rotate(direction, EdgeRotation.LEFT_CLOCKWISE, EdgeRotation.LEFT_COUNTERCLOCKWISE); + } + + @Override + public void right(RotateDirection direction) { + rotate(direction, EdgeRotation.RIGHT_CLOCKWISE, EdgeRotation.RIGHT_COUNTERCLOCKWISE); + } + + @Override public void front(RotateDirection direction) { + rotate(direction, EdgeRotation.FRONT_CLOCKWISE, EdgeRotation.FRONT_COUNTERCLOCKWISE); + } + @Override + public void back(RotateDirection direction) { + rotate(direction, EdgeRotation.BACK_CLOCKWISE, EdgeRotation.BACK_COUNTERCLOCKWISE); } - + public Edge[] getEdges() { return edges; } From c2ee8200794f3a1dfb1629ddde0fd39ebdc520e8 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 7 Feb 2026 22:31:00 +0300 Subject: [PATCH 02/23] atm: solved --- .../java/lectures/lecture3/tasks/atm/Atm.java | 78 +++++++++- .../lecture3/tasks/html/HtmlDocument.java | 143 +++++++++++++++++- 2 files changed, 211 insertions(+), 10 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java b/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java index 3fa91909..e8bbe81c 100644 --- a/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java +++ b/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java @@ -26,19 +26,85 @@ int value() { } } - private final Map banknotes = new EnumMap<>(Denomination.class); + private Map banknotes = new EnumMap<>(Denomination.class); + + private Denomination getDenominationByValue(int value) { + for (Denomination denomination : Denomination.values()) { + if (denomination.value() == value) { + return denomination; + } + } + throw new IllegalArgumentException("Invalid denomination value: " + value); + } public Atm() { } - public void deposit(Map banknotes){} + public void deposit(Map bills) { + if (bills == null) { + throw new InvalidDepositException("Bills map cannot be null"); + } + + for (Map.Entry entry : bills.entrySet()) { + int denominationValue = entry.getKey(); + int count = entry.getValue(); + + if (count <= 0) { + throw new InvalidDepositException("Count must be positive for denomination: " + denominationValue); + } + + Denomination denomination; + try { + denomination = getDenominationByValue(denominationValue); + } catch (IllegalArgumentException e) { + throw new InvalidDepositException("Invalid denomination: " + denominationValue); + } + + banknotes.put(denomination, banknotes.getOrDefault(denomination, 0) + count); + } + } public Map withdraw(int amount) { - return Map.of(); + if (amount <= 0) { + throw new InvalidAmountException("Amount must be positive"); + } + + if (amount > getBalance()) { + throw new InsufficientFundsException("Not enough funds in the ATM"); + } + + Map result = new HashMap<>(); + var banknoteCopy = new EnumMap<>(banknotes); + + + for (int i = Denomination.values().length - 1; i >= 0; i--) { + Denomination denomination = Denomination.values()[i]; + int availableCount = banknotes.getOrDefault(denomination, 0); + int neededCount = amount / denomination.value(); + int countToDispense = Math.min(availableCount, neededCount); + + if (countToDispense > 0) { + result.put(denomination.value(), countToDispense); + amount -= countToDispense * denomination.value(); + banknotes.put(denomination, availableCount - countToDispense); + } + } + + if (amount != 0) { + banknotes = banknoteCopy; + throw new CannotDispenseException("Not enough funds in the ATM"); + } + + return result; } public int getBalance() { - return 0; + int result = 0; + for (Map.Entry entry : banknotes.entrySet()) { + Denomination denomination = entry.getKey(); + int count = entry.getValue(); + result += count * denomination.value(); + } + return result; } - -} +} \ No newline at end of file diff --git a/src/main/java/hse/java/lectures/lecture3/tasks/html/HtmlDocument.java b/src/main/java/hse/java/lectures/lecture3/tasks/html/HtmlDocument.java index 1ddf27bf..b82e54fc 100644 --- a/src/main/java/hse/java/lectures/lecture3/tasks/html/HtmlDocument.java +++ b/src/main/java/hse/java/lectures/lecture3/tasks/html/HtmlDocument.java @@ -4,12 +4,18 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Set; +import java.util.*; public class HtmlDocument { + private static final Map allowedTags = Map.of( + "", "", + "", "", + "", "", + "
", "
", + "

", "

" + ); + public HtmlDocument(String filePath) { this(Path.of(filePath)); } @@ -27,6 +33,135 @@ private String readFile(Path filePath) { } } - private void validate(String content){} + private String ignoreAttributes(String tag) { + String lowerTag = tag.toLowerCase(); + for (String allowed : allowedTags.keySet()) { + var expected = allowed.substring(1, allowed.length() - 1); + if (lowerTag.startsWith("<" + expected)) { + return "<" + expected + ">"; + } + if (lowerTag.startsWith(""; + } + } + return null; + } + + private List extractRootTags(String content) { + List rootTags = new ArrayList<>(); + int pos = 0; + int depth = 0; + + while (pos < content.length()) { + int open = content.indexOf('<', pos); + if (open == -1) break; + + int close = content.indexOf('>', open); + if (close == -1) break; + + String tag = content.substring(open, close + 1).toLowerCase(); + pos = close + 1; + + if (tag.startsWith(""); + int htmlClosePos = content.indexOf(""); + + if (htmlOpenPos == -1 || htmlClosePos == -1 || htmlOpenPos >= htmlClosePos) { + throw new InvalidStructureException("Html tag is needed and must be properly opened before closed."); + } + + for (String openTag : allowedTags.keySet()) { + checkTagPosition(content, openTag, htmlOpenPos, htmlClosePos); + } + for (String closeTag : allowedTags.values()) { + checkTagPosition(content, closeTag, htmlOpenPos, htmlClosePos); + } + + String innerHtml = content.substring(htmlOpenPos + "".length(), htmlClosePos); + List rootTags = extractRootTags(innerHtml); + + if (rootTags.size() != 2) { + throw new InvalidStructureException("Inside there must be exactly followed by ."); + } + + if (!"".equals(rootTags.get(0))) { + throw new InvalidStructureException(" must be the first child of ."); + } + + if (!"".equals(rootTags.get(1))) { + throw new InvalidStructureException(" must follow as second child of ."); + } + } + + private void checkTagPosition(String content, String tag, int htmlOpenPos, int htmlClosePos) { + int index = -1; + while ((index = content.indexOf(tag, index + 1)) != -1) { + if (index < htmlOpenPos || index > htmlClosePos) { + throw new InvalidStructureException( + "Tag '" + tag + "' must be inside ..., but found at position " + index); + } + } + } + + private void validate(String content) { + Stack stack = new Stack<>(); + int pos = 0; + + while (pos < content.length()) { + int tagStart = content.indexOf('<', pos); + int tagEnd = content.indexOf('>', tagStart); + if (tagStart == -1 || tagEnd == -1) break; + + String tag = content.substring(tagStart, tagEnd + 1); + + var normalizedTag = ignoreAttributes(tag); + + if (normalizedTag == null) { + throw new UnsupportedTagException(tag); + } + + if (allowedTags.containsKey(normalizedTag)) { + stack.push(normalizedTag.toLowerCase()); + } else if (normalizedTag.startsWith(" Date: Sat, 7 Feb 2026 00:11:18 +0300 Subject: [PATCH 03/23] feat: implement Cube interface for RubicsCube # Conflicts: # src/main/java/hse/java/practice/task1/RubiksCube.java --- .../hse/java/practice/task1/EdgePosition.java | 2 +- .../hse/java/practice/task1/EdgeRotation.java | 104 ++++++++++++++++++ .../hse/java/practice/task1/PartsIndex.java | 5 + .../hse/java/practice/task1/RubiksCube.java | 39 ++++++- 4 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 src/main/java/hse/java/practice/task1/EdgeRotation.java create mode 100644 src/main/java/hse/java/practice/task1/PartsIndex.java diff --git a/src/main/java/hse/java/practice/task1/EdgePosition.java b/src/main/java/hse/java/practice/task1/EdgePosition.java index f96cdf3c..f7ff700d 100644 --- a/src/main/java/hse/java/practice/task1/EdgePosition.java +++ b/src/main/java/hse/java/practice/task1/EdgePosition.java @@ -14,5 +14,5 @@ public enum EdgePosition { LEFT, RIGHT, FRONT, - BACK + BACK; } diff --git a/src/main/java/hse/java/practice/task1/EdgeRotation.java b/src/main/java/hse/java/practice/task1/EdgeRotation.java new file mode 100644 index 00000000..00bfc51d --- /dev/null +++ b/src/main/java/hse/java/practice/task1/EdgeRotation.java @@ -0,0 +1,104 @@ +package hse.java.practice.task1; + +/** + * Рассматриваем кубик Рубика как подгруппу симметрической группы S48, + * которая действует на 48 стикерах (по 8 стикеров на каждой из 6 граней, центры неподвижны). + * Поворот грани = это перестановка, которая циклически меняет 4 стикера на грани и 12 стикеров на соседних гранях. + * Отсюда и получаем такие страшные массивы циклов...Подробнее на вики + */ +public class EdgeRotation { + private static final int STICKERS_ON_EDGE = 8; + + public static final int[][] LEFT_CLOCKWISE = { + {16, 18, 23, 21}, + {17, 20, 22, 19}, + {0, 32, 8, 47}, + {3, 35, 11, 44}, + {5, 37, 13, 42} + }; + public static final int[][] LEFT_COUNTERCLOCKWISE = reverseDirection(LEFT_CLOCKWISE); + + public static final int[][] FRONT_CLOCKWISE = { + {32, 34, 39, 37}, + {33, 36, 38, 35}, + {5, 24, 10, 23}, + {6, 27, 9, 20}, + {7, 29, 8, 18} + }; + public static final int[][] FRONT_COUNTERCLOCKWISE = reverseDirection(FRONT_CLOCKWISE); + + public static final int[][] RIGHT_CLOCKWISE = { + {24, 26, 31, 29}, + {25, 28, 30, 27}, + {2, 34, 10, 45}, + {4, 36, 12, 43}, + {7, 39, 15, 40} + }; + public static final int[][] RIGHT_COUNTERCLOCKWISE = reverseDirection(RIGHT_CLOCKWISE); + + public static final int[][] BACK_CLOCKWISE = { + {40, 42, 47, 45}, + {41, 44, 46, 43}, + {2, 16, 15, 31}, + {1, 19, 14, 28}, + {0, 21, 13, 26} + }; + public static final int[][] BACK_COUNTERCLOCKWISE = reverseDirection(BACK_CLOCKWISE); + + public static final int[][] UP_CLOCKWISE = { + {0, 2, 7, 5}, + {1, 4, 6, 3}, + {40, 24, 32, 16}, + {41, 25, 33, 17}, + {42, 26, 34, 18} + }; + public static final int[][] UP_COUNTERCLOCKWISE = reverseDirection(UP_CLOCKWISE); + + public static final int[][] DOWN_CLOCKWISE = { + {8, 10, 15, 13}, + {9, 12, 14, 11}, + {37, 29, 45, 21}, + {38, 30, 46, 22}, + {39, 31, 47, 23} + }; + public static final int[][] DOWN_COUNTERCLOCKWISE = reverseDirection(DOWN_CLOCKWISE); + + private EdgeRotation() { + } + + private static int[][] reverseDirection(int[][] rotation) { + var result = new int[rotation.length][]; + + for (int i = 0; i < rotation.length; i++) { + var row = rotation[i]; + var reversedRow = new int[row.length]; + for (int j = 0; j < row.length; j++) { + reversedRow[j] = row[(j + 3) % row.length]; + } + result[i] = reversedRow; + } + + return result; + } + + /** + * По индексу стикера возвращает индекс грани, на которой он находится. + * + * @param stickerIndex индекс стикера от 0 до 47 включительно + * @return индекс грани от 0 до 5 включительно + */ + public static int getEdgeIndexByStickerIndex(int stickerIndex) { + return stickerIndex / STICKERS_ON_EDGE; + } + + /** + * По индексу стикера возвращает индекс части грани, на которой он находится. + * + * @param stickerIndex индекс стикера от 0 до 47 включительно + * @return пара индексов, где первый индекс - это строка, а второй - это столбец + */ + public static PartsIndex stickerIndexToPartsIndex(int stickerIndex) { + int stickerIndexOnEdge = stickerIndex % STICKERS_ON_EDGE; + return new PartsIndex(stickerIndexOnEdge / 3, stickerIndex % 3); + } +} diff --git a/src/main/java/hse/java/practice/task1/PartsIndex.java b/src/main/java/hse/java/practice/task1/PartsIndex.java new file mode 100644 index 00000000..1ef0a303 --- /dev/null +++ b/src/main/java/hse/java/practice/task1/PartsIndex.java @@ -0,0 +1,5 @@ +package hse.java.practice.task1; + +/// Позиция внутри грани куба. +public record PartsIndex(int row, int column) { +} diff --git a/src/main/java/hse/java/practice/task1/RubiksCube.java b/src/main/java/hse/java/practice/task1/RubiksCube.java index d986f9f0..a9f7d76b 100644 --- a/src/main/java/hse/java/practice/task1/RubiksCube.java +++ b/src/main/java/hse/java/practice/task1/RubiksCube.java @@ -2,6 +2,8 @@ import java.util.Arrays; +import static hse.java.practice.task1.EdgeRotation.*; + /** * Необходимо реализовать интерфейс Cube * При повороте передней грани, меняются верх низ право и лево @@ -26,34 +28,59 @@ public RubiksCube() { } } + private void applyCycle(int[] cycle) { + CubeColor temp = getSticker(cycle[cycle.length - 1]); + for (int i = cycle.length - 2; i > 0; i--) { + setSticker(cycle[i], getSticker(cycle[i - 1])); + } + setSticker(cycle[0], temp); + } + + private CubeColor getSticker(int stickerIndex) { + var index = stickerIndexToPartsIndex(stickerIndex); + return edges[getEdgeIndexByStickerIndex(stickerIndex)].getParts()[index.row()][index.column()]; + } + + private void setSticker(int stickerIndex, CubeColor color) { + var index = stickerIndexToPartsIndex(stickerIndex); + edges[getEdgeIndexByStickerIndex(stickerIndex)].getParts()[index.row()][index.column()] = color; + } + + private void rotate(RotateDirection direction, int[][] clockwiseRotation, int[][] counterClockwiseRotation) { + int[][] rotation = (direction == RotateDirection.CLOCKWISE) ? clockwiseRotation : counterClockwiseRotation; + for (int[] cycle : rotation) { + applyCycle(cycle); + } + } + @Override public void up(RotateDirection direction) { - + rotate(direction, EdgeRotation.UP_CLOCKWISE, EdgeRotation.UP_COUNTERCLOCKWISE); } @Override public void down(RotateDirection direction) { - + rotate(direction, EdgeRotation.DOWN_CLOCKWISE, EdgeRotation.DOWN_COUNTERCLOCKWISE); } @Override public void left(RotateDirection direction) { - + rotate(direction, EdgeRotation.LEFT_CLOCKWISE, EdgeRotation.LEFT_COUNTERCLOCKWISE); } @Override public void right(RotateDirection direction) { - + rotate(direction, EdgeRotation.RIGHT_CLOCKWISE, EdgeRotation.RIGHT_COUNTERCLOCKWISE); } @Override public void front(RotateDirection direction) { - + rotate(direction, EdgeRotation.FRONT_CLOCKWISE, EdgeRotation.FRONT_COUNTERCLOCKWISE); } @Override public void back(RotateDirection direction) { - + rotate(direction, EdgeRotation.BACK_CLOCKWISE, EdgeRotation.BACK_COUNTERCLOCKWISE); } public Edge[] getEdges() { From 5d4af8cd1449d4d28f003ca139c1aafb9b6d37e9 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 7 Feb 2026 22:31:00 +0300 Subject: [PATCH 04/23] atm: solved --- .../java/lectures/lecture3/tasks/atm/Atm.java | 93 ++++++++++-- .../lecture3/tasks/html/HtmlDocument.java | 143 +++++++++++++++++- 2 files changed, 218 insertions(+), 18 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java b/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java index 08f551e4..e8bbe81c 100644 --- a/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java +++ b/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java @@ -1,9 +1,14 @@ package hse.java.lectures.lecture3.tasks.atm; -import java.util.*; +import java.util.Collections; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; public class Atm { - public enum Denomination { + private enum Denomination { D50(50), D100(100), D500(500), @@ -19,27 +24,87 @@ public enum Denomination { int value() { return value; } + } + + private Map banknotes = new EnumMap<>(Denomination.class); - public static Denomination fromInt(int value) { - return Arrays.stream(values()).filter(v -> v.value == value) - .findFirst() - .orElse(null); + private Denomination getDenominationByValue(int value) { + for (Denomination denomination : Denomination.values()) { + if (denomination.value() == value) { + return denomination; + } } + throw new IllegalArgumentException("Invalid denomination value: " + value); } - private final Map banknotes = new EnumMap<>(Denomination.class); - public Atm() { } - public void deposit(Map banknotes){} + public void deposit(Map bills) { + if (bills == null) { + throw new InvalidDepositException("Bills map cannot be null"); + } + + for (Map.Entry entry : bills.entrySet()) { + int denominationValue = entry.getKey(); + int count = entry.getValue(); + + if (count <= 0) { + throw new InvalidDepositException("Count must be positive for denomination: " + denominationValue); + } - public Map withdraw(int amount) { - return Map.of(); + Denomination denomination; + try { + denomination = getDenominationByValue(denominationValue); + } catch (IllegalArgumentException e) { + throw new InvalidDepositException("Invalid denomination: " + denominationValue); + } + + banknotes.put(denomination, banknotes.getOrDefault(denomination, 0) + count); + } } - public int getBalance() { - return 0; + public Map withdraw(int amount) { + if (amount <= 0) { + throw new InvalidAmountException("Amount must be positive"); + } + + if (amount > getBalance()) { + throw new InsufficientFundsException("Not enough funds in the ATM"); + } + + Map result = new HashMap<>(); + var banknoteCopy = new EnumMap<>(banknotes); + + + for (int i = Denomination.values().length - 1; i >= 0; i--) { + Denomination denomination = Denomination.values()[i]; + int availableCount = banknotes.getOrDefault(denomination, 0); + int neededCount = amount / denomination.value(); + int countToDispense = Math.min(availableCount, neededCount); + + if (countToDispense > 0) { + result.put(denomination.value(), countToDispense); + amount -= countToDispense * denomination.value(); + banknotes.put(denomination, availableCount - countToDispense); + } + } + + if (amount != 0) { + banknotes = banknoteCopy; + throw new CannotDispenseException("Not enough funds in the ATM"); + } + + return result; } -} + public int getBalance() { + int result = 0; + for (Map.Entry entry : banknotes.entrySet()) { + Denomination denomination = entry.getKey(); + int count = entry.getValue(); + result += count * denomination.value(); + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/hse/java/lectures/lecture3/tasks/html/HtmlDocument.java b/src/main/java/hse/java/lectures/lecture3/tasks/html/HtmlDocument.java index 1ddf27bf..b82e54fc 100644 --- a/src/main/java/hse/java/lectures/lecture3/tasks/html/HtmlDocument.java +++ b/src/main/java/hse/java/lectures/lecture3/tasks/html/HtmlDocument.java @@ -4,12 +4,18 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Set; +import java.util.*; public class HtmlDocument { + private static final Map allowedTags = Map.of( + "", "", + "", "", + "", "", + "
", "
", + "

", "

" + ); + public HtmlDocument(String filePath) { this(Path.of(filePath)); } @@ -27,6 +33,135 @@ private String readFile(Path filePath) { } } - private void validate(String content){} + private String ignoreAttributes(String tag) { + String lowerTag = tag.toLowerCase(); + for (String allowed : allowedTags.keySet()) { + var expected = allowed.substring(1, allowed.length() - 1); + if (lowerTag.startsWith("<" + expected)) { + return "<" + expected + ">"; + } + if (lowerTag.startsWith(""; + } + } + return null; + } + + private List extractRootTags(String content) { + List rootTags = new ArrayList<>(); + int pos = 0; + int depth = 0; + + while (pos < content.length()) { + int open = content.indexOf('<', pos); + if (open == -1) break; + + int close = content.indexOf('>', open); + if (close == -1) break; + + String tag = content.substring(open, close + 1).toLowerCase(); + pos = close + 1; + + if (tag.startsWith(""); + int htmlClosePos = content.indexOf(""); + + if (htmlOpenPos == -1 || htmlClosePos == -1 || htmlOpenPos >= htmlClosePos) { + throw new InvalidStructureException("Html tag is needed and must be properly opened before closed."); + } + + for (String openTag : allowedTags.keySet()) { + checkTagPosition(content, openTag, htmlOpenPos, htmlClosePos); + } + for (String closeTag : allowedTags.values()) { + checkTagPosition(content, closeTag, htmlOpenPos, htmlClosePos); + } + + String innerHtml = content.substring(htmlOpenPos + "".length(), htmlClosePos); + List rootTags = extractRootTags(innerHtml); + + if (rootTags.size() != 2) { + throw new InvalidStructureException("Inside there must be exactly followed by ."); + } + + if (!"".equals(rootTags.get(0))) { + throw new InvalidStructureException(" must be the first child of ."); + } + + if (!"".equals(rootTags.get(1))) { + throw new InvalidStructureException(" must follow as second child of ."); + } + } + + private void checkTagPosition(String content, String tag, int htmlOpenPos, int htmlClosePos) { + int index = -1; + while ((index = content.indexOf(tag, index + 1)) != -1) { + if (index < htmlOpenPos || index > htmlClosePos) { + throw new InvalidStructureException( + "Tag '" + tag + "' must be inside ..., but found at position " + index); + } + } + } + + private void validate(String content) { + Stack stack = new Stack<>(); + int pos = 0; + + while (pos < content.length()) { + int tagStart = content.indexOf('<', pos); + int tagEnd = content.indexOf('>', tagStart); + if (tagStart == -1 || tagEnd == -1) break; + + String tag = content.substring(tagStart, tagEnd + 1); + + var normalizedTag = ignoreAttributes(tag); + + if (normalizedTag == null) { + throw new UnsupportedTagException(tag); + } + + if (allowedTags.containsKey(normalizedTag)) { + stack.push(normalizedTag.toLowerCase()); + } else if (normalizedTag.startsWith(" Date: Sat, 7 Feb 2026 22:33:57 +0300 Subject: [PATCH 05/23] cube: rerun tests --- src/main/java/hse/java/practice/task1/EdgeRotation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/hse/java/practice/task1/EdgeRotation.java b/src/main/java/hse/java/practice/task1/EdgeRotation.java index 00bfc51d..0804ef26 100644 --- a/src/main/java/hse/java/practice/task1/EdgeRotation.java +++ b/src/main/java/hse/java/practice/task1/EdgeRotation.java @@ -4,7 +4,7 @@ * Рассматриваем кубик Рубика как подгруппу симметрической группы S48, * которая действует на 48 стикерах (по 8 стикеров на каждой из 6 граней, центры неподвижны). * Поворот грани = это перестановка, которая циклически меняет 4 стикера на грани и 12 стикеров на соседних гранях. - * Отсюда и получаем такие страшные массивы циклов...Подробнее на вики + * Отсюда и получаем такие страшные массивы циклов...Подробнее на вики. */ public class EdgeRotation { private static final int STICKERS_ON_EDGE = 8; From 52f83c172acf95c523a384657c44553b01cd9ce4 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 7 Feb 2026 22:38:52 +0300 Subject: [PATCH 06/23] cube: try one more time --- src/main/java/hse/java/practice/task1/PartsIndex.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/hse/java/practice/task1/PartsIndex.java b/src/main/java/hse/java/practice/task1/PartsIndex.java index 1ef0a303..a7457a52 100644 --- a/src/main/java/hse/java/practice/task1/PartsIndex.java +++ b/src/main/java/hse/java/practice/task1/PartsIndex.java @@ -1,5 +1,7 @@ package hse.java.practice.task1; -/// Позиция внутри грани куба. +/** + * Позиция внутри грани куба. + */ public record PartsIndex(int row, int column) { } From 01f4ccea627ff69810a48d6b315757cc50372259 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 7 Feb 2026 22:46:47 +0300 Subject: [PATCH 07/23] cube: fix atm tests --- .../java/lectures/lecture3/tasks/atm/Atm.java | 2 +- .../lectures/lecture3/tasks/atm/AtmTest.java | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java b/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java index e8bbe81c..af541449 100644 --- a/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java +++ b/src/main/java/hse/java/lectures/lecture3/tasks/atm/Atm.java @@ -8,7 +8,7 @@ import java.util.TreeSet; public class Atm { - private enum Denomination { + public enum Denomination { D50(50), D100(100), D500(500), diff --git a/src/test/java/hse/java/lectures/lecture3/tasks/atm/AtmTest.java b/src/test/java/hse/java/lectures/lecture3/tasks/atm/AtmTest.java index 5e83fcae..c74a91c0 100644 --- a/src/test/java/hse/java/lectures/lecture3/tasks/atm/AtmTest.java +++ b/src/test/java/hse/java/lectures/lecture3/tasks/atm/AtmTest.java @@ -31,7 +31,7 @@ void initialBalanceIsZero() { @Test void depositIncreasesBalance() { Atm atm = new Atm(); - atm.deposit(Map.of(D100, 10, D500, 5)); + atm.deposit(Map.of(100, 10, 500, 5)); assertEquals(3500, atm.getBalance()); } @@ -45,8 +45,8 @@ void depositRejectsNullMap() { @Test void depositRejectsNonPositiveCountAndKeepsState() { Atm atm = new Atm(); - atm.deposit(Map.of(D100, 1)); - assertThrows(InvalidDepositException.class, () -> atm.deposit(Map.of(D100, 0))); + atm.deposit(Map.of(100, 1)); + assertThrows(InvalidDepositException.class, () -> atm.deposit(Map.of(100, 0))); assertEquals(100, atm.getBalance()); } @@ -54,11 +54,11 @@ void depositRejectsNonPositiveCountAndKeepsState() { void withdrawGreedyAndUpdatesBalance() { Atm atm = new Atm(); // 2000 - atm.deposit(Map.of(D1000, 1, D500, 1, D100, 5)); + atm.deposit(Map.of(1000, 1, 500, 1, 100, 5)); - Map result = atm.withdraw(1700); + Map result = atm.withdraw(1700); - assertEquals(Map.of(D1000, 1, D500, 1, D100, 2), result); + assertEquals(Map.of(1000, 1, 500, 1, 100, 2), result); assertEquals(300, atm.getBalance()); } @@ -72,14 +72,14 @@ void withdrawRejectsInvalidAmount() { @Test void withdrawRejectsInsufficientFunds() { Atm atm = new Atm(); - atm.deposit(Map.of(D100, 2)); + atm.deposit(Map.of(100, 2)); assertThrows(InsufficientFundsException.class, () -> atm.withdraw(300)); } @Test void withdrawRejectsUnmakeableAmountAndKeepsState() { Atm atm = new Atm(); - atm.deposit(Map.of(D500, 1, D100, 1)); + atm.deposit(Map.of(500, 1, 100, 1)); assertThrows(CannotDispenseException.class, () -> atm.withdraw(150)); assertEquals(600, atm.getBalance()); } @@ -99,7 +99,7 @@ void additionalTests(AtmCase atmCase) { "Case: " + atmCase.name); assertEquals(atmCase.expect.balance, atm.getBalance(), "Case: " + atmCase.name); } else { - Map result = atm.withdraw(atmCase.withdraw); + var result = atm.withdraw(atmCase.withdraw); assertEquals(toMap(atmCase.expect.dispense), result, "Case: " + atmCase.name); assertEquals(atmCase.expect.balance, atm.getBalance(), "Case: " + atmCase.name); } @@ -120,10 +120,10 @@ private static List loadCases() throws IOException { } } - private Map toMap(Map source) { - Map result = new HashMap<>(); + private Map toMap(Map source) { + Map result = new HashMap<>(); for (Map.Entry entry : source.entrySet()) { - result.put(Atm.Denomination.fromInt(Integer.parseInt(entry.getKey())), entry.getValue()); + result.put(Integer.parseInt(entry.getKey()), entry.getValue()); } return result; } From 84c0ef5dc2b0205cc613daff692057a68e7c442d Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 7 Feb 2026 23:00:26 +0300 Subject: [PATCH 08/23] cube: fix reverse direction for permutation matrixes --- .../hse/java/practice/task1/EdgeRotation.java | 20 ++++++++++++++++--- .../hse/java/practice/task1/RubiksCube.java | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/java/hse/java/practice/task1/EdgeRotation.java b/src/main/java/hse/java/practice/task1/EdgeRotation.java index 2a84297e..65c4b476 100644 --- a/src/main/java/hse/java/practice/task1/EdgeRotation.java +++ b/src/main/java/hse/java/practice/task1/EdgeRotation.java @@ -4,6 +4,7 @@ * Рассматриваем кубик Рубика как подгруппу симметрической группы S48, * которая действует на 48 стикерах (по 8 стикеров на каждой из 6 граней, центры неподвижны). * Поворот грани = это перестановка, которая циклически меняет 4 стикера на грани и 12 стикеров на соседних гранях. + * НА ВИКИ ПОДРОБНЕЕ. */ public class EdgeRotation { private static final int STICKERS_ON_EDGE = 8; @@ -71,8 +72,9 @@ private static int[][] reverseDirection(int[][] rotation) { for (int i = 0; i < rotation.length; i++) { var row = rotation[i]; var reversedRow = new int[row.length]; - for (int j = 0; j < row.length; j++) { - reversedRow[j] = row[(j + 3) % row.length]; + reversedRow[0] = row[0]; + for (int j = 1; j < row.length; j++) { + reversedRow[j] = row[row.length - j]; } result[i] = reversedRow; } @@ -98,6 +100,18 @@ public static int getEdgeIndexByStickerIndex(int stickerIndex) { */ public static PartsIndex stickerIndexToPartsIndex(int stickerIndex) { int stickerIndexOnEdge = stickerIndex % STICKERS_ON_EDGE; - return new PartsIndex(stickerIndexOnEdge / 3, stickerIndex % 3); + int row; + int column; + if (stickerIndexOnEdge < 3) { + row = 0; + column = stickerIndexOnEdge; + } else if (stickerIndexOnEdge < 5) { + row = 1; + column = (stickerIndexOnEdge == 3) ? 0 : 2; + } else { + row = 2; + column = stickerIndexOnEdge - 5; + } + return new PartsIndex(row, column); } } diff --git a/src/main/java/hse/java/practice/task1/RubiksCube.java b/src/main/java/hse/java/practice/task1/RubiksCube.java index a9f7d76b..6f4927f5 100644 --- a/src/main/java/hse/java/practice/task1/RubiksCube.java +++ b/src/main/java/hse/java/practice/task1/RubiksCube.java @@ -30,7 +30,7 @@ public RubiksCube() { private void applyCycle(int[] cycle) { CubeColor temp = getSticker(cycle[cycle.length - 1]); - for (int i = cycle.length - 2; i > 0; i--) { + for (int i = cycle.length - 1; i > 0; i--) { setSticker(cycle[i], getSticker(cycle[i - 1])); } setSticker(cycle[0], temp); From 534303a2fcca2cbc6760c3d76a1400e771427606 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 7 Feb 2026 23:10:47 +0300 Subject: [PATCH 09/23] cube: fix right clockwise --- src/main/java/hse/java/practice/task1/EdgeRotation.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/hse/java/practice/task1/EdgeRotation.java b/src/main/java/hse/java/practice/task1/EdgeRotation.java index 65c4b476..f98209c5 100644 --- a/src/main/java/hse/java/practice/task1/EdgeRotation.java +++ b/src/main/java/hse/java/practice/task1/EdgeRotation.java @@ -30,9 +30,9 @@ public class EdgeRotation { public static final int[][] RIGHT_CLOCKWISE = { {24, 26, 31, 29}, {25, 28, 30, 27}, - {2, 34, 10, 45}, - {4, 36, 12, 43}, - {7, 39, 15, 40} + {2, 45, 10, 34}, + {4, 43, 12, 36}, + {7, 40, 15, 39} }; public static final int[][] RIGHT_COUNTERCLOCKWISE = reverseDirection(RIGHT_CLOCKWISE); From 02500369b981fc11c2b78b25664236af2b8bc548 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 00:46:56 +0300 Subject: [PATCH 10/23] randomset: first working version --- pom.xml | 22 ++++- .../lecture3/practice/randomSet/Node.java | 32 +++++++ .../practice/randomSet/RandomSet.java | 96 ++++++++++++++++++- 3 files changed, 143 insertions(+), 7 deletions(-) create mode 100644 src/main/java/hse/java/lectures/lecture3/practice/randomSet/Node.java diff --git a/pom.xml b/pom.xml index 648f8788..3ae24282 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,8 @@ UTF-8 5.10.0 3.1.2 + + 1.18.38 @@ -38,6 +40,12 @@ 2.17.2 test + + org.projectlombok + lombok + ${lombok.version} + provided + @@ -47,8 +55,18 @@ maven-compiler-plugin 3.11.0 - 17 - 17 + + + org.projectlombok + lombok + ${lombok.version} + + + ${maven.compiler.source} + ${maven.compiler.target} + + -parameters + diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Node.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Node.java new file mode 100644 index 00000000..d613e287 --- /dev/null +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Node.java @@ -0,0 +1,32 @@ +package hse.java.lectures.lecture3.practice.randomSet; + +import lombok.Getter; +import lombok.Setter; + +@Getter +public class Node { + @Setter + private T value; + @Setter + private Node left; + @Setter + private Node right; + private int size; + + public Node(T value) { + size = 1; + this.value = value; + this.left = null; + this.right = null; + } + + public void recalculateSize() { + size = 1; + if (left != null) { + size += left.getSize(); + } + if (right != null) { + size += right.getSize(); + } + } +} diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index 8af477b5..0660ac9e 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -1,21 +1,107 @@ package hse.java.lectures.lecture3.practice.randomSet; +import java.util.Random; + public class RandomSet { + private Node root; + private final Random random = new Random(); + + private Node recursiveInsert(Node currentNode, T value) { + if (currentNode == null) { + return new Node<>(value); + } + if (currentNode.getValue().equals(value)) { + return null; + } + if (currentNode.getValue().hashCode() > value.hashCode()) { + currentNode.setLeft(recursiveInsert(currentNode.getLeft(), value)); + } else { + currentNode.setRight(recursiveInsert(currentNode.getRight(), value)); + } + currentNode.recalculateSize(); + return currentNode; + } + + private Node findMin(Node node) { + while (node.getLeft() != null) { + node = node.getLeft(); + } + return node; + } + + private Node recursiveDelete(Node currentNode, T value) { + if (currentNode == null) { + return null; + } + if (currentNode.getValue().equals(value)) { + if (currentNode.getLeft() == null) { + return currentNode.getRight(); + } + if (currentNode.getRight() == null) { + return currentNode.getLeft(); + } + Node minNode = findMin(currentNode.getRight()); + currentNode.setValue(minNode.getValue()); + currentNode.setRight(recursiveDelete(currentNode.getRight(), minNode.getValue())); + } else if (currentNode.getValue().hashCode() > value.hashCode()) { + currentNode.setLeft(recursiveDelete(currentNode.getLeft(), value)); + } else { + currentNode.setRight(recursiveDelete(currentNode.getRight(), value)); + } + currentNode.recalculateSize(); + return currentNode; + } + + private boolean find(Node node, T value) { + if (node == null) { + return false; + } + if (node.getValue().equals(value)) { + return true; + } + return find(node.getLeft(), value) || find(node.getRight(), value); + } + + private Node getRandomNode(Node currentNode) { + int leftSize = currentNode.getLeft() != null ? currentNode.getLeft().getSize() : 0; + int rightSize = currentNode.getRight() != null ? currentNode.getRight().getSize() : 0; + int totalSize = leftSize + rightSize + 1; + int randomIndex = random.nextInt(totalSize); + if (randomIndex < leftSize) { + return getRandomNode(currentNode.getLeft()); + } else if (randomIndex == leftSize) { + return currentNode; + } else { + return getRandomNode(currentNode.getRight()); + } + } + + public RandomSet() { + root = null; + } public boolean insert(T value) { - throw new UnsupportedOperationException("Not implemented"); + var result = recursiveInsert(root, value); + if (result == null) { + return false; + } + root = result; + return true; } public boolean remove(T value) { - throw new UnsupportedOperationException("Not implemented"); + if (!contains(value)) { + return false; + } + root = recursiveDelete(root, value); + return true; } public boolean contains(T value) { - throw new UnsupportedOperationException("Not implemented"); + return find(root, value); } public T getRandom() { - throw new UnsupportedOperationException("Not implemented"); + return getRandomNode(root).getValue(); } - } From be1370618e584cd02773a4cacbefe029938070ff Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 00:52:34 +0300 Subject: [PATCH 11/23] randomset: throw exception on empty set access --- .../lecture3/practice/randomSet/RandomSet.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index 0660ac9e..95fa8f4c 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -81,6 +81,9 @@ public RandomSet() { } public boolean insert(T value) { + if (root == null) { + throw new EmptySetException("Set is empty"); + } var result = recursiveInsert(root, value); if (result == null) { return false; @@ -90,6 +93,9 @@ public boolean insert(T value) { } public boolean remove(T value) { + if (root == null) { + throw new EmptySetException("Set is empty"); + } if (!contains(value)) { return false; } @@ -98,10 +104,16 @@ public boolean remove(T value) { } public boolean contains(T value) { + if (root == null) { + throw new EmptySetException("Set is empty"); + } return find(root, value); } public T getRandom() { + if (root == null) { + throw new EmptySetException("Set is empty"); + } return getRandomNode(root).getValue(); } } From 7c40a9466a9abb6308d02d6b8b3cb760afa855ce Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 00:54:46 +0300 Subject: [PATCH 12/23] randomset: remove throwing exception on first insert --- .../java/lectures/lecture3/practice/randomSet/RandomSet.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index 95fa8f4c..310596bb 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -81,9 +81,6 @@ public RandomSet() { } public boolean insert(T value) { - if (root == null) { - throw new EmptySetException("Set is empty"); - } var result = recursiveInsert(root, value); if (result == null) { return false; From c5d1b7db124ffa6708027347c802910886fb2933 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 01:03:27 +0300 Subject: [PATCH 13/23] randomset: remove throwing exception on contains --- .../java/lectures/lecture3/practice/randomSet/RandomSet.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index 310596bb..7ea7accb 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -101,9 +101,6 @@ public boolean remove(T value) { } public boolean contains(T value) { - if (root == null) { - throw new EmptySetException("Set is empty"); - } return find(root, value); } From cdd2f79632db21cd5377a25d6b402cfa3c698114 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 01:09:19 +0300 Subject: [PATCH 14/23] randomset: fix infinite recursion --- .../lectures/lecture3/practice/randomSet/RandomSet.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index 7ea7accb..c3657132 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -13,7 +13,8 @@ private Node recursiveInsert(Node currentNode, T value) { if (currentNode.getValue().equals(value)) { return null; } - if (currentNode.getValue().hashCode() > value.hashCode()) { + + if (currentNode.getValue().hashCode() >= value.hashCode()) { currentNode.setLeft(recursiveInsert(currentNode.getLeft(), value)); } else { currentNode.setRight(recursiveInsert(currentNode.getRight(), value)); @@ -90,9 +91,6 @@ public boolean insert(T value) { } public boolean remove(T value) { - if (root == null) { - throw new EmptySetException("Set is empty"); - } if (!contains(value)) { return false; } From 7d513d8f332a097574d580d36b0ad3fe1f7d10e4 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 01:12:46 +0300 Subject: [PATCH 15/23] randomset: T must be comparable --- .../lectures/lecture3/practice/randomSet/RandomSet.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index c3657132..30cdcb6a 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -2,7 +2,7 @@ import java.util.Random; -public class RandomSet { +public class RandomSet> { private Node root; private final Random random = new Random(); @@ -10,11 +10,14 @@ private Node recursiveInsert(Node currentNode, T value) { if (currentNode == null) { return new Node<>(value); } - if (currentNode.getValue().equals(value)) { + + int compareResult = currentNode.getValue().compareTo(value); + + if (compareResult == 0) { return null; } - if (currentNode.getValue().hashCode() >= value.hashCode()) { + if (compareResult > 0) { currentNode.setLeft(recursiveInsert(currentNode.getLeft(), value)); } else { currentNode.setRight(recursiveInsert(currentNode.getRight(), value)); From 02cc65f93b8a120a31d7eea54c19dea2072fb95f Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 01:19:50 +0300 Subject: [PATCH 16/23] randomset: fix nulling tree if insert duplicate --- .../lecture3/practice/randomSet/RandomSet.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index 30cdcb6a..cd661b10 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -37,7 +37,10 @@ private Node recursiveDelete(Node currentNode, T value) { if (currentNode == null) { return null; } - if (currentNode.getValue().equals(value)) { + + int compareResult = currentNode.getValue().compareTo(value); + + if (compareResult == 0) { if (currentNode.getLeft() == null) { return currentNode.getRight(); } @@ -47,7 +50,7 @@ private Node recursiveDelete(Node currentNode, T value) { Node minNode = findMin(currentNode.getRight()); currentNode.setValue(minNode.getValue()); currentNode.setRight(recursiveDelete(currentNode.getRight(), minNode.getValue())); - } else if (currentNode.getValue().hashCode() > value.hashCode()) { + } else if (compareResult > 0) { currentNode.setLeft(recursiveDelete(currentNode.getLeft(), value)); } else { currentNode.setRight(recursiveDelete(currentNode.getRight(), value)); @@ -85,11 +88,10 @@ public RandomSet() { } public boolean insert(T value) { - var result = recursiveInsert(root, value); - if (result == null) { + if (contains(value)) { return false; } - root = result; + root = recursiveInsert(root, value); return true; } From b485960430b54c1d3b3fbb48bdef6149f8c35405 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 01:23:34 +0300 Subject: [PATCH 17/23] randomset: trying to fix --- .../lecture3/practice/randomSet/RandomSet.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index cd661b10..74501308 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -14,12 +14,12 @@ private Node recursiveInsert(Node currentNode, T value) { int compareResult = currentNode.getValue().compareTo(value); if (compareResult == 0) { - return null; + return currentNode; } if (compareResult > 0) { currentNode.setLeft(recursiveInsert(currentNode.getLeft(), value)); - } else { + } else { currentNode.setRight(recursiveInsert(currentNode.getRight(), value)); } currentNode.recalculateSize(); @@ -63,10 +63,14 @@ private boolean find(Node node, T value) { if (node == null) { return false; } - if (node.getValue().equals(value)) { + int compareResult = node.getValue().compareTo(value); + if (compareResult == 0) { return true; } - return find(node.getLeft(), value) || find(node.getRight(), value); + if (compareResult > 0) { + return find(node.getLeft(), value); + } + return find(node.getRight(), value); } private Node getRandomNode(Node currentNode) { From de59e56bf778da937626e29aade25d5d4c733c0c Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 01:53:01 +0300 Subject: [PATCH 18/23] randomset: rewrite to treap --- .../lecture3/practice/randomSet/Pair.java | 4 + .../practice/randomSet/RandomSet.java | 85 ++++++++----------- 2 files changed, 40 insertions(+), 49 deletions(-) create mode 100644 src/main/java/hse/java/lectures/lecture3/practice/randomSet/Pair.java diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Pair.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Pair.java new file mode 100644 index 00000000..c3c56e79 --- /dev/null +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Pair.java @@ -0,0 +1,4 @@ +package hse.java.lectures.lecture3.practice.randomSet; + +public record Pair(T left, T right) { +} diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index 74501308..47032042 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -6,71 +6,56 @@ public class RandomSet> { private Node root; private final Random random = new Random(); - private Node recursiveInsert(Node currentNode, T value) { - if (currentNode == null) { - return new Node<>(value); + private boolean find(Node node, T value) { + if (node == null) { + return false; } - - int compareResult = currentNode.getValue().compareTo(value); - + int compareResult = node.getValue().compareTo(value); if (compareResult == 0) { - return currentNode; + return true; } - if (compareResult > 0) { - currentNode.setLeft(recursiveInsert(currentNode.getLeft(), value)); - } else { - currentNode.setRight(recursiveInsert(currentNode.getRight(), value)); - } - currentNode.recalculateSize(); - return currentNode; - } - - private Node findMin(Node node) { - while (node.getLeft() != null) { - node = node.getLeft(); + return find(node.getLeft(), value); } - return node; + return find(node.getRight(), value); } - private Node recursiveDelete(Node currentNode, T value) { + private Pair> split(Node currentNode, T value) { if (currentNode == null) { - return null; + return new Pair<>(null, null); } - int compareResult = currentNode.getValue().compareTo(value); - if (compareResult == 0) { - if (currentNode.getLeft() == null) { - return currentNode.getRight(); - } - if (currentNode.getRight() == null) { - return currentNode.getLeft(); - } - Node minNode = findMin(currentNode.getRight()); - currentNode.setValue(minNode.getValue()); - currentNode.setRight(recursiveDelete(currentNode.getRight(), minNode.getValue())); - } else if (compareResult > 0) { - currentNode.setLeft(recursiveDelete(currentNode.getLeft(), value)); + return new Pair<>(currentNode.getLeft(), currentNode.getRight()); + } else if (compareResult < 0) { + var splitResult = split(currentNode.getRight(), value); + currentNode.setRight(splitResult.left()); + currentNode.recalculateSize(); + return new Pair<>(currentNode, splitResult.right()); } else { - currentNode.setRight(recursiveDelete(currentNode.getRight(), value)); + var splitResult = split(currentNode.getLeft(), value); + currentNode.setLeft(splitResult.right()); + currentNode.recalculateSize(); + return new Pair<>(splitResult.left(), currentNode); } - currentNode.recalculateSize(); - return currentNode; } - private boolean find(Node node, T value) { - if (node == null) { - return false; + private Node merge(Node leftNode, Node rightNode) { + if (leftNode == null) { + return rightNode; } - int compareResult = node.getValue().compareTo(value); - if (compareResult == 0) { - return true; + if (rightNode == null) { + return leftNode; } - if (compareResult > 0) { - return find(node.getLeft(), value); + if (random.nextInt(leftNode.getSize() + rightNode.getSize()) < leftNode.getSize()) { + leftNode.setRight(merge(leftNode.getRight(), rightNode)); + leftNode.recalculateSize(); + return leftNode; + } else { + rightNode.setLeft(merge(leftNode, rightNode.getLeft())); + rightNode.recalculateSize(); + return rightNode; } - return find(node.getRight(), value); } private Node getRandomNode(Node currentNode) { @@ -95,7 +80,8 @@ public boolean insert(T value) { if (contains(value)) { return false; } - root = recursiveInsert(root, value); + var splitResult = split(root, value); + root = merge(merge(splitResult.left(), new Node<>(value)), splitResult.right()); return true; } @@ -103,7 +89,8 @@ public boolean remove(T value) { if (!contains(value)) { return false; } - root = recursiveDelete(root, value); + var splitResult = split(root, value); + root = merge(splitResult.left(), splitResult.right()); return true; } From ffaf57799907c340fc05e35cf220b5f4c3beb315 Mon Sep 17 00:00:00 2001 From: illidvn Date: Sat, 14 Feb 2026 02:23:14 +0300 Subject: [PATCH 19/23] randomset: refactor - split treap and randomset --- .../lecture3/practice/randomSet/Pair.java | 2 +- .../practice/randomSet/RandomSet.java | 91 ++------------- .../lecture3/practice/randomSet/Treap.java | 110 ++++++++++++++++++ 3 files changed, 119 insertions(+), 84 deletions(-) create mode 100644 src/main/java/hse/java/lectures/lecture3/practice/randomSet/Treap.java diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Pair.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Pair.java index c3c56e79..5e8b9193 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Pair.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Pair.java @@ -1,4 +1,4 @@ package hse.java.lectures.lecture3.practice.randomSet; -public record Pair(T left, T right) { +public record Pair(L left, R right) { } diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java index 47032042..51e61ac1 100644 --- a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/RandomSet.java @@ -3,105 +3,30 @@ import java.util.Random; public class RandomSet> { - private Node root; + private final Treap treap; private final Random random = new Random(); - private boolean find(Node node, T value) { - if (node == null) { - return false; - } - int compareResult = node.getValue().compareTo(value); - if (compareResult == 0) { - return true; - } - if (compareResult > 0) { - return find(node.getLeft(), value); - } - return find(node.getRight(), value); - } - - private Pair> split(Node currentNode, T value) { - if (currentNode == null) { - return new Pair<>(null, null); - } - int compareResult = currentNode.getValue().compareTo(value); - if (compareResult == 0) { - return new Pair<>(currentNode.getLeft(), currentNode.getRight()); - } else if (compareResult < 0) { - var splitResult = split(currentNode.getRight(), value); - currentNode.setRight(splitResult.left()); - currentNode.recalculateSize(); - return new Pair<>(currentNode, splitResult.right()); - } else { - var splitResult = split(currentNode.getLeft(), value); - currentNode.setLeft(splitResult.right()); - currentNode.recalculateSize(); - return new Pair<>(splitResult.left(), currentNode); - } - } - - private Node merge(Node leftNode, Node rightNode) { - if (leftNode == null) { - return rightNode; - } - if (rightNode == null) { - return leftNode; - } - if (random.nextInt(leftNode.getSize() + rightNode.getSize()) < leftNode.getSize()) { - leftNode.setRight(merge(leftNode.getRight(), rightNode)); - leftNode.recalculateSize(); - return leftNode; - } else { - rightNode.setLeft(merge(leftNode, rightNode.getLeft())); - rightNode.recalculateSize(); - return rightNode; - } - } - - private Node getRandomNode(Node currentNode) { - int leftSize = currentNode.getLeft() != null ? currentNode.getLeft().getSize() : 0; - int rightSize = currentNode.getRight() != null ? currentNode.getRight().getSize() : 0; - int totalSize = leftSize + rightSize + 1; - int randomIndex = random.nextInt(totalSize); - if (randomIndex < leftSize) { - return getRandomNode(currentNode.getLeft()); - } else if (randomIndex == leftSize) { - return currentNode; - } else { - return getRandomNode(currentNode.getRight()); - } - } - public RandomSet() { - root = null; + treap = new Treap<>(); } public boolean insert(T value) { - if (contains(value)) { - return false; - } - var splitResult = split(root, value); - root = merge(merge(splitResult.left(), new Node<>(value)), splitResult.right()); - return true; + return treap.tryInsert(value); } public boolean remove(T value) { - if (!contains(value)) { - return false; - } - var splitResult = split(root, value); - root = merge(splitResult.left(), splitResult.right()); - return true; + return treap.tryRemove(value); } public boolean contains(T value) { - return find(root, value); + return treap.contains(value); } public T getRandom() { - if (root == null) { + if (treap.getSize() == 0) { throw new EmptySetException("Set is empty"); } - return getRandomNode(root).getValue(); + int randomIndex = random.nextInt(treap.getSize()); + return treap.get(randomIndex); } } diff --git a/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Treap.java b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Treap.java new file mode 100644 index 00000000..3e133b13 --- /dev/null +++ b/src/main/java/hse/java/lectures/lecture3/practice/randomSet/Treap.java @@ -0,0 +1,110 @@ +package hse.java.lectures.lecture3.practice.randomSet; + +import java.util.Optional; +import java.util.Random; + +public class Treap> { + private Node root; + private final Random random; + + public Treap() { + root = null; + random = new Random(); + } + + private boolean findByValue(Node node, T value) { + if (node == null) { + return false; + } + int compareResult = node.getValue().compareTo(value); + if (compareResult == 0) { + return true; + } + if (compareResult > 0) { + return findByValue(node.getLeft(), value); + } + return findByValue(node.getRight(), value); + } + + private Optional findByIndex(Node currentNode, int index) { + if (currentNode == null) { + return Optional.empty(); + } + int leftSize = currentNode.getLeft() != null ? currentNode.getLeft().getSize() : 0; + if (index < leftSize) { + return findByIndex(currentNode.getLeft(), index); + } else if (index == leftSize) { + return Optional.of(currentNode.getValue()); + } else { + return findByIndex(currentNode.getRight(), index - leftSize - 1); + } + } + + private Pair, Node> split(Node currentNode, T value) { + if (currentNode == null) { + return new Pair<>(null, null); + } + int compareResult = currentNode.getValue().compareTo(value); + if (compareResult == 0) { + return new Pair<>(currentNode.getLeft(), currentNode.getRight()); + } else if (compareResult < 0) { + var splitResult = split(currentNode.getRight(), value); + currentNode.setRight(splitResult.left()); + currentNode.recalculateSize(); + return new Pair<>(currentNode, splitResult.right()); + } else { + var splitResult = split(currentNode.getLeft(), value); + currentNode.setLeft(splitResult.right()); + currentNode.recalculateSize(); + return new Pair<>(splitResult.left(), currentNode); + } + } + + private Node merge(Node leftNode, Node rightNode) { + if (leftNode == null) { + return rightNode; + } + if (rightNode == null) { + return leftNode; + } + if (random.nextInt(leftNode.getSize() + rightNode.getSize()) < leftNode.getSize()) { + leftNode.setRight(merge(leftNode.getRight(), rightNode)); + leftNode.recalculateSize(); + return leftNode; + } else { + rightNode.setLeft(merge(leftNode, rightNode.getLeft())); + rightNode.recalculateSize(); + return rightNode; + } + } + + public int getSize() { + return root != null ? root.getSize() : 0; + } + + public boolean contains(T value) { + return findByValue(root, value); + } + + public boolean tryInsert(T value) { + if (contains(value)) { + return false; + } + var splitResult = split(root, value); + root = merge(merge(splitResult.left(), new Node<>(value)), splitResult.right()); + return true; + } + + public boolean tryRemove(T value) { + if (!contains(value)) { + return false; + } + var splitResult = split(root, value); + root = merge(splitResult.left(), splitResult.right()); + return true; + } + + public T get(int index) { + return findByIndex(root, index).orElseThrow(() -> new IllegalArgumentException("Index out of bounds")); + } +} From eac937640404ee0cd3a29327a416141dc49f280a Mon Sep 17 00:00:00 2001 From: illidvn Date: Fri, 27 Feb 2026 23:25:47 +0300 Subject: [PATCH 20/23] commander: implement far manager clone --- .../hse/java/commander/MainController.java | 114 +++++++++++++++--- .../hse/java/commander/commander-ui.fxml | 25 +++- 2 files changed, 114 insertions(+), 25 deletions(-) diff --git a/commander/src/main/java/hse/java/commander/MainController.java b/commander/src/main/java/hse/java/commander/MainController.java index 19c700fb..a0e2dda9 100644 --- a/commander/src/main/java/hse/java/commander/MainController.java +++ b/commander/src/main/java/hse/java/commander/MainController.java @@ -2,35 +2,111 @@ import javafx.fxml.FXML; import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.ListView; -public class MainController { +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +public class MainController { @FXML public Button move; + @FXML + public ListView left; + @FXML + public ListView right; + @FXML + public Label leftPathLabel; + @FXML + public Label rightPathLabel; + + private File leftDirectory = new File(System.getProperty("user.home")); + private File rightDirectory = new File(System.getProperty("user.home")); public void initialize() { - move.setOnMouseClicked(event -> { + refresh(left, leftDirectory); + refresh(right, rightDirectory); + left.setOnMouseClicked(event -> { + if (event.getClickCount() == 2) { + handleNavigation(left, true); + } }); - System.out.println(System.getProperty("user.home")); - left.getItems().add("Kek"); - - left.setOnMouseClicked(event -> { - if (event.getClickCount() == 2) { - int index = left.getSelectionModel().getSelectedIndex(); - if (index >= 0) { - left.getItems().set(index, "clicked"); - } - } - }); - } - @FXML - public ListView left; + right.setOnMouseClicked(event -> { + if (event.getClickCount() == 2) { + handleNavigation(right, false); + } + }); - @FXML - public ListView right; + move.setOnAction(event -> moveFile()); + } + + private void refresh(ListView list, File directory) { + list.getItems().clear(); + list.getItems().add(".."); + File[] files = directory.listFiles(); + if (files != null) { + for (File file : files) { + list.getItems().add(file.isDirectory() ? "[" + file.getName() + "]" : file.getName()); + } + } + if (list == left) { + leftPathLabel.setText(directory.getAbsolutePath()); + } else { + rightPathLabel.setText(directory.getAbsolutePath()); + } + } + + private void handleNavigation(ListView list, boolean isLeft) { + String selected = list.getSelectionModel().getSelectedItem(); + if (selected == null) return; + + File currentDir = isLeft ? leftDirectory : rightDirectory; + File nextDirectory; + + if (selected.equals("..")) { + nextDirectory = currentDir.getParentFile(); + } else { + String name = selected.replace("[", "").replace("]", ""); + nextDirectory = new File(currentDir, name); + } + + if (nextDirectory != null && nextDirectory.isDirectory()) { + if (isLeft) { + leftDirectory = nextDirectory; + } else { + rightDirectory = nextDirectory; + } + refresh(list, nextDirectory); + } + } + + private void moveFile() { + boolean moveFromLeft = left.getSelectionModel().getSelectedItem() != null; + ListView fromList = moveFromLeft ? left : right; + File fromDirectory = moveFromLeft ? leftDirectory : rightDirectory; + File toDirectory = moveFromLeft ? rightDirectory : leftDirectory; + + String selected = fromList.getSelectionModel().getSelectedItem(); + + if (selected == null || selected.equals("..")) { + return; + } + String name = selected.replace("[", "").replace("]", ""); + Path source = new File(fromDirectory, name).toPath(); + Path destination = new File(toDirectory, name).toPath(); -} + try { + Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING); + refresh(left, leftDirectory); + refresh(right, rightDirectory); + } catch (IOException e) { + System.err.println("Анлак, не удалось перенести: " + e.getMessage()); + } + } +} \ No newline at end of file diff --git a/commander/src/main/resources/hse/java/commander/commander-ui.fxml b/commander/src/main/resources/hse/java/commander/commander-ui.fxml index 23d8db23..91e0ad8f 100644 --- a/commander/src/main/resources/hse/java/commander/commander-ui.fxml +++ b/commander/src/main/resources/hse/java/commander/commander-ui.fxml @@ -2,13 +2,26 @@ + - - + + + + - - + + + + + -