diff --git a/config.json b/config.json index 9d5eb722..8b13d701 100644 --- a/config.json +++ b/config.json @@ -20,6 +20,19 @@ }, "exercises": { "concept": [ + { + "slug": "freelancer-rates", + "name": "Freelancer Rates", + "uuid": "0aff2fa7-55ea-47e9-af4a-78927d916baf", + "concepts": [ + "numbers", + "arithmetic-operators" + ], + "prerequisites": [ + "basics" + ], + "status": "beta" + }, { "slug": "lasagna", "name": "Lucian's Luscious Lasagna", diff --git a/exercises/concept/freelancer-rates/.docs/hints.md b/exercises/concept/freelancer-rates/.docs/hints.md new file mode 100644 index 00000000..b1dbf0d9 --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/hints.md @@ -0,0 +1,16 @@ +# Hints + +## 1. Calculate the day rate given an hourly rate + +- Use the arithmetic operators as mentioned in the introduction of this exercise. + +## 2. Calculate the number of workdays given a budget + +- First determine the day rate, then calculate the number of days, and finally round that number down. + +## 3. Calculate the discounted rate for large projects + +- Round down the result from division to get the number of full months. +- `100% - discount` equals the percentage charged after the discount is applied. +- Use `%`, the remainder operator, to calculate the number of days exceeding full months. +- Add the discounted month rates and full day rates and round it up \ No newline at end of file diff --git a/exercises/concept/freelancer-rates/.docs/instructions.md b/exercises/concept/freelancer-rates/.docs/instructions.md new file mode 100644 index 00000000..25d708de --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/instructions.md @@ -0,0 +1,45 @@ +# Instructions + +In this exercise you will be writing code to help a freelancer communicate with his clients about the prices of certain projects. You will write a few utility functions to quickly calculate the costs for the clients. + +## 1. Calculate the day rate given an hourly rate + +A client contacts the freelancer to enquire about his rates. +The freelancer explains that he **_works 8 hours a day._** +However, the freelancer knows only his hourly rates for the project. +Help him estimate a day rate given an hourly rate. + +```c +DATA(lcl_freelancer_rates) = new zcl_freelancer_rates( ). +DATA(lv_day_rate) = lcl_freelancer_rates->day_rate( 16 ). +WRITE |My Day Rate is: { lv_day_rate }|. +// => My Day Rate is: 128 +``` + +The day rate does not need to be rounded or changed to a "fixed" precision. + +## 2. Calculate the number of workdays given a fixed budget + +Another day, a project manager offers the freelancer to work on a project with a fixed budget. +Given the fixed budget and the freelancer's hourly rate, help him calculate the number of days he would work until the budget is exhausted. +The result _must_ be **rounded** to the nearest number with 2 decimal places. + +```c +DATA(lcl_freelancer_rates) = new zcl_freelancer_rates( ). +DATA(lv_days_in_budget) = lcl_freelancer_rates->DAYS_IN_BUDGET( budget = 1280 rate_per_hour = 16 ). +WRITE |Budget would last for { lv_days_in_budget } days.|. +// => Budget would last for 10 days. +``` + +## 3. Calculate the discounted rate for large projects + +Often, the freelancer's clients hire him for projects spanning over multiple months. +In these cases, the freelancer decides to offer a discount for every full month, and the remaining days are billed at day rate. +**_Every month has 22 billable days._** +Help him estimate his cost for such projects, given an hourly rate, the number of days the project spans, and a monthly discount rate. +The discount is always passed as a number, where `42%` becomes `0.42`. The result _must_ be **rounded up** to the nearest whole number. + +```javascript +priceWithMonthlyDiscount(89, 230, 0.42); +// => 97972 +``` \ No newline at end of file diff --git a/exercises/concept/freelancer-rates/.docs/introduction.md b/exercises/concept/freelancer-rates/.docs/introduction.md new file mode 100644 index 00000000..0ab457f7 --- /dev/null +++ b/exercises/concept/freelancer-rates/.docs/introduction.md @@ -0,0 +1,84 @@ +# Introduction + +## Numbers + +Many programming languages have specific numeric types to represent different types of numbers, but JavaScript only has two: + +- `number`: a numeric data type in the double-precision 64-bit floating-point format (IEEE 754). + Examples are `-6`, `-2.4`, `0`, `0.1`, `1`, `3.14`, `16.984025`, `25`, `976`, `1024.0` and `500000`. +- `bigint`: a numeric data type that can represent _integers_ in the arbitrary precision format. + Examples are `-12n`, `0n`, `4n`, and `9007199254740991n`. + +If you require arbitrary precision or work with extremely large numbers, use the `bigint` type. +Otherwise, the `number` type is likely the better option. + +### Rounding + +There is a built-in global object called `Math` that provides various [rounding functions][ref-math-object-rounding]. For example, you can round down (`floor`) or round up (`ceil`) decimal numbers to the nearest whole numbers. + +```javascript +Math.floor(234.34); // => 234 +Math.ceil(234.34); // => 235 +``` + +## Arithmetic Operators + +JavaScript provides 6 different operators to perform basic arithmetic operations on numbers. + +- `+`: The addition operator is used to find the sum of numbers. +- `-`: The subtraction operator is used to find the difference between two numbers +- `*`: The multiplication operator is used to find the product of two numbers. +- `/`: The division operator is used to divide two numbers. + +```javascript +2 - 1.5; //=> 0.5 +19 / 2; //=> 9.5 +``` + +- `%`: The remainder operator is used to find the remainder of a division performed. + + ```javascript + 40 % 4; // => 0 + -11 % 4; // => -3 + ``` + +- `**`: The exponentiation operator is used to raise a number to a power. + + ```javascript + 4 ** 3; // => 64 + 4 ** 1 / 2; // => 2 + ``` + +### Order of Operations + +When using multiple operators in a line, JavaScript follows an order of precedence as shown in [this precedence table][mdn-operator-precedence]. +To simplify it to our context, JavaScript uses the PEDMAS (Parentheses, Exponents, Division/Multiplication, Addition/Subtraction) rule we've learnt in elementary math classes. + + +```javascript +const result = 3 ** 3 + 9 * 4 / (3 - 1); +// => 3 ** 3 + 9 * 4/2 +// => 27 + 9 * 4/2 +// => 27 + 18 +// => 45 +``` + + +### Shorthand Assignment Operators + +Shorthand assignment operators are a shorter way of writing code conducting arithmetic operations on a variable, and assigning the new value to the same variable. +For example, consider two variables `x` and `y`. +Then, `x += y` is same as `x = x + y`. +Often, this is used with a number instead of a variable `y`. +The 5 other operations can also be conducted in a similar style. + +```javascript +let x = 5; +x += 25; // x is now 30 + +let y = 31; +y %= 3; // y is now 1 +``` + +[mdn-operator-precedence]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table +[ref-math-object-rounding]: https://javascript.info/number#rounding \ No newline at end of file diff --git a/exercises/concept/freelancer-rates/.meta/config.json b/exercises/concept/freelancer-rates/.meta/config.json new file mode 100644 index 00000000..6e25b607 --- /dev/null +++ b/exercises/concept/freelancer-rates/.meta/config.json @@ -0,0 +1,19 @@ +{ + "authors": [ + "marianfoo" + ], + "contributors": [ + ], + "files": { + "solution": [ + "zcl_freelancer_rates.clas.abap" + ], + "test": [ + "zcl_freelancer_rates.clas.testclasses.abap" + ], + "exemplar": [ + ".meta/zcl_freelancer_rates.clas.abap" + ] + }, + "blurb": "Learn about numbers whilst helping a freelancer communicate with a project manager about day- and month rates." + } \ No newline at end of file diff --git a/exercises/concept/freelancer-rates/.meta/design.md b/exercises/concept/freelancer-rates/.meta/design.md new file mode 100644 index 00000000..d15d70f8 --- /dev/null +++ b/exercises/concept/freelancer-rates/.meta/design.md @@ -0,0 +1,24 @@ +# Design + +## Learning objectives + +- Know how to write floating point literals. +- Know their underlying type (double precision). +- Know their basic limitations (not all numbers can be represented, e.g. `0.1 + 0.2`). +- Know how to truncate floating point numbers to a certain decimal place (`Math.ceil`, `Math.floor`, `Math.round`) +- Know how to use arithmetic operators (`+`, `-`, `*`, `/`, `%`, `**`) +- Learn about shorthand assignment operators (`+=`, etc.) + +## Out of scope + +- Parsing integers and floats from strings. +- Converting integers and floats to strings. + +## Concepts + +- `numbers` +- `arithmetic-operators` + +## Prerequisites + +- `basics` \ No newline at end of file diff --git a/exercises/concept/freelancer-rates/.meta/zcl_freelancer_rates.clas.abap b/exercises/concept/freelancer-rates/.meta/zcl_freelancer_rates.clas.abap new file mode 100644 index 00000000..f40f0562 --- /dev/null +++ b/exercises/concept/freelancer-rates/.meta/zcl_freelancer_rates.clas.abap @@ -0,0 +1,55 @@ +CLASS zcl_freelancer_rates DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + + METHODS day_rate + IMPORTING + !rate_per_hour TYPE f + RETURNING + VALUE(result) TYPE f . + METHODS days_in_budget + IMPORTING + !budget TYPE f + !rate_per_hour TYPE f + RETURNING + VALUE(result) TYPE i . + METHODS price_with_monthly_discount + IMPORTING + !rate_per_hour TYPE f + !num_days TYPE f + !discount TYPE f + RETURNING + VALUE(result) TYPE f . + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS zcl_freelancer_rates IMPLEMENTATION. + + + + METHOD day_rate. + result = round( val = rate_per_hour * 8 dec = 2 ) . + ENDMETHOD. + + METHOD days_in_budget. + result = floor( budget / day_rate( rate_per_hour ) ). + ENDMETHOD. + + + METHOD price_with_monthly_discount. + DATA(lv_months) = floor( num_days / 22 ). + DATA(lv_monthly_rate) = 22 * day_rate( rate_per_hour ). + DATA(lv_monthly_discount_rate) = ( 1 - discount ) * lv_monthly_rate. + + DATA(lv_extra_days) = num_days MOD 22. + DATA(lv_price_extra_days) = lv_extra_days * day_rate( rate_per_hour ). + + result = ceil( lv_months * lv_monthly_discount_rate + lv_price_extra_days ). + ENDMETHOD. +ENDCLASS. \ No newline at end of file diff --git a/exercises/concept/freelancer-rates/package.devc.xml b/exercises/concept/freelancer-rates/package.devc.xml new file mode 100644 index 00000000..541b613d --- /dev/null +++ b/exercises/concept/freelancer-rates/package.devc.xml @@ -0,0 +1,10 @@ + + + + + + Exercism: Freelancer Rates + + + + diff --git a/exercises/concept/freelancer-rates/zcl_freelancer_rates.clas.abap b/exercises/concept/freelancer-rates/zcl_freelancer_rates.clas.abap new file mode 100644 index 00000000..d9e38e06 --- /dev/null +++ b/exercises/concept/freelancer-rates/zcl_freelancer_rates.clas.abap @@ -0,0 +1,51 @@ +CLASS zcl_freelancer_rates DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + + METHODS day_rate + IMPORTING + !rate_per_hour TYPE f + RETURNING + VALUE(result) TYPE f. + + METHODS days_in_budget + IMPORTING + !budget TYPE f + !rate_per_hour TYPE f + RETURNING + VALUE(result) TYPE i. + + METHODS price_with_monthly_discount + IMPORTING + !rate_per_hour TYPE f + !num_days TYPE f + !discount TYPE f + RETURNING + VALUE(result) TYPE f. + + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + + + +CLASS zcl_freelancer_rates IMPLEMENTATION. + + + METHOD days_in_budget. +* add solution here + ENDMETHOD. + + + METHOD day_rate. +* add solution here + ENDMETHOD. + + + METHOD price_with_monthly_discount. +* add solution here + ENDMETHOD. +ENDCLASS. diff --git a/exercises/concept/freelancer-rates/zcl_freelancer_rates.clas.testclasses.abap b/exercises/concept/freelancer-rates/zcl_freelancer_rates.clas.testclasses.abap new file mode 100644 index 00000000..654e90b1 --- /dev/null +++ b/exercises/concept/freelancer-rates/zcl_freelancer_rates.clas.testclasses.abap @@ -0,0 +1,118 @@ +CLASS ltcl_freelancer_rates DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT FINAL. + + PRIVATE SECTION. + + DATA cut TYPE REF TO zcl_freelancer_rates. + METHODS setup. + METHODS test_dayrate16 FOR TESTING RAISING cx_static_check. + METHODS test_dayrate25 FOR TESTING RAISING cx_static_check. + METHODS test_dayrate3140 FOR TESTING RAISING cx_static_check. + METHODS test_dayrate8989 FOR TESTING RAISING cx_static_check. + METHODS test_dayrate9765 FOR TESTING RAISING cx_static_check. + METHODS test_daysinbudget_1280_16 FOR TESTING RAISING cx_static_check. + METHODS test_daysinbudget_1280_25 FOR TESTING RAISING cx_static_check. + METHODS test_daysinbudget_835_12 FOR TESTING RAISING cx_static_check. + METHODS test_discount_16_70_0 FOR TESTING RAISING cx_static_check. + METHODS test_discount_16_130_15 FOR TESTING RAISING cx_static_check. + METHODS test_discount_29_220_112 FOR TESTING RAISING cx_static_check. + METHODS test_discount_29_155_25 FOR TESTING RAISING cx_static_check. + +ENDCLASS. + +CLASS ltcl_freelancer_rates IMPLEMENTATION. + + METHOD setup. + cut = NEW zcl_freelancer_rates( ). + ENDMETHOD. + + METHOD test_dayrate16. + cl_abap_unit_assert=>assert_equals( + act = cut->day_rate( 16 ) + exp = 128 ). + ENDMETHOD. + + METHOD test_dayrate25. + cl_abap_unit_assert=>assert_equals( + act = cut->day_rate( 25 ) + exp = 200 ). + ENDMETHOD. + + METHOD test_dayrate3140. + cl_abap_unit_assert=>assert_equals( + act = cut->day_rate( '31.4' ) + exp = '251.2' ). + ENDMETHOD. + + METHOD test_dayrate8989. + cl_abap_unit_assert=>assert_equals( + act = cut->day_rate( '89.89' ) + exp = '719.12' ). + ENDMETHOD. + + METHOD test_dayrate9765. + cl_abap_unit_assert=>assert_equals( + act = cut->day_rate( '97.654321' ) + exp = '781.23' ). + ENDMETHOD. + + METHOD test_daysinbudget_1280_16. + cl_abap_unit_assert=>assert_equals( + act = cut->days_in_budget( + budget = 1280 + rate_per_hour = 16 ) + exp = 10 ). + ENDMETHOD. + + METHOD test_daysinbudget_1280_25. + cl_abap_unit_assert=>assert_equals( + act = cut->days_in_budget( + budget = 1280 + rate_per_hour = 16 ) + exp = 10 ). + ENDMETHOD. + + METHOD test_daysinbudget_835_12. + cl_abap_unit_assert=>assert_equals( + act = cut->days_in_budget( + budget = 1280 + rate_per_hour = 16 ) + exp = 10 ). + ENDMETHOD. + + METHOD test_discount_16_70_0. + cl_abap_unit_assert=>assert_equals( + act = cut->price_with_monthly_discount( + rate_per_hour = 16 + num_days = 70 + discount = 0 ) + exp = 8960 ). + ENDMETHOD. + + METHOD test_discount_16_130_15. + cl_abap_unit_assert=>assert_equals( + act = cut->price_with_monthly_discount( + rate_per_hour = 16 + num_days = 130 + discount = '0.15' ) + exp = 14528 ). + ENDMETHOD. + + METHOD test_discount_29_220_112. + cl_abap_unit_assert=>assert_equals( + act = cut->price_with_monthly_discount( + rate_per_hour = '29.654321' + num_days = 220 + discount = '0.112' ) + exp = 46346 ). + ENDMETHOD. + + METHOD test_discount_29_155_25. + cl_abap_unit_assert=>assert_equals( + act = cut->price_with_monthly_discount( + rate_per_hour = '29.654321' + num_days = 155 + discount = '0.2547' ) + exp = 27466 ). + ENDMETHOD. + +ENDCLASS. diff --git a/exercises/concept/freelancer-rates/zcl_freelancer_rates.clas.xml b/exercises/concept/freelancer-rates/zcl_freelancer_rates.clas.xml new file mode 100644 index 00000000..c526fa66 --- /dev/null +++ b/exercises/concept/freelancer-rates/zcl_freelancer_rates.clas.xml @@ -0,0 +1,17 @@ + + + + + + ZCL_FREELANCER_RATES + E + Exercism: Freelancer Rates + 1 + X + X + X + X + + + +