{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "This notebook was prepared by [Donne Martin](https://github.com/donnemartin). Source and license info is on [GitHub](https://github.com/donnemartin/interactive-coding-challenges)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Solution Notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Problem: Implement the method draw_line(screen, width, x1, x2) where screen is a list of bytes, width is divisible by 8, and x1, x2 are absolute pixel positions.\n", "\n", "* [Constraints](#Constraints)\n", "* [Test Cases](#Test-Cases)\n", "* [Algorithm](#Algorithm)\n", "* [Code](#Code)\n", "* [Unit Test](#Unit-Test)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Constraints\n", "\n", "* Can we assume the inputs are valid?\n", " * No\n", "* For the output, do we set corresponding bits in screen?\n", " * Yes\n", "* Can we assume this fits memory?\n", " * Yes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Test Cases\n", "\n", "* Invalid inputs -> Exception\n", " * screen is empty\n", " * width = 0\n", " * any input param is None\n", " * x1 or x2 is out of bounds\n", "* General case for len(screen) = 20, width = 32:\n", " * x1 = 2, x2 = 6\n", " * screen[0] = int('00111110', base=2)\n", " * x1 = 68, x2 = 80\n", " * screen[8], int('00001111', base=2)\n", " * screen[9], int('11111111', base=2)\n", " * screen[10], int('10000000', base=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Algorithm\n", "\n", "* Set start offset to x1 % 8\n", "* Set end offset to x2 % 8\n", "* Determine where the first and last full bytes are\n", " * First full byte = x1 // 8\n", " * Increment by 1 if start offset != 0\n", " * Last full byte = x2 // 8\n", " * Decrement by 1 if end offset != 7\n", " * Fill the bytes with 1111 1111\n", "* If x1 and x2 are in the same byte\n", "\n", "
\n",
    "    x1 = 2\n",
    "    x2 = 6\n",
    "\n",
    "    index  0123 4567\n",
    "    byte   0011 1110\n",
    "\n",
    "    We want to build left and right masks such that:\n",
    "\n",
    "    left   0011 1111\n",
    "    right  1111 1110\n",
    "    ----------------\n",
    "           0011 1110  left & right\n",
    "\n",
    "    Build the left mask:\n",
    "\n",
    "    left   0000 0001  1\n",
    "           0100 0000  1 << (8 - x1)\n",
    "           0011 1111  (1 << (8 - x1)) - 1\n",
    "\n",
    "    Build the right mask:\n",
    "\n",
    "    right  0000 0000  1\n",
    "           0000 0010  1 << (8 - x2 - 1)\n",
    "           0000 0001  (1 << (8 - x2 - 1) - 1\n",
    "           1111 1110  ~((1 << (8 - x2 - 1) - 1)\n",
    "\n",
    "    Combine the left and right masks:\n",
    "\n",
    "    left   0011 1111\n",
    "    right  1111 1110\n",
    "    ----------------\n",
    "           0011 1110  left & right\n",
    "\n",
    "    Set screen[x1//8] or screen[x2//8] |= mask\n",
    "
\n", "* Else\n", "
\n",
    "    If our starting partial byte is:\n",
    "           0000 1111\n",
    "\n",
    "    Build start mask:\n",
    "    \n",
    "    start  0000 0001  1\n",
    "           0001 0000  1 << 1 << start offset\n",
    "           0000 1111  (1 << 1 << start offset) - 1\n",
    "\n",
    "    If our ending partial byte is:\n",
    "           1111 1100\n",
    "\n",
    "    Build end mask:\n",
    "    \n",
    "    end    1000 0000  0x10000000\n",
    "           1111 1100  0x10000000 >> end offset\n",
    "\n",
    "    Set screen[x1//8] |= start mask\n",
    "    Set screen[x2//8] |= end mask\n",
    "
\n", "\n", "Complexity:\n", "* Time: O(length of screen)\n", "* Space: O(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Code" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class BitsScreen(object):\n", "\n", " def draw_line(self, screen, width, x1, x2):\n", " if None in (screen, width, x1, x2):\n", " raise TypeError('Invalid argument: None')\n", " if not screen or not width:\n", " raise ValueError('Invalid arg: Empty screen or width')\n", " MAX_BIT_VALUE = len(screen) * 8\n", " if x1 < 0 or x2 < 0 or x1 >= MAX_BIT_VALUE or x2 >= MAX_BIT_VALUE:\n", " raise ValueError('Invalid arg: x1 or x2 out of bounds')\n", " start_bit = x1 % 8\n", " end_bit = x2 % 8\n", " first_full_byte = x1 // 8\n", " if start_bit != 0:\n", " first_full_byte += 1\n", " last_full_byte = x2 // 8\n", " if end_bit != (8 - 1):\n", " last_full_byte -= 1\n", " for byte in range(first_full_byte, last_full_byte + 1):\n", " screen[byte] = int('11111111', base=2)\n", " start_byte = x1 // 8\n", " end_byte = x2 // 8\n", " if start_byte == end_byte:\n", " left_mask = (1 << (8 - start_bit)) - 1\n", " right_mask = ~((1 << (8 - end_bit - 1)) - 1)\n", " mask = left_mask & right_mask\n", " screen[start_byte] |= mask\n", " else:\n", " start_mask = (1 << (8 - start_bit)) - 1\n", " end_mask = 1 << (8 - end_bit - 1)\n", " screen[start_byte] |= start_mask\n", " screen[end_byte] |= end_mask" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Unit Test" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Overwriting test_draw_line.py\n" ] } ], "source": [ "%%writefile test_draw_line.py\n", "from nose.tools import assert_equal\n", "\n", "\n", "class TestBitsScreen(object):\n", "\n", " def test_draw_line(self):\n", " bits_screen = BitsScreen()\n", " screen = []\n", " for _ in range(20):\n", " screen.append(int('00000000', base=2))\n", " bits_screen.draw_line(screen, width=32, x1=68, x2=80)\n", " assert_equal(screen[8], int('00001111', base=2))\n", " assert_equal(screen[9], int('11111111', base=2))\n", " assert_equal(screen[10], int('10000000', base=2))\n", " bits_screen.draw_line(screen, width=32, x1=2, x2=6)\n", " assert_equal(screen[0], int('00111110', base=2))\n", " bits_screen.draw_line(screen, width=32, x1=10, x2=13)\n", " assert_equal(screen[1], int('00111100', base=2))\n", " print('Success: test_draw_line')\n", "\n", "\n", "def main():\n", " test = TestBitsScreen()\n", " test.test_draw_line()\n", "\n", "\n", "if __name__ == '__main__':\n", " main()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Success: test_draw_line\n" ] } ], "source": [ "%run -i test_draw_line.py" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.4.3" } }, "nbformat": 4, "nbformat_minor": 0 }