From 723e517e7d1cf071c3136bbeb44cc70aee8066c9 Mon Sep 17 00:00:00 2001 From: TJirden Date: Sat, 7 Feb 2026 20:05:59 +0300 Subject: [PATCH 1/3] lecture3/tasks --- .../java/lectures/lecture3/tasks/atm/Atm.java | 64 +++++++++- .../lecture3/tasks/html/HtmlDocument.java | 120 +++++++++++++++++- 2 files changed, 177 insertions(+), 7 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..032a2f40 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 @@ -32,14 +32,72 @@ public static Denomination fromInt(int value) { public Atm() { } - public void deposit(Map banknotes){} + public void deposit(Map banknotes) { + if (banknotes == null){ + throw new InvalidDepositException("null"); + } + int sum = 0; + for (var entry : banknotes.entrySet()) { + if (entry.getValue() < 0) { + throw new InvalidDepositException("Отрицательное денег"); + } + + sum += entry.getValue(); + } + if (sum == 0) { + throw new InvalidDepositException("Ноль денег"); + } + for (var entry : banknotes.entrySet()) { + this.banknotes.merge(entry.getKey(), entry.getValue(), Integer::sum); + } + } public Map withdraw(int amount) { - return Map.of(); + if (amount <= 0) { + throw new InvalidAmountException("<=0 денег"); + } else if (amount % 50 != 0) { + throw new CannotDispenseException("Че ты у меня просишь"); + } + if (amount > getBalance()){ + throw new InsufficientFundsException("нет столько денег"); + } + + Map result = new EnumMap<>(Denomination.class); + int remaining = amount; + + Denomination[] denominations = Denomination.values(); + for (int i = denominations.length - 1; i >= 0; i--) { + Denomination denom = denominations[i]; + int available = banknotes.getOrDefault(denom, 0); + if (available > 0 && denom.value() <= remaining) { + int needed = remaining / denom.value(); + int toTake = Math.min(needed, available); + + if (toTake > 0) { + result.put(denom, toTake); + remaining -= toTake * denom.value(); + } + } + } + + if (remaining != 0) { + throw new CannotDispenseException("Невозможно выдать сумму"); + } + + for (var entry : result.entrySet()) { + banknotes.merge(entry.getKey(), entry.getValue(), + (oldVal, newVal) -> oldVal - newVal); + } + + return result; } public int getBalance() { - return 0; + int balance = 0; + for (var entry : banknotes.entrySet()) { + balance += entry.getKey().value() * entry.getValue(); + } + return balance; } } 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..d6229d68 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,12 @@ 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 Set SUPPORTED_TAGS = Set.of("html", "head", "body", "div", "p"); + public HtmlDocument(String filePath) { this(Path.of(filePath)); } @@ -27,6 +27,118 @@ private String readFile(Path filePath) { } } - private void validate(String content){} + private void validate(String content) { + ArrayList tagStack = new ArrayList<>(); + int position = 0; + boolean hasHtmlTag = false; + boolean hasHeadTag = false; + boolean hasBodyTag = false; + boolean headFinished = false; + + while (position < content.length()) { + if (content.charAt(position) != '<') { + position++; + continue; + } + + int tagStart = position; + int tagEnd = content.indexOf('>', tagStart); + + if (tagEnd == -1) { + throw new InvalidStructureException("Unclosed tag bracket at position " + tagStart); + } + + String tagContent = content.substring(tagStart + 1, tagEnd).trim(); + position = tagEnd + 1; + + boolean isClosingTag = false; + if (tagContent.startsWith("/")) { + isClosingTag = true; + tagContent = tagContent.substring(1).trim(); + } + + String tagName = extractTagName(tagContent); + + if (isClosingTag) { + handleClosingTag(tagName, tagStack); + + if (tagName.equals("head")) { + headFinished = true; + } + } else { + handleOpeningTag(tagName, tagStack); + switch (tagName) { + case "html" -> { + if (hasHtmlTag || tagStack.size()!= 1) { + throw new InvalidStructureException("html"); + } + hasHtmlTag = true; + } + case "head" -> { + if (hasHeadTag || hasBodyTag || !hasHtmlTag) { + throw new InvalidStructureException("head"); + } + hasHeadTag = true; + } + case "body" -> { + if (hasBodyTag || !hasHtmlTag || hasHeadTag && !headFinished) { + throw new InvalidStructureException("body"); + } + + hasBodyTag = true; + } + default -> { + if (!(hasBodyTag || hasHeadTag)){ + throw new InvalidStructureException("дефолт"); + } + } + } + + } + } + + if (!tagStack.isEmpty()) { + throw new UnclosedTagException("Незакрытый тег"); + } + + if (!hasHtmlTag) { + throw new InvalidStructureException("Нет html"); + } + } + + private String extractTagName(String tagContent) { + int spaceIndex = tagContent.indexOf(' '); + if (spaceIndex != -1) { + return tagContent.substring(0, spaceIndex).toLowerCase(); + } + return tagContent.toLowerCase(); + } + + private void checkSupport(String tagName) { + if (!SUPPORTED_TAGS.contains(tagName)) { + throw new UnsupportedTagException("Тег фигня"); + } + } + + private void handleOpeningTag(String tagName, ArrayList tagStack) { + checkSupport(tagName); + tagStack.add(tagName); + } + + private void handleClosingTag(String tagName, ArrayList tagStack) { + checkSupport(tagName); + + if (tagStack.isEmpty()) { + throw new UnexpectedClosingTagException("Хочу закрыть а нет открытых"); + } + + String lastOpenedTag = tagStack.get(tagStack.size() - 1); + + if (!lastOpenedTag.equals(tagName)) { + throw new MismatchedClosingTagException("Хочу закрыть а там не тот"); + } + + tagStack.remove(tagStack.size() - 1); + } } From 3e44a4e5219a5e2c1e939219408774fcfbd86da5 Mon Sep 17 00:00:00 2001 From: TJirden Date: Fri, 20 Mar 2026 21:37:51 +0300 Subject: [PATCH 2/3] queue:queue --- .../tasks/queue/BoundedBlockingQueue.java | 39 ++++++++++++++----- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture6/tasks/queue/BoundedBlockingQueue.java b/src/main/java/hse/java/lectures/lecture6/tasks/queue/BoundedBlockingQueue.java index 816f3ee6..ab4059a7 100644 --- a/src/main/java/hse/java/lectures/lecture6/tasks/queue/BoundedBlockingQueue.java +++ b/src/main/java/hse/java/lectures/lecture6/tasks/queue/BoundedBlockingQueue.java @@ -1,25 +1,46 @@ package hse.java.lectures.lecture6.tasks.queue; +import java.util.LinkedList; +import java.util.Queue; + public class BoundedBlockingQueue { + private final Queue queue; + private final int cap; public BoundedBlockingQueue(int capacity) { - + if (capacity <= 0) { + throw new IllegalArgumentException("capacity must be > 0"); + } + this.cap = capacity; + this.queue = new LinkedList<>(); } - public void put(T item) throws InterruptedException { - + public synchronized void put(T item) throws InterruptedException { + if (item == null) { + throw new NullPointerException("null is not allowed"); + } + while (queue.size() == cap) { + wait(); + } + queue.add(item); + notifyAll(); } - public T take() throws InterruptedException { - return null; + public synchronized T take() throws InterruptedException { + while (queue.isEmpty()) { + wait(); + } + T item = queue.poll(); + notifyAll(); + return item; } - public int size() { - return 0; + public synchronized int size() { + return queue.size(); } public int capacity() { - return 0; + return cap; } -} +} \ No newline at end of file From 7a6af3eedd56e2115654dccdda909cfd2a57a562 Mon Sep 17 00:00:00 2001 From: TJirden Date: Fri, 20 Mar 2026 21:55:40 +0300 Subject: [PATCH 3/3] synchronizer:synchronizer --- .../tasks/synchronizer/StreamWriter.java | 9 ++- .../tasks/synchronizer/StreamingMonitor.java | 59 ++++++++++++++++++- .../tasks/synchronizer/Synchronizer.java | 18 +++++- 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/StreamWriter.java b/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/StreamWriter.java index fedb5e66..6215a835 100644 --- a/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/StreamWriter.java +++ b/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/StreamWriter.java @@ -28,9 +28,14 @@ public void attachMonitor(StreamingMonitor monitor) { public void run() { // Writer threads are intentionally infinite for the task contract. while (true) { + if (!monitor.awaitTurn(id)) { + return; + } + output.print(message); onTick.run(); + + monitor.tickDone(); } } - -} +} \ No newline at end of file diff --git a/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/StreamingMonitor.java b/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/StreamingMonitor.java index 68e8f279..d972747a 100644 --- a/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/StreamingMonitor.java +++ b/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/StreamingMonitor.java @@ -1,5 +1,60 @@ package hse.java.lectures.lecture6.tasks.synchronizer; +import java.util.List; + public class StreamingMonitor { - // impl your sync here -} + private final List sortedIds; + private int currentIndex; + + private int totalTicksDone; + private final int totalTicksRequired; + + private boolean finished; + + public StreamingMonitor(List sortedIds, int ticksPerWriter) { + this.sortedIds = sortedIds; + this.currentIndex = 0; + this.totalTicksRequired = sortedIds.size() * ticksPerWriter; + this.totalTicksDone = 0; + this.finished = false; + } + + private int currentId() { + return sortedIds.get(currentIndex); + } + + public synchronized boolean awaitTurn(int writerId) { + while (currentId() != writerId && !finished) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return false; + } + } + return !finished; + } + + public synchronized void tickDone() { + totalTicksDone++; + + currentIndex = (currentIndex + 1) % sortedIds.size(); + + if (totalTicksDone >= totalTicksRequired) { + finished = true; + } + + notifyAll(); + } + + public synchronized void awaitFinished() { + while (!finished) { + try { + wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/Synchronizer.java b/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/Synchronizer.java index 3cb8aded..f19a9d5a 100644 --- a/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/Synchronizer.java +++ b/src/main/java/hse/java/lectures/lecture6/tasks/synchronizer/Synchronizer.java @@ -1,6 +1,7 @@ package hse.java.lectures.lecture6.tasks.synchronizer; import java.util.List; +import java.util.stream.Collectors; public class Synchronizer { @@ -22,12 +23,23 @@ public Synchronizer(List tasks, int ticksPerWriter) { * in strict ascending id order. */ public void execute() { - // add monitor and sync + List sortedIds = tasks.stream() + .map(StreamWriter::getId) + .sorted() + .collect(Collectors.toList()); + + StreamingMonitor monitor = new StreamingMonitor(sortedIds, ticksPerWriter); + + for (StreamWriter writer : tasks) { + writer.attachMonitor(monitor); + } + for (StreamWriter writer : tasks) { Thread worker = new Thread(writer, "stream-writer-" + writer.getId()); worker.setDaemon(true); worker.start(); } - } -} + monitor.awaitFinished(); + } +} \ No newline at end of file