Commit 6b68e93d authored by JP Rosevear's avatar JP Rosevear Committed by JP Rosevear
Browse files

New function that doesn't require an error variable - use only if

2000-06-26  JP Rosevear  <jpr@arcavia.com>

	* src/value.c (value_get_as_checked_bool): New function that
	doesn't require an error variable - use only if confident that
	the value does actually exist.

	* src/value.h: Add new prototype.

	* src/functions/fn-lookup.c: Clean up cruft
	(find_type_valid): See if the value is a string or is gnumeric.
	(find_compare_type_valid): See if the types are comparable.
	(find_bound_walk): Walk an integer range first in one direction
	and then in the other direction.
	(find_index_linear): Do a linear search on a range or array.
	(find_index_bisection): Do a bisection search on a range or array
	that also follows excel rules for for handling type mismatches
	during the search and finding the first and last item matching
	the search.
	(gnumeric_vlookup): Use new functions and implement undocumented
	excel behaviour.
	(gnumeric_hlookup): ditto
	(gnumeric_lookup): ditto
	(gnumeric_match): ditto
parent ef4e3980
2000-06-26 JP Rosevear <jpr@arcavia.com>
* src/value.c (value_get_as_checked_bool): New function that
doesn't require an error variable - use only if confident that
the value does actually exist.
* src/value.h: Add new prototype.
* src/functions/fn-lookup.c: Clean up cruft
(find_type_valid): See if the value is a string or is gnumeric.
(find_compare_type_valid): See if the types are comparable.
(find_bound_walk): Walk an integer range first in one direction
and then in the other direction.
(find_index_linear): Do a linear search on a range or array.
(find_index_bisection): Do a bisection search on a range or array
that also follows excel rules for for handling type mismatches
during the search and finding the first and last item matching
the search.
(gnumeric_vlookup): Use new functions and implement undocumented
excel behaviour.
(gnumeric_hlookup): ditto
(gnumeric_lookup): ditto
(gnumeric_match): ditto
2000-06-26 Morten Welinder <terra@diku.dk>
* src/format.c (lookup_color): Don't double translate. Compare
......
2000-06-26 JP Rosevear <jpr@arcavia.com>
* src/value.c (value_get_as_checked_bool): New function that
doesn't require an error variable - use only if confident that
the value does actually exist.
* src/value.h: Add new prototype.
* src/functions/fn-lookup.c: Clean up cruft
(find_type_valid): See if the value is a string or is gnumeric.
(find_compare_type_valid): See if the types are comparable.
(find_bound_walk): Walk an integer range first in one direction
and then in the other direction.
(find_index_linear): Do a linear search on a range or array.
(find_index_bisection): Do a bisection search on a range or array
that also follows excel rules for for handling type mismatches
during the search and finding the first and last item matching
the search.
(gnumeric_vlookup): Use new functions and implement undocumented
excel behaviour.
(gnumeric_hlookup): ditto
(gnumeric_lookup): ditto
(gnumeric_match): ditto
2000-06-26 Morten Welinder <terra@diku.dk>
* src/format.c (lookup_color): Don't double translate. Compare
......
2000-06-26 JP Rosevear <jpr@arcavia.com>
* src/value.c (value_get_as_checked_bool): New function that
doesn't require an error variable - use only if confident that
the value does actually exist.
* src/value.h: Add new prototype.
* src/functions/fn-lookup.c: Clean up cruft
(find_type_valid): See if the value is a string or is gnumeric.
(find_compare_type_valid): See if the types are comparable.
(find_bound_walk): Walk an integer range first in one direction
and then in the other direction.
(find_index_linear): Do a linear search on a range or array.
(find_index_bisection): Do a bisection search on a range or array
that also follows excel rules for for handling type mismatches
during the search and finding the first and last item matching
the search.
(gnumeric_vlookup): Use new functions and implement undocumented
excel behaviour.
(gnumeric_hlookup): ditto
(gnumeric_lookup): ditto
(gnumeric_match): ditto
2000-06-26 Morten Welinder <terra@diku.dk>
* src/format.c (lookup_color): Don't double translate. Compare
......
2000-06-26 JP Rosevear <jpr@arcavia.com>
* src/value.c (value_get_as_checked_bool): New function that
doesn't require an error variable - use only if confident that
the value does actually exist.
* src/value.h: Add new prototype.
* src/functions/fn-lookup.c: Clean up cruft
(find_type_valid): See if the value is a string or is gnumeric.
(find_compare_type_valid): See if the types are comparable.
(find_bound_walk): Walk an integer range first in one direction
and then in the other direction.
(find_index_linear): Do a linear search on a range or array.
(find_index_bisection): Do a bisection search on a range or array
that also follows excel rules for for handling type mismatches
during the search and finding the first and last item matching
the search.
(gnumeric_vlookup): Use new functions and implement undocumented
excel behaviour.
(gnumeric_hlookup): ditto
(gnumeric_lookup): ditto
(gnumeric_match): ditto
2000-06-26 Morten Welinder <terra@diku.dk>
* src/format.c (lookup_color): Don't double translate. Compare
......
2000-06-26 JP Rosevear <jpr@arcavia.com>
* src/value.c (value_get_as_checked_bool): New function that
doesn't require an error variable - use only if confident that
the value does actually exist.
* src/value.h: Add new prototype.
* src/functions/fn-lookup.c: Clean up cruft
(find_type_valid): See if the value is a string or is gnumeric.
(find_compare_type_valid): See if the types are comparable.
(find_bound_walk): Walk an integer range first in one direction
and then in the other direction.
(find_index_linear): Do a linear search on a range or array.
(find_index_bisection): Do a bisection search on a range or array
that also follows excel rules for for handling type mismatches
during the search and finding the first and last item matching
the search.
(gnumeric_vlookup): Use new functions and implement undocumented
excel behaviour.
(gnumeric_hlookup): ditto
(gnumeric_lookup): ditto
(gnumeric_match): ditto
2000-06-26 Morten Welinder <terra@diku.dk>
* src/format.c (lookup_color): Don't double translate. Compare
......
2000-06-26 JP Rosevear <jpr@arcavia.com>
* src/value.c (value_get_as_checked_bool): New function that
doesn't require an error variable - use only if confident that
the value does actually exist.
* src/value.h: Add new prototype.
* src/functions/fn-lookup.c: Clean up cruft
(find_type_valid): See if the value is a string or is gnumeric.
(find_compare_type_valid): See if the types are comparable.
(find_bound_walk): Walk an integer range first in one direction
and then in the other direction.
(find_index_linear): Do a linear search on a range or array.
(find_index_bisection): Do a bisection search on a range or array
that also follows excel rules for for handling type mismatches
during the search and finding the first and last item matching
the search.
(gnumeric_vlookup): Use new functions and implement undocumented
excel behaviour.
(gnumeric_hlookup): ditto
(gnumeric_lookup): ditto
(gnumeric_match): ditto
2000-06-26 Morten Welinder <terra@diku.dk>
* src/format.c (lookup_color): Don't double translate. Compare
......
2000-06-26 JP Rosevear <jpr@arcavia.com>
* src/value.c (value_get_as_checked_bool): New function that
doesn't require an error variable - use only if confident that
the value does actually exist.
* src/value.h: Add new prototype.
* src/functions/fn-lookup.c: Clean up cruft
(find_type_valid): See if the value is a string or is gnumeric.
(find_compare_type_valid): See if the types are comparable.
(find_bound_walk): Walk an integer range first in one direction
and then in the other direction.
(find_index_linear): Do a linear search on a range or array.
(find_index_bisection): Do a bisection search on a range or array
that also follows excel rules for for handling type mismatches
during the search and finding the first and last item matching
the search.
(gnumeric_vlookup): Use new functions and implement undocumented
excel behaviour.
(gnumeric_hlookup): ditto
(gnumeric_lookup): ditto
(gnumeric_match): ditto
2000-06-26 Morten Welinder <terra@diku.dk>
* src/format.c (lookup_color): Don't double translate. Compare
......
......@@ -4,6 +4,7 @@
* Authors:
* Michael Meeks <michael@imaginator.com>
* Jukka-Pekka Iivonen <iivonen@iki.fi>
* JP Rosevear <jpr@arcavia.com>
*/
#include <config.h>
......@@ -14,6 +15,295 @@
#include "eval.h"
#include "cell.h"
/* Useful routines for multiple functions */
static gboolean
find_type_valid (const Value *find)
{
/* Excel does not lookup errors or blanks */
if (VALUE_IS_NUMBER (find) || find->type == VALUE_STRING) {
return TRUE;
}
return FALSE;
}
static gboolean
find_compare_type_valid (const Value *find, const Value *val)
{
if (!val) {
return FALSE;
}
if ((VALUE_IS_NUMBER (find) && VALUE_IS_NUMBER (val)) ||
(find->type == val->type)) {
return TRUE;
}
return FALSE;
}
/**
* find_bound_walk:
* @l: lower bound
* @h: upper bound
* @start: starting point
* @up: is first step incrementing
* @reset: reset static values
*
* This function takes and upper and lower integer bound
* and then walks that range starting with the given
* starting point. The walk is done by incrementing or
* decrementing the starting point (based on the up value)
* until the upper or lower bound is reached. At this
* point the step is reversed and the values move to the
* opposite boundary (not repeating any values of course)
*
* Return value: the next value in the range
**/
static int
find_bound_walk (int l, int h, int start, gboolean up, gboolean reset)
{
static int low, high, current, orig;
static gboolean sup, started;
g_return_val_if_fail (l >= 0, -1);
g_return_val_if_fail (h >= 0, -1);
g_return_val_if_fail (h >= l, -1);
g_return_val_if_fail (start >= l, -1);
g_return_val_if_fail (start <= h, -1);
if (reset) {
low = l;
high = h;
current = start;
orig = start;
sup = up;
started = FALSE;
return current;
}
if (sup) {
current++;
if (current > high && orig > low) {
current = orig - 1;
sup = FALSE;
} else if (current > high && orig <= low) {
return -1;
}
} else {
current--;
if (current < low && orig < high) {
current = orig + 1;
sup = TRUE;
} else if (current < low && orig >= high) {
return -1;
}
}
return current;
}
static int
find_index_linear (FunctionEvalInfo *ei, Value *find, Value *data,
gint type, gboolean height)
{
const Value *index_val = NULL;
ValueCompare comp;
int length, lp, index = -1;
if (height) {
length = value_area_get_height (ei->pos, data);
} else {
length = value_area_get_width (ei->pos, data);
}
for (lp = 0; lp < length; lp++){
const Value *v;
if (height) {
v = value_area_fetch_x_y (ei->pos, data, 0, lp);
} else {
v = value_area_fetch_x_y (ei->pos, data, lp, 0);
}
g_return_val_if_fail (v != NULL, -1);
if (!find_compare_type_valid (find, v)) {
continue;
}
comp = value_compare (find, v, FALSE);
if (type >= 1 && comp == IS_GREATER) {
ValueCompare comp;
if (index >= 0) {
comp = value_compare (v, index_val, FALSE);
}
if (index < 0 ||
(index >= 0 && comp == IS_GREATER)) {
index = lp;
index_val = v;
}
} else if (type <= -1 && comp == IS_LESS) {
ValueCompare comp;
if (index >= 0) {
comp = value_compare (v, index_val, FALSE);
}
if (index < 0 ||
(index >= 0 && comp == IS_LESS)) {
index = lp;
index_val = v;
}
} else if (comp == IS_EQUAL) {
return lp;
}
}
return index;
}
static int
find_index_bisection (FunctionEvalInfo *ei, Value *find, Value *data,
gint type, gboolean height)
{
ValueCompare comp = TYPE_MISMATCH;
int high, low = 0, prev = -1, mid = -1;
if (height) {
high = value_area_get_height (ei->pos, data);
} else {
high = value_area_get_width (ei->pos, data);
}
high--;
if (high < low) {
return -1;
}
while (low <= high) {
const Value *v = NULL;
int start;
if ((type >= 1) != (comp == IS_LESS)) {
prev = mid;
}
mid = ((low + high) / 2);
mid = find_bound_walk (low, high, mid, TRUE,
type >= 0 ? TRUE : FALSE);
start = mid;
/*
* Excel handles type mismatches by skipping first one
* way then the other (if necessary) to find a valid
* value. The initial direction depends on the search
* type.
*/
while (!find_compare_type_valid (find, v) && mid != -1) {
gboolean rev = FALSE;
if (height) {
v = value_area_fetch_x_y (ei->pos,
data, 0, mid);
} else {
v = value_area_fetch_x_y (ei->pos,
data, mid, 0);
}
g_return_val_if_fail (v != NULL, -1);
if (find_compare_type_valid (find, v)) {
break;
}
mid = find_bound_walk (0, 0, 0, FALSE, FALSE);
if (!rev && type >= 0 && mid < start) {
high = mid;
rev = TRUE;
} else if (!rev && type < 0 && mid > start) {
low = mid;
rev = TRUE;
}
}
/*
* If we couldn't find another entry in the range
* with an appropriate type, return the best previous
* value
*/
if (mid == -1 && ((type >= 1) != (comp == IS_LESS))) {
return prev;
} else if (mid == -1) {
return -1;
}
comp = value_compare (find, v, FALSE);
if (type >= 1 && comp == IS_GREATER) {
low = mid + 1;
} else if (type >= 1 && comp == IS_LESS) {
high = mid - 1;
} else if (type <= -1 && comp == IS_GREATER) {
high = mid - 1;
} else if (type <= -1 && comp == IS_LESS) {
low = mid + 1;
} else if (comp == IS_EQUAL) {
/* This is due to excel, it does a
* linear search after the bisection search
* to find either the first or last value
* that is equal.
*/
while ((type <= -1 && mid > low) ||
(type >= 0 && mid < high)) {
int adj = 0;
if (type >= 0) {
adj = mid + 1;
} else {
adj = mid - 1;
}
if (height) {
v = value_area_fetch_x_y (ei->pos,
data,
0, adj);
} else {
v = value_area_fetch_x_y (ei->pos,
data,
adj, 0);
}
g_return_val_if_fail (v != NULL, -1);
if (!find_compare_type_valid (find, v)) {
break;
}
comp = value_compare (find, v, FALSE);
if (comp != IS_EQUAL) {
break;
}
mid = adj;
}
return mid;
}
}
/* Try and return a reasonable value */
if ((type >= 1) != (comp == IS_LESS)) {
return mid;
}
return prev;
}
/***************************************************************************/
static char *help_address = {
......@@ -205,137 +495,45 @@ static char *help_vlookup = {
"@SEEALSO=HLOOKUP")
};
static int
lookup_similar (const Value *data, const Value *templ,
const Value *next_largest, int approx)
{
int ans;
g_return_val_if_fail (data != NULL, 0);
g_return_val_if_fail (templ != NULL, 0);
switch (templ->type){
case VALUE_EMPTY:
case VALUE_BOOLEAN:
case VALUE_INTEGER:
case VALUE_FLOAT:
{
float_t a, b;
a = value_get_as_float (data);
b = value_get_as_float (templ);
if (a == b)
return 1;
else if (approx && a < b){
if (!next_largest)
return -1;
else if (value_get_as_float (next_largest) <= a)
return -1;
}
return 0;
break;
}
case VALUE_STRING:
case VALUE_ERROR:
default:
{
char *a, *b;
a = value_get_as_string (data);
b = value_get_as_string (templ);
if (approx){
ans = g_strcasecmp (a,b);
if (approx && ans < 0){
if (next_largest){
char *c = value_get_as_string
(next_largest);
int cmp = g_strcasecmp (a,c);
g_free (c);
if (cmp >= 0) {
g_free (a);
g_free (b);
return -1;
}
} else {
g_free (a);
g_free (b);
return -1;
}
}
}
else
ans = strcmp (a, b);
g_free (a);
g_free (b);
return (ans == 0);
break;
}
}
return 0;
}
static Value *
gnumeric_vlookup (FunctionEvalInfo *ei, Value **args)
{
const Value *next_largest = NULL;
int height, lp, approx, col_idx, next_largest_row = 0;
int col_idx, index = -1;
gboolean approx;
height = value_area_get_height (ei->pos, args[1]);
col_idx = value_get_as_int (args[2]);
if (col_idx <= 0)
return value_new_error (ei->pos, gnumeric_err_NUM);
if (col_idx > value_area_get_width (ei->pos, args [1]))
if (!find_type_valid (args[0])) {
return value_new_error (ei->pos, gnumeric_err_NA);
}
if (col_idx <= 0) {
return value_new_error (ei->pos, gnumeric_err_VALUE);
} else if (col_idx > value_area_get_width (ei->pos, args [1])) {
return value_new_error (ei->pos, gnumeric_err_REF);
}
if (args [3]){
gboolean err;
approx = value_get_as_bool (args [3], &err);
if (err)
return value_new_error (ei->pos, gnumeric_err_VALUE);
} else
approx = 1;
for (lp = 0; lp < height; lp++){
int compare;
const Value *v;
v = value_area_fetch_x_y (ei->pos, args[1], 0, lp);
g_return_val_if_fail (v != NULL, NULL);
compare = lookup_similar (v, args[0], next_largest, approx);
if (compare == 1){
const Value *v;