Problem 022

Total of all name scores in data/p022.txt. Parse into NameEntry { packed strcmp keys, letter-score }, then declarative `|> sortBy` (fixed-size array — required by the sort lowering).

Answer871198282
Output871198282
StatusPASS
Native helperno
Runtime0 ms
Peak memory1472 KB
Time complexityO(n) (estimated)
Space complexityO(n) (estimated)

Performance comparison

MetricOur solutionBest known
Time complexityO(n)O(n log n)
Space complexityO(n)O(n)
ApproachFlow solutionSort names, compute scores
VerdictOptimal

Flow source

# Project Euler 022
# Total of all name scores in data/p022.txt.
#
# Parse into NameEntry { packed strcmp keys, letter-score }, then
# declarative `|> sortBy` (fixed-size array — required by the sort lowering).

extern {
    function fopen(path: string, mode: string) -> ptr<void>
    function fgetc(f: ptr<void>) -> i32
    function fclose(f: ptr<void>) -> i32
    function calloc(n: i64, size: i64) -> ptr<void>
    function free(p: ptr<void>) -> void
}

struct NameEntry {
    key0: i64
    key1: i64
    score: i64
}

# Pack 8 bytes of a fixed-width name row into one i64 (MSB-first)
# so ascending key order matches C strcmp on the null-padded row.
function pack8(names: ptr<i8>, row: i32, width: i32, start: i32) -> i64 {
    let mut k: i64 = 0
    for i in 0..8 {
        let mut c: i64 = (names[row * width + start + i] as i32) as i64
        if c < 0 {
            c = c + 256
        }
        k = (k << 8) | c
    }
    return k
}

function name_score(names: ptr<i8>, row: i32, width: i32) -> i64 {
    let mut total: i64 = 0
    for i in 0..width {
        let c: i32 = names[row * width + i] as i32
        if c == 0 {
            break
        }
        if c >= 65 && c <= 90 {
            total = total + ((c - 64) as i64)
        }
    }
    return total
}

function main() -> i32 {
    let width: i32 = 16
    let max_names: i32 = 6000
    let names: ptr<i8> = calloc((max_names * width) as i64, 1)
    if names == null {
        return 1
    }

    let f: ptr<void> = fopen("data/p022.txt", "r")
    if f == null {
        printf("failed to read data/p022.txt\n")
        free(names)
        return 1
    }

    let mut count: i32 = 0
    let mut blen: i32 = 0
    let mut in_name: bool = false
    let mut c: i32 = fgetc(f)
    while c >= 0 {
        if c == 34 {
            if in_name {
                names[count * width + blen] = 0
                count = count + 1
                blen = 0
                in_name = false
            } else {
                in_name = true
                blen = 0
            }
        } elif in_name {
            if blen < width - 1 {
                names[count * width + blen] = c as i8
                blen = blen + 1
            }
        }
        c = fgetc(f)
    }
    fclose(f)

    # Fixed capacity required by `|> sortBy`; unused slots get max keys
    # so they sort after every real name.
    let mut entries: array<NameEntry, 6000>
    let hi: i64 = 9223372036854775807
    for i in 0..max_names {
        if i < count {
            entries[i] = NameEntry {
                key0: pack8(names, i, width, 0),
                key1: pack8(names, i, width, 8),
                score: name_score(names, i, width)
            }
        } else {
            entries[i] = NameEntry { key0: hi, key1: hi, score: 0 }
        }
    }

    entries |> sortBy [asc .key0, asc .key1]

    let mut total: i64 = 0
    for i in 0..count {
        total = total + ((i + 1) as i64) * entries[i].score
    }
    printf("%lld\n", total)
    free(names)
    return 0
}

Generated C

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

/* Flow runtime helpers */
typedef struct flow_temp_node { struct flow_temp_node* next; } flow_temp_node;
static flow_temp_node* flow_temp_head = NULL;
static int flow_temp_atexit_set = 0;
__attribute__((unused)) static void flow_temp_free_all(void) {
    while (flow_temp_head) {
        flow_temp_node* n = flow_temp_head;
        flow_temp_head = n->next;
        free(n);
    }
}
__attribute__((unused)) static void* flow_temp_alloc(size_t nbytes) {
    flow_temp_node* node = (flow_temp_node*)malloc(sizeof(flow_temp_node) + nbytes);
    if (!node) return NULL;
    node->next = flow_temp_head;
    flow_temp_head = node;
    if (!flow_temp_atexit_set) {
        flow_temp_atexit_set = 1;
        atexit(flow_temp_free_all);
    }
    return (void*)(node + 1);
}
#ifndef FLOW_DIAG
#define FLOW_DIAG(msg) fprintf(stderr, "%s", (msg))
#endif
#ifndef FLOW_LOG
#define FLOW_LOG(fmt, ...) printf(fmt, __VA_ARGS__)
#endif
#ifndef FLOW_LOG_EMPTY
#define FLOW_LOG_EMPTY(fmt) printf(fmt)
#endif
static char* flow_strcat(const char* a, const char* b) {
    size_t la = strlen(a ? a : ""), lb = strlen(b ? b : "");
    char* r = (char*)flow_temp_alloc(la + lb + 1);
    if (!r) return NULL;
    if (la) memcpy(r, a, la);
    if (lb) memcpy(r + la, b, lb);
    r[la + lb] = '\0';
    return r;
}

#define __flow_in_arr(arr, val) __extension__ ({ \
    int _found = 0; \
    size_t _n = sizeof(arr)/sizeof((arr)[0]); \
    for (size_t _i = 0; _i < _n; _i++) { \
        if ((arr)[_i] == (val)) { _found = 1; break; } \
    } _found; })

/* Unified fault handler (MISRA #279) — override with -DFLOW_FAULT_HANDLER=fn */
#ifndef FLOW_FAULT_HANDLER
__attribute__((unused)) static inline void flow_fault_handler(const char* msg) {
    fprintf(stderr, "flow: %s\n", msg ? msg : "fault");
    abort();
#if defined(__GNUC__) || defined(__clang__)
    __builtin_unreachable();
#endif
}
#else
#define flow_fault_handler FLOW_FAULT_HANDLER
#endif
#define flow_div_by_zero_handler() flow_fault_handler("division by zero")
#define flow_shift_ub_handler() flow_fault_handler("invalid shift (amount out of range or left-shift of negative)")

#ifndef FLOW_CHECKED_DIV
#define FLOW_CHECKED_DIV(L, R) (((R) != 0) ? ((L) / (R)) : (flow_div_by_zero_handler(), (L) * 0))
#endif
#ifndef FLOW_CHECKED_MOD
#define FLOW_CHECKED_MOD(L, R) (((R) != 0) ? ((L) % (R)) : (flow_div_by_zero_handler(), (L) * 0))
#endif
#ifndef FLOW_CHECKED_SHL
#define FLOW_CHECKED_SHL(L, R) ((((R) >= 0) && ((unsigned long long)(R) < (sizeof(L) * 8ull)) && ((L) >= 0)) ? ((L) << (R)) : (flow_shift_ub_handler(), (L) * 0))
#endif
#ifndef FLOW_CHECKED_SHR
#define FLOW_CHECKED_SHR(L, R) ((((R) >= 0) && ((unsigned long long)(R) < (sizeof(L) * 8ull))) ? ((L) >> (R)) : (flow_shift_ub_handler(), (L) * 0))
#endif

#include <math.h>

void* _ui_state = NULL;

static inline float i32_to_f32(int32_t v) { return (float)v; }

/* Host stub for @gpu kernels (device codegen replaces this). */
static inline int32_t gpu_thread_id(void) { return 0; }

typedef struct NameEntry NameEntry;

struct NameEntry {
    int64_t key0;
    int64_t key1;
    int64_t score;
};

int64_t pack8_ptr_i8_i32_i32_i32(int8_t* names, int32_t row, int32_t width, int32_t start);
int64_t name_score_ptr_i8_i32_i32(int8_t* names, int32_t row, int32_t width);
int32_t main(void);

// Auto-generated declarative sort helpers
/* plan: natural_merge -- cheapest applicable plan: 75304 vs 81304 for bottom_up_merge (7% less work) */
static void __flow_sort_a40743a4ed6d(NameEntry *a, int32_t n) {
    if (n < 2) { return; }
    NameEntry buf[6000];
    int32_t starts[190];
    int32_t nr = 0;
    int32_t i = 0;
    /* Pass 1: walk natural runs. A strictly descending run is
       reversed in place (stable, because it is strict); a run
       shorter than the minimum is grown by insertion. */
    while (i < n) {
        starts[nr++] = i;
        int32_t j = i + 1;
        if (j < n && ((((((a[j]).key0) < ((a[i]).key0) ? -1 : (((a[j]).key0) > ((a[i]).key0) ? 1 : 0))) != 0 ? ((((a[j]).key0) < ((a[i]).key0) ? -1 : (((a[j]).key0) > ((a[i]).key0) ? 1 : 0))) : ((((a[j]).key1) < ((a[i]).key1) ? -1 : (((a[j]).key1) > ((a[i]).key1) ? 1 : 0))))) < 0) {
            while (j < n && ((((((a[j]).key0) < ((a[j - 1]).key0) ? -1 : (((a[j]).key0) > ((a[j - 1]).key0) ? 1 : 0))) != 0 ? ((((a[j]).key0) < ((a[j - 1]).key0) ? -1 : (((a[j]).key0) > ((a[j - 1]).key0) ? 1 : 0))) : ((((a[j]).key1) < ((a[j - 1]).key1) ? -1 : (((a[j]).key1) > ((a[j - 1]).key1) ? 1 : 0))))) < 0) { j++; }
            for (int32_t lo = i, hi = j - 1; lo < hi; lo++, hi--) {
                NameEntry t = a[lo];
                a[lo] = a[hi];
                a[hi] = t;
            }
        } else {
            while (j < n && ((((((a[j]).key0) < ((a[j - 1]).key0) ? -1 : (((a[j]).key0) > ((a[j - 1]).key0) ? 1 : 0))) != 0 ? ((((a[j]).key0) < ((a[j - 1]).key0) ? -1 : (((a[j]).key0) > ((a[j - 1]).key0) ? 1 : 0))) : ((((a[j]).key1) < ((a[j - 1]).key1) ? -1 : (((a[j]).key1) > ((a[j - 1]).key1) ? 1 : 0))))) >= 0) { j++; }
        }
        int32_t want = i + 32; if (want > n) { want = n; }
        while (j < want) {
            NameEntry key = a[j];
            int32_t k = j - 1;
            while (k >= i && ((((((a[k]).key0) < ((key).key0) ? -1 : (((a[k]).key0) > ((key).key0) ? 1 : 0))) != 0 ? ((((a[k]).key0) < ((key).key0) ? -1 : (((a[k]).key0) > ((key).key0) ? 1 : 0))) : ((((a[k]).key1) < ((key).key1) ? -1 : (((a[k]).key1) > ((key).key1) ? 1 : 0))))) > 0) { a[k + 1] = a[k]; k--; }
            a[k + 1] = key;
            j++;
        }
        i = j;
    }
    starts[nr] = n;
    /* Pass 2: merge adjacent runs pairwise until one remains. */
    while (nr > 1) {
        int32_t w = 0;
        for (int32_t r = 0; r + 1 < nr; r += 2) {
            int32_t lo = starts[r], mid = starts[r + 1], hi = starts[r + 2];
            int32_t x = lo, y = mid, k = lo;
            while (x < mid && y < hi) {
                if (((((((a[y]).key0) < ((a[x]).key0) ? -1 : (((a[y]).key0) > ((a[x]).key0) ? 1 : 0))) != 0 ? ((((a[y]).key0) < ((a[x]).key0) ? -1 : (((a[y]).key0) > ((a[x]).key0) ? 1 : 0))) : ((((a[y]).key1) < ((a[x]).key1) ? -1 : (((a[y]).key1) > ((a[x]).key1) ? 1 : 0))))) < 0) { buf[k++] = a[y++]; }
                else { buf[k++] = a[x++]; }
            }
            while (x < mid) { buf[k++] = a[x++]; }
            while (y < hi) { buf[k++] = a[y++]; }
            for (int32_t t = lo; t < hi; t++) { a[t] = buf[t]; }
            starts[w++] = lo;
        }
        if (nr % 2 == 1) { starts[w++] = starts[nr - 1]; }
        starts[w] = n;
        nr = w;
    }
}






int64_t pack8_ptr_i8_i32_i32_i32(int8_t* names, int32_t row, int32_t width, int32_t start) {
    int64_t k = 0;
    int32_t __flow_step_1 = 1;
    for (int32_t i = 0; (0 <= 8) ? i < 8 : i > 8; i += (0 <= 8) ? 1 : -1) {
        int64_t c = ((int64_t)(((int32_t)(names[(((row * width) + start) + i)]))));
        if (c < 0) {
            c = (c + 256);
        }
        k = (FLOW_CHECKED_SHL((k), (8)) | c);
    }
    return k;
}

int64_t name_score_ptr_i8_i32_i32(int8_t* names, int32_t row, int32_t width) {
    int64_t total = 0;
    int32_t __flow_step_2 = 1;
    for (int32_t i = 0; (0 <= width) ? i < width : i > width; i += (0 <= width) ? 1 : -1) {
        int32_t c = ((int32_t)(names[((row * width) + i)]));
        if (c == 0) {
            break;
        }
        if ((c >= 65 && c <= 90)) {
            total = (total + ((int64_t)((c - 64))));
        }
    }
    return total;
}

int32_t main(void) {
    int32_t width = 16;
    int32_t max_names = 6000;
    int8_t* names = (int8_t*)(calloc(((int64_t)((max_names * width))), 1));
    if (names == NULL) {
        return 1;
    }
    void* f = (void*)(fopen("data/p022.txt", "r"));
    if (f == NULL) {
        printf("failed to read data/p022.txt\n");
        free(names);
        return 1;
    }
    int32_t count = 0;
    int32_t blen = 0;
    bool in_name = 0;
    int32_t c = fgetc(f);
    while (c >= 0) {
        if (c == 34) {
            if (in_name) {
                names[((count * width) + blen)] = 0;
                count = (count + 1);
                blen = 0;
                in_name = 0;
            } else {
                in_name = 1;
                blen = 0;
            }
        } else if (in_name) {
            if (blen < (width - 1)) {
                names[((count * width) + blen)] = ((int8_t)(c));
                blen = (blen + 1);
            }
        }
        c = fgetc(f);
    }
    fclose(f);
    NameEntry entries[6000];
    int64_t hi = 9223372036854775807;
    int32_t __flow_step_3 = 1;
    for (int32_t i = 0; (0 <= max_names) ? i < max_names : i > max_names; i += (0 <= max_names) ? 1 : -1) {
        if (i < count) {
            entries[i] = (NameEntry){ .key0 = pack8_ptr_i8_i32_i32_i32(names, i, width, 0), .key1 = pack8_ptr_i8_i32_i32_i32(names, i, width, 8), .score = name_score_ptr_i8_i32_i32(names, i, width) };
        } else {
            entries[i] = (NameEntry){ .key0 = hi, .key1 = hi, .score = 0 };
        }
    }
    ({ __flow_sort_a40743a4ed6d((NameEntry*)(entries), 6000); entries; });
    int64_t total = 0;
    int32_t __flow_step_4 = 1;
    for (int32_t i = 0; (0 <= count) ? i < count : i > count; i += (0 <= count) ? 1 : -1) {
        total = (total + (((int64_t)((i + 1))) * (((unsigned)(i) < 6000) ? entries[i] : (fprintf(stderr, "array index %d out of bounds (size %d)\n", (int)(i), 6000), flow_fault_handler("array index out of bounds"), entries[0])).score));
    }
    printf("%lld\n", total);
    free(names);
    return 0;
}

Generated MLIR

(transpilation failed: Resolving modules...
Parsed 8 functions, 1 structs, 0 effects, 0 capabilities
MLIR generation error: MLIR backend does not support statement type SortExpr; use the C backend (--c))