#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>

#define MAX_BRIGHTNESS 255

// C99 doesn't define M_PI (GNU-C99 does)
#define M_PI 3.14159265358979323846264338327

/*
 * Loading part taken from
 * http://www.vbforums.com/showthread.php?t=261522
 * BMP info:
 * http://en.wikipedia.org/wiki/BMP_file_format
 *
 * Note: the magic number has been removed from the bmpfile_header_t
 * structure since it causes alignment problems
 *     bmpfile_magic_t should be written/read first
 * followed by the
 *     bmpfile_header_t
 * [this avoids compiler-specific alignment pragmas etc.]
 */

typedef struct {
    uint8_t magic[2];
} bmpfile_magic_t;

typedef struct {
    uint32_t filesz;
    uint16_t creator1;
    uint16_t creator2;
    uint32_t bmp_offset;
} bmpfile_header_t;

typedef struct {
    uint32_t header_sz;
    int32_t  width;
    int32_t  height;
    uint16_t nplanes;
    uint16_t bitspp;
    uint32_t compress_type;
    uint32_t bmp_bytesz;
    int32_t  hres;
    int32_t  vres;
    uint32_t ncolors;
    uint32_t nimpcolors;
} bitmap_info_header_t;

typedef struct {
    uint8_t r;
    uint8_t g;
    uint8_t b;
    uint8_t nothing;
} rgb_t;

// Use short int instead `unsigned char' so that we can
// store negative values.
typedef short int pixel_t;

pixel_t *load_bmp(const char *filename,
                  bitmap_info_header_t *bitmapInfoHeader)
{
    FILE *filePtr = fopen(filename, "rb");
    if (filePtr == NULL) {
        perror("fopen()");
        return NULL;
    }

    bmpfile_magic_t mag;
    if (fread(&mag, sizeof(bmpfile_magic_t), 1, filePtr) != 1) {
        fclose(filePtr);
        return NULL;
    }

    // verify that this is a bmp file by check bitmap id
    // warning: dereferencing type-punned pointer will break
    // strict-aliasing rules [-Wstrict-aliasing]
    if (*((uint16_t*)mag.magic) != 0x4D42) {
        fprintf(stderr, "Not a BMP file: magic=%c%c\n",
                mag.magic[0], mag.magic[1]);
        fclose(filePtr);
        return NULL;
    }

    bmpfile_header_t bitmapFileHeader; // our bitmap file header
    // read the bitmap file header
    if (fread(&bitmapFileHeader, sizeof(bmpfile_header_t),
              1, filePtr) != 1) {
        fclose(filePtr);
        return NULL;
    }

    // read the bitmap info header
    if (fread(bitmapInfoHeader, sizeof(bitmap_info_header_t),
              1, filePtr) != 1) {
        fclose(filePtr);
        return NULL;
    }

    if (bitmapInfoHeader->compress_type != 0)
        fprintf(stderr, "Warning, compression is not supported.\n");

    // move file point to the beginning of bitmap data
    if (fseek(filePtr, bitmapFileHeader.bmp_offset, SEEK_SET)) {
        fclose(filePtr);
        return NULL;
    }

    // allocate enough memory for the bitmap image data
    pixel_t *bitmapImage = malloc(bitmapInfoHeader->bmp_bytesz *
                                  sizeof(pixel_t));

    // verify memory allocation
    if (bitmapImage == NULL) {
        fclose(filePtr);
        return NULL;
    }

    // read in the bitmap image data
    size_t pad, count=0;
    unsigned char c;
    pad = 4*ceil(bitmapInfoHeader->bitspp*bitmapInfoHeader->width/32.) - bitmapInfoHeader->width;
    for(size_t i=0; i<bitmapInfoHeader->height; i++){
	    for(size_t j=0; j<bitmapInfoHeader->width; j++){
		    if (fread(&c, sizeof(unsigned char), 1, filePtr) != 1) {
			    fclose(filePtr);
			    return NULL;
		    }
		    bitmapImage[count++] = (pixel_t) c;
	    }
	    fseek(filePtr, pad, SEEK_CUR);
    }

    // If we were using unsigned char as pixel_t, then:
    // fread(bitmapImage, 1, bitmapInfoHeader->bmp_bytesz, filePtr);

    // close file and return bitmap image data
    fclose(filePtr);
    return bitmapImage;
}

// Return: true on error.
bool save_bmp(const char *filename, const bitmap_info_header_t *bmp_ih,
              const pixel_t *data)
{
    FILE* filePtr = fopen(filename, "wb");
    if (filePtr == NULL)
        return true;

    bmpfile_magic_t mag = {{0x42, 0x4d}};
    if (fwrite(&mag, sizeof(bmpfile_magic_t), 1, filePtr) != 1) {
        fclose(filePtr);
        return true;
    }

    const uint32_t offset = sizeof(bmpfile_magic_t) +
                            sizeof(bmpfile_header_t) +
                            sizeof(bitmap_info_header_t) +
                            ((1U << bmp_ih->bitspp) * 4);

    const bmpfile_header_t bmp_fh = {
        .filesz = offset + bmp_ih->bmp_bytesz,
        .creator1 = 0,
        .creator2 = 0,
        .bmp_offset = offset
    };

    if (fwrite(&bmp_fh, sizeof(bmpfile_header_t), 1, filePtr) != 1) {
        fclose(filePtr);
        return true;
    }
    if (fwrite(bmp_ih, sizeof(bitmap_info_header_t), 1, filePtr) != 1) {
        fclose(filePtr);
        return true;
    }

    // Palette
    for (size_t i = 0; i < (1U << bmp_ih->bitspp); i++) {
        const rgb_t color = {(uint8_t)i, (uint8_t)i, (uint8_t)i};
        if (fwrite(&color, sizeof(rgb_t), 1, filePtr) != 1) {
            fclose(filePtr);
            return true;
        }
    }

    // We use int instead of uchar, so we can't write img
    // in 1 call any more.
    // fwrite(data, 1, bmp_ih->bmp_bytesz, filePtr);

    // Padding: http://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage
    size_t pad = 4*ceil(bmp_ih->bitspp*bmp_ih->width/32.) - bmp_ih->width;
    unsigned char c;
    for(size_t i=0; i < bmp_ih->height; i++) {
	    for(size_t j=0; j < bmp_ih->width; j++) {
		    c = (unsigned char) data[j + bmp_ih->width*i];
		    if (fwrite(&c, sizeof(char), 1, filePtr) != 1) {
			    fclose(filePtr);
			    return true;
		    }
	    }
	    c = 0;
	    for(size_t j=0; j<pad; j++)
		    if (fwrite(&c, sizeof(char), 1, filePtr) != 1) {
			    fclose(filePtr);
			    return true;
		    }
    }

    fclose(filePtr);
    return false;
}

// if normalize is true, map pixels to range 0..MAX_BRIGHTNESS
void convolution(const pixel_t *in, pixel_t *out, const float *kernel,
                 const int nx, const int ny, const int kn,
                 const bool normalize)
{
    assert(kn % 2 == 1);
    assert(nx > kn && ny > kn);
    const int khalf = kn / 2;
    float min = FLT_MAX, max = -FLT_MAX;

    if (normalize)
        for (int m = khalf; m < nx - khalf; m++)
            for (int n = khalf; n < ny - khalf; n++) {
                float pixel = 0.0;
                size_t c = 0;
                for (int j = -khalf; j <= khalf; j++)
                    for (int i = -khalf; i <= khalf; i++) {
                        pixel += in[(n - j) * nx + m - i] * kernel[c];
                        c++;
                    }
                if (pixel < min)
                    min = pixel;
                if (pixel > max)
                    max = pixel;
                }

    for (int m = khalf; m < nx - khalf; m++)
        for (int n = khalf; n < ny - khalf; n++) {
            float pixel = 0.0;
            size_t c = 0;
            for (int j = -khalf; j <= khalf; j++)
                for (int i = -khalf; i <= khalf; i++) {
                    pixel += in[(n - j) * nx + m - i] * kernel[c];
                    c++;
                }

            if (normalize)
                pixel = MAX_BRIGHTNESS * (pixel - min) / (max - min);
            out[n * nx + m] = (pixel_t)pixel;
        }
}

/*
 * gaussianFilter:
 * http://www.songho.ca/dsp/cannyedge/cannyedge.html
 * determine size of kernel (odd #)
 * 0.0 <= sigma < 0.5 : 3
 * 0.5 <= sigma < 1.0 : 5
 * 1.0 <= sigma < 1.5 : 7
 * 1.5 <= sigma < 2.0 : 9
 * 2.0 <= sigma < 2.5 : 11
 * 2.5 <= sigma < 3.0 : 13 ...
 * kernelSize = 2 * int(2*sigma) + 3;
 */
void gaussian_filter(const pixel_t *in, pixel_t *out,
                     const int nx, const int ny, const float sigma)
{
    const int n = 2 * (int)(2 * sigma) + 3;
    const float mean = (float)floor(n / 2.0);
    float kernel[n * n]; // variable length array

    fprintf(stderr, "gaussian_filter: kernel size %d, sigma=%g\n",
            n, sigma);
    size_t c = 0;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++) {
            kernel[c] = exp(-0.5 * (pow((i - mean) / sigma, 2.0) +
                                    pow((j - mean) / sigma, 2.0)))
                        / (2 * M_PI * sigma * sigma);
            c++;
        }

    convolution(in, out, kernel, nx, ny, n, true);
}

/*
 * Links:
 * http://en.wikipedia.org/wiki/Canny_edge_detector
 * http://www.tomgibara.com/computer-vision/CannyEdgeDetector.java
 * http://fourier.eng.hmc.edu/e161/lectures/canny/node1.html
 * http://www.songho.ca/dsp/cannyedge/cannyedge.html
 *
 * Note: T1 and T2 are lower and upper thresholds.
 */
pixel_t *canny_edge_detection(const pixel_t *in,
                              const bitmap_info_header_t *bmp_ih,
                              const int tmin, const int tmax,
                              const float sigma)
{
    const int nx = bmp_ih->width;
    const int ny = bmp_ih->height;

    pixel_t *G = calloc(nx * ny * sizeof(pixel_t), 1);
    pixel_t *after_Gx = calloc(nx * ny * sizeof(pixel_t), 1);
    pixel_t *after_Gy = calloc(nx * ny * sizeof(pixel_t), 1);
    pixel_t *nms = calloc(nx * ny * sizeof(pixel_t), 1);
    pixel_t *out = malloc(bmp_ih->bmp_bytesz * sizeof(pixel_t));

    if (G == NULL || after_Gx == NULL || after_Gy == NULL ||
        nms == NULL || out == NULL) {
        fprintf(stderr, "canny_edge_detection:"
                " Failed memory allocation(s).\n");
        exit(1);
    }

    gaussian_filter(in, out, nx, ny, sigma);

    const float Gx[] = {-1, 0, 1,
                        -2, 0, 2,
                        -1, 0, 1};

    convolution(out, after_Gx, Gx, nx, ny, 3, false);

    const float Gy[] = { 1, 2, 1,
                         0, 0, 0,
                        -1,-2,-1};

    convolution(out, after_Gy, Gy, nx, ny, 3, false);

    for (int i = 1; i < nx - 1; i++)
        for (int j = 1; j < ny - 1; j++) {
            const int c = i + nx * j;
            // G[c] = abs(after_Gx[c]) + abs(after_Gy[c]);
            G[c] = (pixel_t)hypot(after_Gx[c], after_Gy[c]);
        }

    // Non-maximum suppression, straightforward implementation.
    for (int i = 1; i < nx - 1; i++)
        for (int j = 1; j < ny - 1; j++) {
            const int c = i + nx * j;
            const int nn = c - nx;
            const int ss = c + nx;
            const int ww = c + 1;
            const int ee = c - 1;
            const int nw = nn + 1;
            const int ne = nn - 1;
            const int sw = ss + 1;
            const int se = ss - 1;

            const float dir = (float)(fmod(atan2(after_Gy[c],
                                                 after_Gx[c]) + M_PI,
                                           M_PI) / M_PI) * 8;

            if (((dir <= 1 || dir > 7) && G[c] > G[ee] &&
                 G[c] > G[ww]) || // 0 deg
                ((dir > 1 && dir <= 3) && G[c] > G[nw] &&
                 G[c] > G[se]) || // 45 deg
                ((dir > 3 && dir <= 5) && G[c] > G[nn] &&
                 G[c] > G[ss]) || // 90 deg
                ((dir > 5 && dir <= 7) && G[c] > G[ne] &&
                 G[c] > G[sw]))   // 135 deg
                nms[c] = G[c];
            else
                nms[c] = 0;
        }

    // Reuse array
    // used as a stack. nx*ny/2 elements should be enough.
    int *edges = (int*) after_Gy;
    memset(out, 0, sizeof(pixel_t) * nx * ny);
    memset(edges, 0, sizeof(pixel_t) * nx * ny);

    // Tracing edges with hysteresis . Non-recursive implementation.
    size_t c = 1;
    for (int j = 1; j < ny - 1; j++)
        for (int i = 1; i < nx - 1; i++) {
            if (nms[c] >= tmax && out[c] == 0) { // trace edges
                out[c] = MAX_BRIGHTNESS;
                int nedges = 1;
                edges[0] = c;

                do {
                    nedges--;
                    const int t = edges[nedges];

                    int nbs[8]; // neighbours
                    nbs[0] = t - nx;     // nn
                    nbs[1] = t + nx;     // ss
                    nbs[2] = t + 1;      // ww
                    nbs[3] = t - 1;      // ee
                    nbs[4] = nbs[0] + 1; // nw
                    nbs[5] = nbs[0] - 1; // ne
                    nbs[6] = nbs[1] + 1; // sw
                    nbs[7] = nbs[1] - 1; // se

                    for (int k = 0; k < 8; k++)
                        if (nms[nbs[k]] >= tmin && out[nbs[k]] == 0) {
                            out[nbs[k]] = MAX_BRIGHTNESS;
                            edges[nedges] = nbs[k];
                            nedges++;
                        }
                } while (nedges > 0);
            }
            c++;
        }

    free(after_Gx);
    free(after_Gy);
    free(G);
    free(nms);

    return out;
}

int main(const int argc, const char ** const argv)
{
    if (argc < 2) {
        printf("Usage: %s image.bmp\n", argv[0]);
        return 1;
    }

    static bitmap_info_header_t ih;
    const pixel_t *in_bitmap_data = load_bmp(argv[1], &ih);
    if (in_bitmap_data == NULL) {
        fprintf(stderr, "main: BMP image not loaded.\n");
        return 1;
    }

    printf("Info: %d x %d x %d\n", ih.width, ih.height, ih.bitspp);

    const pixel_t *out_bitmap_data =
        canny_edge_detection(in_bitmap_data, &ih, 45, 50, 1.0f);
    if (out_bitmap_data == NULL) {
        fprintf(stderr, "main: failed canny_edge_detection.\n");
        return 1;
    }

    if (save_bmp("out.bmp", &ih, out_bitmap_data)) {
        fprintf(stderr, "main: BMP image not saved.\n");
        return 1;
    }

    free((pixel_t*)in_bitmap_data);
    free((pixel_t*)out_bitmap_data);
    return 0;
}


#define MAXLEN 64

type listitem                  ' An item of a list may be a number
    is_num as boolean       ' or another list, so I have to account
    union                      ' for both, implemented as a union.
        list as any ptr        ' FreeBASIC is twitchy about circularly
        num as uinteger        ' defined types, so one workaround is to
    end union                  ' use a generic pointer that I will cast
end type                       ' later.

type list
    length as uinteger              'simple, fixed storage length lists
    item(1 to MAXLEN) as listitem   'are good enough for this example
end type

sub print_list( list as list )
    print "{";
    if list.length = 0 then print "}"; : return
    for i as uinteger = 1 to list.length
        if list.item(i).is_num then
            print str(list.item(i).num);
        else     'recursively print sublist
            print_list( *cast(list ptr, list.item(i).list) )
        end if
    if i<list.length then print ", "; else print "}";   'handle comma
    next i                                              'gracefully
    return
end sub

function cartprod( A as list, B as list ) as list
    dim as uinteger i, j
    dim as list C
    dim as list ptr inner  'for brevity
    C.length = 0
    for i = 1 to A.length
        for j = 1 to B.length
            C.length += 1
            C.item(C.length).is_num = false   'each item of the new list is a list itself
            inner = allocate( sizeof(list) )     'make space for it
            C.item(C.length).list = inner
            inner->length = 2                    'each inner list contains two items
            inner->item(1) = A.item(i)           'one from the first list
            inner->item(2) = B.item(j)           'and one from the second
        next j
    next i
    return C
end function

dim as list EMPTY, A, B, R
EMPTY.length = 0
A.length = 2
A.item(1).is_num = true : A.item(1).num = 1
A.item(2).is_num = true : A.item(2).num = 2
B.length = 2
B.item(1).is_num = true : B.item(1).num = 3
B.item(2).is_num = true : B.item(2).num = 4

R = cartprod(A, B)
print_list(R) : print   'print_list does not supply a final newline
R = cartprod(B, A) : print_list(R) : print
R = cartprod(A, EMPTY) : print_list(R) : print
R = cartprod(EMPTY, A) : print_list(R) : print


show(Limit) :-
    forall(
        carmichael(Limit, P1, P2, P3, C),
        format("~w * ~w * ~w ~t~20| = ~w~n", [P1, P2, P3, C])).

carmichael(Upto, P1, P2, P3, X) :-
    between(2, Upto, P1),
    prime(P1),
    Limit is P1 - 1, between(2, Limit, H3),
    MaxD is H3 + P1 - 1, between(1, MaxD, D),
    (H3 + P1)*(P1 - 1) mod D =:= 0,
    -P1*P1 mod H3 =:= D mod H3,
    P2 is 1 + (P1 - 1)*(H3 + P1) div D,
    prime(P2),
    P3 is 1 + P1*P2 div H3,
    prime(P3),
    X is P1*P2*P3.

wheel235(L) :-
   W = [4, 2, 4, 2, 4, 6, 2, 6 | W],
   L = [1, 2, 2 | W].

prime(N) :-
   N >= 2,
   wheel235(W),
   prime(N, 2, W).

prime(N, D, _) :- D*D > N, !.
prime(N, D, [A|As]) :-
    N mod D =\= 0,
    D2 is D + A, prime(N, D2, As).


class Isprime():
    '''
    Extensible sieve of Eratosthenes

    >>> isprime.check(11)
    True
    >>> isprime.multiples
    {2, 4, 6, 8, 9, 10}
    >>> isprime.primes
    [2, 3, 5, 7, 11]
    >>> isprime(13)
    True
    >>> isprime.multiples
    {2, 4, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 18, 20, 21, 22}
    >>> isprime.primes
    [2, 3, 5, 7, 11, 13, 17, 19]
    >>> isprime.nmax
    22
    >>>
    '''
    multiples = {2}
    primes = [2]
    nmax = 2

    def __init__(self, nmax):
        if nmax > self.nmax:
            self.check(nmax)

    def check(self, n):
        if type(n) == float:
            if not n.is_integer(): return False
            n = int(n)
        multiples = self.multiples
        if n <= self.nmax:
            return n not in multiples
        else:
            # Extend the sieve
            primes, nmax = self.primes, self.nmax
            newmax = max(nmax*2, n)
            for p in primes:
                multiples.update(range(p*((nmax + p + 1) // p), newmax+1, p))
            for i in range(nmax+1, newmax+1):
                if i not in multiples:
                    primes.append(i)
                    multiples.update(range(i*2, newmax+1, i))
            self.nmax = newmax
            return n not in multiples

    __call__ = check

def carmichael(p1):
    ans = []
    if isprime(p1):
        for h3 in range(2, p1):
            g = h3 + p1
            for d in range(1, g):
                if (g * (p1 - 1)) % d == 0 and (-p1 * p1) % h3 == d % h3:
                    p2 = 1 + ((p1 - 1)* g // d)
                    if isprime(p2):
                        p3 = 1 + (p1 * p2 // h3)
                        if isprime(p3):
                            if (p2 * p3) % (p1 - 1) == 1:
                                #print('%i X %i X %i' % (p1, p2, p3))
                                ans += [tuple(sorted((p1, p2, p3)))]
    return ans

isprime = Isprime(2)

ans = sorted(sum((carmichael(n) for n in range(62) if isprime(n)), []))
print(',\n'.join(repr(ans[i:i+5])[1:-1] for i in range(0, len(ans)+1, 5)))


const (
    width = 81
    height = 5
)

fn cantor(mut lines [][]u8, start int, len int, index int) {
    seg := len / 3
    if seg == 0 {
        return
    }
    for i in index.. height {
        for j in start + seg..start + 2 * seg {
            lines[i][j] = ' '[0]
        }
    }
    cantor(mut lines, start, seg, index + 1)
    cantor(mut lines, start + seg * 2, seg, index + 1)
}

fn main() {
	mut lines := [][]u8{len:height, init: []u8{len:width, init:'*'[0]}}
    cantor(mut lines, 0, width, 1)
    for line in lines {
        println(line.bytestr())
    }
}


var width = 81
var height = 5

var lines = [[]] * height
for (i in 0...height) lines[i] = ["*"] * width

var cantor // recursive so need to declare variable first
cantor = Fn.new { |start, len, index|
    var seg = (len/3).floor
    if (seg == 0) return
    for (i in index...height) {
        for (j in (start+seg)...(start+seg*2)) lines[i][j] = " "
    }
    cantor.call(start, seg, index + 1)
    cantor.call(start + seg*2, seg, index + 1)
}

cantor.call(0, width, 1)
for (i in 0...height) System.print(lines[i].join())


import core.stdc.stdio, std.math, std.typecons, std.string, std.conv,
       std.algorithm, std.ascii, std.array, bitmap, grayscale_image;

enum maxBrightness = 255;

alias Pixel = short;
alias IntT = typeof(size_t.init.signed);

// If normalize is true, map pixels to range 0...maxBrightness.
void convolution(bool normalize)(in Pixel[] inp, Pixel[] outp,
                                 in float[] kernel,
                                 in IntT nx, in IntT ny, in IntT kn)
pure nothrow @nogc in {
    assert(kernel.length == kn ^^ 2);
    assert(kn % 2 == 1);
    assert(nx > kn && ny > kn);
    assert(inp.length == outp.length);
} body {
    //immutable IntT kn = sqrti(kernel.length);
    immutable IntT khalf = kn / 2;

    static if (normalize) {
        float pMin = float.max, pMax = -float.max;

        foreach (immutable m; khalf .. nx - khalf) {
            foreach (immutable n; khalf .. ny - khalf) {
                float pixel = 0.0;
                size_t c;
                foreach (immutable j; -khalf .. khalf + 1) {
                    foreach (immutable i; -khalf .. khalf + 1) {
                        pixel += inp[(n - j) * nx + m - i] * kernel[c];
                        c++;
                    }
                }

                if (pixel < pMin) pMin = pixel;
                if (pixel > pMax) pMax = pixel;
            }
        }
    }

    foreach (immutable m; khalf .. nx - khalf) {
        foreach (immutable n; khalf .. ny - khalf) {
            float pixel = 0.0;
            size_t c;
            foreach (immutable j; -khalf .. khalf + 1) {
                foreach (immutable i; -khalf .. khalf + 1) {
                    pixel += inp[(n - j) * nx + m - i] * kernel[c];
                    c++;
                }
            }

            static if (normalize)
                pixel = maxBrightness * (pixel - pMin) / (pMax - pMin);
            outp[n * nx + m] = cast(Pixel)pixel;
        }
    }
}

void gaussianFilter(in Pixel[] inp, Pixel[] outp,
                    in IntT nx, in IntT ny, in float sigma)
pure nothrow in {
    assert(inp.length == outp.length);
} body {
    immutable IntT n = 2 * cast(IntT)(2 * sigma) + 3;
    immutable float mean = floor(n / 2.0);
    auto kernel = new float[n * n];

    debug fprintf(stderr,
                  "gaussianFilter: kernel size %d, sigma=%g\n",
                  n, sigma);

    size_t c;
    foreach (immutable i; 0 .. n) {
        foreach (immutable j; 0 .. n) {
            kernel[c] = exp(-0.5 * (((i - mean) / sigma) ^^ 2 +
                                    ((j - mean) / sigma) ^^ 2))
                        / (2 * PI * sigma * sigma);
            c++;
        }
    }

    convolution!true(inp, outp, kernel, nx, ny, n);
}

Image!Pixel cannyEdgeDetection(in Image!Pixel inp,
                               in IntT tMin, in IntT tMax,
                               in float sigma)
pure nothrow in {
    assert(inp !is null);
} body {
    immutable IntT nx = inp.nx.signed;
    immutable IntT ny = inp.ny.signed;
    auto outp = new Pixel[nx * ny];

    gaussianFilter(inp.image, outp, nx, ny, sigma);

    static immutable float[] Gx = [-1, 0, 1,
                                   -2, 0, 2,
                                   -1, 0, 1];
    auto after_Gx = new Pixel[nx * ny];
    convolution!false(outp, after_Gx, Gx, nx, ny, 3);

    static immutable float[] Gy = [ 1, 2, 1,
                                    0, 0, 0,
                                   -1,-2,-1];
    auto after_Gy = new Pixel[nx * ny];
    convolution!false(outp, after_Gy, Gy, nx, ny, 3);

    auto G = new Pixel[nx * ny];
    foreach (i; 1 .. nx - 1)
        foreach (j; 1 .. ny - 1) {
            immutable size_t c = i + nx * j;
            G[c] = cast(Pixel)hypot(after_Gx[c], after_Gy[c]);
        }

    // Non-maximum suppression, straightforward implementation.
    auto nms = new Pixel[nx * ny];
    foreach (immutable i; 1 .. nx - 1)
        foreach (immutable j; 1 .. ny - 1) {
            immutable IntT c = i + nx * j,
                           nn = c - nx,
                           ss = c + nx,
                           ww = c + 1,
                           ee = c - 1,
                           nw = nn + 1,
                           ne = nn - 1,
                           sw = ss + 1,
                           se = ss - 1;

            immutable aux = atan2(double(after_Gy[c]),
                                  double(after_Gx[c])) + PI;
            immutable float dir = float((aux % PI) / PI) * 8;

            if (((dir <= 1 || dir > 7) && G[c] > G[ee] &&
                 G[c] > G[ww]) || // 0 deg.
                ((dir > 1 && dir <= 3) && G[c] > G[nw] &&
                 G[c] > G[se]) || // 45 deg.
                ((dir > 3 && dir <= 5) && G[c] > G[nn] &&
                 G[c] > G[ss]) || // 90 deg.
                ((dir > 5 && dir <= 7) && G[c] > G[ne] &&
                 G[c] > G[sw]))   // 135 deg.
                nms[c] = G[c];
            else
                nms[c] = 0;
        }

    // Reuse array used as a stack. nx*ny/2 elements should be enough.
    IntT[] edges = (cast(IntT*)after_Gy.ptr)[0 .. after_Gy.length / 2];
    outp[] = Pixel.init;
    edges[] = 0;

    // Tracing edges with hysteresis. Non-recursive implementation.
    size_t c = 1;
    foreach (immutable j; 1 .. ny - 1) {
        foreach (immutable i; 1 .. nx - 1) {
            if (nms[c] >= tMax && outp[c] == 0) { // Trace edges.
                outp[c] = maxBrightness;
                IntT nedges = 1;
                edges[0] = c;

                do {
                    nedges--;
                    immutable IntT t = edges[nedges];

                    immutable IntT[8] neighbours = [
                        t - nx,      // nn
                        t + nx,      // ss
                        t + 1,       // ww
                        t - 1,       // ee
                        t - nx + 1,  // nw
                        t - nx - 1,  // ne
                        t + nx + 1,  // sw
                        t + nx - 1]; // se

                    foreach (immutable n; neighbours)
                        if (nms[n] >= tMin && outp[n] == 0) {
                            outp[n] = maxBrightness;
                            edges[nedges] = n;
                            nedges++;
                        }
                } while (nedges > 0);
            }
            c++;
        }
    }

    return Image!Pixel.fromData(outp, nx, ny);
}

void main(in string[] args) {
    immutable fileName = (args.length == 2) ? args[1] : "lena.pgm";
    Image!Pixel imIn;
    imIn = imIn.loadPGM(fileName);
    printf("Image size: %d x %d\n", imIn.nx, imIn.ny);
    imIn.cannyEdgeDetection(45, 50, 1.0f).savePGM("lena_canny.pgm");
}


 ! Created by simon on 29/04/2021.

 ! ifort -o cartesian_product cartesian_product.f90 -check all

 module tuple
    implicit none
    private
    public :: tuple_t, operator(*), print

    type tuple_t(n)
        integer, len     :: n
        integer, private :: v(n)
    contains
        procedure, public :: print => print_tuple_t
        generic, public :: assignment(=) => eq_tuple_t
        procedure, public :: eq_tuple_t
    end type tuple_t

    interface print
        module procedure print_tuple_a_t
    end interface print
    interface operator(*)
        module procedure tup_times_tup
    end interface

 contains
    subroutine eq_tuple_t(this, src)
        class(tuple_t(*)), intent(inout) :: this
        integer, intent(in)              :: src(:)
        this%v = src
    end subroutine eq_tuple_t

    pure function tup_times_tup(a, b) result(r)
        type(tuple_t(*)), intent(in)  :: a
        type(tuple_t(*)), intent(in)  :: b
        type(tuple_t(2)), allocatable :: r(:)
        integer :: i, j, k

        allocate(r(a%n*b%n))
        k = 0
        do i=1,a%n
            do j=1,b%n
                k = k + 1
                r(k)%v = [a%v(i),b%v(j)]
            end do
        end do
    end function tup_times_tup

    subroutine print_tuple_t(this)
        class(tuple_t(*)), intent(in) :: this
        integer :: i
        write(*,fmt='(a)',advance='no') '{'
        do i=1,size(this%v)
            write(*,fmt='(i0)',advance='no') this%v(i)
            if (i < size(this%v)) write(*,fmt='(a)',advance='no') ','
        end do
        write(*,fmt='(a)',advance='no') '}'
    end subroutine print_tuple_t

    subroutine print_tuple_a_t(r)
        type(tuple_t(*)), intent(in) :: r(:)
        integer :: i
        write(*,fmt='(a)',advance='no') '{'
        do i=1,size(r)
            call r(i)%print
            if (i < size(r)) write(*,fmt='(a)',advance='no') ','
        end do
        write(*,fmt='(a)') '}'
    end subroutine print_tuple_a_t
 end module tuple

 program cartesian_product
    use tuple

    implicit none
    type(tuple_t(2)) :: a, b
    type(tuple_t(0)) :: z

    a = [1,2]
    b = [3,4]

    call print_product(a, b)
    call print_product(b, a)
    call print_product(z, a)
    call print_product(a, z)

    stop
 contains
    subroutine print_product(s, t)
        type(tuple_t(*)), intent(in) :: s
        type(tuple_t(*)), intent(in) :: t
        call s%print
        write(*,fmt='(a)',advance='no') ' x '
        call t%print
        write(*,fmt='(a)',advance='no') ' = '
        call print(s*t)
    end subroutine print_product
 end program cartesian_product


#lang racket
(require math)

(for ([p1 (in-range 3 62)] #:when (prime? p1))
  (for ([h3 (in-range 2 p1)])
    (define g (+ p1 h3))
    (let next ([d 1])
      (when (< d g)
        (when (and (zero? (modulo (* g (- p1 1)) d))
                   (= (modulo (- (sqr p1)) h3) (modulo d h3)))
          (define p2 (+ 1 (quotient (* g (- p1 1)) d)))
          (when (prime? p2)
            (define p3 (+ 1 (quotient (* p1 p2) h3)))
            (when (and (prime? p3) (= 1 (modulo (* p2 p3) (- p1 1))))
              (displayln (list p1 p2 p3 '=> (* p1 p2 p3))))))
        (next (+ d 1))))))


for (2..67).grep: *.is-prime -> \Prime1 {
    for 1 ^..^ Prime1 -> \h3 {
        my \g = h3 + Prime1;
        for 0 ^..^ h3 + Prime1 -> \d {
            if (h3 + Prime1) * (Prime1 - 1) %% d and -Prime1**2 % h3 == d % h3  {
                my \Prime2 = floor 1 + (Prime1 - 1) * g / d;
                next unless Prime2.is-prime;
                my \Prime3 = floor 1 + Prime1 * Prime2 / h3;
                next unless Prime3.is-prime;
                next unless (Prime2 * Prime3) % (Prime1 - 1) == 1;
                say "{Prime1} × {Prime2} × {Prime3} == {Prime1 * Prime2 * Prime3}";
            }
        }
    }
}


/*REXX program calculates  Carmichael  3─strong  pseudoprimes  (up to and including N). */
numeric digits 18                                /*handle big dig #s (9 is the default).*/
parse arg N .;    if N=='' | N==","  then N=61   /*allow user to specify for the search.*/
tell= N>0;           N= abs(N)                   /*N>0?  Then display Carmichael numbers*/
#= 0                                             /*number of Carmichael numbers so far. */
@.=0;   @.2=1; @.3=1; @.5=1; @.7=1; @.11=1; @.13=1; @.17=1; @.19=1; @.23=1; @.29=1; @.31=1
                                                 /*[↑]  prime number memoization array. */
    do p=3  to N  by 2;  pm= p-1;  bot=0;  top=0 /*step through some (odd) prime numbers*/
    if \isPrime(p)  then iterate;  nps= -p*p     /*is   P   a prime?   No, then skip it.*/
    c.= 0                                        /*the list of Carmichael #'s  (so far).*/
             do h3=2  for  pm-1;   g= h3 + p     /*get Carmichael numbers for this prime*/
             npsH3= ((nps // h3) + h3) // h3     /*define a couple of shortcuts for pgm.*/
             gPM= g * pm                         /*define a couple of shortcuts for pgm.*/
                                                 /* [↓] perform some weeding of D values*/
                 do d=1  for g-1;                   if gPM // d    \== 0      then iterate
                                                    if npsH3       \== d//h3  then iterate
                             q= 1  +  gPM   % d;    if \isPrime(q)            then iterate
                             r= 1  +  p * q % h3;   if q * r // pm \== 1      then iterate
                                                    if \isPrime(r)            then iterate
                 #= # + 1;   c.q= r              /*bump Carmichael counter; add to array*/
                 if bot==0  then bot= q;   bot= min(bot, q);             top= max(top, q)
                 end   /*d*/
             end       /*h3*/
    $=                                           /*build list of some Carmichael numbers*/
    if tell  then  do j=bot  to top  by 2;          if c.j\==0  then $= $  p"∙"j'∙'c.j
                   end           /*j*/

    if $\==''  then say  'Carmichael number: '      strip($)
    end                /*p*/
say
say '──────── '     #     " Carmichael numbers found."
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
isPrime: parse arg x;             if @.x      then return 1       /*is X  a known prime?*/
         if x<37  then return 0;  if x//2==0  then return 0; if x// 3==0     then return 0
         parse var x  ''  -1  _;  if _==5     then return 0; if x// 7==0     then return 0
         if x//11==0  then return 0; if x//13==0  then return 0; if x//17==0 then return 0
         if x//19==0  then return 0; if x//23==0  then return 0; if x//29==0 then return 0
                           do k=29  by 6  until k*k>x;    if x//k       ==0  then return 0
                                                          if x//(k+2)   ==0  then return 0
                           end   /*k*/
         @.x=1;                                                                   return 1


proc Cantor(N, LineSeg, Len);   \Delete middle third of LineSeg
int  N; char LineSeg; int Len, Third, I;
[if N>0 and Len>1 then
    [Third:= Len/3;
    for I:= Third, 2*Third-1 do LineSeg(I):= ^ ;
    Cantor(N-1, LineSeg, Third);
    Cantor(N-1, LineSeg+2*Third, Third);
    ];
];

char LineSeg, N;
[LineSeg:=
"#################################################################################
";
for N:= 0 to 4 do
    [Cantor(N, LineSeg, 81);
    Text(0, LineSeg);
    ];
]


const WIDTH=81, HEIGHT=5;
var lines=HEIGHT.pump(List,List.createLong(WIDTH,"\U2588;").copy);  // full block

fcn cantor(start,len,index){
   (seg:=len/3) or return();
   foreach i,j in ([index..HEIGHT-1], [start + seg .. start + seg*2 - 1]){
      lines[i][j]=" ";
   }
   cantor(start, seg, index + 1);
   cantor(start + seg*2, seg, index + 1);
}(0,WIDTH,1);

lines.pump(Console.println,"concat");


package main

import (
    ed "github.com/Ernyoke/Imger/edgedetection"
    "github.com/Ernyoke/Imger/imgio"
    "log"
)

func main() {
    img, err := imgio.ImreadRGBA("Valve_original_(1).png")
    if err != nil {
        log.Fatal("Could not read image", err)
    }

    cny, err := ed.CannyRGBA(img, 15, 45, 5)
    if err != nil {
        log.Fatal("Could not perform Canny Edge detection")
    }

    err = imgio.Imwrite(cny, "Valve_canny_(1).png")
    if err != nil {
        log.Fatal("Could not write Canny image to disk")
    }
}


NB. 2D convolution, filtering, ...

convolve  =: 4 : 'x apply (($x) partition y)'
partition=: 2 1 3 0 |: {:@[ ]\ 2 1 0 |: {[email protected][ ]\ ]
apply=: [: +/ [: +/ *
max3x3 =: 3 : '(0<1{1{y) * (>./>./y)'
addborder =: (0&,@|:@|.)^:4
normalize =: ]%+/@,
attach =: 3 : 'max3x3 (3 3 partition (addborder y))'
unique =: 3 : 'y*i.$y'
connect =: 3 : 'attach^:_ unique y'

NB. on low memory devices, cropping or resampling of high-resolution images may be required
crop      =: 4 : 0
   'h w h0 w0' =: x
   |: w{. w0}. |: h{. h0}. y
)
resample  =: 4 : '|: (1{-x)(+/%#)\ |: (0{-x)(+/%#)\ y'
NB. on e. g. smartphones, image may need to be expanded for viewing
inflate1 =: 4 : 0
   'h w' =: $y
   r =: ,y
   c =: #r
   rr =: (c$x) # r
   (h,x*w)$rr
)
inflate =: 4 : '|: x inflate1 (|: x inflate1 y)'

NB. Step 1 - gaussian smoothing
step1 =: 3 : 0
   NB. Gaussian kernel (from Wikipedia article)
   <] gaussianKernel =: 5 5$2 4 5 4 2 4 9 12 9 4 5 12 15 12 5 4 9 12 9 4 2 4 5 4 2
   gaussianKernel =: gaussianKernel % 159
   gaussianKernel convolve y
)

NB. Step 2 - gradient
step2 =: 3 : 0
   <] gradientKernel =: 3 3$0 _1 0 0j_1 0 0j1 0 1 0
   gradientKernel convolve y
)

NB. Step 3 - edge detection
step3 =: 3 : 0
   NB. find the octant (eighth of circle) in which the gradient lies
   octant =: 3 : '4|(>.(_0.5+((4%(o. 1))*(12&o. y))))'
   <(i:6)(4 : 'octant (x j. y)')"0/(i:6)

   NB. is this gradient greater than [the projection of] a neighbor?
   greaterThan   =: 4 : ' (9 o.((x|.y)%y))<1'

   NB. is this gradient the greatest of immmediate colinear neighbore?
   greatestOf   =: 4 : '(x greaterThan y) *. ((-x) greaterThan y)'

   NB. relative address of neighbor relevant to grad direction
   krnl0 =. _1  0
   krnl1 =. _1 _1
   krnl2 =.  0 _1
   krnl3 =.  1 _1

   image =. y
   og =. octant image

   NB. mask for maximum gradient colinear with gradient
   ok0 =. (0=og) *. krnl0 greatestOf image
   ok1 =. (1=og) *. krnl1 greatestOf image
   ok2 =. (2=og) *. krnl2 greatestOf image
   ok3 =. (3=og) *. krnl3 greatestOf image
   image *. (ok0 +. ok1 +. ok2 +. ok3)
)

NB. Step 4 - Weak edge suppression
step4 =: 3 : 0
   magnitude =. 10&o. y
   NB. weak, strong threshholds
   NB. TODO: parameter picker algorithm or helper
   threshholds =. 1e14 1e15
   nearbyKernel =. 3 3 $ 4 1 4 # 1 0 1
   weak   =. magnitude > 0{threshholds
   strong =. magnitude > 1{threshholds
   strongs =. addborder (nearbyKernel convolve strong) > 0
   strong +. (weak *. strongs)
)

NB. given the edge points, find the edges
  step5 =: connect

canny =: step5 @ step4 @ step3 @ step2 @ step1


package main

import "fmt"

type pair [2]int

func cart2(a, b []int) []pair {
    p := make([]pair, len(a)*len(b))
    i := 0
    for _, a := range a {
        for _, b := range b {
            p[i] = pair{a, b}
            i++
        }
    }
    return p
}

func main() {
    fmt.Println(cart2([]int{1, 2}, []int{3, 4}))
    fmt.Println(cart2([]int{3, 4}, []int{1, 2}))
    fmt.Println(cart2([]int{1, 2}, nil))
    fmt.Println(cart2(nil, []int{1, 2}))
}


# Project : Carmichael 3 strong pseudoprimes

see "The following are Carmichael munbers for p1 <= 61:" + nl
see "p1     p2      p3     product" + nl

for p = 2 to 61
    carmichael3(p)
next

func carmichael3(p1)
       if isprime(p1) = 0  return ok
       for h3 = 1 to p1 -1
            t1 = (h3 + p1) * (p1 -1)
            t2 = (-p1 * p1) % h3
            if t2 < 0
               t2 = t2 + h3
            ok
            for d = 1 to h3 + p1 -1
                 if t1 % d = 0 and t2 = (d % h3)
                   p2 = 1 + (t1 / d)
                   if isprime(p2) = 0
                      loop
                   ok
                   p3 = 1 + floor((p1 * p2 / h3))
                   if isprime(p3) = 0 or ((p2 * p3) % (p1 -1)) != 1
                      loop
                   ok
                   see "" + p1 + "       " + p2 + "      " + p3 + "    " + p1*p2*p3 + nl
                ok
            next
     next

func isprime(num)
       if (num <= 1) return 0 ok
       if (num % 2 = 0) and num != 2
          return 0
       ok
       for i = 3 to floor(num / 2) -1 step 2
           if (num % i = 0)
              return 0
           ok
       next
       return 1


# Generate Charmichael Numbers

require 'prime'

Prime.each(61) do |p|
  (2...p).each do |h3|
    g = h3 + p
    (1...g).each do |d|
      next if (g*(p-1)) % d != 0 or (-p*p) % h3 != d % h3
      q = 1 + ((p - 1) * g / d)
      next unless q.prime?
      r = 1 + (p * q / h3)
      next unless r.prime? and (q * r) % (p - 1) == 1
      puts "#{p} x #{q} x #{r}"
    end
  end
  puts
end


fn is_prime(n: i64) -> bool {
    if n > 1 {
        (2..((n / 2) + 1)).all(|x| n % x != 0)
    } else {
        false
    }
}

// The modulo operator actually calculates the remainder.
fn modulo(n: i64, m: i64) -> i64 {
    ((n % m) + m) % m
}

fn carmichael(p1: i64) -> Vec<(i64, i64, i64)> {
    let mut results = Vec::new();
    if !is_prime(p1) {
        return results;
    }

    for h3 in 2..p1 {
        for d in 1..(h3 + p1) {
            if (h3 + p1) * (p1 - 1) % d != 0 || modulo(-p1 * p1, h3) != d % h3 {
                continue;
            }

            let p2 = 1 + ((p1 - 1) * (h3 + p1) / d);
            if !is_prime(p2) {
                continue;
            }

            let p3 = 1 + (p1 * p2 / h3);
            if !is_prime(p3) || ((p2 * p3) % (p1 - 1) != 1) {
                continue;
            }

            results.push((p1, p2, p3));
        }
    }

    results
}

fn main() {
    (1..62)
        .filter(|&x| is_prime(x))
        .map(carmichael)
        .filter(|x| !x.is_empty())
        .flat_map(|x| x)
        .inspect(|x| println!("{:?}", x))
        .count(); // Evaluate entire iterator
}


$ include "seed7_05.s7i";

const func boolean: isPrime (in integer: number) is func
  result
    var boolean: prime is FALSE;
  local
    var integer: upTo is 0;
    var integer: testNum is 3;
  begin
    if number = 2 then
      prime := TRUE;
    elsif odd(number) and number > 2 then
      upTo := sqrt(number);
      while number rem testNum <> 0 and testNum <= upTo do
        testNum +:= 2;
      end while;
      prime := testNum > upTo;
    end if;
  end func;

const proc: main is func
  local
    var integer: p1 is 0;
    var integer: h3 is 0;
    var integer: g is 0;
    var integer: d is 0;
    var integer: p2 is 0;
    var integer: p3 is 0;
  begin
    for p1 range 2 to 61 do
      if isPrime(p1) then
        for h3 range 2 to p1 do
          g := h3 + p1;
          for d range 1 to pred(g) do
            if (g * pred(p1)) mod d = 0 and -p1 ** 2 mod h3 = d mod h3 then
              p2 := 1 + pred(p1) * g div d;
              if isPrime(p2) then
                p3 := 1 + p1 * p2 div h3;
                if isPrime(p3) and (p2 * p3) mod pred(p1) = 1 then
                  writeln(p1 <& " * " <& p2 <& " * " <& p3 <& " = " <& p1*p2*p3);
                end if;
              end if;
            end if;
          end for;
        end for;
      end if;
    end for;
  end func;


import java.awt.image.BufferedImage;
import java.util.Arrays;

/**
 *
This software has been released into the public domain.
 * Please read the notes in this source file for additional information.
 *

 *
 *
This class provides a configurable implementation of the Canny edge
 * detection algorithm. This classic algorithm has a number of shortcomings,
 * but remains an effective tool in many scenarios. This class is designed
 * for single threaded use only.

 *
 *
Sample usage:

 *
 *

 * //create the detector
 * CannyEdgeDetector detector = new CannyEdgeDetector();
 * //adjust its parameters as desired
 * detector.setLowThreshold(0.5f);
 * detector.setHighThreshold(1f);
 * //apply it to an image
 * detector.setSourceImage(frame);
 * detector.process();
 * BufferedImage edges = detector.getEdgesImage();
 *

 *
 *
For a more complete understanding of this edge detector's parameters
 * consult an explanation of the algorithm.

 *
 * @author Tom Gibara
 *
 */

public class CannyEdgeDetector {

	// statics

	private final static float GAUSSIAN_CUT_OFF = 0.005f;
	private final static float MAGNITUDE_SCALE = 100F;
	private final static float MAGNITUDE_LIMIT = 1000F;
	private final static int MAGNITUDE_MAX = (int) (MAGNITUDE_SCALE * MAGNITUDE_LIMIT);

	// fields

	private int height;
	private int width;
	private int picsize;
	private int[] data;
	private int[] magnitude;
	private BufferedImage sourceImage;
	private BufferedImage edgesImage;

	private float gaussianKernelRadius;
	private float lowThreshold;
	private float highThreshold;
	private int gaussianKernelWidth;
	private boolean contrastNormalized;

	private float[] xConv;
	private float[] yConv;
	private float[] xGradient;
	private float[] yGradient;

	// constructors

	/**
	 * Constructs a new detector with default parameters.
	 */

	public CannyEdgeDetector() {
		lowThreshold = 2.5f;
		highThreshold = 7.5f;
		gaussianKernelRadius = 2f;
		gaussianKernelWidth = 16;
		contrastNormalized = false;
	}

	// accessors

	/**
	 * The image that provides the luminance data used by this detector to
	 * generate edges.
	 *
	 * @return the source image, or null
	 */

	public BufferedImage getSourceImage() {
		return sourceImage;
	}

	/**
	 * Specifies the image that will provide the luminance data in which edges
	 * will be detected. A source image must be set before the process method
	 * is called.
	 *
	 * @param image a source of luminance data
	 */

	public void setSourceImage(BufferedImage image) {
		sourceImage = image;
	}

	/**
	 * Obtains an image containing the edges detected during the last call to
	 * the process method. The buffered image is an opaque image of type
	 * BufferedImage.TYPE_INT_ARGB in which edge pixels are white and all other
	 * pixels are black.
	 *
	 * @return an image containing the detected edges, or null if the process
	 * method has not yet been called.
	 */

	public BufferedImage getEdgesImage() {
		return edgesImage;
	}

	/**
	 * Sets the edges image. Calling this method will not change the operation
	 * of the edge detector in any way. It is intended to provide a means by
	 * which the memory referenced by the detector object may be reduced.
	 *
	 * @param edgesImage expected (though not required) to be null
	 */

	public void setEdgesImage(BufferedImage edgesImage) {
		this.edgesImage = edgesImage;
	}

	/**
	 * The low threshold for hysteresis. The default value is 2.5.
	 *
	 * @return the low hysteresis threshold
	 */

	public float getLowThreshold() {
		return lowThreshold;
	}

	/**
	 * Sets the low threshold for hysteresis. Suitable values for this parameter
	 * must be determined experimentally for each application. It is nonsensical
	 * (though not prohibited) for this value to exceed the high threshold value.
	 *
	 * @param threshold a low hysteresis threshold
	 */

	public void setLowThreshold(float threshold) {
		if (threshold < 0) throw new IllegalArgumentException();
		lowThreshold = threshold;
	}

	/**
	 * The high threshold for hysteresis. The default value is 7.5.
	 *
	 * @return the high hysteresis threshold
	 */

	public float getHighThreshold() {
		return highThreshold;
	}

	/**
	 * Sets the high threshold for hysteresis. Suitable values for this
	 * parameter must be determined experimentally for each application. It is
	 * nonsensical (though not prohibited) for this value to be less than the
	 * low threshold value.
	 *
	 * @param threshold a high hysteresis threshold
	 */

	public void setHighThreshold(float threshold) {
		if (threshold < 0) throw new IllegalArgumentException();
		highThreshold = threshold;
	}

	/**
	 * The number of pixels across which the Gaussian kernel is applied.
	 * The default value is 16.
	 *
	 * @return the radius of the convolution operation in pixels
	 */

	public int getGaussianKernelWidth() {
		return gaussianKernelWidth;
	}

	/**
	 * The number of pixels across which the Gaussian kernel is applied.
	 * This implementation will reduce the radius if the contribution of pixel
	 * values is deemed negligable, so this is actually a maximum radius.
	 *
	 * @param gaussianKernelWidth a radius for the convolution operation in
	 * pixels, at least 2.
	 */

	public void setGaussianKernelWidth(int gaussianKernelWidth) {
		if (gaussianKernelWidth < 2) throw new IllegalArgumentException();
		this.gaussianKernelWidth = gaussianKernelWidth;
	}

	/**
	 * The radius of the Gaussian convolution kernel used to smooth the source
	 * image prior to gradient calculation. The default value is 16.
	 *
	 * @return the Gaussian kernel radius in pixels
	 */

	public float getGaussianKernelRadius() {
		return gaussianKernelRadius;
	}

	/**
	 * Sets the radius of the Gaussian convolution kernel used to smooth the
	 * source image prior to gradient calculation.
	 *
	 * @return a Gaussian kernel radius in pixels, must exceed 0.1f.
	 */

	public void setGaussianKernelRadius(float gaussianKernelRadius) {
		if (gaussianKernelRadius < 0.1f) throw new IllegalArgumentException();
		this.gaussianKernelRadius = gaussianKernelRadius;
	}

	/**
	 * Whether the luminance data extracted from the source image is normalized
	 * by linearizing its histogram prior to edge extraction. The default value
	 * is false.
	 *
	 * @return whether the contrast is normalized
	 */

	public boolean isContrastNormalized() {
		return contrastNormalized;
	}

	/**
	 * Sets whether the contrast is normalized
	 * @param contrastNormalized true if the contrast should be normalized,
	 * false otherwise
	 */

	public void setContrastNormalized(boolean contrastNormalized) {
		this.contrastNormalized = contrastNormalized;
	}

	// methods

	public void process() {
		width = sourceImage.getWidth();
		height = sourceImage.getHeight();
		picsize = width * height;
		initArrays();
		readLuminance();
		if (contrastNormalized) normalizeContrast();
		computeGradients(gaussianKernelRadius, gaussianKernelWidth);
		int low = Math.round(lowThreshold * MAGNITUDE_SCALE);
		int high = Math.round( highThreshold * MAGNITUDE_SCALE);
		performHysteresis(low, high);
		thresholdEdges();
		writeEdges(data);
	}

	// private utility methods

	private void initArrays() {
		if (data == null || picsize != data.length) {
			data = new int[picsize];
			magnitude = new int[picsize];

			xConv = new float[picsize];
			yConv = new float[picsize];
			xGradient = new float[picsize];
			yGradient = new float[picsize];
		}
	}

	//NOTE: The elements of the method below (specifically the technique for
	//non-maximal suppression and the technique for gradient computation)
	//are derived from an implementation posted in the following forum (with the
	//clear intent of others using the code):
	//  http://forum.java.sun.com/thread.jspa?threadID=546211&start=45&tstart=0
	//My code effectively mimics the algorithm exhibited above.
	//Since I don't know the providence of the code that was posted it is a
	//possibility (though I think a very remote one) that this code violates
	//someone's intellectual property rights. If this concerns you feel free to
	//contact me for an alternative, though less efficient, implementation.

	private void computeGradients(float kernelRadius, int kernelWidth) {

		//generate the gaussian convolution masks
		float kernel[] = new float[kernelWidth];
		float diffKernel[] = new float[kernelWidth];
		int kwidth;
		for (kwidth = 0; kwidth < kernelWidth; kwidth++) {
			float g1 = gaussian(kwidth, kernelRadius);
			if (g1 <= GAUSSIAN_CUT_OFF && kwidth >= 2) break;
			float g2 = gaussian(kwidth - 0.5f, kernelRadius);
			float g3 = gaussian(kwidth + 0.5f, kernelRadius);
			kernel[kwidth] = (g1 + g2 + g3) / 3f / (2f * (float) Math.PI * kernelRadius * kernelRadius);
			diffKernel[kwidth] = g3 - g2;
		}

		int initX = kwidth - 1;
		int maxX = width - (kwidth - 1);
		int initY = width * (kwidth - 1);
		int maxY = width * (height - (kwidth - 1));

		//perform convolution in x and y directions
		for (int x = initX; x < maxX; x++) {
			for (int y = initY; y < maxY; y += width) {
				int index = x + y;
				float sumX = data[index] * kernel[0];
				float sumY = sumX;
				int xOffset = 1;
				int yOffset = width;
				for(; xOffset < kwidth ;) {
					sumY += kernel[xOffset] * (data[index - yOffset] + data[index + yOffset]);
					sumX += kernel[xOffset] * (data[index - xOffset] + data[index + xOffset]);
					yOffset += width;
					xOffset++;
				}

				yConv[index] = sumY;
				xConv[index] = sumX;
			}

		}

		for (int x = initX; x < maxX; x++) {
			for (int y = initY; y < maxY; y += width) {
				float sum = 0f;
				int index = x + y;
				for (int i = 1; i < kwidth; i++)
					sum += diffKernel[i] * (yConv[index - i] - yConv[index + i]);

				xGradient[index] = sum;
			}

		}

		for (int x = kwidth; x < width - kwidth; x++) {
			for (int y = initY; y < maxY; y += width) {
				float sum = 0.0f;
				int index = x + y;
				int yOffset = width;
				for (int i = 1; i < kwidth; i++) {
					sum += diffKernel[i] * (xConv[index - yOffset] - xConv[index + yOffset]);
					yOffset += width;
				}

				yGradient[index] = sum;
			}

		}

		initX = kwidth;
		maxX = width - kwidth;
		initY = width * kwidth;
		maxY = width * (height - kwidth);
		for (int x = initX; x < maxX; x++) {
			for (int y = initY; y < maxY; y += width) {
				int index = x + y;
				int indexN = index - width;
				int indexS = index + width;
				int indexW = index - 1;
				int indexE = index + 1;
				int indexNW = indexN - 1;
				int indexNE = indexN + 1;
				int indexSW = indexS - 1;
				int indexSE = indexS + 1;

				float xGrad = xGradient[index];
				float yGrad = yGradient[index];
				float gradMag = hypot(xGrad, yGrad);

				//perform non-maximal supression
				float nMag = hypot(xGradient[indexN], yGradient[indexN]);
				float sMag = hypot(xGradient[indexS], yGradient[indexS]);
				float wMag = hypot(xGradient[indexW], yGradient[indexW]);
				float eMag = hypot(xGradient[indexE], yGradient[indexE]);
				float neMag = hypot(xGradient[indexNE], yGradient[indexNE]);
				float seMag = hypot(xGradient[indexSE], yGradient[indexSE]);
				float swMag = hypot(xGradient[indexSW], yGradient[indexSW]);
				float nwMag = hypot(xGradient[indexNW], yGradient[indexNW]);
				float tmp;
				/*
				 * An explanation of what's happening here, for those who want
				 * to understand the source: This performs the "non-maximal
				 * supression" phase of the Canny edge detection in which we
				 * need to compare the gradient magnitude to that in the
				 * direction of the gradient; only if the value is a local
				 * maximum do we consider the point as an edge candidate.
				 *
				 * We need to break the comparison into a number of different
				 * cases depending on the gradient direction so that the
				 * appropriate values can be used. To avoid computing the
				 * gradient direction, we use two simple comparisons: first we
				 * check that the partial derivatives have the same sign (1)
				 * and then we check which is larger (2). As a consequence, we
				 * have reduced the problem to one of four identical cases that
				 * each test the central gradient magnitude against the values at
				 * two points with 'identical support'; what this means is that
				 * the geometry required to accurately interpolate the magnitude
				 * of gradient function at those points has an identical
				 * geometry (upto right-angled-rotation/reflection).
				 *
				 * When comparing the central gradient to the two interpolated
				 * values, we avoid performing any divisions by multiplying both
				 * sides of each inequality by the greater of the two partial
				 * derivatives. The common comparand is stored in a temporary
				 * variable (3) and reused in the mirror case (4).
				 *
				 */
				if (xGrad * yGrad <= (float) 0 /*(1)*/
					? Math.abs(xGrad) >= Math.abs(yGrad) /*(2)*/
						? (tmp = Math.abs(xGrad * gradMag)) >= Math.abs(yGrad * neMag - (xGrad + yGrad) * eMag) /*(3)*/
							&& tmp > Math.abs(yGrad * swMag - (xGrad + yGrad) * wMag) /*(4)*/
						: (tmp = Math.abs(yGrad * gradMag)) >= Math.abs(xGrad * neMag - (yGrad + xGrad) * nMag) /*(3)*/
							&& tmp > Math.abs(xGrad * swMag - (yGrad + xGrad) * sMag) /*(4)*/
					: Math.abs(xGrad) >= Math.abs(yGrad) /*(2)*/
						? (tmp = Math.abs(xGrad * gradMag)) >= Math.abs(yGrad * seMag + (xGrad - yGrad) * eMag) /*(3)*/
							&& tmp > Math.abs(yGrad * nwMag + (xGrad - yGrad) * wMag) /*(4)*/
						: (tmp = Math.abs(yGrad * gradMag)) >= Math.abs(xGrad * seMag + (yGrad - xGrad) * sMag) /*(3)*/
							&& tmp > Math.abs(xGrad * nwMag + (yGrad - xGrad) * nMag) /*(4)*/
					) {
					magnitude[index] = gradMag >= MAGNITUDE_LIMIT ? MAGNITUDE_MAX : (int) (MAGNITUDE_SCALE * gradMag);
					//NOTE: The orientation of the edge is not employed by this
					//implementation. It is a simple matter to compute it at
					//this point as: Math.atan2(yGrad, xGrad);
				} else {
					magnitude[index] = 0;
				}
			}
		}
	}

	//NOTE: It is quite feasible to replace the implementation of this method
	//with one which only loosely approximates the hypot function. I've tested
	//simple approximations such as Math.abs(x) + Math.abs(y) and they work fine.
	private float hypot(float x, float y) {
		return (float) Math.hypot(x, y);
	}

	private float gaussian(float x, float sigma) {
		return (float) Math.exp(-(x * x) / (2f * sigma * sigma));
	}

	private void performHysteresis(int low, int high) {
		//NOTE: this implementation reuses the data array to store both
		//luminance data from the image, and edge intensity from the processing.
		//This is done for memory efficiency, other implementations may wish
		//to separate these functions.
		Arrays.fill(data, 0);

		int offset = 0;
		for (int y = 0; y < height; y++) {
			for (int x = 0; x < width; x++) {
				if (data[offset] == 0 && magnitude[offset] >= high) {
					follow(x, y, offset, low);
				}
				offset++;
			}
		}
 	}

	private void follow(int x1, int y1, int i1, int threshold) {
		int x0 = x1 == 0 ? x1 : x1 - 1;
		int x2 = x1 == width - 1 ? x1 : x1 + 1;
		int y0 = y1 == 0 ? y1 : y1 - 1;
		int y2 = y1 == height -1 ? y1 : y1 + 1;

		data[i1] = magnitude[i1];
		for (int x = x0; x <= x2; x++) {
			for (int y = y0; y <= y2; y++) {
				int i2 = x + y * width;
				if ((y != y1 || x != x1)
					&& data[i2] == 0
					&& magnitude[i2] >= threshold) {
					follow(x, y, i2, threshold);
					return;
				}
			}
		}
	}

	private void thresholdEdges() {
		for (int i = 0; i < picsize; i++) {
			data[i] = data[i] > 0 ? -1 : 0xff000000;
		}
	}

	private int luminance(float r, float g, float b) {
		return Math.round(0.299f * r + 0.587f * g + 0.114f * b);
	}

	private void readLuminance() {
		int type = sourceImage.getType();
		if (type == BufferedImage.TYPE_INT_RGB || type == BufferedImage.TYPE_INT_ARGB) {
			int[] pixels = (int[]) sourceImage.getData().getDataElements(0, 0, width, height, null);
			for (int i = 0; i < picsize; i++) {
				int p = pixels[i];
				int r = (p & 0xff0000) >> 16;
				int g = (p & 0xff00) >> 8;
				int b = p & 0xff;
				data[i] = luminance(r, g, b);
			}
		} else if (type == BufferedImage.TYPE_BYTE_GRAY) {
			byte[] pixels = (byte[]) sourceImage.getData().getDataElements(0, 0, width, height, null);
			for (int i = 0; i < picsize; i++) {
				data[i] = (pixels[i] & 0xff);
			}
		} else if (type == BufferedImage.TYPE_USHORT_GRAY) {
			short[] pixels = (short[]) sourceImage.getData().getDataElements(0, 0, width, height, null);
			for (int i = 0; i < picsize; i++) {
				data[i] = (pixels[i] & 0xffff) / 256;
			}
		} else if (type == BufferedImage.TYPE_3BYTE_BGR) {
            byte[] pixels = (byte[]) sourceImage.getData().getDataElements(0, 0, width, height, null);
            int offset = 0;
            for (int i = 0; i < picsize; i++) {
                int b = pixels[offset++] & 0xff;
                int g = pixels[offset++] & 0xff;
                int r = pixels[offset++] & 0xff;
                data[i] = luminance(r, g, b);
            }
        } else {
			throw new IllegalArgumentException("Unsupported image type: " + type);
		}
	}

	private void normalizeContrast() {
		int[] histogram = new int[256];
		for (int i = 0; i < data.length; i++) {
			histogram[data[i]]++;
		}
		int[] remap = new int[256];
		int sum = 0;
		int j = 0;
		for (int i = 0; i < histogram.length; i++) {
			sum += histogram[i];
			int target = sum*255/picsize;
			for (int k = j+1; k <=target; k++) {
				remap[k] = i;
			}
			j = target;
		}

		for (int i = 0; i < data.length; i++) {
			data[i] = remap[data[i]];
		}
	}

	private void writeEdges(int pixels[]) {
		//NOTE: There is currently no mechanism for obtaining the edge data
		//in any other format other than an INT_ARGB type BufferedImage.
		//This may be easily remedied by providing alternative accessors.
		if (edgesImage == null) {
			edgesImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
		}
		edgesImage.getWritableTile(0, 0).setDataElements(0, 0, width, height, pixels);
	}

}


class CartesianCategory {
    static Iterable multiply(Iterable a, Iterable b) {
        assert [a,b].every { it != null }
        def (m,n) = [a.size(),b.size()]
        (0..<(m*n)).inject([]) { prod, i -> prod << [a[i.intdiv(n)], b[i%n]].flatten() }
    }
}


cartProd :: [a] -> [b] -> [(a, b)]
cartProd xs ys =
  [ (x, y)
  | x <- xs
  , y <- ys ]


func forprimes(a, b, callback) {
    for (a = (a-1 -> next_prime); a <= b; a.next_prime!) {
        callback(a)
    }
}

forprimes(3, 61, func(p) {
   for h3 in (2 ..^ p) {
      var ph3 = (p + h3)
      for d in (1 ..^ ph3) {
         ((-p * p) % h3) != (d % h3) && next
         ((p-1) * ph3) % d && next
         var q = 1+((p-1) * ph3 / d)
         q.is_prime || next
         var r = 1+((p*q - 1)/h3)
         r.is_prime || next
         (q*r) % (p-1) == 1 || next
         printf("%2d x %5d x %5d = %s\n",p,q,r, p*q*r)
      }
   }
})


import Foundation

extension BinaryInteger {
  @inlinable
  public var isPrime: Bool {
    if self == 0 || self == 1 {
      return false
    } else if self == 2 {
      return true
    }

    let max = Self(ceil((Double(self).squareRoot())))

    for i in stride(from: 2, through: max, by: 1) {
      if self % i == 0 {
        return false
      }
    }

    return true
  }
}

@inlinable
public func carmichael<T: BinaryInteger & SignedNumeric>(p1: T) -> [(T, T, T)] {
  func mod(_ n: T, _ m: T) -> T { (n % m + m) % m }

  var res = [(T, T, T)]()

  guard p1.isPrime else {
    return res
  }

  for h3 in stride(from: 2, to: p1, by: 1) {
    for d in stride(from: 1, to: h3 + p1, by: 1) {
      if (h3 + p1) * (p1 - 1) % d != 0 || mod(-p1 * p1, h3) != d % h3 {
        continue
      }

      let p2 = 1 + (p1 - 1) * (h3 + p1) / d

      guard p2.isPrime else {
        continue
      }

      let p3 = 1 + p1 * p2 / h3

      guard p3.isPrime && (p2 * p3) % (p1 - 1) == 1 else {
        continue
      }

      res.append((p1, p2, p3))
    }
  }

  return res
}

let res =
  (1..<62)
    .lazy
    .filter({ $0.isPrime })
    .map(carmichael)
    .filter({ !$0.isEmpty })
    .flatMap({ $0 })

for c in res {
  print(c)
}


proc carmichael {limit {rounds 10}} {
    set carmichaels {}
    for {set p1 2} {$p1 <= $limit} {incr p1} {
	if {![miller_rabin $p1 $rounds]} continue
	for {set h3 2} {$h3 < $p1} {incr h3} {
	    set g [expr {$h3 + $p1}]
	    for {set d 1} {$d < $h3+$p1} {incr d} {
		if {(($h3+$p1)*($p1-1))%$d != 0} continue
		if {(-($p1**2))%$h3 != $d%$h3} continue

		set p2 [expr {1 + ($p1-1)*$g/$d}]
		if {![miller_rabin $p2 $rounds]} continue

		set p3 [expr {1 + $p1*$p2/$h3}]
		if {![miller_rabin $p3 $rounds]} continue

		if {($p2*$p3)%($p1-1) != 1} continue
		lappend carmichaels $p1 $p2 $p3 [expr {$p1*$p2*$p3}]
	    }
	}
    }
    return $carmichaels
}


using Images

canny_edges = canny(img, sigma = 1.4, upperThreshold = 0.80, lowerThreshold = 0.20)


import lenientops
import math
import nimPNG

const MaxBrightness = 255

type Pixel = int16    # Used instead of byte to be able to store negative values.

#---------------------------------------------------------------------------------------------------

func convolution*[normalize: static bool](input: seq[Pixel]; output: var seq[Pixel];
                                          kernel: seq[float]; nx, ny, kn: int) =
  ## Do a convolution.
  ## If normalize is true, map pixels to range 0...maxBrightness.

  doAssert kernel.len == kn * kn
  doAssert (kn and 1) == 1
  doAssert nx > kn and ny > kn
  doAssert input.len == output.len

  let khalf = kn div 2

  when normalize:

    var pMin = float.high
    var pMax = -float.high

    for m in khalf..<(nx - khalf):
      for n in khalf..<(ny - khalf):
        var pixel = 0.0
        var c = 0
        for j in -khalf..khalf:
          for i in -khalf..khalf:
            pixel += input[(n - j) * nx + m - i] * kernel[c]
            inc c
        if pixel < pMin:
          pMin = pixel
        if pixel > pMax:
          pMax = pixel

  for m in khalf..<(nx - khalf):
    for n in khalf..<(ny - khalf):
      var pixel = 0.0
      var c = 0
      for j in -khalf..khalf:
        for i in -khalf..khalf:
          pixel += input[(n - j) * nx + m - i] * kernel[c]
          inc c
      when normalize:
        pixel = MaxBrightness * (pixel - pMin) / (pMax - pMin)
      output[n * nx + m] = Pixel(pixel)

#---------------------------------------------------------------------------------------------------

func gaussianFilter(input: seq[Pixel]; output: var seq[Pixel]; nx, ny: int; sigma: float) =
  ## Apply a gaussian filter.

  doAssert input.len == output.len

  let n = 2 * (2 * sigma).toInt + 3
  let mean = floor(n / 2)
  var kernel = newSeq[float](n * n)

  var c = 0
  for i in 0..<n:
    for j in 0..<n:
      kernel[c] = exp(-0.5 * (((i - mean) / sigma) ^ 2 + ((j - mean) / sigma) ^ 2)) /
                  (2 * PI * sigma * sigma)
      inc c

  convolution[true](input, output, kernel, nx, ny, n)

#---------------------------------------------------------------------------------------------------

proc cannyEdgeDetection(input: seq[Pixel];
                        nx, ny: int;
                        tmin, tmax: int;
                        sigma: float): seq[byte] =

  ## Detect edges.
  var output = newSeq[Pixel](input.len)
  gaussianFilter(input, output, nx, ny, sigma)

  const Gx = @[float -1, 0, 1,
                     -2, 0, 2,
                     -1, 0, 1]
  var afterGx = newSeq[Pixel](input.len)
  convolution[false](input, afterGx, Gx, nx, ny, 3)

  const Gy = @[float  1,  2,  1,
                      0,  0,  0,
                     -1, -2, -1]
  var afterGy = newSeq[Pixel](input.len)
  convolution[false](input, afterGy, Gy, nx, ny, 3)

  var g = newSeq[Pixel](input.len)
  for i in 1..(nx - 2):
    for j in 1..(ny - 2):
      let c = i + nx * j
      g[c] = hypot(afterGx[c].toFloat, afterGy[c].toFloat).Pixel

  # Non-maximum suppression: straightforward implementation.
  var nms = newSeq[Pixel](input.len)
  for i in 1..(nx - 2):
    for j in 1..(ny - 2):
      let
        c = i + nx * j
        nn = c - nx
        ss = c + nx
        ww = c + 1
        ee = c - 1
        nw = nn + 1
        ne = nn - 1
        sw = ss + 1
        se = ss - 1
      let aux = arctan2(afterGy[c].toFloat, afterGx[c].toFloat) + PI
      let dir = aux mod PI / PI * 8
      if (((dir <= 1 or dir > 7) and g[c] > g[ee] and g[c] > g[ww]) or      # O°.
          ((dir > 1 and dir <= 3) and g[c] > g[nw] and g[c] > g[se]) or     # 45°.
          ((dir > 3 and dir <= 5) and g[c] > g[nn] and g[c] > g[ss]) or     # 90°.
          ((dir > 5 and dir <= 7) and g[c] > g[ne] and g[c] > g[sw])):      # 135°.
        nms[c] = g[c]
      else:
        nms[c] = 0

  # Tracing edges with hysteresis. Non-recursive implementation.
  var edges = newSeq[int](input.len div 2)
  for item in output.mitems: item = 0
  var c = 0
  for j in 1..(ny - 2):
    for i in 1..(nx - 2):
      inc c

      if nms[c] >= tMax and output[c] == 0:
        # Trace edges.
        output[c] = MaxBrightness
        var nedges = 1
        edges[0] = c

        while nedges > 0:
          dec nedges
          let t = edges[nedges]
          let neighbors = [t - nx,      # nn.
                           t + nx,      # ss.
                           t + 1,       # ww.
                           t - 1,       # ee.
                           t - nx + 1,  # nw.
                           t - nx - 1,  # ne.
                           t + nx + 1,  # sw.
                           t + nx - 1]  # se.

          for n in neighbors:
            if nms[n] >= tMin and output[n] == 0:
              output[n] = MaxBrightness
              edges[nedges] = n
              inc nedges

  # Store the result as a sequence of bytes.
  result = newSeqOfCap[byte](output.len)
  for val in output:
    result.add(byte(val))

#———————————————————————————————————————————————————————————————————————————————————————————————————

when isMainModule:

  const
    Input = "Valve.png"
    Output = "Valve_edges.png"

  let pngImage = loadPNG24(seq[byte], Input).get()

  # Convert to grayscale and store luminances as 16 bits signed integers.
  var pixels = newSeq[Pixel](pngImage.width * pngImage.height)
  for i in 0..pixels.high:
    pixels[i] = Pixel(0.2126 * pngImage.data[3 * i] +
                      0.7152 * pngImage.data[3 * i + 1] +
                      0.0722 * pngImage.data[3 * i + 2] + 0.5)

  # Find edges.
  let data = cannyEdgeDetection(pixels, pngImage.width, pngImage.height, 45, 50, 1.0)

  # Save result as a PNG image.
  let status = savePNG(Output, data, LCT_GREY, 8, pngImage.width, pngImage.height)
  if status.isOk:
    echo "File ", Input, " processed. Result is available in file ", Output
  else:
    echo "Error: ", status.error


# 20220120 Perl programming solution

use strict;
use warnings;

use lib '/home/hkdtam/lib';
use Image::EdgeDetect;

my $detector = Image::EdgeDetect->new();
$detector->process('./input.jpg', './output.jpg') or die; # na.cx/i/pHYdUrV.jpg


   { 1776 1789 ; 7 12 ; 4 14 23 ; 0 1   NB. result is 4 dimensional array with shape 2 2 3 2
┌────────────┬────────────┐
│1776 7 4 0  │1776 7 4 1  │
├────────────┼────────────┤
│1776 7 14 0 │1776 7 14 1 │
├────────────┼────────────┤
│1776 7 23 0 │1776 7 23 1 │
└────────────┴────────────┘

┌────────────┬────────────┐
│1776 12 4 0 │1776 12 4 1 │
├────────────┼────────────┤
│1776 12 14 0│1776 12 14 1│
├────────────┼────────────┤
│1776 12 23 0│1776 12 23 1│
└────────────┴────────────┘

┌────────────┬────────────┐
│1789 7 4 0  │1789 7 4 1  │
├────────────┼────────────┤
│1789 7 14 0 │1789 7 14 1 │
├────────────┼────────────┤
│1789 7 23 0 │1789 7 23 1 │
└────────────┴────────────┘

┌────────────┬────────────┐
│1789 12 4 0 │1789 12 4 1 │
├────────────┼────────────┤
│1789 12 14 0│1789 12 14 1│
├────────────┼────────────┤
│1789 12 23 0│1789 12 23 1│
└────────────┴────────────┘
   { 1 2 3 ; 30 ; 50 100    NB. result is a 2-dimensional array with shape 2 3
┌───────┬────────┐
│1 30 50│1 30 100│
├───────┼────────┤
│2 30 50│2 30 100│
├───────┼────────┤
│3 30 50│3 30 100│
└───────┴────────┘
   { 1 2 3 ; '' ; 50 100    NB. result is an empty 3-dimensional array with shape 3 0 2


long mod(long n, long m) {
  return ((n % m) + m) % m;
}

bool is_prime(long n) {
  if (n == 2 || n == 3)
    return true;
  else if (n < 2 || n % 2 == 0 || n % 3 == 0)
    return false;
  for (long div = 5, inc = 2; div * div <= n;
    div += inc, inc = 6 - inc)
    if (n % div == 0)
      return false;
  return true;
}

void main() {
  for (long p = 2; p <= 63; p++) {
    if (!is_prime(p)) continue;
    for (long h3 = 2; h3 <= p; h3++) {
      var g = h3 + p;
      for (long d = 1; d <= g; d++) {
        if ((g * (p - 1)) % d != 0 || mod(-p * p, h3) != d % h3)
          continue;
        var q = 1 + (p - 1) * g / d;
        if (!is_prime(q)) continue;
        var r = 1 + (p * q / h3);
        if (!is_prime(r) || (q * r) % (p - 1) != 1) continue;
        stdout.printf("%5ld × %5ld × %5ld = %10ld\n", p, q, r, p * q * r);
      }
    }
  }
}


import "/fmt" for Fmt
import "/math" for Int

var mod = Fn.new { |n, m| ((n%m) + m) % m }

var carmichael = Fn.new { |p1|
    for (h3 in 2...p1) {
        for (d in 1...h3 + p1) {
            if ((h3 + p1) * (p1 - 1) % d == 0 && mod.call(-p1 * p1, h3) == d % h3) {
                var p2 = 1 + ((p1 - 1) * (h3 + p1) / d).floor
                if (Int.isPrime(p2)) {
                    var p3 = 1 + (p1 * p2 / h3).floor
                    if (Int.isPrime(p3)) {
                        if (p2 * p3 % (p1 - 1) == 1) {
                            var c = p1 * p2 * p3
                            Fmt.print("$2d   $4d   $5d  $10d", p1, p2, p3, c)
                        }
                    }
                }
            }
        }
    }
}

System.print("The following are Carmichael munbers for p1 <= 61:\n")
System.print("p1     p2      p3     product")
System.print("==     ==      ==     =======")
for (p1 in 2..61) {
    if (Int.isPrime(p1)) carmichael.call(p1)
}


// Static
MyClass.method(someParameter);

// Instance
myInstance.method(someParameter);


// input: r,g,b in range 0..255
function RGBtoHSV($r, $g, $b) {
	$r = $r/255.; // convert to range 0..1
	$g = $g/255.;
	$b = $b/255.;
	$cols = array("r" => $r, "g" => $g, "b" => $b);
	asort($cols, SORT_NUMERIC);
	$min = key(array_slice($cols, 1)); // "r", "g" or "b"
	$max = key(array_slice($cols, -1)); // "r", "g" or "b"

	// hue
	if($cols[$min] == $cols[$max]) {
		$h = 0;
	} else {
		if($max == "r") {
			$h = 60. * ( 0 + ( ($cols["g"]-$cols["b"]) / ($cols[$max]-$cols[$min]) ) );
		} elseif ($max == "g") {
			$h = 60. * ( 2 + ( ($cols["b"]-$cols["r"]) / ($cols[$max]-$cols[$min]) ) );
		} elseif ($max == "b") {
			$h = 60. * ( 4 + ( ($cols["r"]-$cols["g"]) / ($cols[$max]-$cols[$min]) ) );
		}
		if($h < 0) {
			$h += 360;
		}
	}

	// saturation
	if($cols[$max] == 0) {
		$s = 0;
	} else {
		$s = ( ($cols[$max]-$cols[$min])/$cols[$max] );
		$s = $s * 255;
	}

	// lightness
	$v = $cols[$max];
	$v = $v * 255;

	return(array($h, $s, $v));
}

$filename = "image.png";
$dimensions = getimagesize($filename);
$w = $dimensions[0]; // width
$h = $dimensions[1]; // height

$im = imagecreatefrompng($filename);

for($hi=0; $hi < $h; $hi++) {

	for($wi=0; $wi < $w; $wi++) {
		$rgb = imagecolorat($im, $wi, $hi);

		$r = ($rgb >> 16) & 0xFF;
		$g = ($rgb >> 8) & 0xFF;
		$b = $rgb & 0xFF;
		$hsv = RGBtoHSV($r, $g, $b);

		// compare pixel below with current pixel
		$brgb = imagecolorat($im, $wi, $hi+1);
		$br = ($brgb >> 16) & 0xFF;
		$bg = ($brgb >> 8) & 0xFF;
		$bb = $brgb & 0xFF;
		$bhsv = RGBtoHSV($br, $bg, $bb);

		// if difference in hue > 20, edge is detected
		if($hsv[2]-$bhsv[2] > 20) {
                    imagesetpixel($im, $wi, $hi, imagecolorallocate($im, 255, 0, 0));
		}
                else {
		    imagesetpixel($im, $wi, $hi, imagecolorallocate($im, 0, 0, 0));
		}

        }

}

header('Content-Type: image/jpeg');
imagepng($im);
imagedestroy($im);


import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Optional.of;
import static java.util.stream.Collectors.toList;

import java.util.List;

public class CartesianProduct {

    public List<?> product(List<?>... a) {
        if (a.length >= 2) {
            List<?> product = a[0];
            for (int i = 1; i < a.length; i++) {
                product = product(product, a[i]);
            }
            return product;
        }

        return emptyList();
    }

    private <A, B> List<?> product(List<A> a, List<B> b) {
        return of(a.stream()
                .map(e1 -> of(b.stream().map(e2 -> asList(e1, e2)).collect(toList())).orElse(emptyList()))
                .flatMap(List::stream)
                .collect(toList())).orElse(emptyList());
    }
}


10 FOR p=2 TO 61
20 LET n=p: GO SUB 1000
30 IF NOT n THEN GO TO 200
40 FOR h=1 TO p-1
50 FOR d=1 TO h-1+p
60 IF NOT (FN m((h+p)*(p-1),d)=0 AND FN w(-p*p,h)=FN m(d,h)) THEN GO TO 180
70 LET q=INT (1+((p-1)*(h+p)/d))
80 LET n=q: GO SUB 1000
90 IF NOT n THEN GO TO 180
100 LET r=INT (1+(p*q/h))
110 LET n=r: GO SUB 1000
120 IF (NOT n) OR ((FN m((q*r),(p-1))<>1)) THEN GO TO 180
130 PRINT p;" ";q;" ";r
180 NEXT d
190 NEXT h
200 NEXT p
210 STOP
1000 IF n<4 THEN LET n=(n>1): RETURN
1010 IF (NOT FN m(n,2)) OR (NOT FN m(n,3)) THEN LET n=0: RETURN
1020 LET i=5
1030 IF NOT ((i*i)<=n) THEN LET n=1: RETURN
1040 IF (NOT FN m(n,i)) OR NOT FN m(n,(i+2)) THEN LET n=0: RETURN
1050 LET i=i+6
1060 GO TO 1030
2000 DEF FN m(a,b)=a-(INT (a/b)*b): REM Mod function
2010 DEF FN w(a,b)=FN m(FN m(a,b)+b,b): REM Mod function modified


   package My_Class is
      type Object is tagged private;
      procedure Primitive(Self: Object); -- primitive subprogram
      procedure Dynamic(Self: Object'Class);
      procedure Static;
   private
      type Object is tagged null record;
   end My_Class;


class myClass
{
	Method(someParameter){
		MsgBox % SomeParameter
	}
}

myClass.method("hi")
myInstance := new myClass
myInstance.Method("bye")


( ( myClass
  =   (name=aClass)
      ( Method
      = .out$(str$("Output from " !(its.name) ": " !arg))
      )
      (new=.!arg:?(its.name))
  )
& (myClass.Method)$"Example of calling a 'class' method"
& new$(myClass,object1):?MyObject
& (MyObject..Method)$"Example of calling an instance method"
& !MyObject:?Alias
& (Alias..Method)$"Example of calling an instance method from an alias"
);


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

typedef struct{
        int x;
        int (*funcPtr)(int);
}functionPair;

int factorial(int num){
        if(num==0||num==1)
                return 1;
        else
                return num*factorial(num-1);
}

int main(int argc,char** argv)
{
        functionPair response;

        if(argc!=2)
                return printf("Usage : %s <non negative integer>",argv[0]);
        else{
                response = (functionPair){.x = atoi(argv[1]),.funcPtr=&factorial};
                printf("\nFactorial of %d is %d\n",response.x,response.funcPtr(response.x));
        }
        return 0;
}


--
-- demo\rosetta\Canny_Edge_Detection.exw
-- =====================================
--
without js -- imImage, im_width, im_height, im_pixel, IupImageRGB,
            -- imFileImageLoadBitmap, and IupImageFromImImage()
include pGUI.e
constant TITLE = "Canny Edge Detection",
         IMGFILE = "Valve.png",
         C_E_D = {{-1, -1, -1},
                  {-1,  8, -1},
                  {-1, -1, -1}}

function detect_edges(imImage img)
    integer width = im_width(img),
            height = im_height(img)
    sequence original = repeat(repeat(0,width),height)
    integer fh = length(C_E_D),  hh=(fh-1)/2,
            fw = length(C_E_D[1]), hw=(fw-1)/2,
            divisor = max(sum(C_E_D),1)

    -- read original pixels and make them grey,
    for y=height-1 to 0 by -1 do
        for x=0 to width-1 do
            integer {c1,c2,c3} = im_pixel(img, x, y),
                    grey = floor((c1*114+c2*587+c3*299)/1000)
            original[height-y,x+1] = {grey,grey,grey}
        end for
    end for

    -- then apply an edge detection filter
    sequence new_image = original
    for y=hh+1 to height-hh-1 do
        for x=hw+1 to width-hw-1 do
            sequence newrgb = {0,0,0}
            for i=-hh to +hh do
                for j=-hw to +hw do
                    newrgb = sq_add(newrgb,sq_mul(C_E_D[i+hh+1,j+hw+1],original[y+i,x+j]))
                end for
            end for
            new_image[y,x] = sq_max(sq_min(sq_floor_div(newrgb,divisor),255),0)
        end for
    end for

    new_image = flatten(new_image) -- (as needed by IupImageRGB)
    Ihandle new_img = IupImageRGB(width, height, new_image)
    return new_img
end function

IupOpen()
imImage im1 = imFileImageLoadBitmap(IMGFILE)
assert(im1!=NULL,"error opening "&IMGFILE)

Ihandle label1 = IupLabel(),
        label2 = IupLabel()
IupSetAttributeHandle(label1, "IMAGE", IupImageFromImImage(im1))
IupSetAttributeHandle(label2, "IMAGE", detect_edges(im1))

Ihandle dlg = IupDialog(IupHbox({label1, label2}),`TITLE="%s"`,{TITLE})
IupShow(dlg)
IupMainLoop()
IupClose()


// Static
MyClass::method(someParameter);

// Instance
myInstance.method(someParameter);

// Pointer
MyPointer->method(someParameter);


// Static
MyClass.Method(someParameter);

// Instance
myInstance.Method(someParameter);


MyClass myClassObject;
myClassObject.myFunction(some parameter);


(Long/toHexString 15) ; use forward slash for static methods
(System/currentTimeMillis)

(.equals 1 2)    ; use dot operator to call instance methods
(. 1 (equals 2)) ; alternative style


*> INVOKE
INVOKE FooClass "someMethod" RETURNING bar        *> Factory object
INVOKE foo-instance "anotherMethod" RETURNING bar *> Instance object

*> Inline method invocation
MOVE FooClass::"someMethod" TO bar        *> Factory object
MOVE foo-instance::"anotherMethod" TO bar *> Instance object


#!/bin/python
import numpy as np
from scipy.ndimage.filters import convolve, gaussian_filter
from scipy.misc import imread, imshow

def CannyEdgeDetector(im, blur = 1, highThreshold = 91, lowThreshold = 31):
	im = np.array(im, dtype=float) #Convert to float to prevent clipping values

	#Gaussian blur to reduce noise
	im2 = gaussian_filter(im, blur)

	#Use sobel filters to get horizontal and vertical gradients
	im3h = convolve(im2,[[-1,0,1],[-2,0,2],[-1,0,1]])
	im3v = convolve(im2,[[1,2,1],[0,0,0],[-1,-2,-1]])

	#Get gradient and direction
	grad = np.power(np.power(im3h, 2.0) + np.power(im3v, 2.0), 0.5)
	theta = np.arctan2(im3v, im3h)
	thetaQ = (np.round(theta * (5.0 / np.pi)) + 5) % 5 #Quantize direction

	#Non-maximum suppression
	gradSup = grad.copy()
	for r in range(im.shape[0]):
		for c in range(im.shape[1]):
			#Suppress pixels at the image edge
			if r == 0 or r == im.shape[0]-1 or c == 0 or c == im.shape[1] - 1:
				gradSup[r, c] = 0
				continue
			tq = thetaQ[r, c] % 4

			if tq == 0: #0 is E-W (horizontal)
				if grad[r, c] <= grad[r, c-1] or grad[r, c] <= grad[r, c+1]:
					gradSup[r, c] = 0
			if tq == 1: #1 is NE-SW
				if grad[r, c] <= grad[r-1, c+1] or grad[r, c] <= grad[r+1, c-1]:
					gradSup[r, c] = 0
			if tq == 2: #2 is N-S (vertical)
				if grad[r, c] <= grad[r-1, c] or grad[r, c] <= grad[r+1, c]:
					gradSup[r, c] = 0
			if tq == 3: #3 is NW-SE
				if grad[r, c] <= grad[r-1, c-1] or grad[r, c] <= grad[r+1, c+1]:
					gradSup[r, c] = 0

	#Double threshold
	strongEdges = (gradSup > highThreshold)

	#Strong has value 2, weak has value 1
	thresholdedEdges = np.array(strongEdges, dtype=np.uint8) + (gradSup > lowThreshold)

	#Tracing edges with hysteresis
	#Find weak edge pixels near strong edge pixels
	finalEdges = strongEdges.copy()
	currentPixels = []
	for r in range(1, im.shape[0]-1):
		for c in range(1, im.shape[1]-1):
			if thresholdedEdges[r, c] != 1:
				continue #Not a weak pixel

			#Get 3x3 patch
			localPatch = thresholdedEdges[r-1:r+2,c-1:c+2]
			patchMax = localPatch.max()
			if patchMax == 2:
				currentPixels.append((r, c))
				finalEdges[r, c] = 1

	#Extend strong edges based on current pixels
	while len(currentPixels) > 0:
		newPix = []
		for r, c in currentPixels:
			for dr in range(-1, 2):
				for dc in range(-1, 2):
					if dr == 0 and dc == 0: continue
					r2 = r+dr
					c2 = c+dc
					if thresholdedEdges[r2, c2] == 1 and finalEdges[r2, c2] == 0:
						#Copy this weak pixel to final result
						newPix.append((r2, c2))
						finalEdges[r2, c2] = 1
		currentPixels = newPix

	return finalEdges

if __name__=="__main__":
	im = imread("test.jpg", mode="L") #Open image, convert to greyscale
	finalEdges = CannyEdgeDetector(im)
	imshow(finalEdges)


CATALAN  CSECT        08/09/2015
         USING  CATALAN,R15
         LA     R7,1               c=1
         LA     R6,1               i=1
LOOPI    CH     R6,=H'15'          do i=1 to 15
         BH     ELOOPI
         XDECO  R6,PG              edit i
         LR     R5,R6              i
         SLA    R5,1               *2
         BCTR   R5,0               -1
         SLA    R5,1               *2
         MR     R4,R7              *c
         LA     R6,1(R6)           i=i+1
         DR     R4,R6              /i
         LR     R7,R5              c=2*(2*i-1)*c/(i+1)
         XDECO  R7,PG+12           edit c
         XPRNT  PG,24              print
         B      LOOPI              next i
ELOOPI   BR     R14
PG       DS     CL24
         YREGS
         END    CATALAN


report z_catalan_numbers.

class catalan_numbers definition.
  public section.
    class-methods:
      get_nth_number
        importing
          i_n                     type int4
        returning
          value(r_catalan_number) type int4.
endclass.

class catalan_numbers implementation.
  method get_nth_number.
    r_catalan_number = cond int4(
      when i_n eq 0
      then 1
      else reduce int4(
        init
          result = 1
          index = 1
        for position = 1 while position <= i_n
        next
          result = result * 2 * ( 2 * index - 1 ) div ( index + 1 )
          index = index + 1 ) ).
  endmethod.
endclass.

start-of-selection.
  do 15 times.
    write / |C({ sy-index - 1 }) = { catalan_numbers=>get_nth_number( sy-index - 1 ) }|.
  enddo.


// version 1.1.2

fun flattenList(nestList: List<Any>): List<Any> {
    val flatList = mutableListOf<Any>()

    fun flatten(list: List<Any>) {
        for (e in list) {
            if (e !is List<*>)
                flatList.add(e)
            else
                @Suppress("UNCHECKED_CAST")
                flatten(e as List<Any>)
        }
    }

    flatten(nestList)
    return flatList
}

operator fun List<Any>.times(other: List<Any>): List<List<Any>> {
    val prod = mutableListOf<List<Any>>()
    for (e in this) {
        for (f in other) {
            prod.add(listOf(e, f))
        }
    }
    return prod
}

fun nAryCartesianProduct(lists: List<List<Any>>): List<List<Any>> {
    require(lists.size >= 2)
    return lists.drop(2).fold(lists[0] * lists[1]) { cp, ls -> cp * ls }.map { flattenList(it) }
}

fun printNAryProduct(lists: List<List<Any>>) {
    println("${lists.joinToString(" x ")} = ")
    println("[")
    println(nAryCartesianProduct(lists).joinToString("\n    ", "    "))
    println("]\n")
}

fun main(args: Array<String>) {
   println("[1, 2] x [3, 4] = ${listOf(1, 2) * listOf(3, 4)}")
   println("[3, 4] x [1, 2] = ${listOf(3, 4) * listOf(1, 2)}")
   println("[1, 2] x []     = ${listOf(1, 2) * listOf()}")
   println("[]     x [1, 2] = ${listOf<Any>() * listOf(1, 2)}")
   println("[1, a] x [2, b] = ${listOf(1, 'a') * listOf(2, 'b')}")
   println()
   printNAryProduct(listOf(listOf(1776, 1789), listOf(7, 12), listOf(4, 14, 23), listOf(0, 1)))
   printNAryProduct(listOf(listOf(1, 2, 3), listOf(30), listOf(500, 100)))
   printNAryProduct(listOf(listOf(1, 2, 3), listOf<Int>(), listOf(500, 100)))
   printNAryProduct(listOf(listOf(1, 2, 3), listOf(30), listOf('a', 'b')))
}


class Foo
    @staticMethod: -> 'Bar'

    instanceMethod: -> 'Baz'

foo = new Foo

foo.instanceMethod() #=> 'Baz'
Foo.staticMethod() #=> 'Bar'


(defclass my-class ()
  ((x
    :accessor get-x  ;; getter function
    :initarg :x      ;; arg name
    :initform 0)))   ;; initial value

;; declaring a public class method
(defmethod square-x ((class-instance my-class))
  (* (get-x class-instance) (get-x class-instance)))

;; create an instance of my-class
(defvar *instance*
  (make-instance 'my-class :x 10))

(format t "Value of x: ~a~%" (get-x *instance*))

(format t "Value of x^2: ~a~%" (square-x *instance*))


struct Cat {
    static int staticMethod() {
        return 2;
    }

    string dynamicMethod() { // Never virtual.
        return "Mew!";
    }
}

class Dog {
    static int staticMethod() {
        return 5;
    }

    string dynamicMethod() { // Virtual method.
        return "Woof!";
    }
}

void main() {
    // Static methods calls:
    assert(Cat.staticMethod() == 2);
    assert(Dog.staticMethod() == 5);

    Cat c; // This is a value on the stack.
    Dog d; // This is just a reference, set to null.

    // Other static method calls, discouraged:
    assert(c.staticMethod() == 2);
    assert(d.staticMethod() == 5);

    // Instance method calls:
    assert(c.dynamicMethod() == "Mew!");
    d = new Dog;
    assert(d.dynamicMethod() == "Woof!");
}


#include <stdio.h>
#include <string.h>
#include <magick/MagickCore.h>

int CannyEdgeDetector(
   const char *infile, const char *outfile,
   double radius, double sigma, double lower, double upper ) {

   ExceptionInfo   *exception;
   Image           *image, *processed_image, *output;
   ImageInfo       *input_info;

   exception   = AcquireExceptionInfo();
   input_info  = CloneImageInfo((ImageInfo *) NULL);
   (void) strcpy(input_info->filename, infile);
   image       = ReadImage(input_info, exception);
   output      = NewImageList();
   processed_image = CannyEdgeImage(image,radius,sigma,lower,upper,exception);
   (void) AppendImageToList(&output, processed_image);
   (void) strcpy(output->filename, outfile);
   WriteImage(input_info, output);
                                    // after-party clean up
   DestroyImage(image);
   output=DestroyImageList(output);
   input_info=DestroyImageInfo(input_info);
   exception=DestroyExceptionInfo(exception);
   MagickCoreTerminus();

   return 0;
}


package require crimp
package require crimp::pgm

proc readPGM {filename} {
    set f [open $filename rb]
    set data [read $f]
    close $f
    return [crimp read pgm $data]
}
proc writePGM {filename image} {
    crimp write 2file pgm-raw $filename $image
}

proc cannyFilterFile {{inputFile "lena.pgm"} {outputFile "lena_canny.pgm"}} {
    writePGM $outputFile [crimp filter canny sobel [readPGM $inputFile]]
}
cannyFilterFile {*}$argv


INCLUDE "D2:REAL.ACT" ;from the Action! Tool Ki

PROC Main()
  REAL c,rnom,rden
  BYTE n,nom,den

  Put(125) PutE() ;clear the screen
  IntToReal(1,c)

  FOR n=1 TO 15
  DO
    nom=(n LSH 1-1) LSH 1
    den=n+1
    IntToReal(nom,rnom)
    IntToReal(den,rden)
    RealMult(c,rnom,c)
    RealDiv(c,rden,c)
    PrintF("C(%B)=",n) PrintRE(c)
  OD
RETURN


  local pk,upk = table.pack, table.unpack
  local getn = function(t)return #t end
  local const = function(k)return function(e) return k end end
  local function attachIdx(f)-- one-time-off function modifier
    local idx = 0
    return function(e)idx=idx+1 ; return f(e,idx)end
  end

  local function reduce(t,acc,f)
    for i=1,t.n or #t do acc=f(acc,t[i])end
    return acc
  end
  local function imap(t,f)
    local r = {n=t.n or #t, r=reduce, u=upk, m=imap}
    for i=1,r.n do r[i]=f(t[i])end
    return r
  end

  local function prod(...)
    local ts = pk(...)
    local limit = imap(ts,getn)
    local idx, cnt = imap(limit,const(1)),  0
    local max = reduce(limit,1,function(a,b)return a*b end)
    local function ret(t,i)return t[idx[i]] end
    return function()
      if cnt>=max then return end -- no more output
      if cnt==0 then -- skip for 1st
        cnt = cnt + 1
      else
        cnt, idx[#idx] = cnt + 1, idx[#idx] + 1
        for i=#idx,2,-1 do -- update index list
          if idx[i]<=limit[i] then
            break -- no further update need
          else -- propagate limit overflow
            idx[i],idx[i-1] = 1, idx[i-1]+1
          end
        end
      end
      return cnt,imap(ts,attachIdx(ret)):u()
    end
  end
--- test
  for i,a,b in prod({1,2},{3,4}) do
    print(i,a,b)
  end
  print()
  for i,a,b in prod({3,4},{1,2}) do
    print(i,a,b)
  end


//Static method on a built-in type Integer
static func Integer.Div(x, y) {
    x / y
}

//Instance method
func Integer.Div(n) {
    this / n
}

print(Integer.Div(12, 3))
print(12.Div(3))


defmodule ObjectCall do
  def new() do
    spawn_link(fn -> loop end)
  end

  defp loop do
    receive do
      {:concat, {caller, [str1, str2]}} ->
        result = str1 <> str2
        send caller, {:ok, result}
        loop
    end
  end

  def concat(obj, str1, str2) do
    send obj, {:concat, {self(), [str1, str2]}}

    receive do
      {:ok, result} ->
        result
    end
  end
end

obj = ObjectCall.new()

IO.puts(obj |> ObjectCall.concat("Hello ", "World!"))


USING: accessors io kernel literals math sequences ;
IN: rosetta-code.call-a-method

! Define some classes.
SINGLETON: dog
TUPLE: cat sassiness ;

! Define a constructor for cat.
C: <cat> cat

! Define a generic word that dispatches on the object at the top
! of the data stack.
GENERIC: speak ( obj -- )

! Define methods in speak which specialize on various classes.
M: dog speak drop "Woof!" print ;
M: cat speak sassiness>> 0.5 > "Hiss!" "Meow!" ? print ;
M: object speak drop "I don't know how to speak!" print ;

! Call speak on various objects.
! Despite being a method, it's called like any other word.
dog speak
0.75 <cat> speak
0.1 <cat> speak
"bird" speak


include lib/compare.4th
include 4pp/lib/foos.4pp

[ASSERT]                               \ enable assertions

:: Cat
   class
     method: dynamicCat                \ virtual method
   end-class {

    :static staticCat { 2 } ;          \ static method
    :method { s" Mew!" } ; defines dynamicCat
  }                                    \ for unrelated classes,
;                                      \ method names have to differ

:: Dog
   class
     method: dynamicDog                \ virtual method
   end-class {

    :static staticDog { 5 } ;
    :method { s" Woof!" } ; defines dynamicDog
  }                                    \ for unrelated classes,
;                                      \ method names have to differ

static Cat c                           \ create two static objects
static Dog d

: main
  assert( class -> staticCat 2 = )     \ check for valid method return
  assert( class -> staticDog 5 = )     \ of a static method

  assert( c -> staticCat 2 = )         \ check for valid method return
  assert( d -> staticDog 5 = )         \ of a static method

  assert( c => dynamicCat s" Mew!"  compare 0= )
  assert( d => dynamicDog s" Woof!" compare 0= )
;                                      \ same for dynamic methods

main