-
Notifications
You must be signed in to change notification settings - Fork 30
/
Copy pathsimple.c
2528 lines (2245 loc) · 73.8 KB
/
simple.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// /Users/tamc/Documents/github/excel_to_code/examples/simple/simple.xlsx approximately translated into C
// definitions
#define NUMBER_OF_REFS 9
#define EXCEL_FILENAME "/Users/tamc/Documents/github/excel_to_code/examples/simple/simple.xlsx"
// end of definitions
// First we have c versions of all the excel functions that we know
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <locale.h>
#ifndef NUMBER_OF_REFS
#define NUMBER_OF_REFS 0
#endif
#ifndef EXCEL_FILENAME
#define EXCEL_FILENAME "NoExcelFilename"
#endif
// Need to retain malloc'd values for a while, so can return to functions that use this library
// So to avoid a memory leak we keep an array of all the values we have malloc'd, which we then
// free when the reset() function is called.
#define MEMORY_TO_BE_FREED_LATER_HEAP_INCREMENT 1000
#define true 1
#define false 0
// These are the various types of excel cell, plus ExcelRange which allows the passing of arrays of cells
typedef enum {ExcelEmpty, ExcelNumber, ExcelString, ExcelBoolean, ExcelError, ExcelRange} ExcelType;
struct excel_value {
ExcelType type;
double number; // Used for numbers and for error types
char *string; // Used for strings
// The following three are used for ranges
void *array;
int rows;
int columns;
};
typedef struct excel_value ExcelValue;
// These are used in the SUMIF and SUMIFS criteria (e.g., when passed a string like "<20")
typedef enum {LessThan, LessThanOrEqual, Equal, NotEqual, MoreThanOrEqual, MoreThan} ExcelComparisonType;
struct excel_comparison {
ExcelComparisonType type;
ExcelValue comparator;
};
typedef struct excel_comparison ExcelComparison;
// Headers
static ExcelValue more_than(ExcelValue a_v, ExcelValue b_v);
static ExcelValue more_than_or_equal(ExcelValue a_v, ExcelValue b_v);
static ExcelValue not_equal(ExcelValue a_v, ExcelValue b_v);
static ExcelValue less_than(ExcelValue a_v, ExcelValue b_v);
static ExcelValue less_than_or_equal(ExcelValue a_v, ExcelValue b_v);
static ExcelValue average(int array_size, ExcelValue *array);
static ExcelValue averageifs(ExcelValue average_range_v, int number_of_arguments, ExcelValue *arguments);
static ExcelValue excel_char(ExcelValue number_v);
static ExcelValue ensure_is_number(ExcelValue maybe_number_v);
static ExcelValue find_2(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v);
static ExcelValue find(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v, ExcelValue position_to_start_at_v);
static ExcelValue hlookup_3(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue row_number_v);
static ExcelValue hlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue row_number_v, ExcelValue match_type_v);
static ExcelValue iferror(ExcelValue value, ExcelValue value_if_error);
static ExcelValue iserr(ExcelValue value);
static ExcelValue iserror(ExcelValue value);
static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, ExcelValue column_number_v);
static ExcelValue excel_index_2(ExcelValue array_v, ExcelValue row_number_v);
static ExcelValue excel_isnumber(ExcelValue number);
static ExcelValue excel_isblank(ExcelValue value);
static ExcelValue forecast(ExcelValue required_x, ExcelValue known_y, ExcelValue known_x);
static ExcelValue large(ExcelValue array_v, ExcelValue k_v);
static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v);
static ExcelValue left_1(ExcelValue string_v);
static ExcelValue len(ExcelValue string_v);
static ExcelValue excel_log(ExcelValue number);
static ExcelValue excel_log_2(ExcelValue number, ExcelValue base);
static ExcelValue ln(ExcelValue number);
static ExcelValue excel_exp(ExcelValue number);
static ExcelValue max(int number_of_arguments, ExcelValue *arguments);
static ExcelValue min(int number_of_arguments, ExcelValue *arguments);
static ExcelValue mmult(ExcelValue a_v, ExcelValue b_v);
static ExcelValue mod(ExcelValue a_v, ExcelValue b_v);
static ExcelValue negative(ExcelValue a_v);
static ExcelValue number_or_zero(ExcelValue maybe_number_v);
static ExcelValue npv(ExcelValue rate, int number_of_arguments, ExcelValue *arguments);
static ExcelValue pmt(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v);
static ExcelValue pmt_4(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v, ExcelValue final_value_v);
static ExcelValue pmt_5(ExcelValue rate_v, ExcelValue number_of_periods_v, ExcelValue present_value_v, ExcelValue final_value_v, ExcelValue type_v);
static ExcelValue power(ExcelValue a_v, ExcelValue b_v);
static ExcelValue pv_3(ExcelValue a_v, ExcelValue b_v, ExcelValue c_v);
static ExcelValue pv_4(ExcelValue a_v, ExcelValue b_v, ExcelValue c_v, ExcelValue d_v);
static ExcelValue pv_5(ExcelValue a_v, ExcelValue b_v, ExcelValue c_v, ExcelValue d_v, ExcelValue e_v);
static ExcelValue excel_round(ExcelValue number_v, ExcelValue decimal_places_v);
static ExcelValue rank(ExcelValue number_v, ExcelValue range_v, ExcelValue order_v);
static ExcelValue rank_2(ExcelValue number_v, ExcelValue range_v);
static ExcelValue right(ExcelValue string_v, ExcelValue number_of_characters_v);
static ExcelValue right_1(ExcelValue string_v);
static ExcelValue rounddown(ExcelValue number_v, ExcelValue decimal_places_v);
static ExcelValue roundup(ExcelValue number_v, ExcelValue decimal_places_v);
static ExcelValue excel_int(ExcelValue number_v);
static ExcelValue string_join(int number_of_arguments, ExcelValue *arguments);
static ExcelValue subtotal(ExcelValue type, int number_of_arguments, ExcelValue *arguments);
static ExcelValue sumifs(ExcelValue sum_range_v, int number_of_arguments, ExcelValue *arguments);
static ExcelValue sumif(ExcelValue check_range_v, ExcelValue criteria_v, ExcelValue sum_range_v );
static ExcelValue sumif_2(ExcelValue check_range_v, ExcelValue criteria_v);
static ExcelValue sumproduct(int number_of_arguments, ExcelValue *arguments);
static ExcelValue text(ExcelValue number_v, ExcelValue format_v);
static ExcelValue value(ExcelValue string_v);
static ExcelValue vlookup_3(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v);
static ExcelValue vlookup(ExcelValue lookup_value_v,ExcelValue lookup_table_v, ExcelValue column_number_v, ExcelValue match_type_v);
// My little heap for keeping pointers to memory that I need to reclaim
void **memory_that_needs_to_be_freed;
int memory_that_needs_to_be_freed_counter = 0;
int memory_that_needs_to_be_freed_size = -1;
static void free_later(void *pointer) {
if(memory_that_needs_to_be_freed_counter >= memory_that_needs_to_be_freed_size) {
if(memory_that_needs_to_be_freed_size <= 0) {
memory_that_needs_to_be_freed = malloc(MEMORY_TO_BE_FREED_LATER_HEAP_INCREMENT*sizeof(void*));
memory_that_needs_to_be_freed_size = MEMORY_TO_BE_FREED_LATER_HEAP_INCREMENT;
} else {
memory_that_needs_to_be_freed_size += MEMORY_TO_BE_FREED_LATER_HEAP_INCREMENT;
memory_that_needs_to_be_freed = realloc(memory_that_needs_to_be_freed, memory_that_needs_to_be_freed_size * sizeof(void*));
if(!memory_that_needs_to_be_freed) {
printf("Could not allocate new memory to memory that needs to be freed array. halting.");
exit(-1);
}
}
}
memory_that_needs_to_be_freed[memory_that_needs_to_be_freed_counter] = pointer;
memory_that_needs_to_be_freed_counter++;
}
static void free_all_allocated_memory() {
int i;
for(i = 0; i < memory_that_needs_to_be_freed_counter; i++) {
free(memory_that_needs_to_be_freed[i]);
}
memory_that_needs_to_be_freed_counter = 0;
}
static int variable_set[NUMBER_OF_REFS];
// Resets all cached and malloc'd values
void reset() {
free_all_allocated_memory();
memset(variable_set, 0, sizeof(variable_set));
}
// Handy macros
#define EXCEL_NUMBER(numberdouble) ((ExcelValue) {.type = ExcelNumber, .number = numberdouble})
#define EXCEL_STRING(stringchar) ((ExcelValue) {.type = ExcelString, .string = stringchar})
#define EXCEL_RANGE(arrayofvalues, rangerows, rangecolumns) ((ExcelValue) {.type = ExcelRange, .array = arrayofvalues, .rows = rangerows, .columns = rangecolumns})
static void * new_excel_value_array(int size) {
ExcelValue *pointer = malloc(sizeof(ExcelValue)*size); // Freed later
if(pointer == 0) {
printf("Out of memory in new_excel_value_array\n");
exit(-1);
}
free_later(pointer);
return pointer;
};
// Constants
static ExcelValue ORIGINAL_EXCEL_FILENAME = {.type = ExcelString, .string = EXCEL_FILENAME };
const ExcelValue BLANK = {.type = ExcelEmpty, .number = 0};
const ExcelValue ZERO = {.type = ExcelNumber, .number = 0};
const ExcelValue ONE = {.type = ExcelNumber, .number = 1};
const ExcelValue TWO = {.type = ExcelNumber, .number = 2};
const ExcelValue THREE = {.type = ExcelNumber, .number = 3};
const ExcelValue FOUR = {.type = ExcelNumber, .number = 4};
const ExcelValue FIVE = {.type = ExcelNumber, .number = 5};
const ExcelValue SIX = {.type = ExcelNumber, .number = 6};
const ExcelValue SEVEN = {.type = ExcelNumber, .number = 7};
const ExcelValue EIGHT = {.type = ExcelNumber, .number = 8};
const ExcelValue NINE = {.type = ExcelNumber, .number = 9};
const ExcelValue TEN = {.type = ExcelNumber, .number = 10};
// Booleans
const ExcelValue TRUE = {.type = ExcelBoolean, .number = true };
const ExcelValue FALSE = {.type = ExcelBoolean, .number = false };
// Errors
const ExcelValue VALUE = {.type = ExcelError, .number = 0};
const ExcelValue NAME = {.type = ExcelError, .number = 1};
const ExcelValue DIV0 = {.type = ExcelError, .number = 2};
const ExcelValue REF = {.type = ExcelError, .number = 3};
const ExcelValue NA = {.type = ExcelError, .number = 4};
const ExcelValue NUM = {.type = ExcelError, .number = 5};
// This is the error flag
static int conversion_error = 0;
// Helpful for debugging
static void inspect_excel_value(ExcelValue v) {
ExcelValue *array;
int i, j, k;
switch (v.type) {
case ExcelNumber:
printf("Number: %f\n",v.number);
break;
case ExcelBoolean:
if(v.number == true) {
printf("True\n");
} else if(v.number == false) {
printf("False\n");
} else {
printf("Boolean with undefined state %f\n",v.number);
}
break;
case ExcelEmpty:
if(v.number == 0) {
printf("Empty\n");
} else {
printf("Empty with unexpected state %f\n",v.number);
}
break;
case ExcelRange:
printf("Range rows: %d, columns: %d\n",v.rows,v.columns);
array = v.array;
for(i = 0; i < v.rows; i++) {
printf("Row %d:\n",i+1);
for(j = 0; j < v.columns; j++ ) {
printf("%d ",j+1);
k = (i * v.columns) + j;
inspect_excel_value(array[k]);
}
}
break;
case ExcelString:
printf("String: '%s'\n",v.string);
break;
case ExcelError:
printf("Error number %f ",v.number);
switch( (int)v.number) {
case 0: printf("VALUE\n"); break;
case 1: printf("NAME\n"); break;
case 2: printf("DIV0\n"); break;
case 3: printf("REF\n"); break;
case 4: printf("NA\n"); break;
case 5: printf("NUM\n"); break;
}
break;
default:
printf("Type %d not recognised",v.type);
};
}
// Extracts numbers from ExcelValues
// Excel treats empty cells as zero
static double number_from(ExcelValue v) {
char *s;
char *p;
double n;
ExcelValue *array;
switch (v.type) {
case ExcelNumber:
case ExcelBoolean:
return v.number;
case ExcelEmpty:
return 0;
case ExcelRange:
array = v.array;
return number_from(array[0]);
case ExcelString:
s = v.string;
if (s == NULL || *s == '\0' || isspace(*s)) {
return 0;
}
n = strtod (s, &p);
if(*p == '\0') {
return n;
}
conversion_error = 1;
return 0;
case ExcelError:
return 0;
}
return 0;
}
#define NUMBER(value_name, name) double name; if(value_name.type == ExcelError) { return value_name; }; name = number_from(value_name);
#define CHECK_FOR_CONVERSION_ERROR if(conversion_error) { conversion_error = 0; return VALUE; };
#define CHECK_FOR_PASSED_ERROR(name) if(name.type == ExcelError) return name;
static ExcelValue excel_abs(ExcelValue a_v) {
CHECK_FOR_PASSED_ERROR(a_v)
NUMBER(a_v, a)
CHECK_FOR_CONVERSION_ERROR
if(a >= 0.0 ) {
return a_v;
} else {
return (ExcelValue) {.type = ExcelNumber, .number = -a};
}
}
static ExcelValue excel_char(ExcelValue a_v) {
CHECK_FOR_PASSED_ERROR(a_v)
NUMBER(a_v, a)
CHECK_FOR_CONVERSION_ERROR
if(a <= 0) { return VALUE; }
if(a >= 256) { return VALUE; }
a = floor(a);
char *string = malloc(1); // Freed later
if(string == 0) {
printf("Out of memory in char");
exit(-1);
}
string[0] = a;
free_later(string);
return EXCEL_STRING(string);
}
static ExcelValue add(ExcelValue a_v, ExcelValue b_v) {
CHECK_FOR_PASSED_ERROR(a_v)
CHECK_FOR_PASSED_ERROR(b_v)
NUMBER(a_v, a)
NUMBER(b_v, b)
CHECK_FOR_CONVERSION_ERROR
return EXCEL_NUMBER(a + b);
}
static ExcelValue ensure_is_number(ExcelValue maybe_number_v) {
if(maybe_number_v.type == ExcelNumber) {
return maybe_number_v;
}
if(maybe_number_v.type == ExcelError) {
return maybe_number_v;
}
NUMBER(maybe_number_v, maybe_number)
CHECK_FOR_CONVERSION_ERROR
return EXCEL_NUMBER(maybe_number);
}
static ExcelValue number_or_zero(ExcelValue maybe_number_v) {
if(maybe_number_v.type == ExcelNumber) {
return maybe_number_v;
}
if(maybe_number_v.type == ExcelError) {
return maybe_number_v;
}
return ZERO;
}
static ExcelValue excel_log(ExcelValue number) {
return excel_log_2(number, TEN);
}
static ExcelValue excel_log_2(ExcelValue number_v, ExcelValue base_v) {
CHECK_FOR_PASSED_ERROR(number_v)
CHECK_FOR_PASSED_ERROR(base_v)
NUMBER(number_v, n)
NUMBER(base_v, b)
CHECK_FOR_CONVERSION_ERROR
if(n<=0) { return NUM; }
if(b<=0) { return NUM; }
return EXCEL_NUMBER(log(n)/log(b));
}
static ExcelValue ln(ExcelValue number_v) {
CHECK_FOR_PASSED_ERROR(number_v)
NUMBER(number_v, n)
CHECK_FOR_CONVERSION_ERROR
if(n<=0) { return NUM; }
return EXCEL_NUMBER(log(n));
}
static ExcelValue excel_exp(ExcelValue number_v) {
CHECK_FOR_PASSED_ERROR(number_v)
NUMBER(number_v, n)
CHECK_FOR_CONVERSION_ERROR
return EXCEL_NUMBER(exp(n));
}
static ExcelValue excel_and(int array_size, ExcelValue *array) {
int i;
ExcelValue current_excel_value, array_result;
for(i=0;i<array_size;i++) {
current_excel_value = array[i];
switch (current_excel_value.type) {
case ExcelNumber:
case ExcelBoolean:
if(current_excel_value.number == false) return FALSE;
break;
case ExcelRange:
array_result = excel_and( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
if(array_result.type == ExcelError) return array_result;
if(array_result.type == ExcelBoolean && array_result.number == false) return FALSE;
break;
case ExcelString:
case ExcelEmpty:
break;
case ExcelError:
return current_excel_value;
break;
}
}
return TRUE;
}
struct average_result {
double sum;
double count;
int has_error;
ExcelValue error;
};
static struct average_result calculate_average(int array_size, ExcelValue *array) {
double sum = 0;
double count = 0;
int i;
ExcelValue current_excel_value;
struct average_result array_result, r;
for(i=0;i<array_size;i++) {
current_excel_value = array[i];
switch (current_excel_value.type) {
case ExcelNumber:
sum += current_excel_value.number;
count++;
break;
case ExcelRange:
array_result = calculate_average( current_excel_value.rows * current_excel_value.columns, current_excel_value.array );
if(array_result.has_error == true) return array_result;
sum += array_result.sum;
count += array_result.count;
break;
case ExcelBoolean:
case ExcelString:
case ExcelEmpty:
break;
case ExcelError:
r.has_error = true;
r.error = current_excel_value;
return r;
break;
}
}
r.count = count;
r.sum = sum;
r.has_error = false;
return r;
}
static ExcelValue average(int array_size, ExcelValue *array) {
struct average_result r = calculate_average(array_size, array);
if(r.has_error == true) return r.error;
if(r.count == 0) return DIV0;
return EXCEL_NUMBER(r.sum/r.count);
}
static ExcelValue forecast(ExcelValue required_x_v, ExcelValue known_y, ExcelValue known_x) {
CHECK_FOR_PASSED_ERROR(required_x_v)
NUMBER(required_x_v, required_x)
CHECK_FOR_CONVERSION_ERROR
if(known_x.type != ExcelRange) { return NA; }
if(known_y.type != ExcelRange) { return NA; }
int known_x_size = known_x.rows * known_x.columns;
int known_y_size = known_y.rows * known_y.columns;
int i;
ExcelValue *x_array, *y_array;
ExcelValue vx, vy;
x_array = known_x.array;
y_array = known_y.array;
for(i=0; i<known_x_size; i++) {
vx = x_array[i];
if(vx.type == ExcelError) {
return vx;
}
}
for(i=0; i<known_x_size; i++) {
vy = y_array[i];
if(vy.type == ExcelError) {
return vy;
}
}
if(known_x_size != known_y_size) { return NA; }
if(known_x_size == 0) { return NA; }
ExcelValue mean_y = average(1, &known_y);
ExcelValue mean_x = average(1, &known_x);
if(mean_y.type == ExcelError) { return mean_y; }
if(mean_x.type == ExcelError) { return mean_x; }
float mx = mean_x.number;
float my = mean_y.number;
float b_numerator, b_denominator, b, a;
b_denominator = 0;
b_numerator = 0;
for(i=0; i<known_x_size; i++) {
vx = x_array[i];
vy = y_array[i];
if(vx.type != ExcelNumber) { continue; }
if(vy.type != ExcelNumber) { continue; }
b_denominator = b_denominator + pow(vx.number - mx, 2);
b_numerator = b_numerator + ((vx.number - mx)*(vy.number-my));
}
if(b_denominator == 0) { return DIV0; }
b = b_numerator / b_denominator;
a = mean_y.number - (b*mean_x.number);
return EXCEL_NUMBER(a + (b*required_x));
}
static ExcelValue choose(ExcelValue index_v, int array_size, ExcelValue *array) {
CHECK_FOR_PASSED_ERROR(index_v)
int index = (int) number_from(index_v);
CHECK_FOR_CONVERSION_ERROR
int i;
for(i=0;i<array_size;i++) {
if(array[i].type == ExcelError) return array[i];
}
if(index < 1) return VALUE;
if(index > array_size) return VALUE;
return array[index-1];
}
static ExcelValue count(int array_size, ExcelValue *array) {
int i;
int n = 0;
ExcelValue current_excel_value;
for(i=0;i<array_size;i++) {
current_excel_value = array[i];
switch (current_excel_value.type) {
case ExcelNumber:
n++;
break;
case ExcelRange:
n += count( current_excel_value.rows * current_excel_value.columns, current_excel_value.array ).number;
break;
case ExcelBoolean:
case ExcelString:
case ExcelEmpty:
case ExcelError:
break;
}
}
return EXCEL_NUMBER(n);
}
static ExcelValue counta(int array_size, ExcelValue *array) {
int i;
int n = 0;
ExcelValue current_excel_value;
for(i=0;i<array_size;i++) {
current_excel_value = array[i];
switch(current_excel_value.type) {
case ExcelNumber:
case ExcelBoolean:
case ExcelString:
case ExcelError:
n++;
break;
case ExcelRange:
n += counta( current_excel_value.rows * current_excel_value.columns, current_excel_value.array ).number;
break;
case ExcelEmpty:
break;
}
}
return EXCEL_NUMBER(n);
}
static ExcelValue divide(ExcelValue a_v, ExcelValue b_v) {
CHECK_FOR_PASSED_ERROR(a_v)
CHECK_FOR_PASSED_ERROR(b_v)
NUMBER(a_v, a)
NUMBER(b_v, b)
CHECK_FOR_CONVERSION_ERROR
if(b == 0) return DIV0;
return EXCEL_NUMBER(a / b);
}
static ExcelValue excel_equal(ExcelValue a_v, ExcelValue b_v) {
CHECK_FOR_PASSED_ERROR(a_v)
CHECK_FOR_PASSED_ERROR(b_v)
if(a_v.type != b_v.type) return FALSE;
switch (a_v.type) {
case ExcelNumber:
case ExcelBoolean:
case ExcelEmpty:
if(a_v.number != b_v.number) return FALSE;
return TRUE;
case ExcelString:
if(strcasecmp(a_v.string,b_v.string) != 0 ) return FALSE;
return TRUE;
case ExcelError:
return a_v;
case ExcelRange:
return NA;
}
return FALSE;
}
static ExcelValue not_equal(ExcelValue a_v, ExcelValue b_v) {
ExcelValue result = excel_equal(a_v, b_v);
if(result.type == ExcelBoolean) {
if(result.number == 0) return TRUE;
return FALSE;
}
return result;
}
static ExcelValue excel_isnumber(ExcelValue potential_number) {
if(potential_number.type == ExcelNumber) {
return TRUE;
} else {
return FALSE;
}
}
static ExcelValue excel_isblank(ExcelValue value) {
if(value.type == ExcelEmpty) {
return TRUE;
} else {
return FALSE;
}
}
static ExcelValue excel_if(ExcelValue condition, ExcelValue true_case, ExcelValue false_case ) {
CHECK_FOR_PASSED_ERROR(condition)
switch (condition.type) {
case ExcelBoolean:
if(condition.number == true) return true_case;
return false_case;
case ExcelNumber:
if(condition.number == false) return false_case;
return true_case;
case ExcelEmpty:
return false_case;
case ExcelString:
return VALUE;
case ExcelError:
return condition;
case ExcelRange:
return VALUE;
}
return condition;
}
static ExcelValue excel_if_2(ExcelValue condition, ExcelValue true_case ) {
return excel_if( condition, true_case, FALSE );
}
static ExcelValue excel_index(ExcelValue array_v, ExcelValue row_number_v, ExcelValue column_number_v) {
CHECK_FOR_PASSED_ERROR(array_v)
CHECK_FOR_PASSED_ERROR(row_number_v)
CHECK_FOR_PASSED_ERROR(column_number_v)
ExcelValue *array;
int rows;
int columns;
NUMBER(row_number_v, row_number)
NUMBER(column_number_v, column_number)
CHECK_FOR_CONVERSION_ERROR
if(array_v.type == ExcelRange) {
array = array_v.array;
rows = array_v.rows;
columns = array_v.columns;
} else {
ExcelValue tmp_array[] = {array_v};
array = tmp_array;
rows = 1;
columns = 1;
}
if(row_number > rows) return REF;
if(column_number > columns) return REF;
if(row_number == 0 && rows == 1) row_number = 1;
if(column_number == 0 && columns == 1) column_number = 1;
if(row_number == 0) { // We need the whole column
if(column_number < 1) return REF;
ExcelValue *result = (ExcelValue *) new_excel_value_array(rows);
int result_index = 0;
ExcelValue r;
int array_index;
int i;
for(i = 0; i < rows; i++) {
array_index = (i*columns) + column_number - 1;
r = array[array_index];
if(r.type == ExcelEmpty) {
result[result_index] = ZERO;
} else {
result[result_index] = r;
}
result_index++;
}
return EXCEL_RANGE(result,rows,1);
} else if(column_number == 0 ) { // We need the whole row
if(row_number < 1) return REF;
ExcelValue *result = (ExcelValue*) new_excel_value_array(columns);
ExcelValue r;
int row_start = ((row_number-1)*columns);
int row_finish = row_start + columns;
int result_index = 0;
int i;
for(i = row_start; i < row_finish; i++) {
r = array[i];
if(r.type == ExcelEmpty) {
result[result_index] = ZERO;
} else {
result[result_index] = r;
}
result_index++;
}
return EXCEL_RANGE(result,1,columns);
} else { // We need a precise point
if(row_number < 1) return REF;
if(column_number < 1) return REF;
int position = ((row_number - 1) * columns) + column_number - 1;
ExcelValue result = array[position];
if(result.type == ExcelEmpty) return ZERO;
return result;
}
return FALSE;
};
static ExcelValue excel_index_2(ExcelValue array_v, ExcelValue offset) {
if(array_v.type == ExcelRange) {
if(array_v.rows == 1) {
return excel_index(array_v,ONE,offset);
} else if (array_v.columns == 1) {
return excel_index(array_v,offset,ONE);
} else {
return REF;
}
} else if (offset.type == ExcelNumber && offset.number == 1) {
return array_v;
} else {
return REF;
}
return REF;
};
int compare_doubles (const void *a, const void *b) {
const double *da = (const double *) a;
const double *db = (const double *) b;
return (*da > *db) - (*da < *db);
}
static ExcelValue large(ExcelValue range_v, ExcelValue k_v) {
CHECK_FOR_PASSED_ERROR(range_v)
CHECK_FOR_PASSED_ERROR(k_v)
int k = (int) number_from(k_v);
CHECK_FOR_CONVERSION_ERROR;
// Check for edge case where just a single number passed
if(range_v.type == ExcelNumber) {
if( k == 1 ) {
return range_v;
} else {
return NUM;
}
}
// Otherwise grumble if not a range
if(!range_v.type == ExcelRange) { return VALUE; }
// Check that our k is within bounds
if(k < 1) { return NUM; }
int range_size = range_v.rows * range_v.columns;
// OK this is a really naive implementation.
// FIXME: implement the BFPRT algorithm
double *sorted = malloc(sizeof(double)*range_size);
int sorted_size = 0;
ExcelValue *array_v = range_v.array;
ExcelValue x_v;
int i;
for(i = 0; i < range_size; i++ ) {
x_v = array_v[i];
if(x_v.type == ExcelError) { free(sorted); return x_v; };
if(x_v.type == ExcelNumber) {
sorted[sorted_size] = x_v.number;
sorted_size++;
}
}
// Check other bound
if(k > sorted_size) { free(sorted); return NUM; }
qsort(sorted, sorted_size, sizeof (double), compare_doubles);
ExcelValue result = EXCEL_NUMBER(sorted[sorted_size - k]);
free(sorted);
return result;
}
static ExcelValue excel_match(ExcelValue lookup_value, ExcelValue lookup_array, ExcelValue match_type ) {
CHECK_FOR_PASSED_ERROR(lookup_value)
CHECK_FOR_PASSED_ERROR(lookup_array)
CHECK_FOR_PASSED_ERROR(match_type)
// Blanks are treaked as zeros
if(lookup_value.type == ExcelEmpty) lookup_value = ZERO;
// Setup the array
ExcelValue *array;
int size;
if(lookup_array.type == ExcelRange) {
// Check that the range is a row or column rather than an area
if((lookup_array.rows == 1) || (lookup_array.columns == 1)) {
array = lookup_array.array;
size = lookup_array.rows * lookup_array.columns;
} else {
// return NA error if covers an area.
return NA;
};
} else {
// Need to wrap the argument up as an array
size = 1;
ExcelValue tmp_array[1] = {lookup_array};
array = tmp_array;
}
int type = (int) number_from(match_type);
CHECK_FOR_CONVERSION_ERROR;
int i;
ExcelValue x;
switch(type) {
case 0:
for(i = 0; i < size; i++ ) {
x = array[i];
if(x.type == ExcelEmpty) x = ZERO;
if(excel_equal(lookup_value,x).number == true) return EXCEL_NUMBER(i+1);
}
return NA;
break;
case 1:
for(i = 0; i < size; i++ ) {
x = array[i];
if(x.type == ExcelEmpty) x = ZERO;
if(more_than(x,lookup_value).number == true) {
if(i==0) return NA;
return EXCEL_NUMBER(i);
}
}
return EXCEL_NUMBER(size);
break;
case -1:
for(i = 0; i < size; i++ ) {
x = array[i];
if(x.type == ExcelEmpty) x = ZERO;
if(less_than(x,lookup_value).number == true) {
if(i==0) return NA;
return EXCEL_NUMBER(i);
}
}
return EXCEL_NUMBER(size-1);
break;
}
return NA;
}
static ExcelValue excel_match_2(ExcelValue lookup_value, ExcelValue lookup_array ) {
return excel_match(lookup_value, lookup_array, ONE);
}
static ExcelValue find(ExcelValue find_text_v, ExcelValue within_text_v, ExcelValue start_number_v) {
CHECK_FOR_PASSED_ERROR(find_text_v)
CHECK_FOR_PASSED_ERROR(within_text_v)
CHECK_FOR_PASSED_ERROR(start_number_v)
char *find_text;
char *within_text;
char *within_text_offset;
char *result;
int start_number = number_from(start_number_v);
CHECK_FOR_CONVERSION_ERROR
// Deal with blanks
if(within_text_v.type == ExcelString) {
within_text = within_text_v.string;
} else if( within_text_v.type == ExcelEmpty) {
within_text = "";
}
if(find_text_v.type == ExcelString) {
find_text = find_text_v.string;
} else if( find_text_v.type == ExcelEmpty) {
return start_number_v;
}
// Check length
if(start_number < 1) return VALUE;
if(start_number > strlen(within_text)) return VALUE;
// Offset our within_text pointer
// FIXME: No way this is utf-8 compatible
within_text_offset = within_text + (start_number - 1);
result = strstr(within_text_offset,find_text);
if(result) {
return EXCEL_NUMBER(result - within_text + 1);
}
return VALUE;
}
static ExcelValue find_2(ExcelValue string_to_look_for_v, ExcelValue string_to_look_in_v) {
return find(string_to_look_for_v, string_to_look_in_v, ONE);
};
static ExcelValue left(ExcelValue string_v, ExcelValue number_of_characters_v) {
CHECK_FOR_PASSED_ERROR(string_v)
CHECK_FOR_PASSED_ERROR(number_of_characters_v)
if(string_v.type == ExcelEmpty) return BLANK;
if(number_of_characters_v.type == ExcelEmpty) return BLANK;
int number_of_characters = (int) number_from(number_of_characters_v);
CHECK_FOR_CONVERSION_ERROR
if(number_of_characters < 0) {
return VALUE;
}
char *string;
int string_must_be_freed = 0;
switch (string_v.type) {
case ExcelString:
string = string_v.string;
break;
case ExcelNumber:
string = malloc(20); // Freed
if(string == 0) {
printf("Out of memory in left");
exit(-1);
}
string_must_be_freed = 1;
snprintf(string,20,"%0.0f",string_v.number);
break;
case ExcelBoolean:
if(string_v.number == true) {
string = "TRUE";
} else {
string = "FALSE";
}
break;
case ExcelEmpty:
case ExcelError:
case ExcelRange:
return string_v;
}
if(number_of_characters > strlen(string)) {
number_of_characters = strlen(string);
}
char *left_string = malloc(number_of_characters+1); // Freed