#|--------------------------------------|#
               #|                                      |#
               #|          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. Then figure out why it breaks. And why it is so durn slow.


     (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.