## Image Algorithms Review

### Rotate Left

```                ```
def rotate_image_left(image):
out = SimpleImage.blank(image.height, image.width)

for y in range(image.height):
for x in range(image.width):
source_pixel = image.get_pixel(x, y)
dest_pixel = image.get_pixel(y, image.width - 1 - x)
dest_pixel.red = source_pixel.red
dest_pixel.green = source_pixel.green
dest_pixel.blue = source_pixel.blue

return out
```
```

### Flip Vertical

```                ```
def flip_vertical(image):
out = SimpleImage.blank(image.height, image.width)

for y in range(image.height):
for x in range(image.width):
source_pixel = image.get_pixel(x, y)
dest_pixel = image.get_pixel(x, image.height - 1 - y)
dest_pixel.red = source_pixel.red
dest_pixel.green = source_pixel.green
dest_pixel.blue = source_pixel.blue

return out
```
```

## Drawing Concentric Ovals

```          ```
def draw_concentric_ovals(canvas, x,y, width, height, num_circles, padding):
x_val = x
y_val = y
canvas.draw_oval(x_val, y_val, width, height)
for i in range(num_circles-1):

canvas.draw_oval(x_val, y_val, width, height)
```
```

## Line Designs

```          ```
def draw_diamond_design(canvas, n, width, height):

center_x = width // 2
center_y = height // 2

x_factor = center_x // n
y_factor = center_y // n

# Top right
for i in range(n):
x = center_x + x_factor * i
y = center_y - y_factor * (n - i)
canvas.draw_line(x, center_y, center_x, y)

# Bottom right
for i in range(n):
x = center_x + x_factor * i
y = center_y + y_factor * (n - i)
canvas.draw_line(x, center_y, center_x, y)

# Bottom left
for i in range(n):
x = center_x - x_factor * i
y = center_y + y_factor * (n - i)
canvas.draw_line(x, center_y, center_x, y)

# Top left
for i in range(n):
x = center_x - x_factor * i
y = center_y - y_factor * (n - i)
canvas.draw_line(x, center_y, center_x, y)
```
```

## Lego Pyramid

```          ```
def draw_pyramid(canvas, width, height, num_rows, block_width, block_height):
"""
Draw a pyramid, with num_rows rows, and the specified block
dimensions.

Arguments:
canvas       -- The canvas to draw on.
width        -- The width of the canvas.
height       -- The height of the canvas.
num_rows     -- The number of rows in the pyamid.
block_width  -- The width of each block in the pyramid.
block_height -- The height of each block in the pyramid.
"""
for row in range(num_rows):
base_x = row * block_width // 2
y = height - (row + 1) * block_height

for block in range(num_rows - row):
x = base_x + block_width * block
canvas.draw_rect(x, y, block_width, block_height)
```
```

## Opening the Black Box

```              ```
def in_range(n, low, high):
if n >= low and n <= high:
return True
return False

def is_even(n):
if n % 2 == 0:
return True
return False

def is_prime(n):
for possible_factor in range(2, n):
# we skip 0 and 1, since they're not the factors we're interested in
if n % possible_factor == 0:
# we've found a factor of n, so n isn't prime and we can return False
return False # ends the function
# if the loop ends, it means we didn't find any factors
# and so we just return True
return True

def only_one_even(num1, num2):
if num1 % 2 == 0 and num2 % 2 == 1:
return True
if num1 % 2 == 1 and num2 % 2 == 0:
return True
return False

def only_one_even(num1, num2):
if (num1 % 2 == 0 and num2 % 2 == 1) or (num1 % 2 == 1 and num2 % 2 == 0):
return True
return False

def is_power_of_two(n):
if n < 1:
# powers of 2 are equal to 1 or greater
return False
while n > 1:
if n % 2 == 1:
# n is odd, so can't be divided by 2. Therefore it's not a power of 2
return False
n /= 2 # divide n by 2, and continue checking
# we got down to 1, so n is a power of 2!
return True
```
```

## Playing with Strings

### Converting Strings to Integers

```                              ```
def convert_to_int(s):
result = 0
for ch in s:
result *= 10
result += int(ch)
return result
```
```

### Remove All Occurrences

```                              ```
def remove_all_occurrences(s, to_remove):
result = ""

for ch in s:
if ch != to_remove:
result += ch

return result
```
```

### Palindrome Checker

```                      ```
def is_palindrome(s):
for i in range(len(s)):
if s[i] != s[len(s) - 1 - i]:
return False
return True
```
```

#### Adding Comma to Numeric Strings

```                              ```
"""
Solution 1
"""
result = ""
num_digits = 0
for char_index in range(len(digits)):
result = digits[len(digits) - char_index - 1] + result
num_digits += 1
if num_digits % 3 == 0 and char_index > 0 and \
num_digits < len(digits):
"""
We need to check that
a) we've added a multiple of 3 number of digits
b) we've added at least one digit
c) there are more digits to add
"""
result="," + result
return result

"""
Alternate Solution
'1,234,567,890'
'12,345'
'1,234'
'123'
'12'
'1'
"""
result = ''
for i in range(len(s)):
# Append the digits left-right as usual.
# Notice if multiple-of-3 to_do after the digit just added.
result += s[i]
to_do = len(s) - (i + 1)
if to_do > 0 and to_do % 3 == 0:
result += ','
return result
```
```

#### Exclaim

```                ```
def exclaim(msg, end, n):
"""
Prints out the message with an exclamatory
ending which is printed n number of times.

Arguments:
msg -- The message to print out.
end -- The exclamation to add to the end of the message.
n   -- The number of times to print the exclamation.
"""
output = msg

for i in range(n):
output += end

print(output)
```
```

• When writing doctests, your test cases should cover a wide enough range of possible inputs that you feel comfortable that your function behaves as expected. Part of this is including what you'd consider to be 'normal' input: that is, input to the function that is easy to reason about and which are easy to program for. However, equally important is testing that your function gracefully handles the more annoying cases that are slightly harder to program for. In programming, these are called 'edge cases' and refer to inputs for which your function might need special logic. As a general rule, try to find test cases that could possibly break your function, rather than those which confirm what you know to be true about it already.

A second consideration is that doctests can help document your function: someone reading them can start to understand what your function does without having to reflect on the code. Thus, it's usually a good idea to have inputs that are relatively simple to reason about: small numbers and short strings, for example. In addition to comments about your function, doctests are a great way of specifying behaviour.

• There's no hard and fast rule about this (unless we specify one in an assignment!). Rather, add doctests until you're confident in your coverage of possible inputs to your function, but don't add doctests for the sake of doctests.

• There is a very long list of possible doctests one could add, but here are a few ideas for inputs you might want to try:

• `in_range`: cases where `n` is and is not in the range, as well as cases in which the range is invalid (i.e `high` is less than `low`), or has the same start and end point.
• `is_power_of_two`: negative input, 0, powers of 2 and non-powers of 2.
• `convert_to_int`: `"0"` and some numbers of different lengths.
• `remove_all_occurrences`: A string which does not contain `to_remove`, a string that is entirely `to_remove`, and some more conventional cases.

Try to think of other cases you might use!