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



327
328
329
330
331
332
# File 'lib/rubylisp/math.rb', line 327

def self.abs_impl(args, env)
  raise "abs needs 1 argument, but received #{args.length}" if args.length != 1
  arg = args.car.evaluate(env)
  raise "abs needs a numeric argument, but received #{arg}" 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)
  raise "add needs at least 1 argument" if args.empty?

  acc = 0
  c = args
  while !c.nil?
    n = c.car.evaluate(env)
    raise "add needs number arguments but was given a #{n.type}: #{n}" 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



237
238
239
240
241
242
# File 'lib/rubylisp/math.rb', line 237

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

.cos_impl(args, env) ⇒ Object



386
387
388
389
390
391
# File 'lib/rubylisp/math.rb', line 386

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

.even_impl(args, env) ⇒ Object



253
254
255
256
257
258
# File 'lib/rubylisp/math.rb', line 253

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

.float_impl(args, env) ⇒ Object



311
312
313
314
315
316
# File 'lib/rubylisp/math.rb', line 311

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

.floor_impl(args, env) ⇒ Object



245
246
247
248
249
250
# File 'lib/rubylisp/math.rb', line 245

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

.integer_impl(args, env) ⇒ Object



319
320
321
322
323
324
# File 'lib/rubylisp/math.rb', line 319

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

.interval_impl(args, env) ⇒ Object



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

def self.interval_impl(args, env)
  raise "interval needs 2 arguments, but received #{args.length}" if args.length != 2
  initial = args.car.evaluate(env)
  raise "interval needs number arguments, but received #{initial}" unless initial.type == :number
  final = args.cadr.evaluate(env)
  raise "interval needs number arguments, but received #{final}" unless final.type == :number
  raise "interval's arguments need to be in natural order" 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



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

def self.max_impl(args, env)
  raise "max needs at least 1 argumenta" if args.length == 0
  initial = args.car.evaluate(env)
  raise "max requires numeric arguments, but received #{initial}" unless initial.type ==:number
  acc = initial.value
  c = args.cdr
  while !c.nil?
    n = c.car.evaluate(env)
    raise "max needs number arguments, but received #{n}" 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



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

def self.min_impl(args, env)
  raise "min needs at least 1 argument" if args.length == 0

  initial = args.car.evaluate(env)
  raise "min requires numeric arguments, but received #{initial}" unless initial.type ==:number
  acc = initial.value
  c = args.cdr
  while !c.nil?
    n = c.car.evaluate(env)
    raise "min needs number arguments, but received #{n}" 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)
  raise "multiply needs at least 1 argument" if args.empty?

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

  Number.with_value(acc)
end

.negative_impl(args, env) ⇒ Object



285
286
287
288
289
290
# File 'lib/rubylisp/math.rb', line 285

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

.odd_impl(args, env) ⇒ Object



261
262
263
264
265
266
# File 'lib/rubylisp/math.rb', line 261

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

.positive_impl(args, env) ⇒ Object



277
278
279
280
281
282
# File 'lib/rubylisp/math.rb', line 277

def self.positive_impl(args, env)
  raise "positive? needs 1 argument, but received #{args.length}" if args.length != 1
  arg = args.car.evaluate(env)
  raise "positive? needs a number argument, but received #{arg}" 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
# File 'lib/rubylisp/math.rb', line 174

def self.quotient_impl(args, env)
  raise "quotient needs at least 1 argument" if args.empty?

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

  Number.with_value(acc)
end

.random_impl(args, env) ⇒ Object



304
305
306
307
308
# File 'lib/rubylisp/math.rb', line 304

def self.random_impl(args, env)
  arg = args.car.evaluate(env)
  raise "random needs a number argument, but received #{arg}" 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



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

def self.remainder_impl(args, env)
  raise "remainder needs at least 1 argument" if args.empty?

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

  Number.with_value(acc)
end

.round_impl(args, env) ⇒ Object



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

def self.round_impl(args, env)
  raise "round needs 1 argument, but received #{args.length}" if args.length != 1
  arg = args.car.evaluate(env)
  raise "round needs a number argument, but received #{arg}" 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



378
379
380
381
382
383
# File 'lib/rubylisp/math.rb', line 378

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

.sqrt_impl(args, env) ⇒ Object



335
336
337
338
339
340
# File 'lib/rubylisp/math.rb', line 335

def self.sqrt_impl(args, env)
  raise "sqrt needs 1 argument, but received #{args.length}" if args.length != 1
  arg = args.car.evaluate(env)
  raise "sqrt needs a numeric argument, but received #{arg}" 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)
  raise "subtract needs at least 1 argument" if args.empty?

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

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

  Number.with_value(acc)
end

.tan_impl(args, env) ⇒ Object



394
395
396
397
398
399
# File 'lib/rubylisp/math.rb', line 394

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

.truncate_impl(args, env) ⇒ Object



211
212
213
214
215
216
# File 'lib/rubylisp/math.rb', line 211

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

.zero_impl(args, env) ⇒ Object



269
270
271
272
273
274
# File 'lib/rubylisp/math.rb', line 269

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