Class: Lisp::Math

Inherits:
Object show all
Defined in:
lib/rubylisp/math.rb

Class Method Summary collapse

Class Method Details

.abs_impl(args, env) ⇒ Object



329
330
331
332
333
334
# File 'lib/rubylisp/math.rb', line 329

def self.abs_impl(args, env)
  return Lisp::Debug.process_error("abs needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("abs needs a numeric argument, but received #{arg}", env) unless arg.number?
  Number.with_value(arg.value.abs)
end

.add_impl(args, env) ⇒ Object



122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/rubylisp/math.rb', line 122

def self.add_impl(args, env)
  return Lisp::Debug.process_error("add needs at least 1 argument", env) if args.empty?

  acc = 0
  c = args
  while !c.nil?
    n = c.car.evaluate(env)
    return Lisp::Debug.process_error("add needs number arguments but was given a #{n.type}: #{n}", env) unless n.type == :number
    acc += n.value
    c = c.cdr
  end

  Number.with_value(acc)
end

.bind(name, number) ⇒ Object



118
119
120
# File 'lib/rubylisp/math.rb', line 118

def self.bind(name, number)
  EnvironmentFrame.global.bind(Symbol.named(name), Number.with_value(number))
end

.ceiling_impl(args, env) ⇒ Object



239
240
241
242
243
244
# File 'lib/rubylisp/math.rb', line 239

def self.ceiling_impl(args, env)
  return Lisp::Debug.process_error("ceiling needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("ceiling needs a number argument, but received #{arg}", env) unless arg.type == :number
  Number.with_value(arg.value.ceil)
end

.cos_impl(args, env) ⇒ Object



388
389
390
391
392
393
# File 'lib/rubylisp/math.rb', line 388

def self.cos_impl(args, env)
  return Lisp::Debug.process_error("cos needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("cos needs a numeric argument, but received #{arg}", env) unless arg.number?
  Number.with_value(::Math.cos(arg.value).round(5))
end

.even_impl(args, env) ⇒ Object



255
256
257
258
259
260
# File 'lib/rubylisp/math.rb', line 255

def self.even_impl(args, env)
  return Lisp::Debug.process_error("even? needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("even? needs a number argument, but received #{arg}", env) unless arg.type == :number
  Boolean.with_value(arg.value.even?)
end

.float_impl(args, env) ⇒ Object



313
314
315
316
317
318
# File 'lib/rubylisp/math.rb', line 313

def self.float_impl(args, env)
  return Lisp::Debug.process_error("float needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("float needs a numeric or string argument, but received #{arg}", env) unless arg.number? || arg.string?
  Number.with_value(arg.value.to_f)
end

.floor_impl(args, env) ⇒ Object



247
248
249
250
251
252
# File 'lib/rubylisp/math.rb', line 247

def self.floor_impl(args, env)
  return Lisp::Debug.process_error("floor needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("floor needs a number argument, but received #{arg}", env) unless arg.type == :number
  Number.with_value(arg.value.floor)
end

.integer_impl(args, env) ⇒ Object



321
322
323
324
325
326
# File 'lib/rubylisp/math.rb', line 321

def self.integer_impl(args, env)
  return Lisp::Debug.process_error("integer needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("integer needs a numeric or string argument, but received #{arg}", env) unless arg.number? || arg.string?
  Number.with_value(arg.value.to_i)
end

.interval_impl(args, env) ⇒ Object



295
296
297
298
299
300
301
302
303
# File 'lib/rubylisp/math.rb', line 295

def self.interval_impl(args, env)
  return Lisp::Debug.process_error("interval needs 2 arguments, but received #{args.length}", env) if args.length != 2
  initial = args.car.evaluate(env)
  return Lisp::Debug.process_error("interval needs number arguments, but received #{initial}", env) unless initial.type == :number
  final = args.cadr.evaluate(env)
  return Lisp::Debug.process_error("interval needs number arguments, but received #{final}", env) unless final.type == :number
  return Lisp::Debug.process_error("interval's arguments need to be in natural order", env) unless initial.value <= final.value
  Lisp::ConsCell.array_to_list((initial.value..final.value).to_a.map {|n| Number.with_value(n)})
end

.max_impl(args, env) ⇒ Object



363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
# File 'lib/rubylisp/math.rb', line 363

def self.max_impl(args, env)
  return Lisp::Debug.process_error("max needs at least 1 argumenta", env) if args.length == 0
  initial = args.car.evaluate(env)
  return Lisp::Debug.process_error("max requires numeric arguments, but received #{initial}", env) unless initial.type ==:number
  acc = initial.value
  c = args.cdr
  while !c.nil?
    n = c.car.evaluate(env)
    return Lisp::Debug.process_error("max needs number arguments, but received #{n}", env) unless n.type == :number
    acc = n.value if n.value > acc
    c = c.cdr
  end

  Number.with_value(acc)
end

.min_impl(args, env) ⇒ Object



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
# File 'lib/rubylisp/math.rb', line 345

def self.min_impl(args, env)
  return Lisp::Debug.process_error("min needs at least 1 argument", env) if args.length == 0

  initial = args.car.evaluate(env)
  return Lisp::Debug.process_error("min requires numeric arguments, but received #{initial}", env) unless initial.type ==:number
  acc = initial.value
  c = args.cdr
  while !c.nil?
    n = c.car.evaluate(env)
    return Lisp::Debug.process_error("min needs number arguments, but received #{n}", env) unless n.type == :number
    acc = n.value if n.value < acc
    c = c.cdr
  end

  Number.with_value(acc)
end

.multiply_impl(args, env) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/rubylisp/math.rb', line 158

def self.multiply_impl(args, env)
  return Lisp::Debug.process_error("multiply needs at least 1 argument", env) if args.empty?

  acc = 1
  c = args
  while !c.nil?
    n = c.car.evaluate(env)
    return Lisp::Debug.process_error("multiply needs number arguments, but received #{n}", env) unless n.type == :number
    acc *= n.value
    c = c.cdr
  end

  Number.with_value(acc)
end

.negative_impl(args, env) ⇒ Object



287
288
289
290
291
292
# File 'lib/rubylisp/math.rb', line 287

def self.negative_impl(args, env)
  return Lisp::Debug.process_error("negative? needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("negative? needs a number argument, but received #{arg}", env) unless arg.type == :number
  Boolean.with_value(arg.value < 0)
end

.odd_impl(args, env) ⇒ Object



263
264
265
266
267
268
# File 'lib/rubylisp/math.rb', line 263

def self.odd_impl(args, env)
  return Lisp::Debug.process_error("odd? needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("odd? needs a number argument, but received #{arg}", env) unless arg.type == :number
  Boolean.with_value(arg.value.odd?)
end

.positive_impl(args, env) ⇒ Object



279
280
281
282
283
284
# File 'lib/rubylisp/math.rb', line 279

def self.positive_impl(args, env)
  return Lisp::Debug.process_error("positive? needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("positive? needs a number argument, but received #{arg}", env) unless arg.type == :number
  Boolean.with_value(arg.value > 0)
end

.quotient_impl(args, env) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/rubylisp/math.rb', line 174

def self.quotient_impl(args, env)
  return Lisp::Debug.process_error("quotient needs at least 1 argument", env) if args.empty?

  first = args.car.evaluate(env)
  return Lisp::Debug.process_error("quotient needs number arguments, but received #{first}", env) unless first.type == :number
  return first if args.length == 1
  acc = first.value
  c = args.cdr
  while !c.nil?
    n = c.car.evaluate(env)
    return Lisp::Debug.process_error("quotient needs number arguments, but received #{n}", env) unless n.type == :number
    return Lisp::Debug.process_error("divide by zero", env) if n.value == 0
    acc /= n.value
    c = c.cdr
  end

  Number.with_value(acc)
end

.random_impl(args, env) ⇒ Object



306
307
308
309
310
# File 'lib/rubylisp/math.rb', line 306

def self.random_impl(args, env)
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("random needs a number argument, but received #{arg}", env) unless arg.nil? || arg.type == :number
  Number.with_value(arg.nil? ? rand() : rand(arg.value))
end

.registerObject



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
# File 'lib/rubylisp/math.rb', line 5

def self.register
  self.bind("PI", ::Math::PI)
  self.bind("E", ::Math::E)
  
  Primitive.register("+", "(+ number...)\n\nAdds a series of numbers.") do |args, env|
    Lisp::Math.add_impl(args, env)
  end
  
  Primitive.register("-", "(- number...)\n\nSequentially subtracts a sequence of numbers.\nAs expected, a unary form of - is available as well.") do |args, env|
    Lisp::Math.subtract_impl(args, env)
  end

  Primitive.register("*", "(* number...)\n\nMultiplies a series of numbers.") do |args, env|
    Lisp::Math.multiply_impl(args, env)
  end

  Primitive.register("/", "(/ number...)\n\nSequentially divides a sequence of numbers.") do |args, env|
    Lisp::Math.quotient_impl(args, env)
  end

  Primitive.register("%", "(% number number)\n\nReturns the remainder of the division of two numbers. NOTE: modulus only works for integers.") do |args, env|
    Lisp::Math.remainder_impl(args, env)
  end

  Primitive.register("modulo", "(modulo number number)\n\nReturns the remainder of the division of two numbers. NOTE: modulus only works for integers.") do |args, env|
    Lisp::Math.remainder_impl(args, env)
  end

  Primitive.register("even?", "(even? number)\n\nReturns whether the argument is even.") do |args, env|
    Lisp::Math.even_impl(args, env)
  end

  Primitive.register("odd?", "(odd? number)\n\nReturns whether the argument is odd.") do |args, env|
    Lisp::Math.odd_impl(args, env)
  end

  Primitive.register("zero?", "(zero? _number_)\n\nReturns whether the argument is zero.") do |args, env|
    Lisp::Math.zero_impl(args, env)
  end

  Primitive.register("positive?", "(positive? _number_)\n\nReturns whether the argument is positive.") do |args, env|
    Lisp::Math.positive_impl(args, env)
  end

  Primitive.register("negative?", "(negative? _number_)\n\nReturns whether the argument is negative.") do |args, env|
    Lisp::Math.negative_impl(args, env)
  end

  Primitive.register("interval", "(interval _lo_ _hi_)\n\nCreates a list of numbers from `lo` to `hi`, inclusive.") do |args, env|
    Lisp::Math.interval_impl(args, env)
  end

  Primitive.register("truncate", "(truncate number)\n\nReturns the integer value of number. If it is an integer, it is simply returned. However, if it is a float the integer part is returned.") do |args, env|
    Lisp::Math.truncate_impl(args, env)
  end

  Primitive.register("round", "(round number)\n\nIf number is an integer, it is simply returned. However, if it is a float the closest integer is returned.") do |args, env|
    Lisp::Math.round_impl(args, env)
  end

  Primitive.register("ceiling", "(ceiling _number_)\n\nIf `number` is an integer, it is simply returned. However, if it is a float the smallest integer greater than or equal to `number` is returned.") do |args, env|
    Lisp::Math.ceiling_impl(args, env)
  end

  Primitive.register("floor", "(floor _number_)\n\nIf `number` is an integer, it is simply returned. However, if it is a float the
largest integer less than or equal to `number` is returned.") do |args, env|
    Lisp::Math.floor_impl(args, env)
  end

  Primitive.register("random", "(random)\n\nReturns a pseudo-random floating point number between 0.0 and 1.0, including 0.0 and excluding 1.0.\n\n(random n)\n\nReturns a pseudo-random integer greater than or equal to 0 and less than n.") do |args, env|
    Lisp::Math.random_impl(args, env)
  end

  Primitive.register("float", "(float number)\n\nReturns the floating point value of number. If it is a float, it is simply returned. However, if it is an integer it is converted to float and returned.") do |args, env|
    Lisp::Math.float_impl(args, env)
  end

  Primitive.register("integer", "(integer number)\n\nReturns the integer value of number. If it is an integer, it is simply returned. However, if it is a float the integer part is returned. This is the same as tuncate.") do |args, env|
    Lisp::Math.integer_impl(args, env)
  end

  Primitive.register("sqrt", "(sqrt _number_)\n\nReturns the square root of `number'.") do |args, env|
    Lisp::Math.sqrt_impl(args, env)
  end

  Primitive.register("min", "(min _number_...)\n\nReturns the smallest of all the `number` arguments.") do |args, env|
    Lisp::Math.min_impl(args, env)
  end

  Primitive.register("max", "(max _number_...)\n\nReturns the largest of all the `number` arguments.") do |args, env|
    Lisp::Math.max_impl(args, env)
  end

  Primitive.register("abs", "(abs _number_)\n\nReturns the absolute value of `number'.") do |args, env|
    Lisp::Math.abs_impl(args, env)
  end

  Primitive.register("sin", "(sin _number_)\n\nReturns the sine of `number'.") do |args, env|
    Lisp::Math.sin_impl(args, env)
  end

  Primitive.register("cos", " (cos _number_)\n\nReturns the cosine of `number'.") do |args, env|
    Lisp::Math.cos_impl(args, env)
  end

  Primitive.register("tan", "(tan _number_)\n\nReturns the tangent of `number'.") do |args, env|
    Lisp::Math.tan_impl(args, env)
  end

  
end

.remainder_impl(args, env) ⇒ Object



194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/rubylisp/math.rb', line 194

def self.remainder_impl(args, env)
  return Lisp::Debug.process_error("remainder needs at least 1 argument", env) if args.empty?

  first = args.car.evaluate(env)
  return Lisp::Debug.process_error("remainder needs number arguments, but received #{first}", env) unless first.type == :number
  return first if args.length == 1
  acc = first.value
  c = args.cdr
  while !c.nil?
    n = c.car.evaluate(env)
    return Lisp::Debug.process_error("remainder needs number arguments, but received #{n}", env) unless n.type == :number
    return Lisp::Debug.process_error("divide by zero", env) if n.value == 0
    acc %= n.value
    c = c.cdr
  end

  Number.with_value(acc)
end

.round_impl(args, env) ⇒ Object



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/rubylisp/math.rb', line 221

def self.round_impl(args, env)
  return Lisp::Debug.process_error("round needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("round needs a number argument, but received #{arg}", env) unless arg.type == :number
  num = arg.value
  int = num.to_i
  Number.with_value(if (num - int).abs == 0.5
                      if int.even?
                        int
                      else
                        int + (int < 0 ? -1 : 1)
                      end
                    else
                      arg.value.round
                    end)
end

.sin_impl(args, env) ⇒ Object



380
381
382
383
384
385
# File 'lib/rubylisp/math.rb', line 380

def self.sin_impl(args, env)
  return Lisp::Debug.process_error("sin needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("sin needs a numeric argument, but received #{arg}", env) unless arg.number?
  Number.with_value(::Math.sin(arg.value).round(5))
end

.sqrt_impl(args, env) ⇒ Object



337
338
339
340
341
342
# File 'lib/rubylisp/math.rb', line 337

def self.sqrt_impl(args, env)
  return Lisp::Debug.process_error("sqrt needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("sqrt needs a numeric argument, but received #{arg}", env) unless arg.number?
  Number.with_value(::Math.sqrt(arg.value).round(5))
end

.subtract_impl(args, env) ⇒ Object



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/rubylisp/math.rb', line 138

def self.subtract_impl(args, env)
  return Lisp::Debug.process_error("subtract needs at least 1 argument", env) if args.empty?

  return Number.with_value(-1 * args.car.evaluate(env).value) if args.length == 1

  first = args.car.evaluate(env)
  return Lisp::Debug.process_error("subtract needs number arguments, but received #{first}", env)  unless first.type == :number
  acc = first.value
  c = args.cdr
  while !c.nil?
    n = c.car.evaluate(env)
    return Lisp::Debug.process_error("subtract needs number arguments, but received #{n}", env) unless n.type == :number
    acc -= n.value
    c = c.cdr
  end

  Number.with_value(acc)
end

.tan_impl(args, env) ⇒ Object



396
397
398
399
400
401
# File 'lib/rubylisp/math.rb', line 396

def self.tan_impl(args, env)
  return Lisp::Debug.process_error("tan needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("tan needs a numeric argument, but received #{arg}", env) unless arg.number?
  Number.with_value(::Math.tan(arg.value).round(5))
end

.truncate_impl(args, env) ⇒ Object



213
214
215
216
217
218
# File 'lib/rubylisp/math.rb', line 213

def self.truncate_impl(args, env)
  return Lisp::Debug.process_error("truncate needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("truncate needs a number argument, but received #{arg}", env) unless arg.type == :number
  Number.with_value(arg.value.truncate)
end

.zero_impl(args, env) ⇒ Object



271
272
273
274
275
276
# File 'lib/rubylisp/math.rb', line 271

def self.zero_impl(args, env)
  return Lisp::Debug.process_error("zero? needs 1 argument, but received #{args.length}", env) if args.length != 1
  arg = args.car.evaluate(env)
  return Lisp::Debug.process_error("zero? needs a number argument, but received #{arg}", env) unless arg.type == :number
  Boolean.with_value(arg.value.zero?)
end