#|--------------------------------------|#
               #|                                      |#
               #|          KSAJ'S LISP THINGS          |#
               #|          ------------------          |#
               #|--------------------------------------|#


; If you use any of the code I put here, I would prefer credit is given. Just my initials KSAJ is fine. I share them because Lisp is cool, and maybe these snippets will be useful to someone. I won't be posting code that is readily available elsewhere, so everything here has some type of nice quirk to them.

; For now I haven't been putting dates on these, so if you want to see if I have updated, you'll have to scroll to the end to see if it is familiar. I suppose if I end up writing enough of these, I'll organize them, and maybe even write more thorough descriptions of how the snippets work.

; I think all of these are unique in one way or other, since I have no interest in posting the type of thing that already shows up in everyone's private libraries. Except maybe for (factorial), but even there I added a feature I rarely see in other people's libraries.

; Enjoy! If you want to email me, non spam of course since spam won't usually get through the filter anyway, you can reach me at KarstenJohansson@gmail.com


; FACTORIALS and MULTIFACTORIALS
; ------------------------------

; Everybody's got their version of how to calculate factorials in Lisp. Here's mine.


     (defun factorial (a &optional (fact 1))
      "Calculate factorial. Optional second arg for multifactorial."

       (cond
         ((< a 0) (format t      "Factorial of < 0 is not defined"))

          (t      (reduce #'*
                    (loop for     i
                          from    a
                          downto  1
                          by      fact
                          collect i)))))


; For regular factorials like 6!, you just call (factorial 6), which is the same as (factorial 6 1). This version also does multifactorials. For example, 6!!!! can be calculated with (factorial 6 4).


; SUPER FAST NTH FIBONACCI WITHOUT LOOPS
; --------------------------------------

; If you are looking for a super-fast way to calculate the nth Fibonacci number, here is a method that doesn't use any form of looping or recursion whatsoever. It took a while to code it because I got the idea by translating Mathematical Notation, and then introduced a few shortcuts. Its workings are definitely non-obvious.

; It looks way simpler than it actually is. Your challenge is to figure out how and why this even works at all.


     (defun fib-nth (n)
      "KSAJ's speedy way to calculate nth Fibonacci number."

      (if (> n 1)
        (progn
          (decf n)

          (logand (truncate (ash 4 (* n (+ 3 n)))

                            (-   (ash 4 (* 2 n))
                                 (ash 2 n)
                                 1))

                  (- (ash 2 n) 1)))

        n))


; ESTIMATING THE PERIMETER OF AN ELLIPSE
; --------------------------------------

; Ever try to calculate the perimeter of an ellipse? So far, nobody knows how to do it. Ramanujan took several stabs at it, so he's gotten towards a pretty good estimate. However, his estimates begin to fall apart in various ways as the sizes change.

; Matt Parker gave it a stab, and came up with a slightly more accurate estimation, in that it bounces around within a close-enough range where Ramanujan's have veered off into lala land.

; Here is Matt's version, that I've coded in Common Lisp. You just need to add the x and y measurements.


     (defun ellipse-perimeter-mp1 (a b)
      "Input X and Y measurements of an ellipse. Output estimated perimeter."

       (if (< a b)
         (rotatef a b))

       (* pi (- (+ (/ (*  53 a)  3)
                   (/ (* 717 b) 35))

                (sqrt (+ (* 269 a a)
                         (* 667 a b)
                         (* 371 b b))))))


; Matt also made a "lazy version" that comes out less accurately, but still without veering off. I love how terse it is.


     (defun ellipse-perimeter-mp2 (a b)
      "Input X and Y measurements of an ellipse. Output estimated perimeter."

       (if (< a b)
         (rotatef a b))

       (* pi (+ (/ (* 6 a) 5)
                (/ (* 3 b) 4))))


; GENERATING ODD NUMBERS USING CONSECUTIVE SQUARES
; ------------------------------------------------

; To some, this might blow your mind. It certainly blew mine. Even though is is almost elemental, it seems so improbable.

; If you take two consecutive numbers and square them, then subtract the difference between the squares, you will get an odd number that increases as consecutive odd numbers.

; When I saw this, I had to code it to prove it to myself. For simplicity I only do the first 10 differences, which can be modified in the (dotimes) statement. Of course that could be turned into an input, or a variable.

; I coded (squared) as a separate function to make the code easier to read.

; Essentially the difference between the square of 1 and the square of 2 is 3. It goes like this indefinitely, even though the numbers get huge quickly. This is why I limited the demonstration code to 10. Try it! Go ahead and increase the number of iterations. Pretty wild!


     (defun squared (n)
       (* n n))


     (defun consecutive-squares ()
       "Calculate the distance between consecutive squared numbers"

       (dotimes (n 10 (values))  ; Change 10 to however many you want to see

       (format t "~d to ~d = ~d~%"
                 (squared n)
                 (squared (+ n 1))
                 (- (squared (+ n 1))
                    (squared n)))))


; THAT'S ALL FOR NOW
; ------------------

; I hope to share some more of my Lispy ideas here. This probably won't ever be a blog, but for now it's just a place to showcase things that interest me in Common Lisp.