Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Building a training set of tags for c #922

Closed
ErikSchierboom opened this issue Oct 31, 2023 · 23 comments
Closed

Building a training set of tags for c #922

ErikSchierboom opened this issue Oct 31, 2023 · 23 comments

Comments

@ErikSchierboom
Copy link
Member

Hello lovely maintainers 👋

We've recently added "tags" to student's solutions. These express the constructs, paradigms and techniques that a solution uses. We are going to be using these tags for lots of things including filtering, pointing a student to alternative approaches, and much more.

In order to do this, we've built out a full AST-based tagger in C#, which has allowed us to do things like detect recursion or bit shifting. We've set things up so other tracks can do the same for their languages, but its a lot of work, and we've determined that actually it may be unnecessary. Instead we think that we can use machine learning to achieve tagging with good enough results. We've fine-tuned a model that can determine the correct tags for C# from the examples with a high success rate. It's also doing reasonably well in an untrained state for other languages. We think that with only a few examples per language, we can potentially get some quite good results, and that we can then refine things further as we go.

I released a new video on the Insiders page that talks through this in more detail.

We're going to be adding a fully-fledged UI in the coming weeks that allow maintainers and mentors to tag solutions and create training sets for the neural networks, but to start with, we're hoping you would be willing to manually tag 20 solutions for this track. In this post we'll add 20 comments, each with a student's solution, and the tags our model has generated. Your mission (should you choose to accept it) is to edit the tags on each issue, removing any incorrect ones, and add any that are missing. In order to build one model that performs well across languages, it's best if you stick as closely as possible to the C# tags as you can. Those are listed here. If you want to add extra tags, that's totally fine, but please don't arbitrarily reword existing tags, even if you don't like what Erik's chosen, as it'll just make it less likely that your language gets the correct tags assigned by the neural network.


To summarise - there are two paths forward for this issue:

  1. You're up for helping: Add a comment saying you're up for helping. Update the tags some time in the next few days. Add a comment when you're done. We'll then add them to our training set and move forward.
  2. You not up for helping: No problem! Just please add a comment letting us know :)

If you tell us you're not able/wanting to help or there's no comment added, we'll automatically crowd-source this in a week or so.

Finally, if you have questions or want to discuss things, it would be best done on the forum, so the knowledge can be shared across all maintainers in all tracks.

Thanks for your help! 💙


Note: Meta discussion on the forum

@ErikSchierboom
Copy link
Member Author

Exercise: space-age

Code

#include <math.h>
#include "space_age.h"

double
convert_planet_age(planet planet, unsigned long age) {
    switch (planet) {
        case MERCURY:
            return age / (EARTH_YEAR * 0.2408467);
            break;
        case VENUS:
            return age / (EARTH_YEAR * 0.61519726);
        case EARTH:
            return age / EARTH_YEAR;
        case MARS:
            return age / (EARTH_YEAR * 1.8808158);
        case JUPITER:
            return age / (EARTH_YEAR * 11.862615);
        case SATURN:
            return age / (EARTH_YEAR * 29.447498);
        case URANUS:
            return age / (EARTH_YEAR * 84.016846);
        default: /* NEPTUNE */
            return age / (EARTH_YEAR * 164.79132);
    }
}

Tags:

construct:break
construct:comment
construct:divide
construct:double
construct:floating-point-number
construct:header
construct:include
construct:long
construct:multiply
construct:return
construct:switch
construct:unsigned
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:procedural

@ErikSchierboom
Copy link
Member Author

Exercise: raindrops

Code

#include <string.h>
#include <stdio.h>

void convert(char *result, int drops)
{
	char *p = result;

	if (drops % 3 == 0) {
		strcpy(p, "Pling");
		p += 5;
	}

	if (drops % 5 == 0) {
		strcpy(p, "Plang");
		p += 5;
	}

	if (drops % 7 == 0) {
		strcpy(p, "Plong");
		p += 5;
	}

	if (p == result)
		sprintf(p, "%d", drops);
}

Tags:

construct:add
construct:assignment
construct:char
construct:comment
construct:conditional
construct:if
construct:include
construct:int
construct:integral-number
construct:invocation
construct:parameter
construct:string
construct:variable
construct:visibility-modifier
paradigm:imperative
paradigm:procedural
uses:stdio.h
uses:string.h

@ErikSchierboom
Copy link
Member Author

Exercise: bob

Code

#include "bob.h"
#include <ctype.h>

char *hey_bob(char *phrase) {
  int is_question = 0;
  int any_upper = 0;
  int any_lower = 0;
  int all_empty = 1;

  char ch;
  while ((ch = *phrase++) != '\0') {
    if (ch != ' ')
      is_question = 0;
    if (ch == '?')
      is_question = 1;
    if (ch != ' ')
      all_empty = 0;
    if (islower(ch))
      any_lower = 1;
    if (isupper(ch))
      any_upper = 1;
  }

  if (any_upper && !any_lower)
    return "Whoa, chill out!";
  if (is_question)
    return "Sure.";
  if (all_empty)
    return "Fine. Be that way!";
  return "Whatever.";
}

Tags:

construct:assignment
construct:char
construct:header
construct:if
construct:include
construct:int
construct:integral
construct:invocation
construct:logical-and
construct:loop
construct:return
construct:string
construct:variable
construct:visibility-modifiers
construct:while-loop
paradigm:imperative
paradigm:procedural
technique:looping

@ErikSchierboom
Copy link
Member Author

Exercise: sum-of-multiples

Code

int is_multiple(const unsigned int *multiples, int count, int n)
{
	int j;

	if (!multiples)
		return 0;

	for (j = 0; j < count; j++) {
		if (multiples[j] && !(n % multiples[j]))
			return 1;
	}

	return 0;
}

int sum_of_multiples(const unsigned int *multiples, int count, int n)
{
	int s = 0;
	int i;

	for (i = 1; i < n; i++) {
		if (is_multiple(multiples, count, i))
			s += i;
	}

	return s;
}

Tags:

construct:assignment
construct:boolean
construct:const
construct:for-loop
construct:function
construct:if
construct:indexing
construct:int
construct:integral-number
construct:logical-and
construct:number
construct:parameter
construct:return
construct:unsigned-int
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:boolean-logic
technique:looping

@ErikSchierboom
Copy link
Member Author

Exercise: roman-numerals

Code

#include <stdlib.h>

static int handle_special_val(int rest, int val, char **pp, char c1, char c2)
{
	if (rest >= val) {
		if (c1)
			*(*pp)++ = c1;
		if (c2)
			*(*pp)++ = c2;
		rest -= val;
	}

	return rest;
}

static int handle_val(int rest, int val, char **pp, char c)
{
	int i;
	int quotient = rest / val;

	for (i = 0; i < quotient; i++)
		*(*pp)++ = c;

	return rest % val;	
}

char *to_roman_numeral(int n)
{
	char *res = malloc(256);
	char *p = res;
	int rest;

	rest = handle_val(n, 1000, &p, 'M');
	rest = handle_special_val(rest, 900, &p, 'C', 'M');
	rest = handle_special_val(rest, 500, &p, 'D', 0);
	rest = handle_special_val(rest, 400, &p, 'C', 'D');
	rest = handle_val(rest, 100, &p, 'C');
	rest = handle_special_val(rest, 90, &p, 'X', 'C');
	rest = handle_special_val(rest, 50, &p, 'L', 0);
	rest = handle_special_val(rest, 40, &p, 'X', 'L');
	rest = handle_val(rest, 10, &p, 'X');
	rest = handle_special_val(rest, 9, &p, 'I', 'X');
	rest = handle_special_val(rest, 5, &p, 'V', 0);
	rest = handle_special_val(rest, 4, &p, 'I', 'V');
	rest = handle_val(rest, 1, &p, 'I');

	// Nul terminate
	*p++ = 0;
	
	return res;
}

Tags:

construct:assignment
construct:bitwise-and
construct:char
construct:comment
construct:divide
construct:for-loop
construct:function
construct:if
construct:implicit-conversion
construct:int
construct:integral-number
construct:invocation
construct:malloc
construct:nested-function
construct:number
construct:parameter
construct:return
construct:static
construct:string
construct:subtract
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:bit-manipulation
technique:bit-shifting
technique:looping
uses:malloc

@ErikSchierboom
Copy link
Member Author

Exercise: series

Code

/*================================================================
*   Copyright (C) 2017. All rights reserved.
*   
*   name:series.c
*   creator:x
*   date:2017年04月19日
*   description:
*
================================================================*/


#include "series.h"
#include <string.h>
#include <stdlib.h>

static SeriesResults_t result;
SeriesResults_t series(char *inputText, unsigned int substringLength)
{
	result.substringCount = 0;
	result.substring = NULL;
	size_t len = strlen(inputText);
	if(len < substringLength || substringLength == 0)
		return result;
	size_t count = len - substringLength + 1;
	result.substringCount = count;
	char **pptr;
	if((pptr = malloc(count * sizeof(char *))) == NULL)
		exit(1);
	result.substring = pptr;
	for(size_t i = 0; i < count; i++){
		if((pptr[i] = malloc(substringLength + 1)) == NULL)
			exit(1);
		pptr[i][substringLength] = '\0';
		strncpy(pptr[i], inputText+i, substringLength);
	}
	return result;
}

Tags:

construct:add
construct:assignment
construct:char
construct:comment
construct:constructor
construct:exit
construct:for-loop
construct:header
construct:if
construct:implicit-conversion
construct:index
construct:int
construct:integral-number
construct:logical-or
construct:long-long
construct:method
construct:multiply
construct:null
construct:nullability
construct:number
construct:parameter
construct:return
construct:size_t
construct:static
construct:string
construct:struct
construct:subtract
construct:typedef
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:boolean-logic
technique:looping
uses:strlen

@ErikSchierboom
Copy link
Member Author

Exercise: phone-number

Code

/*================================================================
*   Copyright (C) 2017. All rights reserved.
*   
*   name:phone_number.c
*   creator:x
*   date:2017年04月16日
*   description:
*
================================================================*/


#include "phone_number.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#define valid_nondigit "() -."
#define is_invalid(c) ((c) != '(' && (c) != ')' && (c) != ' '\
		&& (c) != '-' && (c) != '.' )
char * phone_number_clean(const char *input)
{
	char * ptr;
	if((ptr = malloc(10 + 1)) == NULL)
		exit(1);
	ptr[10] = '\0';
	int len = strlen(input);
	if(len < 10)
		return memset(ptr, '0', 10);
	if(len == 11){
		if(input[0] == '1')
			input++;
		else
			return memset(ptr, '0', 10);
	}
	for(unsigned int i = 0; *input; input++){
		if(isdigit(*input)){
			if(i == 10)
				return memset(ptr, '0', 10);
			ptr[i++] = *input;
		}
		else if(is_invalid(*input))
			return memset(ptr,'0', 10);
	}
	return ptr;
}
char * phone_number_get_area_code(const char *input)
{
	char * area = phone_number_clean(input);
	area[3] = 0;
	return area;
}
char * phone_number_format(const char * input)
{
	char * clean = phone_number_clean(input);
	char * formatted;
	if((formatted = malloc(15)) == NULL)
		exit(1);
	formatted[14] = 0;
	strcpy(formatted, "(xxx) xxx-xxxx");
	strncpy(&formatted[1], clean, 3);
	strncpy(&formatted[6], &clean[3], 3);
	strncpy(&formatted[10], &clean[6], 4);
	free(clean);

	return formatted;
}

Tags:

construct:assignment
construct:bitwise-and
construct:char
construct:define
construct:for-loop
construct:function
construct:header
construct:if
construct:implicit-conversion
construct:indexing
construct:int
construct:integral-number
construct:invocation
construct:logical-and
construct:macro
construct:malloc
construct:number
construct:parameter
construct:return
construct:string
construct:typedef
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:metaprogramming
paradigm:procedural
technique:bit-manipulation
technique:bit-shifting
technique:looping
uses:char
uses:malloc

@ErikSchierboom
Copy link
Member Author

Exercise: sieve

Code

#include "sieve.h"

#include <stdlib.h>

unsigned int sieve(const unsigned int limit, primesArray_t primes) {
  char *composite = calloc(limit + 1, sizeof(char));
  if (!composite) {
    return -1;
  }

  for (unsigned int i = 2; i * i <= limit; ++i) {
    if (composite[i]) {
      continue;
    }

    for (unsigned int mult = i * i; mult <= limit; mult += i) {
      composite[mult] = 1;
    }
  }

  unsigned int n_primes = 0;
  for (unsigned int i = 2; i <= limit; ++i) {
    if (!composite[i]) {
      primes[n_primes++] = i;
    }
  }

  free(composite);

  return n_primes;
}

Tags:

construct:add
construct:assignment
construct:char
construct:continue
construct:for-loop
construct:if
construct:indexing
construct:int
construct:integral-number
construct:invocation
construct:loop
construct:multiply
construct:number
construct:parameter
construct:return
construct:size_t
construct:subtract
construct:typedef
construct:unsigned-int
construct:variable
construct:visibility
paradigm:imperative
paradigm:procedural
technique:looping

@ErikSchierboom
Copy link
Member Author

Exercise: pascals-triangle

Code

#include "pascals_triangle.h"

size_t **create_triangle(int n) {
    size_t **result = NULL;
    int count = 0, row = 1;

    if (n < 0) return NULL;

    if ((n == 0) || (n == 1)) {
        result = (size_t **)malloc(1 * sizeof(size_t *));
        result[0] = calloc(1, sizeof(size_t));
        if (n == 1) result[0][0] = 1;
        return result;
    }
    else {
        result = (size_t **)malloc(n * sizeof(size_t *));
        for (; count < n; count++)
            result[count] = (size_t *)calloc(n, sizeof(size_t));
    }

    result[0][0] = 1;

    for (; row < n; row++)
        for (count = 0; count < n; count++)
            result[row][count] = (count - 1) >= 0? result[row - 1][count - 1] + result[row - 1][count] : result[row - 1][count];

    return result;
}

void free_triangle(size_t **in, int size) {
    for (; size > 0; size--)
        free(in[size - 1]);

    free(in);
}

Tags:

construct:add
construct:assignment
construct:bitwise-or
construct:calloc
construct:conditional
construct:for-loop
construct:if
construct:implicit-conversion
construct:indexing
construct:int
construct:integral-number
construct:invocation
construct:logical-or
construct:malloc
construct:number
construct:null
construct:nullability
construct:parameter
construct:return
construct:size_t
construct:subtract
construct:ternary
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:bitwise
paradigm:declarative
paradigm:functional
paradigm:object-oriented
technique:bit-manipulation
technique:bit-shifting
technique:looping
technique:type-conversion
uses:bitwise-operators
uses:dynamic-memory-allocation

@ErikSchierboom
Copy link
Member Author

Exercise: all-your-base

Code

#include "all_your_base.h"
#include <math.h>
#include <string.h>

size_t rebase(int8_t digits[DIGITS_ARRAY_SIZE], int16_t from_base,
              int16_t to_base, size_t num_digits)
{
   uint16_t denary = 0;
   size_t new_num_digits = 0;

   if ((from_base <= 1) || (to_base <= 1) || (num_digits <= 0))
      return 0;                 /* invalid bases or length */
   if (digits[0] == 0)
      return 0;                 /* leading zeros */

   /* convert to denary */
   for (size_t i = 0; i < num_digits; ++i) {
      if (digits[i] < 0)
         return 0;              /* negative digits */
      if (digits[i] >= from_base)
         return 0;              /* invaid positive digit */
      denary += digits[i] * pow(from_base, (num_digits - i - 1));
   }

   /* calculate number of new digits */
   for (uint16_t j = denary; j > 0;) {
      j /= to_base;
      ++new_num_digits;
   }

   /* calculate and store new digits */
   for (size_t i = new_num_digits - 1; denary > 0; --i) {
      digits[i] = denary % to_base;
      denary /= to_base;
   }

   /* ensure rest of array is zero */
   memset(&digits[new_num_digits], 0, DIGITS_ARRAY_SIZE - new_num_digits);

   return new_num_digits;
}

Tags:

construct:assignment
construct:auto
construct:boolean
construct:comment
construct:divide
construct:double
construct:for-loop
construct:floating-point-number
construct:function
construct:if
construct:implicit-conversion
construct:include
construct:int
construct:integral-number
construct:logical-or
construct:method
construct:multiply
construct:number
construct:parameter
construct:return
construct:size_t
construct:subtract
construct:uint16_t
construct:uint8_t
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:logical
paradigm:object-oriented
technique:boolean-logic
technique:looping

@ErikSchierboom
Copy link
Member Author

Exercise: all-your-base

Code

#include<stdint.h>
#include<stddef.h>
#include<math.h>
#include<stdlib.h>
size_t rebase(int8_t digits[], int16_t input_base, int16_t output_base, size_t input_length){
       
       if(input_base<1 || output_base <1 || digits[0] == 0)
        return 0;
       
       int num=0,r;
       size_t output_length = 0;
       //int digit_inverse[10];
       int *digit_inverse = malloc (sizeof (int8_t) * 10);
       unsigned int i=0;
       
       for(i=0;i<input_length;i++){
        if(digits[i]<0 || digits[i] >= input_base)
            return 0;
        num += digits[i]*pow(input_base,input_length-i-1);
       }
       i=0;
       while(num > 0){
        r = (num%output_base);
        digit_inverse[i++] = r;
        output_length ++;
        num = num/output_base;
       }
       
       for(i=0; i<output_length; i++){
        digits[i] = digit_inverse[output_length-i-1];
       }
       
       return output_length;
       
}

Tags:

construct:assignment
construct:boolean
construct:comment
construct:divide
construct:double
construct:for-loop
construct:floating-point-number
construct:function
construct:if
construct:implicit-conversion
construct:index
construct:int
construct:integral-number
construct:logical-or
construct:malloc
construct:parameter
construct:return
construct:size_t
construct:subtract
construct:variable
construct:visibility-modifier
construct:while-loop
paradigm:imperative
paradigm:procedural
technique:boolean-logic
technique:looping
technique:math
uses:malloc

@ErikSchierboom
Copy link
Member Author

Exercise: queen-attack

Code

#include "queen_attack.h"

#define MAX_SIZE 8

attack_status_t can_attack(position_t queen_1, position_t queen_2) {
    if((queen_1.row == queen_2.row && queen_1.column == queen_2.column) ||
        queen_1.row >= MAX_SIZE || queen_1.column >= MAX_SIZE ||
        queen_2.row >= MAX_SIZE || queen_2.column >= MAX_SIZE) {
        // Shares same position or out of bounds
        return INVALID_POSITION;
    }

    if(queen_1.row == queen_2.row || queen_1.column == queen_2.column) {
        // Shares same row or column
        return CAN_ATTACK;
    }

    double result = (double)(queen_2.row - queen_1.row) / 
                            (queen_2.column - queen_1.column);

    if(result == 1 || result == -1) {
        // Shares same diagonal
        return CAN_ATTACK;
    }

    return CAN_NOT_ATTACK;
}

Tags:

construct:boolean
construct:comment
construct:define
construct:double
construct:explicit-conversion
construct:floating-point-number
construct:if
construct:implicit-conversion
construct:include
construct:int
construct:integral-number
construct:logical-and
construct:logical-or
construct:number
construct:parameter
construct:return
construct:subtract
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:boolean-logic
technique:type-conversion

@ErikSchierboom
Copy link
Member Author

Exercise: etl

Code

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "etl.h"

enum {
   lettersInAlphabet = 26,
};

int convert(const legacy_map * input, const size_t input_len, new_map ** output)
{
   int assignedKeys[lettersInAlphabet] = {0};
   int keyCount = 0;
   int outputIndex = 0;

   for(size_t index = 0; index < input_len; index++)
   {
      int value = input[index].value;
      unsigned long length = strlen(input[index].keys);

      for(unsigned long charIndex = 0; charIndex < length; charIndex++)
      {
         char testChar = input[index].keys[charIndex];

         if(isalpha(testChar))
         {
            int assignedKeyIndex = tolower(testChar) - 'a';

            assignedKeys[assignedKeyIndex] = value;
            keyCount++;
         }
      }
   }

   *output = calloc(keyCount, sizeof(new_map));
   for(int index = 0; index < lettersInAlphabet; index++)
   {
      if(assignedKeys[index] > 0)
      {
         (*output)[outputIndex].key = 'a' + index;
         (*output)[outputIndex].value = assignedKeys[index];
         outputIndex++;
      }
   }
   return keyCount;
}

Tags:

construct:add
construct:assignment
construct:char
construct:const
construct:enum
construct:for-loop
construct:if
construct:implicit-conversion
construct:indexing
construct:int
construct:integral-number
construct:invocation
construct:long
construct:number
construct:parameter
construct:return
construct:size_t
construct:subtract
construct:typedef
construct:variable
construct:visibility-modifier
paradigm:imperative
paradigm:structured
technique:looping

@ErikSchierboom
Copy link
Member Author

Exercise: etl

Code

#include "etl.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int mapcmp(const void* a, const void* b);
int mapcmp(const void* a, const void* b) {
    char ca = ((new_map*)a)->key;
    char cb = ((new_map*)b)->key;
    return ca - cb;
}

void insert(new_map* input, int* len, new_map value);
void insert(new_map* input, int* len, new_map value) {
    for (int i = 0 ; i < *len ; ++i) {
        if (input[i].key == value.key)
            return;
    }

    input[*len].key = value.key;
    input[(*len)++].value = value.value;
}

int convert(const legacy_map * input, const size_t input_len, new_map ** output) {
    new_map* result = malloc(sizeof(new_map) * 26);
    int result_len = 0;

    for (size_t i = 0 ; i < input_len ; ++i) {
        for (size_t j = 0 ; j < strlen(input[i].keys) ; ++j) {
            insert(result, &result_len,
                   (new_map){ tolower(input[i].keys[j]), input[i].value} );
        }
    }

    qsort(result, result_len, sizeof(new_map), mapcmp);
    *output = result;
    return result_len;
}

Tags:

construct:assignment
construct:auto
construct:bitwise-or
construct:char
construct:constructor
construct:explicit-conversion
construct:for-loop
construct:function
construct:if
construct:initializer
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:logical-or
construct:malloc
construct:named-argument
construct:number
construct:parameter
construct:return
construct:size_t
construct:struct
construct:subtract
construct:typedef
construct:variable
construct:visibility-modifiers
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:bit-manipulation
technique:bitwise-operators
technique:higher-order-functions
technique:looping
technique:type-conversion
uses:malloc

@ErikSchierboom
Copy link
Member Author

Exercise: say

Code

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

#define MAX_LEN 512

char *teens[] = { "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
    "sixteen", "seventeen", "eighteen", "nineteen" };
char *tens[] = { "", "ten", "twenty", "thirty", "forty", "fifty", "sixty",
    "seventy", "eighty", "ninty" };
char *ones[] = { "", "one", "two", "three", "four", "five", "six", "seven",
    "eight", "nine" };

char *powers[] = { "", " thousand", " million", " billion", " trillion" };

char *thousands(long digits) {
    char *tmp = calloc(sizeof(char), MAX_LEN);

    long one = digits % 10;
    long ten = (digits % 100) / 10;
    long hundered = (digits % 1000) / 100;

    if(hundered != 0 && ten == 0 && one == 0)
        sprintf(tmp, "%s hundred", ones[hundered]);
    else if(hundered != 0 && ten == 1)
        sprintf(tmp, "%s hundred %s", ones[hundered], teens[one]);
    else if(hundered != 0 && ten > 1 && one == 0)
        sprintf(tmp, "%s hundred %s", ones[hundered], tens[one]);
    else if(hundered != 0 && ten > 1 && one != 0)
        sprintf(tmp, "%s hundred %s-%s", ones[hundered], tens[ten], ones[one]);
    else if(hundered == 0 && ten == 1)
        sprintf(tmp, "%s", teens[one]);
    else if(hundered == 0 && ten > 1 && one != 0)
        sprintf(tmp, "%s-%s", tens[ten], ones[one]);
    else if(hundered == 0 && ten > 1 && one == 0)
        sprintf(tmp, "%s", tens[ten]);
    else if(hundered == 0 && ten == 0 && one != 0)
        sprintf(tmp, "%s", ones[one]);
    return tmp;
}

void strpre(char *dst, char *src) {
    char *tmp = calloc(sizeof(char), strlen(dst)+ strlen(src));
    strcpy(tmp, src);
    strcat(tmp, dst);
    strcpy(dst, tmp);
}

int say(long number, char **buffer) {
    *buffer = strdup("zero");
    if(number == 0) return 0;
    else if(number < 0 || 999999999999 < number) return -1;

    *buffer = calloc(sizeof(char), MAX_LEN);
    for(int p = 0; number > 0; number /= 1000, p++) {
        char *res = thousands(number % 1000);
        if(strlen(res) > 0) {
            strcat(res, powers[p]);
            if(strlen(*buffer) > 0)
                strcat(res, " ");
            strpre(*buffer, res);
        }
    }
    return 0;
}

Tags:

construct:add
construct:assignment
construct:bitwise-and
construct:boolean
construct:char
construct:define
construct:divide
construct:for-loop
construct:if
construct:implicit-conversion
construct:index
construct:int
construct:integral-number
construct:invocation
construct:logical-and
construct:logical-or
construct:long
construct:long-long
construct:macro
construct:nested-scope
construct:number
construct:parameter
construct:return
construct:string
construct:typedef
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:metaprogramming
paradigm:procedural
technique:bit-manipulation
technique:boolean-logic
technique:looping

@ErikSchierboom
Copy link
Member Author

Exercise: secret-handshake

Code

#include "secret_handshake.h"
#include <stdlib.h>
#include <stdbool.h>

void swap(char**, char**);
void swap(char** a, char** b) {
    char* temp = *b;
    *b = *a;
    *a = temp;
}

const char** commands(size_t number) {
    bool reverse = false;
    if (number >= 16) {
        reverse = true;
        number -= 16;
    }

    char** result = malloc(sizeof(char*) * 4);
    if (number == 0 || number == 16) {
        result[0] = NULL;
        return (const char**)result;
    }

    int idx = 0;
    if (number >= 8) {
        result[idx++] = "jump";
        number -= 8;
    }

    if (number >= 4) {
        result[idx++] = "close your eyes";
        number -= 4;
    }

    if (number >= 2) {
        result[idx++] = "double blink";
        number -= 2;
    }

    if (number >= 1) {
        result[idx++] = "wink";
        number -= 1;
    }

    if (!reverse) {
        for (int i = 0 ; i < idx/2 ; ++i)
            swap(&result[i], &result[idx-i-1]);
    }

    return (const char**)result;
}

Tags:

construct:assignment
construct:boolean
construct:char
construct:divide
construct:for-loop
construct:if
construct:implicit-conversion
construct:indexing
construct:int
construct:integral-number
construct:logical-or
construct:malloc
construct:number
construct:parameter
construct:return
construct:string
construct:subtract
construct:typedef
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:object-oriented
technique:boolean-logic
technique:looping
uses:malloc

@ErikSchierboom
Copy link
Member Author

Exercise: wordy

Code

#include "wordy.h"

#include <stdlib.h>
#include <string.h>

enum operator
{
  NONE,
  PLUS,
  MINUS,
  MULTIPLY,
  DIVIDE,
  RAISE
};

bool answer(const char *question, int *result)
{
  if (strncmp(question, "What is ", 8) != 0)
    return false;

  const char *startop1 = question + 8;
  char *endop1;
  long operand1 = strtol(startop1, &endop1, 0);
  if (endop1 == startop1)
    return false; // not a number

  do
  {
    enum operator op = NONE;
    const char *startop2;

#define COMP(str, operation)                         \
    if (strncmp(endop1, str, sizeof (str) - 1) == 0) \
    {                                                \
      op = operation;                                \
      startop2 = endop1 + sizeof (str) - 1;          \
    }
    COMP(" plus ", PLUS)
    COMP(" minus ", MINUS)
    COMP(" multiplied by ", MULTIPLY)
    COMP(" divided by ", DIVIDE)
    COMP(" raised to the ", RAISE);

    if (op == NONE)
      return false;

    char *endop2;
    long operand2 = strtol(startop2, &endop2, 0);
    if (endop2 == startop2)
      return false; // not a number

    if (op == RAISE && strncmp(endop2 + 2, " power", 6))
      endop2 += 6;

    switch (op)
    {
    case NONE:
      abort(); // Unreachable
    case PLUS:
      operand1 += operand2;
      break;
    case MINUS:
      operand1 -= operand2;
      break;
    case MULTIPLY:
      operand1 *= operand2;
      break;
    case DIVIDE:
      operand1 /= operand2;
      break;
    case RAISE:
      {
        long pow = 1;
        for (long i = 1; i < operand2; ++i)
          pow *= operand1;
        operand1 = pow;
      }
      break;
    }

    endop1 = endop2;
  }
  while (*endop1 != '?');

  *result = operand1;
  return true;
}

Tags:

construct:add
construct:assignment
construct:boolean
construct:break
construct:char
construct:comment
construct:const
construct:continue
construct:define
construct:do
construct:enum
construct:for-loop
construct:if
construct:include
construct:int
construct:integral-number
construct:logical-and
construct:long
construct:macro
construct:number
construct:parameter
construct:return
construct:sizeof
construct:string
construct:subtract
construct:switch
construct:variable
construct:visibility-modifiers
paradigm:imperative
paradigm:macro-expansion
paradigm:object-oriented
technique:boolean-logic
technique:looping

@ErikSchierboom
Copy link
Member Author

Exercise: diamond

Code

#include "diamond.h"
#include <stdlib.h>
#include <string.h>

char* reverse_string(char* str);
char* reverse_string(char* str) {
    size_t len = strlen(str);
    char* result = malloc(len+1);

    size_t idx = 0;
    for (size_t i = len ; i > 0 ; --i)
        result[idx++] = str[i-1];
    result[idx] = '\0';

    return result;
}

char **make_diamond(const char letter) {
    size_t size = (letter-'A')*2 + 1;
    char** result = malloc(size * sizeof(char*));

    for (size_t i = 0 ; i < size ; ++i)
        result[i] = malloc(size+1);

    char position = 'A';
    size_t diamond_idx = 0;
    size_t max_padding = size/2;

    while (position <= letter) {
        size_t idx = 0;
        size_t padding = letter-position;

        for (size_t i = 0 ; i < padding ; ++i)
            result[diamond_idx][idx++] = ' ';
        result[diamond_idx][idx++] = position;

        result[diamond_idx][idx] = '\0';
        char* reversed = reverse_string(result[diamond_idx]);

        size_t middle_len = (diamond_idx == 0) ? 0 : 2*diamond_idx-1;
        for (size_t i = 0 ; i < middle_len ; ++i)
            result[diamond_idx][idx++] = ' ';

        result[diamond_idx][idx] = '\0';
        strcat(result[diamond_idx], (diamond_idx == 0) ? (reversed+1) : reversed);
        free(reversed);

        diamond_idx++;
        position++;
    }

    position -= 2;

    while (position >= 'A') {
        size_t idx = 0;
        size_t padding = letter-position;

        for (size_t i = 0 ; i < padding ; ++i)
            result[diamond_idx][idx++] = ' ';
        result[diamond_idx][idx++] = position;

        for (size_t i = 0 ; i < (max_padding - padding) ; ++i)
            result[diamond_idx][idx++] = ' ';
        result[diamond_idx][idx] = '\0';

        char* reversed = reverse_string(result[diamond_idx]);
        strcat(result[diamond_idx], reversed+1);
        free(reversed);

        diamond_idx++;
        position--;
    }

    return result;
}

Tags:

construct:add
construct:assignment
construct:char
construct:divide
construct:for-loop
construct:free
construct:function
construct:function-declaration
construct:header
construct:if
construct:implicit-conversion
construct:indexing
construct:int
construct:integral-number
construct:invocation
construct:lambda
construct:malloc
construct:nested-function
construct:number
construct:parameter
construct:return
construct:size_t
construct:subtract
construct:ternary
construct:typedef
construct:variable
construct:visibility
construct:while-loop
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:higher-order-functions
technique:looping
technique:memory-management

@ErikSchierboom
Copy link
Member Author

Exercise: linked-list

Code

#include "linked_list.h"

#include <stdlib.h>

struct list_item
{
  ll_data_t data;
  struct list_item *next;
};

struct list_item **new_list()
{
  struct list_item **list = malloc(sizeof (struct list_item *));
  *list = NULL;
  return list;
}

bool is_list_empty(struct list_item **list)
{
  return list == NULL || *list == NULL;
}

bool push(struct list_item **list, ll_data_t item_data)
{
  if (list == NULL)
    return false;

  while (*list != NULL)
    list = &((*list)->next);

  *list = malloc(sizeof (struct list_item));
  (*list)->data = item_data;
  (*list)->next = NULL;
  return true;
}

ll_data_t pop(struct list_item **list)
{
  if (list == NULL || *list == NULL)
    return 0;

  while ((*list)->next != NULL)
    list = &((*list)->next);

  ll_data_t ret = (*list)->data;
  free(*list);
  *list = NULL;
  return ret;
}

ll_data_t shift(struct list_item **list)
{
  if (list == NULL || *list == NULL)
    return 0;

  struct list_item *first = *list;
  *list = first->next;
  ll_data_t ret = first->data;
  free(first);
  return ret;
}

bool unshift(struct list_item **list, ll_data_t item_data)
{
  if (list == NULL)
    return false;
  struct list_item *new = malloc(sizeof (struct list_item));
  new->data = item_data;
  new->next = *list;
  *list = new;
  return true;
}

void delete_list(struct list_item **list)
{
  if (list == NULL)
    return;
  struct list_item *tofree = *list;
  while(tofree != NULL)
  {
    struct list_item *tmp = tofree->next;
    free(tofree);
    tofree = tmp;
  }
  free(list);
}

Tags:

construct:assignment
construct:boolean
construct:free
construct:header
construct:if
construct:include
construct:int
construct:integral
construct:logical-or
construct:malloc
construct:null
construct:null-pointer
construct:parameter
construct:return
construct:sizeof
construct:struct
construct:typedef
construct:variable
construct:while-loop
paradigm:imperative
paradigm:procedural
technique:boolean-logic
technique:looping
technique:memory-management

@ErikSchierboom
Copy link
Member Author

Exercise: rational-numbers

Code

#include "rational_numbers.h"
#include <math.h>

rational_t add(rational_t r1, rational_t r2) {
    int16_t n = r1.numerator * r2.denominator 
              + r2.numerator * r1.denominator;
    int16_t d = !n ? 1 : r1.denominator * r2.denominator;
    return reduce((rational_t) {n,d});
}

rational_t subtract(rational_t r1, rational_t r2) {
    int16_t n = r1.numerator * r2.denominator 
              - r2.numerator * r1.denominator;
    int16_t d = !n ? 1 : r1.denominator * r2.denominator;
    return reduce((rational_t) {n,d});
}

rational_t multiply(rational_t r1, rational_t r2) {
    int16_t n = r1.numerator   * r2.numerator;
    int16_t d = r1.denominator * r2.denominator;
    return reduce((rational_t) {n,d});
}

rational_t divide(rational_t r1, rational_t r2) {
    int16_t n = r1.numerator * r2.denominator;
    int16_t d = r2.numerator * r1.denominator;
    return reduce((rational_t) {n,d});
}

rational_t absolute(rational_t r) {
    int16_t n_sign = r.numerator   >> 15;
    int16_t d_sign = r.denominator >> 15;
    int16_t n = (r.numerator   + n_sign) ^ n_sign;
    int16_t d = (r.denominator + d_sign) ^ d_sign;
    return reduce((rational_t) {n,d});
}

rational_t exp_rational(rational_t r, uint16_t n) {
    if (!n)           return (rational_t) {1,1};
    if (!r.numerator) return (rational_t) {0,1};

    int16_t n1 = pow(r.numerator,   n);
    int16_t d1 = pow(r.denominator, n);
    return reduce((rational_t) {n1,d1});
}

static float nth_root(float x, float n) {
    return pow(x, 1.0 / n);
}

float exp_real(uint16_t n, rational_t r) {
    if (!r.numerator) return 1.0;
    r = reduce(r);
    float p = pow(n, r.numerator);
    return nth_root(p, r.denominator);
}

static int16_t gcd(int16_t x, int16_t y) {
    int16_t t;
    while (y) {
        t = y;
        y = x % y;
        x = t;
    }
    return x;
}

rational_t reduce(rational_t r) {
    if (!r.numerator) return (rational_t) {0,1};

    int16_t x = gcd(r.numerator, r.denominator);
    int16_t n = r.numerator   / x;
    int16_t d = r.denominator / x;

    return d < 0 ? (rational_t) {-n,-d} 
                 : (rational_t) { n, d};
}

Tags:

construct:assignment
construct:bitwise-xor
construct:divide
construct:explicit-conversion
construct:float
construct:floating-point-number
construct:if
construct:implicit-conversion
construct:include
construct:int
construct:integral-number
construct:initializer-list
construct:invocation
construct:lambda
construct:left-shift
construct:method
construct:multiply
construct:number
construct:parameter
construct:return
construct:right-shift
construct:static
construct:static-variable
construct:ternary
construct:typedef
construct:variable
construct:visibility-modifiers
construct:while-loop
paradigm:functional
paradigm:imperative
paradigm:object-oriented
technique:bit-manipulation
technique:bit-shifting
technique:higher-order-functions
technique:looping
technique:math

@ErikSchierboom
Copy link
Member Author

This is an automated comment

Hello 👋 Next week we're going to start using the tagging work people are doing on these. If you've already completed the work, thank you! If you've not, but intend to this week, that's great! If you're not going to get round to doing it, and you've not yet posted a comment letting us know, could you please do so, so that we can find other people to do it. Thanks!

@wolf99
Copy link
Contributor

wolf99 commented Nov 14, 2023

I had hoped to find time to make a start on this but have failed to do so.
If there are others that can do it, I'm happy for that to happen.
Otherwise it's still on my list to try to get to (new baby in the house so that may be some time!)

@ErikSchierboom
Copy link
Member Author

No problem. Congrats on the baby!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants