Skip to content

Commit

Permalink
Merge pull request #4 from moebius-rex/csieve_refactor
Browse files Browse the repository at this point in the history
Csieve refactor
  • Loading branch information
moebius-rex authored Apr 24, 2021
2 parents b0769e2 + a503fbd commit 9fb2c11
Show file tree
Hide file tree
Showing 39 changed files with 804 additions and 690 deletions.
51 changes: 51 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Copyright 2021 Shay Gordon
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# START include .gitignore ---------------------------------------------------

# macOS detritus
.DS_Store

# Visual Studio Code-generated files
.settings
.vscode
.classpath
.project

# Makefile-generated files
bin/
lib/
obj/

# Maven-generated files
target
classes/
test-classes/

# Python-generated files
__pycache__/

# END include .gitignore -----------------------------------------------------

# Git files
.git/
.gitignore

# Docker files
.dockerignore
docker-compose.yaml
docker/

# Project docs
**/*.md
21 changes: 13 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,24 @@
# License for the specific language governing permissions and limitations
# under the License.

# build and/or run all language sub-projects
SUBDIRS = src/main

all: maven $(SUBDIRS)

.PHONY: maven $(SUBDIRS) clean test
@# compile anything that needs to be compiled and then run

maven:
@-mvn -q compile
@# compile java classes
@mvn -q compile

$(SUBDIRS):
$(MAKE) --directory $@
@# run make with no arguments for each subdirectory
$(MAKE) --no-print-directory --directory $@

clean:
@mvn -q $@
@for subdir in $(SUBDIRS); do $(MAKE) --no-print-directory --directory $$subdir $@; done

test install:
@for subdir in $(SUBDIRS); do $(MAKE) --no-print-directory --directory $$subdir $@; done

clean test:
@-mvn -q $@
@for subdir in $(SUBDIRS); do $(MAKE) --directory $$subdir $@; done
.PHONY: maven $(SUBDIRS) clean test install
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Primes — The Sieve of Eratosthenes Project
# The Primes project

**Primes** is a project that implements the **Sieve of Eratosthenes** algorithm in several popular programming languages for educational purposes.
**Primes** is a project that implements the **Sieve of Eratosthenes** algorithm to find all prime numbers up to a given range. The project implements the algorithm in several commonly used programming languages.

## Purpose

The purpose of this project is to demonstrate differences and similarities between several popular programming languages by implementing the same algorithm in each language, and to demonstrate the comparative ease or difficulty in implementing the algorithm in each language. The project is *not* intended to provide a bullet-proof and thoroughly documented implementation of the algorithm in the selected languages. The project does *not* provide written documentation explaining the differences between the various language implementations — though it may do in the future — or extolling the virtues of one language over another; the hope instead is that downloaders will find their own uses for the project and its contents, that the set of languages will grow and that more language features will be explored as — or *if* the project's momentum grows.
The purpose of this project is to demonstrate differences and similarities between several popular programming languages by implementing the same algorithm in each language, and to demonstrate the comparative ease or difficulty in implementing the algorithm in each language. The project is *not* intended to provide a bullet-proof and thoroughly documented implementation of the algorithm in the selected languages. The project does *not* provide written documentation explaining the differences between the various language implementations — though it may do in the future — nor does it extol the virtues of one language over another; the hope instead is that downloaders will find their own uses for the project and its contents, that the set of languages will grow, and that more language features will be explored as the project's momentum grows.

## Features

Each language-specific implementation of the Sieve of Eratosthenes algorithm comes in two versions:
- A naïve version that brute forces a solution. In other words, a hack — difficult to read and consequently, difficult to maintain.
- One or more object-oriented versions that should be more maintainable and use the best practices of the language. In all languages except C, this means extracting a `Sieve` class as a separate component. In the C implementation, it means extracting a transparent structure containing algorithm data, and functions that use that data to implement the algorithm — basically, a rudimentary C++ with an *explicit* `this` pointer and data encapsulation, but no data hiding.
Each language-specific implementation of the Sieve of Eratosthenes algorithm comes in a minimum of two versions:
- A naïve version that brute forces the solution. In other words, a hack — difficult to read and to maintain.
- One or more object-oriented versions that should be more maintainable and use the best practices of the language. In all languages except C, this means extracting a `Sieve` class as a separate component. In the C implementation, it means extracting a opaque structure containing algorithm state, and non-member functions to implement the algorithm and access its results — basically, a rudimentary C++ class with an *explicit* `this` pointer and some data encapsulation.

Project features include:

Expand All @@ -20,8 +20,13 @@ Project features include:
- Java
- JavaScript
- Python
- One C++ and one Python implentation are derived by wrapping a C implementation. Similar Java and server-side JavaScript (node.js) implementations will follow.
- The project uses **Maven** under the hood to build Java implementations and GNU **Makefile** to build C and C++ implementations and to run all implementations.
- One **C++** and one **Python** implentation are derived by wrapping a **C** implementation. Similar **Java** and server-side **JavaScript** (**node.js**) implementations will follow.
- The project uses GNU **make** to:
- compile and link C and C++ implementations,
- install reusable libraries, shared objects and header files,
- run all, or a subset of, implementations,
- remove generated files.
- The project uses **Maven** behind the scenes to compile and package Java implementations.
- All implementations are designed to be run from, and print to, a terminal window.
- Where possible, all implementations produce the identical output for the identical input.
- All C and C++ implementations produce zero `valgrind` errors or leaks when compiled with gcc/g++ on Debian-based Linux systems.
Expand All @@ -42,13 +47,13 @@ The project's Sieve of Eratosthenes implementations have been tested on Ubuntu 2

## Installation

Follow [these instructions](./installer/README.md) to install the project.
Follow [these instructions](./setup/README.md) to install the project.

## Running

Once you've completed the installation, you can run the implementations. To run them all back-to-back (make sure you are in the project home directory):

```
```bash
% make
Sieve of Eratosthenes: Find all prime numbers in a given range
Prime numbers in range 0-1000 inclusive:
Expand All @@ -59,7 +64,7 @@ Found 168 prime(s) in 1000 integers in 4 microseconds

Alternatively, you can run only the implemnatations for a given language:

```
```bash
% cd src/main
% ls
Makefile c/ cpp/ java/ js/ python/
Expand All @@ -70,7 +75,7 @@ Sieve of Eratosthenes: Find all prime numbers in a given range

And finally, for a given example of a given language:

```
```bash
% cd c
% ls
Makefile app/ app2/ app3/ sieve/
Expand Down Expand Up @@ -141,7 +146,7 @@ This section contains some notes about this installation.

### Maven errors

On Ubuntu, the `mvn clean` command invoked by running `make clean` on the project top-level `Makefile` has been seen to cause the following messages to be written to the console. The messages do not appear to affect behavior in any way and can probably be ignored:
On Ubuntu systems, the `mvn clean` command invoked by running `make clean` on the project top-level `Makefile` causes the following messages to be written to the console. The messages do not appear to affect behavior in any way and can probably be ignored:

``` 
WARNING: An illegal reflective access operation has occurred
Expand Down
108 changes: 57 additions & 51 deletions docker/README.md → docker/DOCKER.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Using Docker to run Sieve of Eratosthenes implementations
# Using Docker to run the Prime project

The Primes project includes **Docker** configuration files that let you build Docker images and launch Docker containers that run the project's Sieve of Eratosthenes implementations to compute and print all prime numbers in a given range. This document is a guide to building one or more of these images and launching one or more of these containers. An understanding of Docker is *not* required — simply follow the step-by-step instructions in this document.
The Primes project includes **Docker** configuration files that let you build Docker images containing a reduced-size version of the Primes projectthat can then be run in Docker containers. This document is a guide to building these images and launching one or more of these containers. An understanding of Docker is *not* required — simply follow the step-by-step instructions in this document.

## Prerequisites

Expand All @@ -12,80 +12,87 @@ Throughout this guide, the `%` sign is used to indicate the command prompt; what

### Summary of steps

The steps below execute the following commands in order:
The detailed steps below execute the following commands:

```bash
% docker-compose build
% docker-compose up -d
% docker-compose exec alpine make
% docker-compose exec fedora make
% docker-compose exec ubuntu make
% docker-compose down
% docker compose build
% docker compose up -d
% docker compose exec alpine make
% docker compose exec fedora make
% docker compose exec ubuntu make
% docker compose down
% docker system prune -a
```

### Step 1: Build Docker images

Up to three Docker images may be built, each one based on a different Linux distribution:
The project's `docker` subdirectory provides Docker files to build up to three Docker images, each one based on a different Linux distribution:

- Alpine 3.13, a container-only Linux distribution.
- Fedora 3.3, the current version of Fedora.
- Ubuntu Focal, a version of Ubuntu 20.04 LTS optimized for containers.

To build all images, enter the following commands from the project's top-level directory:
Each image contains a copy of the project, omitting the `.git` repository, all docker files and project documents. The project files are installed in the `/prime` directory of the Docker image.

```
% docker-compose build
To build all images, enter the following command from the project's home directory:

```bash
% docker compose build
```

It takes in the order of a minute to build each image. Use `docker images` to view details about the resulting images:

```
```bash
% docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
sieve ubuntu 18e8f59a6c31 5 seconds ago 652MB
sieve fedora 0abdd04df7f4 About a minute ago 1.16GB
sieve alpine 5d00d4db8dd7 4 minutes ago 669MB
sieve ubuntu 18e8f59a6c31 5 seconds ago 644MB
sieve fedora 0abdd04df7f4 About a minute ago 1.21GB
sieve alpine 5d00d4db8dd7 4 minutes ago 668MB
```

If you wished to build a subset of these images, say, the Alpine and Ubuntu images but not the Fedora image, you would enter the following command instead:

```
% docker-compose build alpine fedora
```bash
% docker compose build alpine fedora
```

### Step 2: Start Docker containers

A Docker container is a **virtual machine** that loads and runs a Docker image. To start Docker containers for all Primes project images and run them in the background, i.e., detached from the terminal:

```
% docker-compose up -d
```bash
% docker compose up -d
[+] Running 4/4
⠿ Network "primes_default" Created 3.8s
⠿ Container alpine Started 3.8s
⠿ Container fedora Started 4.2s
⠿ Container ubuntu Started 3.6s
```

To start a selected container, say, the Fedora container, you would enter the following command instead:

```
% docker-compose up -d fedora
```bash
% docker compose up -d fedora
```

Use `docker ps` to view running containers:

```
```bash
% docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
314fba7533e6 sieve:ubuntu "tail -f /dev/null" 17 seconds ago Up 15 seconds ubuntu
0a0689fe1cf4 sieve:ubuntu "tail -f /dev/null" 17 seconds ago Up 15 seconds ubuntu
16e78a1ee98e sieve:alpine "tail -f /dev/null" 17 seconds ago Up 15 seconds alpine
6f5b92d53669 sieve:fedora "tail -f /dev/null" 17 seconds ago Up 15 seconds fedora
```

The `COMMAND` column shows the shell command that was called in the container once it was started. The Linux `tail -f /dev/null` command is a standard trick that keeps a container running until you decide to stop it with `docker-compose down` command; if we were to give it a command that returns immediately, the container would stop once the command completed.
The `COMMAND` column shows the shell command that was called in the container once it was started. The Linux `tail -f /dev/null` command is a standard trick that keeps a container running until you decide to stop it with `docker compose down` command; if we were to give it a command that returned immediately, the container would stop as soon as the command completed. The `NAMES` column provides the container names to use in container-ralted Docker commands.

### Step 3: Run sieve implementations in a Docker container

The `docker-conpose` exec command runs a command in a named container. For example, to run all sieve implementations in, say, the Alpine container, you would enter the following:
The `docker conpose exec` command runs a command in a named container. For example, to run all sieve implementations in, say, the Alpine container, you would enter the following:

```bash
% docker-compose exec alpine make
% docker compose exec alpine make
[snip]
Sieve of Eratosthenes: Find all prime numbers in a given range
Prime numbers in range 0-1000 inclusive:
Expand All @@ -94,54 +101,53 @@ Found 168 prime(s) in 1,000 integers in 98 microseconds
[snip]
```

If you would like to run only a subset of the implementations in a given container, or you would simply like to explore a container's environment, you can open up a `Bourne` or `Bash` shell as follows:
If you would like to run only a subset of the implementations in a given container, say `ubuntu`, or you would simply like to explore a container's environment, you can open up a `Bourne` or `Bash` shell as follows:

```bash
% docker-compose exec ubuntu sh
% docker compose exec ubuntu sh
OR
% docker-compose exec ubuntu bash
root@314fba7533e6:/prime# cd src/main/python
root@314fba7533e6:/prime/src/main/python# ls
% docker compose exec ubuntu bash
[root@0a0689fe1cf4:/prime/src/main/python]# cd src/main/python
[root@0a0689fe1cf4:/prime/src/main/python]# ls
Makefile app.py app2.py app3.py csieve.py sieve.py
root@314fba7533e6:/prime/src/main/python# make
[root@0a0689fe1cf4:/prime/src/main/python]# make
python/app
Sieve of Eratosthenes: Find all prime numbers in a given range
Prime numbers in range 0-1,000 inclusive:
Prime numbers in range 0-1000 inclusive:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997
Found 168 prime(s) in 1,000 integers in 102 microseconds
Found 168 prime(s) in 1000 integers in 100 microseconds

python/app2
Sieve of Eratosthenes: Find all prime numbers in a given range
Prime numbers in range 0-1,000 inclusive:
Prime numbers in range 0-1000 inclusive:
2 3 5 7 11 13 17 19 23 29 .. 937 941 947 953 967 971 977 983 991 997
Found 168 prime(s) in 1,000 integers in 174 microseconds
Found 168 prime(s) in 1000 integers in 188 microseconds

python/app3
Sieve of Eratosthenes: Find all prime numbers in a given range
Prime numbers in range 0-1,000 inclusive:
Prime numbers in range 0-1000 inclusive:
2 3 5 7 11 13 17 19 23 29 .. 937 941 947 953 967 971 977 983 991 997
Found 168 prime(s) in 1,000 integers in 19 microseconds
Found 168 prime(s) in 1000 integers in 17 microseconds

root@314fba7533e6:/prime/src/main/python#
[root@0a0689fe1cf4:/prime/src/main/python]#
```

### Step 4. Stop all containers

As already mentioned, the sieve implementation containers are configured to run until a command is issued to stop them as follows:
As already mentioned, the containers are configured to run until a command is issued to stop them, like this:

```bash
% docker-compose down
Stopping ubuntu ... done
Stopping alpine ... done
Stopping fedora ... done
Removing ubuntu ... done
Removing alpine ... done
Removing fedora ... done
% docker compose down
[+] Running 4/4
⠿ Container alpine Removed 10.3s
⠿ Container ubuntu Removed 10.3s
⠿ Container fedora Removed 10.2s
⠿ Network "primes_default" Removed 2.8s
```

### Step 5: Clean up

Docker images take up disk space. If you wish to reclaim that space, you can delete all sieve implementation Docker images like this:
Docker images take up disk space, about 2.5GB in the case of the project's three of-the-shelf images. If you wish to reclaim that space, you can delete all Docker images like this:

```bash
% docker system prune -a
Expand All @@ -160,7 +166,7 @@ deleted: sha256:17683f22293e9cf90e02bcc103ef011778e4217b100a121bf3f45da07ceabf09

Deleted build cache objects:
[snip]
Total reclaimed space: 2.232GB
Total reclaimed space: 2.272GB
```


3 changes: 2 additions & 1 deletion docker/alpine.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ RUN apk del openjdk8
# copy project source files to image & remove generated files
WORKDIR /prime
COPY . .
RUN ./installer/install.sh
RUN if [ ! -e /usr/local/include ]; then mkdir /usr/local/include; fi
RUN ./setup/setup.sh
4 changes: 2 additions & 2 deletions docker/fedora.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ RUN dnf install -y file less tree vim
# install project build & run toolchains
RUN dnf install -y gcc
RUN dnf install -y gcc-c++
RUN dnf install -y maven
RUN dnf install -y make
RUN dnf install -y maven
RUN dnf install -y nodejs
RUN dnf install -y python3

# copy project source files to image & remove generated files
WORKDIR /prime
COPY . .
RUN ./installer/install.sh
RUN ./setup/setup.sh
Loading

0 comments on commit 9fb2c11

Please sign in to comment.