{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [Project Euler, first problem: \"Multiples of 3 and 5\"](https://projecteuler.net/problem=1)\n",
    "\n",
    "    If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.\n",
    "\n",
    "    Find the sum of all the multiples of 3 or 5 below 1000."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from notebook_preamble import J, V, define"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's create a predicate that returns `True` if a number is a multiple of 3 or 5 and `False` otherwise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('P [3 % not] dupdip 5 % not or')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "             • 80 P\n",
      "          80 • P\n",
      "          80 • [3 % not] dupdip 5 % not or\n",
      "80 [3 % not] • dupdip 5 % not or\n",
      "          80 • 3 % not 80 5 % not or\n",
      "        80 3 • % not 80 5 % not or\n",
      "           2 • not 80 5 % not or\n",
      "       False • 80 5 % not or\n",
      "    False 80 • 5 % not or\n",
      "  False 80 5 • % not or\n",
      "     False 0 • not or\n",
      "  False True • or\n",
      "        True • \n"
     ]
    }
   ],
   "source": [
    "V('80 P')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Given the predicate function `P` a suitable program is:\n",
    "\n",
    "    PE1 == 1000 range [P] filter sum\n",
    "\n",
    "This function generates a list of the integers from 0 to 999, filters\n",
    "that list by `P`, and then sums the result.\n",
    "\n",
    "Logically this is fine, but pragmatically we are doing more work than we\n",
    "should be; we generate one thousand integers but actually use less than\n",
    "half of them.  A better solution would be to generate just the multiples\n",
    "we want to sum, and to add them as we go rather than storing them and\n",
    "adding summing them at the end.\n",
    "\n",
    "At first I had the idea to use two counters and increase them by three\n",
    "and five, respectively.  This way we only generate the terms that we\n",
    "actually want to sum.  We have to proceed by incrementing the counter\n",
    "that is lower, or if they are equal, the three counter, and we have to\n",
    "take care not to double add numbers like 15 that are multiples of both\n",
    "three and five.\n",
    "\n",
    "This seemed a little clunky, so I tried a different approach.\n",
    "\n",
    "Consider the first few terms in the series:\n",
    "\n",
    "    3 5 6 9 10 12 15 18 20 21 ...\n",
    "\n",
    "Subtract each number from the one after it (subtracting 0 from 3):\n",
    "\n",
    "    3 5 6 9 10 12 15 18 20 21 24 25 27 30 ...\n",
    "    0 3 5 6  9 10 12 15 18 20 21 24 25 27 ...\n",
    "    -------------------------------------------\n",
    "    3 2 1 3  1  2  3  3  2  1  3  1  2  3 ...\n",
    "\n",
    "You get this lovely repeating palindromic sequence:\n",
    "\n",
    "    3 2 1 3 1 2 3\n",
    "\n",
    "To make a counter that increments by factors of 3 and 5 you just add\n",
    "these differences to the counter one-by-one in a loop.\n",
    "\n",
    "\n",
    "To make use of this sequence to increment a counter and sum terms as we\n",
    "go we need a function that will accept the sum, the counter, and the next\n",
    "term to add, and that adds the term to the counter and a copy of the\n",
    "counter to the running sum.  This function will do that:\n",
    "\n",
    "    PE1.1 == + [+] dupdip"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('PE1.1 + [+] dupdip')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "        • 0 0 3 PE1.1\n",
      "      0 • 0 3 PE1.1\n",
      "    0 0 • 3 PE1.1\n",
      "  0 0 3 • PE1.1\n",
      "  0 0 3 • + [+] dupdip\n",
      "    0 3 • [+] dupdip\n",
      "0 3 [+] • dupdip\n",
      "    0 3 • + 3\n",
      "      3 • 3\n",
      "    3 3 • \n"
     ]
    }
   ],
   "source": [
    "V('0 0 3 PE1.1')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                            • 0 0 [3 2 1 3 1 2 3] [PE1.1] step\n",
      "                          0 • 0 [3 2 1 3 1 2 3] [PE1.1] step\n",
      "                        0 0 • [3 2 1 3 1 2 3] [PE1.1] step\n",
      "        0 0 [3 2 1 3 1 2 3] • [PE1.1] step\n",
      "0 0 [3 2 1 3 1 2 3] [PE1.1] • step\n",
      "              0 0 3 [PE1.1] • i [2 1 3 1 2 3] [PE1.1] step\n",
      "                      0 0 3 • PE1.1 [2 1 3 1 2 3] [PE1.1] step\n",
      "                      0 0 3 • + [+] dupdip [2 1 3 1 2 3] [PE1.1] step\n",
      "                        0 3 • [+] dupdip [2 1 3 1 2 3] [PE1.1] step\n",
      "                    0 3 [+] • dupdip [2 1 3 1 2 3] [PE1.1] step\n",
      "                        0 3 • + 3 [2 1 3 1 2 3] [PE1.1] step\n",
      "                          3 • 3 [2 1 3 1 2 3] [PE1.1] step\n",
      "                        3 3 • [2 1 3 1 2 3] [PE1.1] step\n",
      "          3 3 [2 1 3 1 2 3] • [PE1.1] step\n",
      "  3 3 [2 1 3 1 2 3] [PE1.1] • step\n",
      "              3 3 2 [PE1.1] • i [1 3 1 2 3] [PE1.1] step\n",
      "                      3 3 2 • PE1.1 [1 3 1 2 3] [PE1.1] step\n",
      "                      3 3 2 • + [+] dupdip [1 3 1 2 3] [PE1.1] step\n",
      "                        3 5 • [+] dupdip [1 3 1 2 3] [PE1.1] step\n",
      "                    3 5 [+] • dupdip [1 3 1 2 3] [PE1.1] step\n",
      "                        3 5 • + 5 [1 3 1 2 3] [PE1.1] step\n",
      "                          8 • 5 [1 3 1 2 3] [PE1.1] step\n",
      "                        8 5 • [1 3 1 2 3] [PE1.1] step\n",
      "            8 5 [1 3 1 2 3] • [PE1.1] step\n",
      "    8 5 [1 3 1 2 3] [PE1.1] • step\n",
      "              8 5 1 [PE1.1] • i [3 1 2 3] [PE1.1] step\n",
      "                      8 5 1 • PE1.1 [3 1 2 3] [PE1.1] step\n",
      "                      8 5 1 • + [+] dupdip [3 1 2 3] [PE1.1] step\n",
      "                        8 6 • [+] dupdip [3 1 2 3] [PE1.1] step\n",
      "                    8 6 [+] • dupdip [3 1 2 3] [PE1.1] step\n",
      "                        8 6 • + 6 [3 1 2 3] [PE1.1] step\n",
      "                         14 • 6 [3 1 2 3] [PE1.1] step\n",
      "                       14 6 • [3 1 2 3] [PE1.1] step\n",
      "             14 6 [3 1 2 3] • [PE1.1] step\n",
      "     14 6 [3 1 2 3] [PE1.1] • step\n",
      "             14 6 3 [PE1.1] • i [1 2 3] [PE1.1] step\n",
      "                     14 6 3 • PE1.1 [1 2 3] [PE1.1] step\n",
      "                     14 6 3 • + [+] dupdip [1 2 3] [PE1.1] step\n",
      "                       14 9 • [+] dupdip [1 2 3] [PE1.1] step\n",
      "                   14 9 [+] • dupdip [1 2 3] [PE1.1] step\n",
      "                       14 9 • + 9 [1 2 3] [PE1.1] step\n",
      "                         23 • 9 [1 2 3] [PE1.1] step\n",
      "                       23 9 • [1 2 3] [PE1.1] step\n",
      "               23 9 [1 2 3] • [PE1.1] step\n",
      "       23 9 [1 2 3] [PE1.1] • step\n",
      "             23 9 1 [PE1.1] • i [2 3] [PE1.1] step\n",
      "                     23 9 1 • PE1.1 [2 3] [PE1.1] step\n",
      "                     23 9 1 • + [+] dupdip [2 3] [PE1.1] step\n",
      "                      23 10 • [+] dupdip [2 3] [PE1.1] step\n",
      "                  23 10 [+] • dupdip [2 3] [PE1.1] step\n",
      "                      23 10 • + 10 [2 3] [PE1.1] step\n",
      "                         33 • 10 [2 3] [PE1.1] step\n",
      "                      33 10 • [2 3] [PE1.1] step\n",
      "                33 10 [2 3] • [PE1.1] step\n",
      "        33 10 [2 3] [PE1.1] • step\n",
      "            33 10 2 [PE1.1] • i [3] [PE1.1] step\n",
      "                    33 10 2 • PE1.1 [3] [PE1.1] step\n",
      "                    33 10 2 • + [+] dupdip [3] [PE1.1] step\n",
      "                      33 12 • [+] dupdip [3] [PE1.1] step\n",
      "                  33 12 [+] • dupdip [3] [PE1.1] step\n",
      "                      33 12 • + 12 [3] [PE1.1] step\n",
      "                         45 • 12 [3] [PE1.1] step\n",
      "                      45 12 • [3] [PE1.1] step\n",
      "                  45 12 [3] • [PE1.1] step\n",
      "          45 12 [3] [PE1.1] • step\n",
      "            45 12 3 [PE1.1] • i\n",
      "                    45 12 3 • PE1.1\n",
      "                    45 12 3 • + [+] dupdip\n",
      "                      45 15 • [+] dupdip\n",
      "                  45 15 [+] • dupdip\n",
      "                      45 15 • + 15\n",
      "                         60 • 15\n",
      "                      60 15 • \n"
     ]
    }
   ],
   "source": [
    "V('0 0 [3 2 1 3 1 2 3] [PE1.1] step')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "So one `step` through all seven terms brings the counter to 15 and the total to 60."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "66.66666666666667"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1000 / 15"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "990"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "66 * 15"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "1000 - 990"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "999 - 990"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That means we want to run the full list of numbers sixty-six times to get to 990 and then the first four numbers 3 2 1 3 to get to 999."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('PE1 0 0 66 [[3 2 1 3 1 2 3] [PE1.1] step] times [3 2 1 3] [PE1.1] step pop')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "233168\n"
     ]
    }
   ],
   "source": [
    "J('PE1')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This form uses no extra storage and produces no unused summands.  It's\n",
    "good but there's one more trick we can apply.  The list of seven terms\n",
    "takes up at least seven bytes.  But notice that all of the terms are less\n",
    "than four, and so each can fit in just two bits.  We could store all\n",
    "seven terms in just fourteen bits and use masking and shifts to pick out\n",
    "each term as we go.  This will use less space and save time loading whole\n",
    "integer terms from the list.\n",
    "\n",
    "        3  2  1  3  1  2  3\n",
    "    0b 11 10 01 11 01 10 11 == 14811"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "14811"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "0b11100111011011"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('PE1.2 [3 & PE1.1] dupdip 2 >>')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                      • 0 0 14811 PE1.2\n",
      "                    0 • 0 14811 PE1.2\n",
      "                  0 0 • 14811 PE1.2\n",
      "            0 0 14811 • PE1.2\n",
      "            0 0 14811 • [3 & PE1.1] dupdip 2 >>\n",
      "0 0 14811 [3 & PE1.1] • dupdip 2 >>\n",
      "            0 0 14811 • 3 & PE1.1 14811 2 >>\n",
      "          0 0 14811 3 • & PE1.1 14811 2 >>\n",
      "                0 0 3 • PE1.1 14811 2 >>\n",
      "                0 0 3 • + [+] dupdip 14811 2 >>\n",
      "                  0 3 • [+] dupdip 14811 2 >>\n",
      "              0 3 [+] • dupdip 14811 2 >>\n",
      "                  0 3 • + 3 14811 2 >>\n",
      "                    3 • 3 14811 2 >>\n",
      "                  3 3 • 14811 2 >>\n",
      "            3 3 14811 • 2 >>\n",
      "          3 3 14811 2 • >>\n",
      "             3 3 3702 • \n"
     ]
    }
   ],
   "source": [
    "V('0 0 14811 PE1.2')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                     • 3 3 3702 PE1.2\n",
      "                   3 • 3 3702 PE1.2\n",
      "                 3 3 • 3702 PE1.2\n",
      "            3 3 3702 • PE1.2\n",
      "            3 3 3702 • [3 & PE1.1] dupdip 2 >>\n",
      "3 3 3702 [3 & PE1.1] • dupdip 2 >>\n",
      "            3 3 3702 • 3 & PE1.1 3702 2 >>\n",
      "          3 3 3702 3 • & PE1.1 3702 2 >>\n",
      "               3 3 2 • PE1.1 3702 2 >>\n",
      "               3 3 2 • + [+] dupdip 3702 2 >>\n",
      "                 3 5 • [+] dupdip 3702 2 >>\n",
      "             3 5 [+] • dupdip 3702 2 >>\n",
      "                 3 5 • + 5 3702 2 >>\n",
      "                   8 • 5 3702 2 >>\n",
      "                 8 5 • 3702 2 >>\n",
      "            8 5 3702 • 2 >>\n",
      "          8 5 3702 2 • >>\n",
      "             8 5 925 • \n"
     ]
    }
   ],
   "source": [
    "V('3 3 3702 PE1.2')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "                      • 0 0 14811 7 [PE1.2] times pop\n",
      "                    0 • 0 14811 7 [PE1.2] times pop\n",
      "                  0 0 • 14811 7 [PE1.2] times pop\n",
      "            0 0 14811 • 7 [PE1.2] times pop\n",
      "          0 0 14811 7 • [PE1.2] times pop\n",
      "  0 0 14811 7 [PE1.2] • times pop\n",
      "            0 0 14811 • PE1.2 6 [PE1.2] times pop\n",
      "            0 0 14811 • [3 & PE1.1] dupdip 2 >> 6 [PE1.2] times pop\n",
      "0 0 14811 [3 & PE1.1] • dupdip 2 >> 6 [PE1.2] times pop\n",
      "            0 0 14811 • 3 & PE1.1 14811 2 >> 6 [PE1.2] times pop\n",
      "          0 0 14811 3 • & PE1.1 14811 2 >> 6 [PE1.2] times pop\n",
      "                0 0 3 • PE1.1 14811 2 >> 6 [PE1.2] times pop\n",
      "                0 0 3 • + [+] dupdip 14811 2 >> 6 [PE1.2] times pop\n",
      "                  0 3 • [+] dupdip 14811 2 >> 6 [PE1.2] times pop\n",
      "              0 3 [+] • dupdip 14811 2 >> 6 [PE1.2] times pop\n",
      "                  0 3 • + 3 14811 2 >> 6 [PE1.2] times pop\n",
      "                    3 • 3 14811 2 >> 6 [PE1.2] times pop\n",
      "                  3 3 • 14811 2 >> 6 [PE1.2] times pop\n",
      "            3 3 14811 • 2 >> 6 [PE1.2] times pop\n",
      "          3 3 14811 2 • >> 6 [PE1.2] times pop\n",
      "             3 3 3702 • 6 [PE1.2] times pop\n",
      "           3 3 3702 6 • [PE1.2] times pop\n",
      "   3 3 3702 6 [PE1.2] • times pop\n",
      "             3 3 3702 • PE1.2 5 [PE1.2] times pop\n",
      "             3 3 3702 • [3 & PE1.1] dupdip 2 >> 5 [PE1.2] times pop\n",
      " 3 3 3702 [3 & PE1.1] • dupdip 2 >> 5 [PE1.2] times pop\n",
      "             3 3 3702 • 3 & PE1.1 3702 2 >> 5 [PE1.2] times pop\n",
      "           3 3 3702 3 • & PE1.1 3702 2 >> 5 [PE1.2] times pop\n",
      "                3 3 2 • PE1.1 3702 2 >> 5 [PE1.2] times pop\n",
      "                3 3 2 • + [+] dupdip 3702 2 >> 5 [PE1.2] times pop\n",
      "                  3 5 • [+] dupdip 3702 2 >> 5 [PE1.2] times pop\n",
      "              3 5 [+] • dupdip 3702 2 >> 5 [PE1.2] times pop\n",
      "                  3 5 • + 5 3702 2 >> 5 [PE1.2] times pop\n",
      "                    8 • 5 3702 2 >> 5 [PE1.2] times pop\n",
      "                  8 5 • 3702 2 >> 5 [PE1.2] times pop\n",
      "             8 5 3702 • 2 >> 5 [PE1.2] times pop\n",
      "           8 5 3702 2 • >> 5 [PE1.2] times pop\n",
      "              8 5 925 • 5 [PE1.2] times pop\n",
      "            8 5 925 5 • [PE1.2] times pop\n",
      "    8 5 925 5 [PE1.2] • times pop\n",
      "              8 5 925 • PE1.2 4 [PE1.2] times pop\n",
      "              8 5 925 • [3 & PE1.1] dupdip 2 >> 4 [PE1.2] times pop\n",
      "  8 5 925 [3 & PE1.1] • dupdip 2 >> 4 [PE1.2] times pop\n",
      "              8 5 925 • 3 & PE1.1 925 2 >> 4 [PE1.2] times pop\n",
      "            8 5 925 3 • & PE1.1 925 2 >> 4 [PE1.2] times pop\n",
      "                8 5 1 • PE1.1 925 2 >> 4 [PE1.2] times pop\n",
      "                8 5 1 • + [+] dupdip 925 2 >> 4 [PE1.2] times pop\n",
      "                  8 6 • [+] dupdip 925 2 >> 4 [PE1.2] times pop\n",
      "              8 6 [+] • dupdip 925 2 >> 4 [PE1.2] times pop\n",
      "                  8 6 • + 6 925 2 >> 4 [PE1.2] times pop\n",
      "                   14 • 6 925 2 >> 4 [PE1.2] times pop\n",
      "                 14 6 • 925 2 >> 4 [PE1.2] times pop\n",
      "             14 6 925 • 2 >> 4 [PE1.2] times pop\n",
      "           14 6 925 2 • >> 4 [PE1.2] times pop\n",
      "             14 6 231 • 4 [PE1.2] times pop\n",
      "           14 6 231 4 • [PE1.2] times pop\n",
      "   14 6 231 4 [PE1.2] • times pop\n",
      "             14 6 231 • PE1.2 3 [PE1.2] times pop\n",
      "             14 6 231 • [3 & PE1.1] dupdip 2 >> 3 [PE1.2] times pop\n",
      " 14 6 231 [3 & PE1.1] • dupdip 2 >> 3 [PE1.2] times pop\n",
      "             14 6 231 • 3 & PE1.1 231 2 >> 3 [PE1.2] times pop\n",
      "           14 6 231 3 • & PE1.1 231 2 >> 3 [PE1.2] times pop\n",
      "               14 6 3 • PE1.1 231 2 >> 3 [PE1.2] times pop\n",
      "               14 6 3 • + [+] dupdip 231 2 >> 3 [PE1.2] times pop\n",
      "                 14 9 • [+] dupdip 231 2 >> 3 [PE1.2] times pop\n",
      "             14 9 [+] • dupdip 231 2 >> 3 [PE1.2] times pop\n",
      "                 14 9 • + 9 231 2 >> 3 [PE1.2] times pop\n",
      "                   23 • 9 231 2 >> 3 [PE1.2] times pop\n",
      "                 23 9 • 231 2 >> 3 [PE1.2] times pop\n",
      "             23 9 231 • 2 >> 3 [PE1.2] times pop\n",
      "           23 9 231 2 • >> 3 [PE1.2] times pop\n",
      "              23 9 57 • 3 [PE1.2] times pop\n",
      "            23 9 57 3 • [PE1.2] times pop\n",
      "    23 9 57 3 [PE1.2] • times pop\n",
      "              23 9 57 • PE1.2 2 [PE1.2] times pop\n",
      "              23 9 57 • [3 & PE1.1] dupdip 2 >> 2 [PE1.2] times pop\n",
      "  23 9 57 [3 & PE1.1] • dupdip 2 >> 2 [PE1.2] times pop\n",
      "              23 9 57 • 3 & PE1.1 57 2 >> 2 [PE1.2] times pop\n",
      "            23 9 57 3 • & PE1.1 57 2 >> 2 [PE1.2] times pop\n",
      "               23 9 1 • PE1.1 57 2 >> 2 [PE1.2] times pop\n",
      "               23 9 1 • + [+] dupdip 57 2 >> 2 [PE1.2] times pop\n",
      "                23 10 • [+] dupdip 57 2 >> 2 [PE1.2] times pop\n",
      "            23 10 [+] • dupdip 57 2 >> 2 [PE1.2] times pop\n",
      "                23 10 • + 10 57 2 >> 2 [PE1.2] times pop\n",
      "                   33 • 10 57 2 >> 2 [PE1.2] times pop\n",
      "                33 10 • 57 2 >> 2 [PE1.2] times pop\n",
      "             33 10 57 • 2 >> 2 [PE1.2] times pop\n",
      "           33 10 57 2 • >> 2 [PE1.2] times pop\n",
      "             33 10 14 • 2 [PE1.2] times pop\n",
      "           33 10 14 2 • [PE1.2] times pop\n",
      "   33 10 14 2 [PE1.2] • times pop\n",
      "             33 10 14 • PE1.2 1 [PE1.2] times pop\n",
      "             33 10 14 • [3 & PE1.1] dupdip 2 >> 1 [PE1.2] times pop\n",
      " 33 10 14 [3 & PE1.1] • dupdip 2 >> 1 [PE1.2] times pop\n",
      "             33 10 14 • 3 & PE1.1 14 2 >> 1 [PE1.2] times pop\n",
      "           33 10 14 3 • & PE1.1 14 2 >> 1 [PE1.2] times pop\n",
      "              33 10 2 • PE1.1 14 2 >> 1 [PE1.2] times pop\n",
      "              33 10 2 • + [+] dupdip 14 2 >> 1 [PE1.2] times pop\n",
      "                33 12 • [+] dupdip 14 2 >> 1 [PE1.2] times pop\n",
      "            33 12 [+] • dupdip 14 2 >> 1 [PE1.2] times pop\n",
      "                33 12 • + 12 14 2 >> 1 [PE1.2] times pop\n",
      "                   45 • 12 14 2 >> 1 [PE1.2] times pop\n",
      "                45 12 • 14 2 >> 1 [PE1.2] times pop\n",
      "             45 12 14 • 2 >> 1 [PE1.2] times pop\n",
      "           45 12 14 2 • >> 1 [PE1.2] times pop\n",
      "              45 12 3 • 1 [PE1.2] times pop\n",
      "            45 12 3 1 • [PE1.2] times pop\n",
      "    45 12 3 1 [PE1.2] • times pop\n",
      "              45 12 3 • PE1.2 pop\n",
      "              45 12 3 • [3 & PE1.1] dupdip 2 >> pop\n",
      "  45 12 3 [3 & PE1.1] • dupdip 2 >> pop\n",
      "              45 12 3 • 3 & PE1.1 3 2 >> pop\n",
      "            45 12 3 3 • & PE1.1 3 2 >> pop\n",
      "              45 12 3 • PE1.1 3 2 >> pop\n",
      "              45 12 3 • + [+] dupdip 3 2 >> pop\n",
      "                45 15 • [+] dupdip 3 2 >> pop\n",
      "            45 15 [+] • dupdip 3 2 >> pop\n",
      "                45 15 • + 15 3 2 >> pop\n",
      "                   60 • 15 3 2 >> pop\n",
      "                60 15 • 3 2 >> pop\n",
      "              60 15 3 • 2 >> pop\n",
      "            60 15 3 2 • >> pop\n",
      "              60 15 0 • pop\n",
      "                60 15 • \n"
     ]
    }
   ],
   "source": [
    "V('0 0 14811 7 [PE1.2] times pop')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And so we have at last:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('PE1 0 0 66 [14811 7 [PE1.2] times pop] times 14811 4 [PE1.2] times popop')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "233168\n"
     ]
    }
   ],
   "source": [
    "J('PE1')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's refactor.\n",
    "\n",
    "      14811 7 [PE1.2] times pop\n",
    "      14811 4 [PE1.2] times pop\n",
    "      14811 n [PE1.2] times pop\n",
    "    n 14811 swap [PE1.2] times pop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('PE1.3 14811 swap [PE1.2] times pop')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can simplify the definition above:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('PE1 0 0 66 [7 PE1.3] times 4 PE1.3 pop')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "233168\n"
     ]
    }
   ],
   "source": [
    "J('PE1')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here's our joy program all in one place.  It doesn't make so much sense, but if you have read through the above description of how it was derived I hope it's clear.\n",
    "\n",
    "    PE1.1 == + [+] dupdip\n",
    "    PE1.2 == [3 & PE1.1] dupdip 2 >>\n",
    "    PE1.3 == 14811 swap [PE1.2] times pop\n",
    "    PE1 == 0 0 66 [7 PE1.3] times 4 PE1.3 pop"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Generator Version\n",
    "It's a little clunky iterating sixty-six times though the seven numbers then four more.  In the _Generator Programs_ notebook we derive a generator that can be repeatedly driven by the `x` combinator to produce a stream of the seven numbers repeating over and over again."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('PE1.terms [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 [0 swap [dup [pop 14811] [] branch [3 &] dupdip 2 >>] dip rest cons]\n"
     ]
    }
   ],
   "source": [
    "J('PE1.terms 21 [x] times')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We know from above that we need sixty-six times seven then four more terms to reach up to but not over one thousand."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "466\n"
     ]
    }
   ],
   "source": [
    "J('7 66 * 4 +')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Here they are..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3 1 2 3 3 2 1 3\n"
     ]
    }
   ],
   "source": [
    "J('PE1.terms 466 [x] times pop')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ...and they do sum to 999."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "999\n"
     ]
    }
   ],
   "source": [
    "J('[PE1.terms 466 [x] times pop] run sum')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can use `PE1.1` to accumulate the terms as we go, and then `pop` the generator and the counter from the stack when we're done, leaving just the sum."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "233168\n"
     ]
    }
   ],
   "source": [
    "J('0 0 PE1.terms 466 [x [PE1.1] dip] times popop')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# A little further analysis renders iteration unnecessary.\n",
    "Consider finding the sum of the positive integers less than or equal to ten."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "55\n"
     ]
    }
   ],
   "source": [
    "J('[10 9 8 7 6 5 4 3 2 1] sum')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Instead of summing them, [observe](https://en.wikipedia.org/wiki/File:Animated_proof_for_the_formula_giving_the_sum_of_the_first_integers_1%2B2%2B...%2Bn.gif):\n",
    "\n",
    "      10  9  8  7  6\n",
    "    +  1  2  3  4  5\n",
    "    ---- -- -- -- --\n",
    "      11 11 11 11 11\n",
    "      \n",
    "      11 * 5 = 55\n",
    "\n",
    "From the above example we can deduce that the sum of the first N positive integers is:\n",
    "\n",
    "    (N + 1) * N / 2 \n",
    "\n",
    "(The formula also works for odd values of N, I'll leave that to you if you want to work it out or you can take my word for it.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "define('F dup ++ * 2 floordiv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "      • 10 F\n",
      "   10 • F\n",
      "   10 • dup ++ * 2 floordiv\n",
      "10 10 • ++ * 2 floordiv\n",
      "10 11 • * 2 floordiv\n",
      "  110 • 2 floordiv\n",
      "110 2 • floordiv\n",
      "   55 • \n"
     ]
    }
   ],
   "source": [
    "V('10 F')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generalizing to Blocks of Terms\n",
    "We can apply the same reasoning to the PE1 problem.\n",
    "\n",
    "Between 0 and 990 inclusive there are sixty-six \"blocks\" of seven terms each, starting with:\n",
    "\n",
    "    [3 5 6 9 10 12 15]\n",
    "    \n",
    "And ending with:\n",
    "\n",
    "    [978 980 981 984 985 987 990]\n",
    "    \n",
    "If we reverse one of these two blocks and sum pairs..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[978 15] [980 12] [981 10] [984 9] [985 6] [987 5] [990 3]]\n"
     ]
    }
   ],
   "source": [
    "J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[993 992 991 993 991 992 993]\n"
     ]
    }
   ],
   "source": [
    "J('[3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(Interesting that the sequence of seven numbers appears again in the rightmost digit of each term.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6945\n"
     ]
    }
   ],
   "source": [
    "J('[ 3 5 6 9 10 12 15] reverse [978 980 981 984 985 987 990] zip [sum] map sum')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since there are sixty-six blocks and we are pairing them up, there must be thirty-three pairs, each of which sums to 6945.  We also have these additional unpaired terms between 990 and 1000:\n",
    "\n",
    "    993 995 996 999\n",
    "    \n",
    "So we can give the \"sum of all the multiples of 3 or 5 below 1000\" like so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "233168\n"
     ]
    }
   ],
   "source": [
    "J('6945 33 * [993 995 996 999] cons sum')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's worth noting, I think, that this same reasoning holds for any two numbers $n$ and $m$ the multiples of which we hope to sum.  The multiples would have a cycle of differences of length $k$ and so we could compute the sum of $Nk$ multiples as above.\n",
    "\n",
    "The sequence of differences will always be a palidrome.  Consider an interval spanning the least common multiple of $n$ and $m$:\n",
    "\n",
    "    |   |   |   |   |   |   |   |\n",
    "    |      |      |      |      |\n",
    "    \n",
    "Here we have 4 and 7, and you can read off the sequence of differences directly from the diagram: 4 3 1 4 2 2 4 1 3 4.\n",
    "    \n",
    "Geometrically, the actual values of $n$ and $m$ and their *lcm* don't matter, the pattern they make will always be symmetrical around its midpoint.  The same reasoning holds for multiples of more than two numbers."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# The Simplest Program"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Of course, the simplest joy program for the first Project Euler problem is just:\n",
    "\n",
    "    PE1 == 233168\n",
    "\n",
    "Fin."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.7.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
