From afbfecdc55f4b1a205227756c4931579c559dce1 Mon Sep 17 00:00:00 2001 From: Andreas Zweili Date: Mon, 1 Nov 2021 20:18:10 +0100 Subject: [PATCH] add new exercise --- .../currency-exchange/.exercism/config.json | 10 + .../currency-exchange/.exercism/metadata.json | 1 + python/currency-exchange/HELP.md | 55 +++++ python/currency-exchange/HINTS.md | 31 +++ python/currency-exchange/README.md | 188 ++++++++++++++++++ python/currency-exchange/exchange.py | 68 +++++++ python/currency-exchange/exchange_test.py | 77 +++++++ 7 files changed, 430 insertions(+) create mode 100644 python/currency-exchange/.exercism/config.json create mode 100644 python/currency-exchange/.exercism/metadata.json create mode 100644 python/currency-exchange/HELP.md create mode 100644 python/currency-exchange/HINTS.md create mode 100644 python/currency-exchange/README.md create mode 100644 python/currency-exchange/exchange.py create mode 100644 python/currency-exchange/exchange_test.py diff --git a/python/currency-exchange/.exercism/config.json b/python/currency-exchange/.exercism/config.json new file mode 100644 index 0000000..11c09f8 --- /dev/null +++ b/python/currency-exchange/.exercism/config.json @@ -0,0 +1,10 @@ +{ + "blurb": "Learn about numbers by solving Chandler's currency exchange conundrums.", + "icon": "hyperia-forex", + "authors": ["Ticktakto", "Yabby1997", "limm-jk", "OMEGA-Y", "wnstj2007", "J08K"], + "files": { + "solution": ["exchange.py"], + "test": ["exchange_test.py"], + "exemplar": [".meta/exemplar.py"] + } +} diff --git a/python/currency-exchange/.exercism/metadata.json b/python/currency-exchange/.exercism/metadata.json new file mode 100644 index 0000000..c4904cc --- /dev/null +++ b/python/currency-exchange/.exercism/metadata.json @@ -0,0 +1 @@ +{"track":"python","exercise":"currency-exchange","id":"f1e3f1d8394941a7b649aba8641e3c4a","url":"https://exercism.org/tracks/python/exercises/currency-exchange","handle":"Nebucatnetzer","is_requester":true,"auto_approve":false} \ No newline at end of file diff --git a/python/currency-exchange/HELP.md b/python/currency-exchange/HELP.md new file mode 100644 index 0000000..baa2cc3 --- /dev/null +++ b/python/currency-exchange/HELP.md @@ -0,0 +1,55 @@ +# Help + +## Running the tests + +To run the included *tests*, run the test file using the `pytest` module, replacing `{exercise_name}`: + +```bash +$ python3 -m pytest {exercise_name}_test.py +``` + +Many IDE's and code editors have built-in support for using Pytest to run tests; check them out [here](https://github.com/exercism/python/blob/main/docs/TOOLS.md#editors-and-ides). + +For more information about running tests using `pytest`, checkout our [Python testing guide](https://github.com/exercism/python/blob/main/docs/TESTS.md#pytest). + +### Common pytest options + +- `-v` : enable verbose output. +- `-x` : stop running tests on first failure. +- `--ff` : run failures from previous test before running other test cases. + +For other options, see `python3 -m pytest -h`. + +## Submitting your solution + +You can submit your solution using the `exercism submit exchange.py` command. +This command will upload your solution to the Exercism website and print the solution page's URL. + +It's possible to submit an incomplete solution which allows you to: + +- See how others have completed the exercise +- Request help from a mentor + +## Need to get help? + +If you'd like help solving the exercise, check the following pages: + +- The [Python track's documentation](https://exercism.org/docs/tracks/python) +- [Exercism's support channel on gitter](https://gitter.im/exercism/support) +- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) + +Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. + +Below are some resources for getting help if you run into trouble: + +- [The PSF](https://www.python.org) hosts Python downloads, documentation, and community resources. +- [Python Community on Discord](https://pythondiscord.com/) is a very helpful and active community. +- [#python on Libera.chat](https://www.python.org/community/irc/) this is where the cored developers for the language hang out and get work done. +- [Exercism on Gitter](https://gitter.im/exercism/home) join the Python room for Python-related questions or problems. +- [/r/learnpython/](https://www.reddit.com/r/learnpython/) is a subreddit designed for Python learners. +- [Python Community Forums](https://discuss.python.org/) +- [Pythontutor](http://pythontutor.com/) for stepping through small code snippets visually. + + +Additionally, [StackOverflow](http://stackoverflow.com/questions/tagged/python) is a good spot to search for your problem/question to see if it has been answered already. + If not - you can always [ask](https://stackoverflow.com/help/how-to-ask) or [answer](https://stackoverflow.com/help/how-to-answer) someone else's question. \ No newline at end of file diff --git a/python/currency-exchange/HINTS.md b/python/currency-exchange/HINTS.md new file mode 100644 index 0000000..9330ce7 --- /dev/null +++ b/python/currency-exchange/HINTS.md @@ -0,0 +1,31 @@ +# Hints + +## General + +- [The Python Numbers Tutorial](https://docs.python.org/3/tutorial/introduction.html#numbers) and [Python numeric types](https://docs.python.org/3.9/library/stdtypes.html#numeric-types-int-float-complex) can be a great introduction. + +## 1. Estimating exchangeable value + +- You can use the [division operator](https://docs.python.org/3/tutorial/introduction.html#numbers) to get the value of exchanged currency. + +## 2. Changes after exchanging + +- You can use the [subtraction operator](https://docs.python.org/3/tutorial/introduction.html#numbers) to get the amount of changes. + +## 3. Calculate value of bills + +- You can use the [multiplication operator](https://docs.python.org/3/tutorial/introduction.html#numbers) to get the value of bills. + +## 4. Calculate number of bills + +- You need to divide `budget` into `denomination`. +- You need to use type casting to _int_ to get the exact number of bills. +- To remove decimal places from a `float`, you can convert it to `int`. + + **Note:** The `//` operator also does floor division. But, if the operand has `float`, the result is still `float`. + +## 5. Calculate exchangeable value + +- You need to calculate `spread` percent of `exchange_rate` using multiplication operator and add it to `exchange_rate` to get the exchanged currency. +- The actual rate needs to be computed. Remember to add exchange rate and exchange fee. +- You can get exchanged money affected by commission by using divide operation and type casting _int_. \ No newline at end of file diff --git a/python/currency-exchange/README.md b/python/currency-exchange/README.md new file mode 100644 index 0000000..c0ec5a6 --- /dev/null +++ b/python/currency-exchange/README.md @@ -0,0 +1,188 @@ +# Currency Exchange + +Welcome to Currency Exchange on Exercism's Python Track. +If you need help running the tests or submitting your code, check out `HELP.md`. +If you get stuck on the exercise, check out `HINTS.md`, but try and solve it without using those first :) + +## Introduction + +## Numbers + +There are three different kinds of built-in numbers in Python : `ints`, `floats`, and `complex`. However, in this exercise you'll be dealing only with `ints` and `floats`. + +### ints + +`ints` are whole numbers. e.g. `1234`, `-10`, `20201278`. + +Integers in Python have [arbitrary precision][arbitrary-precision] -- the amount of digits is limited only by the available memory of the host system. + +### floats + +`floats` are numbers containing a decimal point. e.g. `0.0`,`3.14`,`-9.01`. + +Floating point numbers are usually implemented in Python using a `double` in C (_15 decimal places of precision_), but will vary in representation based on the host system and other implementation details. This can create some surprises when working with floats, but is "good enough" for most situations. + +You can see more details and discussions in the following resources: + +- [Python numeric type documentation][numeric-type-docs] +- [The Python Tutorial][floating point math] +- [Documentation for `int()` built in][`int()` built in] +- [Documentation for `float()` built in][`float()` built in] +- [0.30000000000000004.com][0.30000000000000004.com] + +## Arithmetic + +Python fully supports arithmetic between `ints` and `floats`. It will convert narrower numbers to match their less narrow counterparts when used with the binary arithmetic operators (`+`, `-`, `*`, `/`, `//`, and `%`). When division with `/`, `//` returns the quotient and `%` returns the remainder. + +Python considers `ints` narrower than `floats`. So, using a float in an expression ensures the result will be a float too. However, when doing division, the result will always be a float, even if only integers are used. + +```python +# The int is widened to a float here, and a float type is returned. +>>> 3 + 4.0 +7.0 +>>> 3 * 4.0 +12.0 +>>> 3 - 2.0 +1.0 +# Division always returns a float. +>>> 6 / 2 +3.0 +>>> 7 / 4 +1.75 +# Calculating remainders. +>>> 7 % 4 +3 +>>> 2 % 4 +2 +>>> 12.75 % 3 +0.75 +``` + +If an int result is needed, you can use `//` to truncate the result. + +```python +>>> 6 // 2 +3 +>>> 7 // 4 +1 +``` + +To convert a float to an integer, you can use `int()`. Also, to convert an integer to a float, you can use `float()`. + +```python +>>> int(6 / 2) +3 +>>> float(1 + 2) +3.0 +``` + +[arbitrary-precision]: https://en.wikipedia.org/wiki/Arbitrary-precision_arithmetic#:~:text=In%20computer%20science%2C%20arbitrary%2Dprecision,memory%20of%20the%20host%20system. +[numeric-type-docs]: https://docs.python.org/3/library/stdtypes.html#typesnumeric +[`int()` built in]: https://docs.python.org/3/library/functions.html#int +[`float()` built in]: https://docs.python.org/3/library/functions.html#float +[0.30000000000000004.com]: https://0.30000000000000004.com/ +[floating point math]: https://docs.python.org/3.9/tutorial/floatingpoint.html + +## Instructions + +Your friend Chandler plans to visit exotic countries all around the world. Sadly, Chandler's math skills aren't good. He's pretty worried about being scammed by currency exchanges during his trip - and he wants you to make a currency calculator for him. Here are his specifications for the app: + +## 1. Estimate value after exchange + +Create the `exchange_money()` function, taking 2 parameters: + +1. `budget` : The amount of money you are planning to exchange. +2. `exchange_rate` : Unit value of the foreign currency. + +This function should return the value of the exchanged currency. + +**Note:** If your currency is USD and you want to exchange USD for EUR with an exchange rate of `1.20`, then `1.20 USD == 1 EUR`. + +```python +>>> exchange_money(127.5, 1.2) +106.25 +``` + +## 2. Calculate currency left after an exchange + +Create the `get_change()` function, taking 2 parameters: + +1. `budget` : Amount of money before exchange. +2. `exchanging_value` : Amount of money that is *taken* from the budget to be exchanged. + +This function should return the amount of money that *is left* from the budget. + +```python +>>> get_change(127.5, 120) +7.5 +``` + +## 3. Calculate value of bills + +Create the `get_value_of_bills()` function, taking 2 parameters: + +1. `denomination` : The value of a single bill. +2. `number_of_bills` : Amount of bills you received. + +This function should return the total value of the given *bills*. + +```python +>>> get_value_of_bills(5, 128) +640 +``` + +## 4. Calculate number of bills + +Create the `get_number_of_bills()` function, taking `budget` and `denomination`. + +This function should return the *number of bills* that you can get using the *budget*. +**Note: ** You can only receive _whole bills_, not fractions of bills, so remember to divide accordingly. +```python +>>> get_number_of_bills(127.5, 5) +25 +``` + +## 5. Calculate value after exchange + +Create the `exchangeable_value()` function, taking `budget`, `exchange_rate`, `spread`, and `denomination`. + +Parameter `spread` is the *percentage taken* as an exchange fee. +If `1.00 EUR == 1.20 USD` and the *spread* is `10`, the actual exchange will be: `1.00 EUR == 1.32 USD`. + +This function should return the maximum value of the new currency after calculating the *exchange rate* plus the *spread*. +Remember that the currency *denomination* is a whole number, and cannot be sub-divided. + +**Note:** Returned value should be `int` type. + +```python +>>> exchangeable_value(127.25, 1.20, 10, 20) +80 +>>> exchangeable_value(127.25, 1.20, 10, 5) +95 +``` + +## 6. Calculate non-exchangeable value + +Create the `non_exchangeable_value()` function, taking `budget`, `exchange_rate`, `spread`, and `denomination`. + +This function should return the value that is *not* exchangeable due to the *denomination* of the bills. + +**Note:** Returned value should be `int` type. + +```python +>>> non_exchangeable_value(127.25, 1.20, 10, 20) +16 +>>> non_exchangeable_value(127.25, 1.20, 10, 5) +1 +``` + +## Source + +### Created by + +- @Ticktakto +- @Yabby1997 +- @limm-jk +- @OMEGA-Y +- @wnstj2007 +- @J08K \ No newline at end of file diff --git a/python/currency-exchange/exchange.py b/python/currency-exchange/exchange.py new file mode 100644 index 0000000..29019e8 --- /dev/null +++ b/python/currency-exchange/exchange.py @@ -0,0 +1,68 @@ +def exchange_money(budget, exchange_rate): + """ + + :param budget: float - amount of money you are planning to exchange. + :param exchange_rate: float - unit value of the foreign currency. + :return: float - exchanged value of the foreign currency you can receive. + """ + + pass + + +def get_change(budget, exchanging_value): + """ + + :param budget: float - amount of money you own. + :param exchanging_value: int - amount of your money you want to exchange now. + :return: float - amount left of your starting currency after exchanging. + """ + + pass + + +def get_value_of_bills(denomination, number_of_bills): + """ + + :param denomination: int - the value of a bill. + :param number_of_bills: int - amount of bills you received. + :return: int - total value of bills you now have. + """ + + pass + + +def get_number_of_bills(budget, denomination): + """ + + :param budget: float - the amount of money you are planning to exchange. + :param denomination: int - the value of a single bill. + :return: int - number of bills after exchanging all your money. + """ + + pass + + +def exchangeable_value(budget, exchange_rate, spread, denomination): + """ + + :param budget: float - the amount of your money you are planning to exchange. + :param exchange_rate: float - the unit value of the foreign currency. + :param spread: int - percentage that is taken as an exchange fee. + :param denomination: int - the value of a single bill. + :return: int - maximum value you can get. + """ + + pass + + +def non_exchangeable_value(budget, exchange_rate, spread, denomination): + """ + + :param budget: float - the amount of your money you are planning to exchange. + :param exchange_rate: float - the unit value of the foreign currency. + :param spread: int - percentage that is taken as an exchange fee. + :param denomination: int - the value of a single bill. + :return: int non-exchangeable value. + """ + + pass diff --git a/python/currency-exchange/exchange_test.py b/python/currency-exchange/exchange_test.py new file mode 100644 index 0000000..0eb24a2 --- /dev/null +++ b/python/currency-exchange/exchange_test.py @@ -0,0 +1,77 @@ +import unittest +import pytest +from exchange import ( + exchange_money, + get_change, + get_value_of_bills, + get_number_of_bills, + exchangeable_value, + non_exchangeable_value) + + +class CurrencyExchangeTest(unittest.TestCase): + + @pytest.mark.task(taskno=1) + def test_exchange_money(self): + input_data = [(100000, 0.84), (700000, 10.1)] + output_data = [119047, 69306] + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data): + self.assertEqual(int(exchange_money(input_data[0], input_data[1])), output_data) + + @pytest.mark.task(taskno=2) + def test_get_change(self): + input_data = [(463000, 5000), (1250, 120), (15000, 1380)] + output_data = [458000, 1130, 13620] + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data): + self.assertEqual(get_change(input_data[0], input_data[1]), output_data) + + @pytest.mark.task(taskno=3) + def test_get_value_of_bills(self): + input_data = [(10000, 128), (50, 360), (200, 200)] + output_data = [1280000, 18000, 40000] + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data): + self.assertEqual(get_value_of_bills(input_data[0], input_data[1]), output_data) + + @pytest.mark.task(taskno=4) + def test_get_number_of_bills(self): + input_data = [(163270, 50000), (54361, 1000)] + output_data = [3, 54] + + for variant, (input_data, output_data) in enumerate(zip(input_data, output_data), start=1): + with self.subTest(f"variation #{variant}", input_data=input_data, output_data=output_data): + self.assertEqual(get_number_of_bills(input_data[0], input_data[1]), output_data) + + @pytest.mark.task(taskno=5) + def test_exchangeable_value(self): + inputs = [ + (100000, 10.61, 10, 1), + (1500, 0.84, 25, 40), + (470000, 1050, 30, 10000000000), + (470000, 0.00000009, 30, 700), + (425.33, 0.0009, 30, 700)] + + output_data = [8568, 1400, 0, 4017094016600, 363300] + + for variant, (inputs, output_data) in enumerate(zip(inputs, output_data), start=1): + with self.subTest(f"variation #{variant}", inputs=inputs, output_data=output_data): + self.assertEqual(exchangeable_value(inputs[0], inputs[1], inputs[2], inputs[3]), output_data) + + @pytest.mark.task(taskno=6) + def test_non_exchangeable_value(self): + inputs = [ + (100000, 10.61, 10, 1), + (1500, 0.84, 25, 40), + (425.33, 0.0009, 30, 700), + (12000, 0.0096, 10, 50)] + + output_data = [0, 28, 229, 13] + + for variant, (inputs, output_data) in enumerate(zip(inputs, output_data), start=1): + with self.subTest(f"variation #{variant}", inputs=inputs, output_data=output_data): + self.assertEqual(non_exchangeable_value(inputs[0], inputs[1], inputs[2], inputs[3]), output_data)