excel_to_code 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +41 -0
- data/bin/excel_to_c +63 -0
- data/bin/excel_to_ruby +9 -0
- data/src/commands.rb +2 -0
- data/src/commands/excel_to_c.rb +858 -0
- data/src/commands/excel_to_ruby.rb +620 -0
- data/src/compile.rb +2 -0
- data/src/compile/c.rb +5 -0
- data/src/compile/c/compile_to_c.rb +62 -0
- data/src/compile/c/compile_to_c_header.rb +26 -0
- data/src/compile/c/compile_to_c_unit_test.rb +42 -0
- data/src/compile/c/excel_to_c_runtime.c +2029 -0
- data/src/compile/c/map_formulae_to_c.rb +184 -0
- data/src/compile/c/map_sheet_names_to_c_names.rb +19 -0
- data/src/compile/c/map_values_to_c.rb +85 -0
- data/src/compile/c/map_values_to_c_structs.rb +37 -0
- data/src/compile/ruby.rb +3 -0
- data/src/compile/ruby/compile_to_ruby.rb +33 -0
- data/src/compile/ruby/compile_to_ruby_unit_test.rb +28 -0
- data/src/compile/ruby/excel_to_ruby_runtime.rb +1 -0
- data/src/compile/ruby/map_formulae_to_ruby.rb +95 -0
- data/src/compile/ruby/map_sheet_names_to_ruby_names.rb +19 -0
- data/src/compile/ruby/map_values_to_ruby.rb +65 -0
- data/src/excel.rb +5 -0
- data/src/excel/area.rb +93 -0
- data/src/excel/excel_functions.rb +84 -0
- data/src/excel/excel_functions/abs.rb +14 -0
- data/src/excel/excel_functions/add.rb +18 -0
- data/src/excel/excel_functions/and.rb +30 -0
- data/src/excel/excel_functions/apply_to_range.rb +17 -0
- data/src/excel/excel_functions/average.rb +12 -0
- data/src/excel/excel_functions/choose.rb +18 -0
- data/src/excel/excel_functions/cosh.rb +9 -0
- data/src/excel/excel_functions/count.rb +9 -0
- data/src/excel/excel_functions/counta.rb +8 -0
- data/src/excel/excel_functions/divide.rb +23 -0
- data/src/excel/excel_functions/excel_equal.rb +20 -0
- data/src/excel/excel_functions/excel_if.rb +8 -0
- data/src/excel/excel_functions/excel_match.rb +51 -0
- data/src/excel/excel_functions/find.rb +39 -0
- data/src/excel/excel_functions/iferror.rb +10 -0
- data/src/excel/excel_functions/index.rb +48 -0
- data/src/excel/excel_functions/left.rb +12 -0
- data/src/excel/excel_functions/less_than.rb +26 -0
- data/src/excel/excel_functions/less_than_or_equal.rb +26 -0
- data/src/excel/excel_functions/max.rb +12 -0
- data/src/excel/excel_functions/min.rb +12 -0
- data/src/excel/excel_functions/mod.rb +15 -0
- data/src/excel/excel_functions/more_than.rb +26 -0
- data/src/excel/excel_functions/more_than_or_equal.rb +26 -0
- data/src/excel/excel_functions/multiply.rb +24 -0
- data/src/excel/excel_functions/negative.rb +12 -0
- data/src/excel/excel_functions/not_equal.rb +19 -0
- data/src/excel/excel_functions/number_argument.rb +30 -0
- data/src/excel/excel_functions/pi.rb +7 -0
- data/src/excel/excel_functions/pmt.rb +16 -0
- data/src/excel/excel_functions/power.rb +18 -0
- data/src/excel/excel_functions/round.rb +13 -0
- data/src/excel/excel_functions/rounddown.rb +14 -0
- data/src/excel/excel_functions/roundup.rb +17 -0
- data/src/excel/excel_functions/string_join.rb +19 -0
- data/src/excel/excel_functions/subtotal.rb +13 -0
- data/src/excel/excel_functions/subtract.rb +18 -0
- data/src/excel/excel_functions/sum.rb +8 -0
- data/src/excel/excel_functions/sumif.rb +7 -0
- data/src/excel/excel_functions/sumifs.rb +74 -0
- data/src/excel/excel_functions/sumproduct.rb +32 -0
- data/src/excel/excel_functions/vlookup.rb +49 -0
- data/src/excel/formula_peg.rb +238 -0
- data/src/excel/formula_peg.txt +45 -0
- data/src/excel/reference.rb +56 -0
- data/src/excel/table.rb +108 -0
- data/src/excel_to_code.rb +7 -0
- data/src/extract.rb +13 -0
- data/src/extract/check_for_unknown_functions.rb +20 -0
- data/src/extract/extract_array_formulae.rb +23 -0
- data/src/extract/extract_formulae.rb +36 -0
- data/src/extract/extract_named_references.rb +38 -0
- data/src/extract/extract_relationships.rb +10 -0
- data/src/extract/extract_shared_formulae.rb +23 -0
- data/src/extract/extract_shared_strings.rb +20 -0
- data/src/extract/extract_simple_formulae.rb +18 -0
- data/src/extract/extract_table.rb +24 -0
- data/src/extract/extract_values.rb +29 -0
- data/src/extract/extract_worksheet_dimensions.rb +11 -0
- data/src/extract/extract_worksheet_names.rb +10 -0
- data/src/extract/extract_worksheet_table_relationships.rb +10 -0
- data/src/extract/simple_extract_from_xml.rb +19 -0
- data/src/rewrite.rb +10 -0
- data/src/rewrite/ast_copy_formula.rb +42 -0
- data/src/rewrite/ast_expand_array_formulae.rb +180 -0
- data/src/rewrite/rewrite_array_formulae.rb +71 -0
- data/src/rewrite/rewrite_array_formulae_to_arrays.rb +18 -0
- data/src/rewrite/rewrite_cell_references_to_include_sheet.rb +56 -0
- data/src/rewrite/rewrite_formulae_to_ast.rb +24 -0
- data/src/rewrite/rewrite_merge_formulae_and_values.rb +18 -0
- data/src/rewrite/rewrite_relationship_id_to_filename.rb +22 -0
- data/src/rewrite/rewrite_shared_formulae.rb +38 -0
- data/src/rewrite/rewrite_values_to_ast.rb +28 -0
- data/src/rewrite/rewrite_whole_row_column_references_to_areas.rb +90 -0
- data/src/rewrite/rewrite_worksheet_names.rb +20 -0
- data/src/simplify.rb +16 -0
- data/src/simplify/count_formula_references.rb +58 -0
- data/src/simplify/identify_dependencies.rb +56 -0
- data/src/simplify/identify_repeated_formula_elements.rb +37 -0
- data/src/simplify/inline_formulae.rb +77 -0
- data/src/simplify/map_formulae_to_values.rb +157 -0
- data/src/simplify/remove_cells.rb +18 -0
- data/src/simplify/replace_arrays_with_single_cells.rb +27 -0
- data/src/simplify/replace_blanks.rb +58 -0
- data/src/simplify/replace_common_elements_in_formulae.rb +19 -0
- data/src/simplify/replace_formulae_with_calculated_values.rb +21 -0
- data/src/simplify/replace_indirects_with_references.rb +44 -0
- data/src/simplify/replace_named_references.rb +82 -0
- data/src/simplify/replace_ranges_with_array_literals.rb +54 -0
- data/src/simplify/replace_shared_strings.rb +49 -0
- data/src/simplify/replace_table_references.rb +71 -0
- data/src/simplify/replace_values_with_constants.rb +47 -0
- data/src/simplify/simplify_arithmetic.rb +54 -0
- data/src/util.rb +2 -0
- data/src/util/not_supported_exception.rb +2 -0
- data/src/util/try.rb +9 -0
- metadata +207 -0
data/src/compile.rb
ADDED
data/src/compile/c.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
require_relative 'map_formulae_to_c'
|
2
|
+
|
3
|
+
class CompileToC
|
4
|
+
|
5
|
+
attr_accessor :settable
|
6
|
+
attr_accessor :gettable
|
7
|
+
attr_accessor :worksheet
|
8
|
+
attr_accessor :variable_set_counter
|
9
|
+
|
10
|
+
def self.rewrite(*args)
|
11
|
+
self.new.rewrite(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def rewrite(input,sheet_names_file,output)
|
15
|
+
self.settable ||= lambda { |ref| false }
|
16
|
+
self.gettable ||= lambda { |ref| true }
|
17
|
+
@variable_set_counter ||= 0
|
18
|
+
mapper = MapFormulaeToC.new
|
19
|
+
mapper.worksheet = worksheet
|
20
|
+
mapper.sheet_names = Hash[sheet_names_file.readlines.map { |line| line.strip.split("\t")}]
|
21
|
+
c_name = mapper.sheet_names[worksheet]
|
22
|
+
input.lines do |line|
|
23
|
+
begin
|
24
|
+
ref, formula = line.split("\t")
|
25
|
+
ast = eval(formula)
|
26
|
+
calculation = mapper.map(ast)
|
27
|
+
name = "#{c_name}_#{ref.downcase}"
|
28
|
+
static_or_not = gettable.call(ref) ? "" : "static "
|
29
|
+
if settable.call(ref)
|
30
|
+
output.puts "ExcelValue #{name}_default() {"
|
31
|
+
mapper.initializers.each do |i|
|
32
|
+
output.puts " #{i}"
|
33
|
+
end
|
34
|
+
output.puts " return #{calculation};"
|
35
|
+
output.puts "}"
|
36
|
+
output.puts "static ExcelValue #{name}_variable;"
|
37
|
+
output.puts "ExcelValue #{name}() { if(variable_set[#{@variable_set_counter}] == 1) { return #{name}_variable; } else { return #{c_name}_#{ref.downcase}_default(); } }"
|
38
|
+
output.puts "void set_#{name}(ExcelValue newValue) { variable_set[#{@variable_set_counter}] = 1; #{name}_variable = newValue; }"
|
39
|
+
else
|
40
|
+
output.puts "#{static_or_not}ExcelValue #{name}() {"
|
41
|
+
output.puts " static ExcelValue result;"
|
42
|
+
output.puts " if(variable_set[#{@variable_set_counter}] == 1) { return result;}"
|
43
|
+
mapper.initializers.each do |i|
|
44
|
+
output.puts " #{i}"
|
45
|
+
end
|
46
|
+
#output.puts " return #{calculation};"
|
47
|
+
output.puts " result = #{calculation};"
|
48
|
+
output.puts " variable_set[#{@variable_set_counter}] = 1;"
|
49
|
+
output.puts " return result;"
|
50
|
+
output.puts "}"
|
51
|
+
end
|
52
|
+
output.puts
|
53
|
+
@variable_set_counter += 1
|
54
|
+
mapper.reset
|
55
|
+
rescue Exception => e
|
56
|
+
puts "Exception at line #{line}"
|
57
|
+
raise
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class CompileToCHeader
|
2
|
+
|
3
|
+
attr_accessor :worksheet
|
4
|
+
attr_accessor :gettable
|
5
|
+
attr_accessor :settable
|
6
|
+
|
7
|
+
def self.rewrite(*args)
|
8
|
+
self.new.rewrite(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def rewrite(input,sheet_names_file,output,defaults = nil)
|
12
|
+
c_name = Hash[sheet_names_file.readlines.map { |line| line.strip.split("\t")}][worksheet]
|
13
|
+
self.gettable ||= lambda { |ref| true }
|
14
|
+
self.settable ||= lambda { |ref| false }
|
15
|
+
input.lines do |line|
|
16
|
+
begin
|
17
|
+
ref, formula = line.split("\t")
|
18
|
+
static_or_not = (gettable.call(ref) || settable.call(ref)) ? "" : "static "
|
19
|
+
output.puts "#{static_or_not}ExcelValue #{c_name}_#{ref.downcase}();"
|
20
|
+
rescue Exception => e
|
21
|
+
puts "Exception at line #{line}"
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class CompileToCUnitTest
|
2
|
+
|
3
|
+
def self.rewrite(*args)
|
4
|
+
self.new.rewrite(*args)
|
5
|
+
end
|
6
|
+
|
7
|
+
def rewrite(input,c_name, refs_to_test, output)
|
8
|
+
input.lines do |line|
|
9
|
+
begin
|
10
|
+
ref, formula = line.split("\t")
|
11
|
+
next unless refs_to_test.include?(ref)
|
12
|
+
output.puts "def test_#{c_name}_#{ref.downcase}"
|
13
|
+
output.puts " r = spreadsheet.#{c_name}_#{ref.downcase}"
|
14
|
+
ast = eval(formula)
|
15
|
+
case ast.first
|
16
|
+
when :number, :percentage
|
17
|
+
output.puts " assert_equal(:ExcelNumber,r[:type])"
|
18
|
+
output.puts " assert_in_epsilon(#{ast.last.to_f.to_s},r[:number])"
|
19
|
+
when :error
|
20
|
+
output.puts " assert_equal(:ExcelError,r[:type])"
|
21
|
+
when :string
|
22
|
+
output.puts " assert_equal(:ExcelString,r[:type])"
|
23
|
+
output.puts " assert_equal(#{ast.last.inspect},r[:string].force_encoding('utf-8'))"
|
24
|
+
when :boolean_true
|
25
|
+
output.puts " assert_equal(:ExcelBoolean,r[:type])"
|
26
|
+
output.puts " assert_equal(1,r[:number])"
|
27
|
+
when :boolean_false
|
28
|
+
output.puts " assert_equal(:ExcelBoolean,r[:type])"
|
29
|
+
output.puts " assert_equal(0,r[:number])"
|
30
|
+
else
|
31
|
+
raise NotSupportedException.new("#{ast} type can't be settable")
|
32
|
+
end
|
33
|
+
output.puts "end"
|
34
|
+
output.puts
|
35
|
+
rescue Exception => e
|
36
|
+
puts "Exception at line #{line}"
|
37
|
+
raise
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,2029 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <assert.h>
|
3
|
+
#include <string.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <ctype.h>
|
6
|
+
#include <math.h>
|
7
|
+
|
8
|
+
// FIXME: Extract a header file
|
9
|
+
|
10
|
+
// I predefine an array of ExcelValues to store calculations
|
11
|
+
// Probably bad practice. At the very least, I should make it
|
12
|
+
// link to the cell reference in some way.
|
13
|
+
#define MAX_EXCEL_VALUE_HEAP_SIZE 1000000
|
14
|
+
|
15
|
+
#define true 1
|
16
|
+
#define false 0
|
17
|
+
|
18
|
+
// These are the various types of excel cell, plus ExcelRange which allows the passing of arrays of cells
|
19
|
+
typedef enum {ExcelEmpty, ExcelNumber, ExcelString, ExcelBoolean, ExcelError, ExcelRange} ExcelType;
|
20
|
+
|
21
|
+
struct excel_value {
|
22
|
+
ExcelType type;
|
23
|
+
|
24
|
+
double number; // Used for numbers and for error types
|
25
|
+
char *string; // Used for strings
|
26
|
+
|
27
|
+
// The following three are used for ranges
|
28
|
+
void *array;
|
29
|
+
int rows;
|
30
|
+
int columns;
|
31
|
+
};
|
32
|
+
|
33
|
+
typedef struct excel_value ExcelValue;
|
34
|
+
|
35
|
+
|
36
|
+
// These are used in the SUMIF and SUMIFS criteria (e.g., when passed a string like "<20")
|
37
|
+
typedef enum {LessThan, LessThanOrEqual, Equal, NotEqual, MoreThanOrEqual, MoreThan} ExcelComparisonType;
|
38
|
+
|
39
|
+
struct excel_comparison {
|
40
|
+
ExcelComparisonType type;
|
41
|
+
ExcelValue comparator;
|
42
|
+
};
|
43
|
+
|
44
|
+
typedef struct excel_comparison ExcelComparison;
|
45
|
+
|
46
|
+
// Headers
|
47
|
+
static ExcelValue more_than(ExcelValue a_v, ExcelValue b_v);
|
48
|
+
static ExcelValue more_than_or_equal(ExcelValue a_v, ExcelValue b_v);
|
49
|
+
static ExcelValue not_equal(ExcelValue a_v, ExcelValue b_v);
|
50
|
+
static ExcelValue less_than(ExcelValue a_v, ExcelValue b_v);
|
51
|
+
static ExcelValue less_than_or_equal(ExcelValue a_v, ExcelValue b_v);
|
52
|
+
static ExcelValue find_2(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v);
|
53
|
+
static ExcelValue find(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v, ExcelValue position_to_start_at_v);
|
54
|
+
static ExcelValue iferror(ExcelValue value, ExcelValue value_if_error);
|
55
|
+
static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, ExcelValue column_number_v);
|
56
|
+
static ExcelValue excel_index_2(ExcelValue array_v, ExcelValue row_number_v);
|
57
|
+
static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v);
|
58
|
+
static ExcelValue left_1(ExcelValue string_v);
|
59
|
+
static ExcelValue max(int number_of_arguments, ExcelValue *arguments);
|
60
|
+
static ExcelValue min(int number_of_arguments, ExcelValue *arguments);
|
61
|
+
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v);
|
62
|
+
static ExcelValue negative(ExcelValue a_v);
|
63
|
+
static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v);
|
64
|
+
static ExcelValue power(ExcelValue a_v, ExcelValue b_v);
|
65
|
+
static ExcelValue excel_round(ExcelValue number_v, ExcelValue decimal_places_v);
|
66
|
+
static ExcelValue rounddown(ExcelValue number_v, ExcelValue decimal_places_v);
|
67
|
+
static ExcelValue roundup(ExcelValue number_v, ExcelValue decimal_places_v);
|
68
|
+
static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments);
|
69
|
+
static ExcelValue subtotal(ExcelValue type, int number_of_arguments, ExcelValue *arguments);
|
70
|
+
static ExcelValue sumifs(ExcelValue sum_range_v, int number_of_arguments, ExcelValue *arguments);
|
71
|
+
static ExcelValue sumif(ExcelValue check_range_v, ExcelValue criteria_v, ExcelValue sum_range_v );
|
72
|
+
static ExcelValue sumif_2(ExcelValue check_range_v, ExcelValue criteria_v);
|
73
|
+
static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments);
|
74
|
+
static ExcelValue vlookup_3(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v);
|
75
|
+
static ExcelValue vlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v, ExcelValue match_type_v);
|
76
|
+
|
77
|
+
// My little heap
|
78
|
+
ExcelValue cells[MAX_EXCEL_VALUE_HEAP_SIZE];
|
79
|
+
int cell_counter = 0;
|
80
|
+
|
81
|
+
#define HEAPCHECK if(cell_counter >= MAX_EXCEL_VALUE_HEAP_SIZE) { printf("Heap exceeded"); exit(-1); }
|
82
|
+
|
83
|
+
// The object initializers
|
84
|
+
static ExcelValue new_excel_number(double number) {
|
85
|
+
cell_counter++;
|
86
|
+
HEAPCHECK
|
87
|
+
ExcelValue new_cell = cells[cell_counter];
|
88
|
+
new_cell.type = ExcelNumber;
|
89
|
+
new_cell.number = number;
|
90
|
+
return new_cell;
|
91
|
+
};
|
92
|
+
|
93
|
+
static ExcelValue new_excel_string(char *string) {
|
94
|
+
cell_counter++;
|
95
|
+
HEAPCHECK
|
96
|
+
ExcelValue new_cell = cells[cell_counter];
|
97
|
+
new_cell.type = ExcelString;
|
98
|
+
new_cell.string = string;
|
99
|
+
return new_cell;
|
100
|
+
};
|
101
|
+
|
102
|
+
static ExcelValue new_excel_range(void *array, int rows, int columns) {
|
103
|
+
cell_counter++;
|
104
|
+
HEAPCHECK
|
105
|
+
ExcelValue new_cell = cells[cell_counter];
|
106
|
+
new_cell.type = ExcelRange;
|
107
|
+
new_cell.array =array;
|
108
|
+
new_cell.rows = rows;
|
109
|
+
new_cell.columns = columns;
|
110
|
+
return new_cell;
|
111
|
+
};
|
112
|
+
|
113
|
+
static void * new_excel_value_array(int size) {
|
114
|
+
ExcelValue *pointer = malloc(sizeof(ExcelValue)*size);
|
115
|
+
if(pointer == 0) {
|
116
|
+
printf("Out of memory\n");
|
117
|
+
exit(-1);
|
118
|
+
}
|
119
|
+
return pointer;
|
120
|
+
};
|
121
|
+
|
122
|
+
// Constants
|
123
|
+
const ExcelValue BLANK = {.type = ExcelEmpty, .number = 0};
|
124
|
+
|
125
|
+
const ExcelValue ZERO = {.type = ExcelNumber, .number = 0};
|
126
|
+
const ExcelValue ONE = {.type = ExcelNumber, .number = 1};
|
127
|
+
const ExcelValue TWO = {.type = ExcelNumber, .number = 2};
|
128
|
+
const ExcelValue THREE = {.type = ExcelNumber, .number = 3};
|
129
|
+
const ExcelValue FOUR = {.type = ExcelNumber, .number = 4};
|
130
|
+
const ExcelValue FIVE = {.type = ExcelNumber, .number = 5};
|
131
|
+
const ExcelValue SIX = {.type = ExcelNumber, .number = 6};
|
132
|
+
const ExcelValue SEVEN = {.type = ExcelNumber, .number = 7};
|
133
|
+
const ExcelValue EIGHT = {.type = ExcelNumber, .number = 8};
|
134
|
+
const ExcelValue NINE = {.type = ExcelNumber, .number = 9};
|
135
|
+
const ExcelValue TEN = {.type = ExcelNumber, .number = 10};
|
136
|
+
|
137
|
+
|
138
|
+
// Booleans
|
139
|
+
const ExcelValue TRUE = {.type = ExcelBoolean, .number = true };
|
140
|
+
const ExcelValue FALSE = {.type = ExcelBoolean, .number = false };
|
141
|
+
|
142
|
+
// Errors
|
143
|
+
const ExcelValue VALUE = {.type = ExcelError, .number = 0};
|
144
|
+
const ExcelValue NAME = {.type = ExcelError, .number = 1};
|
145
|
+
const ExcelValue DIV0 = {.type = ExcelError, .number = 2};
|
146
|
+
const ExcelValue REF = {.type = ExcelError, .number = 3};
|
147
|
+
const ExcelValue NA = {.type = ExcelError, .number = 4};
|
148
|
+
|
149
|
+
// This is the error flag
|
150
|
+
static int conversion_error = 0;
|
151
|
+
|
152
|
+
// Helpful for debugging
|
153
|
+
static void inspect_excel_value(ExcelValue v) {
|
154
|
+
ExcelValue *array;
|
155
|
+
int i, j, k;
|
156
|
+
switch (v.type) {
|
157
|
+
case ExcelNumber:
|
158
|
+
printf("Number: %f\n",v.number);
|
159
|
+
break;
|
160
|
+
case ExcelBoolean:
|
161
|
+
if(v.number == true) {
|
162
|
+
printf("True\n");
|
163
|
+
} else if(v.number == false) {
|
164
|
+
printf("False\n");
|
165
|
+
} else {
|
166
|
+
printf("Boolean with undefined state %f\n",v.number);
|
167
|
+
}
|
168
|
+
break;
|
169
|
+
case ExcelEmpty:
|
170
|
+
if(v.number == 0) {
|
171
|
+
printf("Empty\n");
|
172
|
+
} else {
|
173
|
+
printf("Empty with unexpected state %f\n",v.number);
|
174
|
+
}
|
175
|
+
break;
|
176
|
+
case ExcelRange:
|
177
|
+
printf("Range rows: %d, columns: %d\n",v.rows,v.columns);
|
178
|
+
array = v.array;
|
179
|
+
for(i = 0; i < v.rows; i++) {
|
180
|
+
printf("Row %d:\n",i+1);
|
181
|
+
for(j = 0; j < v.columns; j++ ) {
|
182
|
+
printf("%d ",j+1);
|
183
|
+
k = (i * v.columns) + j;
|
184
|
+
inspect_excel_value(array[k]);
|
185
|
+
}
|
186
|
+
}
|
187
|
+
break;
|
188
|
+
case ExcelString:
|
189
|
+
printf("String: '%s'\n",v.string);
|
190
|
+
break;
|
191
|
+
case ExcelError:
|
192
|
+
printf("Error number %f ",v.number);
|
193
|
+
switch( (int)v.number) {
|
194
|
+
case 0: printf("VALUE\n"); break;
|
195
|
+
case 1: printf("NAME\n"); break;
|
196
|
+
case 2: printf("DIV0\n"); break;
|
197
|
+
case 3: printf("REF\n"); break;
|
198
|
+
case 4: printf("NA\n"); break;
|
199
|
+
}
|
200
|
+
break;
|
201
|
+
default:
|
202
|
+
printf("Type %d not recognised",v.type);
|
203
|
+
};
|
204
|
+
}
|
205
|
+
|
206
|
+
// Extracts numbers from ExcelValues
|
207
|
+
// Excel treats empty cells as zero
|
208
|
+
static double number_from(ExcelValue v) {
|
209
|
+
char *s;
|
210
|
+
char *p;
|
211
|
+
double n;
|
212
|
+
ExcelValue *array;
|
213
|
+
switch (v.type) {
|
214
|
+
case ExcelNumber:
|
215
|
+
case ExcelBoolean:
|
216
|
+
return v.number;
|
217
|
+
case ExcelEmpty:
|
218
|
+
return 0;
|
219
|
+
case ExcelRange:
|
220
|
+
array = v.array;
|
221
|
+
return number_from(array[0]);
|
222
|
+
case ExcelString:
|
223
|
+
s = v.string;
|
224
|
+
if (s == NULL || *s == '\0' || isspace(*s)) {
|
225
|
+
return 0;
|
226
|
+
}
|
227
|
+
n = strtod (s, &p);
|
228
|
+
if(*p == '\0') {
|
229
|
+
return n;
|
230
|
+
}
|
231
|
+
conversion_error = 1;
|
232
|
+
return 0;
|
233
|
+
case ExcelError:
|
234
|
+
return 0;
|
235
|
+
}
|
236
|
+
return 0;
|
237
|
+
}
|
238
|
+
|
239
|
+
#define NUMBER(value_name, name) double name; if(value_name.type == ExcelError) { return value_name; }; name = number_from(value_name);
|
240
|
+
#define CHECK_FOR_CONVERSION_ERROR if(conversion_error) { conversion_error = 0; return VALUE; };
|
241
|
+
#define CHECK_FOR_PASSED_ERROR(name) if(name.type == ExcelError) return name;
|
242
|
+
|
243
|
+
static ExcelValue excel_abs(ExcelValue a_v) {
|
244
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
245
|
+
NUMBER(a_v, a)
|
246
|
+
CHECK_FOR_CONVERSION_ERROR
|
247
|
+
|
248
|
+
if(a >= 0.0 ) {
|
249
|
+
return a_v;
|
250
|
+
} else {
|
251
|
+
return new_excel_number(-a);
|
252
|
+
}
|
253
|
+
}
|
254
|
+
|
255
|
+
static ExcelValue add(ExcelValue a_v, ExcelValue b_v) {
|
256
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
257
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
258
|
+
NUMBER(a_v, a)
|
259
|
+
NUMBER(b_v, b)
|
260
|
+
CHECK_FOR_CONVERSION_ERROR
|
261
|
+
return new_excel_number(a + b);
|
262
|
+
}
|
263
|
+
|
264
|
+
static ExcelValue excel_and(int array_size, ExcelValue *array) {
|
265
|
+
int i;
|
266
|
+
ExcelValue current_excel_value, array_result;
|
267
|
+
|
268
|
+
for(i=0;i<array_size;i++) {
|
269
|
+
current_excel_value = array[i];
|
270
|
+
switch (current_excel_value.type) {
|
271
|
+
case ExcelNumber:
|
272
|
+
case ExcelBoolean:
|
273
|
+
if(current_excel_value.number == false) return FALSE;
|
274
|
+
break;
|
275
|
+
case ExcelRange:
|
276
|
+
array_result = excel_and( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
277
|
+
if(array_result.type == ExcelError) return array_result;
|
278
|
+
if(array_result.type == ExcelBoolean && array_result.number == false) return FALSE;
|
279
|
+
break;
|
280
|
+
case ExcelString:
|
281
|
+
case ExcelEmpty:
|
282
|
+
break;
|
283
|
+
case ExcelError:
|
284
|
+
return current_excel_value;
|
285
|
+
break;
|
286
|
+
}
|
287
|
+
}
|
288
|
+
return TRUE;
|
289
|
+
}
|
290
|
+
|
291
|
+
struct average_result {
|
292
|
+
double sum;
|
293
|
+
double count;
|
294
|
+
int has_error;
|
295
|
+
ExcelValue error;
|
296
|
+
};
|
297
|
+
|
298
|
+
static struct average_result calculate_average(int array_size, ExcelValue *array) {
|
299
|
+
double sum = 0;
|
300
|
+
double count = 0;
|
301
|
+
int i;
|
302
|
+
ExcelValue current_excel_value;
|
303
|
+
struct average_result array_result, r;
|
304
|
+
|
305
|
+
for(i=0;i<array_size;i++) {
|
306
|
+
current_excel_value = array[i];
|
307
|
+
switch (current_excel_value.type) {
|
308
|
+
case ExcelNumber:
|
309
|
+
sum += current_excel_value.number;
|
310
|
+
count++;
|
311
|
+
break;
|
312
|
+
case ExcelRange:
|
313
|
+
array_result = calculate_average( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
314
|
+
if(array_result.has_error == true) return array_result;
|
315
|
+
sum += array_result.sum;
|
316
|
+
count += array_result.count;
|
317
|
+
break;
|
318
|
+
case ExcelBoolean:
|
319
|
+
case ExcelString:
|
320
|
+
case ExcelEmpty:
|
321
|
+
break;
|
322
|
+
case ExcelError:
|
323
|
+
r.has_error = true;
|
324
|
+
r.error = current_excel_value;
|
325
|
+
return r;
|
326
|
+
break;
|
327
|
+
}
|
328
|
+
}
|
329
|
+
r.count = count;
|
330
|
+
r.sum = sum;
|
331
|
+
return r;
|
332
|
+
}
|
333
|
+
|
334
|
+
static ExcelValue average(int array_size, ExcelValue *array) {
|
335
|
+
struct average_result r = calculate_average(array_size, array);
|
336
|
+
if(r.has_error == true) return r.error;
|
337
|
+
if(r.count == 0) return DIV0;
|
338
|
+
return new_excel_number(r.sum/r.count);
|
339
|
+
}
|
340
|
+
|
341
|
+
static ExcelValue choose(ExcelValue index_v, int array_size, ExcelValue *array) {
|
342
|
+
CHECK_FOR_PASSED_ERROR(index_v)
|
343
|
+
|
344
|
+
int index = (int) number_from(index_v);
|
345
|
+
CHECK_FOR_CONVERSION_ERROR
|
346
|
+
int i;
|
347
|
+
for(i=0;i<array_size;i++) {
|
348
|
+
if(array[i].type == ExcelError) return array[i];
|
349
|
+
}
|
350
|
+
if(index < 1) return VALUE;
|
351
|
+
if(index > array_size) return VALUE;
|
352
|
+
return array[index-1];
|
353
|
+
}
|
354
|
+
|
355
|
+
static ExcelValue count(int array_size, ExcelValue *array) {
|
356
|
+
int i;
|
357
|
+
int n = 0;
|
358
|
+
ExcelValue current_excel_value;
|
359
|
+
|
360
|
+
for(i=0;i<array_size;i++) {
|
361
|
+
current_excel_value = array[i];
|
362
|
+
switch (current_excel_value.type) {
|
363
|
+
case ExcelNumber:
|
364
|
+
n++;
|
365
|
+
break;
|
366
|
+
case ExcelRange:
|
367
|
+
n += count( current_excel_value.rows * current_excel_value.columns, current_excel_value.array ).number;
|
368
|
+
break;
|
369
|
+
case ExcelBoolean:
|
370
|
+
case ExcelString:
|
371
|
+
case ExcelEmpty:
|
372
|
+
case ExcelError:
|
373
|
+
break;
|
374
|
+
}
|
375
|
+
}
|
376
|
+
return new_excel_number(n);
|
377
|
+
}
|
378
|
+
|
379
|
+
static ExcelValue counta(int array_size, ExcelValue *array) {
|
380
|
+
int i;
|
381
|
+
int n = 0;
|
382
|
+
ExcelValue current_excel_value;
|
383
|
+
|
384
|
+
for(i=0;i<array_size;i++) {
|
385
|
+
current_excel_value = array[i];
|
386
|
+
switch(current_excel_value.type) {
|
387
|
+
case ExcelNumber:
|
388
|
+
case ExcelBoolean:
|
389
|
+
case ExcelString:
|
390
|
+
case ExcelError:
|
391
|
+
n++;
|
392
|
+
break;
|
393
|
+
case ExcelRange:
|
394
|
+
n += counta( current_excel_value.rows * current_excel_value.columns, current_excel_value.array ).number;
|
395
|
+
break;
|
396
|
+
case ExcelEmpty:
|
397
|
+
break;
|
398
|
+
}
|
399
|
+
}
|
400
|
+
return new_excel_number(n);
|
401
|
+
}
|
402
|
+
|
403
|
+
static ExcelValue divide(ExcelValue a_v, ExcelValue b_v) {
|
404
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
405
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
406
|
+
NUMBER(a_v, a)
|
407
|
+
NUMBER(b_v, b)
|
408
|
+
CHECK_FOR_CONVERSION_ERROR
|
409
|
+
if(b == 0) return DIV0;
|
410
|
+
return new_excel_number(a / b);
|
411
|
+
}
|
412
|
+
|
413
|
+
static ExcelValue excel_equal(ExcelValue a_v, ExcelValue b_v) {
|
414
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
415
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
416
|
+
|
417
|
+
if(a_v.type != b_v.type) return FALSE;
|
418
|
+
|
419
|
+
switch (a_v.type) {
|
420
|
+
case ExcelNumber:
|
421
|
+
case ExcelBoolean:
|
422
|
+
case ExcelEmpty:
|
423
|
+
if(a_v.number != b_v.number) return FALSE;
|
424
|
+
return TRUE;
|
425
|
+
case ExcelString:
|
426
|
+
if(strcasecmp(a_v.string,b_v.string) != 0 ) return FALSE;
|
427
|
+
return TRUE;
|
428
|
+
case ExcelError:
|
429
|
+
return a_v;
|
430
|
+
case ExcelRange:
|
431
|
+
return NA;
|
432
|
+
}
|
433
|
+
return FALSE;
|
434
|
+
}
|
435
|
+
|
436
|
+
static ExcelValue not_equal(ExcelValue a_v, ExcelValue b_v) {
|
437
|
+
ExcelValue result = excel_equal(a_v, b_v);
|
438
|
+
if(result.type == ExcelBoolean) {
|
439
|
+
if(result.number == 0) return TRUE;
|
440
|
+
return FALSE;
|
441
|
+
}
|
442
|
+
return result;
|
443
|
+
}
|
444
|
+
|
445
|
+
static ExcelValue excel_if(ExcelValue condition, ExcelValue true_case, ExcelValue false_case ) {
|
446
|
+
CHECK_FOR_PASSED_ERROR(condition)
|
447
|
+
CHECK_FOR_PASSED_ERROR(true_case)
|
448
|
+
CHECK_FOR_PASSED_ERROR(false_case)
|
449
|
+
|
450
|
+
switch (condition.type) {
|
451
|
+
case ExcelBoolean:
|
452
|
+
if(condition.number == true) return true_case;
|
453
|
+
return false_case;
|
454
|
+
case ExcelNumber:
|
455
|
+
if(condition.number == false) return false_case;
|
456
|
+
return true_case;
|
457
|
+
case ExcelEmpty:
|
458
|
+
return false_case;
|
459
|
+
case ExcelString:
|
460
|
+
return VALUE;
|
461
|
+
case ExcelError:
|
462
|
+
return condition;
|
463
|
+
case ExcelRange:
|
464
|
+
return VALUE;
|
465
|
+
}
|
466
|
+
return condition;
|
467
|
+
}
|
468
|
+
|
469
|
+
static ExcelValue excel_if_2(ExcelValue condition, ExcelValue true_case ) {
|
470
|
+
return excel_if( condition, true_case, FALSE );
|
471
|
+
}
|
472
|
+
|
473
|
+
static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, ExcelValue column_number_v) {
|
474
|
+
CHECK_FOR_PASSED_ERROR(array_v)
|
475
|
+
CHECK_FOR_PASSED_ERROR(row_number_v)
|
476
|
+
CHECK_FOR_PASSED_ERROR(column_number_v)
|
477
|
+
|
478
|
+
ExcelValue *array;
|
479
|
+
int rows;
|
480
|
+
int columns;
|
481
|
+
|
482
|
+
NUMBER(row_number_v, row_number)
|
483
|
+
NUMBER(column_number_v, column_number)
|
484
|
+
CHECK_FOR_CONVERSION_ERROR
|
485
|
+
|
486
|
+
if(array_v.type == ExcelRange) {
|
487
|
+
array = array_v.array;
|
488
|
+
rows = array_v.rows;
|
489
|
+
columns = array_v.columns;
|
490
|
+
} else {
|
491
|
+
ExcelValue tmp_array[] = {array_v};
|
492
|
+
array = tmp_array;
|
493
|
+
rows = 1;
|
494
|
+
columns = 1;
|
495
|
+
}
|
496
|
+
|
497
|
+
if(row_number > rows) return REF;
|
498
|
+
if(column_number > columns) return REF;
|
499
|
+
|
500
|
+
if(row_number == 0) { // We need the whole column
|
501
|
+
if(column_number < 1) return REF;
|
502
|
+
ExcelValue *result = (ExcelValue *) new_excel_value_array(rows);
|
503
|
+
int result_index = 0;
|
504
|
+
ExcelValue r;
|
505
|
+
int array_index;
|
506
|
+
int i;
|
507
|
+
for(i = 0; i < rows; i++) {
|
508
|
+
array_index = (i*columns) + column_number - 1;
|
509
|
+
r = array[array_index];
|
510
|
+
if(r.type == ExcelEmpty) {
|
511
|
+
result[result_index] = ZERO;
|
512
|
+
} else {
|
513
|
+
result[result_index] = r;
|
514
|
+
}
|
515
|
+
result_index++;
|
516
|
+
}
|
517
|
+
return new_excel_range(result,rows,1);
|
518
|
+
} else if(column_number == 0 ) { // We need the whole row
|
519
|
+
if(row_number < 1) return REF;
|
520
|
+
ExcelValue *result = (ExcelValue*) new_excel_value_array(columns);
|
521
|
+
ExcelValue r;
|
522
|
+
int row_start = ((row_number-1)*columns);
|
523
|
+
int row_finish = row_start + columns;
|
524
|
+
int result_index = 0;
|
525
|
+
int i;
|
526
|
+
for(i = row_start; i < row_finish; i++) {
|
527
|
+
r = array[i];
|
528
|
+
if(r.type == ExcelEmpty) {
|
529
|
+
result[result_index] = ZERO;
|
530
|
+
} else {
|
531
|
+
result[result_index] = r;
|
532
|
+
}
|
533
|
+
result_index++;
|
534
|
+
}
|
535
|
+
return new_excel_range(result,1,columns);
|
536
|
+
} else { // We need a precise point
|
537
|
+
if(row_number < 1) return REF;
|
538
|
+
if(column_number < 1) return REF;
|
539
|
+
int position = ((row_number - 1) * columns) + column_number - 1;
|
540
|
+
ExcelValue result = array[position];
|
541
|
+
if(result.type == ExcelEmpty) return ZERO;
|
542
|
+
return result;
|
543
|
+
}
|
544
|
+
|
545
|
+
return FALSE;
|
546
|
+
};
|
547
|
+
|
548
|
+
static ExcelValue excel_index_2(ExcelValue array_v, ExcelValue offset) {
|
549
|
+
if(array_v.type == ExcelRange) {
|
550
|
+
if(array_v.rows == 1) {
|
551
|
+
return excel_index(array_v,ONE,offset);
|
552
|
+
} else if (array_v.columns == 1) {
|
553
|
+
return excel_index(array_v,offset,ONE);
|
554
|
+
} else {
|
555
|
+
return REF;
|
556
|
+
}
|
557
|
+
} else if (offset.type == ExcelNumber && offset.number == 1) {
|
558
|
+
return array_v;
|
559
|
+
} else {
|
560
|
+
return REF;
|
561
|
+
}
|
562
|
+
return REF;
|
563
|
+
};
|
564
|
+
|
565
|
+
|
566
|
+
static ExcelValue excel_match(ExcelValue lookup_value, ExcelValue lookup_array, ExcelValue match_type ) {
|
567
|
+
CHECK_FOR_PASSED_ERROR(lookup_value)
|
568
|
+
CHECK_FOR_PASSED_ERROR(lookup_array)
|
569
|
+
CHECK_FOR_PASSED_ERROR(match_type)
|
570
|
+
|
571
|
+
// Blanks are treaked as zeros
|
572
|
+
if(lookup_value.type == ExcelEmpty) lookup_value = ZERO;
|
573
|
+
|
574
|
+
// Setup the array
|
575
|
+
ExcelValue *array;
|
576
|
+
int size;
|
577
|
+
if(lookup_array.type == ExcelRange) {
|
578
|
+
// Check that the range is a row or column rather than an area
|
579
|
+
if((lookup_array.rows == 1) || (lookup_array.columns == 1)) {
|
580
|
+
array = lookup_array.array;
|
581
|
+
size = lookup_array.rows * lookup_array.columns;
|
582
|
+
} else {
|
583
|
+
// return NA error if covers an area.
|
584
|
+
return NA;
|
585
|
+
};
|
586
|
+
} else {
|
587
|
+
// Need to wrap the argument up as an array
|
588
|
+
size = 1;
|
589
|
+
ExcelValue tmp_array[1] = {lookup_array};
|
590
|
+
array = tmp_array;
|
591
|
+
}
|
592
|
+
|
593
|
+
int type = (int) number_from(match_type);
|
594
|
+
CHECK_FOR_CONVERSION_ERROR;
|
595
|
+
|
596
|
+
int i;
|
597
|
+
ExcelValue x;
|
598
|
+
|
599
|
+
switch(type) {
|
600
|
+
case 0:
|
601
|
+
for(i = 0; i < size; i++ ) {
|
602
|
+
x = array[i];
|
603
|
+
if(x.type == ExcelEmpty) x = ZERO;
|
604
|
+
if(excel_equal(lookup_value,x).number == true) return new_excel_number(i+1);
|
605
|
+
}
|
606
|
+
return NA;
|
607
|
+
break;
|
608
|
+
case 1:
|
609
|
+
for(i = 0; i < size; i++ ) {
|
610
|
+
x = array[i];
|
611
|
+
if(x.type == ExcelEmpty) x = ZERO;
|
612
|
+
if(more_than(x,lookup_value).number == true) {
|
613
|
+
if(i==0) return NA;
|
614
|
+
return new_excel_number(i);
|
615
|
+
}
|
616
|
+
}
|
617
|
+
return new_excel_number(size);
|
618
|
+
break;
|
619
|
+
case -1:
|
620
|
+
for(i = 0; i < size; i++ ) {
|
621
|
+
x = array[i];
|
622
|
+
if(x.type == ExcelEmpty) x = ZERO;
|
623
|
+
if(less_than(x,lookup_value).number == true) {
|
624
|
+
if(i==0) return NA;
|
625
|
+
return new_excel_number(i);
|
626
|
+
}
|
627
|
+
}
|
628
|
+
return new_excel_number(size-1);
|
629
|
+
break;
|
630
|
+
}
|
631
|
+
return NA;
|
632
|
+
}
|
633
|
+
|
634
|
+
static ExcelValue excel_match_2(ExcelValue lookup_value, ExcelValue lookup_array ) {
|
635
|
+
return excel_match(lookup_value,lookup_array,new_excel_number(0));
|
636
|
+
}
|
637
|
+
|
638
|
+
static ExcelValue find(ExcelValue find_text_v, ExcelValue within_text_v, ExcelValue start_number_v) {
|
639
|
+
CHECK_FOR_PASSED_ERROR(find_text_v)
|
640
|
+
CHECK_FOR_PASSED_ERROR(within_text_v)
|
641
|
+
CHECK_FOR_PASSED_ERROR(start_number_v)
|
642
|
+
|
643
|
+
char *find_text;
|
644
|
+
char *within_text;
|
645
|
+
char *within_text_offset;
|
646
|
+
char *result;
|
647
|
+
int start_number = number_from(start_number_v);
|
648
|
+
CHECK_FOR_CONVERSION_ERROR
|
649
|
+
|
650
|
+
// Deal with blanks
|
651
|
+
if(within_text_v.type == ExcelString) {
|
652
|
+
within_text = within_text_v.string;
|
653
|
+
} else if( within_text_v.type == ExcelEmpty) {
|
654
|
+
within_text = "";
|
655
|
+
}
|
656
|
+
|
657
|
+
if(find_text_v.type == ExcelString) {
|
658
|
+
find_text = find_text_v.string;
|
659
|
+
} else if( find_text_v.type == ExcelEmpty) {
|
660
|
+
return start_number_v;
|
661
|
+
}
|
662
|
+
|
663
|
+
// Check length
|
664
|
+
if(start_number < 1) return VALUE;
|
665
|
+
if(start_number > strlen(within_text)) return VALUE;
|
666
|
+
|
667
|
+
// Offset our within_text pointer
|
668
|
+
// FIXME: No way this is utf-8 compatible
|
669
|
+
within_text_offset = within_text + (start_number - 1);
|
670
|
+
result = strstr(within_text_offset,find_text);
|
671
|
+
if(result) {
|
672
|
+
return new_excel_number(result - within_text + 1);
|
673
|
+
}
|
674
|
+
return VALUE;
|
675
|
+
}
|
676
|
+
|
677
|
+
static ExcelValue find_2(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v) {
|
678
|
+
return find(string_to_look_for_v, string_to_look_in_v, ONE);
|
679
|
+
};
|
680
|
+
|
681
|
+
static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v) {
|
682
|
+
CHECK_FOR_PASSED_ERROR(string_v)
|
683
|
+
CHECK_FOR_PASSED_ERROR(number_of_characters_v)
|
684
|
+
if(string_v.type == ExcelEmpty) return BLANK;
|
685
|
+
if(number_of_characters_v.type == ExcelEmpty) return BLANK;
|
686
|
+
|
687
|
+
int number_of_characters = (int) number_from(number_of_characters_v);
|
688
|
+
CHECK_FOR_CONVERSION_ERROR
|
689
|
+
|
690
|
+
char *string;
|
691
|
+
switch (string_v.type) {
|
692
|
+
case ExcelString:
|
693
|
+
string = string_v.string;
|
694
|
+
break;
|
695
|
+
case ExcelNumber:
|
696
|
+
string = malloc(20);
|
697
|
+
if(string == 0) {
|
698
|
+
printf("Out of memory");
|
699
|
+
exit(-1);
|
700
|
+
}
|
701
|
+
snprintf(string,20,"%f",string_v.number);
|
702
|
+
break;
|
703
|
+
case ExcelBoolean:
|
704
|
+
if(string_v.number == true) {
|
705
|
+
string = "TRUE";
|
706
|
+
} else {
|
707
|
+
string = "FALSE";
|
708
|
+
}
|
709
|
+
break;
|
710
|
+
case ExcelEmpty:
|
711
|
+
case ExcelError:
|
712
|
+
case ExcelRange:
|
713
|
+
return string_v;
|
714
|
+
}
|
715
|
+
|
716
|
+
char *left_string = malloc(number_of_characters+1);
|
717
|
+
if(left_string == 0) {
|
718
|
+
printf("Out of memory");
|
719
|
+
exit(-1);
|
720
|
+
}
|
721
|
+
memcpy(left_string,string,number_of_characters);
|
722
|
+
left_string[number_of_characters] = '\0';
|
723
|
+
return new_excel_string(left_string);
|
724
|
+
}
|
725
|
+
|
726
|
+
static ExcelValue left_1(ExcelValue string_v) {
|
727
|
+
return left(string_v, ONE);
|
728
|
+
}
|
729
|
+
|
730
|
+
static ExcelValue iferror(ExcelValue value, ExcelValue value_if_error) {
|
731
|
+
if(value.type == ExcelError) return value_if_error;
|
732
|
+
return value;
|
733
|
+
}
|
734
|
+
|
735
|
+
static ExcelValue more_than(ExcelValue a_v, ExcelValue b_v) {
|
736
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
737
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
738
|
+
|
739
|
+
switch (a_v.type) {
|
740
|
+
case ExcelNumber:
|
741
|
+
case ExcelBoolean:
|
742
|
+
case ExcelEmpty:
|
743
|
+
if((b_v.type == ExcelNumber) || (b_v.type == ExcelBoolean) || (b_v.type == ExcelEmpty)) {
|
744
|
+
if(a_v.number <= b_v.number) return FALSE;
|
745
|
+
return TRUE;
|
746
|
+
}
|
747
|
+
return FALSE;
|
748
|
+
case ExcelString:
|
749
|
+
if(b_v.type == ExcelString) {
|
750
|
+
if(strcasecmp(a_v.string,b_v.string) <= 0 ) return FALSE;
|
751
|
+
return TRUE;
|
752
|
+
}
|
753
|
+
return FALSE;
|
754
|
+
case ExcelError:
|
755
|
+
return a_v;
|
756
|
+
case ExcelRange:
|
757
|
+
return NA;
|
758
|
+
}
|
759
|
+
return FALSE;
|
760
|
+
}
|
761
|
+
|
762
|
+
static ExcelValue more_than_or_equal(ExcelValue a_v, ExcelValue b_v) {
|
763
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
764
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
765
|
+
|
766
|
+
switch (a_v.type) {
|
767
|
+
case ExcelNumber:
|
768
|
+
case ExcelBoolean:
|
769
|
+
case ExcelEmpty:
|
770
|
+
if((b_v.type == ExcelNumber) || (b_v.type == ExcelBoolean) || (b_v.type == ExcelEmpty)) {
|
771
|
+
if(a_v.number < b_v.number) return FALSE;
|
772
|
+
return TRUE;
|
773
|
+
}
|
774
|
+
return FALSE;
|
775
|
+
case ExcelString:
|
776
|
+
if(b_v.type == ExcelString) {
|
777
|
+
if(strcasecmp(a_v.string,b_v.string) < 0 ) return FALSE;
|
778
|
+
return TRUE;
|
779
|
+
}
|
780
|
+
return FALSE;
|
781
|
+
case ExcelError:
|
782
|
+
return a_v;
|
783
|
+
case ExcelRange:
|
784
|
+
return NA;
|
785
|
+
}
|
786
|
+
return FALSE;
|
787
|
+
}
|
788
|
+
|
789
|
+
|
790
|
+
static ExcelValue less_than(ExcelValue a_v, ExcelValue b_v) {
|
791
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
792
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
793
|
+
|
794
|
+
switch (a_v.type) {
|
795
|
+
case ExcelNumber:
|
796
|
+
case ExcelBoolean:
|
797
|
+
case ExcelEmpty:
|
798
|
+
if((b_v.type == ExcelNumber) || (b_v.type == ExcelBoolean) || (b_v.type == ExcelEmpty)) {
|
799
|
+
if(a_v.number >= b_v.number) return FALSE;
|
800
|
+
return TRUE;
|
801
|
+
}
|
802
|
+
return FALSE;
|
803
|
+
case ExcelString:
|
804
|
+
if(b_v.type == ExcelString) {
|
805
|
+
if(strcasecmp(a_v.string,b_v.string) >= 0 ) return FALSE;
|
806
|
+
return TRUE;
|
807
|
+
}
|
808
|
+
return FALSE;
|
809
|
+
case ExcelError:
|
810
|
+
return a_v;
|
811
|
+
case ExcelRange:
|
812
|
+
return NA;
|
813
|
+
}
|
814
|
+
return FALSE;
|
815
|
+
}
|
816
|
+
|
817
|
+
static ExcelValue less_than_or_equal(ExcelValue a_v, ExcelValue b_v) {
|
818
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
819
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
820
|
+
|
821
|
+
switch (a_v.type) {
|
822
|
+
case ExcelNumber:
|
823
|
+
case ExcelBoolean:
|
824
|
+
case ExcelEmpty:
|
825
|
+
if((b_v.type == ExcelNumber) || (b_v.type == ExcelBoolean) || (b_v.type == ExcelEmpty)) {
|
826
|
+
if(a_v.number > b_v.number) return FALSE;
|
827
|
+
return TRUE;
|
828
|
+
}
|
829
|
+
return FALSE;
|
830
|
+
case ExcelString:
|
831
|
+
if(b_v.type == ExcelString) {
|
832
|
+
if(strcasecmp(a_v.string,b_v.string) > 0 ) return FALSE;
|
833
|
+
return TRUE;
|
834
|
+
}
|
835
|
+
return FALSE;
|
836
|
+
case ExcelError:
|
837
|
+
return a_v;
|
838
|
+
case ExcelRange:
|
839
|
+
return NA;
|
840
|
+
}
|
841
|
+
return FALSE;
|
842
|
+
}
|
843
|
+
|
844
|
+
static ExcelValue subtract(ExcelValue a_v, ExcelValue b_v) {
|
845
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
846
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
847
|
+
NUMBER(a_v, a)
|
848
|
+
NUMBER(b_v, b)
|
849
|
+
CHECK_FOR_CONVERSION_ERROR
|
850
|
+
return new_excel_number(a - b);
|
851
|
+
}
|
852
|
+
|
853
|
+
static ExcelValue multiply(ExcelValue a_v, ExcelValue b_v) {
|
854
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
855
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
856
|
+
NUMBER(a_v, a)
|
857
|
+
NUMBER(b_v, b)
|
858
|
+
CHECK_FOR_CONVERSION_ERROR
|
859
|
+
return new_excel_number(a * b);
|
860
|
+
}
|
861
|
+
|
862
|
+
static ExcelValue sum(int array_size, ExcelValue *array) {
|
863
|
+
double total = 0;
|
864
|
+
int i;
|
865
|
+
for(i=0;i<array_size;i++) {
|
866
|
+
switch(array[i].type) {
|
867
|
+
case ExcelNumber:
|
868
|
+
total += array[i].number;
|
869
|
+
break;
|
870
|
+
case ExcelRange:
|
871
|
+
total += number_from(sum( array[i].rows * array[i].columns, array[i].array ));
|
872
|
+
break;
|
873
|
+
case ExcelError:
|
874
|
+
return array[i];
|
875
|
+
break;
|
876
|
+
default:
|
877
|
+
break;
|
878
|
+
}
|
879
|
+
}
|
880
|
+
return new_excel_number(total);
|
881
|
+
}
|
882
|
+
|
883
|
+
static ExcelValue max(int number_of_arguments, ExcelValue *arguments) {
|
884
|
+
double biggest_number_found;
|
885
|
+
int any_number_found = 0;
|
886
|
+
int i;
|
887
|
+
ExcelValue current_excel_value;
|
888
|
+
|
889
|
+
for(i=0;i<number_of_arguments;i++) {
|
890
|
+
current_excel_value = arguments[i];
|
891
|
+
if(current_excel_value.type == ExcelNumber) {
|
892
|
+
if(!any_number_found) {
|
893
|
+
any_number_found = 1;
|
894
|
+
biggest_number_found = current_excel_value.number;
|
895
|
+
}
|
896
|
+
if(current_excel_value.number > biggest_number_found) biggest_number_found = current_excel_value.number;
|
897
|
+
} else if(current_excel_value.type == ExcelRange) {
|
898
|
+
current_excel_value = max( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
899
|
+
if(current_excel_value.type == ExcelError) return current_excel_value;
|
900
|
+
if(current_excel_value.type == ExcelNumber)
|
901
|
+
if(!any_number_found) {
|
902
|
+
any_number_found = 1;
|
903
|
+
biggest_number_found = current_excel_value.number;
|
904
|
+
}
|
905
|
+
if(current_excel_value.number > biggest_number_found) biggest_number_found = current_excel_value.number;
|
906
|
+
} else if(current_excel_value.type == ExcelError) {
|
907
|
+
return current_excel_value;
|
908
|
+
}
|
909
|
+
}
|
910
|
+
if(!any_number_found) {
|
911
|
+
any_number_found = 1;
|
912
|
+
biggest_number_found = 0;
|
913
|
+
}
|
914
|
+
return new_excel_number(biggest_number_found);
|
915
|
+
}
|
916
|
+
|
917
|
+
static ExcelValue min(int number_of_arguments, ExcelValue *arguments) {
|
918
|
+
double smallest_number_found = 0;
|
919
|
+
int any_number_found = 0;
|
920
|
+
int i;
|
921
|
+
ExcelValue current_excel_value;
|
922
|
+
|
923
|
+
for(i=0;i<number_of_arguments;i++) {
|
924
|
+
current_excel_value = arguments[i];
|
925
|
+
if(current_excel_value.type == ExcelNumber) {
|
926
|
+
if(!any_number_found) {
|
927
|
+
any_number_found = 1;
|
928
|
+
smallest_number_found = current_excel_value.number;
|
929
|
+
}
|
930
|
+
if(current_excel_value.number < smallest_number_found) smallest_number_found = current_excel_value.number;
|
931
|
+
} else if(current_excel_value.type == ExcelRange) {
|
932
|
+
current_excel_value = min( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
|
933
|
+
if(current_excel_value.type == ExcelError) return current_excel_value;
|
934
|
+
if(current_excel_value.type == ExcelNumber)
|
935
|
+
if(!any_number_found) {
|
936
|
+
any_number_found = 1;
|
937
|
+
smallest_number_found = current_excel_value.number;
|
938
|
+
}
|
939
|
+
if(current_excel_value.number < smallest_number_found) smallest_number_found = current_excel_value.number;
|
940
|
+
} else if(current_excel_value.type == ExcelError) {
|
941
|
+
return current_excel_value;
|
942
|
+
}
|
943
|
+
}
|
944
|
+
if(!any_number_found) {
|
945
|
+
any_number_found = 1;
|
946
|
+
smallest_number_found = 0;
|
947
|
+
}
|
948
|
+
return new_excel_number(smallest_number_found);
|
949
|
+
}
|
950
|
+
|
951
|
+
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v) {
|
952
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
953
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
954
|
+
|
955
|
+
NUMBER(a_v, a)
|
956
|
+
NUMBER(b_v, b)
|
957
|
+
CHECK_FOR_CONVERSION_ERROR
|
958
|
+
if(b == 0) return DIV0;
|
959
|
+
return new_excel_number(fmod(a,b));
|
960
|
+
}
|
961
|
+
|
962
|
+
static ExcelValue negative(ExcelValue a_v) {
|
963
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
964
|
+
NUMBER(a_v, a)
|
965
|
+
CHECK_FOR_CONVERSION_ERROR
|
966
|
+
return new_excel_number(-a);
|
967
|
+
}
|
968
|
+
|
969
|
+
static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v) {
|
970
|
+
CHECK_FOR_PASSED_ERROR(rate_v)
|
971
|
+
CHECK_FOR_PASSED_ERROR(number_of_periods_v)
|
972
|
+
CHECK_FOR_PASSED_ERROR(present_value_v)
|
973
|
+
|
974
|
+
NUMBER(rate_v,rate)
|
975
|
+
NUMBER(number_of_periods_v,number_of_periods)
|
976
|
+
NUMBER(present_value_v,present_value)
|
977
|
+
CHECK_FOR_CONVERSION_ERROR
|
978
|
+
|
979
|
+
if(rate == 0) return new_excel_number(-(present_value / number_of_periods));
|
980
|
+
return new_excel_number(-present_value*(rate*(pow((1+rate),number_of_periods)))/((pow((1+rate),number_of_periods))-1));
|
981
|
+
}
|
982
|
+
|
983
|
+
static ExcelValue power(ExcelValue a_v, ExcelValue b_v) {
|
984
|
+
CHECK_FOR_PASSED_ERROR(a_v)
|
985
|
+
CHECK_FOR_PASSED_ERROR(b_v)
|
986
|
+
|
987
|
+
NUMBER(a_v, a)
|
988
|
+
NUMBER(b_v, b)
|
989
|
+
CHECK_FOR_CONVERSION_ERROR
|
990
|
+
return new_excel_number(pow(a,b));
|
991
|
+
}
|
992
|
+
|
993
|
+
static ExcelValue excel_round(ExcelValue number_v, ExcelValue decimal_places_v) {
|
994
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
995
|
+
CHECK_FOR_PASSED_ERROR(decimal_places_v)
|
996
|
+
|
997
|
+
NUMBER(number_v, number)
|
998
|
+
NUMBER(decimal_places_v, decimal_places)
|
999
|
+
CHECK_FOR_CONVERSION_ERROR
|
1000
|
+
|
1001
|
+
double multiple = pow(10,decimal_places);
|
1002
|
+
|
1003
|
+
return new_excel_number( round(number * multiple) / multiple );
|
1004
|
+
}
|
1005
|
+
|
1006
|
+
static ExcelValue rounddown(ExcelValue number_v, ExcelValue decimal_places_v) {
|
1007
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
1008
|
+
CHECK_FOR_PASSED_ERROR(decimal_places_v)
|
1009
|
+
|
1010
|
+
NUMBER(number_v, number)
|
1011
|
+
NUMBER(decimal_places_v, decimal_places)
|
1012
|
+
CHECK_FOR_CONVERSION_ERROR
|
1013
|
+
|
1014
|
+
double multiple = pow(10,decimal_places);
|
1015
|
+
|
1016
|
+
return new_excel_number( trunc(number * multiple) / multiple );
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
static ExcelValue roundup(ExcelValue number_v, ExcelValue decimal_places_v) {
|
1020
|
+
CHECK_FOR_PASSED_ERROR(number_v)
|
1021
|
+
CHECK_FOR_PASSED_ERROR(decimal_places_v)
|
1022
|
+
|
1023
|
+
NUMBER(number_v, number)
|
1024
|
+
NUMBER(decimal_places_v, decimal_places)
|
1025
|
+
CHECK_FOR_CONVERSION_ERROR
|
1026
|
+
|
1027
|
+
double multiple = pow(10,decimal_places);
|
1028
|
+
if(number < 0) return new_excel_number( floor(number * multiple) / multiple );
|
1029
|
+
return new_excel_number( ceil(number * multiple) / multiple );
|
1030
|
+
}
|
1031
|
+
|
1032
|
+
static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments) {
|
1033
|
+
int allocated_length = 100;
|
1034
|
+
int used_length = 0;
|
1035
|
+
char *string = malloc(allocated_length);
|
1036
|
+
if(string == 0) {
|
1037
|
+
printf("Out of memory");
|
1038
|
+
exit(-1);
|
1039
|
+
}
|
1040
|
+
char *current_string;
|
1041
|
+
int current_string_length;
|
1042
|
+
ExcelValue current_v;
|
1043
|
+
int i;
|
1044
|
+
for(i=0;i<number_of_arguments;i++) {
|
1045
|
+
current_v = (ExcelValue) arguments[i];
|
1046
|
+
switch (current_v.type) {
|
1047
|
+
case ExcelString:
|
1048
|
+
current_string = current_v.string;
|
1049
|
+
break;
|
1050
|
+
case ExcelNumber:
|
1051
|
+
current_string = malloc(20);
|
1052
|
+
if(current_string == 0) {
|
1053
|
+
printf("Out of memory");
|
1054
|
+
exit(-1);
|
1055
|
+
}
|
1056
|
+
snprintf(current_string,20,"%g",current_v.number);
|
1057
|
+
break;
|
1058
|
+
case ExcelBoolean:
|
1059
|
+
if(current_v.number == true) {
|
1060
|
+
current_string = "TRUE";
|
1061
|
+
} else {
|
1062
|
+
current_string = "FALSE";
|
1063
|
+
}
|
1064
|
+
break;
|
1065
|
+
case ExcelEmpty:
|
1066
|
+
current_string = "";
|
1067
|
+
break;
|
1068
|
+
case ExcelError:
|
1069
|
+
return current_v;
|
1070
|
+
case ExcelRange:
|
1071
|
+
return VALUE;
|
1072
|
+
}
|
1073
|
+
current_string_length = strlen(current_string);
|
1074
|
+
if( (used_length + current_string_length + 1) > allocated_length) {
|
1075
|
+
allocated_length += 100;
|
1076
|
+
string = realloc(string,allocated_length);
|
1077
|
+
}
|
1078
|
+
memcpy(string + used_length, current_string, current_string_length);
|
1079
|
+
used_length = used_length + current_string_length;
|
1080
|
+
}
|
1081
|
+
string = realloc(string,used_length+1);
|
1082
|
+
return new_excel_string(string);
|
1083
|
+
}
|
1084
|
+
|
1085
|
+
static ExcelValue subtotal(ExcelValue subtotal_type_v, int number_of_arguments, ExcelValue *arguments) {
|
1086
|
+
CHECK_FOR_PASSED_ERROR(subtotal_type_v)
|
1087
|
+
NUMBER(subtotal_type_v,subtotal_type)
|
1088
|
+
CHECK_FOR_CONVERSION_ERROR
|
1089
|
+
|
1090
|
+
switch((int) subtotal_type) {
|
1091
|
+
case 1:
|
1092
|
+
case 101:
|
1093
|
+
return average(number_of_arguments,arguments);
|
1094
|
+
break;
|
1095
|
+
case 2:
|
1096
|
+
case 102:
|
1097
|
+
return count(number_of_arguments,arguments);
|
1098
|
+
break;
|
1099
|
+
case 3:
|
1100
|
+
case 103:
|
1101
|
+
return counta(number_of_arguments,arguments);
|
1102
|
+
break;
|
1103
|
+
case 9:
|
1104
|
+
case 109:
|
1105
|
+
return sum(number_of_arguments,arguments);
|
1106
|
+
break;
|
1107
|
+
default:
|
1108
|
+
return VALUE;
|
1109
|
+
break;
|
1110
|
+
}
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
static ExcelValue sumifs(ExcelValue sum_range_v, int number_of_arguments, ExcelValue *arguments) {
|
1114
|
+
// First, set up the sum_range
|
1115
|
+
CHECK_FOR_PASSED_ERROR(sum_range_v);
|
1116
|
+
|
1117
|
+
// Set up the sum range
|
1118
|
+
ExcelValue *sum_range;
|
1119
|
+
int sum_range_rows, sum_range_columns;
|
1120
|
+
|
1121
|
+
if(sum_range_v.type == ExcelRange) {
|
1122
|
+
sum_range = sum_range_v.array;
|
1123
|
+
sum_range_rows = sum_range_v.rows;
|
1124
|
+
sum_range_columns = sum_range_v.columns;
|
1125
|
+
} else {
|
1126
|
+
sum_range = (ExcelValue*) new_excel_value_array(1);
|
1127
|
+
sum_range[0] = sum_range_v;
|
1128
|
+
sum_range_rows = 1;
|
1129
|
+
sum_range_columns = 1;
|
1130
|
+
}
|
1131
|
+
|
1132
|
+
// Then go through and set up the check ranges
|
1133
|
+
if(number_of_arguments % 2 != 0) return VALUE;
|
1134
|
+
int number_of_criteria = number_of_arguments / 2;
|
1135
|
+
ExcelValue *criteria_range = (ExcelValue*) new_excel_value_array(number_of_criteria);
|
1136
|
+
ExcelValue current_value;
|
1137
|
+
int i;
|
1138
|
+
for(i = 0; i < number_of_criteria; i++) {
|
1139
|
+
current_value = arguments[i*2];
|
1140
|
+
if(current_value.type == ExcelRange) {
|
1141
|
+
criteria_range[i] = current_value;
|
1142
|
+
if(current_value.rows != sum_range_rows) return VALUE;
|
1143
|
+
if(current_value.columns != sum_range_columns) return VALUE;
|
1144
|
+
} else {
|
1145
|
+
if(sum_range_rows != 1) return VALUE;
|
1146
|
+
if(sum_range_columns != 1) return VALUE;
|
1147
|
+
ExcelValue *tmp_array2 = (ExcelValue*) new_excel_value_array(1);
|
1148
|
+
tmp_array2[0] = current_value;
|
1149
|
+
criteria_range[i] = new_excel_range(tmp_array2,1,1);
|
1150
|
+
}
|
1151
|
+
}
|
1152
|
+
|
1153
|
+
// Now go through and set up the criteria
|
1154
|
+
ExcelComparison *criteria = malloc(sizeof(ExcelComparison)*number_of_criteria);
|
1155
|
+
if(criteria == 0) {
|
1156
|
+
printf("Out of memory\n");
|
1157
|
+
exit(-1);
|
1158
|
+
}
|
1159
|
+
char *s;
|
1160
|
+
for(i = 0; i < number_of_criteria; i++) {
|
1161
|
+
current_value = arguments[(i*2)+1];
|
1162
|
+
|
1163
|
+
if(current_value.type == ExcelString) {
|
1164
|
+
s = current_value.string;
|
1165
|
+
if(s[0] == '<') {
|
1166
|
+
if( s[1] == '>') {
|
1167
|
+
criteria[i].type = NotEqual;
|
1168
|
+
criteria[i].comparator = new_excel_string(strndup(s+2,strlen(s)-2));
|
1169
|
+
} else if(s[1] == '=') {
|
1170
|
+
criteria[i].type = LessThanOrEqual;
|
1171
|
+
criteria[i].comparator = new_excel_string(strndup(s+2,strlen(s)-2));
|
1172
|
+
} else {
|
1173
|
+
criteria[i].type = LessThan;
|
1174
|
+
criteria[i].comparator = new_excel_string(strndup(s+1,strlen(s)-1));
|
1175
|
+
}
|
1176
|
+
} else if(s[0] == '>') {
|
1177
|
+
if(s[1] == '=') {
|
1178
|
+
criteria[i].type = MoreThanOrEqual;
|
1179
|
+
criteria[i].comparator = new_excel_string(strndup(s+2,strlen(s)-2));
|
1180
|
+
} else {
|
1181
|
+
criteria[i].type = MoreThan;
|
1182
|
+
criteria[i].comparator = new_excel_string(strndup(s+1,strlen(s)-1));
|
1183
|
+
}
|
1184
|
+
} else if(s[0] == '=') {
|
1185
|
+
criteria[i].type = Equal;
|
1186
|
+
criteria[i].comparator = new_excel_string(strndup(s+1,strlen(s)-1));
|
1187
|
+
} else {
|
1188
|
+
criteria[i].type = Equal;
|
1189
|
+
criteria[i].comparator = current_value;
|
1190
|
+
}
|
1191
|
+
} else {
|
1192
|
+
criteria[i].type = Equal;
|
1193
|
+
criteria[i].comparator = current_value;
|
1194
|
+
}
|
1195
|
+
}
|
1196
|
+
|
1197
|
+
double total = 0;
|
1198
|
+
int size = sum_range_columns * sum_range_rows;
|
1199
|
+
int j;
|
1200
|
+
int passed = 0;
|
1201
|
+
ExcelValue value_to_be_checked;
|
1202
|
+
ExcelComparison comparison;
|
1203
|
+
ExcelValue comparator;
|
1204
|
+
double number;
|
1205
|
+
// For each cell in the sum range
|
1206
|
+
for(j=0; j < size; j++ ) {
|
1207
|
+
passed = 1;
|
1208
|
+
for(i=0; i < number_of_criteria; i++) {
|
1209
|
+
value_to_be_checked = ((ExcelValue *) ((ExcelValue) criteria_range[i]).array)[j];
|
1210
|
+
comparison = criteria[i];
|
1211
|
+
comparator = comparison.comparator;
|
1212
|
+
|
1213
|
+
switch(value_to_be_checked.type) {
|
1214
|
+
case ExcelError: // Errors match only errors
|
1215
|
+
if(comparison.type != Equal) passed = 0;
|
1216
|
+
if(comparator.type != ExcelError) passed = 0;
|
1217
|
+
if(value_to_be_checked.number != comparator.number) passed = 0;
|
1218
|
+
break;
|
1219
|
+
case ExcelBoolean: // Booleans match only booleans (FIXME: I think?)
|
1220
|
+
if(comparison.type != Equal) passed = 0;
|
1221
|
+
if(comparator.type != ExcelBoolean ) passed = 0;
|
1222
|
+
if(value_to_be_checked.number != comparator.number) passed = 0;
|
1223
|
+
break;
|
1224
|
+
case ExcelEmpty:
|
1225
|
+
// if(comparator.type == ExcelEmpty) break; // FIXME: Huh? In excel blank doesn't match blank?!
|
1226
|
+
if(comparator.type != ExcelString) {
|
1227
|
+
passed = 0;
|
1228
|
+
break;
|
1229
|
+
} else {
|
1230
|
+
if(strlen(comparator.string) != 0) passed = 0; // Empty strings match blanks.
|
1231
|
+
break;
|
1232
|
+
}
|
1233
|
+
break;
|
1234
|
+
case ExcelNumber:
|
1235
|
+
if(comparator.type == ExcelNumber) {
|
1236
|
+
number = comparator.number;
|
1237
|
+
} else if(comparator.type == ExcelString) {
|
1238
|
+
number = number_from(comparator);
|
1239
|
+
if(conversion_error == 1) {
|
1240
|
+
conversion_error = 0;
|
1241
|
+
passed = 0;
|
1242
|
+
break;
|
1243
|
+
}
|
1244
|
+
} else {
|
1245
|
+
passed = 0;
|
1246
|
+
break;
|
1247
|
+
}
|
1248
|
+
switch(comparison.type) {
|
1249
|
+
case Equal:
|
1250
|
+
if(value_to_be_checked.number != number) passed = 0;
|
1251
|
+
break;
|
1252
|
+
case LessThan:
|
1253
|
+
if(value_to_be_checked.number >= number) passed = 0;
|
1254
|
+
break;
|
1255
|
+
case LessThanOrEqual:
|
1256
|
+
if(value_to_be_checked.number > number) passed = 0;
|
1257
|
+
break;
|
1258
|
+
case NotEqual:
|
1259
|
+
if(value_to_be_checked.number == number) passed = 0;
|
1260
|
+
break;
|
1261
|
+
case MoreThanOrEqual:
|
1262
|
+
if(value_to_be_checked.number < number) passed = 0;
|
1263
|
+
break;
|
1264
|
+
case MoreThan:
|
1265
|
+
if(value_to_be_checked.number <= number) passed = 0;
|
1266
|
+
break;
|
1267
|
+
}
|
1268
|
+
break;
|
1269
|
+
case ExcelString:
|
1270
|
+
// First case, the comparator is a number, simplification is that it can only be equal
|
1271
|
+
if(comparator.type == ExcelNumber) {
|
1272
|
+
if(comparison.type != Equal) {
|
1273
|
+
printf("This shouldn't be possible?");
|
1274
|
+
passed = 0;
|
1275
|
+
break;
|
1276
|
+
}
|
1277
|
+
number = number_from(value_to_be_checked);
|
1278
|
+
if(conversion_error == 1) {
|
1279
|
+
conversion_error = 0;
|
1280
|
+
passed = 0;
|
1281
|
+
break;
|
1282
|
+
}
|
1283
|
+
if(number != comparator.number) {
|
1284
|
+
passed = 0;
|
1285
|
+
break;
|
1286
|
+
} else {
|
1287
|
+
break;
|
1288
|
+
}
|
1289
|
+
// Second case, the comparator is also a string, so need to be able to do full range of tests
|
1290
|
+
} else if(comparator.type == ExcelString) {
|
1291
|
+
switch(comparison.type) {
|
1292
|
+
case Equal:
|
1293
|
+
if(excel_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
1294
|
+
break;
|
1295
|
+
case LessThan:
|
1296
|
+
if(less_than(value_to_be_checked,comparator).number == 0) passed = 0;
|
1297
|
+
break;
|
1298
|
+
case LessThanOrEqual:
|
1299
|
+
if(less_than_or_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
1300
|
+
break;
|
1301
|
+
case NotEqual:
|
1302
|
+
if(not_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
1303
|
+
break;
|
1304
|
+
case MoreThanOrEqual:
|
1305
|
+
if(more_than_or_equal(value_to_be_checked,comparator).number == 0) passed = 0;
|
1306
|
+
break;
|
1307
|
+
case MoreThan:
|
1308
|
+
if(more_than(value_to_be_checked,comparator).number == 0) passed = 0;
|
1309
|
+
break;
|
1310
|
+
}
|
1311
|
+
} else {
|
1312
|
+
passed = 0;
|
1313
|
+
break;
|
1314
|
+
}
|
1315
|
+
break;
|
1316
|
+
case ExcelRange:
|
1317
|
+
return VALUE;
|
1318
|
+
}
|
1319
|
+
if(passed == 0) break;
|
1320
|
+
}
|
1321
|
+
if(passed == 1) {
|
1322
|
+
current_value = sum_range[j];
|
1323
|
+
if(current_value.type == ExcelError) {
|
1324
|
+
return current_value;
|
1325
|
+
} else if(current_value.type == ExcelNumber) {
|
1326
|
+
total += current_value.number;
|
1327
|
+
}
|
1328
|
+
}
|
1329
|
+
}
|
1330
|
+
return new_excel_number(total);
|
1331
|
+
}
|
1332
|
+
|
1333
|
+
static ExcelValue sumif(ExcelValue check_range_v, ExcelValue criteria_v, ExcelValue sum_range_v ) {
|
1334
|
+
ExcelValue tmp_array_sumif[] = {check_range_v, criteria_v};
|
1335
|
+
return sumifs(sum_range_v,2,tmp_array_sumif);
|
1336
|
+
}
|
1337
|
+
|
1338
|
+
static ExcelValue sumif_2(ExcelValue check_range_v, ExcelValue criteria_v) {
|
1339
|
+
ExcelValue tmp_array_sumif2[] = {check_range_v, criteria_v};
|
1340
|
+
return sumifs(check_range_v,2,tmp_array_sumif2);
|
1341
|
+
}
|
1342
|
+
|
1343
|
+
static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments) {
|
1344
|
+
if(number_of_arguments <1) return VALUE;
|
1345
|
+
|
1346
|
+
int a;
|
1347
|
+
int i;
|
1348
|
+
int j;
|
1349
|
+
int rows;
|
1350
|
+
int columns;
|
1351
|
+
ExcelValue current_value;
|
1352
|
+
ExcelValue **ranges = malloc(sizeof(ExcelValue *)*number_of_arguments);
|
1353
|
+
if(ranges == 0) {
|
1354
|
+
printf("Out of memory\n");
|
1355
|
+
exit(-1);
|
1356
|
+
}
|
1357
|
+
double product = 1;
|
1358
|
+
double sum = 0;
|
1359
|
+
|
1360
|
+
// Find out dimensions of first argument
|
1361
|
+
if(arguments[0].type == ExcelRange) {
|
1362
|
+
rows = arguments[0].rows;
|
1363
|
+
columns = arguments[0].columns;
|
1364
|
+
} else {
|
1365
|
+
rows = 1;
|
1366
|
+
columns = 1;
|
1367
|
+
}
|
1368
|
+
// Extract arrays from each of the given ranges, checking for errors and bounds as we go
|
1369
|
+
for(a=0;a<number_of_arguments;a++) {
|
1370
|
+
current_value = arguments[a];
|
1371
|
+
switch(current_value.type) {
|
1372
|
+
case ExcelRange:
|
1373
|
+
if(current_value.rows != rows || current_value.columns != columns) return VALUE;
|
1374
|
+
ranges[a] = current_value.array;
|
1375
|
+
break;
|
1376
|
+
case ExcelError:
|
1377
|
+
return current_value;
|
1378
|
+
break;
|
1379
|
+
case ExcelEmpty:
|
1380
|
+
return VALUE;
|
1381
|
+
break;
|
1382
|
+
default:
|
1383
|
+
if(rows != 1 && columns !=1) return VALUE;
|
1384
|
+
ranges[a] = (ExcelValue*) new_excel_value_array(1);
|
1385
|
+
ranges[a][0] = arguments[a];
|
1386
|
+
break;
|
1387
|
+
}
|
1388
|
+
}
|
1389
|
+
|
1390
|
+
for(i=0;i<rows;i++) {
|
1391
|
+
for(j=0;j<columns;j++) {
|
1392
|
+
product = 1;
|
1393
|
+
for(a=0;a<number_of_arguments;a++) {
|
1394
|
+
current_value = ranges[a][(i*columns)+j];
|
1395
|
+
if(current_value.type == ExcelNumber) {
|
1396
|
+
product *= current_value.number;
|
1397
|
+
} else {
|
1398
|
+
product *= 0;
|
1399
|
+
}
|
1400
|
+
}
|
1401
|
+
sum += product;
|
1402
|
+
}
|
1403
|
+
}
|
1404
|
+
return new_excel_number(sum);
|
1405
|
+
}
|
1406
|
+
|
1407
|
+
static ExcelValue vlookup_3(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v) {
|
1408
|
+
return vlookup(lookup_value_v,lookup_table_v,column_number_v,TRUE);
|
1409
|
+
}
|
1410
|
+
|
1411
|
+
static ExcelValue vlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v, ExcelValue match_type_v) {
|
1412
|
+
CHECK_FOR_PASSED_ERROR(lookup_value_v)
|
1413
|
+
CHECK_FOR_PASSED_ERROR(lookup_table_v)
|
1414
|
+
CHECK_FOR_PASSED_ERROR(column_number_v)
|
1415
|
+
CHECK_FOR_PASSED_ERROR(match_type_v)
|
1416
|
+
|
1417
|
+
if(lookup_value_v.type == ExcelEmpty) return NA;
|
1418
|
+
if(lookup_table_v.type != ExcelRange) return NA;
|
1419
|
+
if(column_number_v.type != ExcelNumber) return NA;
|
1420
|
+
if(match_type_v.type != ExcelBoolean) return NA;
|
1421
|
+
|
1422
|
+
int i;
|
1423
|
+
int last_good_match = 0;
|
1424
|
+
int rows = lookup_table_v.rows;
|
1425
|
+
int columns = lookup_table_v.columns;
|
1426
|
+
ExcelValue *array = lookup_table_v.array;
|
1427
|
+
ExcelValue possible_match_v;
|
1428
|
+
|
1429
|
+
if(column_number_v.number > columns) return REF;
|
1430
|
+
if(column_number_v.number < 1) return VALUE;
|
1431
|
+
|
1432
|
+
if(match_type_v.number == false) { // Exact match required
|
1433
|
+
for(i=0; i< rows; i++) {
|
1434
|
+
possible_match_v = array[i*columns];
|
1435
|
+
if(excel_equal(lookup_value_v,possible_match_v).number == true) {
|
1436
|
+
return array[(i*columns)+(((int) column_number_v.number) - 1)];
|
1437
|
+
}
|
1438
|
+
}
|
1439
|
+
return NA;
|
1440
|
+
} else { // Highest value that is less than or equal
|
1441
|
+
for(i=0; i< rows; i++) {
|
1442
|
+
possible_match_v = array[i*columns];
|
1443
|
+
if(lookup_value_v.type != possible_match_v.type) continue;
|
1444
|
+
if(more_than(possible_match_v,lookup_value_v).number == true) {
|
1445
|
+
if(i == 0) return NA;
|
1446
|
+
return array[((i-1)*columns)+(((int) column_number_v.number) - 1)];
|
1447
|
+
} else {
|
1448
|
+
last_good_match = i;
|
1449
|
+
}
|
1450
|
+
}
|
1451
|
+
return array[(last_good_match*columns)+(((int) column_number_v.number) - 1)];
|
1452
|
+
}
|
1453
|
+
return NA;
|
1454
|
+
}
|
1455
|
+
|
1456
|
+
|
1457
|
+
|
1458
|
+
int test_functions() {
|
1459
|
+
// Test ABS
|
1460
|
+
assert(excel_abs(ONE).number == 1);
|
1461
|
+
assert(excel_abs(new_excel_number(-1)).number == 1);
|
1462
|
+
assert(excel_abs(VALUE).type == ExcelError);
|
1463
|
+
|
1464
|
+
// Test ADD
|
1465
|
+
assert(add(ONE,new_excel_number(-2.5)).number == -1.5);
|
1466
|
+
assert(add(ONE,VALUE).type == ExcelError);
|
1467
|
+
|
1468
|
+
// Test AND
|
1469
|
+
ExcelValue true_array1[] = { TRUE, new_excel_number(10)};
|
1470
|
+
ExcelValue true_array2[] = { ONE };
|
1471
|
+
ExcelValue false_array1[] = { FALSE, new_excel_number(10)};
|
1472
|
+
ExcelValue false_array2[] = { TRUE, new_excel_number(0)};
|
1473
|
+
// ExcelValue error_array1[] = { new_excel_number(10)}; // Not implemented
|
1474
|
+
ExcelValue error_array2[] = { TRUE, NA};
|
1475
|
+
assert(excel_and(2,true_array1).number == 1);
|
1476
|
+
assert(excel_and(1,true_array2).number == 1);
|
1477
|
+
assert(excel_and(2,false_array1).number == 0);
|
1478
|
+
assert(excel_and(2,false_array2).number == 0);
|
1479
|
+
// assert(excel_and(1,error_array1).type == ExcelError); // Not implemented
|
1480
|
+
assert(excel_and(2,error_array2).type == ExcelError);
|
1481
|
+
|
1482
|
+
// Test AVERAGE
|
1483
|
+
ExcelValue array1[] = { new_excel_number(10), new_excel_number(5), TRUE, FALSE};
|
1484
|
+
ExcelValue array1_v = new_excel_range(array1,2,2);
|
1485
|
+
ExcelValue array2[] = { array1_v, new_excel_number(9), new_excel_string("Hello")};
|
1486
|
+
ExcelValue array3[] = { array1_v, new_excel_number(9), new_excel_string("Hello"), VALUE};
|
1487
|
+
assert(average(4, array1).number == 7.5);
|
1488
|
+
assert(average(3, array2).number == 8);
|
1489
|
+
assert(average(4, array3).type == ExcelError);
|
1490
|
+
|
1491
|
+
// Test CHOOSE
|
1492
|
+
assert(choose(ONE,4,array1).number == 10);
|
1493
|
+
assert(choose(new_excel_number(4),4,array1).type == ExcelBoolean);
|
1494
|
+
assert(choose(new_excel_number(0),4,array1).type == ExcelError);
|
1495
|
+
assert(choose(new_excel_number(5),4,array1).type == ExcelError);
|
1496
|
+
assert(choose(ONE,4,array3).type == ExcelError);
|
1497
|
+
|
1498
|
+
// Test COUNT
|
1499
|
+
assert(count(4,array1).number == 2);
|
1500
|
+
assert(count(3,array2).number == 3);
|
1501
|
+
assert(count(4,array3).number == 3);
|
1502
|
+
|
1503
|
+
// Test COUNTA
|
1504
|
+
ExcelValue count_a_test_array_1[] = { new_excel_number(10), new_excel_number(5), TRUE, FALSE, new_excel_string("Hello"), VALUE, BLANK};
|
1505
|
+
ExcelValue count_a_test_array_1_v = new_excel_range(count_a_test_array_1,7,1);
|
1506
|
+
ExcelValue count_a_test_array_2[] = {new_excel_string("Bye"),count_a_test_array_1_v};
|
1507
|
+
assert(counta(7, count_a_test_array_1).number == 6);
|
1508
|
+
assert(counta(2, count_a_test_array_2).number == 7);
|
1509
|
+
|
1510
|
+
// Test divide
|
1511
|
+
assert(divide(new_excel_number(12.4),new_excel_number(3.2)).number == 3.875);
|
1512
|
+
assert(divide(new_excel_number(12.4),new_excel_number(0)).type == ExcelError);
|
1513
|
+
|
1514
|
+
// Test excel_equal
|
1515
|
+
assert(excel_equal(new_excel_number(1.2),new_excel_number(3.4)).type == ExcelBoolean);
|
1516
|
+
assert(excel_equal(new_excel_number(1.2),new_excel_number(3.4)).number == false);
|
1517
|
+
assert(excel_equal(new_excel_number(1.2),new_excel_number(1.2)).number == true);
|
1518
|
+
assert(excel_equal(new_excel_string("hello"), new_excel_string("HELLO")).number == true);
|
1519
|
+
assert(excel_equal(new_excel_string("hello world"), new_excel_string("HELLO")).number == false);
|
1520
|
+
assert(excel_equal(new_excel_string("1"), ONE).number == false);
|
1521
|
+
assert(excel_equal(DIV0, ONE).type == ExcelError);
|
1522
|
+
|
1523
|
+
// Test not_equal
|
1524
|
+
assert(not_equal(new_excel_number(1.2),new_excel_number(3.4)).type == ExcelBoolean);
|
1525
|
+
assert(not_equal(new_excel_number(1.2),new_excel_number(3.4)).number == true);
|
1526
|
+
assert(not_equal(new_excel_number(1.2),new_excel_number(1.2)).number == false);
|
1527
|
+
assert(not_equal(new_excel_string("hello"), new_excel_string("HELLO")).number == false);
|
1528
|
+
assert(not_equal(new_excel_string("hello world"), new_excel_string("HELLO")).number == true);
|
1529
|
+
assert(not_equal(new_excel_string("1"), ONE).number == true);
|
1530
|
+
assert(not_equal(DIV0, ONE).type == ExcelError);
|
1531
|
+
|
1532
|
+
// Test excel_if
|
1533
|
+
// Two argument version
|
1534
|
+
assert(excel_if_2(TRUE,new_excel_number(10)).type == ExcelNumber);
|
1535
|
+
assert(excel_if_2(TRUE,new_excel_number(10)).number == 10);
|
1536
|
+
assert(excel_if_2(FALSE,new_excel_number(10)).type == ExcelBoolean);
|
1537
|
+
assert(excel_if_2(FALSE,new_excel_number(10)).number == false);
|
1538
|
+
assert(excel_if_2(NA,new_excel_number(10)).type == ExcelError);
|
1539
|
+
// Three argument version
|
1540
|
+
assert(excel_if(TRUE,new_excel_number(10),new_excel_number(20)).type == ExcelNumber);
|
1541
|
+
assert(excel_if(TRUE,new_excel_number(10),new_excel_number(20)).number == 10);
|
1542
|
+
assert(excel_if(FALSE,new_excel_number(10),new_excel_number(20)).type == ExcelNumber);
|
1543
|
+
assert(excel_if(FALSE,new_excel_number(10),new_excel_number(20)).number == 20);
|
1544
|
+
assert(excel_if(NA,new_excel_number(10),new_excel_number(20)).type == ExcelError);
|
1545
|
+
|
1546
|
+
// Test excel_match
|
1547
|
+
ExcelValue excel_match_array_1[] = { new_excel_number(10), new_excel_number(100) };
|
1548
|
+
ExcelValue excel_match_array_1_v = new_excel_range(excel_match_array_1,1,2);
|
1549
|
+
ExcelValue excel_match_array_2[] = { new_excel_string("Pear"), new_excel_string("Bear"), new_excel_string("Apple") };
|
1550
|
+
ExcelValue excel_match_array_2_v = new_excel_range(excel_match_array_2,3,1);
|
1551
|
+
ExcelValue excel_match_array_4[] = { ONE, BLANK, new_excel_number(0) };
|
1552
|
+
ExcelValue excel_match_array_4_v = new_excel_range(excel_match_array_4,1,3);
|
1553
|
+
ExcelValue excel_match_array_5[] = { ONE, new_excel_number(0), BLANK };
|
1554
|
+
ExcelValue excel_match_array_5_v = new_excel_range(excel_match_array_5,1,3);
|
1555
|
+
|
1556
|
+
// Two argument version
|
1557
|
+
assert(excel_match_2(new_excel_number(10),excel_match_array_1_v).number == 1);
|
1558
|
+
assert(excel_match_2(new_excel_number(100),excel_match_array_1_v).number == 2);
|
1559
|
+
assert(excel_match_2(new_excel_number(1000),excel_match_array_1_v).type == ExcelError);
|
1560
|
+
assert(excel_match_2(new_excel_number(0), excel_match_array_4_v).number == 2);
|
1561
|
+
assert(excel_match_2(BLANK, excel_match_array_5_v).number == 2);
|
1562
|
+
|
1563
|
+
// Three argument version
|
1564
|
+
assert(excel_match(new_excel_number(10.0), excel_match_array_1_v, new_excel_number(0) ).number == 1);
|
1565
|
+
assert(excel_match(new_excel_number(100.0), excel_match_array_1_v, new_excel_number(0) ).number == 2);
|
1566
|
+
assert(excel_match(new_excel_number(1000.0), excel_match_array_1_v, new_excel_number(0) ).type == ExcelError);
|
1567
|
+
assert(excel_match(new_excel_string("bEAr"), excel_match_array_2_v, new_excel_number(0) ).number == 2);
|
1568
|
+
assert(excel_match(new_excel_number(1000.0), excel_match_array_1_v, ONE ).number == 2);
|
1569
|
+
assert(excel_match(new_excel_number(1.0), excel_match_array_1_v, ONE ).type == ExcelError);
|
1570
|
+
assert(excel_match(new_excel_string("Care"), excel_match_array_2_v, new_excel_number(-1) ).number == 1 );
|
1571
|
+
assert(excel_match(new_excel_string("Zebra"), excel_match_array_2_v, new_excel_number(-1) ).type == ExcelError);
|
1572
|
+
assert(excel_match(new_excel_string("a"), excel_match_array_2_v, new_excel_number(-1) ).number == 2);
|
1573
|
+
|
1574
|
+
// When not given a range
|
1575
|
+
assert(excel_match(new_excel_number(10.0), new_excel_number(10), new_excel_number(0.0)).number == 1);
|
1576
|
+
assert(excel_match(new_excel_number(20.0), new_excel_number(10), new_excel_number(0.0)).type == ExcelError);
|
1577
|
+
assert(excel_match(new_excel_number(10.0), excel_match_array_1_v, BLANK).number == 1);
|
1578
|
+
|
1579
|
+
// Test more than on
|
1580
|
+
// .. numbers
|
1581
|
+
assert(more_than(ONE,new_excel_number(2)).number == false);
|
1582
|
+
assert(more_than(ONE,ONE).number == false);
|
1583
|
+
assert(more_than(ONE,new_excel_number(0)).number == true);
|
1584
|
+
// .. booleans
|
1585
|
+
assert(more_than(FALSE,FALSE).number == false);
|
1586
|
+
assert(more_than(FALSE,TRUE).number == false);
|
1587
|
+
assert(more_than(TRUE,FALSE).number == true);
|
1588
|
+
assert(more_than(TRUE,TRUE).number == false);
|
1589
|
+
// ..strings
|
1590
|
+
assert(more_than(new_excel_string("HELLO"),new_excel_string("Ardvark")).number == true);
|
1591
|
+
assert(more_than(new_excel_string("HELLO"),new_excel_string("world")).number == false);
|
1592
|
+
assert(more_than(new_excel_string("HELLO"),new_excel_string("hello")).number == false);
|
1593
|
+
// ..blanks
|
1594
|
+
assert(more_than(BLANK,ONE).number == false);
|
1595
|
+
assert(more_than(BLANK,new_excel_number(-1)).number == true);
|
1596
|
+
assert(more_than(ONE,BLANK).number == true);
|
1597
|
+
assert(more_than(new_excel_number(-1),BLANK).number == false);
|
1598
|
+
|
1599
|
+
// Test less than on
|
1600
|
+
// .. numbers
|
1601
|
+
assert(less_than(ONE,new_excel_number(2)).number == true);
|
1602
|
+
assert(less_than(ONE,ONE).number == false);
|
1603
|
+
assert(less_than(ONE,new_excel_number(0)).number == false);
|
1604
|
+
// .. booleans
|
1605
|
+
assert(less_than(FALSE,FALSE).number == false);
|
1606
|
+
assert(less_than(FALSE,TRUE).number == true);
|
1607
|
+
assert(less_than(TRUE,FALSE).number == false);
|
1608
|
+
assert(less_than(TRUE,TRUE).number == false);
|
1609
|
+
// ..strings
|
1610
|
+
assert(less_than(new_excel_string("HELLO"),new_excel_string("Ardvark")).number == false);
|
1611
|
+
assert(less_than(new_excel_string("HELLO"),new_excel_string("world")).number == true);
|
1612
|
+
assert(less_than(new_excel_string("HELLO"),new_excel_string("hello")).number == false);
|
1613
|
+
// ..blanks
|
1614
|
+
assert(less_than(BLANK,ONE).number == true);
|
1615
|
+
assert(less_than(BLANK,new_excel_number(-1)).number == false);
|
1616
|
+
assert(less_than(ONE,BLANK).number == false);
|
1617
|
+
assert(less_than(new_excel_number(-1),BLANK).number == true);
|
1618
|
+
|
1619
|
+
// Test FIND function
|
1620
|
+
// ... should find the first occurrence of one string in another, returning :value if the string doesn't match
|
1621
|
+
assert(find_2(new_excel_string("one"),new_excel_string("onetwothree")).number == 1);
|
1622
|
+
assert(find_2(new_excel_string("one"),new_excel_string("twoonethree")).number == 4);
|
1623
|
+
assert(find_2(new_excel_string("one"),new_excel_string("twoonthree")).type == ExcelError);
|
1624
|
+
// ... should find the first occurrence of one string in another after a given index, returning :value if the string doesn't match
|
1625
|
+
assert(find(new_excel_string("one"),new_excel_string("onetwothree"),ONE).number == 1);
|
1626
|
+
assert(find(new_excel_string("one"),new_excel_string("twoonethree"),new_excel_number(5)).type == ExcelError);
|
1627
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_number(2)).number == 4);
|
1628
|
+
// ... should be possible for the start_num to be a string, if that string converts to a number
|
1629
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_string("2")).number == 4);
|
1630
|
+
// ... should return a :value error when given anything but a number as the third argument
|
1631
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_string("a")).type == ExcelError);
|
1632
|
+
// ... should return a :value error when given a third argument that is less than 1 or greater than the length of the string
|
1633
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_number(0)).type == ExcelError);
|
1634
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_number(-1)).type == ExcelError);
|
1635
|
+
assert(find(new_excel_string("one"),new_excel_string("oneone"),new_excel_number(7)).type == ExcelError);
|
1636
|
+
// ... BLANK in the first argument matches any character
|
1637
|
+
assert(find_2(BLANK,new_excel_string("abcdefg")).number == 1);
|
1638
|
+
assert(find(BLANK,new_excel_string("abcdefg"),new_excel_number(4)).number == 4);
|
1639
|
+
// ... should treat BLANK in the second argument as an empty string
|
1640
|
+
assert(find_2(BLANK,BLANK).number == 1);
|
1641
|
+
assert(find_2(new_excel_string("a"),BLANK).type == ExcelError);
|
1642
|
+
// ... should return an error if any argument is an error
|
1643
|
+
assert(find(new_excel_string("one"),new_excel_string("onetwothree"),NA).type == ExcelError);
|
1644
|
+
assert(find(new_excel_string("one"),NA,ONE).type == ExcelError);
|
1645
|
+
assert(find(NA,new_excel_string("onetwothree"),ONE).type == ExcelError);
|
1646
|
+
|
1647
|
+
// Test the IFERROR function
|
1648
|
+
assert(iferror(new_excel_string("ok"),ONE).type == ExcelString);
|
1649
|
+
assert(iferror(VALUE,ONE).type == ExcelNumber);
|
1650
|
+
|
1651
|
+
// Test the INDEX function
|
1652
|
+
ExcelValue index_array_1[] = { new_excel_number(10), new_excel_number(20), BLANK };
|
1653
|
+
ExcelValue index_array_1_v_column = new_excel_range(index_array_1,3,1);
|
1654
|
+
ExcelValue index_array_1_v_row = new_excel_range(index_array_1,1,3);
|
1655
|
+
ExcelValue index_array_2[] = { BLANK, ONE, new_excel_number(10), new_excel_number(11), new_excel_number(100), new_excel_number(101) };
|
1656
|
+
ExcelValue index_array_2_v = new_excel_range(index_array_2,3,2);
|
1657
|
+
// ... if given one argument should return the value at that offset in the range
|
1658
|
+
assert(excel_index_2(index_array_1_v_column,new_excel_number(2.0)).number == 20);
|
1659
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(2.0)).number == 20);
|
1660
|
+
// ... but not if the range is not a single row or single column
|
1661
|
+
assert(excel_index_2(index_array_2_v,new_excel_number(2.0)).type == ExcelError);
|
1662
|
+
// ... it should return the value in the array at position row_number, column_number
|
1663
|
+
assert(excel_index(new_excel_number(10),ONE,ONE).number == 10);
|
1664
|
+
assert(excel_index(index_array_2_v,new_excel_number(1.0),new_excel_number(2.0)).number == 1);
|
1665
|
+
assert(excel_index(index_array_2_v,new_excel_number(2.0),new_excel_number(1.0)).number == 10);
|
1666
|
+
assert(excel_index(index_array_2_v,new_excel_number(3.0),new_excel_number(1.0)).number == 100);
|
1667
|
+
assert(excel_index(index_array_2_v,new_excel_number(3.0),new_excel_number(3.0)).type == ExcelError);
|
1668
|
+
// ... it should return ZERO not blank, if a blank cell is picked
|
1669
|
+
assert(excel_index(index_array_2_v,new_excel_number(1.0),new_excel_number(1.0)).type == ExcelNumber);
|
1670
|
+
assert(excel_index(index_array_2_v,new_excel_number(1.0),new_excel_number(1.0)).number == 0);
|
1671
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(3.0)).type == ExcelNumber);
|
1672
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(3.0)).number == 0);
|
1673
|
+
// ... it should return the whole row if given a zero column number
|
1674
|
+
ExcelValue index_result_1_v = excel_index(index_array_2_v,new_excel_number(1.0),new_excel_number(0.0));
|
1675
|
+
assert(index_result_1_v.type == ExcelRange);
|
1676
|
+
assert(index_result_1_v.rows == 1);
|
1677
|
+
assert(index_result_1_v.columns == 2);
|
1678
|
+
ExcelValue *index_result_1_a = index_result_1_v.array;
|
1679
|
+
assert(index_result_1_a[0].number == 0);
|
1680
|
+
assert(index_result_1_a[1].number == 1);
|
1681
|
+
// ... it should return the whole column if given a zero row number
|
1682
|
+
ExcelValue index_result_2_v = excel_index(index_array_2_v,new_excel_number(0),new_excel_number(1.0));
|
1683
|
+
assert(index_result_2_v.type == ExcelRange);
|
1684
|
+
assert(index_result_2_v.rows == 3);
|
1685
|
+
assert(index_result_2_v.columns == 1);
|
1686
|
+
ExcelValue *index_result_2_a = index_result_2_v.array;
|
1687
|
+
assert(index_result_2_a[0].number == 0);
|
1688
|
+
assert(index_result_2_a[1].number == 10);
|
1689
|
+
assert(index_result_2_a[2].number == 100);
|
1690
|
+
// ... it should return a :ref error when given arguments outside array range
|
1691
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(-1)).type == ExcelError);
|
1692
|
+
assert(excel_index_2(index_array_1_v_row,new_excel_number(4)).type == ExcelError);
|
1693
|
+
// ... it should treat BLANK as zero if given as a required row or column number
|
1694
|
+
assert(excel_index(index_array_2_v,new_excel_number(1.0),BLANK).type == ExcelRange);
|
1695
|
+
assert(excel_index(index_array_2_v,BLANK,new_excel_number(2.0)).type == ExcelRange);
|
1696
|
+
// ... it should return an error if an argument is an error
|
1697
|
+
assert(excel_index(NA,NA,NA).type == ExcelError);
|
1698
|
+
|
1699
|
+
// LEFT(string,[characters])
|
1700
|
+
// ... should return the left n characters from a string
|
1701
|
+
assert(strcmp(left_1(new_excel_string("ONE")).string,"O") == 0);
|
1702
|
+
assert(strcmp(left(new_excel_string("ONE"),ONE).string,"O") == 0);
|
1703
|
+
assert(strcmp(left(new_excel_string("ONE"),new_excel_number(3)).string,"ONE") == 0);
|
1704
|
+
// ... should turn numbers into strings before processing
|
1705
|
+
assert(strcmp(left(new_excel_number(1.31e12),new_excel_number(3)).string, "131") == 0);
|
1706
|
+
// ... should turn booleans into the words TRUE and FALSE before processing
|
1707
|
+
assert(strcmp(left(TRUE,new_excel_number(3)).string,"TRU") == 0);
|
1708
|
+
assert(strcmp(left(FALSE,new_excel_number(3)).string,"FAL") == 0);
|
1709
|
+
// ... should return BLANK if given BLANK for either argument
|
1710
|
+
assert(left(BLANK,new_excel_number(3)).type == ExcelEmpty);
|
1711
|
+
assert(left(new_excel_string("ONE"),BLANK).type == ExcelEmpty);
|
1712
|
+
// ... should return an error if an argument is an error
|
1713
|
+
assert(left_1(NA).type == ExcelError);
|
1714
|
+
assert(left(new_excel_string("ONE"),NA).type == ExcelError);
|
1715
|
+
|
1716
|
+
// Test less than or equal to
|
1717
|
+
// .. numbers
|
1718
|
+
assert(less_than_or_equal(ONE,new_excel_number(2)).number == true);
|
1719
|
+
assert(less_than_or_equal(ONE,ONE).number == true);
|
1720
|
+
assert(less_than_or_equal(ONE,new_excel_number(0)).number == false);
|
1721
|
+
// .. booleans
|
1722
|
+
assert(less_than_or_equal(FALSE,FALSE).number == true);
|
1723
|
+
assert(less_than_or_equal(FALSE,TRUE).number == true);
|
1724
|
+
assert(less_than_or_equal(TRUE,FALSE).number == false);
|
1725
|
+
assert(less_than_or_equal(TRUE,TRUE).number == true);
|
1726
|
+
// ..strings
|
1727
|
+
assert(less_than_or_equal(new_excel_string("HELLO"),new_excel_string("Ardvark")).number == false);
|
1728
|
+
assert(less_than_or_equal(new_excel_string("HELLO"),new_excel_string("world")).number == true);
|
1729
|
+
assert(less_than_or_equal(new_excel_string("HELLO"),new_excel_string("hello")).number == true);
|
1730
|
+
// ..blanks
|
1731
|
+
assert(less_than_or_equal(BLANK,ONE).number == true);
|
1732
|
+
assert(less_than_or_equal(BLANK,new_excel_number(-1)).number == false);
|
1733
|
+
assert(less_than_or_equal(ONE,BLANK).number == false);
|
1734
|
+
assert(less_than_or_equal(new_excel_number(-1),BLANK).number == true);
|
1735
|
+
|
1736
|
+
// Test MAX
|
1737
|
+
assert(max(4, array1).number == 10);
|
1738
|
+
assert(max(3, array2).number == 10);
|
1739
|
+
assert(max(4, array3).type == ExcelError);
|
1740
|
+
|
1741
|
+
// Test MIN
|
1742
|
+
assert(min(4, array1).number == 5);
|
1743
|
+
assert(min(3, array2).number == 5);
|
1744
|
+
assert(min(4, array3).type == ExcelError);
|
1745
|
+
|
1746
|
+
// Test MOD
|
1747
|
+
// ... should return the remainder of a number
|
1748
|
+
assert(mod(new_excel_number(10), new_excel_number(3)).number == 1.0);
|
1749
|
+
assert(mod(new_excel_number(10), new_excel_number(5)).number == 0.0);
|
1750
|
+
// ... should be possible for the the arguments to be strings, if they convert to a number
|
1751
|
+
assert(mod(new_excel_string("3.5"),new_excel_string("2")).number == 1.5);
|
1752
|
+
// ... should treat BLANK as zero
|
1753
|
+
assert(mod(BLANK,new_excel_number(10)).number == 0);
|
1754
|
+
assert(mod(new_excel_number(10),BLANK).type == ExcelError);
|
1755
|
+
assert(mod(BLANK,BLANK).type == ExcelError);
|
1756
|
+
// ... should treat true as 1 and FALSE as 0
|
1757
|
+
assert((mod(new_excel_number(1.1),TRUE).number - 0.1) < 0.001);
|
1758
|
+
assert(mod(new_excel_number(1.1),FALSE).type == ExcelError);
|
1759
|
+
assert(mod(FALSE,new_excel_number(10)).number == 0);
|
1760
|
+
// ... should return an error when given inappropriate arguments
|
1761
|
+
assert(mod(new_excel_string("Asdasddf"),new_excel_string("adsfads")).type == ExcelError);
|
1762
|
+
// ... should return an error if an argument is an error
|
1763
|
+
assert(mod(new_excel_number(1),VALUE).type == ExcelError);
|
1764
|
+
assert(mod(VALUE,new_excel_number(1)).type == ExcelError);
|
1765
|
+
assert(mod(VALUE,VALUE).type == ExcelError);
|
1766
|
+
|
1767
|
+
// Test more than or equal to on
|
1768
|
+
// .. numbers
|
1769
|
+
assert(more_than_or_equal(ONE,new_excel_number(2)).number == false);
|
1770
|
+
assert(more_than_or_equal(ONE,ONE).number == true);
|
1771
|
+
assert(more_than_or_equal(ONE,new_excel_number(0)).number == true);
|
1772
|
+
// .. booleans
|
1773
|
+
assert(more_than_or_equal(FALSE,FALSE).number == true);
|
1774
|
+
assert(more_than_or_equal(FALSE,TRUE).number == false);
|
1775
|
+
assert(more_than_or_equal(TRUE,FALSE).number == true);
|
1776
|
+
assert(more_than_or_equal(TRUE,TRUE).number == true);
|
1777
|
+
// ..strings
|
1778
|
+
assert(more_than_or_equal(new_excel_string("HELLO"),new_excel_string("Ardvark")).number == true);
|
1779
|
+
assert(more_than_or_equal(new_excel_string("HELLO"),new_excel_string("world")).number == false);
|
1780
|
+
assert(more_than_or_equal(new_excel_string("HELLO"),new_excel_string("hello")).number == true);
|
1781
|
+
// ..blanks
|
1782
|
+
assert(more_than_or_equal(BLANK,BLANK).number == true);
|
1783
|
+
assert(more_than_or_equal(BLANK,ONE).number == false);
|
1784
|
+
assert(more_than_or_equal(BLANK,new_excel_number(-1)).number == true);
|
1785
|
+
assert(more_than_or_equal(ONE,BLANK).number == true);
|
1786
|
+
assert(more_than_or_equal(new_excel_number(-1),BLANK).number == false);
|
1787
|
+
|
1788
|
+
// Test negative
|
1789
|
+
// ... should return the negative of its arguments
|
1790
|
+
assert(negative(new_excel_number(1)).number == -1);
|
1791
|
+
assert(negative(new_excel_number(-1)).number == 1);
|
1792
|
+
// ... should treat strings that only contain numbers as numbers
|
1793
|
+
assert(negative(new_excel_string("10")).number == -10);
|
1794
|
+
assert(negative(new_excel_string("-1.3")).number == 1.3);
|
1795
|
+
// ... should return an error when given inappropriate arguments
|
1796
|
+
assert(negative(new_excel_string("Asdasddf")).type == ExcelError);
|
1797
|
+
// ... should treat BLANK as zero
|
1798
|
+
assert(negative(BLANK).number == 0);
|
1799
|
+
|
1800
|
+
// Test PMT(rate,number_of_periods,present_value) - optional arguments not yet implemented
|
1801
|
+
// ... should calculate the monthly payment required for a given principal, interest rate and loan period
|
1802
|
+
assert((pmt(new_excel_number(0.1),new_excel_number(10),new_excel_number(100)).number - -16.27) < 0.01);
|
1803
|
+
assert((pmt(new_excel_number(0.0123),new_excel_number(99.1),new_excel_number(123.32)).number - -2.159) < 0.01);
|
1804
|
+
assert((pmt(new_excel_number(0),new_excel_number(2),new_excel_number(10)).number - -5) < 0.01);
|
1805
|
+
|
1806
|
+
// Test power
|
1807
|
+
// ... should return sum of its arguments
|
1808
|
+
assert(power(new_excel_number(2),new_excel_number(3)).number == 8);
|
1809
|
+
assert(power(new_excel_number(4.0),new_excel_number(0.5)).number == 2.0);
|
1810
|
+
|
1811
|
+
// Test round
|
1812
|
+
assert(excel_round(new_excel_number(1.1), new_excel_number(0)).number == 1.0);
|
1813
|
+
assert(excel_round(new_excel_number(1.5), new_excel_number(0)).number == 2.0);
|
1814
|
+
assert(excel_round(new_excel_number(1.56),new_excel_number(1)).number == 1.6);
|
1815
|
+
assert(excel_round(new_excel_number(-1.56),new_excel_number(1)).number == -1.6);
|
1816
|
+
|
1817
|
+
// Test rounddown
|
1818
|
+
assert(rounddown(new_excel_number(1.1), new_excel_number(0)).number == 1.0);
|
1819
|
+
assert(rounddown(new_excel_number(1.5), new_excel_number(0)).number == 1.0);
|
1820
|
+
assert(rounddown(new_excel_number(1.56),new_excel_number(1)).number == 1.5);
|
1821
|
+
assert(rounddown(new_excel_number(-1.56),new_excel_number(1)).number == -1.5);
|
1822
|
+
|
1823
|
+
// Test roundup
|
1824
|
+
assert(roundup(new_excel_number(1.1), new_excel_number(0)).number == 2.0);
|
1825
|
+
assert(roundup(new_excel_number(1.5), new_excel_number(0)).number == 2.0);
|
1826
|
+
assert(roundup(new_excel_number(1.56),new_excel_number(1)).number == 1.6);
|
1827
|
+
assert(roundup(new_excel_number(-1.56),new_excel_number(1)).number == -1.6);
|
1828
|
+
|
1829
|
+
// Test string joining
|
1830
|
+
ExcelValue string_join_array_1[] = {new_excel_string("Hello "), new_excel_string("world")};
|
1831
|
+
ExcelValue string_join_array_2[] = {new_excel_string("Hello "), new_excel_string("world"), new_excel_string("!")};
|
1832
|
+
ExcelValue string_join_array_3[] = {new_excel_string("Top "), new_excel_number(10.0)};
|
1833
|
+
ExcelValue string_join_array_4[] = {new_excel_string("Top "), new_excel_number(10.5)};
|
1834
|
+
ExcelValue string_join_array_5[] = {new_excel_string("Top "), TRUE, FALSE};
|
1835
|
+
// ... should return a string by combining its arguments
|
1836
|
+
// inspect_excel_value(string_join(2, string_join_array_1));
|
1837
|
+
assert(string_join(2, string_join_array_1).string[6] == 'w');
|
1838
|
+
// ... should cope with an arbitrary number of arguments
|
1839
|
+
assert(string_join(3, string_join_array_2).string[11] == '!');
|
1840
|
+
// ... should convert values to strings as it goes
|
1841
|
+
assert(string_join(2, string_join_array_3).string[4] == '1');
|
1842
|
+
// ... should convert integer values into strings without decimal points
|
1843
|
+
assert(string_join(2, string_join_array_3).string[7] == '\0');
|
1844
|
+
assert(string_join(2, string_join_array_4).string[7] == '5');
|
1845
|
+
// ... should convert TRUE and FALSE into strings
|
1846
|
+
assert(string_join(3,string_join_array_5).string[4] == 'T');
|
1847
|
+
|
1848
|
+
// Test SUBTOTAL function
|
1849
|
+
ExcelValue subtotal_array_1[] = {new_excel_number(10),new_excel_number(100),BLANK};
|
1850
|
+
ExcelValue subtotal_array_1_v = new_excel_range(subtotal_array_1,3,1);
|
1851
|
+
ExcelValue subtotal_array_2[] = {new_excel_number(1),new_excel_string("two"),subtotal_array_1_v};
|
1852
|
+
|
1853
|
+
assert(subtotal(new_excel_number(1.0),3,subtotal_array_2).number == 111.0/3.0);
|
1854
|
+
assert(subtotal(new_excel_number(2.0),3,subtotal_array_2).number == 3);
|
1855
|
+
assert(subtotal(new_excel_number(3.0),7, count_a_test_array_1).number == 6);
|
1856
|
+
assert(subtotal(new_excel_number(3.0),3,subtotal_array_2).number == 4);
|
1857
|
+
assert(subtotal(new_excel_number(9.0),3,subtotal_array_2).number == 111);
|
1858
|
+
assert(subtotal(new_excel_number(101.0),3,subtotal_array_2).number == 111.0/3.0);
|
1859
|
+
assert(subtotal(new_excel_number(102.0),3,subtotal_array_2).number == 3);
|
1860
|
+
assert(subtotal(new_excel_number(103.0),3,subtotal_array_2).number == 4);
|
1861
|
+
assert(subtotal(new_excel_number(109.0),3,subtotal_array_2).number == 111);
|
1862
|
+
|
1863
|
+
// Test SUMIFS function
|
1864
|
+
ExcelValue sumifs_array_1[] = {new_excel_number(10),new_excel_number(100),BLANK};
|
1865
|
+
ExcelValue sumifs_array_1_v = new_excel_range(sumifs_array_1,3,1);
|
1866
|
+
ExcelValue sumifs_array_2[] = {new_excel_string("pear"),new_excel_string("bear"),new_excel_string("apple")};
|
1867
|
+
ExcelValue sumifs_array_2_v = new_excel_range(sumifs_array_2,3,1);
|
1868
|
+
ExcelValue sumifs_array_3[] = {new_excel_number(1),new_excel_number(2),new_excel_number(3),new_excel_number(4),new_excel_number(5),new_excel_number(5)};
|
1869
|
+
ExcelValue sumifs_array_3_v = new_excel_range(sumifs_array_3,6,1);
|
1870
|
+
ExcelValue sumifs_array_4[] = {new_excel_string("CO2"),new_excel_string("CH4"),new_excel_string("N2O"),new_excel_string("CH4"),new_excel_string("N2O"),new_excel_string("CO2")};
|
1871
|
+
ExcelValue sumifs_array_4_v = new_excel_range(sumifs_array_4,6,1);
|
1872
|
+
ExcelValue sumifs_array_5[] = {new_excel_string("1A"),new_excel_string("1A"),new_excel_string("1A"),new_excel_number(4),new_excel_number(4),new_excel_number(5)};
|
1873
|
+
ExcelValue sumifs_array_5_v = new_excel_range(sumifs_array_5,6,1);
|
1874
|
+
|
1875
|
+
// ... should only sum values that meet all of the criteria
|
1876
|
+
ExcelValue sumifs_array_6[] = { sumifs_array_1_v, new_excel_number(10), sumifs_array_2_v, new_excel_string("Bear") };
|
1877
|
+
assert(sumifs(sumifs_array_1_v,4,sumifs_array_6).number == 0.0);
|
1878
|
+
|
1879
|
+
ExcelValue sumifs_array_7[] = { sumifs_array_1_v, new_excel_number(10), sumifs_array_2_v, new_excel_string("Pear") };
|
1880
|
+
assert(sumifs(sumifs_array_1_v,4,sumifs_array_7).number == 10.0);
|
1881
|
+
|
1882
|
+
// ... should work when single cells are given where ranges expected
|
1883
|
+
ExcelValue sumifs_array_8[] = { new_excel_string("CAR"), new_excel_string("CAR"), new_excel_string("FCV"), new_excel_string("FCV")};
|
1884
|
+
assert(sumifs(new_excel_number(0.143897265452564), 4, sumifs_array_8).number == 0.143897265452564);
|
1885
|
+
|
1886
|
+
// ... should match numbers with strings that contain numbers
|
1887
|
+
ExcelValue sumifs_array_9[] = { new_excel_number(10), new_excel_string("10.0")};
|
1888
|
+
assert(sumifs(new_excel_number(100),2,sumifs_array_9).number == 100);
|
1889
|
+
|
1890
|
+
ExcelValue sumifs_array_10[] = { sumifs_array_4_v, new_excel_string("CO2"), sumifs_array_5_v, new_excel_number(2)};
|
1891
|
+
assert(sumifs(sumifs_array_3_v,4, sumifs_array_10).number == 0);
|
1892
|
+
|
1893
|
+
// ... should match with strings that contain criteria
|
1894
|
+
ExcelValue sumifs_array_10a[] = { sumifs_array_3_v, new_excel_string("=5")};
|
1895
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10a).number == 10);
|
1896
|
+
|
1897
|
+
ExcelValue sumifs_array_10b[] = { sumifs_array_3_v, new_excel_string("<>3")};
|
1898
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10b).number == 17);
|
1899
|
+
|
1900
|
+
ExcelValue sumifs_array_10c[] = { sumifs_array_3_v, new_excel_string("<3")};
|
1901
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10c).number == 3);
|
1902
|
+
|
1903
|
+
ExcelValue sumifs_array_10d[] = { sumifs_array_3_v, new_excel_string("<=3")};
|
1904
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10d).number == 6);
|
1905
|
+
|
1906
|
+
ExcelValue sumifs_array_10e[] = { sumifs_array_3_v, new_excel_string(">3")};
|
1907
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10e).number == 14);
|
1908
|
+
|
1909
|
+
ExcelValue sumifs_array_10f[] = { sumifs_array_3_v, new_excel_string(">=3")};
|
1910
|
+
assert(sumifs(sumifs_array_3_v,2, sumifs_array_10f).number == 17);
|
1911
|
+
|
1912
|
+
// ... should treat BLANK as an empty string when in the check_range, but not in the criteria
|
1913
|
+
ExcelValue sumifs_array_11[] = { BLANK, new_excel_number(20)};
|
1914
|
+
assert(sumifs(new_excel_number(100),2,sumifs_array_11).number == 0);
|
1915
|
+
|
1916
|
+
ExcelValue sumifs_array_12[] = {BLANK, new_excel_string("")};
|
1917
|
+
assert(sumifs(new_excel_number(100),2,sumifs_array_12).number == 100);
|
1918
|
+
|
1919
|
+
ExcelValue sumifs_array_13[] = {BLANK, BLANK};
|
1920
|
+
assert(sumifs(new_excel_number(100),2,sumifs_array_13).number == 0);
|
1921
|
+
|
1922
|
+
// ... should return an error if range argument is an error
|
1923
|
+
assert(sumifs(REF,2,sumifs_array_13).type == ExcelError);
|
1924
|
+
|
1925
|
+
|
1926
|
+
// Test SUMIF
|
1927
|
+
// ... where there is only a check range
|
1928
|
+
assert(sumif_2(sumifs_array_1_v,new_excel_string(">0")).number == 110.0);
|
1929
|
+
assert(sumif_2(sumifs_array_1_v,new_excel_string(">10")).number == 100.0);
|
1930
|
+
assert(sumif_2(sumifs_array_1_v,new_excel_string("<100")).number == 10.0);
|
1931
|
+
|
1932
|
+
// ... where there is a seprate sum range
|
1933
|
+
ExcelValue sumif_array_1[] = {new_excel_number(15),new_excel_number(20), new_excel_number(30)};
|
1934
|
+
ExcelValue sumif_array_1_v = new_excel_range(sumif_array_1,3,1);
|
1935
|
+
assert(sumif(sumifs_array_1_v,new_excel_string("10"),sumif_array_1_v).number == 15);
|
1936
|
+
|
1937
|
+
|
1938
|
+
// Test SUMPRODUCT
|
1939
|
+
ExcelValue sumproduct_1[] = { new_excel_number(10), new_excel_number(100), BLANK};
|
1940
|
+
ExcelValue sumproduct_2[] = { BLANK, new_excel_number(100), new_excel_number(10), BLANK};
|
1941
|
+
ExcelValue sumproduct_3[] = { BLANK };
|
1942
|
+
ExcelValue sumproduct_4[] = { new_excel_number(10), new_excel_number(100), new_excel_number(1000)};
|
1943
|
+
ExcelValue sumproduct_5[] = { new_excel_number(1), new_excel_number(2), new_excel_number(3)};
|
1944
|
+
ExcelValue sumproduct_6[] = { new_excel_number(1), new_excel_number(2), new_excel_number(4), new_excel_number(5)};
|
1945
|
+
ExcelValue sumproduct_7[] = { new_excel_number(10), new_excel_number(20), new_excel_number(40), new_excel_number(50)};
|
1946
|
+
ExcelValue sumproduct_8[] = { new_excel_number(11), new_excel_number(21), new_excel_number(41), new_excel_number(51)};
|
1947
|
+
ExcelValue sumproduct_9[] = { BLANK, BLANK };
|
1948
|
+
|
1949
|
+
ExcelValue sumproduct_1_v = new_excel_range( sumproduct_1, 3, 1);
|
1950
|
+
ExcelValue sumproduct_2_v = new_excel_range( sumproduct_2, 3, 1);
|
1951
|
+
ExcelValue sumproduct_3_v = new_excel_range( sumproduct_3, 1, 1);
|
1952
|
+
// ExcelValue sumproduct_4_v = new_excel_range( sumproduct_4, 1, 3); // Unused
|
1953
|
+
ExcelValue sumproduct_5_v = new_excel_range( sumproduct_5, 3, 1);
|
1954
|
+
ExcelValue sumproduct_6_v = new_excel_range( sumproduct_6, 2, 2);
|
1955
|
+
ExcelValue sumproduct_7_v = new_excel_range( sumproduct_7, 2, 2);
|
1956
|
+
ExcelValue sumproduct_8_v = new_excel_range( sumproduct_8, 2, 2);
|
1957
|
+
ExcelValue sumproduct_9_v = new_excel_range( sumproduct_9, 2, 1);
|
1958
|
+
|
1959
|
+
// ... should multiply together and then sum the elements in row or column areas given as arguments
|
1960
|
+
ExcelValue sumproducta_1[] = {sumproduct_1_v, sumproduct_2_v};
|
1961
|
+
assert(sumproduct(2,sumproducta_1).number == 100*100);
|
1962
|
+
|
1963
|
+
// ... should return :value when miss-matched array sizes
|
1964
|
+
ExcelValue sumproducta_2[] = {sumproduct_1_v, sumproduct_3_v};
|
1965
|
+
assert(sumproduct(2,sumproducta_2).type == ExcelError);
|
1966
|
+
|
1967
|
+
// ... if all its arguments are single values, should multiply them together
|
1968
|
+
// ExcelValue *sumproducta_3 = sumproduct_4;
|
1969
|
+
assert(sumproduct(3,sumproduct_4).number == 10*100*1000);
|
1970
|
+
|
1971
|
+
// ... if it only has one range as an argument, should add its elements together
|
1972
|
+
ExcelValue sumproducta_4[] = {sumproduct_5_v};
|
1973
|
+
assert(sumproduct(1,sumproducta_4).number == 1 + 2 + 3);
|
1974
|
+
|
1975
|
+
// ... if given multi row and column areas as arguments, should multipy the corresponding cell in each area and then add them all
|
1976
|
+
ExcelValue sumproducta_5[] = {sumproduct_6_v, sumproduct_7_v, sumproduct_8_v};
|
1977
|
+
assert(sumproduct(3,sumproducta_5).number == 1*10*11 + 2*20*21 + 4*40*41 + 5*50*51);
|
1978
|
+
|
1979
|
+
// ... should raise an error if BLANK values outside of an array
|
1980
|
+
ExcelValue sumproducta_6[] = {BLANK,new_excel_number(1)};
|
1981
|
+
assert(sumproduct(2,sumproducta_6).type == ExcelError);
|
1982
|
+
|
1983
|
+
// ... should ignore non-numeric values within an array
|
1984
|
+
ExcelValue sumproducta_7[] = {sumproduct_9_v, sumproduct_9_v};
|
1985
|
+
assert(sumproduct(2,sumproducta_7).number == 0);
|
1986
|
+
|
1987
|
+
// ... should return an error if an argument is an error
|
1988
|
+
ExcelValue sumproducta_8[] = {VALUE};
|
1989
|
+
assert(sumproduct(1,sumproducta_8).type == ExcelError);
|
1990
|
+
|
1991
|
+
// Test VLOOKUP
|
1992
|
+
ExcelValue vlookup_a1[] = {new_excel_number(1),new_excel_number(10),new_excel_number(2),new_excel_number(20),new_excel_number(3),new_excel_number(30)};
|
1993
|
+
ExcelValue vlookup_a2[] = {new_excel_string("hello"),new_excel_number(10),new_excel_number(2),new_excel_number(20),new_excel_number(3),new_excel_number(30)};
|
1994
|
+
ExcelValue vlookup_a3[] = {BLANK,new_excel_number(10),new_excel_number(2),new_excel_number(20),new_excel_number(3),new_excel_number(30)};
|
1995
|
+
ExcelValue vlookup_a1_v = new_excel_range(vlookup_a1,3,2);
|
1996
|
+
ExcelValue vlookup_a2_v = new_excel_range(vlookup_a2,3,2);
|
1997
|
+
ExcelValue vlookup_a3_v = new_excel_range(vlookup_a3,3,2);
|
1998
|
+
// ... should match the first argument against the first column of the table in the second argument, returning the value in the column specified by the third argument
|
1999
|
+
assert(vlookup_3(new_excel_number(2.0),vlookup_a1_v,new_excel_number(2)).number == 20);
|
2000
|
+
assert(vlookup_3(new_excel_number(1.5),vlookup_a1_v,new_excel_number(2)).number == 10);
|
2001
|
+
assert(vlookup_3(new_excel_number(0.5),vlookup_a1_v,new_excel_number(2)).type == ExcelError);
|
2002
|
+
assert(vlookup_3(new_excel_number(10),vlookup_a1_v,new_excel_number(2)).number == 30);
|
2003
|
+
assert(vlookup_3(new_excel_number(2.6),vlookup_a1_v,new_excel_number(2)).number == 20);
|
2004
|
+
// ... has a four argument variant that matches the lookup type
|
2005
|
+
assert(vlookup(new_excel_number(2.6),vlookup_a1_v,new_excel_number(2),TRUE).number == 20);
|
2006
|
+
assert(vlookup(new_excel_number(2.6),vlookup_a1_v,new_excel_number(2),FALSE).type == ExcelError);
|
2007
|
+
assert(vlookup(new_excel_string("HELLO"),vlookup_a2_v,new_excel_number(2),FALSE).number == 10);
|
2008
|
+
assert(vlookup(new_excel_string("HELMP"),vlookup_a2_v,new_excel_number(2),TRUE).number == 10);
|
2009
|
+
// ... BLANK should not match with anything" do
|
2010
|
+
assert(vlookup_3(BLANK,vlookup_a3_v,new_excel_number(2)).type == ExcelError);
|
2011
|
+
// ... should return an error if an argument is an error" do
|
2012
|
+
assert(vlookup(VALUE,vlookup_a1_v,new_excel_number(2),FALSE).type == ExcelError);
|
2013
|
+
assert(vlookup(new_excel_number(2.0),VALUE,new_excel_number(2),FALSE).type == ExcelError);
|
2014
|
+
assert(vlookup(new_excel_number(2.0),vlookup_a1_v,VALUE,FALSE).type == ExcelError);
|
2015
|
+
assert(vlookup(new_excel_number(2.0),vlookup_a1_v,new_excel_number(2),VALUE).type == ExcelError);
|
2016
|
+
assert(vlookup(VALUE,VALUE,VALUE,VALUE).type == ExcelError);
|
2017
|
+
|
2018
|
+
// Test SUM
|
2019
|
+
ExcelValue sum_array_0[] = {new_excel_number(1084.4557258064517),new_excel_number(32.0516914516129),new_excel_number(137.36439193548387)};
|
2020
|
+
ExcelValue sum_array_0_v = new_excel_range(sum_array_0,3,1);
|
2021
|
+
ExcelValue sum_array_1[] = {sum_array_0_v};
|
2022
|
+
assert(sum(1,sum_array_1).number == 1253.8718091935484);
|
2023
|
+
|
2024
|
+
return 0;
|
2025
|
+
}
|
2026
|
+
|
2027
|
+
int main() {
|
2028
|
+
return test_functions();
|
2029
|
+
}
|