GitLab repository storage has been migrated to hashed layout. Please contact Infrastructure team if you notice any issues with repositories or hooks.

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

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;
v = value_area_fetch_x_y (ei->pos, args [1],
col_idx-1, lp);
g_return_val_if_fail (v != NULL, NULL);
return value_duplicate (v);
}
if (compare < 0){
next_largest = v;
next_largest_row = lp;
}
if (!args[3]) {
approx = TRUE;
} else {
approx = value_get_as_checked_bool (args [3]);
}
if (approx && next_largest){
if (approx) {
index = find_index_bisection (ei, args[0], args[1], 1, TRUE);
} else {
index = find_index_linear (ei, args[0], args[1], 0, TRUE);
}
if (index >= 0) {
const Value *v;
v = value_area_fetch_x_y (ei->pos, args [1], col_idx-1,
next_largest_row);
v = value_area_fetch_x_y (ei->pos, args [1], col_idx-1, index);
g_return_val_if_fail (v != NULL, NULL);
return value_duplicate (v);
}
else
return value_new_error (ei->pos, gnumeric_err_NA);
return NULL;
return value_new_error (ei->pos, gnumeric_err_NA);
}
/***************************************************************************/
......@@ -364,64 +562,42 @@ static char *help_hlookup = {
static Value *
gnumeric_hlookup (FunctionEvalInfo *ei, Value **args)
{
const Value *next_largest = NULL;
int height, lp, approx, row_idx, next_largest_col = 0;
int row_idx, index = -1;
gboolean approx;
row_idx = value_get_as_int (args [2]);
height = value_area_get_width (ei->pos, args [1]);
row_idx = value_get_as_int (args[2]);
if (row_idx <= 0)
return value_new_error (ei->pos, gnumeric_err_NUM);
if (row_idx > value_area_get_height (ei->pos, args [1]))
if (!find_type_valid (args[0])) {
return value_new_error (ei->pos, gnumeric_err_NA);
}
if (row_idx <= 0) {
return value_new_error (ei->pos, gnumeric_err_VALUE);
} else if (row_idx > value_area_get_width (ei->pos, args [1])) {
return value_new_error (ei->pos, gnumeric_err_REF);
}
if (args [3]){