static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
			       char *data, int datalen)
{
	struct iscsi_reject *reject = (struct iscsi_reject *)hdr;
	struct iscsi_hdr rejected_pdu;
	int opcode, rc = 0;

	conn->exp_statsn = be32_to_cpu(reject->statsn) + 1;

	if (ntoh24(reject->dlength) > datalen ||
	    ntoh24(reject->dlength) < sizeof(struct iscsi_hdr)) {
		iscsi_conn_printk(KERN_ERR, conn, "Cannot handle rejected "
				  "pdu. Invalid data length (pdu dlength "
				  "%u, datalen %d\n", ntoh24(reject->dlength),
				  datalen);
		return ISCSI_ERR_PROTO;
	}
	memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
	opcode = rejected_pdu.opcode & ISCSI_OPCODE_MASK;

	switch (reject->reason) {
	case ISCSI_REASON_DATA_DIGEST_ERROR:
		iscsi_conn_printk(KERN_ERR, conn,
				  "pdu (op 0x%x itt 0x%x) rejected "
				  "due to DataDigest error.\n",
				  opcode, rejected_pdu.itt);
		break;
	case ISCSI_REASON_IMM_CMD_REJECT:
		iscsi_conn_printk(KERN_ERR, conn,
				  "pdu (op 0x%x itt 0x%x) rejected. Too many "
				  "immediate commands.\n",
				  opcode, rejected_pdu.itt);
		/*
		 * We only send one TMF at a time so if the target could not
		 * handle it, then it should get fixed (RFC mandates that
		 * a target can handle one immediate TMF per conn).
		 *
		 * For nops-outs, we could have sent more than one if
		 * the target is sending us lots of nop-ins
		 */
		if (opcode != ISCSI_OP_NOOP_OUT)
			return 0;

		if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG)) {
			/*
			 * nop-out in response to target's nop-out rejected.
			 * Just resend.
			 */
			/* In RX path we are under back lock */
			spin_unlock(&conn->session->back_lock);
			spin_lock(&conn->session->frwd_lock);
			iscsi_send_nopout(conn,
					  (struct iscsi_nopin*)&rejected_pdu);
			spin_unlock(&conn->session->frwd_lock);
			spin_lock(&conn->session->back_lock);
		} else {
			struct iscsi_task *task;
			/*
			 * Our nop as ping got dropped. We know the target
			 * and transport are ok so just clean up
			 */
			task = iscsi_itt_to_task(conn, rejected_pdu.itt);
			if (!task) {
				iscsi_conn_printk(KERN_ERR, conn,
						 "Invalid pdu reject. Could "
						 "not lookup rejected task.\n");
				rc = ISCSI_ERR_BAD_ITT;
			} else
				rc = iscsi_nop_out_rsp(task,
					(struct iscsi_nopin*)&rejected_pdu,
					NULL, 0);
		}
		break;
	default:
		iscsi_conn_printk(KERN_ERR, conn,
				  "pdu (op 0x%x itt 0x%x) rejected. Reason "
				  "code 0x%x\n", rejected_pdu.opcode,
				  rejected_pdu.itt, reject->reason);
		break;
	}
	return rc;
}


 *  %NULL on failure
 */
struct iscsi_bus_flash_conn *
iscsi_create_flashnode_conn(struct Scsi_Host *shost,
			    struct iscsi_bus_flash_session *fnode_sess,
			    struct iscsi_transport *transport,
			    int dd_size)
{
	struct iscsi_bus_flash_conn *fnode_conn;
	int err;

	fnode_conn = kzalloc(sizeof(*fnode_conn) + dd_size, GFP_KERNEL);
	if (!fnode_conn)
		return NULL;

	fnode_conn->transport = transport;
	fnode_conn->dev.type = &iscsi_flashnode_conn_dev_type;
	fnode_conn->dev.bus = &iscsi_flashnode_bus;
	fnode_conn->dev.parent = &fnode_sess->dev;
	dev_set_name(&fnode_conn->dev, "flashnode_conn-%u:%u:0",
		     shost->host_no, fnode_sess->target_id);

	err = device_register(&fnode_conn->dev);
	if (err)
		goto free_fnode_conn;

	if (dd_size)
		fnode_conn->dd_data = &fnode_conn[1];

	return fnode_conn;

free_fnode_conn:


void intel_guc_notify(struct intel_guc *guc)
{
	struct intel_gt *gt = guc_to_gt(guc);

	/*
	 * On Gen11+, the value written to the register is passes as a payload
	 * to the FW. However, the FW currently treats all values the same way
	 * (H2G interrupt), so we can just write the value that the HW expects
	 * on older gens.
	 */
	intel_uncore_write(gt->uncore, guc->notify_reg, GUC_SEND_TRIGGER);
}


int intel_guc_reset_engine(struct intel_guc *guc,
			   struct intel_engine_cs *engine)
{
	/* XXX: to be implemented with submission interface rework */

	return -ENODEV;
}


static void guc_ads_private_data_reset(struct intel_guc *guc)
{
	u32 size;

	size = guc_ads_private_data_size(guc);
	if (!size)
		return;

	memset((void *)guc->ads_blob + guc_ads_private_data_offset(guc), 0,
	       size);
}


static int engine_init_common(struct intel_engine_cs *engine)
{
	struct intel_context *ce;
	int ret;

	engine->set_default_submission(engine);

	/*
	 * We may need to do things with the shrinker which
	 * require us to immediately switch back to the default
	 * context. This can cause a problem as pinning the
	 * default context also requires GTT space which may not
	 * be available. To avoid this we always pin the default
	 * context.
	 */
	ce = create_kernel_context(engine);
	if (IS_ERR(ce))
		return PTR_ERR(ce);

	ret = measure_breadcrumb_dw(ce);
	if (ret < 0)
		goto err_context;

	engine->emit_fini_breadcrumb_dw = ret;
	engine->kernel_context = ce;

	return 0;

err_context:
	intel_context_put(ce);
	return ret;
}


static u32 guc_ctl_ads_flags(struct intel_guc *guc)
{
	u32 ads = intel_guc_ggtt_offset(guc, guc->ads_vma) >> PAGE_SHIFT;
	u32 flags = ads << GUC_ADS_ADDR_SHIFT;

	return flags;
}


int intel_guc_ads_create(struct intel_guc *guc)
{
	u32 size;
	int ret;

	GEM_BUG_ON(guc->ads_vma);

	size = guc_ads_blob_size(guc);

	ret = intel_guc_allocate_and_map_vma(guc, size, &guc->ads_vma,
					     (void **)&guc->ads_blob);
	if (ret)
		return ret;

	__guc_ads_init(guc);

	return 0;
}


static const char *repr_timer(const struct timer_list *t)
{
	if (!READ_ONCE(t->expires))
		return "inactive";

	if (timer_pending(t))
		return "active";

	return "expired";
}


static unsigned long stop_timeout(const struct intel_engine_cs *engine)
{
	if (in_atomic() || irqs_disabled()) /* inside atomic preempt-reset? */
		return 0;

	/*
	 * If we are doing a normal GPU reset, we can take our time and allow
	 * the engine to quiesce. We've stopped submission to the engine, and
	 * if we wait long enough an innocent context should complete and
	 * leave the engine idle. So they should not be caught unaware by
	 * the forthcoming GPU reset (which usually follows the stop_cs)!
	 */
	return READ_ONCE(engine->props.stop_timeout_ms);
}


void intel_engine_init_execlists(struct intel_engine_cs *engine)
{
	struct intel_engine_execlists * const execlists = &engine->execlists;

	execlists->port_mask = 1;
	GEM_BUG_ON(!is_power_of_2(execlists_num_ports(execlists)));
	GEM_BUG_ON(execlists_num_ports(execlists) > EXECLIST_MAX_PORTS);

	memset(execlists->pending, 0, sizeof(execlists->pending));
	execlists->active =
		memset(execlists->inflight, 0, sizeof(execlists->inflight));

	execlists->queue_priority_hint = INT_MIN;
	execlists->queue = RB_ROOT_CACHED;
}


void intel_engine_set_hwsp_writemask(struct intel_engine_cs *engine, u32 mask)
{
	/*
	 * Though they added more rings on g4x/ilk, they did not add
	 * per-engine HWSTAM until gen6.
	 */
	if (INTEL_GEN(engine->i915) < 6 && engine->class != RENDER_CLASS)
		return;

	if (INTEL_GEN(engine->i915) >= 3)
		ENGINE_WRITE(engine, RING_HWSTAM, mask);
	else
		ENGINE_WRITE16(engine, RING_HWSTAM, mask);
}


intel_engine_find_active_request(struct intel_engine_cs *engine)
{
	struct i915_request *request, *active = NULL;

	/*
	 * We are called by the error capture, reset and to dump engine
	 * state at random points in time. In particular, note that neither is
	 * crucially ordered with an interrupt. After a hang, the GPU is dead
	 * and we assume that no more writes can happen (we waited long enough
	 * for all writes that were in transaction to be flushed) - adding an
	 * extra delay for a recent interrupt is pointless. Hence, we do
	 * not need an engine->irq_seqno_barrier() before the seqno reads.
	 * At all other times, we must assume the GPU is still running, but
	 * we only care about the snapshot of this moment.
	 */
	lockdep_assert_held(&engine->active.lock);

	rcu_read_lock();
	request = execlists_active(&engine->execlists);
	if (request) {
		struct intel_timeline *tl = request->context->timeline;

		list_for_each_entry_from_reverse(request, &tl->requests, link) {
			if (i915_request_completed(request))
				break;

			active = request;
		}
	}
	rcu_read_unlock();
	if (active)
		return active;

	list_for_each_entry(request, &engine->active.requests, sched.link) {
		if (i915_request_completed(request))
			continue;

		if (!i915_request_started(request))
			continue;

		/* More than one preemptible request may match! */
		if (!match_ring(request))
			continue;

		active = request;
		break;
	}

	return active;
}


static u32 guc_ctl_debug_flags(struct intel_guc *guc)
{
	u32 level = intel_guc_log_get_level(&guc->log);
	u32 flags = 0;

	if (!GUC_LOG_LEVEL_IS_VERBOSE(level))
		flags |= GUC_LOG_DISABLED;
	else
		flags |= GUC_LOG_LEVEL_TO_VERBOSITY(level) <<
			 GUC_LOG_VERBOSITY_SHIFT;

	return flags;
}


void t2p_pdf_tifftime(T2P* t2p, TIFF* input)
{
	char* datetime;

	if (TIFFGetField(input, TIFFTAG_DATETIME, &datetime) != 0
	    && (strlen(datetime) >= 19) ){
		t2p->pdf_datetime[0]='D';
		t2p->pdf_datetime[1]=':';
		t2p->pdf_datetime[2]=datetime[0];
		t2p->pdf_datetime[3]=datetime[1];
		t2p->pdf_datetime[4]=datetime[2];
		t2p->pdf_datetime[5]=datetime[3];
		t2p->pdf_datetime[6]=datetime[5];
		t2p->pdf_datetime[7]=datetime[6];
		t2p->pdf_datetime[8]=datetime[8];
		t2p->pdf_datetime[9]=datetime[9];
		t2p->pdf_datetime[10]=datetime[11];
		t2p->pdf_datetime[11]=datetime[12];
		t2p->pdf_datetime[12]=datetime[14];
		t2p->pdf_datetime[13]=datetime[15];
		t2p->pdf_datetime[14]=datetime[17];
		t2p->pdf_datetime[15]=datetime[18];
		t2p->pdf_datetime[16] = '\0';
	} else {
		t2p_pdf_currenttime(t2p);
	}

	return;
}


void t2p_tile_collapse_left(
	tdata_t buffer,
	tsize_t scanwidth,
	uint32 tilewidth,
	uint32 edgetilewidth,
	uint32 tilelength){

	uint32 i;
	tsize_t edgescanwidth=0;

	edgescanwidth = (scanwidth * edgetilewidth + (tilewidth - 1))/ tilewidth;
	for(i=0;i<tilelength;i++){
                /* We use memmove() since there can be overlaps in src and dst buffers for the first items */
		memmove(
			&(((char*)buffer)[edgescanwidth*i]),
			&(((char*)buffer)[scanwidth*i]),
			edgescanwidth);
	}

	return;
}


tsize_t t2p_write_pdf_obj_end(TIFF* output){

	tsize_t written=0;

	written += t2pWriteFile(output, (tdata_t) "endobj\n", 7);

	return(written);
}


tsize_t t2p_sample_planar_separate_to_contig(
											T2P* t2p,
											unsigned char* buffer,
											unsigned char* samplebuffer,
											tsize_t samplebuffersize){

	tsize_t stride=0;
	tsize_t i=0;
	tsize_t j=0;

	stride=samplebuffersize/t2p->tiff_samplesperpixel;
	for(i=0;i<stride;i++){
		for(j=0;j<t2p->tiff_samplesperpixel;j++){
			buffer[i*t2p->tiff_samplesperpixel + j] = samplebuffer[i + j*stride];
		}
	}

	return(samplebuffersize);
}


t2p_writeproc(thandle_t handle, tdata_t data, tmsize_t size)
{
	T2P *t2p = (T2P*) handle;
	if (t2p->outputdisable <= 0 && t2p->outputfile) {
		tsize_t written = fwrite(data, 1, size, t2p->outputfile);
		t2p->outputwritten += written;
		return written;
	}
	return size;
}


t2p_readproc(thandle_t handle, tdata_t data, tmsize_t size)
{
	(void) handle, (void) data, (void) size;
	return -1;
}


void *zcalloc_usable(size_t size, size_t *usable) {
    void *ptr = ztrycalloc_usable(size, usable);
    if (!ptr) zmalloc_oom_handler(size);
    return ptr;
}


int zmalloc_get_allocator_info(size_t *allocated,
                               size_t *active,
                               size_t *resident) {
    uint64_t epoch = 1;
    size_t sz;
    *allocated = *resident = *active = 0;
    /* Update the statistics cached by mallctl. */
    sz = sizeof(epoch);
    je_mallctl("epoch", &epoch, &sz, &epoch, sz);
    sz = sizeof(size_t);
    /* Unlike RSS, this does not include RSS from shared libraries and other non
     * heap mappings. */
    je_mallctl("stats.resident", resident, &sz, NULL, 0);
    /* Unlike resident, this doesn't not include the pages jemalloc reserves
     * for re-use (purge will clean that). */
    je_mallctl("stats.active", active, &sz, NULL, 0);
    /* Unlike zmalloc_used_memory, this matches the stats.resident by taking
     * into account all allocations done by this process (not only zmalloc). */
    je_mallctl("stats.allocated", allocated, &sz, NULL, 0);
    return 1;
}


sds sdscatprintf(sds s, const char *fmt, ...) {
    va_list ap;
    char *t;
    va_start(ap, fmt);
    t = sdscatvprintf(s,fmt,ap);
    va_end(ap);
    return t;
}


size_t zmalloc_size(void *ptr) {
    void *realptr = (char*)ptr-PREFIX_SIZE;
    size_t size = *((size_t*)realptr);
    return size+PREFIX_SIZE;
}


void *ztrymalloc_usable(size_t size, size_t *usable) {
    ASSERT_NO_SIZE_OVERFLOW(size);
    void *ptr = malloc(size+PREFIX_SIZE);

    if (!ptr) return NULL;
#ifdef HAVE_MALLOC_SIZE
    size = zmalloc_size(ptr);
    update_zmalloc_stat_alloc(size);
    if (usable) *usable = size;
    return ptr;
#else
    *((size_t*)ptr) = size;
    update_zmalloc_stat_alloc(size+PREFIX_SIZE);
    if (usable) *usable = size;
    return (char*)ptr+PREFIX_SIZE;
#endif
}


sds sdsRemoveFreeSpace(sds s) {
    void *sh, *newsh;
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen, oldhdrlen = sdsHdrSize(oldtype);
    size_t len = sdslen(s);
    size_t avail = sdsavail(s);
    sh = (char*)s-oldhdrlen;

    /* Return ASAP if there is no space left. */
    if (avail == 0) return s;

    /* Check what would be the minimum SDS header that is just good enough to
     * fit this string. */
    type = sdsReqType(len);
    hdrlen = sdsHdrSize(type);

    /* If the type is the same, or at least a large enough type is still
     * required, we just realloc(), letting the allocator to do the copy
     * only if really needed. Otherwise if the change is huge, we manually
     * reallocate the string to use the different header type. */
    if (oldtype==type || type > SDS_TYPE_8) {
        newsh = s_realloc(sh, oldhdrlen+len+1);
        if (newsh == NULL) return NULL;
        s = (char*)newsh+oldhdrlen;
    } else {
        newsh = s_malloc(hdrlen+len+1);
        if (newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, s, len+1);
        s_free(sh);
        s = (char*)newsh+hdrlen;
        s[-1] = type;
        sdssetlen(s, len);
    }
    sdssetalloc(s, len);
    return s;
}


sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) {
    size_t j, i, l = sdslen(s);

    for (j = 0; j < l; j++) {
        for (i = 0; i < setlen; i++) {
            if (s[j] == from[i]) {
                s[j] = to[i];
                break;
            }
        }
    }
    return s;
}


NetClientState *qemu_get_peer(NetClientState *nc, int queue_index)
{
    assert(nc != NULL);
    NetClientState *ncs = nc + queue_index;
    return ncs->peer;
}


static void qemu_net_queue_append(NetQueue *queue,
                                  NetClientState *sender,
                                  unsigned flags,
                                  const uint8_t *buf,
                                  size_t size,
                                  NetPacketSent *sent_cb)
{
    NetPacket *packet;

    if (queue->nq_count >= queue->nq_maxlen && !sent_cb) {
        return; /* drop if queue full and no callback */
    }
    packet = g_malloc(sizeof(NetPacket) + size);
    packet->sender = sender;
    packet->flags = flags;
    packet->size = size;
    packet->sent_cb = sent_cb;
    memcpy(packet->data, buf, size);

    queue->nq_count++;
    QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
}


void qemu_del_net_queue(NetQueue *queue)
{
    NetPacket *packet, *next;

    QTAILQ_FOREACH_SAFE(packet, &queue->packets, entry, next) {
        QTAILQ_REMOVE(&queue->packets, packet, entry);
        g_free(packet);
    }

    g_free(queue);
}


static void e1000_reset(void *opaque)
{
    E1000State *d = opaque;
    E1000BaseClass *edc = E1000_GET_CLASS(d);
    uint8_t *macaddr = d->conf.macaddr.a;

    timer_del(d->autoneg_timer);
    timer_del(d->mit_timer);
    timer_del(d->flush_queue_timer);
    d->mit_timer_on = 0;
    d->mit_irq_level = 0;
    d->mit_ide = 0;
    memset(d->phy_reg, 0, sizeof d->phy_reg);
    memmove(d->phy_reg, phy_reg_init, sizeof phy_reg_init);
    d->phy_reg[PHY_ID2] = edc->phy_id2;
    memset(d->mac_reg, 0, sizeof d->mac_reg);
    memmove(d->mac_reg, mac_reg_init, sizeof mac_reg_init);
    d->rxbuf_min_shift = 1;
    memset(&d->tx, 0, sizeof d->tx);

    if (qemu_get_queue(d->nic)->link_down) {
        e1000x_update_regs_on_link_down(d->mac_reg, d->phy_reg);
    }

    e1000x_reset_mac_addr(d->nic, d->mac_reg, macaddr);
}


e1000_send_packet(E1000State *s, const uint8_t *buf, int size)
{
    static const int PTCregs[6] = { PTC64, PTC127, PTC255, PTC511,
                                    PTC1023, PTC1522 };

    NetClientState *nc = qemu_get_queue(s->nic);
    if (s->phy_reg[PHY_CTRL] & MII_CR_LOOPBACK) {
        qemu_receive_packet(nc, buf, size);
    } else {
        qemu_send_packet(nc, buf, size);
    }
    inc_tx_bcast_or_mcast_count(s, buf);
    e1000x_increase_size_stats(s->mac_reg, PTCregs, size);
}


static void dp8393x_do_transmit_packets(dp8393xState *s)
{
    NetClientState *nc = qemu_get_queue(s->nic);
    int width, size;
    int tx_len, len;
    uint16_t i;

    width = (s->regs[SONIC_DCR] & SONIC_DCR_DW) ? 2 : 1;

    while (1) {
        /* Read memory */
        size = sizeof(uint16_t) * 6 * width;
        s->regs[SONIC_TTDA] = s->regs[SONIC_CTDA];
        DPRINTF("Transmit packet at %08x\n", dp8393x_ttda(s));
        address_space_read(&s->as, dp8393x_ttda(s) + sizeof(uint16_t) * width,
                           MEMTXATTRS_UNSPECIFIED, s->data, size);
        tx_len = 0;

        /* Update registers */
        s->regs[SONIC_TCR] = dp8393x_get(s, width, 0) & 0xf000;
        s->regs[SONIC_TPS] = dp8393x_get(s, width, 1);
        s->regs[SONIC_TFC] = dp8393x_get(s, width, 2);
        s->regs[SONIC_TSA0] = dp8393x_get(s, width, 3);
        s->regs[SONIC_TSA1] = dp8393x_get(s, width, 4);
        s->regs[SONIC_TFS] = dp8393x_get(s, width, 5);

        /* Handle programmable interrupt */
        if (s->regs[SONIC_TCR] & SONIC_TCR_PINT) {
            s->regs[SONIC_ISR] |= SONIC_ISR_PINT;
        } else {
            s->regs[SONIC_ISR] &= ~SONIC_ISR_PINT;
        }

        for (i = 0; i < s->regs[SONIC_TFC]; ) {
            /* Append fragment */
            len = s->regs[SONIC_TFS];
            if (tx_len + len > sizeof(s->tx_buffer)) {
                len = sizeof(s->tx_buffer) - tx_len;
            }
            address_space_read(&s->as, dp8393x_tsa(s), MEMTXATTRS_UNSPECIFIED,
                               &s->tx_buffer[tx_len], len);
            tx_len += len;

            i++;
            if (i != s->regs[SONIC_TFC]) {
                /* Read next fragment details */
                size = sizeof(uint16_t) * 3 * width;
                address_space_read(&s->as,
                                   dp8393x_ttda(s)
                                   + sizeof(uint16_t) * width * (4 + 3 * i),
                                   MEMTXATTRS_UNSPECIFIED, s->data,
                                   size);
                s->regs[SONIC_TSA0] = dp8393x_get(s, width, 0);
                s->regs[SONIC_TSA1] = dp8393x_get(s, width, 1);
                s->regs[SONIC_TFS] = dp8393x_get(s, width, 2);
            }
        }

        /* Handle Ethernet checksum */
        if (!(s->regs[SONIC_TCR] & SONIC_TCR_CRCI)) {
            /* Don't append FCS there, to look like slirp packets
             * which don't have one */
        } else {
            /* Remove existing FCS */
            tx_len -= 4;
            if (tx_len < 0) {
                SONIC_ERROR("tx_len is %d\n", tx_len);
                break;
            }
        }

        if (s->regs[SONIC_RCR] & (SONIC_RCR_LB1 | SONIC_RCR_LB0)) {
            /* Loopback */
            s->regs[SONIC_TCR] |= SONIC_TCR_CRSL;
            if (nc->info->can_receive(nc)) {
                s->loopback_packet = 1;
                qemu_receive_packet(nc, s->tx_buffer, tx_len);
            }
        } else {
            /* Transmit packet */
            qemu_send_packet(nc, s->tx_buffer, tx_len);
        }
        s->regs[SONIC_TCR] |= SONIC_TCR_PTX;

        /* Write status */
        dp8393x_put(s, width, 0,
                    s->regs[SONIC_TCR] & 0x0fff); /* status */
        size = sizeof(uint16_t) * width;
        address_space_write(&s->as, dp8393x_ttda(s),
                            MEMTXATTRS_UNSPECIFIED, s->data, size);

        if (!(s->regs[SONIC_CR] & SONIC_CR_HTX)) {
            /* Read footer of packet */
            size = sizeof(uint16_t) * width;
            address_space_read(&s->as,
                               dp8393x_ttda(s)
                               + sizeof(uint16_t) * width
                                 * (4 + 3 * s->regs[SONIC_TFC]),
                               MEMTXATTRS_UNSPECIFIED, s->data,
                               size);
            s->regs[SONIC_CTDA] = dp8393x_get(s, width, 0);
            if (s->regs[SONIC_CTDA] & SONIC_DESC_EOL) {
                /* EOL detected */
                break;
            }
        }
    }

    /* Done */
    s->regs[SONIC_CR] &= ~SONIC_CR_TXP;
    s->regs[SONIC_ISR] |= SONIC_ISR_TXDN;
    dp8393x_update_irq(s);
}


static void msf2_emac_init(Object *obj)
{
    MSF2EmacState *s = MSS_EMAC(obj);

    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);

    memory_region_init_io(&s->mmio, obj, &emac_ops, s,
                          "msf2-emac", R_MAX * 4);
    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
}


static void emac_load_desc(MSF2EmacState *s, EmacDesc *d, hwaddr desc)
{
    address_space_read(&s->dma_as, desc, MEMTXATTRS_UNSPECIFIED, d, sizeof *d);
    /* Convert from LE into host endianness. */
    d->pktaddr = le32_to_cpu(d->pktaddr);
    d->pktsize = le32_to_cpu(d->pktsize);
    d->next = le32_to_cpu(d->next);
}


static uint64_t sungem_mmio_rxdma_read(void *opaque, hwaddr addr, unsigned size)
{
    SunGEMState *s = opaque;
    uint32_t val;

    if (!(addr <= 0x28) && !(addr >= 0x100 && addr <= 0x120)) {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "Read from unknown RXDMA register 0x%"HWADDR_PRIx"\n",
                      addr);
        return 0;
    }

    val = s->rxdmaregs[addr >> 2];

    trace_sungem_mmio_rxdma_read(addr, val);

    return val;
}


static void sungem_eval_irq(SunGEMState *s)
{
    uint32_t stat, mask;

    mask = s->gregs[GREG_IMASK >> 2];
    stat = s->gregs[GREG_STAT >> 2] & ~GREG_STAT_TXNR;
    if (stat & ~mask) {
        pci_set_irq(PCI_DEVICE(s), 1);
    } else {
        pci_set_irq(PCI_DEVICE(s), 0);
    }
}


static void sungem_mmio_rxdma_write(void *opaque, hwaddr addr, uint64_t val,
                                    unsigned size)
{
    SunGEMState *s = opaque;

    if (!(addr <= 0x28) && !(addr >= 0x100 && addr <= 0x120)) {
        qemu_log_mask(LOG_GUEST_ERROR,
                      "Write to unknown RXDMA register 0x%"HWADDR_PRIx"\n",
                      addr);
        return;
    }

    trace_sungem_mmio_rxdma_write(addr, val);

    /* Pre-write filter */
    switch (addr) {
    /* Read only registers */
    case RXDMA_DONE:
    case RXDMA_PCNT:
    case RXDMA_SMACHINE:
    case RXDMA_DPLOW:
    case RXDMA_DPHI:
    case RXDMA_FSZ:
    case RXDMA_FTAG:
        return; /* No actual write */
    }

    s->rxdmaregs[addr >> 2] = val;

    /* Post write action */
    switch (addr) {
    case RXDMA_KICK:
        trace_sungem_rx_kick(val);
        break;
    case RXDMA_CFG:
        sungem_update_masks(s);
        if ((s->macregs[MAC_RXCFG >> 2] & MAC_RXCFG_ENAB) != 0 &&
            (s->rxdmaregs[RXDMA_CFG >> 2] & RXDMA_CFG_ENABLE) != 0) {
            qemu_flush_queued_packets(qemu_get_queue(s->nic));
        }
        break;
    }
}


static void rtl8139_MultiIntr_write(RTL8139State *s, uint32_t val)
{
    DPRINTF("MultiIntr write(w) val=0x%04x\n", val);

    /* mask unwritable bits */
    val = SET_MASKED(val, 0xf000, s->MultiIntr);

    s->MultiIntr = val;
}


static void rtl8139_set_link_status(NetClientState *nc)
{
    RTL8139State *s = qemu_get_nic_opaque(nc);

    if (nc->link_down) {
        s->BasicModeStatus &= ~0x04;
    } else {
        s->BasicModeStatus |= 0x04;
    }

    s->IntrStatus |= RxUnderrun;
    rtl8139_update_irq(s);
}


static bool rtl8139_hotplug_ready_needed(void *opaque)
{
    return qdev_machine_modified();
}


static uint32_t rtl8139_BasicModeStatus_read(RTL8139State *s)
{
    uint32_t ret = s->BasicModeStatus;

    DPRINTF("BasicModeStatus register read(w) val=0x%04x\n", ret);

    return ret;
}


static void rtl8139_reset(DeviceState *d)
{
    RTL8139State *s = RTL8139(d);
    int i;

    /* restore MAC address */
    memcpy(s->phys, s->conf.macaddr.a, 6);
    qemu_format_nic_info_str(qemu_get_queue(s->nic), s->phys);

    /* reset interrupt mask */
    s->IntrStatus = 0;
    s->IntrMask = 0;

    rtl8139_update_irq(s);

    /* mark all status registers as owned by host */
    for (i = 0; i < 4; ++i)
    {
        s->TxStatus[i] = TxHostOwns;
    }

    s->currTxDesc = 0;
    s->currCPlusRxDesc = 0;
    s->currCPlusTxDesc = 0;

    s->RxRingAddrLO = 0;
    s->RxRingAddrHI = 0;

    s->RxBuf = 0;

    rtl8139_reset_rxring(s, 8192);

    /* ACK the reset */
    s->TxConfig = 0;

#if 0
//    s->TxConfig |= HW_REVID(1, 0, 0, 0, 0, 0, 0); // RTL-8139  HasHltClk
    s->clock_enabled = 0;
#else
    s->TxConfig |= HW_REVID(1, 1, 1, 0, 1, 1, 0); // RTL-8139C+ HasLWake
    s->clock_enabled = 1;
#endif

    s->bChipCmdState = CmdReset; /* RxBufEmpty bit is calculated on read from ChipCmd */;

    /* set initial state data */
    s->Config0 = 0x0; /* No boot ROM */
    s->Config1 = 0xC; /* IO mapped and MEM mapped registers available */
    s->Config3 = 0x1; /* fast back-to-back compatible */
    s->Config5 = 0x0;

    s->CpCmd   = 0x0; /* reset C+ mode */
    s->cplus_enabled = 0;

//    s->BasicModeCtrl = 0x3100; // 100Mbps, full duplex, autonegotiation
//    s->BasicModeCtrl = 0x2100; // 100Mbps, full duplex
    s->BasicModeCtrl = 0x1000; // autonegotiation

    rtl8139_reset_phy(s);

    /* also reset timer and disable timer interrupt */
    s->TCTR = 0;
    s->TimerInt = 0;
    s->TCTR_base = 0;
    rtl8139_set_next_tctr_time(s);

    /* reset tally counters */
    RTL8139TallyCounters_clear(&s->tally_counters);
}


static uint32_t rtl8139_RxBufAddr_read(RTL8139State *s)
{
    /* this value is NOT off by 16 */
    uint32_t ret = s->RxBufAddr;

    DPRINTF("RxBufAddr read val=0x%04x\n", ret);

    return ret;
}


static void prom9346_shift_clock(EEprom9346 *eeprom)
{
    int bit = eeprom->eedi?1:0;

    ++ eeprom->tick;

    DPRINTF("eeprom: tick %d eedi=%d eedo=%d\n", eeprom->tick, eeprom->eedi,
        eeprom->eedo);

    switch (eeprom->mode)
    {
        case Chip9346_enter_command_mode:
            if (bit)
            {
                eeprom->mode = Chip9346_read_command;
                eeprom->tick = 0;
                eeprom->input = 0;
                DPRINTF("eeprom: +++ synchronized, begin command read\n");
            }
            break;

        case Chip9346_read_command:
            eeprom->input = (eeprom->input << 1) | (bit & 1);
            if (eeprom->tick == 8)
            {
                prom9346_decode_command(eeprom, eeprom->input & 0xff);
            }
            break;

        case Chip9346_data_read:
            eeprom->eedo = (eeprom->output & 0x8000)?1:0;
            eeprom->output <<= 1;
            if (eeprom->tick == 16)
            {
#if 1
        // the FreeBSD drivers (rl and re) don't explicitly toggle
        // CS between reads (or does setting Cfg9346 to 0 count too?),
        // so we need to enter wait-for-command state here
                eeprom->mode = Chip9346_enter_command_mode;
                eeprom->input = 0;
                eeprom->tick = 0;

                DPRINTF("eeprom: +++ end of read, awaiting next command\n");
#else
        // original behaviour
                ++eeprom->address;
                eeprom->address &= EEPROM_9346_ADDR_MASK;
                eeprom->output = eeprom->contents[eeprom->address];
                eeprom->tick = 0;

                DPRINTF("eeprom: +++ read next address 0x%02x data=0x%04x\n",
                    eeprom->address, eeprom->output);
#endif
            }
            break;

        case Chip9346_data_write:
            eeprom->input = (eeprom->input << 1) | (bit & 1);
            if (eeprom->tick == 16)
            {
                DPRINTF("eeprom write to address 0x%02x data=0x%04x\n",
                    eeprom->address, eeprom->input);

                eeprom->contents[eeprom->address] = eeprom->input;
                eeprom->mode = Chip9346_none; /* waiting for next command after CS cycle */
                eeprom->tick = 0;
                eeprom->input = 0;
            }
            break;

        case Chip9346_data_write_all:
            eeprom->input = (eeprom->input << 1) | (bit & 1);
            if (eeprom->tick == 16)
            {
                int i;
                for (i = 0; i < EEPROM_9346_SIZE; i++)
                {
                    eeprom->contents[i] = eeprom->input;
                }
                DPRINTF("eeprom filled with data=0x%04x\n", eeprom->input);

                eeprom->mode = Chip9346_enter_command_mode;
                eeprom->tick = 0;
                eeprom->input = 0;
            }
            break;

        default:
            break;
    }
}


static void rtl8139_RxConfig_write(RTL8139State *s, uint32_t val)
{
    DPRINTF("RxConfig write val=0x%08x\n", val);

    /* mask unwritable bits */
    val = SET_MASKED(val, 0xf0fc0040, s->RxConfig);

    s->RxConfig = val;

    /* reset buffer size and read/write pointers */
    rtl8139_reset_rxring(s, 8192 << ((s->RxConfig >> 11) & 0x3));

    DPRINTF("RxConfig write reset buffer size to %d\n", s->RxBufferSize);
}


static inline void pcnet_tmd_load(PCNetState *s, struct pcnet_TMD *tmd,
                                  hwaddr addr)
{
    if (!BCR_SSIZE32(s)) {
        struct {
            uint32_t tbadr;
            int16_t length;
            int16_t status;
        } xda;
        s->phys_mem_read(s->dma_opaque, addr, (void *)&xda, sizeof(xda), 0);
        tmd->tbadr = le32_to_cpu(xda.tbadr) & 0xffffff;
        tmd->length = le16_to_cpu(xda.length);
        tmd->status = (le32_to_cpu(xda.tbadr) >> 16) & 0xff00;
        tmd->misc = le16_to_cpu(xda.status) << 16;
        tmd->res = 0;
    } else {
        s->phys_mem_read(s->dma_opaque, addr, (void *)tmd, sizeof(*tmd), 0);
        le32_to_cpus(&tmd->tbadr);
        le16_to_cpus((uint16_t *)&tmd->length);
        le16_to_cpus((uint16_t *)&tmd->status);
        le32_to_cpus(&tmd->misc);
        le32_to_cpus(&tmd->res);
        if (BCR_SWSTYLE(s) == 3) {
            uint32_t tmp = tmd->tbadr;
            tmd->tbadr = tmd->misc;
            tmd->misc = tmp;
        }
    }
}


static void gem_set_isr(CadenceGEMState *s, int q, uint32_t flag)
{
    if (q == 0) {
        s->regs[GEM_ISR] |= flag & ~(s->regs[GEM_IMR]);
    } else {
        s->regs[GEM_INT_Q1_STATUS + q - 1] |= flag &
                                      ~(s->regs[GEM_INT_Q1_MASK + q - 1]);
    }
}


static inline void rx_desc_set_ownership(uint32_t *desc)
{
    desc[0] |= DESC_0_RX_OWNERSHIP;
}


static inline void rx_desc_set_eof(uint32_t *desc)
{
    desc[1] |= DESC_1_RX_EOF;
}


static uint64_t lan9118_16bit_mode_read(void *opaque, hwaddr offset,
                                        unsigned size)
{
    switch (size) {
    case 2:
        return lan9118_readw(opaque, offset);
    case 4:
        return lan9118_readl(opaque, offset, size);
    }

    hw_error("lan9118_read: Bad size 0x%x\n", size);
    return 0;
}


static void extend_tile_hint(int x, int y, int tw, int th, hint_t *hint) {
	int w = dpy_x - x;
	int h = dpy_y - y;

	if (w > tw) {
		w = tw;
	}
	if (h > th) {
		h = th;
	}

	if (hint->x > x) {			/* extend to the left */
		hint->w += hint->x - x;
		hint->x = x;
	}
	if (hint->y > y) {			/* extend upward */
		hint->h += hint->y - y;
		hint->y = y;
	}

	if (hint->x + hint->w < x + w) {	/* extend to the right */
		hint->w = x + w - hint->x;
	}
	if (hint->y + hint->h < y + h) {	/* extend downward */
		hint->h = y + h - hint->y;
	}
}


void shm_delete(XShmSegmentInfo *shm) {
#if HAVE_XSHM
	if (getenv("X11VNC_SHM_DEBUG")) fprintf(stderr, "shm_delete:    %p\n", (void *) shm);
	if (shm != NULL && shm->shmaddr != (char *) -1) {
		shmdt(shm->shmaddr);
	}
	if (shm != NULL && shm->shmid != -1) {
		shmctl(shm->shmid, IPC_RMID, 0);
	}
	if (shm != NULL) {
		shm->shmaddr = (char *) -1;
		shm->shmid = -1;
	}
#else
	if (!shm) {}
#endif
}


static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
                     mode_t mode)
{
    lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
}


static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
                         struct fuse_bufvec *in_buf, off_t off,
                         struct fuse_file_info *fi)
{
    (void)ino;
    ssize_t res;
    struct fuse_bufvec out_buf = FUSE_BUFVEC_INIT(fuse_buf_size(in_buf));
    bool cap_fsetid_dropped = false;

    out_buf.buf[0].flags = FUSE_BUF_IS_FD | FUSE_BUF_FD_SEEK;
    out_buf.buf[0].fd = lo_fi_fd(req, fi);
    out_buf.buf[0].pos = off;

    fuse_log(FUSE_LOG_DEBUG,
             "lo_write_buf(ino=%" PRIu64 ", size=%zd, off=%lu)\n", ino,
             out_buf.buf[0].size, (unsigned long)off);

    /*
     * If kill_priv is set, drop CAP_FSETID which should lead to kernel
     * clearing setuid/setgid on file.
     */
    if (fi->kill_priv) {
        res = drop_effective_cap("FSETID", &cap_fsetid_dropped);
        if (res != 0) {
            fuse_reply_err(req, res);
            return;
        }
    }

    res = fuse_buf_copy(&out_buf, in_buf);
    if (res < 0) {
        fuse_reply_err(req, -res);
    } else {
        fuse_reply_write(req, (size_t)res);
    }

    if (cap_fsetid_dropped) {
        res = gain_effective_cap("FSETID");
        if (res) {
            fuse_log(FUSE_LOG_ERR, "Failed to gain CAP_FSETID\n");
        }
    }
}


static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
{
    if (!inode) {
        return;
    }

    assert(inode->nlookup >= n);
    inode->nlookup -= n;
    if (!inode->nlookup) {
        lo_map_remove(&lo->ino_map, inode->fuse_ino);
        g_hash_table_remove(lo->inodes, &inode->key);
        if (g_hash_table_size(inode->posix_locks)) {
            fuse_log(FUSE_LOG_WARNING, "Hash table is not empty\n");
        }
        g_hash_table_destroy(inode->posix_locks);
        pthread_mutex_destroy(&inode->plock_mutex);

        /* Drop our refcount from lo_do_lookup() */
        lo_inode_put(lo, &inode);
    }
}


static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
                      mode_t mode, struct fuse_file_info *fi)
{
    int fd;
    struct lo_data *lo = lo_data(req);
    struct lo_inode *parent_inode;
    struct fuse_entry_param e;
    int err;
    struct lo_cred old = {};

    fuse_log(FUSE_LOG_DEBUG, "lo_create(parent=%" PRIu64 ", name=%s)\n", parent,
             name);

    if (!is_safe_path_component(name)) {
        fuse_reply_err(req, EINVAL);
        return;
    }

    parent_inode = lo_inode(req, parent);
    if (!parent_inode) {
        fuse_reply_err(req, EBADF);
        return;
    }

    err = lo_change_cred(req, &old);
    if (err) {
        goto out;
    }

    update_open_flags(lo->writeback, lo->allow_direct_io, fi);

    fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW,
                mode);
    err = fd == -1 ? errno : 0;
    lo_restore_cred(&old);

    if (!err) {
        ssize_t fh;

        pthread_mutex_lock(&lo->mutex);
        fh = lo_add_fd_mapping(req, fd);
        pthread_mutex_unlock(&lo->mutex);
        if (fh == -1) {
            close(fd);
            err = ENOMEM;
            goto out;
        }

        fi->fh = fh;
        err = lo_do_lookup(req, parent, name, &e);
    }
    if (lo->cache == CACHE_NONE) {
        fi->direct_io = 1;
    } else if (lo->cache == CACHE_ALWAYS) {
        fi->keep_cache = 1;
    }

out:
    lo_inode_put(lo, &parent_inode);

    if (err) {
        fuse_reply_err(req, err);
    } else {
        fuse_reply_create(req, &e, fi);
    }
}


static void add_xattrmap_entry(struct lo_data *lo,
                               const XattrMapEntry *new_entry)
{
    XattrMapEntry *res = g_realloc_n(lo->xattr_map_list,
                                     lo->xattr_map_nentries + 1,
                                     sizeof(XattrMapEntry));
    res[lo->xattr_map_nentries++] = *new_entry;

    lo->xattr_map_list = res;
}


static int load_capng(void)
{
    if (!cap_loaded) {
        pthread_mutex_lock(&cap.mutex);
        capng_restore_state(&cap.saved);
        /*
         * restore_state free's the saved copy
         * so make another.
         */
        cap.saved = capng_save_state();
        if (!cap.saved) {
            pthread_mutex_unlock(&cap.mutex);
            fuse_log(FUSE_LOG_ERR, "capng_save_state (thread)\n");
            return -EINVAL;
        }
        pthread_mutex_unlock(&cap.mutex);

        /*
         * We want to use the loaded state for our pid,
         * not the original
         */
        capng_setpid(syscall(SYS_gettid));
        cap_loaded = true;
    }
    return 0;
}


static struct lo_map_elem *lo_map_get(struct lo_map *map, size_t key)
{
    if (key >= map->nelems) {
        return NULL;
    }
    if (!map->elems[key].in_use) {
        return NULL;
    }
    return &map->elems[key];
}


static void lo_flock(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi,
                     int op)
{
    int res;
    (void)ino;

    res = flock(lo_fi_fd(req, fi), op);

    fuse_reply_err(req, res == -1 ? errno : 0);
}


static void setup_mounts(const char *source)
{
    int oldroot;
    int newroot;

    if (mount(source, source, NULL, MS_BIND | MS_REC, NULL) < 0) {
        fuse_log(FUSE_LOG_ERR, "mount(%s, %s, MS_BIND): %m\n", source, source);
        exit(1);
    }

    /* This magic is based on lxc's lxc_pivot_root() */
    oldroot = open("/", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
    if (oldroot < 0) {
        fuse_log(FUSE_LOG_ERR, "open(/): %m\n");
        exit(1);
    }

    newroot = open(source, O_DIRECTORY | O_RDONLY | O_CLOEXEC);
    if (newroot < 0) {
        fuse_log(FUSE_LOG_ERR, "open(%s): %m\n", source);
        exit(1);
    }

    if (fchdir(newroot) < 0) {
        fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n");
        exit(1);
    }

    if (syscall(__NR_pivot_root, ".", ".") < 0) {
        fuse_log(FUSE_LOG_ERR, "pivot_root(., .): %m\n");
        exit(1);
    }

    if (fchdir(oldroot) < 0) {
        fuse_log(FUSE_LOG_ERR, "fchdir(oldroot): %m\n");
        exit(1);
    }

    if (mount("", ".", "", MS_SLAVE | MS_REC, NULL) < 0) {
        fuse_log(FUSE_LOG_ERR, "mount(., MS_SLAVE | MS_REC): %m\n");
        exit(1);
    }

    if (umount2(".", MNT_DETACH) < 0) {
        fuse_log(FUSE_LOG_ERR, "umount2(., MNT_DETACH): %m\n");
        exit(1);
    }

    if (fchdir(newroot) < 0) {
        fuse_log(FUSE_LOG_ERR, "fchdir(newroot): %m\n");
        exit(1);
    }

    close(newroot);
    close(oldroot);
}


static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
                             const char *name, mode_t mode, dev_t rdev,
                             const char *link)
{
    int res;
    int saverr;
    struct lo_data *lo = lo_data(req);
    struct lo_inode *dir;
    struct fuse_entry_param e;
    struct lo_cred old = {};

    if (!is_safe_path_component(name)) {
        fuse_reply_err(req, EINVAL);
        return;
    }

    dir = lo_inode(req, parent);
    if (!dir) {
        fuse_reply_err(req, EBADF);
        return;
    }

    saverr = lo_change_cred(req, &old);
    if (saverr) {
        goto out;
    }

    res = mknod_wrapper(dir->fd, name, link, mode, rdev);

    saverr = errno;

    lo_restore_cred(&old);

    if (res == -1) {
        goto out;
    }

    saverr = lo_do_lookup(req, parent, name, &e);
    if (saverr) {
        goto out;
    }

    fuse_log(FUSE_LOG_DEBUG, "  %lli/%s -> %lli\n", (unsigned long long)parent,
             name, (unsigned long long)e.ino);

    fuse_reply_entry(req, &e);
    lo_inode_put(lo, &dir);
    return;

out:
    lo_inode_put(lo, &dir);
    fuse_reply_err(req, saverr);
}


static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
                        struct fuse_file_info *fi)
{
    int res;
    struct lo_dirp *d;
    int fd;

    (void)ino;

    d = lo_dirp(req, fi);
    if (!d) {
        fuse_reply_err(req, EBADF);
        return;
    }

    fd = dirfd(d->dp);
    if (datasync) {
        res = fdatasync(fd);
    } else {
        res = fsync(fd);
    }

    lo_dirp_put(&d);

    fuse_reply_err(req, res == -1 ? errno : 0);
}


static void lo_map_remove(struct lo_map *map, size_t key)
{
    struct lo_map_elem *elem;

    if (key >= map->nelems) {
        return;
    }

    elem = &map->elems[key];
    if (!elem->in_use) {
        return;
    }

    elem->in_use = false;

    elem->freelist = map->freelist;
    map->freelist = key;
}


static void lo_readlink(fuse_req_t req, fuse_ino_t ino)
{
    char buf[PATH_MAX + 1];
    int res;

    res = readlinkat(lo_fd(req, ino), "", buf, sizeof(buf));
    if (res == -1) {
        return (void)fuse_reply_err(req, errno);
    }

    if (res == sizeof(buf)) {
        return (void)fuse_reply_err(req, ENAMETOOLONG);
    }

    buf[res] = '\0';

    fuse_reply_readlink(req, buf);
}


static void lo_removexattr(fuse_req_t req, fuse_ino_t ino, const char *name)
{
    char procname[64];
    struct lo_data *lo = lo_data(req);
    struct lo_inode *inode;
    ssize_t ret;
    int saverr;
    int fd = -1;

    inode = lo_inode(req, ino);
    if (!inode) {
        fuse_reply_err(req, EBADF);
        return;
    }

    saverr = ENOSYS;
    if (!lo_data(req)->xattr) {
        goto out;
    }

    fuse_log(FUSE_LOG_DEBUG, "lo_removexattr(ino=%" PRIu64 ", name=%s)\n", ino,
             name);

    sprintf(procname, "%i", inode->fd);
    if (S_ISREG(inode->filetype) || S_ISDIR(inode->filetype)) {
        fd = openat(lo->proc_self_fd, procname, O_RDONLY);
        if (fd < 0) {
            saverr = errno;
            goto out;
        }
        ret = fremovexattr(fd, name);
    } else {
        /* fchdir should not fail here */
        assert(fchdir(lo->proc_self_fd) == 0);
        ret = removexattr(procname, name);
        assert(fchdir(lo->root.fd) == 0);
    }

    saverr = ret == -1 ? errno : 0;

out:
    if (fd >= 0) {
        close(fd);
    }

    lo_inode_put(lo, &inode);
    fuse_reply_err(req, saverr);
}


static guint lo_key_hash(gconstpointer key)
{
    const struct lo_key *lkey = key;

    return (guint)lkey->ino + (guint)lkey->dev;
}


static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
    int res;
    (void)ino;
    struct lo_inode *inode;
    struct lo_data *lo = lo_data(req);

    inode = lo_inode(req, ino);
    if (!inode) {
        fuse_reply_err(req, EBADF);
        return;
    }

    if (!S_ISREG(inode->filetype)) {
        lo_inode_put(lo, &inode);
        fuse_reply_err(req, EBADF);
        return;
    }

    /* An fd is going away. Cleanup associated posix locks */
    if (lo->posix_lock) {
        pthread_mutex_lock(&inode->plock_mutex);
        g_hash_table_remove(inode->posix_locks,
            GUINT_TO_POINTER(fi->lock_owner));
        pthread_mutex_unlock(&inode->plock_mutex);
    }
    res = close(dup(lo_fi_fd(req, fi)));
    lo_inode_put(lo, &inode);
    fuse_reply_err(req, res == -1 ? errno : 0);
}


static void setup_root(struct lo_data *lo, struct lo_inode *root)
{
    int fd, res;
    struct stat stat;
    uint64_t mnt_id;

    fd = open("/", O_PATH);
    if (fd == -1) {
        fuse_log(FUSE_LOG_ERR, "open(%s, O_PATH): %m\n", lo->source);
        exit(1);
    }

    res = do_statx(lo, fd, "", &stat, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW,
                   &mnt_id);
    if (res == -1) {
        fuse_log(FUSE_LOG_ERR, "fstatat(%s): %m\n", lo->source);
        exit(1);
    }

    root->filetype = S_IFDIR;
    root->fd = fd;
    root->key.ino = stat.st_ino;
    root->key.dev = stat.st_dev;
    root->key.mnt_id = mnt_id;
    root->nlookup = 2;
    g_atomic_int_set(&root->refcount, 2);
    if (lo->posix_lock) {
        pthread_mutex_init(&root->plock_mutex, NULL);
        root->posix_locks = g_hash_table_new_full(
            g_direct_hash, g_direct_equal, NULL, posix_locks_value_destroy);
    }
}


static void setup_capabilities(char *modcaps_in)
{
    char *modcaps = modcaps_in;
    pthread_mutex_lock(&cap.mutex);
    capng_restore_state(&cap.saved);

    /*
     * Add to allowlist file system-related capabilities that are needed for a
     * file server to act like root.  Drop everything else like networking and
     * sysadmin capabilities.
     *
     * Exclusions:
     * 1. CAP_LINUX_IMMUTABLE is not included because it's only used via ioctl
     *    and we don't support that.
     * 2. CAP_MAC_OVERRIDE is not included because it only seems to be
     *    used by the Smack LSM.  Omit it until there is demand for it.
     */
    capng_setpid(syscall(SYS_gettid));
    capng_clear(CAPNG_SELECT_BOTH);
    if (capng_updatev(CAPNG_ADD, CAPNG_PERMITTED | CAPNG_EFFECTIVE,
            CAP_CHOWN,
            CAP_DAC_OVERRIDE,
            CAP_FOWNER,
            CAP_FSETID,
            CAP_SETGID,
            CAP_SETUID,
            CAP_MKNOD,
            CAP_SETFCAP,
            -1)) {
        fuse_log(FUSE_LOG_ERR, "%s: capng_updatev failed\n", __func__);
        exit(1);
    }

    /*
     * The modcaps option is a colon separated list of caps,
     * each preceded by either + or -.
     */
    while (modcaps) {
        capng_act_t action;
        int cap;

        char *next = strchr(modcaps, ':');
        if (next) {
            *next = '\0';
            next++;
        }

        switch (modcaps[0]) {
        case '+':
            action = CAPNG_ADD;
            break;

        case '-':
            action = CAPNG_DROP;
            break;

        default:
            fuse_log(FUSE_LOG_ERR,
                     "%s: Expecting '+'/'-' in modcaps but found '%c'\n",
                     __func__, modcaps[0]);
            exit(1);
        }
        cap = capng_name_to_capability(modcaps + 1);
        if (cap < 0) {
            fuse_log(FUSE_LOG_ERR, "%s: Unknown capability '%s'\n", __func__,
                     modcaps);
            exit(1);
        }
        if (capng_update(action, CAPNG_PERMITTED | CAPNG_EFFECTIVE, cap)) {
            fuse_log(FUSE_LOG_ERR, "%s: capng_update failed for '%s'\n",
                     __func__, modcaps);
            exit(1);
        }

        modcaps = next;
    }
    g_free(modcaps_in);

    if (capng_apply(CAPNG_SELECT_BOTH)) {
        fuse_log(FUSE_LOG_ERR, "%s: capng_apply failed\n", __func__);
        exit(1);
    }

    cap.saved = capng_save_state();
    if (!cap.saved) {
        fuse_log(FUSE_LOG_ERR, "%s: capng_save_state failed\n", __func__);
        exit(1);
    }
    pthread_mutex_unlock(&cap.mutex);
}


static int xattr_map_server(const struct lo_data *lo, const char *server_name,
                            const char **out_name)
{
    size_t i;
    const char *end;

    for (i = 0; i < lo->xattr_map_nentries; i++) {
        const XattrMapEntry *cur_entry = lo->xattr_map_list + i;

        if ((cur_entry->flags & XATTR_MAP_FLAG_SERVER) &&
            (strstart(server_name, cur_entry->prepend, &end))) {
            if (cur_entry->flags & XATTR_MAP_FLAG_BAD) {
                return -ENODATA;
            }
            if (cur_entry->flags & XATTR_MAP_FLAG_OK) {
                *out_name = server_name;
                return 0;
            }
            if (cur_entry->flags & XATTR_MAP_FLAG_PREFIX) {
                /* Remove prefix */
                *out_name = end;
                return 0;
            }
        }
    }

    return -ENODATA;
}


static int drop_security_capability(const struct lo_data *lo, int fd)
{
    if (!lo->xattr_security_capability) {
        /* We didn't remap the name, let the host kernel do it */
        return 0;
    }
    if (!fremovexattr(fd, lo->xattr_security_capability)) {
        /* All good */
        return 0;
    }

    switch (errno) {
    case ENODATA:
        /* Attribute didn't exist, that's fine */
        return 0;

    case ENOTSUP:
        /* FS didn't support attribute anyway, also fine */
        return 0;

    default:
        /* Hmm other error */
        return errno;
    }
}


static double *GenerateCoefficients(const Image *image,
  DistortMethod *method,const size_t number_arguments,const double *arguments,
  size_t number_values,ExceptionInfo *exception)
{
  double
    *coeff;

  size_t
    i;

  size_t
    number_coefficients, /* number of coefficients to return (array size) */
    cp_size,      /* number floating point numbers per control point */
    cp_x,cp_y,    /* the x,y indexes for control point */
    cp_values;    /* index of values for this control point */
    /* number_values   Number of values given per control point */

  if ( number_values == 0 ) {
    /* Image distortion using control points (or other distortion)
       That is generate a mapping so that   x,y->u,v   given  u,v,x,y
    */
    number_values = 2;   /* special case: two values of u,v */
    cp_values = 0;       /* the values i,j are BEFORE the destination CP x,y */
    cp_x = 2;            /* location of x,y in input control values */
    cp_y = 3;
    /* NOTE: cp_values, also used for later 'reverse map distort' tests */
  }
  else {
    cp_x = 0;            /* location of x,y in input control values */
    cp_y = 1;
    cp_values = 2;       /* and the other values are after x,y */
    /* Typically in this case the values are R,G,B color values */
  }
  cp_size = number_values+2; /* each CP defintion involves this many numbers */

  /* If not enough control point pairs are found for specific distortions
     fall back to Affine distortion (allowing 0 to 3 point pairs)
  */
  if ( number_arguments < 4*cp_size &&
       (  *method == BilinearForwardDistortion
       || *method == BilinearReverseDistortion
       || *method == PerspectiveDistortion
       ) )
    *method = AffineDistortion;

  number_coefficients=0;
  switch (*method) {
    case AffineDistortion:
    case RigidAffineDistortion:
    /* also BarycentricColorInterpolate: */
      number_coefficients=3*number_values;
      break;
    case PolynomialDistortion:
      /* number of coefficents depend on the given polynomal 'order' */
      i = poly_number_terms(arguments[0]);
      number_coefficients = 2 + i*number_values;
      if ( i == 0 ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
                   "InvalidArgument","%s : '%s'","Polynomial",
                   "Invalid order, should be interger 1 to 5, or 1.5");
        return((double *) NULL);
      }
      if ( number_arguments < 1+i*cp_size ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
               "InvalidArgument", "%s : 'require at least %.20g CPs'",
               "Polynomial", (double) i);
        return((double *) NULL);
      }
      break;
    case BilinearReverseDistortion:
      number_coefficients=4*number_values;
      break;
    /*
      The rest are constants as they are only used for image distorts
    */
    case BilinearForwardDistortion:
      number_coefficients=10; /* 2*4 coeff plus 2 constants */
      cp_x = 0;        /* Reverse src/dest coords for forward mapping */
      cp_y = 1;
      cp_values = 2;
      break;
#if 0
    case QuadraterialDistortion:
      number_coefficients=19; /* BilinearForward + BilinearReverse */
#endif
      break;
    case ShepardsDistortion:
      number_coefficients=1;  /* The power factor to use */
      break;
    case ArcDistortion:
      number_coefficients=5;
      break;
    case ScaleRotateTranslateDistortion:
    case AffineProjectionDistortion:
    case Plane2CylinderDistortion:
    case Cylinder2PlaneDistortion:
      number_coefficients=6;
      break;
    case PolarDistortion:
    case DePolarDistortion:
      number_coefficients=8;
      break;
    case PerspectiveDistortion:
    case PerspectiveProjectionDistortion:
      number_coefficients=9;
      break;
    case BarrelDistortion:
    case BarrelInverseDistortion:
      number_coefficients=10;
      break;
    default:
      perror("unknown method given"); /* just fail assertion */
  }

  /* allocate the array of coefficients needed */
  coeff=(double *) AcquireQuantumMemory(number_coefficients,sizeof(*coeff));
  if (coeff == (double *) NULL)
    {
      (void) ThrowMagickException(exception,GetMagickModule(),
        ResourceLimitError,"MemoryAllocationFailed","%s",
        "GenerateCoefficients");
      return((double *) NULL);
    }

  /* zero out coefficients array */
  for (i=0; i < number_coefficients; i++)
    coeff[i] = 0.0;

  switch (*method)
  {
    case AffineDistortion:
    {
      /* Affine Distortion
           v =  c0*x + c1*y + c2
         for each 'value' given

         Input Arguments are sets of control points...
         For Distort Images    u,v, x,y  ...
         For Sparse Gradients  x,y, r,g,b  ...
      */
      if ( number_arguments%cp_size != 0 ||
           number_arguments < cp_size ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
               "InvalidArgument", "%s : 'require at least %.20g CPs'",
               "Affine", 1.0);
        coeff=(double *) RelinquishMagickMemory(coeff);
        return((double *) NULL);
      }
      /* handle special cases of not enough arguments */
      if ( number_arguments == cp_size ) {
        /* Only 1 CP Set Given */
        if ( cp_values == 0 ) {
          /* image distortion - translate the image */
          coeff[0] = 1.0;
          coeff[2] = arguments[0] - arguments[2];
          coeff[4] = 1.0;
          coeff[5] = arguments[1] - arguments[3];
        }
        else {
          /* sparse gradient - use the values directly */
          for (i=0; i<number_values; i++)
            coeff[i*3+2] = arguments[cp_values+i];
        }
      }
      else {
        /* 2 or more points (usally 3) given.
           Solve a least squares simultaneous equation for coefficients.
        */
        double
          **matrix,
          **vectors,
          terms[3];

        MagickBooleanType
          status;

        /* create matrix, and a fake vectors matrix */
        matrix=AcquireMagickMatrix(3UL,3UL);
        vectors=(double **) AcquireQuantumMemory(number_values,
          sizeof(*vectors));
        if (matrix == (double **) NULL || vectors == (double **) NULL)
        {
          matrix  = RelinquishMagickMatrix(matrix, 3UL);
          vectors = (double **) RelinquishMagickMemory(vectors);
          coeff   = (double *) RelinquishMagickMemory(coeff);
          (void) ThrowMagickException(exception,GetMagickModule(),
                  ResourceLimitError,"MemoryAllocationFailed",
                  "%s", "DistortCoefficients");
          return((double *) NULL);
        }
        /* fake a number_values x3 vectors matrix from coefficients array */
        for (i=0; i < number_values; i++)
          vectors[i] = &(coeff[i*3]);
        /* Add given control point pairs for least squares solving */
        for (i=0; i < number_arguments; i+=cp_size) {
          terms[0] = arguments[i+cp_x];  /* x */
          terms[1] = arguments[i+cp_y];  /* y */
          terms[2] = 1;                  /* 1 */
          LeastSquaresAddTerms(matrix,vectors,terms,
                   &(arguments[i+cp_values]),3UL,number_values);
        }
        if ( number_arguments == 2*cp_size ) {
          /* Only two pairs were given, but we need 3 to solve the affine.
             Fake extra coordinates by rotating p1 around p0 by 90 degrees.
               x2 = x0 - (y1-y0)   y2 = y0 + (x1-x0)
           */
          terms[0] = arguments[cp_x]
                   - ( arguments[cp_size+cp_y] - arguments[cp_y] ); /* x2 */
          terms[1] = arguments[cp_y] +
                   + ( arguments[cp_size+cp_x] - arguments[cp_x] ); /* y2 */
          terms[2] = 1;                                             /* 1 */
          if ( cp_values == 0 ) {
            /* Image Distortion - rotate the u,v coordients too */
            double
              uv2[2];
            uv2[0] = arguments[0] - arguments[5] + arguments[1];   /* u2 */
            uv2[1] = arguments[1] + arguments[4] - arguments[0];   /* v2 */
            LeastSquaresAddTerms(matrix,vectors,terms,uv2,3UL,2UL);
          }
          else {
            /* Sparse Gradient - use values of p0 for linear gradient */
            LeastSquaresAddTerms(matrix,vectors,terms,
                  &(arguments[cp_values]),3UL,number_values);
          }
        }
        /* Solve for LeastSquares Coefficients */
        status=GaussJordanElimination(matrix,vectors,3UL,number_values);
        matrix = RelinquishMagickMatrix(matrix, 3UL);
        vectors = (double **) RelinquishMagickMemory(vectors);
        if ( status == MagickFalse ) {
          coeff = (double *) RelinquishMagickMemory(coeff);
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
              "InvalidArgument","%s : 'Unsolvable Matrix'",
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
          return((double *) NULL);
        }
      }
      return(coeff);
    }
    case RigidAffineDistortion:
    {
      double
        inverse[6],
        **matrix,
        terms[5],
        *vectors[1];

      MagickBooleanType
        status;

      /*
        Rigid affine (also known as a Euclidean transform), restricts affine
        coefficients to 4 (S, R, Tx, Ty) with Sy=Sx and Ry = -Rx so that one has
        only scale, rotation and translation. No skew.
      */
      if (((number_arguments % cp_size) != 0) || (number_arguments < cp_size))
        {
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "InvalidArgument", "%s : 'require at least %.20g CPs'",
            CommandOptionToMnemonic(MagickDistortOptions,*method),2.0);
          coeff=(double *) RelinquishMagickMemory(coeff);
          return((double *) NULL);
        }
      /*
        Rigid affine requires a 4x4 least-squares matrix (zeroed).
      */
      matrix=AcquireMagickMatrix(4UL,4UL);
      if (matrix == (double **) NULL)
        {
          coeff=(double *) RelinquishMagickMemory(coeff);
          (void) ThrowMagickException(exception,GetMagickModule(),
            ResourceLimitError,"MemoryAllocationFailed","%s",
            CommandOptionToMnemonic(MagickDistortOptions,*method));
          return((double *) NULL);
        }
      /*
        Add control points for least squares solving.
      */
      vectors[0]=(&(coeff[0]));
      for (i=0; i < number_arguments; i+=4)
      {
        terms[0]=arguments[i+0];
        terms[1]=(-arguments[i+1]);
        terms[2]=1.0;
        terms[3]=0.0;
        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+2]),4UL,1UL);
        terms[0]=arguments[i+1];
        terms[1]=arguments[i+0];
        terms[2]=0.0;
        terms[3]=1.0;
        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+3]),4UL,1UL);
      }
      /*
        Solve for least-squares coefficients.
      */
      status=GaussJordanElimination(matrix,vectors,4UL,1UL);
      matrix=RelinquishMagickMatrix(matrix,4UL);
      if (status == MagickFalse)
        {
          coeff=(double *) RelinquishMagickMemory(coeff);
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "InvalidArgument","%s : 'Unsolvable Matrix'",
            CommandOptionToMnemonic(MagickDistortOptions,*method));
          return((double *) NULL);
        }
      /*
        Convert (S, R, Tx, Ty) to an affine projection.
      */
      inverse[0]=coeff[0];
      inverse[1]=coeff[1];
      inverse[2]=(-coeff[1]);
      inverse[3]=coeff[0];
      inverse[4]=coeff[2];
      inverse[5]=coeff[3];
      AffineArgsToCoefficients(inverse);
      InvertAffineCoefficients(inverse,coeff);
      *method=AffineDistortion;
      return(coeff);
    }
    case AffineProjectionDistortion:
    {
      /*
        Arguments: Affine Matrix (forward mapping)
        Arguments  sx, rx, ry, sy, tx, ty
        Where      u = sx*x + ry*y + tx
                   v = rx*x + sy*y + ty

        Returns coefficients (in there inverse form) ordered as...
             sx ry tx  rx sy ty

        AffineProjection Distortion Notes...
           + Will only work with a 2 number_values for Image Distortion
           + Can not be used for generating a sparse gradient (interpolation)
      */
      double inverse[8];
      if (number_arguments != 6) {
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
              "InvalidArgument","%s : 'Needs 6 coeff values'",
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
        return((double *) NULL);
      }
      /* FUTURE: trap test for sx*sy-rx*ry == 0 (determinant = 0, no inverse) */
      for(i=0; i<6UL; i++ )
        inverse[i] = arguments[i];
      AffineArgsToCoefficients(inverse); /* map into coefficents */
      InvertAffineCoefficients(inverse, coeff); /* invert */
      *method = AffineDistortion;

      return(coeff);
    }
    case ScaleRotateTranslateDistortion:
    {
      /* Scale, Rotate and Translate Distortion
         An alternative Affine Distortion
         Argument options, by number of arguments given:
           7: x,y, sx,sy, a, nx,ny
           6: x,y,   s,   a, nx,ny
           5: x,y, sx,sy, a
           4: x,y,   s,   a
           3: x,y,        a
           2:        s,   a
           1:             a
         Where actions are (in order of application)
            x,y     'center' of transforms     (default = image center)
            sx,sy   scale image by this amount (default = 1)
            a       angle of rotation          (argument required)
            nx,ny   move 'center' here         (default = x,y or no movement)
         And convert to affine mapping coefficients

         ScaleRotateTranslate Distortion Notes...
           + Does not use a set of CPs in any normal way
           + Will only work with a 2 number_valuesal Image Distortion
           + Cannot be used for generating a sparse gradient (interpolation)
      */
      double
        cosine, sine,
        x,y,sx,sy,a,nx,ny;

      /* set default center, and default scale */
      x = nx = (double)(image->columns)/2.0 + (double)image->page.x;
      y = ny = (double)(image->rows)/2.0    + (double)image->page.y;
      sx = sy = 1.0;
      switch ( number_arguments ) {
      case 0:
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
              "InvalidArgument","%s : 'Needs at least 1 argument'",
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
        return((double *) NULL);
      case 1:
        a = arguments[0];
        break;
      case 2:
        sx = sy = arguments[0];
        a = arguments[1];
        break;
      default:
        x = nx = arguments[0];
        y = ny = arguments[1];
        switch ( number_arguments ) {
        case 3:
          a = arguments[2];
          break;
        case 4:
          sx = sy = arguments[2];
          a = arguments[3];
          break;
        case 5:
          sx = arguments[2];
          sy = arguments[3];
          a = arguments[4];
          break;
        case 6:
          sx = sy = arguments[2];
          a = arguments[3];
          nx = arguments[4];
          ny = arguments[5];
          break;
        case 7:
          sx = arguments[2];
          sy = arguments[3];
          a = arguments[4];
          nx = arguments[5];
          ny = arguments[6];
          break;
        default:
          coeff = (double *) RelinquishMagickMemory(coeff);
          (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
              "InvalidArgument","%s : 'Too Many Arguments (7 or less)'",
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
          return((double *) NULL);
        }
        break;
      }
      /* Trap if sx or sy == 0 -- image is scaled out of existance! */
      if ( fabs(sx) < MagickEpsilon || fabs(sy) < MagickEpsilon ) {
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
              "InvalidArgument","%s : 'Zero Scale Given'",
              CommandOptionToMnemonic(MagickDistortOptions, *method) );
        return((double *) NULL);
      }
      /* Save the given arguments as an affine distortion */
      a=DegreesToRadians(a); cosine=cos(a); sine=sin(a);

      *method = AffineDistortion;
      coeff[0]=cosine/sx;
      coeff[1]=sine/sx;
      coeff[2]=x-nx*coeff[0]-ny*coeff[1];
      coeff[3]=(-sine)/sy;
      coeff[4]=cosine/sy;
      coeff[5]=y-nx*coeff[3]-ny*coeff[4];
      return(coeff);
    }
    case PerspectiveDistortion:
    { /*
         Perspective Distortion (a ratio of affine distortions)

                p(x,y)    c0*x + c1*y + c2
            u = ------ = ------------------
                r(x,y)    c6*x + c7*y + 1

                q(x,y)    c3*x + c4*y + c5
            v = ------ = ------------------
                r(x,y)    c6*x + c7*y + 1

           c8 = Sign of 'r', or the denominator affine, for the actual image.
                This determines what part of the distorted image is 'ground'
                side of the horizon, the other part is 'sky' or invalid.
                Valid values are  +1.0  or  -1.0  only.

         Input Arguments are sets of control points...
         For Distort Images    u,v, x,y  ...
         For Sparse Gradients  x,y, r,g,b  ...

         Perspective Distortion Notes...
           + Can be thought of as ratio of  3 affine transformations
           + Not separatable: r() or c6 and c7 are used by both equations
           + All 8 coefficients must be determined simultaniously
           + Will only work with a 2 number_valuesal Image Distortion
           + Can not be used for generating a sparse gradient (interpolation)
           + It is not linear, but is simple to generate an inverse
           + All lines within an image remain lines.
           + but distances between points may vary.
      */
      double
        **matrix,
        *vectors[1],
        terms[8];

      size_t
        cp_u = cp_values,
        cp_v = cp_values+1;

      MagickBooleanType
        status;

      if ( number_arguments%cp_size != 0 ||
           number_arguments < cp_size*4 ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
              "InvalidArgument", "%s : 'require at least %.20g CPs'",
              CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
        coeff=(double *) RelinquishMagickMemory(coeff);
        return((double *) NULL);
      }
      /* fake 1x8 vectors matrix directly using the coefficients array */
      vectors[0] = &(coeff[0]);
      /* 8x8 least-squares matrix (zeroed) */
      matrix = AcquireMagickMatrix(8UL,8UL);
      if (matrix == (double **) NULL) {
        coeff=(double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),
                  ResourceLimitError,"MemoryAllocationFailed",
                  "%s", "DistortCoefficients");
        return((double *) NULL);
      }
      /* Add control points for least squares solving */
      for (i=0; i < number_arguments; i+=4) {
        terms[0]=arguments[i+cp_x];            /*   c0*x   */
        terms[1]=arguments[i+cp_y];            /*   c1*y   */
        terms[2]=1.0;                          /*   c2*1   */
        terms[3]=0.0;
        terms[4]=0.0;
        terms[5]=0.0;
        terms[6]=-terms[0]*arguments[i+cp_u];  /* 1/(c6*x) */
        terms[7]=-terms[1]*arguments[i+cp_u];  /* 1/(c7*y) */
        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_u]),
            8UL,1UL);

        terms[0]=0.0;
        terms[1]=0.0;
        terms[2]=0.0;
        terms[3]=arguments[i+cp_x];           /*   c3*x   */
        terms[4]=arguments[i+cp_y];           /*   c4*y   */
        terms[5]=1.0;                         /*   c5*1   */
        terms[6]=-terms[3]*arguments[i+cp_v]; /* 1/(c6*x) */
        terms[7]=-terms[4]*arguments[i+cp_v]; /* 1/(c7*y) */
        LeastSquaresAddTerms(matrix,vectors,terms,&(arguments[i+cp_v]),
            8UL,1UL);
      }
      /* Solve for LeastSquares Coefficients */
      status=GaussJordanElimination(matrix,vectors,8UL,1UL);
      matrix = RelinquishMagickMatrix(matrix, 8UL);
      if ( status == MagickFalse ) {
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "InvalidArgument","%s : 'Unsolvable Matrix'",
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
        return((double *) NULL);
      }
      /*
        Calculate 9'th coefficient! The ground-sky determination.
        What is sign of the 'ground' in r() denominator affine function?
        Just use any valid image coordinate (first control point) in
        destination for determination of what part of view is 'ground'.
      */
      coeff[8] = coeff[6]*arguments[cp_x]
                      + coeff[7]*arguments[cp_y] + 1.0;
      coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;

      return(coeff);
    }
    case PerspectiveProjectionDistortion:
    {
      /*
        Arguments: Perspective Coefficents (forward mapping)
      */
      if (number_arguments != 8) {
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
              "InvalidArgument", "%s : 'Needs 8 coefficient values'",
              CommandOptionToMnemonic(MagickDistortOptions, *method));
        return((double *) NULL);
      }
      /* FUTURE: trap test  c0*c4-c3*c1 == 0  (determinate = 0, no inverse) */
      InvertPerspectiveCoefficients(arguments, coeff);
      /*
        Calculate 9'th coefficient! The ground-sky determination.
        What is sign of the 'ground' in r() denominator affine function?
        Just use any valid image cocodinate in destination for determination.
        For a forward mapped perspective the images 0,0 coord will map to
        c2,c5 in the distorted image, so set the sign of denominator of that.
      */
      coeff[8] = coeff[6]*arguments[2]
                           + coeff[7]*arguments[5] + 1.0;
      coeff[8] = (coeff[8] < 0.0) ? -1.0 : +1.0;
      *method = PerspectiveDistortion;

      return(coeff);
    }
    case BilinearForwardDistortion:
    case BilinearReverseDistortion:
    {
      /* Bilinear Distortion (Forward mapping)
            v = c0*x + c1*y + c2*x*y + c3;
         for each 'value' given

         This is actually a simple polynomial Distortion!  The difference
         however is when we need to reverse the above equation to generate a
         BilinearForwardDistortion (see below).

         Input Arguments are sets of control points...
         For Distort Images    u,v, x,y  ...
         For Sparse Gradients  x,y, r,g,b  ...

      */
      double
        **matrix,
        **vectors,
        terms[4];

      MagickBooleanType
        status;

      /* check the number of arguments */
      if ( number_arguments%cp_size != 0 ||
           number_arguments < cp_size*4 ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
              "InvalidArgument", "%s : 'require at least %.20g CPs'",
              CommandOptionToMnemonic(MagickDistortOptions, *method), 4.0);
        coeff=(double *) RelinquishMagickMemory(coeff);
        return((double *) NULL);
      }
      /* create matrix, and a fake vectors matrix */
      matrix=AcquireMagickMatrix(4UL,4UL);
      vectors=(double **) AcquireQuantumMemory(number_values,sizeof(*vectors));
      if (matrix == (double **) NULL || vectors == (double **) NULL)
      {
        matrix  = RelinquishMagickMatrix(matrix, 4UL);
        vectors = (double **) RelinquishMagickMemory(vectors);
        coeff   = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),
                ResourceLimitError,"MemoryAllocationFailed",
                "%s", "DistortCoefficients");
        return((double *) NULL);
      }
      /* fake a number_values x4 vectors matrix from coefficients array */
      for (i=0; i < number_values; i++)
        vectors[i] = &(coeff[i*4]);
      /* Add given control point pairs for least squares solving */
      for (i=0; i < number_arguments; i+=cp_size) {
        terms[0] = arguments[i+cp_x];   /*  x  */
        terms[1] = arguments[i+cp_y];   /*  y  */
        terms[2] = terms[0]*terms[1];   /* x*y */
        terms[3] = 1;                   /*  1  */
        LeastSquaresAddTerms(matrix,vectors,terms,
             &(arguments[i+cp_values]),4UL,number_values);
      }
      /* Solve for LeastSquares Coefficients */
      status=GaussJordanElimination(matrix,vectors,4UL,number_values);
      matrix  = RelinquishMagickMatrix(matrix, 4UL);
      vectors = (double **) RelinquishMagickMemory(vectors);
      if ( status == MagickFalse ) {
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "InvalidArgument","%s : 'Unsolvable Matrix'",
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
        return((double *) NULL);
      }
      if ( *method == BilinearForwardDistortion ) {
         /* Bilinear Forward Mapped Distortion

         The above least-squares solved for coefficents but in the forward
         direction, due to changes to indexing constants.

            i = c0*x + c1*y + c2*x*y + c3;
            j = c4*x + c5*y + c6*x*y + c7;

         where i,j are in the destination image, NOT the source.

         Reverse Pixel mapping however needs to use reverse of these
         functions.  It required a full page of algbra to work out the
         reversed mapping formula, but resolves down to the following...

            c8 = c0*c5-c1*c4;
            c9 = 2*(c2*c5-c1*c6);   // '2*a' in the quadratic formula

            i = i - c3;   j = j - c7;
            b = c6*i - c2*j + c8;   // So that   a*y^2 + b*y + c == 0
            c = c4*i -  c0*j;       // y = ( -b +- sqrt(bb - 4ac) ) / (2*a)

            r = b*b - c9*(c+c);
            if ( c9 != 0 )
              y = ( -b + sqrt(r) ) / c9;
            else
              y = -c/b;

            x = ( i - c1*y) / ( c1 - c2*y );

         NB: if 'r' is negative there is no solution!
         NB: the sign of the sqrt() should be negative if image becomes
             flipped or flopped, or crosses over itself.
         NB: techniqually coefficient c5 is not needed, anymore,
             but kept for completness.

         See Anthony Thyssen <A.Thyssen@griffith.edu.au>
         or  Fred Weinhaus <fmw@alink.net>  for more details.

         */
         coeff[8] = coeff[0]*coeff[5] - coeff[1]*coeff[4];
         coeff[9] = 2*(coeff[2]*coeff[5] - coeff[1]*coeff[6]);
      }
      return(coeff);
    }
#if 0
    case QuadrilateralDistortion:
    {
      /* Map a Quadrilateral to a unit square using BilinearReverse
         Then map that unit square back to the final Quadrilateral
         using BilinearForward.

         Input Arguments are sets of control points...
         For Distort Images    u,v, x,y  ...
         For Sparse Gradients  x,y, r,g,b  ...

      */
      /* UNDER CONSTRUCTION */
      return(coeff);
    }
#endif

    case PolynomialDistortion:
    {
      /* Polynomial Distortion

         First two coefficents are used to hole global polynomal information
           c0 = Order of the polynimial being created
           c1 = number_of_terms in one polynomial equation

         Rest of the coefficients map to the equations....
            v = c0 + c1*x + c2*y + c3*x*y + c4*x^2 + c5*y^2 + c6*x^3 + ...
         for each 'value' (number_values of them) given.
         As such total coefficients =  2 + number_terms * number_values

         Input Arguments are sets of control points...
         For Distort Images    order  [u,v, x,y] ...
         For Sparse Gradients  order  [x,y, r,g,b] ...

         Polynomial Distortion Notes...
           + UNDER DEVELOPMENT -- Do not expect this to remain as is.
           + Currently polynomial is a reversed mapped distortion.
           + Order 1.5 is fudged to map into a bilinear distortion.
             though it is not the same order as that distortion.
      */
      double
        **matrix,
        **vectors,
        *terms;

      size_t
        nterms;   /* number of polynomial terms per number_values */

      ssize_t
        j;

      MagickBooleanType
        status;

      /* first two coefficients hold polynomial order information */
      coeff[0] = arguments[0];
      coeff[1] = (double) poly_number_terms(arguments[0]);
      nterms = (size_t) coeff[1];

      /* create matrix, a fake vectors matrix, and least sqs terms */
      matrix=AcquireMagickMatrix(nterms,nterms);
      vectors=(double **) AcquireQuantumMemory(number_values,
        sizeof(*vectors));
      terms=(double *) AcquireQuantumMemory(nterms,sizeof(*terms));
      if ((matrix  == (double **) NULL) || (vectors == (double **) NULL) ||
          (terms   == (double *) NULL))
      {
        matrix  = RelinquishMagickMatrix(matrix, nterms);
        vectors = (double **) RelinquishMagickMemory(vectors);
        terms   = (double *) RelinquishMagickMemory(terms);
        coeff   = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),
                ResourceLimitError,"MemoryAllocationFailed",
                "%s", "DistortCoefficients");
        return((double *) NULL);
      }
      /* fake a number_values x3 vectors matrix from coefficients array */
      for (i=0; i < number_values; i++)
        vectors[i] = &(coeff[2+i*nterms]);
      /* Add given control point pairs for least squares solving */
      for (i=1; i < number_arguments; i+=cp_size) { /* NB: start = 1 not 0 */
        for (j=0; j < (ssize_t) nterms; j++)
          terms[j] = poly_basis_fn(j,arguments[i+cp_x],arguments[i+cp_y]);
        LeastSquaresAddTerms(matrix,vectors,terms,
             &(arguments[i+cp_values]),nterms,number_values);
      }
      terms = (double *) RelinquishMagickMemory(terms);
      /* Solve for LeastSquares Coefficients */
      status=GaussJordanElimination(matrix,vectors,nterms,number_values);
      matrix  = RelinquishMagickMatrix(matrix, nterms);
      vectors = (double **) RelinquishMagickMemory(vectors);
      if ( status == MagickFalse ) {
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "InvalidArgument","%s : 'Unsolvable Matrix'",
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
        return((double *) NULL);
      }
      return(coeff);
    }
    case ArcDistortion:
    {
      /* Arc Distortion
         Args: arc_width  rotate  top_edge_radius  bottom_edge_radius
         All but first argument are optional
            arc_width      The angle over which to arc the image side-to-side
            rotate         Angle to rotate image from vertical center
            top_radius     Set top edge of source image at this radius
            bottom_radius  Set bootom edge to this radius (radial scaling)

         By default, if the radii arguments are nor provided the image radius
         is calculated so the horizontal center-line is fits the given arc
         without scaling.

         The output image size is ALWAYS adjusted to contain the whole image,
         and an offset is given to position image relative to the 0,0 point of
         the origin, allowing users to use relative positioning onto larger
         background (via -flatten).

         The arguments are converted to these coefficients
            c0: angle for center of source image
            c1: angle scale for mapping to source image
            c2: radius for top of source image
            c3: radius scale for mapping source image
            c4: centerline of arc within source image

         Note the coefficients use a center angle, so asymptotic join is
         furthest from both sides of the source image. This also means that
         for arc angles greater than 360 the sides of the image will be
         trimmed equally.

         Arc Distortion Notes...
           + Does not use a set of CPs
           + Will only work with Image Distortion
           + Can not be used for generating a sparse gradient (interpolation)
      */
      if ( number_arguments >= 1 && arguments[0] < MagickEpsilon ) {
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "InvalidArgument","%s : 'Arc Angle Too Small'",
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
        return((double *) NULL);
      }
      if ( number_arguments >= 3 && arguments[2] < MagickEpsilon ) {
        coeff = (double *) RelinquishMagickMemory(coeff);
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "InvalidArgument","%s : 'Outer Radius Too Small'",
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
        return((double *) NULL);
      }
      coeff[0] = -MagickPI2;   /* -90, place at top! */
      if ( number_arguments >= 1 )
        coeff[1] = DegreesToRadians(arguments[0]);
      else
        coeff[1] = MagickPI2;   /* zero arguments - center is at top */
      if ( number_arguments >= 2 )
        coeff[0] += DegreesToRadians(arguments[1]);
      coeff[0] /= Magick2PI;  /* normalize radians */
      coeff[0] -= MagickRound(coeff[0]);
      coeff[0] *= Magick2PI;  /* de-normalize back to radians */
      coeff[3] = (double)image->rows-1;
      coeff[2] = (double)image->columns/coeff[1] + coeff[3]/2.0;
      if ( number_arguments >= 3 ) {
        if ( number_arguments >= 4 )
          coeff[3] = arguments[2] - arguments[3];
        else
          coeff[3] *= arguments[2]/coeff[2];
        coeff[2] = arguments[2];
      }
      coeff[4] = ((double)image->columns-1.0)/2.0;

      return(coeff);
    }
    case PolarDistortion:
    case DePolarDistortion:
    {
      /* (De)Polar Distortion   (same set of arguments)
         Args:  Rmax, Rmin,  Xcenter,Ycenter,  Afrom,Ato
         DePolar can also have the extra arguments of Width, Height

         Coefficients 0 to 5 is the sanatized version first 6 input args
         Coefficient 6  is the angle to coord ratio  and visa-versa
         Coefficient 7  is the radius to coord ratio and visa-versa

         WARNING: It is possible for  Radius max<min  and/or  Angle from>to
      */
      if ( number_arguments == 3
          || ( number_arguments > 6 && *method == PolarDistortion )
          || number_arguments > 8 ) {
          (void) ThrowMagickException(exception,GetMagickModule(),
            OptionError,"InvalidArgument", "%s : number of arguments",
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
        coeff=(double *) RelinquishMagickMemory(coeff);
        return((double *) NULL);
      }
      /* Rmax -  if 0 calculate appropriate value */
      if ( number_arguments >= 1 )
        coeff[0] = arguments[0];
      else
        coeff[0] = 0.0;
      /* Rmin  - usally 0 */
      coeff[1] = number_arguments >= 2 ? arguments[1] : 0.0;
      /* Center X,Y */
      if ( number_arguments >= 4 ) {
        coeff[2] = arguments[2];
        coeff[3] = arguments[3];
      }
      else { /* center of actual image */
        coeff[2] = (double)(image->columns)/2.0+image->page.x;
        coeff[3] = (double)(image->rows)/2.0+image->page.y;
      }
      /* Angle from,to - about polar center 0 is downward */
      coeff[4] = -MagickPI;
      if ( number_arguments >= 5 )
        coeff[4] = DegreesToRadians(arguments[4]);
      coeff[5] = coeff[4];
      if ( number_arguments >= 6 )
        coeff[5] = DegreesToRadians(arguments[5]);
      if ( fabs(coeff[4]-coeff[5]) < MagickEpsilon )
        coeff[5] += Magick2PI; /* same angle is a full circle */
      /* if radius 0 or negative,  its a special value... */
      if ( coeff[0] < MagickEpsilon ) {
        /* Use closest edge  if radius == 0 */
        if ( fabs(coeff[0]) < MagickEpsilon ) {
          coeff[0]=MagickMin(fabs(coeff[2]-image->page.x),
                             fabs(coeff[3]-image->page.y));
          coeff[0]=MagickMin(coeff[0],
                       fabs(coeff[2]-image->page.x-image->columns));
          coeff[0]=MagickMin(coeff[0],
                       fabs(coeff[3]-image->page.y-image->rows));
        }
        /* furthest diagonal if radius == -1 */
        if ( fabs(-1.0-coeff[0]) < MagickEpsilon ) {
          double rx,ry;
          rx = coeff[2]-image->page.x;
          ry = coeff[3]-image->page.y;
          coeff[0] = rx*rx+ry*ry;
          ry = coeff[3]-image->page.y-image->rows;
          coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
          rx = coeff[2]-image->page.x-image->columns;
          coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
          ry = coeff[3]-image->page.y;
          coeff[0] = MagickMax(coeff[0],rx*rx+ry*ry);
          coeff[0] = sqrt(coeff[0]);
        }
      }
      /* IF Rmax <= 0 or Rmin < 0 OR Rmax < Rmin, THEN error */
      if ( coeff[0] < MagickEpsilon || coeff[1] < -MagickEpsilon
           || (coeff[0]-coeff[1]) < MagickEpsilon ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "InvalidArgument", "%s : Invalid Radius",
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
        coeff=(double *) RelinquishMagickMemory(coeff);
        return((double *) NULL);
      }
      /* converstion ratios */
      if ( *method == PolarDistortion ) {
        coeff[6]=(double) image->columns/(coeff[5]-coeff[4]);
        coeff[7]=(double) image->rows/(coeff[0]-coeff[1]);
      }
      else { /* *method == DePolarDistortion */
        coeff[6]=(coeff[5]-coeff[4])/image->columns;
        coeff[7]=(coeff[0]-coeff[1])/image->rows;
      }
      return(coeff);
    }
    case Cylinder2PlaneDistortion:
    case Plane2CylinderDistortion:
    {
      /* 3D Cylinder to/from a Tangential Plane

         Projection between a clinder and flat plain from a point on the
         center line of the cylinder.

         The two surfaces coincide in 3D space at the given centers of
         distortion (perpendicular to projection point) on both images.

         Args:  FOV_arc_width
         Coefficents: FOV(radians), Radius, center_x,y, dest_center_x,y

         FOV (Field Of View) the angular field of view of the distortion,
         across the width of the image, in degrees.  The centers are the
         points of least distortion in the input and resulting images.

         These centers are however determined later.

         Coeff 0 is the FOV angle of view of image width in radians
         Coeff 1 is calculated radius of cylinder.
         Coeff 2,3  center of distortion of input image
         Coefficents 4,5 Center of Distortion of dest (determined later)
      */
      if ( arguments[0] < MagickEpsilon || arguments[0] > 160.0 ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
            "InvalidArgument", "%s : Invalid FOV Angle",
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
        coeff=(double *) RelinquishMagickMemory(coeff);
        return((double *) NULL);
      }
      coeff[0] = DegreesToRadians(arguments[0]);
      if ( *method == Cylinder2PlaneDistortion )
        /* image is curved around cylinder, so FOV angle (in radians)
         * scales directly to image X coordinate, according to its radius.
         */
        coeff[1] = (double) image->columns/coeff[0];
      else
        /* radius is distance away from an image with this angular FOV */
        coeff[1] = (double) image->columns / ( 2 * tan(coeff[0]/2) );

      coeff[2] = (double)(image->columns)/2.0+image->page.x;
      coeff[3] = (double)(image->rows)/2.0+image->page.y;
      coeff[4] = coeff[2];
      coeff[5] = coeff[3]; /* assuming image size is the same */
      return(coeff);
    }
    case BarrelDistortion:
    case BarrelInverseDistortion:
    {
      /* Barrel Distortion
           Rs=(A*Rd^3 + B*Rd^2 + C*Rd + D)*Rd
         BarrelInv Distortion
           Rs=Rd/(A*Rd^3 + B*Rd^2 + C*Rd + D)

        Where Rd is the normalized radius from corner to middle of image
        Input Arguments are one of the following forms (number of arguments)...
            3:  A,B,C
            4:  A,B,C,D
            5:  A,B,C    X,Y
            6:  A,B,C,D  X,Y
            8:  Ax,Bx,Cx,Dx  Ay,By,Cy,Dy
           10:  Ax,Bx,Cx,Dx  Ay,By,Cy,Dy   X,Y

        Returns 10 coefficent values, which are de-normalized (pixel scale)
          Ax, Bx, Cx, Dx,   Ay, By, Cy, Dy,    Xc, Yc
      */
      /* Radius de-normalization scaling factor */
      double
        rscale = 2.0/MagickMin((double) image->columns,(double) image->rows);

      /* sanity check  number of args must = 3,4,5,6,8,10 or error */
      if ( (number_arguments  < 3) || (number_arguments == 7) ||
           (number_arguments == 9) || (number_arguments > 10) )
        {
          coeff=(double *) RelinquishMagickMemory(coeff);
          (void) ThrowMagickException(exception,GetMagickModule(),
            OptionError,"InvalidArgument", "%s : number of arguments",
            CommandOptionToMnemonic(MagickDistortOptions, *method) );
          return((double *) NULL);
        }
      /* A,B,C,D coefficients */
      coeff[0] = arguments[0];
      coeff[1] = arguments[1];
      coeff[2] = arguments[2];
      if ((number_arguments == 3) || (number_arguments == 5) )
        coeff[3] = 1.0 - coeff[0] - coeff[1] - coeff[2];
      else
        coeff[3] = arguments[3];
      /* de-normalize the coefficients */
      coeff[0] *= pow(rscale,3.0);
      coeff[1] *= rscale*rscale;
      coeff[2] *= rscale;
      /* Y coefficients: as given OR same as X coefficients */
      if ( number_arguments >= 8 ) {
        coeff[4] = arguments[4] * pow(rscale,3.0);
        coeff[5] = arguments[5] * rscale*rscale;
        coeff[6] = arguments[6] * rscale;
        coeff[7] = arguments[7];
      }
      else {
        coeff[4] = coeff[0];
        coeff[5] = coeff[1];
        coeff[6] = coeff[2];
        coeff[7] = coeff[3];
      }
      /* X,Y Center of Distortion (image coodinates) */
      if ( number_arguments == 5 )  {
        coeff[8] = arguments[3];
        coeff[9] = arguments[4];
      }
      else if ( number_arguments == 6 ) {
        coeff[8] = arguments[4];
        coeff[9] = arguments[5];
      }
      else if ( number_arguments == 10 ) {
        coeff[8] = arguments[8];
        coeff[9] = arguments[9];
      }
      else {
        /* center of the image provided (image coodinates) */
        coeff[8] = (double)image->columns/2.0 + image->page.x;
        coeff[9] = (double)image->rows/2.0    + image->page.y;
      }
      return(coeff);
    }
    case ShepardsDistortion:
    {
      /* Shepards Distortion  input arguments are the coefficents!
         Just check the number of arguments is valid!
         Args:  u1,v1, x1,y1, ...
          OR :  u1,v1, r1,g1,c1, ...
      */
      if ( number_arguments%cp_size != 0 ||
           number_arguments < cp_size ) {
        (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
              "InvalidArgument", "%s : 'requires CP's (4 numbers each)'",
              CommandOptionToMnemonic(MagickDistortOptions, *method));
        coeff=(double *) RelinquishMagickMemory(coeff);
        return((double *) NULL);
      }
      /* User defined weighting power for Shepard's Method */
      { const char *artifact=GetImageArtifact(image,"shepards:power");
        if ( artifact != (const char *) NULL ) {
          coeff[0]=StringToDouble(artifact,(char **) NULL) / 2.0;
          if ( coeff[0] < MagickEpsilon ) {
            (void) ThrowMagickException(exception,GetMagickModule(),
                OptionError,"InvalidArgument","%s", "-define shepards:power" );
            coeff=(double *) RelinquishMagickMemory(coeff);
            return((double *) NULL);
          }
        }
        else
          coeff[0]=1.0;  /* Default power of 2 (Inverse Squared) */
      }
      return(coeff);
    }
    default:
      break;
  }
  /* you should never reach this point */
  perror("no method handler"); /* just fail assertion */
  return((double *) NULL);
}


static PixelList *AcquirePixelList(const size_t width,const size_t height)
{
  PixelList
    *pixel_list;

  ssize_t
    i;

  pixel_list=(PixelList *) AcquireMagickMemory(sizeof(*pixel_list));
  if (pixel_list == (PixelList *) NULL)
    return(pixel_list);
  (void) memset((void *) pixel_list,0,sizeof(*pixel_list));
  pixel_list->length=width*height;
  for (i=0; i < ListChannels; i++)
  {
    pixel_list->lists[i].nodes=(ListNode *) AcquireAlignedMemory(65537UL,
      sizeof(*pixel_list->lists[i].nodes));
    if (pixel_list->lists[i].nodes == (ListNode *) NULL)
      return(DestroyPixelList(pixel_list));
    (void) memset(pixel_list->lists[i].nodes,0,65537UL*
      sizeof(*pixel_list->lists[i].nodes));
  }
  pixel_list->signature=MagickCoreSignature;
  return(pixel_list);
}


static void GetMinimumPixelList(PixelList *pixel_list,MagickPixelPacket *pixel)
{
  SkipList
    *list;

  ssize_t
    channel;

  size_t
    color,
    minimum;

  ssize_t
    count;

  unsigned short
    channels[ListChannels];

  /*
    Find the minimum value for each of the color.
  */
  for (channel=0; channel < 5; channel++)
  {
    list=pixel_list->lists+channel;
    count=0;
    color=65536UL;
    minimum=list->nodes[color].next[0];
    do
    {
      color=list->nodes[color].next[0];
      if (color < minimum)
        minimum=color;
      count+=list->nodes[color].count;
    } while (count < (ssize_t) pixel_list->length);
    channels[channel]=(unsigned short) minimum;
  }
  pixel->red=(MagickRealType) ScaleShortToQuantum(channels[0]);
  pixel->green=(MagickRealType) ScaleShortToQuantum(channels[1]);
  pixel->blue=(MagickRealType) ScaleShortToQuantum(channels[2]);
  pixel->opacity=(MagickRealType) ScaleShortToQuantum(channels[3]);
  pixel->index=(MagickRealType) ScaleShortToQuantum(channels[4]);
}


MagickExport MagickBooleanType GetImageChannelKurtosis(const Image *image,
  const ChannelType channel,double *kurtosis,double *skewness,
  ExceptionInfo *exception)
{
  double
    area,
    mean,
    standard_deviation,
    sum_squares,
    sum_cubes,
    sum_fourth_power;

  ssize_t
    y;

  assert(image != (Image *) NULL);
  assert(image->signature == MagickCoreSignature);
  if (image->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  *kurtosis=0.0;
  *skewness=0.0;
  area=0.0;
  mean=0.0;
  standard_deviation=0.0;
  sum_squares=0.0;
  sum_cubes=0.0;
  sum_fourth_power=0.0;
  for (y=0; y < (ssize_t) image->rows; y++)
  {
    const IndexPacket
      *magick_restrict indexes;

    const PixelPacket
      *magick_restrict p;

    ssize_t
      x;

    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
    if (p == (const PixelPacket *) NULL)
      break;
    indexes=GetVirtualIndexQueue(image);
    for (x=0; x < (ssize_t) image->columns; x++)
    {
      if ((channel & RedChannel) != 0)
        {
          mean+=GetPixelRed(p);
          sum_squares+=(double) GetPixelRed(p)*GetPixelRed(p);
          sum_cubes+=(double) GetPixelRed(p)*GetPixelRed(p)*GetPixelRed(p);
          sum_fourth_power+=(double) GetPixelRed(p)*GetPixelRed(p)*
            GetPixelRed(p)*GetPixelRed(p);
          area++;
        }
      if ((channel & GreenChannel) != 0)
        {
          mean+=GetPixelGreen(p);
          sum_squares+=(double) GetPixelGreen(p)*GetPixelGreen(p);
          sum_cubes+=(double) GetPixelGreen(p)*GetPixelGreen(p)*
            GetPixelGreen(p);
          sum_fourth_power+=(double) GetPixelGreen(p)*GetPixelGreen(p)*
            GetPixelGreen(p)*GetPixelGreen(p);
          area++;
        }
      if ((channel & BlueChannel) != 0)
        {
          mean+=GetPixelBlue(p);
          sum_squares+=(double) GetPixelBlue(p)*GetPixelBlue(p);
          sum_cubes+=(double) GetPixelBlue(p)*GetPixelBlue(p)*GetPixelBlue(p);
          sum_fourth_power+=(double) GetPixelBlue(p)*GetPixelBlue(p)*
            GetPixelBlue(p)*GetPixelBlue(p);
          area++;
        }
      if ((channel & OpacityChannel) != 0)
        {
          mean+=GetPixelAlpha(p);
          sum_squares+=(double) GetPixelOpacity(p)*GetPixelAlpha(p);
          sum_cubes+=(double) GetPixelOpacity(p)*GetPixelAlpha(p)*
            GetPixelAlpha(p);
          sum_fourth_power+=(double) GetPixelAlpha(p)*GetPixelAlpha(p)*
            GetPixelAlpha(p)*GetPixelAlpha(p);
          area++;
        }
      if (((channel & IndexChannel) != 0) &&
          (image->colorspace == CMYKColorspace))
        {
          double
            index;

          index=(double) GetPixelIndex(indexes+x);
          mean+=index;
          sum_squares+=index*index;
          sum_cubes+=index*index*index;
          sum_fourth_power+=index*index*index*index;
          area++;
        }
      p++;
    }
  }
  if (y < (ssize_t) image->rows)
    return(MagickFalse);
  if (area != 0.0)
    {
      mean/=area;
      sum_squares/=area;
      sum_cubes/=area;
      sum_fourth_power/=area;
    }
  standard_deviation=sqrt(sum_squares-(mean*mean));
  if (standard_deviation != 0.0)
    {
      *kurtosis=sum_fourth_power-4.0*mean*sum_cubes+6.0*mean*mean*sum_squares-
        3.0*mean*mean*mean*mean;
      *kurtosis/=standard_deviation*standard_deviation*standard_deviation*
        standard_deviation;
      *kurtosis-=3.0;
      *skewness=sum_cubes-3.0*mean*sum_squares+2.0*mean*mean*mean;
      *skewness/=standard_deviation*standard_deviation*standard_deviation;
    }
  return(y == (ssize_t) image->rows ? MagickTrue : MagickFalse);
}