excel_to_code 0.3.17 → 0.3.18.beta.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +67 -34
- data/bin/excel_to_c +8 -78
- data/bin/excel_to_go +41 -0
- data/bin/excel_to_ruby +2 -69
- data/src/commands.rb +2 -0
- data/src/commands/common_command_line_options.rb +81 -0
- data/src/commands/excel_to_c.rb +3 -0
- data/src/commands/excel_to_go.rb +91 -0
- data/src/commands/excel_to_x.rb +77 -11
- data/src/compile.rb +1 -0
- data/src/compile/c/a.out +0 -0
- data/src/compile/c/a.out.dSYM/Contents/Resources/DWARF/a.out +0 -0
- data/src/compile/c/compile_to_c.rb +2 -0
- data/src/compile/c/excel_to_c_runtime.c +691 -145
- data/src/compile/c/excel_to_c_runtime_test.c +226 -20
- data/src/compile/c/map_formulae_to_c.rb +62 -23
- data/src/compile/c/run_c_unit_tests +3 -0
- data/src/compile/cd.rb +6 -0
- data/src/compile/go.rb +3 -0
- data/src/compile/go/compile_to_go.rb +85 -0
- data/src/compile/go/compile_to_go_test.rb +73 -0
- data/src/compile/go/excel.go +171 -0
- data/src/compile/go/excel_test.go +54 -0
- data/src/compile/go/map_values_to_go.rb +67 -0
- data/src/compile/ruby/map_formulae_to_ruby.rb +30 -12
- data/src/excel/excel_functions.rb +26 -1
- data/src/excel/excel_functions/ceiling.rb +23 -0
- data/src/excel/excel_functions/countif.rb +15 -0
- data/src/excel/excel_functions/countifs.rb +10 -0
- data/src/excel/excel_functions/floor.rb +14 -0
- data/src/excel/excel_functions/hyperlink.rb +9 -0
- data/src/excel/excel_functions/na.rb +7 -0
- data/src/excel/excel_functions/not.rb +13 -0
- data/src/excel/excel_functions/or.rb +30 -0
- data/src/excel/excel_functions/product.rb +8 -0
- data/src/excel/excel_functions/rate.rb +16 -0
- data/src/excel/excel_functions/replace.rb +13 -0
- data/src/excel/excel_functions/scurve.rb +73 -0
- data/src/excel/excel_functions/sqrt.rb +11 -0
- data/src/excel/excel_functions/string_argument.rb +37 -0
- data/src/excel/excel_functions/sumifs.rb +19 -8
- data/src/excel/excel_functions/text.rb +3 -3
- data/src/excel/formula_peg.rb +1 -1
- data/src/excel/formula_peg.txt +2 -3
- data/src/excel/table.rb +15 -15
- data/src/excel_to_code.rb +1 -4
- data/src/extract/extract_data_from_worksheet.rb +8 -1
- data/src/rewrite/ast_expand_array_formulae.rb +4 -0
- data/src/rewrite/caching_formula_parser.rb +16 -11
- data/src/simplify.rb +1 -0
- data/src/simplify/inline_formulae.rb +16 -0
- data/src/simplify/replace_arithmetic_on_ranges.rb +14 -1
- data/src/simplify/replace_arrays_with_single_cells.rb +42 -15
- data/src/simplify/replace_cell_addresses_with_references.rb +70 -0
- data/src/simplify/replace_column_with_column_number.rb +8 -1
- data/src/simplify/replace_table_references.rb +40 -19
- data/src/simplify/simplify_arithmetic.rb +15 -10
- data/src/version.rb +4 -0
- metadata +115 -43
- data/TODO +0 -25
data/src/commands/excel_to_c.rb
CHANGED
@@ -10,6 +10,8 @@ class ExcelToC < ExcelToX
|
|
10
10
|
attr_accessor :create_makefile
|
11
11
|
# If true, writes tests in C rather than in ruby
|
12
12
|
attr_accessor :write_tests_in_c
|
13
|
+
# If true, allows unknown Excel functions rather than aborting
|
14
|
+
attr_accessor :allow_unknown_functions
|
13
15
|
|
14
16
|
def set_defaults
|
15
17
|
super
|
@@ -78,6 +80,7 @@ class ExcelToC < ExcelToX
|
|
78
80
|
variable_set_counter = 0
|
79
81
|
|
80
82
|
c = CompileToC.new
|
83
|
+
c.allow_unknown_functions = self.allow_unknown_functions
|
81
84
|
c.variable_set_counter = variable_set_counter
|
82
85
|
# Output the elements from each worksheet in turn
|
83
86
|
c.settable = settable
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require_relative 'excel_to_x'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
class ExcelToGo < ExcelToX
|
7
|
+
|
8
|
+
def language
|
9
|
+
'go'
|
10
|
+
end
|
11
|
+
|
12
|
+
# Skip this
|
13
|
+
def replace_values_with_constants
|
14
|
+
end
|
15
|
+
|
16
|
+
# These actually create the code version of the excel
|
17
|
+
def write_code
|
18
|
+
write_out_excel_as_code
|
19
|
+
write_out_test_as_code
|
20
|
+
end
|
21
|
+
|
22
|
+
def write_out_excel_as_code
|
23
|
+
log.info "Starting to write out code"
|
24
|
+
|
25
|
+
o = output("#{output_name.downcase}.go")
|
26
|
+
|
27
|
+
o.puts "// Compiled version of #{excel_file}"
|
28
|
+
o.puts "package #{output_name.downcase}"
|
29
|
+
o.puts
|
30
|
+
o.puts excel_lib_imports
|
31
|
+
o.puts
|
32
|
+
|
33
|
+
c = CompileToGo.new
|
34
|
+
c.settable = settable
|
35
|
+
c.gettable = gettable
|
36
|
+
c.rewrite @formulae, @worksheet_c_names, o
|
37
|
+
o.puts
|
38
|
+
|
39
|
+
o.puts excel_lib_functions
|
40
|
+
o.puts
|
41
|
+
|
42
|
+
close(o)
|
43
|
+
log.info "Finished writing code"
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
def excel_lib
|
48
|
+
@excel_lib ||= IO.readlines(File.join(File.dirname(__FILE__),'..','compile','go','excel.go')).join
|
49
|
+
end
|
50
|
+
|
51
|
+
def excel_lib_imports
|
52
|
+
excel_lib[/import \(.*?\)/m]
|
53
|
+
end
|
54
|
+
|
55
|
+
def excel_lib_functions
|
56
|
+
excel_lib[/import \(.*?\)(.*)/m,1]
|
57
|
+
end
|
58
|
+
|
59
|
+
def write_out_test_as_code
|
60
|
+
log.info "Starting to write out test"
|
61
|
+
|
62
|
+
o = output("#{output_name.downcase}_test.go")
|
63
|
+
|
64
|
+
o.puts "// Test of compiled version of #{excel_file}"
|
65
|
+
o.puts "package #{output_name.downcase}"
|
66
|
+
o.puts
|
67
|
+
o.puts "import ("
|
68
|
+
o.puts " \"testing\""
|
69
|
+
o.puts ")"
|
70
|
+
o.puts
|
71
|
+
|
72
|
+
c = CompileToGoTest.new
|
73
|
+
c.settable = settable
|
74
|
+
c.gettable = gettable
|
75
|
+
c.rewrite @formulae, @worksheet_c_names, o
|
76
|
+
o.puts
|
77
|
+
|
78
|
+
close(o)
|
79
|
+
log.info "Finished writing tests"
|
80
|
+
end
|
81
|
+
|
82
|
+
def compile_code
|
83
|
+
# Not needed
|
84
|
+
end
|
85
|
+
|
86
|
+
def run_tests
|
87
|
+
return unless actually_run_tests
|
88
|
+
log.info "Running the resulting tests"
|
89
|
+
log.info `cd #{File.join(output_directory)}; go test`
|
90
|
+
end
|
91
|
+
end
|
data/src/commands/excel_to_x.rb
CHANGED
@@ -6,6 +6,7 @@ class XMLFileNotFoundException < Exception; end
|
|
6
6
|
require 'fileutils'
|
7
7
|
require 'logger'
|
8
8
|
require 'tmpdir'
|
9
|
+
require 'shellwords'
|
9
10
|
require_relative '../excel_to_code'
|
10
11
|
|
11
12
|
# FIXME: Correct case for all worksheet references
|
@@ -121,6 +122,22 @@ class ExcelToX
|
|
121
122
|
# cells on tha sheet and nothing else.
|
122
123
|
attr_accessor :isolate
|
123
124
|
|
125
|
+
# Optional attribute, Boolean. Default false
|
126
|
+
# If set to true, will persevere through some errors where it can rather than aborting
|
127
|
+
# immediately. This can be helpful in getting to grips with conversion errors on a
|
128
|
+
# really messy sheet, since it allows you to see all the errors at once and which are
|
129
|
+
# really fatal.
|
130
|
+
attr_accessor :persevere
|
131
|
+
|
132
|
+
# This is a private method, default is a hash, keys are cell references.
|
133
|
+
# The code will dump debuging information about the given cells as they
|
134
|
+
# progress through the conversion
|
135
|
+
attr_accessor :dump_steps
|
136
|
+
|
137
|
+
# This is a method to use carefull, if true will not abort on external references, but instead
|
138
|
+
# treat them as local references.
|
139
|
+
attr_accessor :treat_external_references_as_local
|
140
|
+
|
124
141
|
# This is the main method. Once all the above attributes have been set, it should be called to actually do the work.
|
125
142
|
def go!
|
126
143
|
# This sorts out the settings
|
@@ -226,6 +243,9 @@ class ExcelToX
|
|
226
243
|
# Make sure the relevant directories exist
|
227
244
|
self.excel_file = File.expand_path(excel_file)
|
228
245
|
self.output_directory = File.expand_path(output_directory)
|
246
|
+
|
247
|
+
# For debugging
|
248
|
+
self.dump_steps ||= {}
|
229
249
|
|
230
250
|
# Set up our log file
|
231
251
|
unless self.log
|
@@ -268,8 +288,20 @@ class ExcelToX
|
|
268
288
|
|
269
289
|
# FIXME: Replace these with pure ruby versions?
|
270
290
|
def unzip_excel
|
271
|
-
log.info "Removing
|
272
|
-
|
291
|
+
log.info "Removing old folders"
|
292
|
+
execute_system_command 'rm', '-fr', xml_directory
|
293
|
+
log.info "Unzipping the spreadsheet"
|
294
|
+
execute_system_command 'unzip', '-q', excel_file, '-d', xml_directory
|
295
|
+
end
|
296
|
+
|
297
|
+
def execute_system_command(*args)
|
298
|
+
c = args.shelljoin
|
299
|
+
output = `#{c}`
|
300
|
+
unless $?.exitstatus == 0
|
301
|
+
log.error "Command failed: #{c}"
|
302
|
+
log.error output
|
303
|
+
exit 1
|
304
|
+
end
|
273
305
|
end
|
274
306
|
|
275
307
|
# The excel workbook.xml and allied relationship files knows about
|
@@ -308,16 +340,24 @@ class ExcelToX
|
|
308
340
|
# Then we parse them
|
309
341
|
@named_references.each do |name, reference|
|
310
342
|
begin
|
311
|
-
parsed = CachingFormulaParser.parse(reference)
|
343
|
+
parsed = CachingFormulaParser.parse(reference, treat_external_references_as_local)
|
312
344
|
if parsed
|
313
345
|
@named_references[name] = parsed
|
314
346
|
else
|
315
347
|
$stderr.puts "Named reference #{name} #{reference} not parsed"
|
316
348
|
exit
|
317
349
|
end
|
318
|
-
rescue Exception
|
319
|
-
|
320
|
-
|
350
|
+
rescue Exception => e
|
351
|
+
if e.respond_to?(:'ref=')
|
352
|
+
e.ref = ['Named reference', name]
|
353
|
+
end
|
354
|
+
if persevere
|
355
|
+
$stderr.puts e.message
|
356
|
+
$stderr.puts "--persevere true, so setting #{name} = #REF!"
|
357
|
+
@named_references[name] = [:error, "#REF!"]
|
358
|
+
else
|
359
|
+
raise
|
360
|
+
end
|
321
361
|
end
|
322
362
|
end
|
323
363
|
|
@@ -441,6 +481,10 @@ class ExcelToX
|
|
441
481
|
# Named references_to_keep can be passed a block, in which case this loops
|
442
482
|
# through offering up the named references. If the block returns true then
|
443
483
|
# the named reference is kept
|
484
|
+
if named_references_to_keep == :all
|
485
|
+
@named_references_to_keep = @named_references.keys.concat(@table_areas.keys)
|
486
|
+
end
|
487
|
+
|
444
488
|
if named_references_to_keep.is_a?(Proc)
|
445
489
|
new_named_references_to_keep = @named_references.keys.select do |named_reference|
|
446
490
|
named_references_to_keep.call(named_reference)
|
@@ -495,6 +539,7 @@ class ExcelToX
|
|
495
539
|
# All are hashes of the format ["SheetName", "A1"] => [:number, "1"]
|
496
540
|
# This one has a series of table references
|
497
541
|
extractor = ExtractDataFromWorksheet.new
|
542
|
+
extractor.persevere = persevere
|
498
543
|
|
499
544
|
# Loop through the worksheets
|
500
545
|
# FIXME: make xml_filename be the IO object?
|
@@ -857,7 +902,7 @@ class ExcelToX
|
|
857
902
|
sheet = ref[1]
|
858
903
|
cell = Reference.for(ref[2][1]).unfix.to_sym
|
859
904
|
s = cells_that_can_be_set[sheet]
|
860
|
-
if s && s.include?(cell)
|
905
|
+
if s && ( s == :all || s.include?(cell) )
|
861
906
|
@named_references_that_can_be_set_at_runtime << name
|
862
907
|
cells_that_can_be_set_due_to_named_reference[sheet] << cell.to_sym
|
863
908
|
cells_that_can_be_set_due_to_named_reference[sheet].uniq!
|
@@ -902,6 +947,11 @@ class ExcelToX
|
|
902
947
|
end
|
903
948
|
|
904
949
|
end
|
950
|
+
|
951
|
+
def debug_dump(ref, ast, location = "")
|
952
|
+
return unless dump_steps[ref]
|
953
|
+
puts "#{location}: #{ref} = #{ast}"
|
954
|
+
end
|
905
955
|
|
906
956
|
def simplify(cells = @formulae)
|
907
957
|
log.info "Simplifying cells"
|
@@ -923,6 +973,7 @@ class ExcelToX
|
|
923
973
|
#require 'pry'; binding.pry
|
924
974
|
|
925
975
|
cells.each do |ref, ast|
|
976
|
+
debug_dump(ref, ast, "simplify 1 - start")
|
926
977
|
begin
|
927
978
|
@sheetless_cell_reference_replacer.worksheet = ref.first
|
928
979
|
@sheetless_cell_reference_replacer.map(ast)
|
@@ -932,6 +983,17 @@ class ExcelToX
|
|
932
983
|
@table_reference_replacer.worksheet = ref.first
|
933
984
|
@table_reference_replacer.referring_cell = ref.last
|
934
985
|
@table_reference_replacer.map(ast)
|
986
|
+
column_and_row_function_replacement.current_reference = ref.last
|
987
|
+
column_and_row_function_replacement.replace(ast)
|
988
|
+
rescue Exception => e
|
989
|
+
log.fatal "Exception when simplifying #{ref}: #{ast}"
|
990
|
+
raise
|
991
|
+
end
|
992
|
+
debug_dump(ref, ast, "simplify 1 - finish")
|
993
|
+
end
|
994
|
+
cells.each do |ref, ast|
|
995
|
+
debug_dump(ref, ast, "simplify 2 - start")
|
996
|
+
begin
|
935
997
|
@replace_ranges_with_array_literals_replacer.map(ast)
|
936
998
|
@replace_arrays_with_single_cells_replacer.ref = ref
|
937
999
|
a = @replace_arrays_with_single_cells_replacer.map(ast)
|
@@ -941,8 +1003,6 @@ class ExcelToX
|
|
941
1003
|
@replace_arithmetic_on_ranges_replacer.map(ast)
|
942
1004
|
@replace_string_joins_on_ranges_replacer.map(ast)
|
943
1005
|
@wrap_formulae_that_return_arrays_replacer.map(ast)
|
944
|
-
column_and_row_function_replacement.current_reference = ref.last
|
945
|
-
column_and_row_function_replacement.replace(ast)
|
946
1006
|
@replace_references_to_blanks_with_zeros.current_sheet_name = ref.first
|
947
1007
|
@replace_references_to_blanks_with_zeros.map(ast)
|
948
1008
|
@fix_subtotal_of_subtotals.map(ast)
|
@@ -950,6 +1010,7 @@ class ExcelToX
|
|
950
1010
|
log.fatal "Exception when simplifying #{ref}: #{ast}"
|
951
1011
|
raise
|
952
1012
|
end
|
1013
|
+
debug_dump(ref, ast, "simplify 2 - finish")
|
953
1014
|
end
|
954
1015
|
end
|
955
1016
|
|
@@ -1014,6 +1075,7 @@ class ExcelToX
|
|
1014
1075
|
indirect_replacement = ReplaceIndirectsWithReferencesAst.new
|
1015
1076
|
column_and_row_function_replacement = ReplaceColumnAndRowFunctionsAST.new
|
1016
1077
|
offset_replacement = ReplaceOffsetsWithReferencesAst.new
|
1078
|
+
cell_address_replacement = ReplaceCellAddressesWithReferencesAst.new
|
1017
1079
|
|
1018
1080
|
begin
|
1019
1081
|
number_of_passes += 1
|
@@ -1024,6 +1086,7 @@ class ExcelToX
|
|
1024
1086
|
value_replacer.replacements_made_in_the_last_pass = 0
|
1025
1087
|
column_and_row_function_replacement.count_replaced = 0
|
1026
1088
|
offset_replacement.count_replaced = 0
|
1089
|
+
cell_address_replacement.count_replaced = 0
|
1027
1090
|
indirect_replacement.count_replaced = 0
|
1028
1091
|
references_that_need_updating = {}
|
1029
1092
|
|
@@ -1036,6 +1099,9 @@ class ExcelToX
|
|
1036
1099
|
if offset_replacement.replace(ast)
|
1037
1100
|
references_that_need_updating[ref] = ast
|
1038
1101
|
end
|
1102
|
+
if cell_address_replacement.replace(ast)
|
1103
|
+
references_that_need_updating[ref] = ast
|
1104
|
+
end
|
1039
1105
|
# FIXME: Shouldn't need to wrap ref.fist in an array
|
1040
1106
|
inline_replacer.current_sheet_name = [ref.first]
|
1041
1107
|
inline_replacer.map(ast)
|
@@ -1051,7 +1117,6 @@ class ExcelToX
|
|
1051
1117
|
raise
|
1052
1118
|
end
|
1053
1119
|
end
|
1054
|
-
|
1055
1120
|
|
1056
1121
|
@named_references.each do |ref, ast|
|
1057
1122
|
inline_replacer.current_sheet_name = ref.is_a?(Array) ? [ref.first] : []
|
@@ -1064,6 +1129,7 @@ class ExcelToX
|
|
1064
1129
|
replacements_made_in_the_last_pass += value_replacer.replacements_made_in_the_last_pass
|
1065
1130
|
replacements_made_in_the_last_pass += column_and_row_function_replacement.count_replaced
|
1066
1131
|
replacements_made_in_the_last_pass += offset_replacement.count_replaced
|
1132
|
+
replacements_made_in_the_last_pass += cell_address_replacement.count_replaced
|
1067
1133
|
replacements_made_in_the_last_pass += indirect_replacement.count_replaced
|
1068
1134
|
|
1069
1135
|
log.info "Pass #{number_of_passes}: Made #{replacements_made_in_the_last_pass} replacements"
|
@@ -1352,7 +1418,7 @@ class ExcelToX
|
|
1352
1418
|
args.flatten!
|
1353
1419
|
File.open(File.join(output_directory,*args),'w')
|
1354
1420
|
end
|
1355
|
-
|
1421
|
+
|
1356
1422
|
def close(*args)
|
1357
1423
|
args.map do |f|
|
1358
1424
|
next if f.is_a?(StringIO)
|
data/src/compile.rb
CHANGED
data/src/compile/c/a.out
CHANGED
Binary file
|
Binary file
|
@@ -5,6 +5,7 @@ class CompileToC
|
|
5
5
|
attr_accessor :settable
|
6
6
|
attr_accessor :gettable
|
7
7
|
attr_accessor :variable_set_counter
|
8
|
+
attr_accessor :allow_unknown_functions
|
8
9
|
|
9
10
|
def self.rewrite(*args)
|
10
11
|
self.new.rewrite(*args)
|
@@ -16,6 +17,7 @@ class CompileToC
|
|
16
17
|
@variable_set_counter ||= 0
|
17
18
|
|
18
19
|
mapper = MapFormulaeToC.new
|
20
|
+
mapper.allow_unknown_functions = self.allow_unknown_functions
|
19
21
|
mapper.sheet_names = sheet_names
|
20
22
|
formulae.each do |ref, ast|
|
21
23
|
begin
|
@@ -11,7 +11,7 @@
|
|
11
11
|
#endif
|
12
12
|
|
13
13
|
#ifndef EXCEL_FILENAME
|
14
|
-
#define EXCEL_FILENAME "NoExcelFilename"
|
14
|
+
#define EXCEL_FILENAME "NoExcelFilename"
|
15
15
|
#endif
|
16
16
|
|
17
17
|
// Need to retain malloc'd values for a while, so can return to functions that use this library
|
@@ -27,10 +27,10 @@ typedef enum {ExcelEmpty, ExcelNumber, ExcelString, ExcelBoolean, ExcelError, Ex
|
|
27
27
|
|
28
28
|
struct excel_value {
|
29
29
|
ExcelType type;
|
30
|
-
|
30
|
+
|
31
31
|
double number; // Used for numbers and for error types
|
32
32
|
char *string; // Used for strings
|
33
|
-
|
33
|
+
|
34
34
|
// The following three are used for ranges
|
35
35
|
void *array;
|
36
36
|
int rows;
|
@@ -59,6 +59,8 @@ static ExcelValue less_than_or_equal(ExcelValue a_v, ExcelValue b_v);
|
|
59
59
|
static ExcelValue average(int array_size, ExcelValue *array);
|
60
60
|
static ExcelValue averageifs(ExcelValue average_range_v, int number_of_arguments, ExcelValue *arguments);
|
61
61
|
static ExcelValue excel_char(ExcelValue number_v);
|
62
|
+
static ExcelValue excel_ceiling_math_2(ExcelValue number_v, ExcelValue multiple_v);
|
63
|
+
static ExcelValue excel_ceiling_math(ExcelValue number_v, ExcelValue multiple_v, ExcelValue mode_v);
|
62
64
|
static ExcelValue ensure_is_number(ExcelValue maybe_number_v);
|
63
65
|
static ExcelValue find_2(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v);
|
64
66
|
static ExcelValue find(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v, ExcelValue position_to_start_at_v);
|
@@ -84,7 +86,9 @@ static ExcelValue max(int number_of_arguments, ExcelValue *arguments);
|
|
84
86
|
static ExcelValue min(int number_of_arguments, ExcelValue *arguments);
|
85
87
|
static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v);
|
86
88
|
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v);
|
89
|
+
static ExcelValue na();
|
87
90
|
static ExcelValue negative(ExcelValue a_v);
|
91
|
+
static ExcelValue excel_not(ExcelValue a_v);
|
88
92
|
static ExcelValue number_or_zero(ExcelValue maybe_number_v);
|
89
93
|
static ExcelValue npv(ExcelValue rate, int number_of_arguments, ExcelValue *arguments);
|
90
94
|
static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v);
|
@@ -112,6 +116,19 @@ static ExcelValue text(ExcelValue number_v, ExcelValue format_v);
|
|
112
116
|
static ExcelValue value(ExcelValue string_v);
|
113
117
|
static ExcelValue vlookup_3(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v);
|
114
118
|
static ExcelValue vlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v, ExcelValue match_type_v);
|
119
|
+
static ExcelValue scurve_4(ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration);
|
120
|
+
static ExcelValue scurve(ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration, ExcelValue startYear);
|
121
|
+
static ExcelValue halfscurve_4(ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration);
|
122
|
+
static ExcelValue halfscurve(ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration, ExcelValue startYear);
|
123
|
+
static ExcelValue lcurve_4(ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration);
|
124
|
+
static ExcelValue lcurve(ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration, ExcelValue startYear);
|
125
|
+
static ExcelValue curve_5(ExcelValue curveType, ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration);
|
126
|
+
static ExcelValue curve(ExcelValue curveType, ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration, ExcelValue startYear);
|
127
|
+
|
128
|
+
static ExcelValue product(int number_of_arguments, ExcelValue *arguments);
|
129
|
+
static ExcelValue excel_floor(ExcelValue number_v, ExcelValue multiple_v);
|
130
|
+
static ExcelValue rate(ExcelValue a1, ExcelValue a2, ExcelValue a3, ExcelValue a4);
|
131
|
+
static ExcelValue excel_sqrt(ExcelValue number_v);
|
115
132
|
|
116
133
|
// My little heap for keeping pointers to memory that I need to reclaim
|
117
134
|
void **memory_that_needs_to_be_freed;
|
@@ -119,7 +136,7 @@ int memory_that_needs_to_be_freed_counter = 0;
|
|
119
136
|
int memory_that_needs_to_be_freed_size = -1;
|
120
137
|
|
121
138
|
static void free_later(void *pointer) {
|
122
|
-
if(memory_that_needs_to_be_freed_counter >= memory_that_needs_to_be_freed_size) {
|
139
|
+
if(memory_that_needs_to_be_freed_counter >= memory_that_needs_to_be_freed_size) {
|
123
140
|
if(memory_that_needs_to_be_freed_size <= 0) {
|
124
141
|
memory_that_needs_to_be_freed = malloc(MEMORY_TO_BE_FREED_LATER_HEAP_INCREMENT*sizeof(void*));
|
125
142
|
memory_that_needs_to_be_freed_size = MEMORY_TO_BE_FREED_LATER_HEAP_INCREMENT;
|
@@ -221,7 +238,7 @@ static void inspect_excel_value(ExcelValue v) {
|
|
221
238
|
if(v.number == 0) {
|
222
239
|
printf("Empty\n");
|
223
240
|
} else {
|
224
|
-
printf("Empty with unexpected state %f\n",v.number);
|
241
|
+
printf("Empty with unexpected state %f\n",v.number);
|
225
242
|
}
|
226
243
|
break;
|
227
244
|
case ExcelRange:
|
@@ -264,18 +281,18 @@ static double number_from(ExcelValue v) {
|
|
264
281
|
ExcelValue *array;
|
265
282
|
switch (v.type) {
|
266
283
|
case ExcelNumber:
|
267
|
-
case ExcelBoolean:
|
284
|
+
case ExcelBoolean:
|
268
285
|
return v.number;
|
269
|
-
case ExcelEmpty:
|
286
|
+
case ExcelEmpty:
|
270
287
|
return 0;
|
271
|
-
case ExcelRange:
|
288
|
+
case ExcelRange:
|
272
289
|
array = v.array;
|
273
290
|
return number_from(array[0]);
|
274
291
|
case ExcelString:
|
275
292
|
s = v.string;
|
276
293
|
if (s == NULL || *s == '\0' || isspace(*s)) {
|
277
294
|
return 0;
|
278
|
-
}
|
295
|
+
}
|
279
296
|
n = strtod (s, &p);
|
280
297
|
if(*p == '\0') {
|
281
298
|
return n;
|
@@ -291,12 +308,12 @@ static double number_from(ExcelValue v) {
|
|
291
308
|
#define NUMBER(value_name, name) double name; if(value_name.type == ExcelError) { return value_name; }; name = number_from(value_name);
|
292
309
|
#define CHECK_FOR_CONVERSION_ERROR if(conversion_error) { conversion_error = 0; return VALUE; };
|
293
310
|
#define CHECK_FOR_PASSED_ERROR(name) if(name.type == ExcelError) return name;
|
294
|
-
|
311
|
+
|
295
312
|
static ExcelValue excel_abs(ExcelValue a_v) {
|
296
|
-
CHECK_FOR_PASSED_ERROR(a_v)
|
313
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
297
314
|
NUMBER(a_v, a)
|
298
315
|
CHECK_FOR_CONVERSION_ERROR
|
299
|
-
|
316
|
+
|
300
317
|
if(a >= 0.0 ) {
|
301
318
|
return a_v;
|
302
319
|
} else {
|
@@ -305,7 +322,7 @@ static ExcelValue excel_abs(ExcelValue a_v) {
|
|
305
322
|
}
|
306
323
|
|
307
324
|
static ExcelValue excel_char(ExcelValue a_v) {
|
308
|
-
CHECK_FOR_PASSED_ERROR(a_v)
|
325
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
309
326
|
NUMBER(a_v, a)
|
310
327
|
CHECK_FOR_CONVERSION_ERROR
|
311
328
|
if(a <= 0) { return VALUE; }
|
@@ -387,18 +404,88 @@ static ExcelValue excel_exp(ExcelValue number_v) {
|
|
387
404
|
return EXCEL_NUMBER(exp(n));
|
388
405
|
}
|
389
406
|
|
407
|
+
static ExcelValue excel_sqrt(ExcelValue number_v) {
|
408
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
409
|
+
NUMBER(number_v, n)
|
410
|
+
CHECK_FOR_CONVERSION_ERROR
|
411
|
+
|
412
|
+
if(n<0) { return NUM; }
|
413
|
+
|
414
|
+
return EXCEL_NUMBER(sqrt(n));
|
415
|
+
}
|
416
|
+
|
417
|
+
static ExcelValue excel_floor(ExcelValue number_v, ExcelValue multiple_v) {
|
418
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
419
|
+
CHECK_FOR_PASSED_ERROR(multiple_v)
|
420
|
+
NUMBER(number_v, n)
|
421
|
+
NUMBER(multiple_v, m)
|
422
|
+
CHECK_FOR_CONVERSION_ERROR
|
423
|
+
if(m == 0) { return DIV0; }
|
424
|
+
if(m < 0) { return NUM; }
|
425
|
+
return EXCEL_NUMBER((n - fmod(n, m)));
|
426
|
+
}
|
427
|
+
|
428
|
+
static ExcelValue excel_ceiling_math_2(ExcelValue number_v, ExcelValue multiple_v) {
|
429
|
+
return excel_ceiling_math(number_v, multiple_v, ZERO);
|
430
|
+
}
|
431
|
+
|
432
|
+
static ExcelValue excel_ceiling_math(ExcelValue number_v, ExcelValue multiple_v, ExcelValue mode_v) {
|
433
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
434
|
+
CHECK_FOR_PASSED_ERROR(multiple_v)
|
435
|
+
CHECK_FOR_PASSED_ERROR(mode_v)
|
436
|
+
NUMBER(number_v, n)
|
437
|
+
NUMBER(multiple_v, m)
|
438
|
+
NUMBER(mode_v, d)
|
439
|
+
CHECK_FOR_CONVERSION_ERROR
|
440
|
+
if(m == 0) { return ZERO; }
|
441
|
+
|
442
|
+
if(d == 0 || n > 0 ) {
|
443
|
+
double remainder = fmod(n, m);
|
444
|
+
if(remainder == 0) {
|
445
|
+
return number_v;
|
446
|
+
} else {
|
447
|
+
return EXCEL_NUMBER(((double) ceil(n/m))*m);
|
448
|
+
}
|
449
|
+
|
450
|
+
|
451
|
+
} else { // Need to round negative away from zero
|
452
|
+
return negative(excel_ceiling_math(excel_abs(number_v), multiple_v, mode_v));
|
453
|
+
}
|
454
|
+
|
455
|
+
return EXCEL_NUMBER((n - fmod(n, m)));
|
456
|
+
}
|
457
|
+
|
458
|
+
static ExcelValue rate(ExcelValue periods_v, ExcelValue payment_v, ExcelValue presentValue_v, ExcelValue finalValue_v) {
|
459
|
+
CHECK_FOR_PASSED_ERROR(periods_v)
|
460
|
+
CHECK_FOR_PASSED_ERROR(payment_v)
|
461
|
+
CHECK_FOR_PASSED_ERROR(presentValue_v)
|
462
|
+
CHECK_FOR_PASSED_ERROR(finalValue_v)
|
463
|
+
|
464
|
+
NUMBER(periods_v, periods)
|
465
|
+
NUMBER(payment_v, payment)
|
466
|
+
NUMBER(presentValue_v, presentValue)
|
467
|
+
NUMBER(finalValue_v, finalValue)
|
468
|
+
|
469
|
+
// FIXME: Only implemented the case where payment is zero
|
470
|
+
if(payment != 0) {
|
471
|
+
return NA;
|
472
|
+
}
|
473
|
+
|
474
|
+
return EXCEL_NUMBER(pow((finalValue/(-presentValue)),(1.0/periods))-1.0);
|
475
|
+
}
|
476
|
+
|
390
477
|
static ExcelValue excel_and(int array_size, ExcelValue *array) {
|
391
478
|
int i;
|
392
479
|
ExcelValue current_excel_value, array_result;
|
393
|
-
|
480
|
+
|
394
481
|
for(i=0;i<array_size;i++) {
|
395
482
|
current_excel_value = array[i];
|
396
483
|
switch (current_excel_value.type) {
|
397
|
-
case ExcelNumber:
|
398
|
-
case ExcelBoolean:
|
484
|
+
case ExcelNumber:
|
485
|
+
case ExcelBoolean:
|
399
486
|
if(current_excel_value.number == false) return FALSE;
|
400
487
|
break;
|
401
|
-
case ExcelRange:
|
488
|
+
case ExcelRange:
|
402
489
|
array_result = excel_and( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
403
490
|
if(array_result.type == ExcelError) return array_result;
|
404
491
|
if(array_result.type == ExcelBoolean && array_result.number == false) return FALSE;
|
@@ -414,20 +501,71 @@ static ExcelValue excel_and(int array_size, ExcelValue *array) {
|
|
414
501
|
return TRUE;
|
415
502
|
}
|
416
503
|
|
504
|
+
static ExcelValue excel_or(int array_size, ExcelValue *array) {
|
505
|
+
int i;
|
506
|
+
ExcelValue current_excel_value, array_result;
|
507
|
+
|
508
|
+
for(i=0;i<array_size;i++) {
|
509
|
+
current_excel_value = array[i];
|
510
|
+
switch (current_excel_value.type) {
|
511
|
+
case ExcelNumber:
|
512
|
+
case ExcelBoolean:
|
513
|
+
if(current_excel_value.number == true) return TRUE;
|
514
|
+
break;
|
515
|
+
case ExcelRange:
|
516
|
+
array_result = excel_or( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
517
|
+
if(array_result.type == ExcelError) return array_result;
|
518
|
+
if(array_result.type == ExcelBoolean && array_result.number == true) return TRUE;
|
519
|
+
break;
|
520
|
+
case ExcelString:
|
521
|
+
case ExcelEmpty:
|
522
|
+
break;
|
523
|
+
case ExcelError:
|
524
|
+
return current_excel_value;
|
525
|
+
break;
|
526
|
+
}
|
527
|
+
}
|
528
|
+
return FALSE;
|
529
|
+
}
|
530
|
+
|
531
|
+
static ExcelValue excel_not(ExcelValue boolean_v) {
|
532
|
+
switch (boolean_v.type) {
|
533
|
+
case ExcelNumber:
|
534
|
+
if(boolean_v.number == 0) return TRUE;
|
535
|
+
return FALSE;
|
536
|
+
|
537
|
+
case ExcelBoolean:
|
538
|
+
if(boolean_v.number == false) return TRUE;
|
539
|
+
return FALSE;
|
540
|
+
|
541
|
+
case ExcelRange:
|
542
|
+
return VALUE;
|
543
|
+
|
544
|
+
case ExcelString:
|
545
|
+
return VALUE;
|
546
|
+
|
547
|
+
case ExcelEmpty:
|
548
|
+
return TRUE;
|
549
|
+
|
550
|
+
case ExcelError:
|
551
|
+
return boolean_v;
|
552
|
+
}
|
553
|
+
}
|
554
|
+
|
417
555
|
struct average_result {
|
418
556
|
double sum;
|
419
557
|
double count;
|
420
558
|
int has_error;
|
421
559
|
ExcelValue error;
|
422
560
|
};
|
423
|
-
|
561
|
+
|
424
562
|
static struct average_result calculate_average(int array_size, ExcelValue *array) {
|
425
563
|
double sum = 0;
|
426
564
|
double count = 0;
|
427
565
|
int i;
|
428
566
|
ExcelValue current_excel_value;
|
429
567
|
struct average_result array_result, r;
|
430
|
-
|
568
|
+
|
431
569
|
for(i=0;i<array_size;i++) {
|
432
570
|
current_excel_value = array[i];
|
433
571
|
switch (current_excel_value.type) {
|
@@ -435,13 +573,13 @@ static struct average_result calculate_average(int array_size, ExcelValue *array
|
|
435
573
|
sum += current_excel_value.number;
|
436
574
|
count++;
|
437
575
|
break;
|
438
|
-
case ExcelRange:
|
576
|
+
case ExcelRange:
|
439
577
|
array_result = calculate_average( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
440
578
|
if(array_result.has_error == true) return array_result;
|
441
579
|
sum += array_result.sum;
|
442
580
|
count += array_result.count;
|
443
581
|
break;
|
444
|
-
case ExcelBoolean:
|
582
|
+
case ExcelBoolean:
|
445
583
|
case ExcelString:
|
446
584
|
case ExcelEmpty:
|
447
585
|
break;
|
@@ -511,7 +649,7 @@ static ExcelValue forecast(ExcelValue required_x_v, ExcelValue known_y, ExcelVal
|
|
511
649
|
float my = mean_y.number;
|
512
650
|
|
513
651
|
float b_numerator, b_denominator, b, a;
|
514
|
-
|
652
|
+
|
515
653
|
b_denominator = 0;
|
516
654
|
b_numerator = 0;
|
517
655
|
|
@@ -537,7 +675,7 @@ static ExcelValue choose(ExcelValue index_v, int array_size, ExcelValue *array)
|
|
537
675
|
CHECK_FOR_PASSED_ERROR(index_v)
|
538
676
|
|
539
677
|
int index = (int) number_from(index_v);
|
540
|
-
CHECK_FOR_CONVERSION_ERROR
|
678
|
+
CHECK_FOR_CONVERSION_ERROR
|
541
679
|
int i;
|
542
680
|
for(i=0;i<array_size;i++) {
|
543
681
|
if(array[i].type == ExcelError) return array[i];
|
@@ -545,23 +683,23 @@ static ExcelValue choose(ExcelValue index_v, int array_size, ExcelValue *array)
|
|
545
683
|
if(index < 1) return VALUE;
|
546
684
|
if(index > array_size) return VALUE;
|
547
685
|
return array[index-1];
|
548
|
-
}
|
686
|
+
}
|
549
687
|
|
550
688
|
static ExcelValue count(int array_size, ExcelValue *array) {
|
551
689
|
int i;
|
552
690
|
int n = 0;
|
553
691
|
ExcelValue current_excel_value;
|
554
|
-
|
692
|
+
|
555
693
|
for(i=0;i<array_size;i++) {
|
556
694
|
current_excel_value = array[i];
|
557
695
|
switch (current_excel_value.type) {
|
558
696
|
case ExcelNumber:
|
559
697
|
n++;
|
560
698
|
break;
|
561
|
-
case ExcelRange:
|
699
|
+
case ExcelRange:
|
562
700
|
n += count( current_excel_value.rows * current_excel_value.columns, current_excel_value.array ).number;
|
563
701
|
break;
|
564
|
-
case ExcelBoolean:
|
702
|
+
case ExcelBoolean:
|
565
703
|
case ExcelString:
|
566
704
|
case ExcelEmpty:
|
567
705
|
case ExcelError:
|
@@ -575,7 +713,7 @@ static ExcelValue counta(int array_size, ExcelValue *array) {
|
|
575
713
|
int i;
|
576
714
|
int n = 0;
|
577
715
|
ExcelValue current_excel_value;
|
578
|
-
|
716
|
+
|
579
717
|
for(i=0;i<array_size;i++) {
|
580
718
|
current_excel_value = array[i];
|
581
719
|
switch(current_excel_value.type) {
|
@@ -585,7 +723,7 @@ static ExcelValue counta(int array_size, ExcelValue *array) {
|
|
585
723
|
case ExcelError:
|
586
724
|
n++;
|
587
725
|
break;
|
588
|
-
case ExcelRange:
|
726
|
+
case ExcelRange:
|
589
727
|
n += counta( current_excel_value.rows * current_excel_value.columns, current_excel_value.array ).number;
|
590
728
|
break;
|
591
729
|
case ExcelEmpty:
|
@@ -610,11 +748,11 @@ static ExcelValue excel_equal(ExcelValue a_v, ExcelValue b_v) {
|
|
610
748
|
CHECK_FOR_PASSED_ERROR(b_v)
|
611
749
|
|
612
750
|
if(a_v.type != b_v.type) return FALSE;
|
613
|
-
|
751
|
+
|
614
752
|
switch (a_v.type) {
|
615
753
|
case ExcelNumber:
|
616
|
-
case ExcelBoolean:
|
617
|
-
case ExcelEmpty:
|
754
|
+
case ExcelBoolean:
|
755
|
+
case ExcelEmpty:
|
618
756
|
if(a_v.number != b_v.number) return FALSE;
|
619
757
|
return TRUE;
|
620
758
|
case ExcelString:
|
@@ -655,7 +793,7 @@ static ExcelValue excel_isblank(ExcelValue value) {
|
|
655
793
|
|
656
794
|
static ExcelValue excel_if(ExcelValue condition, ExcelValue true_case, ExcelValue false_case ) {
|
657
795
|
CHECK_FOR_PASSED_ERROR(condition)
|
658
|
-
|
796
|
+
|
659
797
|
switch (condition.type) {
|
660
798
|
case ExcelBoolean:
|
661
799
|
if(condition.number == true) return true_case;
|
@@ -663,7 +801,7 @@ static ExcelValue excel_if(ExcelValue condition, ExcelValue true_case, ExcelValu
|
|
663
801
|
case ExcelNumber:
|
664
802
|
if(condition.number == false) return false_case;
|
665
803
|
return true_case;
|
666
|
-
case ExcelEmpty:
|
804
|
+
case ExcelEmpty:
|
667
805
|
return false_case;
|
668
806
|
case ExcelString:
|
669
807
|
return VALUE;
|
@@ -683,15 +821,15 @@ static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, Excel
|
|
683
821
|
CHECK_FOR_PASSED_ERROR(array_v)
|
684
822
|
CHECK_FOR_PASSED_ERROR(row_number_v)
|
685
823
|
CHECK_FOR_PASSED_ERROR(column_number_v)
|
686
|
-
|
824
|
+
|
687
825
|
ExcelValue *array;
|
688
826
|
int rows;
|
689
827
|
int columns;
|
690
|
-
|
828
|
+
|
691
829
|
NUMBER(row_number_v, row_number)
|
692
830
|
NUMBER(column_number_v, column_number)
|
693
831
|
CHECK_FOR_CONVERSION_ERROR
|
694
|
-
|
832
|
+
|
695
833
|
if(array_v.type == ExcelRange) {
|
696
834
|
array = array_v.array;
|
697
835
|
rows = array_v.rows;
|
@@ -702,13 +840,13 @@ static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, Excel
|
|
702
840
|
rows = 1;
|
703
841
|
columns = 1;
|
704
842
|
}
|
705
|
-
|
843
|
+
|
706
844
|
if(row_number > rows) return REF;
|
707
845
|
if(column_number > columns) return REF;
|
708
846
|
|
709
847
|
if(row_number == 0 && rows == 1) row_number = 1;
|
710
848
|
if(column_number == 0 && columns == 1) column_number = 1;
|
711
|
-
|
849
|
+
|
712
850
|
if(row_number == 0) { // We need the whole column
|
713
851
|
if(column_number < 1) return REF;
|
714
852
|
ExcelValue *result = (ExcelValue *) new_excel_value_array(rows);
|
@@ -723,7 +861,7 @@ static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, Excel
|
|
723
861
|
result[result_index] = ZERO;
|
724
862
|
} else {
|
725
863
|
result[result_index] = r;
|
726
|
-
}
|
864
|
+
}
|
727
865
|
result_index++;
|
728
866
|
}
|
729
867
|
return EXCEL_RANGE(result,rows,1);
|
@@ -753,7 +891,7 @@ static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, Excel
|
|
753
891
|
if(result.type == ExcelEmpty) return ZERO;
|
754
892
|
return result;
|
755
893
|
}
|
756
|
-
|
894
|
+
|
757
895
|
return FALSE;
|
758
896
|
};
|
759
897
|
|
@@ -798,7 +936,7 @@ static ExcelValue large(ExcelValue range_v, ExcelValue k_v) {
|
|
798
936
|
}
|
799
937
|
|
800
938
|
// Otherwise grumble if not a range
|
801
|
-
if(
|
939
|
+
if(range_v.type != ExcelRange) { return VALUE; }
|
802
940
|
|
803
941
|
// Check that our k is within bounds
|
804
942
|
if(k < 1) { return NUM; }
|
@@ -810,7 +948,7 @@ static ExcelValue large(ExcelValue range_v, ExcelValue k_v) {
|
|
810
948
|
int sorted_size = 0;
|
811
949
|
ExcelValue *array_v = range_v.array;
|
812
950
|
ExcelValue x_v;
|
813
|
-
int i;
|
951
|
+
int i;
|
814
952
|
for(i = 0; i < range_size; i++ ) {
|
815
953
|
x_v = array_v[i];
|
816
954
|
if(x_v.type == ExcelError) { free(sorted); return x_v; };
|
@@ -834,7 +972,7 @@ static ExcelValue excel_match(ExcelValue lookup_value, ExcelValue lookup_array,
|
|
834
972
|
CHECK_FOR_PASSED_ERROR(lookup_value)
|
835
973
|
CHECK_FOR_PASSED_ERROR(lookup_array)
|
836
974
|
CHECK_FOR_PASSED_ERROR(match_type)
|
837
|
-
|
975
|
+
|
838
976
|
// Blanks are treaked as zeros
|
839
977
|
if(lookup_value.type == ExcelEmpty) lookup_value = ZERO;
|
840
978
|
|
@@ -856,13 +994,13 @@ static ExcelValue excel_match(ExcelValue lookup_value, ExcelValue lookup_array,
|
|
856
994
|
ExcelValue tmp_array[1] = {lookup_array};
|
857
995
|
array = tmp_array;
|
858
996
|
}
|
859
|
-
|
997
|
+
|
860
998
|
int type = (int) number_from(match_type);
|
861
999
|
CHECK_FOR_CONVERSION_ERROR;
|
862
|
-
|
1000
|
+
|
863
1001
|
int i;
|
864
1002
|
ExcelValue x;
|
865
|
-
|
1003
|
+
|
866
1004
|
switch(type) {
|
867
1005
|
case 0:
|
868
1006
|
for(i = 0; i < size; i++ ) {
|
@@ -875,7 +1013,7 @@ static ExcelValue excel_match(ExcelValue lookup_value, ExcelValue lookup_array,
|
|
875
1013
|
case 1:
|
876
1014
|
for(i = 0; i < size; i++ ) {
|
877
1015
|
x = array[i];
|
878
|
-
if(x.type
|
1016
|
+
if(lookup_value.type != x.type ) { continue; }
|
879
1017
|
if(more_than(x,lookup_value).number == true) {
|
880
1018
|
if(i==0) return NA;
|
881
1019
|
return EXCEL_NUMBER(i);
|
@@ -886,7 +1024,7 @@ static ExcelValue excel_match(ExcelValue lookup_value, ExcelValue lookup_array,
|
|
886
1024
|
case -1:
|
887
1025
|
for(i = 0; i < size; i++ ) {
|
888
1026
|
x = array[i];
|
889
|
-
|
1027
|
+
if(lookup_value.type != x.type ) { continue; }
|
890
1028
|
if(less_than(x,lookup_value).number == true) {
|
891
1029
|
if(i==0) return NA;
|
892
1030
|
return EXCEL_NUMBER(i);
|
@@ -907,14 +1045,14 @@ static ExcelValue find(ExcelValue find_text_v, ExcelValue within_text_v, ExcelVa
|
|
907
1045
|
CHECK_FOR_PASSED_ERROR(within_text_v)
|
908
1046
|
CHECK_FOR_PASSED_ERROR(start_number_v)
|
909
1047
|
|
910
|
-
char *find_text;
|
1048
|
+
char *find_text;
|
911
1049
|
char *within_text;
|
912
1050
|
char *within_text_offset;
|
913
1051
|
char *result;
|
914
1052
|
int start_number = number_from(start_number_v);
|
915
1053
|
CHECK_FOR_CONVERSION_ERROR
|
916
1054
|
|
917
|
-
// Deal with blanks
|
1055
|
+
// Deal with blanks
|
918
1056
|
if(within_text_v.type == ExcelString) {
|
919
1057
|
within_text = within_text_v.string;
|
920
1058
|
} else if( within_text_v.type == ExcelEmpty) {
|
@@ -926,11 +1064,11 @@ static ExcelValue find(ExcelValue find_text_v, ExcelValue within_text_v, ExcelVa
|
|
926
1064
|
} else if( find_text_v.type == ExcelEmpty) {
|
927
1065
|
return start_number_v;
|
928
1066
|
}
|
929
|
-
|
1067
|
+
|
930
1068
|
// Check length
|
931
1069
|
if(start_number < 1) return VALUE;
|
932
1070
|
if(start_number > strlen(within_text)) return VALUE;
|
933
|
-
|
1071
|
+
|
934
1072
|
// Offset our within_text pointer
|
935
1073
|
// FIXME: No way this is utf-8 compatible
|
936
1074
|
within_text_offset = within_text + (start_number - 1);
|
@@ -950,7 +1088,7 @@ static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v) {
|
|
950
1088
|
CHECK_FOR_PASSED_ERROR(number_of_characters_v)
|
951
1089
|
if(string_v.type == ExcelEmpty) return BLANK;
|
952
1090
|
if(number_of_characters_v.type == ExcelEmpty) return BLANK;
|
953
|
-
|
1091
|
+
|
954
1092
|
int number_of_characters = (int) number_from(number_of_characters_v);
|
955
1093
|
CHECK_FOR_CONVERSION_ERROR
|
956
1094
|
|
@@ -980,7 +1118,7 @@ static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v) {
|
|
980
1118
|
string = "FALSE";
|
981
1119
|
}
|
982
1120
|
break;
|
983
|
-
case ExcelEmpty:
|
1121
|
+
case ExcelEmpty:
|
984
1122
|
case ExcelError:
|
985
1123
|
case ExcelRange:
|
986
1124
|
return string_v;
|
@@ -989,7 +1127,7 @@ static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v) {
|
|
989
1127
|
if(number_of_characters > strlen(string)) {
|
990
1128
|
number_of_characters = strlen(string);
|
991
1129
|
}
|
992
|
-
|
1130
|
+
|
993
1131
|
char *left_string = malloc(number_of_characters+1); // Freed
|
994
1132
|
if(left_string == 0) {
|
995
1133
|
printf("Out of memoryn in left");
|
@@ -1034,7 +1172,7 @@ static ExcelValue len(ExcelValue string_v) {
|
|
1034
1172
|
string = "FALSE";
|
1035
1173
|
}
|
1036
1174
|
break;
|
1037
|
-
case ExcelEmpty:
|
1175
|
+
case ExcelEmpty:
|
1038
1176
|
case ExcelError:
|
1039
1177
|
case ExcelRange:
|
1040
1178
|
return string_v;
|
@@ -1052,7 +1190,7 @@ static ExcelValue right(ExcelValue string_v, ExcelValue number_of_characters_v)
|
|
1052
1190
|
CHECK_FOR_PASSED_ERROR(number_of_characters_v)
|
1053
1191
|
if(string_v.type == ExcelEmpty) return BLANK;
|
1054
1192
|
if(number_of_characters_v.type == ExcelEmpty) return BLANK;
|
1055
|
-
|
1193
|
+
|
1056
1194
|
int number_of_characters = (int) number_from(number_of_characters_v);
|
1057
1195
|
CHECK_FOR_CONVERSION_ERROR
|
1058
1196
|
|
@@ -1082,12 +1220,12 @@ static ExcelValue right(ExcelValue string_v, ExcelValue number_of_characters_v)
|
|
1082
1220
|
string = "FALSE";
|
1083
1221
|
}
|
1084
1222
|
break;
|
1085
|
-
case ExcelEmpty:
|
1223
|
+
case ExcelEmpty:
|
1086
1224
|
case ExcelError:
|
1087
1225
|
case ExcelRange:
|
1088
1226
|
return string_v;
|
1089
1227
|
}
|
1090
|
-
|
1228
|
+
|
1091
1229
|
char *right_string = malloc(number_of_characters+1); // Freed
|
1092
1230
|
if(right_string == 0) {
|
1093
1231
|
printf("Out of memory in right");
|
@@ -1149,18 +1287,18 @@ static ExcelValue more_than(ExcelValue a_v, ExcelValue b_v) {
|
|
1149
1287
|
case ExcelString:
|
1150
1288
|
switch (b_v.type) {
|
1151
1289
|
case ExcelString:
|
1152
|
-
if(strcasecmp(a_v.string,b_v.string) <= 0 ) {return FALSE;} else {return TRUE;}
|
1153
|
-
case ExcelNumber:
|
1290
|
+
if(strcasecmp(a_v.string,b_v.string) <= 0 ) {return FALSE;} else {return TRUE;}
|
1291
|
+
case ExcelNumber:
|
1154
1292
|
return TRUE;
|
1155
1293
|
case ExcelBoolean:
|
1156
1294
|
return FALSE;
|
1157
1295
|
// Following shouldn't happen
|
1158
|
-
case ExcelEmpty:
|
1159
|
-
case ExcelError:
|
1296
|
+
case ExcelEmpty:
|
1297
|
+
case ExcelError:
|
1160
1298
|
case ExcelRange:
|
1161
1299
|
return NA;
|
1162
1300
|
}
|
1163
|
-
case ExcelBoolean:
|
1301
|
+
case ExcelBoolean:
|
1164
1302
|
switch (b_v.type) {
|
1165
1303
|
case ExcelBoolean:
|
1166
1304
|
if(a_v.number == true) {
|
@@ -1169,11 +1307,11 @@ static ExcelValue more_than(ExcelValue a_v, ExcelValue b_v) {
|
|
1169
1307
|
return FALSE;
|
1170
1308
|
}
|
1171
1309
|
case ExcelString:
|
1172
|
-
case ExcelNumber:
|
1310
|
+
case ExcelNumber:
|
1173
1311
|
return TRUE;
|
1174
1312
|
// Following shouldn't happen
|
1175
|
-
case ExcelEmpty:
|
1176
|
-
case ExcelError:
|
1313
|
+
case ExcelEmpty:
|
1314
|
+
case ExcelError:
|
1177
1315
|
case ExcelRange:
|
1178
1316
|
return NA;
|
1179
1317
|
}
|
@@ -1185,17 +1323,17 @@ static ExcelValue more_than(ExcelValue a_v, ExcelValue b_v) {
|
|
1185
1323
|
case ExcelBoolean:
|
1186
1324
|
return FALSE;
|
1187
1325
|
// Following shouldn't happen
|
1188
|
-
case ExcelEmpty:
|
1189
|
-
case ExcelError:
|
1326
|
+
case ExcelEmpty:
|
1327
|
+
case ExcelError:
|
1190
1328
|
case ExcelRange:
|
1191
1329
|
return NA;
|
1192
1330
|
}
|
1193
1331
|
// Following shouldn't happen
|
1194
|
-
case ExcelEmpty:
|
1195
|
-
case ExcelError:
|
1332
|
+
case ExcelEmpty:
|
1333
|
+
case ExcelError:
|
1196
1334
|
case ExcelRange:
|
1197
1335
|
return NA;
|
1198
|
-
}
|
1336
|
+
}
|
1199
1337
|
// Shouldn't reach here
|
1200
1338
|
return NA;
|
1201
1339
|
}
|
@@ -1264,7 +1402,7 @@ static ExcelValue less_than(ExcelValue a_v, ExcelValue b_v) {
|
|
1264
1402
|
}
|
1265
1403
|
case ExcelBoolean:
|
1266
1404
|
switch(b_v.type) {
|
1267
|
-
case ExcelBoolean:
|
1405
|
+
case ExcelBoolean:
|
1268
1406
|
if(a_v.number == true) {
|
1269
1407
|
return FALSE;
|
1270
1408
|
} else { // a_v.number == false
|
@@ -1395,7 +1533,7 @@ static ExcelValue max(int number_of_arguments, ExcelValue *arguments) {
|
|
1395
1533
|
int any_number_found = 0;
|
1396
1534
|
int i;
|
1397
1535
|
ExcelValue current_excel_value;
|
1398
|
-
|
1536
|
+
|
1399
1537
|
for(i=0;i<number_of_arguments;i++) {
|
1400
1538
|
current_excel_value = arguments[i];
|
1401
1539
|
if(current_excel_value.type == ExcelNumber) {
|
@@ -1403,7 +1541,7 @@ static ExcelValue max(int number_of_arguments, ExcelValue *arguments) {
|
|
1403
1541
|
any_number_found = 1;
|
1404
1542
|
biggest_number_found = current_excel_value.number;
|
1405
1543
|
}
|
1406
|
-
if(current_excel_value.number > biggest_number_found) biggest_number_found = current_excel_value.number;
|
1544
|
+
if(current_excel_value.number > biggest_number_found) biggest_number_found = current_excel_value.number;
|
1407
1545
|
} else if(current_excel_value.type == ExcelRange) {
|
1408
1546
|
current_excel_value = max( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
1409
1547
|
if(current_excel_value.type == ExcelError) return current_excel_value;
|
@@ -1412,7 +1550,7 @@ static ExcelValue max(int number_of_arguments, ExcelValue *arguments) {
|
|
1412
1550
|
any_number_found = 1;
|
1413
1551
|
biggest_number_found = current_excel_value.number;
|
1414
1552
|
}
|
1415
|
-
if(current_excel_value.number > biggest_number_found) biggest_number_found = current_excel_value.number;
|
1553
|
+
if(current_excel_value.number > biggest_number_found) biggest_number_found = current_excel_value.number;
|
1416
1554
|
} else if(current_excel_value.type == ExcelError) {
|
1417
1555
|
return current_excel_value;
|
1418
1556
|
}
|
@@ -1421,7 +1559,7 @@ static ExcelValue max(int number_of_arguments, ExcelValue *arguments) {
|
|
1421
1559
|
any_number_found = 1;
|
1422
1560
|
biggest_number_found = 0;
|
1423
1561
|
}
|
1424
|
-
return EXCEL_NUMBER(biggest_number_found);
|
1562
|
+
return EXCEL_NUMBER(biggest_number_found);
|
1425
1563
|
}
|
1426
1564
|
|
1427
1565
|
static ExcelValue min(int number_of_arguments, ExcelValue *arguments) {
|
@@ -1429,7 +1567,7 @@ static ExcelValue min(int number_of_arguments, ExcelValue *arguments) {
|
|
1429
1567
|
int any_number_found = 0;
|
1430
1568
|
int i;
|
1431
1569
|
ExcelValue current_excel_value;
|
1432
|
-
|
1570
|
+
|
1433
1571
|
for(i=0;i<number_of_arguments;i++) {
|
1434
1572
|
current_excel_value = arguments[i];
|
1435
1573
|
if(current_excel_value.type == ExcelNumber) {
|
@@ -1437,7 +1575,7 @@ static ExcelValue min(int number_of_arguments, ExcelValue *arguments) {
|
|
1437
1575
|
any_number_found = 1;
|
1438
1576
|
smallest_number_found = current_excel_value.number;
|
1439
1577
|
}
|
1440
|
-
if(current_excel_value.number < smallest_number_found) smallest_number_found = current_excel_value.number;
|
1578
|
+
if(current_excel_value.number < smallest_number_found) smallest_number_found = current_excel_value.number;
|
1441
1579
|
} else if(current_excel_value.type == ExcelRange) {
|
1442
1580
|
current_excel_value = min( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
1443
1581
|
if(current_excel_value.type == ExcelError) return current_excel_value;
|
@@ -1446,7 +1584,7 @@ static ExcelValue min(int number_of_arguments, ExcelValue *arguments) {
|
|
1446
1584
|
any_number_found = 1;
|
1447
1585
|
smallest_number_found = current_excel_value.number;
|
1448
1586
|
}
|
1449
|
-
if(current_excel_value.number < smallest_number_found) smallest_number_found = current_excel_value.number;
|
1587
|
+
if(current_excel_value.number < smallest_number_found) smallest_number_found = current_excel_value.number;
|
1450
1588
|
} else if(current_excel_value.type == ExcelError) {
|
1451
1589
|
return current_excel_value;
|
1452
1590
|
}
|
@@ -1455,7 +1593,7 @@ static ExcelValue min(int number_of_arguments, ExcelValue *arguments) {
|
|
1455
1593
|
any_number_found = 1;
|
1456
1594
|
smallest_number_found = 0;
|
1457
1595
|
}
|
1458
|
-
return EXCEL_NUMBER(smallest_number_found);
|
1596
|
+
return EXCEL_NUMBER(smallest_number_found);
|
1459
1597
|
}
|
1460
1598
|
|
1461
1599
|
static ExcelValue mmult_error(ExcelValue a_v, ExcelValue b_v) {
|
@@ -1485,13 +1623,13 @@ static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v) {
|
|
1485
1623
|
int b_columns = b_v.columns;
|
1486
1624
|
ExcelValue *result = (ExcelValue*) new_excel_value_array(a_rows*b_columns);
|
1487
1625
|
int i, j, k;
|
1488
|
-
double sum;
|
1626
|
+
double sum;
|
1489
1627
|
ExcelValue *array_a = a_v.array;
|
1490
1628
|
ExcelValue *array_b = b_v.array;
|
1491
1629
|
|
1492
1630
|
ExcelValue a;
|
1493
1631
|
ExcelValue b;
|
1494
|
-
|
1632
|
+
|
1495
1633
|
for(i=0; i<a_rows; i++) {
|
1496
1634
|
for(j=0; j<b_columns; j++) {
|
1497
1635
|
sum = 0;
|
@@ -1511,7 +1649,7 @@ static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v) {
|
|
1511
1649
|
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v) {
|
1512
1650
|
CHECK_FOR_PASSED_ERROR(a_v)
|
1513
1651
|
CHECK_FOR_PASSED_ERROR(b_v)
|
1514
|
-
|
1652
|
+
|
1515
1653
|
NUMBER(a_v, a)
|
1516
1654
|
NUMBER(b_v, b)
|
1517
1655
|
CHECK_FOR_CONVERSION_ERROR
|
@@ -1519,6 +1657,10 @@ static ExcelValue mod(ExcelValue a_v, ExcelValue b_v) {
|
|
1519
1657
|
return EXCEL_NUMBER(fmod(a,b));
|
1520
1658
|
}
|
1521
1659
|
|
1660
|
+
static ExcelValue na() {
|
1661
|
+
return NA;
|
1662
|
+
}
|
1663
|
+
|
1522
1664
|
static ExcelValue negative(ExcelValue a_v) {
|
1523
1665
|
CHECK_FOR_PASSED_ERROR(a_v)
|
1524
1666
|
NUMBER(a_v, a)
|
@@ -1530,22 +1672,22 @@ static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelVa
|
|
1530
1672
|
CHECK_FOR_PASSED_ERROR(rate_v)
|
1531
1673
|
CHECK_FOR_PASSED_ERROR(number_of_periods_v)
|
1532
1674
|
CHECK_FOR_PASSED_ERROR(present_value_v)
|
1533
|
-
|
1675
|
+
|
1534
1676
|
NUMBER(rate_v,rate)
|
1535
1677
|
NUMBER(number_of_periods_v,number_of_periods)
|
1536
1678
|
NUMBER(present_value_v,present_value)
|
1537
1679
|
CHECK_FOR_CONVERSION_ERROR
|
1538
|
-
|
1680
|
+
|
1539
1681
|
if(rate == 0) return EXCEL_NUMBER(-(present_value / number_of_periods));
|
1540
1682
|
return EXCEL_NUMBER(-present_value*(rate*(pow((1+rate),number_of_periods)))/((pow((1+rate),number_of_periods))-1));
|
1541
1683
|
}
|
1542
1684
|
|
1543
1685
|
static ExcelValue pmt_4(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v, ExcelValue final_value_v) {
|
1544
1686
|
CHECK_FOR_PASSED_ERROR(final_value_v)
|
1545
|
-
|
1687
|
+
|
1546
1688
|
NUMBER(final_value_v, final_value)
|
1547
1689
|
CHECK_FOR_CONVERSION_ERROR
|
1548
|
-
|
1690
|
+
|
1549
1691
|
if(final_value == 0) return pmt(rate_v, number_of_periods_v, present_value_v);
|
1550
1692
|
printf("PMT with non-zero final_value not implemented. halting.");
|
1551
1693
|
exit(-1);
|
@@ -1553,10 +1695,10 @@ static ExcelValue pmt_4(ExcelValue rate_v, ExcelValue number_of_periods_v, Excel
|
|
1553
1695
|
|
1554
1696
|
static ExcelValue pmt_5(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v, ExcelValue final_value_v, ExcelValue type_v) {
|
1555
1697
|
CHECK_FOR_PASSED_ERROR(type_v)
|
1556
|
-
|
1698
|
+
|
1557
1699
|
NUMBER(type_v, type)
|
1558
1700
|
CHECK_FOR_CONVERSION_ERROR
|
1559
|
-
|
1701
|
+
|
1560
1702
|
if(type == 0) return pmt(rate_v, number_of_periods_v, present_value_v);
|
1561
1703
|
printf("PMT with non-zero type not implemented. halting.");
|
1562
1704
|
exit(-1);
|
@@ -1605,11 +1747,11 @@ static ExcelValue pv_5(ExcelValue rate_v, ExcelValue nper_v, ExcelValue pmt_v, E
|
|
1605
1747
|
present_value = present_value * (1+rate);
|
1606
1748
|
} else {
|
1607
1749
|
return VALUE;
|
1608
|
-
}
|
1750
|
+
}
|
1609
1751
|
|
1610
1752
|
// Add on the final value
|
1611
1753
|
present_value = present_value - (fv/pow(1+rate,nper));
|
1612
|
-
|
1754
|
+
|
1613
1755
|
return EXCEL_NUMBER(present_value);
|
1614
1756
|
}
|
1615
1757
|
|
@@ -1617,7 +1759,7 @@ static ExcelValue pv_5(ExcelValue rate_v, ExcelValue nper_v, ExcelValue pmt_v, E
|
|
1617
1759
|
static ExcelValue power(ExcelValue a_v, ExcelValue b_v) {
|
1618
1760
|
CHECK_FOR_PASSED_ERROR(a_v)
|
1619
1761
|
CHECK_FOR_PASSED_ERROR(b_v)
|
1620
|
-
|
1762
|
+
|
1621
1763
|
NUMBER(a_v, a)
|
1622
1764
|
NUMBER(b_v, b)
|
1623
1765
|
CHECK_FOR_CONVERSION_ERROR
|
@@ -1676,48 +1818,48 @@ static ExcelValue rank_2(ExcelValue number_v, ExcelValue range_v) {
|
|
1676
1818
|
static ExcelValue excel_round(ExcelValue number_v, ExcelValue decimal_places_v) {
|
1677
1819
|
CHECK_FOR_PASSED_ERROR(number_v)
|
1678
1820
|
CHECK_FOR_PASSED_ERROR(decimal_places_v)
|
1679
|
-
|
1821
|
+
|
1680
1822
|
NUMBER(number_v, number)
|
1681
1823
|
NUMBER(decimal_places_v, decimal_places)
|
1682
1824
|
CHECK_FOR_CONVERSION_ERROR
|
1683
|
-
|
1825
|
+
|
1684
1826
|
double multiple = pow(10,decimal_places);
|
1685
|
-
|
1827
|
+
|
1686
1828
|
return EXCEL_NUMBER( round(number * multiple) / multiple );
|
1687
1829
|
}
|
1688
1830
|
|
1689
1831
|
static ExcelValue rounddown(ExcelValue number_v, ExcelValue decimal_places_v) {
|
1690
1832
|
CHECK_FOR_PASSED_ERROR(number_v)
|
1691
1833
|
CHECK_FOR_PASSED_ERROR(decimal_places_v)
|
1692
|
-
|
1834
|
+
|
1693
1835
|
NUMBER(number_v, number)
|
1694
1836
|
NUMBER(decimal_places_v, decimal_places)
|
1695
1837
|
CHECK_FOR_CONVERSION_ERROR
|
1696
|
-
|
1838
|
+
|
1697
1839
|
double multiple = pow(10,decimal_places);
|
1698
|
-
|
1699
|
-
return EXCEL_NUMBER( trunc(number * multiple) / multiple );
|
1840
|
+
|
1841
|
+
return EXCEL_NUMBER( trunc(number * multiple) / multiple );
|
1700
1842
|
}
|
1701
1843
|
|
1702
1844
|
static ExcelValue roundup(ExcelValue number_v, ExcelValue decimal_places_v) {
|
1703
1845
|
CHECK_FOR_PASSED_ERROR(number_v)
|
1704
1846
|
CHECK_FOR_PASSED_ERROR(decimal_places_v)
|
1705
|
-
|
1847
|
+
|
1706
1848
|
NUMBER(number_v, number)
|
1707
1849
|
NUMBER(decimal_places_v, decimal_places)
|
1708
1850
|
CHECK_FOR_CONVERSION_ERROR
|
1709
|
-
|
1851
|
+
|
1710
1852
|
double multiple = pow(10,decimal_places);
|
1711
1853
|
if(number < 0) return EXCEL_NUMBER( floor(number * multiple) / multiple );
|
1712
|
-
return EXCEL_NUMBER( ceil(number * multiple) / multiple );
|
1854
|
+
return EXCEL_NUMBER( ceil(number * multiple) / multiple );
|
1713
1855
|
}
|
1714
1856
|
|
1715
1857
|
static ExcelValue excel_int(ExcelValue number_v) {
|
1716
1858
|
CHECK_FOR_PASSED_ERROR(number_v)
|
1717
|
-
|
1859
|
+
|
1718
1860
|
NUMBER(number_v, number)
|
1719
1861
|
CHECK_FOR_CONVERSION_ERROR
|
1720
|
-
|
1862
|
+
|
1721
1863
|
return EXCEL_NUMBER(floor(number));
|
1722
1864
|
}
|
1723
1865
|
|
@@ -1747,7 +1889,7 @@ static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments) {
|
|
1747
1889
|
printf("Out of memory in string join");
|
1748
1890
|
exit(-1);
|
1749
1891
|
}
|
1750
|
-
must_free_current_string = 1;
|
1892
|
+
must_free_current_string = 1;
|
1751
1893
|
snprintf(current_string,20,"%g",current_v.number);
|
1752
1894
|
break;
|
1753
1895
|
case ExcelBoolean:
|
@@ -1796,7 +1938,7 @@ static ExcelValue subtotal(ExcelValue subtotal_type_v, int number_of_arguments,
|
|
1796
1938
|
CHECK_FOR_PASSED_ERROR(subtotal_type_v)
|
1797
1939
|
NUMBER(subtotal_type_v,subtotal_type)
|
1798
1940
|
CHECK_FOR_CONVERSION_ERROR
|
1799
|
-
|
1941
|
+
|
1800
1942
|
switch((int) subtotal_type) {
|
1801
1943
|
case 1:
|
1802
1944
|
case 101:
|
@@ -1829,22 +1971,22 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
|
|
1829
1971
|
// Set up the sum range
|
1830
1972
|
ExcelValue *original_range;
|
1831
1973
|
int original_range_rows, original_range_columns;
|
1832
|
-
|
1974
|
+
|
1833
1975
|
if(original_range_v.type == ExcelRange) {
|
1834
1976
|
original_range = original_range_v.array;
|
1835
1977
|
original_range_rows = original_range_v.rows;
|
1836
1978
|
original_range_columns = original_range_v.columns;
|
1837
1979
|
} else {
|
1838
1980
|
original_range = (ExcelValue*) new_excel_value_array(1);
|
1839
|
-
|
1981
|
+
original_range[0] = original_range_v;
|
1840
1982
|
original_range_rows = 1;
|
1841
1983
|
original_range_columns = 1;
|
1842
1984
|
}
|
1843
1985
|
|
1844
1986
|
// This is the filtered range
|
1845
1987
|
ExcelValue *filtered_range = new_excel_value_array(original_range_rows*original_range_columns);
|
1846
|
-
int number_of_filtered_values = 0;
|
1847
|
-
|
1988
|
+
int number_of_filtered_values = 0;
|
1989
|
+
|
1848
1990
|
// Then go through and set up the check ranges
|
1849
1991
|
if(number_of_arguments % 2 != 0) return VALUE;
|
1850
1992
|
int number_of_criteria = number_of_arguments / 2;
|
@@ -1865,7 +2007,7 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
|
|
1865
2007
|
criteria_range[i] = EXCEL_RANGE(tmp_array2,1,1);
|
1866
2008
|
}
|
1867
2009
|
}
|
1868
|
-
|
2010
|
+
|
1869
2011
|
// Now go through and set up the criteria
|
1870
2012
|
ExcelComparison *criteria = malloc(sizeof(ExcelComparison)*number_of_criteria); // freed at end of function
|
1871
2013
|
if(criteria == 0) {
|
@@ -1877,7 +2019,7 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
|
|
1877
2019
|
|
1878
2020
|
for(i = 0; i < number_of_criteria; i++) {
|
1879
2021
|
current_value = arguments[(i*2)+1];
|
1880
|
-
|
2022
|
+
|
1881
2023
|
if(current_value.type == ExcelString) {
|
1882
2024
|
s = current_value.string;
|
1883
2025
|
if(s[0] == '<') {
|
@@ -1916,14 +2058,14 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
|
|
1916
2058
|
criteria[i].comparator = EXCEL_STRING(new_comparator);
|
1917
2059
|
} else {
|
1918
2060
|
criteria[i].type = Equal;
|
1919
|
-
criteria[i].comparator = current_value;
|
2061
|
+
criteria[i].comparator = current_value;
|
1920
2062
|
}
|
1921
2063
|
} else {
|
1922
2064
|
criteria[i].type = Equal;
|
1923
2065
|
criteria[i].comparator = current_value;
|
1924
2066
|
}
|
1925
2067
|
}
|
1926
|
-
|
2068
|
+
|
1927
2069
|
int size = original_range_columns * original_range_rows;
|
1928
2070
|
int j;
|
1929
2071
|
int passed = 0;
|
@@ -1943,7 +2085,7 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
|
|
1943
2085
|
if(comparator.type == ExcelEmpty) {
|
1944
2086
|
comparator = ZERO;
|
1945
2087
|
}
|
1946
|
-
|
2088
|
+
|
1947
2089
|
switch(value_to_be_checked.type) {
|
1948
2090
|
case ExcelError: // Errors match only errors
|
1949
2091
|
if(comparison.type != Equal) passed = 0;
|
@@ -1984,16 +2126,16 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
|
|
1984
2126
|
break;
|
1985
2127
|
case LessThan:
|
1986
2128
|
if(value_to_be_checked.number >= number) passed = 0;
|
1987
|
-
break;
|
2129
|
+
break;
|
1988
2130
|
case LessThanOrEqual:
|
1989
2131
|
if(value_to_be_checked.number > number) passed = 0;
|
1990
|
-
break;
|
2132
|
+
break;
|
1991
2133
|
case NotEqual:
|
1992
2134
|
if(value_to_be_checked.number == number) passed = 0;
|
1993
|
-
break;
|
2135
|
+
break;
|
1994
2136
|
case MoreThanOrEqual:
|
1995
2137
|
if(value_to_be_checked.number < number) passed = 0;
|
1996
|
-
break;
|
2138
|
+
break;
|
1997
2139
|
case MoreThan:
|
1998
2140
|
if(value_to_be_checked.number <= number) passed = 0;
|
1999
2141
|
break;
|
@@ -2034,16 +2176,16 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
|
|
2034
2176
|
break;
|
2035
2177
|
case LessThan:
|
2036
2178
|
if(less_than(value_to_be_checked,comparator).number == 0) passed = 0;
|
2037
|
-
break;
|
2179
|
+
break;
|
2038
2180
|
case LessThanOrEqual:
|
2039
2181
|
if(less_than_or_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
2040
|
-
break;
|
2182
|
+
break;
|
2041
2183
|
case NotEqual:
|
2042
2184
|
if(not_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
2043
|
-
break;
|
2185
|
+
break;
|
2044
2186
|
case MoreThanOrEqual:
|
2045
2187
|
if(more_than_or_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
2046
|
-
break;
|
2188
|
+
break;
|
2047
2189
|
case MoreThan:
|
2048
2190
|
if(more_than(value_to_be_checked,comparator).number == 0) passed = 0;
|
2049
2191
|
break;
|
@@ -2055,7 +2197,7 @@ static ExcelValue filter_range(ExcelValue original_range_v, int number_of_argume
|
|
2055
2197
|
break;
|
2056
2198
|
case ExcelRange:
|
2057
2199
|
free(criteria);
|
2058
|
-
return VALUE;
|
2200
|
+
return VALUE;
|
2059
2201
|
}
|
2060
2202
|
if(passed == 0) break;
|
2061
2203
|
}
|
@@ -2080,6 +2222,245 @@ static ExcelValue sumifs(ExcelValue sum_range_v, int number_of_arguments, ExcelV
|
|
2080
2222
|
return sum(1,&filtered_range);
|
2081
2223
|
}
|
2082
2224
|
|
2225
|
+
static ExcelValue countifs(int number_of_arguments, ExcelValue *arguments) {
|
2226
|
+
if(number_of_arguments < 2) { return NA;}
|
2227
|
+
// Set up the sum range
|
2228
|
+
ExcelValue range = arguments[0];
|
2229
|
+
int rows, columns;
|
2230
|
+
|
2231
|
+
if(range.type == ExcelRange) {
|
2232
|
+
rows = range.rows;
|
2233
|
+
columns = range.columns;
|
2234
|
+
} else {
|
2235
|
+
rows = 1;
|
2236
|
+
columns = 1;
|
2237
|
+
}
|
2238
|
+
|
2239
|
+
int count = 0;
|
2240
|
+
|
2241
|
+
// Then go through and set up the check ranges
|
2242
|
+
if(number_of_arguments % 2 != 0) return VALUE;
|
2243
|
+
int number_of_criteria = number_of_arguments / 2;
|
2244
|
+
ExcelValue *criteria_range = (ExcelValue*) new_excel_value_array(number_of_criteria);
|
2245
|
+
ExcelValue current_value;
|
2246
|
+
int i;
|
2247
|
+
for(i = 0; i < number_of_criteria; i++) {
|
2248
|
+
current_value = arguments[i*2];
|
2249
|
+
if(current_value.type == ExcelRange) {
|
2250
|
+
criteria_range[i] = current_value;
|
2251
|
+
if(current_value.rows != rows) return VALUE;
|
2252
|
+
if(current_value.columns != columns) return VALUE;
|
2253
|
+
} else {
|
2254
|
+
if(rows != 1) return VALUE;
|
2255
|
+
if(columns != 1) return VALUE;
|
2256
|
+
ExcelValue *tmp_array2 = (ExcelValue*) new_excel_value_array(1);
|
2257
|
+
tmp_array2[0] = current_value;
|
2258
|
+
criteria_range[i] = EXCEL_RANGE(tmp_array2,1,1);
|
2259
|
+
}
|
2260
|
+
}
|
2261
|
+
|
2262
|
+
// Now go through and set up the criteria
|
2263
|
+
ExcelComparison *criteria = malloc(sizeof(ExcelComparison)*number_of_criteria); // freed at end of function
|
2264
|
+
if(criteria == 0) {
|
2265
|
+
printf("Out of memory in filter_range\n");
|
2266
|
+
exit(-1);
|
2267
|
+
}
|
2268
|
+
char *s;
|
2269
|
+
char *new_comparator;
|
2270
|
+
|
2271
|
+
for(i = 0; i < number_of_criteria; i++) {
|
2272
|
+
current_value = arguments[(i*2)+1];
|
2273
|
+
|
2274
|
+
if(current_value.type == ExcelString) {
|
2275
|
+
s = current_value.string;
|
2276
|
+
if(s[0] == '<') {
|
2277
|
+
if( s[1] == '>') {
|
2278
|
+
new_comparator = strndup(s+2,strlen(s)-2);
|
2279
|
+
free_later(new_comparator);
|
2280
|
+
criteria[i].type = NotEqual;
|
2281
|
+
criteria[i].comparator = EXCEL_STRING(new_comparator);
|
2282
|
+
} else if(s[1] == '=') {
|
2283
|
+
new_comparator = strndup(s+2,strlen(s)-2);
|
2284
|
+
free_later(new_comparator);
|
2285
|
+
criteria[i].type = LessThanOrEqual;
|
2286
|
+
criteria[i].comparator = EXCEL_STRING(new_comparator);
|
2287
|
+
} else {
|
2288
|
+
new_comparator = strndup(s+1,strlen(s)-1);
|
2289
|
+
free_later(new_comparator);
|
2290
|
+
criteria[i].type = LessThan;
|
2291
|
+
criteria[i].comparator = EXCEL_STRING(new_comparator);
|
2292
|
+
}
|
2293
|
+
} else if(s[0] == '>') {
|
2294
|
+
if(s[1] == '=') {
|
2295
|
+
new_comparator = strndup(s+2,strlen(s)-2);
|
2296
|
+
free_later(new_comparator);
|
2297
|
+
criteria[i].type = MoreThanOrEqual;
|
2298
|
+
criteria[i].comparator = EXCEL_STRING(new_comparator);
|
2299
|
+
} else {
|
2300
|
+
new_comparator = strndup(s+1,strlen(s)-1);
|
2301
|
+
free_later(new_comparator);
|
2302
|
+
criteria[i].type = MoreThan;
|
2303
|
+
criteria[i].comparator = EXCEL_STRING(new_comparator);
|
2304
|
+
}
|
2305
|
+
} else if(s[0] == '=') {
|
2306
|
+
new_comparator = strndup(s+1,strlen(s)-1);
|
2307
|
+
free_later(new_comparator);
|
2308
|
+
criteria[i].type = Equal;
|
2309
|
+
criteria[i].comparator = EXCEL_STRING(new_comparator);
|
2310
|
+
} else {
|
2311
|
+
criteria[i].type = Equal;
|
2312
|
+
criteria[i].comparator = current_value;
|
2313
|
+
}
|
2314
|
+
} else {
|
2315
|
+
criteria[i].type = Equal;
|
2316
|
+
criteria[i].comparator = current_value;
|
2317
|
+
}
|
2318
|
+
}
|
2319
|
+
|
2320
|
+
int size = columns * rows;
|
2321
|
+
int j;
|
2322
|
+
int passed = 0;
|
2323
|
+
ExcelValue value_to_be_checked;
|
2324
|
+
ExcelComparison comparison;
|
2325
|
+
ExcelValue comparator;
|
2326
|
+
double number;
|
2327
|
+
// For each cell in the sum range
|
2328
|
+
for(j=0; j < size; j++ ) {
|
2329
|
+
passed = 1;
|
2330
|
+
for(i=0; i < number_of_criteria; i++) {
|
2331
|
+
value_to_be_checked = ((ExcelValue *) ((ExcelValue) criteria_range[i]).array)[j];
|
2332
|
+
comparison = criteria[i];
|
2333
|
+
comparator = comparison.comparator;
|
2334
|
+
|
2335
|
+
// For the purposes of comparison, treates a blank criteria as matching zeros.
|
2336
|
+
if(comparator.type == ExcelEmpty) {
|
2337
|
+
comparator = ZERO;
|
2338
|
+
}
|
2339
|
+
|
2340
|
+
switch(value_to_be_checked.type) {
|
2341
|
+
case ExcelError: // Errors match only errors
|
2342
|
+
if(comparison.type != Equal) passed = 0;
|
2343
|
+
if(comparator.type != ExcelError) passed = 0;
|
2344
|
+
if(value_to_be_checked.number != comparator.number) passed = 0;
|
2345
|
+
break;
|
2346
|
+
case ExcelBoolean: // Booleans match only booleans (FIXME: I think?)
|
2347
|
+
if(comparison.type != Equal) passed = 0;
|
2348
|
+
if(comparator.type != ExcelBoolean ) passed = 0;
|
2349
|
+
if(value_to_be_checked.number != comparator.number) passed = 0;
|
2350
|
+
break;
|
2351
|
+
case ExcelEmpty:
|
2352
|
+
// if(comparator.type == ExcelEmpty) break; // FIXME: Huh? In excel blank doesn't match blank?!
|
2353
|
+
if(comparator.type != ExcelString) {
|
2354
|
+
passed = 0;
|
2355
|
+
break;
|
2356
|
+
} else {
|
2357
|
+
if(strlen(comparator.string) != 0) passed = 0; // Empty strings match blanks.
|
2358
|
+
break;
|
2359
|
+
}
|
2360
|
+
case ExcelNumber:
|
2361
|
+
if(comparator.type == ExcelNumber) {
|
2362
|
+
number = comparator.number;
|
2363
|
+
} else if(comparator.type == ExcelString) {
|
2364
|
+
number = number_from(comparator);
|
2365
|
+
if(conversion_error == 1) {
|
2366
|
+
conversion_error = 0;
|
2367
|
+
passed = 0;
|
2368
|
+
break;
|
2369
|
+
}
|
2370
|
+
} else {
|
2371
|
+
passed = 0;
|
2372
|
+
break;
|
2373
|
+
}
|
2374
|
+
switch(comparison.type) {
|
2375
|
+
case Equal:
|
2376
|
+
if(value_to_be_checked.number != number) passed = 0;
|
2377
|
+
break;
|
2378
|
+
case LessThan:
|
2379
|
+
if(value_to_be_checked.number >= number) passed = 0;
|
2380
|
+
break;
|
2381
|
+
case LessThanOrEqual:
|
2382
|
+
if(value_to_be_checked.number > number) passed = 0;
|
2383
|
+
break;
|
2384
|
+
case NotEqual:
|
2385
|
+
if(value_to_be_checked.number == number) passed = 0;
|
2386
|
+
break;
|
2387
|
+
case MoreThanOrEqual:
|
2388
|
+
if(value_to_be_checked.number < number) passed = 0;
|
2389
|
+
break;
|
2390
|
+
case MoreThan:
|
2391
|
+
if(value_to_be_checked.number <= number) passed = 0;
|
2392
|
+
break;
|
2393
|
+
}
|
2394
|
+
break;
|
2395
|
+
case ExcelString:
|
2396
|
+
// First case, the comparator is a number, simplification is that it can only be equal
|
2397
|
+
if(comparator.type == ExcelNumber) {
|
2398
|
+
if(comparison.type != Equal) {
|
2399
|
+
printf("This shouldn't be possible?");
|
2400
|
+
passed = 0;
|
2401
|
+
break;
|
2402
|
+
}
|
2403
|
+
|
2404
|
+
// Special case, empty strings don't match zeros here
|
2405
|
+
if(strlen(value_to_be_checked.string) == 0) {
|
2406
|
+
passed = 0;
|
2407
|
+
break;
|
2408
|
+
}
|
2409
|
+
|
2410
|
+
number = number_from(value_to_be_checked);
|
2411
|
+
if(conversion_error == 1) {
|
2412
|
+
conversion_error = 0;
|
2413
|
+
passed = 0;
|
2414
|
+
break;
|
2415
|
+
}
|
2416
|
+
if(number != comparator.number) {
|
2417
|
+
passed = 0;
|
2418
|
+
break;
|
2419
|
+
} else {
|
2420
|
+
break;
|
2421
|
+
}
|
2422
|
+
// Second case, the comparator is also a string, so need to be able to do full range of tests
|
2423
|
+
} else if(comparator.type == ExcelString) {
|
2424
|
+
switch(comparison.type) {
|
2425
|
+
case Equal:
|
2426
|
+
if(excel_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
2427
|
+
break;
|
2428
|
+
case LessThan:
|
2429
|
+
if(less_than(value_to_be_checked,comparator).number == 0) passed = 0;
|
2430
|
+
break;
|
2431
|
+
case LessThanOrEqual:
|
2432
|
+
if(less_than_or_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
2433
|
+
break;
|
2434
|
+
case NotEqual:
|
2435
|
+
if(not_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
2436
|
+
break;
|
2437
|
+
case MoreThanOrEqual:
|
2438
|
+
if(more_than_or_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
2439
|
+
break;
|
2440
|
+
case MoreThan:
|
2441
|
+
if(more_than(value_to_be_checked,comparator).number == 0) passed = 0;
|
2442
|
+
break;
|
2443
|
+
}
|
2444
|
+
} else {
|
2445
|
+
passed = 0;
|
2446
|
+
break;
|
2447
|
+
}
|
2448
|
+
break;
|
2449
|
+
case ExcelRange:
|
2450
|
+
free(criteria);
|
2451
|
+
return VALUE;
|
2452
|
+
}
|
2453
|
+
if(passed == 0) break;
|
2454
|
+
}
|
2455
|
+
if(passed == 1) {
|
2456
|
+
count += 1;
|
2457
|
+
}
|
2458
|
+
}
|
2459
|
+
// Tidy up
|
2460
|
+
free(criteria);
|
2461
|
+
return EXCEL_NUMBER(count);
|
2462
|
+
}
|
2463
|
+
|
2083
2464
|
static ExcelValue averageifs(ExcelValue average_range_v, int number_of_arguments, ExcelValue *arguments) {
|
2084
2465
|
ExcelValue filtered_range = filter_range(average_range_v, number_of_arguments, arguments);
|
2085
2466
|
return average(1,&filtered_range);
|
@@ -2097,7 +2478,7 @@ static ExcelValue sumif_2(ExcelValue check_range_v, ExcelValue criteria_v) {
|
|
2097
2478
|
|
2098
2479
|
static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments) {
|
2099
2480
|
if(number_of_arguments <1) return VALUE;
|
2100
|
-
|
2481
|
+
|
2101
2482
|
int a;
|
2102
2483
|
int i;
|
2103
2484
|
int j;
|
@@ -2111,7 +2492,7 @@ static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments) {
|
|
2111
2492
|
}
|
2112
2493
|
double product = 1;
|
2113
2494
|
double sum = 0;
|
2114
|
-
|
2495
|
+
|
2115
2496
|
// Find out dimensions of first argument
|
2116
2497
|
if(arguments[0].type == ExcelRange) {
|
2117
2498
|
rows = arguments[0].rows;
|
@@ -2143,7 +2524,7 @@ static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments) {
|
|
2143
2524
|
break;
|
2144
2525
|
}
|
2145
2526
|
}
|
2146
|
-
|
2527
|
+
|
2147
2528
|
for(i=0;i<rows;i++) {
|
2148
2529
|
for(j=0;j<columns;j++) {
|
2149
2530
|
product = 1;
|
@@ -2162,6 +2543,71 @@ static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments) {
|
|
2162
2543
|
return EXCEL_NUMBER(sum);
|
2163
2544
|
}
|
2164
2545
|
|
2546
|
+
static ExcelValue product(int number_of_arguments, ExcelValue *arguments) {
|
2547
|
+
if(number_of_arguments <1) return VALUE;
|
2548
|
+
|
2549
|
+
int a,b;
|
2550
|
+
ExcelValue sub_total;
|
2551
|
+
ExcelValue current_value;
|
2552
|
+
int sub_total_array_size;
|
2553
|
+
ExcelValue *sub_total_array;
|
2554
|
+
ExcelValue sub_total_value;
|
2555
|
+
double total = 0;
|
2556
|
+
|
2557
|
+
// Extract arrays from each of the given ranges, checking for errors and bounds as we go
|
2558
|
+
for(a=0;a<number_of_arguments;a++) {
|
2559
|
+
current_value = arguments[a];
|
2560
|
+
switch(current_value.type) {
|
2561
|
+
case ExcelRange:
|
2562
|
+
sub_total_array_size = current_value.rows * current_value.columns;
|
2563
|
+
sub_total_array = current_value.array;
|
2564
|
+
// We don't use recursion, because we need to check if
|
2565
|
+
// the result is 0 becaues a zero, or zero because all blank.
|
2566
|
+
for(b=0;b<sub_total_array_size;b++) {
|
2567
|
+
sub_total_value = sub_total_array[b];
|
2568
|
+
switch(sub_total_value.type) {
|
2569
|
+
case ExcelError:
|
2570
|
+
return sub_total_value;
|
2571
|
+
break;
|
2572
|
+
|
2573
|
+
case ExcelNumber:
|
2574
|
+
// We do this rather than starting with total = 1
|
2575
|
+
// so that the product of all blanks is zero
|
2576
|
+
if(total == 0) {
|
2577
|
+
total = sub_total_value.number;
|
2578
|
+
} else {
|
2579
|
+
total *= sub_total_value.number;
|
2580
|
+
}
|
2581
|
+
break;
|
2582
|
+
|
2583
|
+
default:
|
2584
|
+
// Skip
|
2585
|
+
break;
|
2586
|
+
}
|
2587
|
+
}
|
2588
|
+
break;
|
2589
|
+
|
2590
|
+
case ExcelError:
|
2591
|
+
return current_value;
|
2592
|
+
break;
|
2593
|
+
|
2594
|
+
case ExcelNumber:
|
2595
|
+
if(total == 0) {
|
2596
|
+
total = current_value.number;
|
2597
|
+
} else {
|
2598
|
+
total *= current_value.number;
|
2599
|
+
}
|
2600
|
+
break;
|
2601
|
+
|
2602
|
+
default:
|
2603
|
+
// Skip
|
2604
|
+
break;
|
2605
|
+
}
|
2606
|
+
}
|
2607
|
+
|
2608
|
+
return EXCEL_NUMBER(total);
|
2609
|
+
}
|
2610
|
+
|
2165
2611
|
// FIXME: This could do with being done properly, rather than
|
2166
2612
|
// on a case by case basis.
|
2167
2613
|
static ExcelValue text(ExcelValue number_v, ExcelValue format_v) {
|
@@ -2189,7 +2635,7 @@ static ExcelValue text(ExcelValue number_v, ExcelValue format_v) {
|
|
2189
2635
|
s = number_v.string;
|
2190
2636
|
if (s == NULL || *s == '\0' || isspace(*s)) {
|
2191
2637
|
number_v = ZERO;
|
2192
|
-
}
|
2638
|
+
}
|
2193
2639
|
n = strtod (s, &p);
|
2194
2640
|
if(*p == '\0') {
|
2195
2641
|
number_v = EXCEL_NUMBER(n);
|
@@ -2204,7 +2650,7 @@ static ExcelValue text(ExcelValue number_v, ExcelValue format_v) {
|
|
2204
2650
|
return format_v;
|
2205
2651
|
}
|
2206
2652
|
|
2207
|
-
// FIXME: Too little?
|
2653
|
+
// FIXME: Too little?
|
2208
2654
|
s = malloc(100);
|
2209
2655
|
setlocale(LC_ALL,"");
|
2210
2656
|
|
@@ -2259,17 +2705,17 @@ static ExcelValue vlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, E
|
|
2259
2705
|
match_type_v.type = ExcelBoolean;
|
2260
2706
|
}
|
2261
2707
|
if(match_type_v.type != ExcelBoolean) return NA;
|
2262
|
-
|
2708
|
+
|
2263
2709
|
int i;
|
2264
2710
|
int last_good_match = 0;
|
2265
2711
|
int rows = lookup_table_v.rows;
|
2266
2712
|
int columns = lookup_table_v.columns;
|
2267
2713
|
ExcelValue *array = lookup_table_v.array;
|
2268
2714
|
ExcelValue possible_match_v;
|
2269
|
-
|
2715
|
+
|
2270
2716
|
if(column_number_v.number > columns) return REF;
|
2271
2717
|
if(column_number_v.number < 1) return VALUE;
|
2272
|
-
|
2718
|
+
|
2273
2719
|
if(match_type_v.number == false) { // Exact match required
|
2274
2720
|
for(i=0; i< rows; i++) {
|
2275
2721
|
possible_match_v = array[i*columns];
|
@@ -2289,7 +2735,7 @@ static ExcelValue vlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, E
|
|
2289
2735
|
last_good_match = i;
|
2290
2736
|
}
|
2291
2737
|
}
|
2292
|
-
return array[(last_good_match*columns)+(((int) column_number_v.number) - 1)];
|
2738
|
+
return array[(last_good_match*columns)+(((int) column_number_v.number) - 1)];
|
2293
2739
|
}
|
2294
2740
|
return NA;
|
2295
2741
|
}
|
@@ -2311,17 +2757,17 @@ static ExcelValue hlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, E
|
|
2311
2757
|
match_type_v.type = ExcelBoolean;
|
2312
2758
|
}
|
2313
2759
|
if(match_type_v.type != ExcelBoolean) return NA;
|
2314
|
-
|
2760
|
+
|
2315
2761
|
int i;
|
2316
2762
|
int last_good_match = 0;
|
2317
2763
|
int rows = lookup_table_v.rows;
|
2318
2764
|
int columns = lookup_table_v.columns;
|
2319
2765
|
ExcelValue *array = lookup_table_v.array;
|
2320
2766
|
ExcelValue possible_match_v;
|
2321
|
-
|
2767
|
+
|
2322
2768
|
if(row_number_v.number > rows) return REF;
|
2323
2769
|
if(row_number_v.number < 1) return VALUE;
|
2324
|
-
|
2770
|
+
|
2325
2771
|
if(match_type_v.number == false) { // Exact match required
|
2326
2772
|
for(i=0; i< columns; i++) {
|
2327
2773
|
possible_match_v = array[i];
|
@@ -2354,14 +2800,116 @@ static ExcelValue value(ExcelValue string_v) {
|
|
2354
2800
|
return EXCEL_NUMBER(a);
|
2355
2801
|
}
|
2356
2802
|
|
2803
|
+
static ExcelValue scurve_4(ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration) {
|
2804
|
+
ExcelValue startYear = EXCEL_NUMBER(2018);
|
2805
|
+
return scurve(currentYear, startValue, endValue, duration, startYear);
|
2806
|
+
}
|
2807
|
+
|
2808
|
+
static ExcelValue halfscurve_4(ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration) {
|
2809
|
+
ExcelValue startYear = EXCEL_NUMBER(2018);
|
2810
|
+
return halfscurve(currentYear, startValue, endValue, duration, startYear);
|
2811
|
+
}
|
2812
|
+
|
2813
|
+
static ExcelValue lcurve_4(ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration) {
|
2814
|
+
ExcelValue startYear = EXCEL_NUMBER(2018);
|
2815
|
+
return lcurve(currentYear, startValue, endValue, duration, startYear);
|
2816
|
+
}
|
2817
|
+
|
2818
|
+
static ExcelValue curve_5(ExcelValue curveType, ExcelValue currentYear, ExcelValue startValue, ExcelValue endValue, ExcelValue duration) {
|
2819
|
+
ExcelValue startYear = EXCEL_NUMBER(2018);
|
2820
|
+
return curve(curveType, currentYear, startValue, endValue, duration, startYear);
|
2821
|
+
}
|
2822
|
+
|
2823
|
+
static ExcelValue scurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
|
2824
|
+
|
2825
|
+
NUMBER(currentYear_v, currentYear)
|
2826
|
+
NUMBER(startValue_v, startValue)
|
2827
|
+
NUMBER(endValue_v, endValue)
|
2828
|
+
NUMBER(duration_v, duration)
|
2829
|
+
NUMBER(startYear_v, startYear)
|
2830
|
+
CHECK_FOR_CONVERSION_ERROR
|
2831
|
+
|
2832
|
+
if(currentYear < startYear) {
|
2833
|
+
return startValue_v;
|
2834
|
+
}
|
2835
|
+
double x = (currentYear - startYear) / duration;
|
2836
|
+
double x0 = 0.0;
|
2837
|
+
double a = endValue - startValue;
|
2838
|
+
double sc = 0.999;
|
2839
|
+
double eps = 1.0 - sc;
|
2840
|
+
double mu = 0.5;
|
2841
|
+
double beta = (mu - 1.0) / log(1.0 / sc - 1);
|
2842
|
+
double scurve = a * (pow((exp(-(x - mu) / beta) + 1),-1) - pow((exp(-(x0 - mu) / beta) + 1),-1)) + startValue;
|
2843
|
+
return EXCEL_NUMBER(scurve);
|
2844
|
+
}
|
2845
|
+
|
2846
|
+
static ExcelValue halfscurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
|
2847
|
+
|
2848
|
+
NUMBER(currentYear_v, currentYear)
|
2849
|
+
NUMBER(startValue_v, startValue)
|
2850
|
+
NUMBER(endValue_v, endValue)
|
2851
|
+
NUMBER(duration_v, duration)
|
2852
|
+
NUMBER(startYear_v, startYear)
|
2853
|
+
CHECK_FOR_CONVERSION_ERROR
|
2854
|
+
|
2855
|
+
if(currentYear < startYear) {
|
2856
|
+
return startValue_v;
|
2857
|
+
}
|
2858
|
+
|
2859
|
+
ExcelValue newCurrentYear = EXCEL_NUMBER(currentYear + duration);
|
2860
|
+
ExcelValue newDuration = EXCEL_NUMBER(duration *2);
|
2861
|
+
ExcelValue result_v = scurve(newCurrentYear, startValue_v, endValue_v, newDuration, startYear_v);
|
2862
|
+
|
2863
|
+
NUMBER(result_v, result)
|
2864
|
+
CHECK_FOR_CONVERSION_ERROR
|
2865
|
+
|
2866
|
+
return EXCEL_NUMBER(result -((endValue - startValue)/2.0));
|
2867
|
+
}
|
2868
|
+
|
2869
|
+
static ExcelValue lcurve(ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
|
2870
|
+
|
2871
|
+
NUMBER(currentYear_v, currentYear)
|
2872
|
+
NUMBER(startValue_v, startValue)
|
2873
|
+
NUMBER(endValue_v, endValue)
|
2874
|
+
NUMBER(duration_v, duration)
|
2875
|
+
NUMBER(startYear_v, startYear)
|
2876
|
+
CHECK_FOR_CONVERSION_ERROR
|
2877
|
+
|
2878
|
+
if(currentYear > (startYear + duration)) {
|
2879
|
+
return endValue_v;
|
2880
|
+
}
|
2881
|
+
|
2882
|
+
if(currentYear < startYear) {
|
2883
|
+
return startValue_v;
|
2884
|
+
}
|
2885
|
+
|
2886
|
+
double result = startValue + (((endValue - startValue) / duration) * (currentYear - startYear));
|
2887
|
+
return EXCEL_NUMBER(result);
|
2888
|
+
}
|
2889
|
+
|
2890
|
+
static ExcelValue curve(ExcelValue type_v, ExcelValue currentYear_v, ExcelValue startValue_v, ExcelValue endValue_v, ExcelValue duration_v, ExcelValue startYear_v) {
|
2891
|
+
|
2892
|
+
if(type_v.type == ExcelString && strcasecmp(type_v.string, "s") == 0 ) {
|
2893
|
+
return scurve(currentYear_v, startValue_v, endValue_v, duration_v, startYear_v);
|
2894
|
+
}
|
2895
|
+
|
2896
|
+
if(type_v.type == ExcelString && strcasecmp(type_v.string, "hs") == 0 ) {
|
2897
|
+
return halfscurve(currentYear_v, startValue_v, endValue_v, duration_v, startYear_v);
|
2898
|
+
}
|
2899
|
+
|
2900
|
+
return lcurve(currentYear_v, startValue_v, endValue_v, duration_v, startYear_v);
|
2901
|
+
}
|
2902
|
+
|
2903
|
+
|
2904
|
+
|
2357
2905
|
// Allows numbers to be 0.1% different
|
2358
2906
|
static ExcelValue roughly_equal(ExcelValue a_v, ExcelValue b_v) {
|
2359
2907
|
|
2360
2908
|
if(a_v.type == ExcelEmpty && b_v.type == ExcelNumber && b_v.number == 0) return TRUE;
|
2361
2909
|
if(b_v.type == ExcelEmpty && a_v.type == ExcelNumber && a_v.number == 0) return TRUE;
|
2362
|
-
|
2910
|
+
|
2363
2911
|
if(a_v.type != b_v.type) return FALSE;
|
2364
|
-
|
2912
|
+
|
2365
2913
|
float epsilon, difference;
|
2366
2914
|
|
2367
2915
|
switch (a_v.type) {
|
@@ -2378,8 +2926,8 @@ static ExcelValue roughly_equal(ExcelValue a_v, ExcelValue b_v) {
|
|
2378
2926
|
if(difference <= epsilon) return TRUE;
|
2379
2927
|
// For debuging: printf("a: %e b:%e d: %e e: %e", a_v.number, b_v.number, difference, epsilon);
|
2380
2928
|
return FALSE;
|
2381
|
-
case ExcelBoolean:
|
2382
|
-
case ExcelEmpty:
|
2929
|
+
case ExcelBoolean:
|
2930
|
+
case ExcelEmpty:
|
2383
2931
|
if(a_v.number != b_v.number) return FALSE;
|
2384
2932
|
return TRUE;
|
2385
2933
|
case ExcelString:
|
@@ -2393,7 +2941,7 @@ static ExcelValue roughly_equal(ExcelValue a_v, ExcelValue b_v) {
|
|
2393
2941
|
}
|
2394
2942
|
return FALSE;
|
2395
2943
|
}
|
2396
|
-
|
2944
|
+
|
2397
2945
|
|
2398
2946
|
static void assert_equal(ExcelValue expected, ExcelValue actual, char location[]) {
|
2399
2947
|
ExcelValue comparison = roughly_equal(actual, expected);
|
@@ -2408,5 +2956,3 @@ static void assert_equal(ExcelValue expected, ExcelValue actual, char location[]
|
|
2408
2956
|
putchar('\n');
|
2409
2957
|
}
|
2410
2958
|
}
|
2411
|
-
|
2412
|
-
|