0

Comparing output with ~cs50/pset5/speller texts/austinpowers.txt returns successfully and valgrind works. I don't know why Check50 gives an error. I would appreciate some help please.

Check50 NOW returns:

:) dictionary.c, dictionary.h, and Makefile exist
:) speller compiles
:( handles most basic words properly
    valgrind tests failed; rerun with --log for more information.
:( handles min length (1-char) words
    valgrind tests failed; rerun with --log for more information.
:( handles max length (45-char) words
    valgrind tests failed; rerun with --log for more information.
:( handles words with apostrophes properly
    valgrind tests failed; rerun with --log for more information.
:( spell-checking is case-insensitive
    valgrind tests failed; rerun with --log for more information.
:( handles substrings properly
    valgrind tests failed; rerun with --log for more information.

My Code (Added fclose() and free(new_node)):

/**
 * Implements a dictionary's functionality.
 */

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "dictionary.h"


//basic node structure
typedef struct node
{
    char word[LENGTH + 1];
    struct node *next;
}
node;

char new_word[LENGTH + 1];
int n = 0;
node *loaded_dict;
node *loaded_head;
int uni_size = 0;



//Returns true if word is in dictionary else false.

bool check(const char *word)
{
    char low_word[LENGTH + 1];
    char low_cursor[LENGTH + 1];

    node *cursor = loaded_head;


    for(int i = 0; i < LENGTH + 1; i++)
    {
        low_word[i] = ' ';
    }
    for(int j = 0; j < strlen(word); j++)
    {
        low_word[j] = tolower(word[j]);
    }


    //printf("\n%s/%s and %s", cursor->next->word, cursor->next->next->word, word);

    // TODO
    while(cursor != NULL)
    {

        for(int k = 0; k < LENGTH + 1; k++)
        {
            low_cursor[k] = ' ';
        }
        for(int l = 0; l < strlen(cursor->word); l++)
        {
            low_cursor[l] = tolower(cursor->word[l]);
        }

        //printf("\n%s and %s\n--\n", low_cursor, low_word);


        //compare strings
        if(strcoll(low_cursor, low_word) == 0)
        {
            //printf("\nmatch!");
            return true;
        }

        //printf("\nnot match");


        cursor = cursor->next;

    }
    //printf("\n\n");
    return false;
}

//Loads dictionary into memory. Returns true if successful else false.

bool load(const char *dictionary)
{
    // TODO
    FILE *dictfp = fopen(dictionary, "r");

    //node *new_node = malloc(sizeof(node));
    node *head = malloc(sizeof(node));
    //printf("Malloc Head\n");

    while(fscanf(dictfp, "%s", new_word)!= EOF)
    {
        node *new_node = malloc(sizeof(node));
        //printf("Malloc NewNode\n");
        if(new_node == NULL)
        {
            unload();
            free(new_node);
            free(head);
            fclose(dictfp);
            //printf("new_node = NULL");
            return false;

        }
        strcpy(new_node->word, new_word);

        //node *last_node = new_node;

        if(n != 0)
        {
            new_node->next = head;
            head = new_node;
            //printf("Not First: %s %s %s\n", head->word, head->next->word, head->next->next->word);
        }
        else
        {
            //strcpy(head->word, new_node->word);
            //head->next = new_node;
            new_node->next = head;
            head = new_node;
            //printf("First: %s %s %s\n", head->word, head->next->word, head->next->next->word);
        }

        n++;
        //free(new_node);

    }
    //printf("Finished: %s %s %s\n", head->word, head->next->word, head->next->next->word);
    uni_size = n;
    loaded_dict = head->next;
    loaded_head = head;

    fclose(dictfp);
    free(head);
    //printf("Free Head\n");
    //printf("Return True");
    return true;
}


//Returns number of words in dictionary if loaded else 0 if not yet loaded.

unsigned int size(void)
{
    // TODO
    return uni_size;
}


//Unloads dictionary from memory. Returns true if successful else false.

bool unload(void)
{
    // TODO
    while(loaded_dict != NULL)
    {
        node *temp = loaded_dict;
        loaded_dict = loaded_dict->next;
        //printf("Freeing %s, ", temp->word);
        free(temp);
        //printf("Free Temp\n");
    }
    return true;
}

Valgrind:

~/workspace/pset5/ $ valgrind ./speller texts/ralph.txt                                                                                                                                                                               
==28530== Memcheck, a memory error detector
==28530== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==28530== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==28530== Command: ./speller texts/ralph.txt
==28530== 

MISSPELLED WORDS

==28530== Invalid read of size 1
==28530==    at 0x4C2E0E2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x40121E: check (dictionary.c:60)
==28530==    by 0x400D61: main (speller.c:112)
==28530==  Address 0x6373e40 is 0 bytes inside a block of size 56 free'd
==28530==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x401401: load (dictionary.c:139)
==28530==    by 0x400A04: main (speller.c:40)
==28530== 
==28530== Invalid read of size 1
==28530==    at 0x4C2E0F4: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x40121E: check (dictionary.c:60)
==28530==    by 0x400D61: main (speller.c:112)
==28530==  Address 0x6373e41 is 1 bytes inside a block of size 56 free'd
==28530==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x401401: load (dictionary.c:139)
==28530==    by 0x400A04: main (speller.c:40)
==28530== 
==28530== Invalid read of size 1
==28530==    at 0x40123A: check (dictionary.c:62)
==28530==    by 0x400D61: main (speller.c:112)
==28530==  Address 0x6373e40 is 0 bytes inside a block of size 56 free'd
==28530==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x401401: load (dictionary.c:139)
==28530==    by 0x400A04: main (speller.c:40)
==28530== 
==28530== Invalid read of size 8
==28530==    at 0x401287: check (dictionary.c:78)
==28530==    by 0x400D61: main (speller.c:112)
==28530==  Address 0x6373e70 is 48 bytes inside a block of size 56 free'd
==28530==    at 0x4C2BDEC: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x401401: load (dictionary.c:139)
==28530==    by 0x400A04: main (speller.c:40)
==28530== 
==28530== Conditional jump or move depends on uninitialised value(s)
==28530==    at 0x4C2F220: __GI_strcmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x4ECC672: strcoll_l (strcoll_l.c:490)
==28530==    by 0x401270: check (dictionary.c:69)
==28530==    by 0x400D61: main (speller.c:112)
==28530==  Uninitialised value was created by a stack allocation
==28530==    at 0x401120: check (dictionary.c:33)
==28530== 
==28530== Conditional jump or move depends on uninitialised value(s)
==28530==    at 0x4C2F224: __GI_strcmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x4ECC672: strcoll_l (strcoll_l.c:490)
==28530==    by 0x401270: check (dictionary.c:69)
==28530==    by 0x400D61: main (speller.c:112)
==28530==  Uninitialised value was created by a stack allocation
==28530==    at 0x401120: check (dictionary.c:33)
==28530== 
==28530== Conditional jump or move depends on uninitialised value(s)
==28530==    at 0x4C2E0E9: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x40121E: check (dictionary.c:60)
==28530==    by 0x400D61: main (speller.c:112)
==28530==  Uninitialised value was created by a heap allocation
==28530==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x4012DE: load (dictionary.c:93)
==28530==    by 0x400A04: main (speller.c:40)
==28530== 
==28530== Conditional jump or move depends on uninitialised value(s)
==28530==    at 0x4011C1: check (dictionary.c:53)
==28530==    by 0x400D61: main (speller.c:112)
==28530==  Uninitialised value was created by a heap allocation
==28530==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x4012DE: load (dictionary.c:93)
==28530==    by 0x400A04: main (speller.c:40)
==28530== 
Wiggum
==28530== Conditional jump or move depends on uninitialised value(s)
==28530==    at 0x401431: unload (dictionary.c:160)
==28530==    by 0x400EE1: main (speller.c:152)
==28530==  Uninitialised value was created by a heap allocation
==28530==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==28530==    by 0x4012DE: load (dictionary.c:93)
==28530==    by 0x400A04: main (speller.c:40)
==28530== 

WORDS MISSPELLED:     1
WORDS IN DICTIONARY:  143091
WORDS IN TEXT:        20
TIME IN load:         1.82
TIME IN check:        14.69
TIME IN size:         0.00
TIME IN unload:       0.20
TIME IN TOTAL:        16.72

==28530== 
==28530== HEAP SUMMARY:
==28530==     in use at exit: 0 bytes in 0 blocks
==28530==   total heap usage: 143,094 allocs, 143,094 frees, 8,014,288 bytes allocated
==28530== 
==28530== All heap blocks were freed -- no leaks are possible
==28530== 
==28530== For counts of detected and suppressed errors, rerun with: -v
==28530== ERROR SUMMARY: 1481 errors from 9 contexts (suppressed: 0 from 0)

valgrind -v:

~/workspace/pset5/ $ valgrind -v ./speller texts/ralph.txt                                                                                                                                                                             
==26233== Memcheck, a memory error detector
==26233== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26233== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==26233== Command: ./speller texts/ralph.txt
==26233== 
--26233-- Valgrind options:
--26233--    --memcheck:leak-check=full
--26233--    --memcheck:track-origins=yes
--26233--    -v
--26233-- Contents of /proc/version:
--26233--   Linux version 4.9.17-c9 (root@30db80bfe262) (gcc version 4.9.2 (Debian 4.9.2-10) ) #1 SMP Thu Mar 23 01:38:54 UTC 2017
--26233-- Arch and hwcaps: AMD64, LittleEndian, amd64-cx16-rdtscp-sse3-avx
--26233-- Page sizes: currently 4096, max supported 4096
--26233-- Valgrind library directory: /usr/lib/valgrind
--26233-- Reading syms from /bin/dash
--26233--    object doesn't have a symbol table
--26233-- Reading syms from /lib/x86_64-linux-gnu/ld-2.19.so
--26233--   Considering /lib/x86_64-linux-gnu/ld-2.19.so ..
--26233--   .. CRC mismatch (computed 46abf574 wanted 3ca2d3ca)
--26233--   Considering /usr/lib/debug/lib/x86_64-linux-gnu/ld-2.19.so ..
--26233--   .. CRC is valid
--26233-- Reading syms from /usr/lib/valgrind/memcheck-amd64-linux
--26233--   Considering /usr/lib/valgrind/memcheck-amd64-linux ..
--26233--   .. CRC mismatch (computed 4f1eed43 wanted a323a3ab)
--26233--    object doesn't have a symbol table
--26233--    object doesn't have a dynamic symbol table
--26233-- Scheduler: using generic scheduler lock implementation.
--26233-- Reading suppressions file: /usr/lib/valgrind/default.supp
==26233== embedded gdbserver: reading from /tmp/vgdb-pipe-from-vgdb-to-26233-by-ubuntu-on-???
==26233== embedded gdbserver: writing to   /tmp/vgdb-pipe-to-vgdb-from-26233-by-ubuntu-on-???
==26233== embedded gdbserver: shared mem   /tmp/vgdb-pipe-shared-mem-vgdb-26233-by-ubuntu-on-???
==26233== 
==26233== TO CONTROL THIS PROCESS USING vgdb (which you probably
==26233== don't want to do, unless you know exactly what you're doing,
==26233== or are doing some strange experiment):
==26233==   /usr/lib/valgrind/../../bin/vgdb --pid=26233 ...command...
==26233== 
==26233== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==26233==   /path/to/gdb ./speller
==26233== and then give GDB the following command
==26233==   target remote | /usr/lib/valgrind/../../bin/vgdb --pid=26233
==26233== --pid is optional if only one valgrind process is running
==26233== 
--26233-- REDIR: 0x4019c50 (ld-linux-x86-64.so.2:strlen) redirected to 0x380764b1 (???)
--26233-- Reading syms from /usr/lib/valgrind/vgpreload_core-amd64-linux.so
--26233--   Considering /usr/lib/valgrind/vgpreload_core-amd64-linux.so ..
--26233--   .. CRC mismatch (computed fc68135e wanted 45f5e986)
--26233--    object doesn't have a symbol table
--26233-- Reading syms from /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so
--26233--   Considering /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so ..
--26233--   .. CRC mismatch (computed ae683f7e wanted 08c06df2)
--26233--    object doesn't have a symbol table
==26233== WARNING: new redirection conflicts with existing -- ignoring it
--26233--     old: 0x04019c50 (strlen              ) R-> (0000.0) 0x380764b1 ???
--26233--     new: 0x04019c50 (strlen              ) R-> (2007.0) 0x04c2e1a0 strlen
--26233-- REDIR: 0x4019a00 (ld-linux-x86-64.so.2:index) redirected to 0x4c2dd50 (index)
--26233-- REDIR: 0x4019c20 (ld-linux-x86-64.so.2:strcmp) redirected to 0x4c2f2f0 (strcmp)
--26233-- REDIR: 0x401a970 (ld-linux-x86-64.so.2:mempcpy) redirected to 0x4c31da0 (mempcpy)
--26233-- Reading syms from /lib/x86_64-linux-gnu/libc-2.19.so
--26233--   Considering /lib/x86_64-linux-gnu/libc-2.19.so ..
--26233--   .. CRC mismatch (computed ac9b5ddb wanted a10d05bf)
--26233--   Considering /usr/lib/debug/lib/x86_64-linux-gnu/libc-2.19.so ..
--26233--   .. CRC is valid
--26233-- REDIR: 0x4ec3c50 (libc.so.6:strcasecmp) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec5f40 (libc.so.6:strncasecmp) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec3420 (libc.so.6:memcpy@GLIBC_2.2.5) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec28c0 (libc.so.6:strstr) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ebf3f0 (libc.so.6:strcpy) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec3aa0 (libc.so.6:stpcpy) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ebdd10 (libc.so.6:index) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec1d30 (libc.so.6:strspn) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ebf510 (libc.so.6:strcspn) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec2e50 (libc.so.6:bcmp) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ebdf60 (libc.so.6:strcmp) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec8620 (libc.so.6:memcpy@@GLIBC_2.14) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec19a0 (libc.so.6:strpbrk) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec35b0 (libc.so.6:mempcpy) redirected to 0x4a25720 (_vgnU_ifunc_wrapper)
--26233-- REDIR: 0x4ec16b0 (libc.so.6:rindex) redirected to 0x4c2da30 (rindex)
--26233-- REDIR: 0x4ebdd40 (libc.so.6:__GI_strchr) redirected to 0x4c2db90 (__GI_strchr)
--26233-- REDIR: 0x4eb9660 (libc.so.6:malloc) redirected to 0x4c2ab10 (malloc)
--26233-- REDIR: 0x4eca9b0 (libc.so.6:strchrnul) redirected to 0x4c319b0 (strchrnul)
--26233-- REDIR: 0x4ebf9b0 (libc.so.6:strlen) redirected to 0x4c2e0e0 (strlen)
--26233-- REDIR: 0x4ec8670 (libc.so.6:__GI_memcpy) redirected to 0x4c2fc90 (__GI_memcpy)
--26233-- REDIR: 0x4f841c0 (libc.so.6:__mempcpy_ssse3_back) redirected to 0x4c31ad0 (mempcpy)
--26233-- REDIR: 0x4eb9d00 (libc.so.6:free) redirected to 0x4c2bd80 (free)
==26233== 
==26233== HEAP SUMMARY:
==26233==     in use at exit: 3,038 bytes in 95 blocks
==26233==   total heap usage: 95 allocs, 0 frees, 3,038 bytes allocated
==26233== 
==26233== Searching for pointers to 95 not-freed blocks
==26233== Checked 89,240 bytes
==26233== 
==26233== LEAK SUMMARY:
==26233==    definitely lost: 0 bytes in 0 blocks
==26233==    indirectly lost: 0 bytes in 0 blocks
==26233==      possibly lost: 0 bytes in 0 blocks
==26233==    still reachable: 3,038 bytes in 95 blocks
==26233==         suppressed: 0 bytes in 0 blocks
==26233== Reachable blocks (those to which a pointer was found) are not shown.
==26233== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==26233== 
==26233== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
==26233== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
0

1 Answer 1

0

Unless you've posted the wrong code, the code above has some valgrind errors. Based on the blocks still in use, there appears to be an unfreed node (56 bytes lost) and a file that was left open(568 bytes still accessible) - see end of printout. There are also some issues with uninitialized vars.

Given that you reported that valgrind works, I'll let you have a chance to investigate the problems further. If you can't resolve, leave a comment describing what you've found and where you are.

If this answers your question, please click on the check mark to accept. Let's keep up on forum maintenance. ;-)

~/workspace/test/pset5/ $ valgrind ./speller small small                                                                                                                                   
==2430== Memcheck, a memory error detector
==2430== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==2430== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==2430== Command: ./speller small small
==2430== 

MISSPELLED WORDS

==2430== Conditional jump or move depends on uninitialised value(s)
==2430==    at 0x4C2F220: __GI_strcmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2430==    by 0x4ECCA92: strcoll_l (strcoll_l.c:490)
==2430==    by 0x4012C1: check (dictionary.c:67)
==2430==    by 0x400D97: main (speller.c:117)
==2430==  Uninitialised value was created by a stack allocation
==2430==    at 0x401160: check (dictionary.c:31)
==2430== 
==2430== Conditional jump or move depends on uninitialised value(s)
==2430==    at 0x4C2F224: __GI_strcmp (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2430==    by 0x4ECCA92: strcoll_l (strcoll_l.c:490)
==2430==    by 0x4012C1: check (dictionary.c:67)
==2430==    by 0x400D97: main (speller.c:117)
==2430==  Uninitialised value was created by a stack allocation
==2430==    at 0x401160: check (dictionary.c:31)
==2430== 
==2430== Conditional jump or move depends on uninitialised value(s)
==2430==    at 0x401434: unload (dictionary.c:138)
==2430==    by 0x400F1C: main (speller.c:157)
==2430==  Uninitialised value was created by a heap allocation
==2430==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2430==    by 0x401368: load (dictionary.c:94)
==2430==    by 0x400A0D: main (speller.c:45)
==2430== 

WORDS MISSPELLED:     0
WORDS IN DICTIONARY:  5
WORDS IN TEXT:        5
TIME IN load:         0.03
TIME IN check:        0.01
TIME IN size:         0.00
TIME IN unload:       0.00
TIME IN TOTAL:        0.04

==2430== 
==2430== HEAP SUMMARY:
==2430==     in use at exit: 624 bytes in 2 blocks
==2430==   total heap usage: 8 allocs, 6 frees, 1,472 bytes allocated
==2430== 
==2430== 56 bytes in 1 blocks are definitely lost in loss record 1 of 2
==2430==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2430==    by 0x40132E: load (dictionary.c:90)
==2430==    by 0x400A0D: main (speller.c:45)
==2430== 
==2430== LEAK SUMMARY:
==2430==    definitely lost: 56 bytes in 1 blocks
==2430==    indirectly lost: 0 bytes in 0 blocks
==2430==      possibly lost: 0 bytes in 0 blocks
==2430==    still reachable: 568 bytes in 1 blocks
==2430==         suppressed: 0 bytes in 0 blocks
==2430== Reachable blocks (those to which a pointer was found) are not shown.
==2430== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==2430== 
==2430== For counts of detected and suppressed errors, rerun with: -v
==2430== ERROR SUMMARY: 12 errors from 4 contexts (suppressed: 0 from 0)
~/workspace/test/pset5/ $ 
18
  • It seems to be a valgrind, and using fclose() at the end of load, it has a leak summary of 56 bytes in 1 block. I do not know where this block is. Commented Oct 8, 2017 at 0:55
  • Look closely at line numbers indicated in valgrind, particularly those in dictionary.c That will give you a clue. At least, you should be able to narrow it down to one function.
    – Cliff B
    Commented Oct 8, 2017 at 1:13
  • I have freed new_node at the end of the while loop (as per "load (dictionary.c:94)") but valgrind points to it as an error Commented Oct 8, 2017 at 2:11
  • Well, I must be too tired right now. I've been looking at it and I've found the source of the leak. I've even fixed it. I just can't explain the exact mechanism of how it's getting leaked. I'll have to work on it. You should too. If you can figure it out, it'll be a great lesson for you! I'll give you a hint. It's definitely an issue with load. Since the dictionary is loading and you're getting correct output, look for a node that seems different from others in the code. ;-)
    – Cliff B
    Commented Oct 8, 2017 at 2:12
  • Still working on it. 'new_node' and 'head' are the only "malloc"ed nodes (the problem is with one of these?) and I have defined them in the same way. I've tried different approaches to ensure I've freed both. And come up with the same '1 block of 56' error. Is it a definition problem rather than a 'free' problem? Commented Oct 8, 2017 at 3:05

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .