about summary refs log tree commit diff
path: root/configs/shared/.emacs.d/wpc/number.el
blob: 81d3c5d2b935606f67ab35331d058acf9407bbc5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
;;; number.el --- Functions for working with numbers -*- lexical-binding: t -*-
;; Author: William Carroll <wpcarro@gmail.com>

;;; Commentary:
;;
;; Classifications of numbers:
;; - Natural: (a.k.a positive integers, counting numbers); {1, 2, 3, ... }
;;
;; - Whole: Natural Numbers, plus zero; {0, 1, 2, 3, ...}
;;
;; - Integers: Whole numbers plus all the negatives of the natural numbers;
;;   {... , -2, -1, 0, 1, 2, ...}
;;
;; - Rational numbers: (a.k.a. fractions) where the top and bottom numbers are
;;   integers; e.g., 1/2, 3/4, 7/2, ⁻4/3, 4/1.  Note: The denominator cannot be
;;   0, but the numerator can be.
;;
;; - Real numbers: All numbers that can be written as a decimal.  This includes
;;   fractions written in decimal form e.g., 0.5, 0.75 2.35, ⁻0.073, 0.3333, or
;;   2.142857. It also includes all the irrational numbers such as π, √2 etc.
;;   Every real number corresponds to a point on the number line.
;;
;; The functions defined herein attempt to capture the mathematical definitions
;; of numbers and their classifications as defined above.

;;; Code:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Dependencies
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(require 'prelude)
(require 'dash)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Library
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defconst number/test? t
  "When t, run the test suite defined herein.")

;; TODO: What about int.el?

;; TODO: How do we handle a number typeclass?

(defun number/positive? (x)
  "Return t if `X' is a positive number."
  (> x 0))

(defun number/negative? (x)
  "Return t if `X' is a positive number."
  (< x 0))

;; TODO: Don't rely on this. Need to have 10.0 and 10 behave similarly.
(defun number/float? (x)
  "Return t if `X' is a floating point number."
  (floatp x))

(defun number/natural? (x)
  "Return t if `X' is a natural number."
  (and (number/positive? x)
       (not (number/float? x))))

(defun number/whole? (x)
  "Return t if `X' is a whole number."
  (or (= 0 x)
      (number/natural? x)))

(defun number/integer? (x)
  "Return t if `X' is an integer."
  (or (number/whole? x)
      (number/natural? (- x))))

;; TODO: How defensive should these guards be?  Should we assert that the inputs
;; are integers before checking evenness or oddness?

;; TODO: Look up Runar (from Unison) definition of handling zero as even or odd.

;; TODO: How should rational numbers be handled? Lisp is supposedly famous for
;; its handling of rational numbers.
;; TODO: `calc-mode' supports rational numbers as "1:2" meaning "1/2"
;; (defun number/rational? (x))

;; TODO: Can or should I support real numbers?
;; (defun number/real? (x))

(defun number/even? (x)
  "Return t if `X' is an even number."
  (or (= 0 x)
      (= 0 (mod x 2))))

(defun number/odd? (x)
  "Return t if `X' is an odd number."
  (not (number/even? x)))

(defun number/dec (x)
  "Subtract one from `X'.
While this function is undeniably trivial, I have unintentionally done (- 1 x)
  when in fact I meant to do (- x 1) that I figure it's better for this function
  to exist, and for me to train myself to reach for it and its inc counterpart."
  (- x 1))

(defun number/inc (x)
  "Add one to `X'."
  (+ x 1))

;; TODO: Does this belong in a math module?  Is math too vague?  Or is number
;; too vague?
(defun number/factorial (x)
  "Return factorial of `X'."
  (cond
   ((number/negative? x) (error "Will not take factorial of negative numbers"))
   ((= 0 x) 1)
   ;; NOTE: Using `series/range' introduces a circular dependency because:
   ;; series -> number -> series.  Conceptually, however, this should be
   ;; perfectly acceptable.
   (t (->> (series/range 1 x)
           (list/reduce 1 #'*)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Tests
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(when number/test?
  (prelude/assert
   (number/positive? 10))
  (prelude/assert
   (number/natural? 10))
  (prelude/assert
   (number/whole? 10))
  (prelude/assert
   (number/whole? 0))
  (prelude/assert
   (number/integer? 10))
  (prelude/assert
   (= 120 (number/factorial 5)))
  (prelude/assert
   (number/even? 6))
  (prelude/refute
   (number/odd? 6))
  (prelude/refute
   (number/positive? -10))
  (prelude/refute
   (number/natural? 10.0))
  (prelude/refute
   (number/natural? -10))
  (prelude/refute
   (number/natural? -10.0)))

(provide 'number)
;;; number.el ends here