From 527f5c249a8c5c59ee79382005b33e07df0cd7f2 Mon Sep 17 00:00:00 2001 From: Richard Elkins Date: Thu, 16 Jan 2025 11:45:36 -0600 Subject: [PATCH] New test case: blocking-queues-2-3-5 --- HISTORY.md | 1 + TEST_CASE_CATALOGUE.md | 1 + VERSION.txt | 2 +- tests/blocking-queues-2-3-5/main.java | 139 ++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 tests/blocking-queues-2-3-5/main.java diff --git a/HISTORY.md b/HISTORY.md index 498a53b..12794b1 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -4,6 +4,7 @@ This file is a version history of jacotest amendments. Entries appear in versio | `Date` | `Version` | `Contents` | | :------------: | :---: | :--- | |||| +| 2025-01-16 | 3.5.3 | New test case: blocking-queues-2-3-5. | | 2025-01-09 | 3.5.2 | New test case: iface-with-default-func. | | 2024-12-30 | 3.5.1 | JACOBIN-634: report fix. | | 2024-12-29 | 3.4.38 | New test case: crypto-salsa must throw new AssertionError. | diff --git a/TEST_CASE_CATALOGUE.md b/TEST_CASE_CATALOGUE.md index 2de8c02..74630e4 100644 --- a/TEST_CASE_CATALOGUE.md +++ b/TEST_CASE_CATALOGUE.md @@ -12,6 +12,7 @@ This file is a catalogue of jacotest test cases. Entries appear in alphabetic o | big-integer-2 | java.math.BigInteger tests #2 | | big-integer-3 | java.math.BigInteger tests #3 | | blockchain | Create a blockchain, amend it, and validate it | +| blocking-queues-2-3-5 | Solve the 2,3,5 problem; blocking queues | | blum-blum-shub | Blum-Blum-Shub (BBS) Algorithm for PseudoRandom Number Generation | | bohr-atom | Compute properties of the Bohr model of the atom | | casting | Casting between primitive variable types | diff --git a/VERSION.txt b/VERSION.txt index ccedee9..5c2545f 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -v3.5.2 +v3.5.3 diff --git a/tests/blocking-queues-2-3-5/main.java b/tests/blocking-queues-2-3-5/main.java new file mode 100644 index 0000000..495802f --- /dev/null +++ b/tests/blocking-queues-2-3-5/main.java @@ -0,0 +1,139 @@ +// Inspiration: https://github.com/golang/go/blob/master/test/235.go + +// Solve the 2,3,5 problem (print all numbers with 2, 3, or 5 as a factor). +// Multithreaded. +// Instead of Golang channels, we'll use Java blocking queues. + +import java.util.PriorityQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class main { + + private static final int CHANNEL_CAPACITY = 100; + + // Golang channel implementation using a Java BlockingQueue. + static class Channel { + private final BlockingQueue queue; + + public Channel(int capacity) { + queue = new LinkedBlockingQueue<>(capacity); + } + + public void send(long value) throws InterruptedException { + queue.put(value); + } + + public long receive() throws InterruptedException { + return queue.take(); + } + } + + // CreateServerThread object. + // Function startup: + // * Create a thread receiving an input value and multiply it by the startup factor. + // * Return the 2 created channels to the caller. + static class CreateServerThread { + + public static Channel[] startup(long factor) { + Channel input = new Channel(CHANNEL_CAPACITY); + Channel output = new Channel(CHANNEL_CAPACITY); + + // Define a channel server based on the initial factor. + Thread th = new Thread(() -> { + try { + while (true) { + long value = input.receive(); + // Stop request? + if (value < 1L) + return; // Yes. Conclude this thread. + output.send(value * factor); + } + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + } + ); + + // Start the thread. + th.start(); + + // Return the 2 channels to caller. + return new Channel[]{input, output}; + } + } + + // Helper function to find the minimum value in an array of longs. + static long minimum(long[] values) { + long minValue = values[0]; + for (int ix = 1; ix < values.length; ix++) { + if (values[ix] < minValue) { + minValue = values[ix]; + } + } + return minValue; + } + + // Main entry point. + public static void main(String[] args) throws InterruptedException { + // 2, 3, 5: + long[] factors = {2, 3, 5}; + // Number of channel pairs: + int numFactors = factors.length; + long[] expectedOutput = { + 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27, 30, 32, 36, + 40, 45, 48, 50, 54, 60, 64, 72, 75, 80, 81, 90, 96, 100, 108, 120, 125, + 128, 135, 144, 150, 160, 162, 180, 192, 200, 216, 225, 240, 243, 250, + 256, 270, 288, 300, 320, 324, 360, 375, 384, 400, 405, 432, 450, 480, + 486, 500, 512, 540, 576, 600, 625, 640, 648, 675, 720, 729, 750, 768, + 800, 810, 864, 900, 960, 972, 1000, 1024, 1080, 1125, 1152, 1200, 1215, + 1250, 1280, 1296, 1350, 1440, 1458, 1500, 1536, 1600}; + + long mini = 1; + Channel[] inputs = new Channel[numFactors]; + Channel[] outputs = new Channel[numFactors]; + long[] values = new long[numFactors]; + + // For each factor, create a thread associated with a channel pair + // and initialize its values entry to 1. + for (int ix = 0; ix < numFactors; ix++) { + Channel[] channels = CreateServerThread.startup(factors[ix]); + inputs[ix] = channels[0]; + outputs[ix] = channels[1]; + values[ix] = mini; + } + + // Process all the expected output. + for (long expected : expectedOutput) { + + // Send mini to all threads. + for (int ix = 0; ix < numFactors; ix++) { + inputs[ix].send(mini); + } + + // For all threads: if the current value is unchanged, receive a new value. + for (int ix = 0; ix < numFactors; ix++) { + if (values[ix] == mini) { + values[ix] = outputs[ix].receive(); + } + } + + // mini = minimum value over all threads. + mini = minimum(values); + if (mini != expected) { + String errMsg = String.format("*** ERROR, expected %d, observed %d\n", + expected, mini); + throw new AssertionError(errMsg); + } + } + + // Send stop request to all threads. + for (int ix = 0; ix < numFactors; ix++) { + inputs[ix].send(0L); + } + + System.out.println("Success!"); + } +} + +