/* This is -*- C -*- */ /* vim: set sw=2: */ /* * guppi-seq-categorical.c * * Copyright (C) 2000 EMC Capital Management, Inc. * Copyright (C) 2001 The Free Software Foundation * * Developed by Jon Trowbridge * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include #include "guppi-seq-categorical.h" /* #include */ #include #include #include #include #include typedef struct _GuppiSeqCategoricalPrivate GuppiSeqCategoricalPrivate; struct _GuppiSeqCategoricalPrivate { GuppiCategory *category; GuppiSeqInteger *seq; gboolean auto_add; GHashTable *freq_table; }; static GtkObjectClass *parent_class = NULL; static void guppi_seq_categorical_finalize (GtkObject *obj) { GuppiSeqCategorical *seq = GUPPI_SEQ_CATEGORICAL (obj); guppi_unref0 (seq->priv->category); if (seq->priv->freq_table) g_hash_table_foreach (seq->priv->freq_table, guppi_free_hash_val, NULL); g_hash_table_destroy (seq->priv->freq_table); seq->priv->freq_table = NULL; guppi_free0 (seq->priv); if (parent_class->finalize) parent_class->finalize (obj); } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /* GuppiSeqInteger virtual functions */ static void range (GuppiSeqInteger *si, gint *min, gint *max) { GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq; GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass); klass->range (proxy, min, max); } static gint frequency (GuppiSeqInteger *si, gint x) { GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq; GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass); return klass->frequency (proxy, x); } static gint get (GuppiSeqInteger *si, gint i) { GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq; GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass); return klass->get (proxy, i); } static void set (GuppiSeqInteger *si, gint i, gint val) { GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq; GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass); klass->set (proxy, i, val); } static void insert (GuppiSeqInteger *si, gint i, const gint *ptr, gsize N) { GuppiSeqInteger *proxy = GUPPI_SEQ_CATEGORICAL (si)->priv->seq; GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass); klass->insert (proxy, i, ptr, N); } /* GuppiSeq virtual functions */ static void size_hint (GuppiSeq *seq, gsize N) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); klass->size_hint (proxy, N); } static void get_bounds (GuppiSeq *seq, gint *min, gint *max) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); klass->get_bounds (proxy, min, max); } static void shift_indices (GuppiSeq *seq, gint delta) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); klass->shift_indices (proxy, delta); } static void insert_generic (GuppiSeq *seq, gint i, gsize N) { GuppiSeqInteger *proxy = GUPPI_SEQ_INTEGER (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqIntegerClass *klass = GUPPI_SEQ_INTEGER_CLASS (GTK_OBJECT (proxy)->klass); code_t c; gint j; c = guppi_category_min_code (GUPPI_SEQ_CATEGORICAL (seq)->priv->category); for (j = 0; j < (gint) N; ++j) { klass->insert (proxy, i, &c, 1); } /* We don't want to chain insert_generic */ } static void delete_many (GuppiSeq *seq, gint i, gsize N) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); klass->delete_many (proxy, i, N); } static gboolean has_missing (GuppiSeq *seq) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); return klass->has_missing (proxy); } static gsize missing_count (GuppiSeq *seq) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); return klass->missing_count (proxy); } static gboolean missing (GuppiSeq *seq, gint i) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); return klass->missing (proxy, i); } static void set_missing (GuppiSeq *seq, gint i, gboolean x) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); klass->set_missing (proxy, i, x); } static void set_range_missing (GuppiSeq *seq, gint i0, gint i1, gboolean x) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); klass->set_range_missing (proxy, i0, i1, x); } static void set_many_missing (GuppiSeq *seq, gint *ind, gsize N, gboolean x) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); klass->set_many_missing (proxy, ind, N, x); } static void set_all_missing (GuppiSeq *seq, gboolean x) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); klass->set_all_missing (proxy, x); } static void insert_missing (GuppiSeq *seq, gint i, gboolean x, gsize N) { GuppiSeq *proxy = GUPPI_SEQ (GUPPI_SEQ_CATEGORICAL (seq)->priv->seq); GuppiSeqClass *klass = GUPPI_SEQ_CLASS (GTK_OBJECT (proxy)->klass); g_message ("guppi-seq-categorical insert_missing"); klass->insert_missing (seq, i, x, N); } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /*** Frequency Table Updating **** */ /* By doing all of this in signals, we ensure that the tables get updated even if we directly manipulate the underlying GuppiSeqInteger object. Cool, 'eh? */ static void freq_adjust (GuppiSeqCategorical *seq, gint i0, gint i1, gint adj) { GuppiSeqCategoricalPrivate *p = seq->priv; GHashTable *freq = p->freq_table; gint i; gboolean has_missing; code_t last_code = GUPPI_INVALID_CODE; gpointer last_val = NULL; i0 = MAX (i0, guppi_seq_min_index (GUPPI_SEQ (seq))); i1 = MIN (i1, guppi_seq_max_index (GUPPI_SEQ (seq))); has_missing = guppi_seq_has_missing (GUPPI_SEQ (seq)); for (i = i0; i <= i1; ++i) { code_t c; gpointer val; if (!has_missing || guppi_seq_available (GUPPI_SEQ (seq), i)) { c = (code_t) guppi_seq_integer_get (GUPPI_SEQ_INTEGER (seq), i); if (last_val && (c == last_code)) { val = last_val; } else { gpointer ptr = GUPPI_CATEGORY_CODE_TO_POINTER (c); val = g_hash_table_lookup (freq, ptr); if (val == NULL) { val = guppi_new0 (gint, 1); g_hash_table_insert (freq, ptr, val); } last_code = c; last_val = val; } *(gint *) val += adj; } } } static void set_before_cb (GuppiSeq *seq, gint i0, gint i1, gpointer foo) { freq_adjust (GUPPI_SEQ_CATEGORICAL (seq), i0, i1, -1); } static void set_after_cb (GuppiSeq *seq, gint i0, gint i1, gpointer foo) { freq_adjust (GUPPI_SEQ_CATEGORICAL (seq), i0, i1, 1); } static void delete_before_cb (GuppiSeq *seq, gint i, gsize N, gpointer foo) { freq_adjust (GUPPI_SEQ_CATEGORICAL (seq), i, i - 1 + N, -1); } static void insert_after_cb (GuppiSeq *seq, gint i, gsize N, gpointer foo) { freq_adjust (GUPPI_SEQ_CATEGORICAL (seq), i, i - 1 + N, 1); } static void guppi_seq_categorical_class_init (GuppiSeqCategoricalClass *klass) { GtkObjectClass *object_class = (GtkObjectClass *) klass; GuppiDataClass *data_class = GUPPI_DATA_CLASS (klass); GuppiSeqIntegerClass *seq_int_class = GUPPI_SEQ_INTEGER_CLASS (klass); GuppiSeqClass *seq_class = GUPPI_SEQ_CLASS (klass); parent_class = gtk_type_class (GUPPI_TYPE_SEQ_INTEGER); seq_int_class->range = range; seq_int_class->frequency = frequency; seq_int_class->get = get; seq_int_class->set = set; seq_int_class->insert = insert; seq_class->size_hint = size_hint; seq_class->get_bounds = get_bounds; seq_class->shift_indices = shift_indices; seq_class->insert_generic = insert_generic; seq_class->delete_many = delete_many; seq_class->has_missing = has_missing; seq_class->missing_count = missing_count; seq_class->missing = missing; seq_class->set_missing = set_missing; seq_class->set_many_missing = set_many_missing; seq_class->set_range_missing = set_range_missing; seq_class->set_all_missing = set_all_missing; seq_class->insert_missing = insert_missing; seq_class->support_missing_values = TRUE; data_class->is_leaf_type = TRUE; object_class->finalize = guppi_seq_categorical_finalize; } static void guppi_seq_categorical_init (GuppiSeqCategorical *obj) { GuppiSeqCategoricalPrivate *p; p = obj->priv = guppi_new0 (GuppiSeqCategoricalPrivate, 1); /* FIXME: Underlying implementation should be used-definable */ p->seq = GUPPI_SEQ_INTEGER (guppi_data_new ("GuppiSeqIntegerCore")); p->category = NULL; p->auto_add = TRUE; p->freq_table = g_hash_table_new (NULL, NULL); gtk_signal_connect (GTK_OBJECT (obj), "changed_set", GTK_SIGNAL_FUNC (set_before_cb), NULL); gtk_signal_connect_after (GTK_OBJECT (obj), "changed_set", GTK_SIGNAL_FUNC (set_after_cb), NULL); gtk_signal_connect_after (GTK_OBJECT (obj), "changed_insert", GTK_SIGNAL_FUNC (insert_after_cb), NULL); gtk_signal_connect (GTK_OBJECT (obj), "changed_delete", GTK_SIGNAL_FUNC (delete_before_cb), NULL); } GtkType guppi_seq_categorical_get_type (void) { static GtkType guppi_seq_categorical_type = 0; if (!guppi_seq_categorical_type) { static const GtkTypeInfo guppi_seq_categorical_info = { "GuppiSeqCategorical", sizeof (GuppiSeqCategorical), sizeof (GuppiSeqCategoricalClass), (GtkClassInitFunc) guppi_seq_categorical_class_init, (GtkObjectInitFunc) guppi_seq_categorical_init, NULL, NULL, (GtkClassInitFunc) NULL }; guppi_seq_categorical_type = gtk_type_unique (GUPPI_TYPE_SEQ_INTEGER, &guppi_seq_categorical_info); } return guppi_seq_categorical_type; } GuppiCategory * guppi_seq_categorical_category (GuppiSeqCategorical *seq) { g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), NULL); return seq->priv->category; } void guppi_seq_categorical_set_category (GuppiSeqCategorical *seq, GuppiCategory *cat) { GuppiSeqCategoricalPrivate *p; g_return_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq)); g_return_if_fail (cat != NULL && GUPPI_IS_CATEGORY (cat)); p = seq->priv; guppi_refcounting_assign (p->category, cat); p->auto_add = FALSE; } gboolean guppi_seq_categorical_auto_add (GuppiSeqCategorical *seq) { g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE); return seq->priv->auto_add; } void guppi_seq_categorical_set_auto_add (GuppiSeqCategorical *seq, gboolean x) { g_return_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq)); seq->priv->auto_add = x; } const gchar * guppi_seq_categorical_get (GuppiSeqCategorical *seq, gint i) { code_t c; g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), NULL); g_return_val_if_fail (guppi_seq_in_bounds (GUPPI_SEQ (seq), i), NULL); g_return_val_if_fail (seq->priv->category != NULL, NULL); c = (code_t) guppi_seq_integer_get (GUPPI_SEQ_INTEGER (seq), i); return guppi_category_find_by_code (seq->priv->category, c); } gboolean guppi_seq_categorical_set (GuppiSeqCategorical *seq, gint i, const gchar *str) { code_t c; g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE); g_return_val_if_fail (guppi_seq_in_bounds (GUPPI_SEQ (seq), i), FALSE); g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (seq)), FALSE); g_return_val_if_fail (seq->priv->category != NULL, FALSE); c = guppi_category_find_by_name (seq->priv->category, str); if (seq->priv->auto_add && c == GUPPI_INVALID_CODE) c = guppi_category_add_by_name (seq->priv->category, str); if (c != GUPPI_INVALID_CODE) guppi_seq_integer_set (GUPPI_SEQ_INTEGER (seq), i, c); return c != GUPPI_INVALID_CODE; } gboolean guppi_seq_categorical_prepend (GuppiSeqCategorical *seq, const gchar *str) { code_t c; g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE); g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (seq)), FALSE); g_return_val_if_fail (seq->priv->category != NULL, FALSE); c = guppi_category_find_by_name (seq->priv->category, str); if (seq->priv->auto_add && c == GUPPI_INVALID_CODE) c = guppi_category_add_by_name (seq->priv->category, str); if (c != GUPPI_INVALID_CODE) guppi_seq_integer_prepend (GUPPI_SEQ_INTEGER (seq), c); return c != GUPPI_INVALID_CODE; } gboolean guppi_seq_categorical_append (GuppiSeqCategorical *seq, const gchar *str) { code_t c; g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE); g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (seq)), FALSE); g_return_val_if_fail (seq->priv->category != NULL, FALSE); c = guppi_category_find_by_name (seq->priv->category, str); if (seq->priv->auto_add && c == GUPPI_INVALID_CODE) c = guppi_category_add_by_name (seq->priv->category, str); if (c != GUPPI_INVALID_CODE) guppi_seq_integer_append (GUPPI_SEQ_INTEGER (seq), c); return c != GUPPI_INVALID_CODE; } gboolean guppi_seq_categorical_insert (GuppiSeqCategorical *seq, gint i, const gchar *str) { code_t c; g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), FALSE); g_return_val_if_fail (guppi_data_can_change (GUPPI_DATA (seq)), FALSE); g_return_val_if_fail (seq->priv->category != NULL, FALSE); c = guppi_category_find_by_name (seq->priv->category, str); if (seq->priv->auto_add && c == GUPPI_INVALID_CODE) c = guppi_category_add_by_name (seq->priv->category, str); if (c != GUPPI_INVALID_CODE) guppi_seq_integer_insert (GUPPI_SEQ_INTEGER (seq), i, c); return c != GUPPI_INVALID_CODE; } gint guppi_seq_categorical_frequency (GuppiSeqCategorical *seq, const gchar *str) { code_t c; gpointer ptr; g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), 0); g_return_val_if_fail (str != NULL, 0); g_return_val_if_fail (seq->priv->category != NULL, 0); c = guppi_category_find_by_name (seq->priv->category, str); ptr = g_hash_table_lookup (seq->priv->freq_table, GUPPI_CATEGORY_CODE_TO_POINTER (c)); return ptr ? *(gint *) ptr : 0; } double guppi_seq_categorical_percentage (GuppiSeqCategorical *seq, const gchar *str) { gsize N; g_return_val_if_fail (seq != NULL && GUPPI_IS_SEQ_CATEGORICAL (seq), 0); g_return_val_if_fail (str != NULL, 0); g_return_val_if_fail (seq->priv->category != NULL, 0); N = guppi_seq_count (GUPPI_SEQ (seq)); if (N == 0) return -1; return guppi_seq_categorical_frequency (seq, str) / (double) N; }