excel_to_code 0.3.17 → 0.3.18.beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
|