partial commit of new render module, render_file should work, web_main doesn't
authorTero Marttila <terom@fixme.fi>
Sat, 31 May 2008 02:22:27 +0300
changeset 1 6aa1a0d1f88d
parent 0 5b010627d7ed
child 2 69f8c0acaac7
partial commit of new render module, render_file should work, web_main doesn't

committer: Tero Marttila <terom@fixme.fi>
Makefile
mandelbrot.c
mandelbrot.h
render.c
render.h
render_file.c
render_node.c
--- a/Makefile	Fri May 30 14:24:23 2008 +0300
+++ b/Makefile	Sat May 31 02:22:27 2008 +0300
@@ -1,9 +1,12 @@
 LDFLAGS = -L/usr/local/lib -levent -lpng
 CFLAGS = -Wall -g
 
-render_file: mandelbrot.o common.o
+OBJS = common.o mandelbrot.o render.o
+HEADERS = common.h mandelbrot.h render.h
 
-web_main: mandelbrot.o common.o
+render_file: ${OBJS}
 
-render_node: mandelbrot.o common.o
+web_main: ${OBJS}
 
+render_node: ${OBJS}
+
--- a/mandelbrot.c	Fri May 30 14:24:23 2008 +0300
+++ b/mandelbrot.c	Sat May 31 02:22:27 2008 +0300
@@ -1,39 +1,23 @@
-#include <string.h>
 #include <stdlib.h>
 #include <time.h>
-#include <arpa/inet.h>
 
 #include <png.h>
 
+#include "render.h"
 #include "mandelbrot.h"
 #include "common.h"
 
-
 #define DETAIL 255
 
 #define absdelta(a, b) (a>b ? a-b : b-a)
 
-void render_ctx_set (struct render_ctx *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg) {
-    memset(ctx, 0, sizeof(*ctx));
-    
-    ctx->write_fn = write_fn;
-    ctx->flush_fn = flush_fn;
-    ctx->cb_arg = arg;
-}
-
-void render_ctx_stream (struct render_ctx *ctx, FILE *fh) {
-    memset(ctx, 0, sizeof(*ctx));
-
-    ctx->stream = fh;
-}
-
 void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
     struct render_ctx *ctx = (struct render_ctx *) png_get_io_ptr(png_ptr);
 
-    if (ctx->write_fn)
-        switch (ctx->write_fn(data, length, ctx->cb_arg)) {
+    if (ctx->io_write_fn)
+        switch (ctx->io_write_fn(data, length, ctx->io_cb_arg)) {
             case RENDER_CB_ERR :
-                ctx->_error = 1;
+                ctx->io_error = 1;
                 break;
 
             case RENDER_CB_OK :
@@ -45,10 +29,10 @@
 void user_flush_data(png_structp png_ptr) {
     struct render_ctx *ctx = (struct render_ctx *) png_get_io_ptr(png_ptr);
 
-    if (ctx->flush_fn)
-        switch (ctx->flush_fn(ctx->cb_arg)) {
+    if (ctx->io_flush_fn)
+        switch (ctx->io_flush_fn(ctx->io_cb_arg)) {
             case RENDER_CB_ERR :
-                ctx->_error = 1;
+                ctx->io_error = 1;
                 break;
 
             case RENDER_CB_OK :
@@ -57,94 +41,70 @@
         }
 }
 
-int _validate_render (
-        u_int32_t img_w, u_int32_t img_h,
-        double x1, double y1, double x2, double y2
-) {
-    if (
-            (img_w == 0 || img_h == 0)      // zero-size image
-         || (x1 >= x2 || y1 >= y2)          // invalid region
-    )
-        return MANDELBROT_ERR_INVALID;
-    else
-        return MANDELBROT_OK;
-}
+int mandelbrot_render (render_t *ctx) {
+    // libpng handles
+    png_structp png_ptr = NULL;
+    png_infop info_ptr = NULL;
 
-
-int mandelbrot_render_region (
-        struct render_ctx *ctx, 
-        u_int32_t img_w, u_int32_t img_h, 
-        double x1, double y1, double x2, double y2
-) {
+    // render algorithm vars
     u_int32_t img_x, img_y;
     double x0, y0, x, y, _x, _y, w_scale, h_scale;
     u_int8_t iter;
     u_int8_t *row;
 
-    // validate!
-    int valid = _validate_render(img_w, img_h, x1, y1, x2, y2);
-
-    if (valid != MANDELBROT_OK)
-        return valid;
-
     // clear out any potential error in ctx
-    ctx->_error = 0;
+    ctx->io_error = 0;
     
     // calcluate the scale factors
-    w_scale = img_w/absdelta(x1, x2);
-    h_scale = img_h/absdelta(y1, y2);
+    w_scale = ctx->img_w/absdelta(ctx->x1, ctx->x2);
+    h_scale = ctx->img_h/absdelta(ctx->y1, ctx->y2);
     
     // malloc the memory used to render each row
-    row = (u_int8_t *) malloc(img_w);
+    row = (u_int8_t *) malloc(ctx->img_w);
 
     if (!row)
-        return MANDELBROT_ERR_MALLOC;
-
-    // libpng initialization
-    png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
-
-    if (!png_ptr) {
-        free(row);
-        return MANDELBROT_ERR_PNG_INIT;
-    }
-
-    png_infop info_ptr = png_create_info_struct(png_ptr);
-
-    if (!info_ptr) {
-        png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
-        free(row);
-        return MANDELBROT_ERR_PNG_INIT;
-    }
+        goto error;
     
-    if (ctx->stream) {
-        // use normal libpng I/O
-        png_init_io(png_ptr, ctx->stream);
-    } else {
-        // setup our custom I/O callbacks
-        png_set_write_fn(png_ptr, ctx, &user_write_data, &user_flush_data);
-    }
+    // PNG or not?
+    if (ctx->mode == RENDER_PNG) {
+        // libpng initialization
+        png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
 
-    // some PNG metadata
-    png_set_IHDR(png_ptr, info_ptr, img_w, img_h, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+        if (!png_ptr)
+            goto error;
 
-    // write out the PNG header
-    png_write_info(png_ptr, info_ptr);
+        info_ptr = png_create_info_struct(png_ptr);
 
-    // possible error return
-    if (ctx->_error) {
-        free(row);
-        png_destroy_write_struct(&png_ptr, &info_ptr);
-        return MANDELBROT_ERR_CB;
+        if (!info_ptr)
+            goto error;
+        
+        if (ctx->io_stream) {
+            // use normal libpng I/O
+            png_init_io(png_ptr, ctx->io_stream);
+        } else {
+            // setup our custom I/O callbacks
+            png_set_write_fn(png_ptr, ctx, &user_write_data, &user_flush_data);
+        }
+
+        // some PNG metadata
+        png_set_IHDR(png_ptr, info_ptr, ctx->img_w, ctx->img_h, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+        // write out the PNG header
+        png_write_info(png_ptr, info_ptr);
+
+        // possible error return
+        if (ctx->io_error)
+            goto error;
     }
 
     // start rendering!
-    for (img_y=0; img_y < img_h; img_y++) {
+    for (img_y=0; img_y < ctx->img_h; img_y++) {
         // render the current row
-        for (img_x=0; img_x < img_w; img_x++) {
+        for (img_x=0; img_x < ctx->img_w; img_x++) {
             x = 0;
             y = 0;
-            x0 = img_x/w_scale + x1;
-            y0 = img_y/h_scale + y1;
+            x0 = img_x/w_scale + ctx->x1;
+            y0 = img_y/h_scale + ctx->y1;
             iter = DETAIL; 
 
             while (x*x + y*y < (2*2) && iter > 0) {
@@ -160,40 +120,53 @@
             row[img_x] = iter;
         }
         
-        // write the raw pixels to libpng
-        png_write_row(png_ptr, row);
-        
-        // check for user errors return
-        if (ctx->_error) {
-            free(row);
-            png_destroy_write_struct(&png_ptr, &info_ptr);
-            return MANDELBROT_ERR_CB;
+        if (ctx->mode == RENDER_PNG) {
+            // write the raw pixels to libpng
+            png_write_row(png_ptr, row);
+            
+            // check for user errors return
+            if (ctx->io_error)
+                goto error;
+        } else {
+            // pass on the pixels to the io callback
+            if (ctx->io_write_fn(row, ctx->img_w, ctx->io_cb_arg) == RENDER_CB_ERR) {
+                ctx->io_error = 1;
+                goto error;
+            }
         }
     }
-    
-    // finished writing
-    png_write_end(png_ptr, info_ptr);
+   
+    if (ctx->mode == RENDER_PNG) {
+        // finished writing
+        png_write_end(png_ptr, info_ptr);
+    }
     
     // clean up
     png_destroy_write_struct(&png_ptr, &info_ptr);
-    free(row);
+    free(row); row = NULL;
 
     // check for user errors return
-    if (ctx->_error)
-        return MANDELBROT_ERR_CB;
+    if (ctx->io_error)
+        goto error;
     
     // return succesfully
     return MANDELBROT_OK;
+
+error:
+    if (png_ptr || info_ptr)
+        png_destroy_write_struct(&png_ptr, &info_ptr);
+
+    if (row) 
+        free(row);
+    
+    row = NULL;
+
+    return MANDELBROT_ERR;
 }
 
-int mandelbrot_render_region_timed (
-        struct render_ctx *ctx, 
-        u_int32_t img_w, u_int32_t img_h, 
-        double x1, double y1, double x2, double y2,
-        double *duration
-) {
+int mandelbrot_render_timed (render_t *ctx, double *duration) {
     clock_t t1 = clock();
-    int ret = mandelbrot_render_region(ctx, img_w, img_h, x1, y1, x2, y2);
+    int ret = mandelbrot_render(ctx);
     clock_t t2 = clock();
 
     *duration = ((double)(t2 - t1))/CLOCKS_PER_SEC;
@@ -201,24 +174,3 @@
     return ret;
 }
 
-int render_cmd_set (
-        struct render_cmd *cmd,
-        u_int32_t img_w, u_int32_t img_h, 
-        double x1, double y1, double x2, double y2
-) {
-    // validate!
-    int valid = _validate_render(img_w, img_h, x1, y1, x2, y2);
-
-    if (valid != MANDELBROT_OK)
-        return valid;
-
-   cmd->img_w = htonl(img_w);
-   cmd->img_h = htonl(img_h);
-   cmd->x1 = x1;
-   cmd->y1 = y1;
-   cmd->x2 = x2;
-   cmd->y2 = y2;
-
-   return MANDELBROT_OK;
-}
-
--- a/mandelbrot.h	Fri May 30 14:24:23 2008 +0300
+++ b/mandelbrot.h	Sat May 31 02:22:27 2008 +0300
@@ -1,117 +1,18 @@
-/*
- * code to render a mandelbrot
- */
-
-#include <sys/types.h>
-
-typedef int (*render_ctx_write_cb)(const unsigned char *data, size_t length, void *arg);
-typedef int (*render_ctx_flush_cb)(void *arg);
-
-#define RENDER_CB_OK 0
-#define RENDER_CB_ERR 1
-
-struct render_ctx {
-    /*
-     * If we just use normal FILE* stream operations
-     */
-    FILE *stream;
-
-    /*
-     * These callback functions are used to handle the PNG data as it's rendered.
-     *
-     * If they return RENDER_CB_OK, all is fine and rendering will continue normally.
-     * If they return RENDER_CB_ERR, rendering will abort.
-     */
-
-    // called to handle the output data
-    render_ctx_write_cb write_fn;
-
-    // called when the output data should be flushed - can be safely ignored if not needed
-    render_ctx_flush_cb flush_fn;
-
-    // the callback argument
-    void *cb_arg;
-
-    // error status
-    int _error;
-};
+#include "render.h"
 
 /*
- * initialize the given struct render_ctx
+ * the mandelbrot-rendering algorithm
  */
-void render_ctx_set (struct render_ctx *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg);
-void render_ctx_stream (struct render_ctx *ctx, FILE *fh);
 
 /*
  * return codes for mandelbrot_render
  */
 #define MANDELBROT_OK 0
-#define MANDELBROT_ERR_MALLOC 1
-#define MANDELBROT_ERR_PNG_INIT 2
-#define MANDELBROT_ERR_CB 3
-#define MANDELBROT_ERR_INVALID 4
-
-/*
- * render an <img_w>x<img_h> PNG image of the mandelbrot region bounded by (x1, y1) -> (x2, y2)
- */
-int mandelbrot_render_region (
-        struct render_ctx *ctx, 
-        u_int32_t img_w, u_int32_t img_h, 
-        double x1, double y1, double x2, double y2
-);
-
-int mandelbrot_render_region_timed (
-        struct render_ctx *ctx, 
-        u_int32_t img_w, u_int32_t img_h, 
-        double x1, double y1, double x2, double y2,
-        double *duration
-);
+#define MANDELBROT_ERR 1
 
 /*
- * remote rendering
+ * Render the image specified by the given render context
  */
-
-#pragma pack(push)
-#pragma pack(1)
-
-struct render_cmd {
-    u_int32_t   img_w;
-    u_int32_t   img_h;
-
-    double      x1;
-    double      y1;
-    double      x2;
-    double      y2;
-};
-
-#pragma pack(pop)
-
-#define RENDER_PORT 6159
-#define RENDER_CMD_SIZE sizeof(struct render_cmd)
+int mandelbrot_render (render_t *ctx);
+int mandelbrot_render_timed (render_t *ctx, double *duration);
 
-/*
- * build the command to send for remote rendering
- */
-int render_cmd_set (
-        struct render_cmd *cmd,
-        u_int32_t img_w, u_int32_t img_h, 
-        double x1, double y1, double x2, double y2
-);
-
-/* 
- * The "full" mandelbrot region
- */
-#define REGION_X1 -2.0
-#define REGION_Y1 -1.5
-#define REGION_X2 1.0
-#define REGION_Y2 1.5
-
-#define mandelbrot_render_full(ctx, img_w, img_h) \
-    mandelbrot_render_region(ctx, img_w, img_h, REGION_X1, REGION_Y1, REGION_X2, REGION_Y2)
-
-#define mandelbrot_render_full_timed(ctx, img_w, img_h, duration) \
-    mandelbrot_render_region_timed(ctx, img_w, img_h, REGION_X1, REGION_Y1, REGION_X2, REGION_Y2, duration)
-
-#define render_cmd_set_full(cmd, img_w, img_h) \
-    render_cmd_set(cmd, img_w, img_h, REGION_X1, REGION_Y1, REGION_X2, REGION_Y2)
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render.c	Sat May 31 02:22:27 2008 +0300
@@ -0,0 +1,77 @@
+#include <string.h>
+#include <arpa/inet.h>
+
+#include "render.h"
+
+int render_init (render_t *ctx, int mode) {
+    memset(ctx, 0, sizeof(*ctx));
+    
+    return render_set_mode(ctx, mode);
+}
+
+int render_set_mode (render_t *ctx, int mode) {
+    if (mode != RENDER_RAW && mode != RENDER_PNG)
+        return RENDER_ERR;
+
+    ctx->mode = mode;
+
+    return RENDER_OK;
+}
+
+int render_set_size (render_t *ctx, u_int32_t img_w, u_int32_t img_h) {
+    if (img_w == 0 || img_h == 0)
+        return RENDER_ERR;
+
+    ctx->img_w = img_w;
+    ctx->img_h = img_h;
+
+    return RENDER_OK;
+}
+
+int render_region_full (render_t *ctx) {
+    return render_region_raw(ctx, REGION_X1, REGION_Y1, REGION_X2, REGION_Y2);
+}
+
+int render_region_raw (render_t *ctx, double x1, double y1, double x2, double y2) {
+    if ((x1 >= x2) || (y1 >= y2))
+        return RENDER_ERR;
+
+    ctx->x1 = x1;
+    ctx->y1 = y1;
+    ctx->x2 = x2;
+    ctx->y2 = y2;
+
+    return RENDER_OK;
+}
+
+int render_io_custom (render_t *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg) {
+    if (!write_fn)
+        return RENDER_ERR;
+
+    ctx->io_write_fn = write_fn;
+    ctx->io_flush_fn = flush_fn;
+    ctx->io_cb_arg = arg;
+
+    return RENDER_OK;
+}
+
+int render_io_stream (render_t *ctx, FILE *fh) {
+    if (!fh)
+        return RENDER_ERR;
+
+    ctx->io_stream = fh;
+
+    return RENDER_OK;
+}
+
+int render_cmd_build (render_t *ctx, struct render_cmd *cmd) {
+   cmd->img_w = htonl(ctx->img_w);
+   cmd->img_h = htonl(ctx->img_h);
+   cmd->x1 = ctx->x1;
+   cmd->y1 = ctx->y1;
+   cmd->x2 = ctx->x2;
+   cmd->y2 = ctx->y2;
+
+   return RENDER_OK;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/render.h	Sat May 31 02:22:27 2008 +0300
@@ -0,0 +1,120 @@
+#ifndef RENDER_H
+#define RENDER_H
+
+#include <sys/types.h>
+#include <stdio.h>
+
+/*
+ * This module provides various ways to render mandelbrot images in various formats
+ */
+
+// custom I/O callbacks function signatures
+typedef int (*render_ctx_write_cb)(const unsigned char *data, size_t length, void *arg);
+typedef int (*render_ctx_flush_cb)(void *arg);
+
+// callback return codes
+#define RENDER_CB_OK 0
+#define RENDER_CB_ERR 1
+
+#define RENDER_OK 0
+#define RENDER_ERR 1
+
+// output types
+enum {
+    RENDER_RAW,     // raw pixel data
+    RENDER_PNG,     // a png image
+};
+
+typedef struct render_ctx {
+/* render mode */
+    int mode;
+
+/* image size */
+    u_int32_t img_w;
+    u_int32_t img_h;
+
+/* mandelbrot region */
+    double      x1;
+    double      y1;
+    double      x2;
+    double      y2;
+
+/* I/O parameters */
+    // if this is non-NULL, use libpng's normal IO on this stream
+    FILE *io_stream;
+
+    // called to handle the output data
+    render_ctx_write_cb io_write_fn;
+
+    // called when the output data should be flushed - can be safely ignored if not needed
+    render_ctx_flush_cb io_flush_fn;
+
+    // the callback argument
+    void *io_cb_arg;
+
+    // error status
+    int io_error;
+} render_t;
+
+/*
+ * Clear out the render context and set the mode
+ */
+int render_init (render_t *ctx, int mode);
+
+/*
+ * What kind of image to render, PNG or RAW?
+ */
+int render_set_mode (render_t *ctx, int mode);
+
+/*
+ * What size of image to render
+ */
+int render_set_size (render_t *ctx, u_int32_t img_w, u_int32_t img_h);
+
+/*
+ * Select what region to render
+ */
+
+// the "full" mandelbrot region
+#define REGION_X1 -2.0
+#define REGION_Y1 -1.5
+#define REGION_X2 1.0
+#define REGION_Y2 1.5
+
+int render_region_full (render_t *ctx);
+int render_region_raw (render_t *ctx, double x1, double y1, double x2, double y2);
+
+/*
+ * How to handle the I/O
+ */
+int render_io_custom (render_t *ctx, render_ctx_write_cb write_fn, render_ctx_flush_cb flush_fn, void *arg);
+int render_io_stream (render_t *ctx, FILE *fh);
+
+/*
+ * Remote rendering
+ */
+
+#pragma pack(push)
+#pragma pack(1)
+
+struct render_cmd {
+    u_int32_t   img_w;
+    u_int32_t   img_h;
+
+    double      x1;
+    double      y1;
+    double      x2;
+    double      y2;
+};
+
+#pragma pack(pop)
+
+#define RENDER_PORT 6159
+#define RENDER_CMD_SIZE sizeof(struct render_cmd)
+
+/*
+ * build the command to send for remote rendering
+ */
+int render_cmd_build (render_t *ctx, struct render_cmd *cmd);
+
+#endif /* RENDER_H */
--- a/render_file.c	Fri May 30 14:24:23 2008 +0300
+++ b/render_file.c	Sat May 31 02:22:27 2008 +0300
@@ -11,24 +11,17 @@
 
 static int verbose;
 
-int file_write (const unsigned char *data, size_t length, void *arg) {
-    size_t ret = fwrite(data, length, 1, (FILE *) arg);
-    
-    return (ret == 1) ? RENDER_CB_OK : RENDER_CB_ERR;
-}
+void render_local (int img_w, int img_h, FILE *output) {
+    render_t ctx;
 
-void render_local (int img_w, int img_h, FILE *output) {
-    struct render_ctx ctx;
-
-    render_ctx_set(&ctx, &file_write, NULL, output);
+    render_init(&ctx, RENDER_PNG);
+    render_set_size(&ctx, img_w, img_h);
+    render_region_full(&ctx);
+    render_io_stream(&ctx, output);
 
     double duration;
 
-    if (mandelbrot_render_full_timed(
-            &ctx, 
-            img_w, img_h, 
-            &duration
-    ) != MANDELBROT_OK)
+    if (mandelbrot_render_timed(&ctx, &duration) != MANDELBROT_OK)
         err_exit("mandelbrot_render_region failed");
     
     if (verbose) 
@@ -36,11 +29,15 @@
 }
 
 void render_remote (int img_w, int img_h, FILE *output, FILE *remote) {
+    render_t ctx;
     struct render_cmd cmd;
 
-    if (render_cmd_set_full(&cmd, img_w, img_h) != MANDELBROT_OK) {
+    render_init(&ctx, RENDER_PNG);
+    render_set_size(&ctx, img_w, img_h);
+    render_region_full(&ctx);
+
+    if (render_cmd_build(&ctx, &cmd))
         err_exit("mandelbrot_render_remote failed");
-    }
 
     int ret = fwrite((void *) &cmd, sizeof(cmd), 1, remote);
     
--- a/render_node.c	Fri May 30 14:24:23 2008 +0300
+++ b/render_node.c	Sat May 31 02:22:27 2008 +0300
@@ -7,36 +7,37 @@
 #include "mandelbrot.h"
 #include "common.h"
 
-int read_int (FILE *fh, u_int32_t *i) {
-    printf("read_int: fread(%p, %zu, 1, %p)... ", i, sizeof(*i), fh);
-    fflush(stdout);
-    int ret = fread(i, sizeof(*i), 1, fh);
-    printf("ok: %d\n", ret);
+int my_fread(FILE *fh, void *ptr, size_t size) {
+    int ret = fread(ptr, size, 1, fh);
     
     if (ret == 0) {
         error("EOF");
         return 0;
+
     } else if (ret != 1) {
-        perror("read_int");
+        perror("fread");
         return 0;
     }
 
+    return 1;
+}
+
+int read_byte (FILE *fh, u_int8_t *byte) {
+    return my_fread(fh, byte, sizeof(*byte));
+}
+
+int read_int (FILE *fh, u_int32_t *i) {
+    if (!my_fread(fh, i, sizeof(*i)))
+        return 0;
+
     *i = ntohl(*i);
 
-    printf("read_int: %i\n", *i);
-
     return 1;
 }
 
 int read_double (FILE *fh, double *dbl) {
-    int ret = fread(dbl, sizeof(*dbl), 1, fh);
-
-    if (ret != 1) {
-        perror("read_double");
+    if (!my_fread(fh, dbl, sizeof(*dbl)))
         return 0;
-    }
-
-    printf("read_double: %f\n", *dbl);
 
     return 1;
 }
@@ -46,10 +47,12 @@
     FILE *fh = fdopen(sock, "r+");
     
     // read the parameters
+    u_int8_t mode;
     u_int32_t img_w, img_h;
     double x1, y1, x2, y2;
 
     if (
+        !read_byte(fh, &mode) ||
         !read_int(fh, &img_w) ||
         !read_int(fh, &img_h) ||
         !read_double(fh, &x1) ||