From 0cee62d2bef149e1b0e1b1a1271d15653465183f Mon Sep 17 00:00:00 2001 From: Ana Date: Thu, 2 Nov 2023 10:42:25 +0100 Subject: [PATCH 1/5] update readme policy --- CONTRIBUTING.md | 57 ------------------------------------------------- README.md | 32 ++++++--------------------- 2 files changed, 7 insertions(+), 82 deletions(-) delete mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 85ab22a..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,57 +0,0 @@ -*Detailed instructions on how to contribute to the project, if applicable. Must include section about Oracle Contributor Agreement with link and instructions* - -# Contributing to this repository - -We welcome your contributions! There are multiple ways to contribute. - -## Opening issues - -For bugs or enhancement requests, please file a GitHub issue unless it's -security related. When filing a bug remember that the better written the bug is, -the more likely it is to be fixed. If you think you've found a security -vulnerability, do not raise a GitHub issue and follow the instructions in our -[security policy](./SECURITY.md). - -## Contributing code - -We welcome your code contributions. Before submitting code via a pull request, -you will need to have signed the [Oracle Contributor Agreement][OCA] (OCA) and -your commits need to include the following line using the name and e-mail -address you used to sign the OCA: - -```text -Signed-off-by: Your Name -``` - -This can be automatically added to pull requests by committing with `--sign-off` -or `-s`, e.g. - -```text -git commit --signoff -``` - -Only pull requests from committers that can be verified as having signed the OCA -can be accepted. - -## Pull request process - -1. Ensure there is an issue created to track and discuss the fix or enhancement - you intend to submit. -1. Fork this repository. -1. Create a branch in your fork to implement the changes. We recommend using - the issue number as part of your branch name, e.g. `1234-fixes`. -1. Ensure that any documentation is updated with the changes that are required - by your change. -1. Ensure that any samples are updated if the base image has been changed. -1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly - what your changes are meant to do and provide simple steps on how to validate. - your changes. Ensure that you reference the issue you created as well. -1. We will assign the pull request to 2-3 people for review before it is merged. - -## Code of conduct - -Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If you'd -like more specific guidelines, see the [Contributor Covenant Code of Conduct][COC]. - -[OCA]: https://oca.opensource.oracle.com -[COC]: https://www.contributor-covenant.org/version/1/4/code-of-conduct/ diff --git a/README.md b/README.md index fe93493..6ee02e4 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,19 @@ -*This repository acts as a template for all of Oracle’s GitHub repositories. It contains information about the guidelines for those repositories. All files and sections contained in this template are mandatory, and a GitHub app ensures alignment with these guidelines. To get started with a new repository, replace the italic paragraphs with the respective text for your project.* +# Samples -# Project name - -*Describe your project's features, functionality and target audience* +This repository contains sample code in order for developers to reproduce complex examples shared on https://inside.java or https://dev.java. +The code is stored in a directory named after the URL slug of the published article. ## Installation -*Provide detailed step-by-step installation instructions. You can name this section **How to Run** or **Getting Started** instead of **Installation** if that's more acceptable for your project* - -## Documentation - -*Developer-oriented documentation can be published on GitHub, but all product documentation must be published on * - -## Examples - -*Describe any included examples or provide a link to a demo/tutorial* +Each sample directory contains its own instructions (SETUP.md) on how to run the example(s). -## Help +## Content -*Inform users on where to get help or how to receive official support from Oracle (if applicable)* + ## Contributing -*If your project has specific contribution requirements, update the CONTRIBUTING.md file to ensure those requirements are clearly explained* - -This project welcomes contributions from the community. Before submitting a pull request, please [review our contribution guide](./CONTRIBUTING.md) +If you spot an error or you know of an alternative way/an extra question/solution that you can offer, please reach out to us by email [java_devrel_grp](mailto:java_devrel_grp@oracle.com). ## Security @@ -32,13 +21,6 @@ Please consult the [security guide](./SECURITY.md) for our responsible security ## License -*The correct copyright notice format for both documentation and software is* - "Copyright (c) [year,] year Oracle and/or its affiliates." -*You must include the year the content was first released (on any platform) and the most recent year in which it was revised* - Copyright (c) 2023 Oracle and/or its affiliates. - -*Replace this statement if your project is not licensed under the UPL* - Released under the Universal Permissive License v1.0 as shown at . From 21f7a271fd9e498f049ccf4d7b10caf0c7cab024 Mon Sep 17 00:00:00 2001 From: Ana Date: Thu, 2 Nov 2023 11:01:50 +0100 Subject: [PATCH 2/5] Add CountDownProblem directory --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6ee02e4..2cb1210 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,11 @@ Each sample directory contains its own instructions (SETUP.md) on how to run the ## Content - +* [CountDownProblem.java](countdown-haskell-java/CountDownProblem.java) published on https://inside.java/2023/11/02/countdown-haskell-java ## Contributing -If you spot an error or you know of an alternative way/an extra question/solution that you can offer, please reach out to us by email [java_devrel_grp](mailto:java_devrel_grp@oracle.com). +If you spot an error, or you know of an alternative way/an extra question/solution that you can offer, please reach out to us by email [java_devrel_grp](mailto:java_devrel_grp@oracle.com). ## Security From dbb7d5028c97ed8bcd1342ecd1edfba000208a3b Mon Sep 17 00:00:00 2001 From: Ana Date: Thu, 2 Nov 2023 12:36:05 +0100 Subject: [PATCH 3/5] Update directory name --- README.md | 3 +- .../CountDownProblem.java | 317 ++++++++++++++++++ countdown-problem-java21/SETUP.md | 20 ++ 3 files changed, 338 insertions(+), 2 deletions(-) create mode 100644 countdown-problem-java21/CountDownProblem.java create mode 100644 countdown-problem-java21/SETUP.md diff --git a/README.md b/README.md index 2cb1210..6272c8b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # Samples This repository contains sample code in order for developers to reproduce complex examples shared on https://inside.java or https://dev.java. -The code is stored in a directory named after the URL slug of the published article. ## Installation @@ -9,7 +8,7 @@ Each sample directory contains its own instructions (SETUP.md) on how to run the ## Content -* [CountDownProblem.java](countdown-haskell-java/CountDownProblem.java) published on https://inside.java/2023/11/02/countdown-haskell-java +* [CountDownProblem.java](countdown-problem-java21/CountDownProblem.java) published on https://inside.java/2023/11/02/countdown-haskell-java ## Contributing diff --git a/countdown-problem-java21/CountDownProblem.java b/countdown-problem-java21/CountDownProblem.java new file mode 100644 index 0000000..5780388 --- /dev/null +++ b/countdown-problem-java21/CountDownProblem.java @@ -0,0 +1,317 @@ +import java.util.ArrayList; +import java.util.List; +import java.util.OptionalInt; +import java.util.Set; +import java.util.stream.Stream; + +/* + * This program is Java port of the Haskell example at + * https://www.cs.nott.ac.uk/~pszgmh/pgp-countdown.hs + * + * The problem and the solution approaches are explained + * in Prof. Graham Hutton's youtube video at + * https://youtu.be/CiXDS3bBBUo?list=PLF1Z-APd9zK7usPMx3LGMZEHrECUGodd3 + * + * This Java program requires JDK 21+ + */ +class CountDownProblem { + + // data Op = Add | Sub | Mul | Div + enum Op { + Add, Sub, Mul, Div; + + // instance show Op + @Override + public String toString() { + return switch (this) { + case Add -> "+"; + case Sub -> "-"; + case Mul -> "*"; + case Div -> "/"; + }; + } + } + + // cache enum value array + static final Op[] operators = Op.values(); + + // valid' :: Op -> Int -> Int -> Bool + static boolean isValid(Op op, int x, int y) { + return switch (op) { + case Add -> x <= y; + case Sub -> x > y; + case Mul -> x != 1 && y != 1 && x <= y; + case Div -> y != 1 && x % y == 0; + }; + } + + // apply :: Op -> Int -> Int -> Int + static int apply(Op op, int x, int y) { + return switch (op) { + case Add -> x + y; + case Sub -> x - y; + case Mul -> x * y; + case Div -> x / y; + }; + } + + // data Expr = Val Int | App Op Expr Expr + sealed interface Expr { + // brak helper for instance Show Expr + static String brak(Expr expr) { + return switch (expr) { + // brak (Val n) = show n + case Val(var n) -> Integer.toString(n); + + // brak e = "(" ++ show e ++ ")" + default -> "(" + toStr(expr) + ")"; + }; + } + + // instance Show Expr + static String toStr(Expr expr) { + return switch (expr) { + // show (Val n) = show n + case Val(var n) -> Integer.toString(n); + + // show (App o l r) = brak l ++ show o ++ brak r + // where + // brak (Val n) = show n + // brak e = "(" ++ show e ++ ")" + case App(var op, var l, var r) -> brak(l) + op + brak(r); + }; + } + } + + record Val(int v) implements Expr { + // instance Show Expr + @Override + public String toString() { + return Expr.toStr(this); + } + } + + record App(Op op, Expr l, Expr r) implements Expr { + // instance Show Expr + @Override + public String toString() { + return Expr.toStr(this); + } + } + + // eval :: Expr -> [Int] + // Using OptionalInt instead of List + static OptionalInt eval(Expr expr) { + return switch (expr) { + // eval (Val n) = [n | n > 0] + case Val(var n) -> n > 0 ? OptionalInt.of(n) : OptionalInt.empty(); + + + // eval (App o l r) = [apply o x y | x <- eval l, + // y <- eval r, + // valid o x y] + case App(var op, var l, var r) -> { + var x = eval(l); + var y = eval(r); + yield (x.isPresent() && y.isPresent() && + isValid(op, x.getAsInt(), y.getAsInt())) ? + OptionalInt.of(apply(op, x.getAsInt(), y.getAsInt())) : + OptionalInt.empty(); + } + }; + } + + // type Result = (Expr,Int) + record Result(Expr expr, int value) { + @Override + public String toString() { + return expr.toString() + " = " + value; + } + } + + // combine'' :: Result -> Result -> [Result] + static List combine(Result lx, Result ry) { + // (l,x), (r,y) pattern + var l = lx.expr(); + var x = lx.value(); + var r = ry.expr(); + var y = ry.value(); + + // combine'' (l,x) (r,y) = [(App o l r, apply o x y) | o <- ops, valid' o x y] + return Stream.of(operators). + filter(op -> isValid(op, x, y)). + map(op -> new Result(new App(op, l, r), apply(op, x, y))). + toList(); + } + + // results' :: [Int] -> [Result] + static List results(List ns) { + // results' [] = [] + if (ns.isEmpty()) { + return List.of(); + } + + // results' [n] = [(Val n,n) | n > 0] + if (ns.size() == 1) { + var n = head(ns); + return n > 0 ? List.of(new Result(new Val(n), n)) : List.of(); + } + + // results' ns = [res | (ls,rs) <- split ns, + // lx <- results' ls, + // ry <- results' rs, + // res <- combine'' lx ry] + var res = new ArrayList(); + + // all possible non-empty splits of the input list + // split :: [a] -> [([a],[a])] equivalent for-loop + for (int i = 1; i < ns.size(); i++) { + var ls = ns.subList(0, i); + var rs = ns.subList(i, ns.size()); + var lxs = results(ls); + var rys = results(rs); + for (Result lx : lxs) { + for (Result ry : rys) { + res.addAll(combine(lx, ry)); + } + } + } + return res; + } + + // List utilities + // : operator + static List cons(T head, List tail) { + final var tailLen = tail.size(); + return switch (tailLen) { + case 0 -> List.of(head); + case 1 -> List.of(head, tail.get(0)); + case 2 -> List.of(head, tail.get(0), tail.get(1)); + case 3 -> List.of(head, tail.get(0), tail.get(1), tail.get(2)); + default -> { + var res = new ArrayList(1 + tailLen); + res.add(head); + res.addAll(tail); + yield res; + } + }; + } + + static T head(List list) { + return list.get(0); + } + + static List tail(List list) { + final var len = list.size(); + return len == 1 ? List.of() : list.subList(1, len); + } + + // subs :: [a] -> [[a]] + static List> subs(List ns) { + // subs [] = [[]] + if (ns.isEmpty()) { + return List.of(List.of()); + } + + // subs (x:xs) + var x = head(ns); + var xs = tail(ns); + + // where yss = sub(xs) + var yss = subs(xs); + + // yss ++ map (x:) yss + var res = new ArrayList>(); + res.addAll(yss); + yss.stream(). + map(l -> cons(x, l)). + forEach(res::add); + + return res; + } + + // interleave :: a -> [a] -> [[a]] + // Using Stream instead of List> + static Stream> interleave(int x, List ns) { + // interleave x [] = [[x]] + if (ns.isEmpty()) { + return Stream.of(List.of(x)); + } + + // interleave x (y:ys) + var y = head(ns); + var ys = tail(ns); + + // outer : translated as Stream.concat + // (x:y:ys) : map (y:) (interleave x ys) + return Stream.concat( + // x:y:ys == x:ns + Stream.of(cons(x, ns)), + // map (y:) (interleave x ys) + interleave(x, ys).map(l -> cons(y, l)) + ); + } + + // perms :: [a] -> [[a]] + // Using Stream instead of List> + static Stream> perms(List ns) { + // perms [] = [[]] + if (ns.isEmpty()) { + return Stream.of(List.of()); + } + + // perms (x:xs) + var x = head(ns); + var xs = tail(ns); + + // concat (map ...) is translated as flatMap + // concat (map (interleave x) (perms xs)) + return perms(xs).flatMap(l -> interleave(x, l)); + } + + // choices :: [a] -> [[a]] + // Using Stream instead of List> + static Stream> choices(List ns) { + // concat . map is translated as flatMap + // choices = concat . map perms . subs + return subs(ns).stream().flatMap(CountDownProblem::perms); + } + + // solutions'' :: [Int] -> Int -> [Expr] + // Using Stream instead of List + static Stream solutions(List ns, int n) { + // solutions'' ns n = [e | ns' <- choices ns, (e,m) <- results' ns', m == n] + return choices(ns). + flatMap(choice -> results(choice).stream()). + filter(res -> res.value() == n). + map(Result::expr); + } + + /* + * usage example: + * + * java CountDownProblem.java 1,3,7,10,25,50 765 + */ + public static void main(String[] args) { + if (args.length != 2) { + System.err.println("usage: java CountDownProblem.java "); + System.exit(1); + } + + int target = Integer.parseInt(args[1]); + List numbers = Stream.of(args[0].split(",")).map(Integer::parseInt).toList(); + // uniqueness check + try { + Set.of(numbers.toArray()); + } catch (IllegalArgumentException iae) { + System.err.println(iae); + System.exit(2); + } + + var start = System.currentTimeMillis(); + solutions(numbers, target).forEach(e -> { + System.out.println(e); + }); + System.out.println("Time taken (ms): " + (System.currentTimeMillis() - start)); + } +} diff --git a/countdown-problem-java21/SETUP.md b/countdown-problem-java21/SETUP.md new file mode 100644 index 0000000..73a6c48 --- /dev/null +++ b/countdown-problem-java21/SETUP.md @@ -0,0 +1,20 @@ +# Countdown Problem + +[CountDownProblem.java](CountDownProblem.java) is the complete example used in the article https://inside.java/2023/11/02/countdown-haskell-java. +The code represents the Java port of [a Haskell example](https://www.cs.nott.ac.uk/~pszgmh/pgp-countdown.hs) authored by Prof. Graham Hutton. +The problem and the solution approaches of the Haskell example are explained in https://youtu.be/CiXDS3bBBUo?list=PLF1Z-APd9zK7usPMx3LGMZEHrECUGodd3. + +## Prerequisites + +You need JDK 21+ installed on your machine to run this sample. +Find out how to achieve that by going through the [setting up a JDK section](https://dev.java/learn/getting-started/#setting-up-jdk) of https://dev.java/. + +## Run the progam + +Make sure you are inside the _countdown-haskell-java_ directory and then run the following command in a terminal: + +```shell + java CountDownProblem.java 1,3,7,10,25,50 765 +``` + +You may try different arguments to check for different results. From 611807393b686f100a2a2939ce6baaa466fb8c4b Mon Sep 17 00:00:00 2001 From: Ana Date: Thu, 2 Nov 2023 13:54:53 +0100 Subject: [PATCH 4/5] Change article date --- README.md | 2 +- countdown-problem-java21/SETUP.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6272c8b..f0621d0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Each sample directory contains its own instructions (SETUP.md) on how to run the ## Content -* [CountDownProblem.java](countdown-problem-java21/CountDownProblem.java) published on https://inside.java/2023/11/02/countdown-haskell-java +* [CountDownProblem.java](countdown-problem-java21/CountDownProblem.java) published on https://inside.java/2023/11/03/countdown-haskell-java ## Contributing diff --git a/countdown-problem-java21/SETUP.md b/countdown-problem-java21/SETUP.md index 73a6c48..4e7c1ed 100644 --- a/countdown-problem-java21/SETUP.md +++ b/countdown-problem-java21/SETUP.md @@ -1,6 +1,6 @@ # Countdown Problem -[CountDownProblem.java](CountDownProblem.java) is the complete example used in the article https://inside.java/2023/11/02/countdown-haskell-java. +[CountDownProblem.java](CountDownProblem.java) is the complete example used in the article https://inside.java/2023/11/03/countdown-haskell-java. The code represents the Java port of [a Haskell example](https://www.cs.nott.ac.uk/~pszgmh/pgp-countdown.hs) authored by Prof. Graham Hutton. The problem and the solution approaches of the Haskell example are explained in https://youtu.be/CiXDS3bBBUo?list=PLF1Z-APd9zK7usPMx3LGMZEHrECUGodd3. From caca5982335aea2ecf2a6c7f4e2f40fa62ebd81a Mon Sep 17 00:00:00 2001 From: Ana Date: Thu, 2 Nov 2023 14:07:41 +0100 Subject: [PATCH 5/5] Replace Contributing with Feedback --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f0621d0..fb34923 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ Each sample directory contains its own instructions (SETUP.md) on how to run the * [CountDownProblem.java](countdown-problem-java21/CountDownProblem.java) published on https://inside.java/2023/11/03/countdown-haskell-java -## Contributing +## Feedback -If you spot an error, or you know of an alternative way/an extra question/solution that you can offer, please reach out to us by email [java_devrel_grp](mailto:java_devrel_grp@oracle.com). +If you spot an error, or you know of an alternative way/an extra question/solution that you can offer, please raise an issue on https://github.com/java/devrel. ## Security