Eating Ternary Input

I’m happy I chose to write the function that consumes a ternary value last, shown in last week’s Lesson. The process turns out to be not that big of a deal, though trepidations scurried around me as I wrote the code.

As with translating a decimal value to ternary, the reverse process starts with the powers of 3 table, which I borrowed from earlier code:

 /* create the 3 powers table */
    powers[0] = 1;
    for(x=1;x<11;x++)
    {
        powers[x] = powers[x-1]*3;
    }
    /* backup over the null character */
    len--;

Because numbers are read from right to left, the next step is to locate the end of the string. The following loop does so, using integer len to mark the last character offset in the string referenced by char pointer *t.

    /* translate the string */
        /* find the end of the string */
    len = 0;
    while(1)
    {
        if( *(t+len)=='\n' || *(t+len)=='\0' )
        {
            break;
        }
        len++;
        /* limit to 11 digits */
        if( len==11 )
            break;
    }

An if statement in the loop guards against overflow. When it occurs, the input string is malformed because it’s longer than the powers of 3 table allows to translate a value. (The function should probably return -1 as an error, but I didn’t write this part of the code.)

With the offset obtained in variable len, the final step is to process the string backwards while working through the powers[] table forward. I chose a for loop for this operation, which allows me to manipulate both forward (p) and backward (x) variables at the same time:

    /* process the string backward
       but process the powers table forward */
    r = 0;
    for( x=len,p=0 ; x>=0 ; x--,p++)
    {
        /* bail on invalid digit */
        if( *(t+x)<'0' || *(t+x)>'2' )
            return(-1);
        r += ( (*(t+x)-'0') * powers[p] );
    }

The loop initializes variable x to the final character in the string (len) and p to zero, the first element in the powers[] array:

for( x=len,p=0 ; x>=0 ; x--,p++)

The loop spins until x is equal to zero. Along the way, x is decremented, working backwards through the string; p is incremented, working forward through the powers[] array.

The loop terminates upon an invalid digit, when *(t+x) is outside the range of '0', '1', or '2'. This behavior is similar to traditional C input functions, though I believe C checks the input string from left-to-right.

Variable r holds the return value, which starts as zero but increases with each valid digit found in the string, multiplied by its corresponding power of 3 from the powers[] table:

r += ( (*(t+x)-'0') * powers[p] );

The ASCII value of the string character, *(t+x), is reduced by '0' which makes its value 0, 1, or 2. This value is multiplied by a power of 3 from the powers[] table.

Here is the full source code listing:

2020_06_20-Lesson.c

#include <stdio.h>
#include <stdlib.h>

unsigned ternary_in(char *t)
{
    int powers[11];
    int len,x,r,p;

    /* create the 3 powers table */
    powers[0] = 1;
    for(x=1;x<11;x++)
    {
        powers[x] = powers[x-1]*3;
    }

    /* translate the string */
        /* find the end of the string */
    len = 0;
    while(1)
    {
        if( *(t+len)=='\n' || *(t+len)=='\0' )
        {
            break;
        }
        len++;
        /* limit to 11 digits */
        if( len==11 )
            break;
    }
    /* backup over the null character */
    len--;

    /* process the string backward
       but process the powers table forward */
    r = 0;
    for( x=len,p=0 ; x>=0 ; x--,p++)
    {
        /* bail on invalid digit */
        if( *(t+x)<'0' || *(t+x)>'2' )
            return(-1);
        r += ( (*(t+x)-'0') * powers[p] );
    }

    return(r);
}

int main()
{
    char input[12];
    int ternary;

    /* prompt for input */
    printf("Enter a value in ternary: ");
    fgets(input,12,stdin);

    ternary = ternary_in(input);
    if( ternary==-1 )
        puts("Invalid input");
    else
        printf("This value is %d decimal\n",ternary_in(input));

    return(0);
}

Sample run:

Enter a value in ternary: 210210210
This value is 15897 decimal

The real test, however, is to run value 15897 through the other program, the one I wrote last week to translate decimal into ternary. Here’s how it goes:

Enter a value: 15897
15897 in ternary is 210210210

So everything works. And if I’m correct, it’s possible to merge both ternary_in() and ternary_out() functions into a single program. Further, if the code is solid, you can change the base. I explore this insanity in next week’s Lesson.

Leave a Reply