Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement exercise complex-numbers #523

Merged
merged 7 commits into from
Sep 8, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,17 @@
"control-flow (loops)"
]
},
{
"uuid": "7f4d5743-7ab8-42ca-b393-767f7e9a4e97",
"slug": "complex-numbers",
"core": false,
"unlocked_by": "leap",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please share with us the reasoning behind this decision?
Don't get me wrong, I am not against it, just curios

And it turned out (while testing a new version of Exercism) that non-core exercise should be unlocked by only a core one.
We should revisit the whole track curriculum, perhaps you can help with a fresh view

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be completely honest, I was looking at a c# track, it looks somewhat finished, so I figured that their unlocking graph is finished too. If we will be redoing the whole of dependency graph, maybe it warrants separate issue and separate PR?

Copy link
Contributor

@ilya-khadykin ilya-khadykin Sep 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this should be addressed in a separate issue\PR.
I didn't have a chance to create it, I'll create it later today
But still, it's better to leave unlocked_by empty for now (until we'll figure out a list of core exercises)

"difficulty": 6,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same for difficulty.
What affected the score to be 6?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, same hear. Took it from C# track. As I understood, difficulty grade is kinda... subjective.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Maybe we can come with some sort of formula to compute it, what do you think?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this question maybe hurried somewhere in general discussions repo. Btw, will difficulty be used at all in version 2, or there would be relying on "unlocked_by" prerequisites?

In general, it could be done, of course, simplest heuristic would be to split topics that we have in to tiers, from simplest to hardest and add the difficulty level for each additional topic for exercise. So if basically tier_level1+....tier_level2

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lilislilit, hm, this a really good point, thanks! I haven't thought about it this way
I've created the following issue exercism/discussions#193 in general discussion, feel free to add your thoughts there

"topics": [
"Tuples",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good choice.

We are trying to standardize topics across all tracks and decided to use snake_case notation as a naming convention. List of of common topics can be found here - https://github.com/exercism/problem-specifications/blob/master/TOPICS.txt
Could you please convert them to 'snake_case'

"Mathematics"
]
},
{
"uuid": "e7351e8e-d3ff-4621-b818-cd55cf05bffd",
"slug": "accumulate",
Expand Down
22 changes: 22 additions & 0 deletions exercises/complex-numbers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Complex Numbers

A complex number is a number in the form `a + b * i` where `a` and `b` are real and `i` satisfies `i^2 = -1`.

Assume the programming language you are using does not have an implementation of complex numbers.

### Submitting Exercises

Note that, when trying to submit an exercise, make sure the solution is in the `exercism/python/<exerciseName>` directory.

For example, if you're submitting `bob.py` for the Bob exercise, the submit command would be something like `exercism submit <path_to_exercism_dir>/python/bob/bob.py`.


For more detailed information about running tests, code style and linting,
please see the [help page](http://exercism.io/languages/python).

## Source

Wikipedia [https://en.wikipedia.org/wiki/Complex_number](https://en.wikipedia.org/wiki/Complex_number)

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
3 changes: 3 additions & 0 deletions exercises/complex-numbers/complex_numbers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ComplexNumber(object):
def __init__(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it was discussed in #509 it would be a good idea to include expected parameters to not force the user to go through a test class

What are your thoughts on this?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think it is a wise thing to do

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although, should I also include method signatures? Or only params for constructor? If we don't want people to go through test suite too much, including method signatures makes sense, some tracks here do this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it makes sense to include method signatures since the user uses the provided test suite anyway.
And if someone wants to create their own unique solution, they are free to change provided class placeholder or even test suite (rewrite it, add or remove tests)

I don't see how providing method signatures can influence the user's overall implementation. The most important thing for me is convenience

pass
156 changes: 156 additions & 0 deletions exercises/complex-numbers/complex_numbers_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import unittest

import math

from complex_numbers import ComplexNumber


class ComplexNumbersTest(unittest.TestCase):

def test_real_part_of_a_purely_real_number(self):
input_number = ComplexNumber(1, 0)
self.assertEqual(input_number.real, 1)

def test_real_part_of_a_purely_imaginary_number(self):
input_number = ComplexNumber(0, 1)
self.assertEqual(input_number.real, 0)

def test_real_part_of_a_number_with_real_and_imaginary_part(self):
input_number = ComplexNumber(1, 2)
self.assertEqual(input_number.real, 1)

def test_imaginary_part_of_a_purely_real_number(self):
input_number = ComplexNumber(1, 0)
self.assertEqual(input_number.imaginary, 0)

def test_imaginary_part_of_a_purely_imaginary_number(self):
input_number = ComplexNumber(0, 1)
self.assertEqual(input_number.imaginary, 1)

def test_maginary_part_of_a_number_with_real_and_imaginary_part(self):
input_number = ComplexNumber(1, 2)
self.assertEqual(input_number.imaginary, 2)

def test_add_purely_real_numbers(self):
first_input = ComplexNumber(1, 0)
second_input = ComplexNumber(3, 0)
self.assertEqual(first_input.add(second_input).real, 4)
self.assertEqual(first_input.add(second_input).imaginary, 0)

def test_add_purely_imaginary_numbers(self):
first_input = ComplexNumber(0, 1)
second_input = ComplexNumber(0, 3)
self.assertEqual(first_input.add(second_input).real, 0)
self.assertEqual(first_input.add(second_input).imaginary, 4)

def test_add_numbers_with_real_and_imaginary_part(self):
first_input = ComplexNumber(1, 2)
second_input = ComplexNumber(4, 6)
self.assertEqual(first_input.add(second_input).real, 5)
self.assertEqual(first_input.add(second_input).imaginary, 8)

def test_subtract_purely_real_numbers(self):
first_input = ComplexNumber(1, 0)
second_input = ComplexNumber(-1, 0)
self.assertEqual(first_input.sub(second_input).real, 2)
self.assertEqual(first_input.sub(second_input).imaginary, 0)

def test_substract_numbers_with_real_and_imaginary_part(self):
first_input = ComplexNumber(1, 2)
second_input = ComplexNumber(-2, -2)
self.assertEqual(first_input.sub(second_input).real, 3)
self.assertEqual(first_input.sub(second_input).imaginary, 4)

def test_multiply_purely_real_numbers(self):
first_input = ComplexNumber(1, 0)
second_input = ComplexNumber(2, 0)
self.assertEqual(first_input.mul(second_input).real, 2)
self.assertEqual(first_input.mul(second_input).imaginary, 0)

def test_multiply_numbers_with_real_and_imaginary_part(self):
first_input = ComplexNumber(1, 2)
second_input = ComplexNumber(-5, 10)
self.assertEqual(first_input.mul(second_input).real, -5)
self.assertEqual(first_input.mul(second_input).imaginary, 20)

def test_divide_purely_real_numbers(self):
input_number = ComplexNumber(1.0, 0.0)
expected = ComplexNumber(0.5, 0.0)
divider = ComplexNumber(2.0, 0.0)
self.assertEqual(expected.real, input_number.div(divider).real)
self.assertEqual(expected.imaginary,
input_number.div(divider).imaginary)

def test_divide_purely_imaginary_numbers(self):
input_number = ComplexNumber(0, 1)
expected = ComplexNumber(0.5, 0)
divider = ComplexNumber(0, 2)
self.assertEqual(expected.real, input_number.div(divider).real)
self.assertEqual(expected.imaginary,
input_number.div(divider).imaginary)

def test_divide_numbers_with_real_and_imaginary_part(self):
input_number = ComplexNumber(1, 2)
expected = ComplexNumber(0.44, 0.08)
divider = ComplexNumber(3, 4)
self.assertEqual(expected.real, input_number.div(divider).real)
self.assertEqual(expected.imaginary,
input_number.div(divider).imaginary)

def test_absolute_value_of_a_positive_purely_real_number(self):
self.assertEqual(ComplexNumber(5, 0).abs(), 5)

def test_absolute_value_of_a_negative_purely_real_number(self):
self.assertEqual(ComplexNumber(-5, 0).abs(), 5)

def test_absolute_value_of_imaginary_number_negative_imaginary_part(self):
self.assertEqual(ComplexNumber(0, -5).abs(), 5)

def test_absolute_value_of_imaginary_number_positive_imaginary_part(self):
self.assertEqual(ComplexNumber(0, 5).abs(), 5)

def test_absolute_value_of_a_number_with_real_and_imaginary_part(self):
self.assertEqual(ComplexNumber(3, 4).abs(), 5)

def test_conjugate_a_purely_real_number(self):
input_number = ComplexNumber(5, 0)
expected = ComplexNumber(5, 0)
self.assertEqual(expected.real, input_number.conjugate().real)
self.assertEqual(expected.imaginary,
input_number.conjugate().imaginary)

def test_conjugate_a_purely_imaginary_number(self):
input_number = ComplexNumber(0, 5)
expected = ComplexNumber(0, -5)
self.assertEqual(expected.real, input_number.conjugate().real)
self.assertEqual(expected.imaginary,
input_number.conjugate().imaginary)

def conjugate_a_number_with_real_and_imaginary_part(self):
input_number = ComplexNumber(1, 1)
expected = ComplexNumber(1, -1)
self.assertEqual(expected.real, input_number.conjugate().real)
self.assertEqual(expected.imaginary,
input_number.conjugate().imaginary)

def test_eulers_identity_formula(self):
input_number = ComplexNumber(0, math.pi)
expected = ComplexNumber(-1, 0)
self.assertEqual(expected.real, input_number.exp().real)
self.assertEqual(expected.imaginary, input_number.exp().imaginary)

def test_exponential_of_0(self):
input_number = ComplexNumber(0, 0)
expected = ComplexNumber(1, 0)
self.assertEqual(expected.real, input_number.exp().real)
self.assertEqual(expected.imaginary, input_number.exp().imaginary)

def test_exponential_of_a_purely_real_number(self):
input_number = ComplexNumber(1, 0)
expected = ComplexNumber(math.e, 0)
self.assertEqual(expected.real, input_number.exp().real)
self.assertEqual(expected.imaginary, input_number.exp().imaginary)


if __name__ == '__main__':
unittest.main()
42 changes: 42 additions & 0 deletions exercises/complex-numbers/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import math


class ComplexNumber(object):
def __init__(self, real, imaginary):
self.real = real
self.imaginary = imaginary

def add(self, other):
r = self.real + other.real
i = self.imaginary + other.imaginary
return ComplexNumber(r, i)

def mul(self, other):
r = self.real * other.real
i = self.imaginary * other.imaginary
return ComplexNumber(r, i)

def sub(self, other):
r = self.real - other.real
i = self.imaginary - other.imaginary
return ComplexNumber(r, i)

def div(self, other):
d = other.real * other.real + other.imaginary * other.imaginary
r = (self.real * other.real + self.imaginary *
other.imaginary) / float(d)
i = (self.imaginary * other.real - self.real *
self.real * other.imaginary) / float(d)
return ComplexNumber(r, i)

def abs(self):
square_sum = self.real * self.real + self.imaginary * self.imaginary
return math.sqrt(square_sum)

def conjugate(self):
return ComplexNumber(self.real, -1 * self.imaginary)

def exp(self):
r = round(math.cos(self.imaginary), 8) * math.exp(self.real)
i = round(math.sin(self.imaginary), 8) * math.exp(self.real)
return ComplexNumber(r, i)