This commit is contained in:
2025-04-01 23:08:30 +03:00
commit 28b59bb2e7
9 changed files with 302 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*__cache__*
*__pycache__*
.venv
.pytest_cache

43
README.md Normal file
View File

@@ -0,0 +1,43 @@
# Nordhealth test
## Problem
Given an unsorted array A[]. The task is to print all unique pairs in the unsorted array with equal sum.
Note: Print the result in the format as shown in the below examples.
Examples:
```
Input: A[] = { 6, 4, 12, 10, 22, 54, 32, 42, 21, 11}
Output:
Pairs : ( 4, 12) ( 6, 10) have sum : 16
Pairs : ( 10, 22) ( 21, 11) have sum : 32
Pairs : ( 12, 21) ( 22, 11) have sum : 33
Pairs : ( 22, 21) ( 32, 11) have sum : 43
Pairs : ( 32, 21) ( 42, 11) have sum : 53
Pairs : ( 12, 42) ( 22, 32) have sum : 54
Pairs : ( 10, 54) ( 22, 42) have sum : 64
```
```
Input:A[]= { 4, 23, 65, 67, 24, 12, 86}
Output:
Pairs : ( 4, 86) ( 23, 67) have sum : 90
```
## Solution
Solution in `nordhealth_test/solution.py:51` (print_pairs_with_same_sum())
Tests for the inputs are defined it `tests/test_solution.py`
For package manager I am using poetry, so to activate venv:
- `poetry install`
- `poetry env activate`
## Running
to run the solution:
- `chmod +x ./nordhealth_test/solution.py`
- `./nordhealth_test/solution.py [path to input file]`
- Example: `./nordhealth_test/solution.py ./inputs/input1.txt`
To run the tests:
- `pytest tests/`

1
inputs/input1.txt Normal file
View File

@@ -0,0 +1 @@
6,4,12,10,22,54,32,42,21,11

1
inputs/input2.txt Normal file
View File

@@ -0,0 +1 @@
4,23,65,67,24,12,86

View File

98
nordhealth_test/solution.py Executable file
View File

@@ -0,0 +1,98 @@
#!/usr/bin/env python3
import sys
from typing import List
from collections import defaultdict
"""
Given an unsorted array A[]. The task is to print all unique pairs in the unsorted array with equal sum.
Note: Print the result in the format as shown in the below examples.
Examples:
Input: A[] = { 6, 4, 12, 10, 22, 54, 32, 42, 21, 11}
Output:
Pairs : ( 4, 12) ( 6, 10) have sum : 16
Pairs : ( 10, 22) ( 21, 11) have sum : 32
Pairs : ( 12, 21) ( 22, 11) have sum : 33
Pairs : ( 22, 21) ( 32, 11) have sum : 43
Pairs : ( 32, 21) ( 42, 11) have sum : 53
Pairs : ( 12, 42) ( 22, 32) have sum : 54
Pairs : ( 10, 54) ( 22, 42) have sum : 64
Input:A[]= { 4, 23, 65, 67, 24, 12, 86}
Output:
Pairs : ( 4, 86) ( 23, 67) have sum : 90
"""
def main() -> None:
argc = len(sys.argv)
argv = sys.argv
if argc != 2:
raise ValueError("Invalid input. You need to provide a single path to the input file. \n Ex.: `./solution.py ./input1.txt`")
# Read input from the file and process the data
try:
input_data = read_input(argv[1])
print_pairs_with_same_sum(input_data)
except ValueError as e:
print(f"Error: {e}")
sys.exit(1)
def read_input(input_file: str) -> List[int]:
""" Reads the input file and returns a list of integers. """
try:
with open(input_file, "r") as file:
lst = file.readline().split(',')
parsed = [int(x.strip()) for x in lst]
return parsed
except Exception as e:
raise ValueError(f"Failed to read or parse the file: {e}")
def print_pairs_with_same_sum(arr: List[int]) -> None:
"""
Solution to the problem. Since performance is required it's in one method, but could be separated:
1). Calculate the sums into a hashmap and add the pairs
2). print the result
"""
# Not a valid array
if len(arr) < 2:
print("No pairs can be formed from the input file.")
sum_map = defaultdict(list)
# Check for duplicate pairs.
seen_pairs = set()
n = len(arr)
for i in range(n):
# Time Complexity (O(n^2)), as we have n(n-1)/2 for the second loop.
for j in range(i + 1, n):
current_sum = arr[i] + arr[j]
pair = tuple((arr[i], arr[j]))
if pair not in seen_pairs:
# This will output nothing for [2,2,2,2]. Depending on the requirements we can store the pair using indices,
# then the result would be:
# (A[0],A[1]) (A[0],A[2]) (A[0],A[3]) (A[1],A[2]) (A[1],A[3]) (A[2],A[3]) - sum: 4
# And we will not need the seen_pairs set.
sum_map[current_sum].append(pair)
seen_pairs.add(pair)
# This is required to follow the example output, as it is presented as sorted by sum in the task description.
# We can skip the sorting if this is not really part of the requiremetns.
for sum_val in sorted(sum_map.keys()):
pairs = sum_map[sum_val]
if len(pairs) > 1:
pairs.sort()
print(f"Pairs : ", end="")
for pair in pairs:
print(f"( {pair[0]}, {pair[1]})", end=" ")
print(f"have sum : {sum_val}")
if __name__ == "__main__":
main()

80
poetry.lock generated Normal file
View File

@@ -0,0 +1,80 @@
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["dev"]
markers = "sys_platform == \"win32\""
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "iniconfig"
version = "2.1.0"
description = "brain-dead simple config-ini parsing"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"},
{file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
]
[[package]]
name = "packaging"
version = "24.2"
description = "Core utilities for Python packages"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
]
[[package]]
name = "pluggy"
version = "1.5.0"
description = "plugin and hook calling mechanisms for python"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"},
{file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"},
]
[package.extras]
dev = ["pre-commit", "tox"]
testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "pytest"
version = "8.3.5"
description = "pytest: simple powerful testing with Python"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"},
{file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"},
]
[package.dependencies]
colorama = {version = "*", markers = "sys_platform == \"win32\""}
iniconfig = "*"
packaging = "*"
pluggy = ">=1.5,<2"
[package.extras]
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.13"
content-hash = "8ef1d528c82ebf4a734833eed980b6b031e288bf5563655b512f75969b90d60a"

20
pyproject.toml Normal file
View File

@@ -0,0 +1,20 @@
[project]
name = "nordhealth-test"
version = "0.1.0"
description = ""
authors = [
{name = "viktor",email = "me@vpanteleev.com"}
]
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
]
[tool.poetry]
[tool.poetry.group.dev.dependencies]
pytest = "^8.3.5"
[build-system]
requires = ["poetry-core>=2.0.0,<3.0.0"]
build-backend = "poetry.core.masonry.api"

55
tests/test_solution.py Normal file
View File

@@ -0,0 +1,55 @@
import sys
from io import StringIO
from nordhealth_test.solution import print_pairs_with_same_sum
def capture_output(func, *args, **kwargs):
captured_output = StringIO()
sys.stdout = captured_output
func(*args, **kwargs)
sys.stdout = sys.__stdout__
return captured_output.getvalue()
# Test case Input 1: [6, 4, 12, 10, 22, 54, 32, 42, 21, 11]
def test_input_1():
arr = [6, 4, 12, 10, 22, 54, 32, 42, 21, 11]
expected_output = (
"Pairs : ( 4, 12) ( 6, 10) have sum : 16\n"
"Pairs : ( 10, 22) ( 21, 11) have sum : 32\n"
"Pairs : ( 12, 21) ( 22, 11) have sum : 33\n"
"Pairs : ( 22, 21) ( 32, 11) have sum : 43\n"
"Pairs : ( 32, 21) ( 42, 11) have sum : 53\n"
"Pairs : ( 12, 42) ( 22, 32) have sum : 54\n"
"Pairs : ( 10, 54) ( 22, 42) have sum : 64\n"
)
result = capture_output(print_pairs_with_same_sum, arr)
assert result == expected_output
# Test case Input 2: [4, 23, 65, 67, 24, 12, 86]
def test_input_2():
arr = [4, 23, 65, 67, 24, 12, 86]
expected_output = (
"Pairs : ( 4, 86) ( 23, 67) have sum : 90\n"
)
result = capture_output(print_pairs_with_same_sum, arr)
assert result == expected_output
# Test case: Array with no valid pairs
def test_no_valid_pairs():
arr = [1, 2, 3, 5]
expected_output = ""
result = capture_output(print_pairs_with_same_sum, arr)
assert result == expected_output
# Test case: Array with exactly one element
def test_single_element():
arr = [10]
expected_output = "No pairs can be formed from the input file.\n"
result = capture_output(print_pairs_with_same_sum, arr)
assert result == expected_output
# Test case: Empty array
def test_empty_array():
arr = []
expected_output = "No pairs can be formed from the input file.\n"
result = capture_output(print_pairs_with_same_sum, arr)
assert result == expected_output