Search This Blog

Wednesday, December 22, 2021

Christmas Theorem: Principled Windmills

#!/usr/bin/env clojure

;; Fermats' Christmas Theorem: Principled Windmills

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Here's a bunch of code to make svg files of arrangements of coloured squares.
;; I'm using this to draw the windmills.
;; It's safe to ignore this if you're not interested in how to create such svg files.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(require 'clojure.xml)

(def squaresize 10)

(defn make-svg-rect [i j colour]
  {:tag :rect
   :attrs {:x (str (* i squaresize)) :y (str (* j squaresize)) :width  (str squaresize) :height (str squaresize)
           :style (str "fill:", colour, ";stroke:black;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1")}})

(defn adjust-list [rectlist]
  (if (empty? rectlist) rectlist
      (let [hmin (apply min (map first  rectlist))
            vmax (apply max (map second rectlist))]
        (for [[a b c] rectlist] [(- a hmin) (- vmax b) c]))))

(defn make-svg [objects]
  {:tag :svg :attrs { :version "1.1"  :xmlns "http://www.w3.org/2000/svg"}
   :content (for [[i j c] (adjust-list objects)] (make-svg-rect i j c))})

(defn svg-file-from-rectlist [filename objects]
  (spit (str filename ".svg") (with-out-str (clojure.xml/emit (make-svg objects)))))

(defn hjoin
  ([sql1 sql2] (hjoin sql1 sql2 1))
  ([sql1 sql2 sep]
   (cond (empty? sql1) sql2
         (empty? sql2) sql1
         :else (let [xmax1 (apply max (map first sql1))
                     xmin2 (apply min (map first sql2))
                     shift  (+ 1 sep (- xmax1 xmin2))]
                 (concat sql1 (for [[h v c] sql2] [(+ shift h) v c]))))))

(defn hcombine [& sqllist] (reduce hjoin '() sqllist))

(defn svg-file [filename & objects]
  (svg-file-from-rectlist filename (apply hcombine objects)))


(defn orange [n] (if (< n 0) (range 0 n -1) (range 0 n 1)))

(defn make-composite-rectangle [h v hsquares vsquares colour]
  (for [i (orange hsquares) j (orange vsquares)] [(+ i h) (+ j v) colour]))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; end of drawing code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Let's do another example in a more principled way

;; We'll think in terms of triples [s, p, n]

;; Where s is the size of the red square, p (parallel) is the width of the arms , and n (normal) is
;; the length of the arms.

;; Here's a function to draw the windmill that represents such a triple

(defn make-windmill [[s p n]]
            (let [ s2 (quot s 2)
                  is2 (inc s2)
                  ds2 (- is2)]
              (concat (make-composite-rectangle  (- s2)  (- s2) s      s     "red")
                      (make-composite-rectangle  (- s2)  is2    p      n     "white")
                      (make-composite-rectangle  s2      ds2    (- p)  (- n) "white")
                      (make-composite-rectangle  ds2     (- s2) (- n)  p     "green")
                      (make-composite-rectangle  is2     s2     n      (- p) "green"))))

(make-windmill [1 1 1]) ; ([0 0 "red"] [0 1 "white"] [0 -1 "white"] [-1 0 "green"] [1 0 "green"])

(svg-file "windmill" (make-windmill [1 1 1]))

;; As we do our windmill transformations s*s + 4 * p * n should always stay the same
(defn total [[s p n]]
  (+ (* s s) (* 4 p n)))

(total [1 1 1]) ; 5



;; So with this new way of representing things:

;; Consider 37 = 4 * 9 + 1

;; Our first triple will be

[1 1 9]

(total [1 1 9]) ; 37

;; And its windmill looks like:
(svg-file "windmill-37-1" (make-windmill [1 1 9]))
 

 
 
 ;; We can't change the size of the red square here, so the other thing we can do is to rotate the arms

;; In terms of triples, [1 1 9] -> [1 9 1]

(total [1 9 1]) ; 37

(svg-file "windmill-37-2" (make-windmill [1 9 1]))


;; Now we can change the size of the red square, it can increase to three, and that means that we have to shorten the arms by two ;; [1 9 1] -> [3 1 7] (total [3 1 7]) ; 37 (svg-file "windmill-37-3" (make-windmill [3 1 7]))
;; Note that this also changes the colour of the arms, but that doesn't matter, the only reason the ;; arms are two different colours is to make it easier to see what's going on. If it bothers you ;; just go and change white to green in the windmill code! ;; From [3 1 7] the only change we can make to the size of the red square is to put it back to one ;; So instead, we'll swap the arms again ;; [3 1 7] -> [3 7 1] (total [3 7 1]) ; 37 (svg-file "windmill-37-4" (make-windmill [3 7 1]))
;; Now, swapping the arms just moves us back a step, but we can increase the size of the red square to five ;; and shorten the arms by two ;; [3 7 1] -> [5 1 3] (total [5 1 3]) ; 37 (svg-file "windmill-37-5" (make-windmill [5 1 3]))
;; Again, the only way to change the size of the red square is to put it back, so let's rotate arms ;; I'm going to call changing the size of the red square the red transformation ;; and rotating the arms the green transformation ;; The green transformation is easy to express in terms of triples (defn green [[s p n]] [s n p]) (green [5 1 3]) ; [5 3 1] (total [5 3 1]) ; 37 (svg-file "windmill-37-6" (make-windmill [5 3 1]))
;; Now, the green transformation just puts us back a step, and it looks like we can't increase the size ;; of the red square, so are we stuck? ;; No! If you stare at the diagram for long enough, you'll see that we can *reduce* the size of the ;; red square instead of increasing it, growing the arms inward until the red shape is square again. ;; And in fact that's our only possible move. ;; [5 3 1] -> [1 3 3] (svg-file "windmill-37-7" (make-windmill [1 3 3]))
;; It's kind of annoying that this flips the shape! But it's obviously still the same total number ;; of squares, so just like with the colour flip I'm going to ignore that for now rather than ;; introduce unnecessary complexity to the drawing code ;; I'm going to call both reducing and increasing the size of the red square a "red transformation", ;; and the red transformation is going to need a parameter to say how much to change the size of the ;; square ;; Let's say, as above, that we want to shift the boundaries of the big red square in by two small unit squares ;; so say delta = -2 ;; that means that the new red square is size one, five less two squares on either edge ;; that means that the red square has changed from size twenty-five to size one ;; that leaves twenty-four spare squares, to be distributed between the four arms ;; which is six spare squares per arm ;; since we're just moving the boundary of the square, or alternatively extending the arms into the ;; square, that doesn't change p, the width of the arm parallel to the square ;; so we add the six squares in rows of p. ;; in our example above, p is three, so those six squares result in the arms lengthening by two ;; In code (defn red [[s p n] delta] (let [news (+ s (* 2 delta)) spare (- ( * s s ) (* news news)) sparesperarm (/ spare 4) lengthchange (/ sparesperarm p)] [news (+ n lengthchange) p])) (total [5 3 1]) ; 37 (red [5 3 1] -2) ; [1 3 3] (total (red [5 3 1] -2)) ; 37 (svg-file "windmill" (make-windmill [5 3 1 ])) ; nil (svg-file "windmill" (make-windmill (red [5 3 1] -2))) ; nil (svg-file "windmill-37-7" (make-windmill [1 3 3])) 
 
;; Now we have a real problem. The only red transformation we can make is to go back a step, but
;; the green transformation does nothing useful here:

(green [1 3 3]) ; [1 3 3]


;; But every problem is an opportunity, as they say:

;; We can split up the rectangle
(svg-file "windmill-37-split"
          (make-composite-rectangle 0 0 1 1 "red")
          (make-composite-rectangle 0 0 3 3 "green")
          (make-composite-rectangle 0 0 3 3 "green")
          (make-composite-rectangle 0 0 3 3 "white")
          (make-composite-rectangle 0 0 3 3 "white"))
          
          
(svg-file "windmill-37-recombine" (make-composite-rectangle 0 0 1 1 "red") (concat (make-composite-rectangle 0 0 3 3 "green") (make-composite-rectangle 3 3 3 3 "green") (make-composite-rectangle 0 3 3 3 "white") (make-composite-rectangle 3 0 3 3 "white")))
;; The green transformation fails if and only if the arms are squares, and if the arms are squares, we can ;; combine them to form one big even square (svg-file "windmill-37-final" (make-composite-rectangle 0 0 1 1 "red") (concat (make-composite-rectangle 0 0 6 6 "green")))
;; So 37 = 1*1 + 6*6 ;; Which is what we're trying to show, thirty-seven is the sum of one odd and one even square

No comments:

Post a Comment

Followers