    private Job startCreateJob(EntityReference entityReference, EditForm editForm) throws XWikiException
    {
        if (StringUtils.isBlank(editForm.getTemplate())) {
            // No template specified, nothing more to do.
            return null;
        }

        // If a template is set in the request, then this is a create action which needs to be handled by a create job,
        // but skipping the target document, which is now already saved by the save action.

        RefactoringScriptService refactoring =
            (RefactoringScriptService) Utils.getComponent(ScriptService.class, "refactoring");

        CreateRequest request = refactoring.getRequestFactory().createCreateRequest(Arrays.asList(entityReference));
        request.setCheckAuthorRights(false);
        // Set the target document.
        request.setEntityReferences(Arrays.asList(entityReference));
        // Set the template to use.
        DocumentReferenceResolver<String> resolver =
            Utils.getComponent(DocumentReferenceResolver.TYPE_STRING, "currentmixed");
        EntityReference templateReference = resolver.resolve(editForm.getTemplate());
        request.setTemplateReference(templateReference);
        // We`ve already created and populated the fields of the target document, focus only on the remaining children
        // specified in the template.
        request.setSkippedEntities(Arrays.asList(entityReference));

        Job createJob = refactoring.create(request);
        if (createJob != null) {
            return createJob;
        } else {
            throw new XWikiException(String.format("Failed to schedule the create job for [%s]", entityReference),
                refactoring.getLastError());
        }
    }


    public void existingDocumentTerminalFromUICheckEscaping() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X.Y&name=Z&tocreate=termina
        when(mockRequest.getParameter("spaceReference")).thenReturn("X.Y");
        when(mockRequest.getParameter("name")).thenReturn("Z");
        when(mockRequest.getParameter("tocreate")).thenReturn("terminal");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating X.Y.Z instead of X.Y.Z.WebHome because the tocreate parameter says "terminal".
        verify(mockURLFactory).createURL("X.Y", "Z", "edit", "template=&parent=Main.WebHome&title=Z", null, "xwiki",
            context);
    }


    public void existingDocumentFromUIDeprecated() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI space=X&page=Y
        when(mockRequest.getParameter("space")).thenReturn("X");
        when(mockRequest.getParameter("page")).thenReturn("Y");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating X.Y since the deprecated parameters were creating terminal documents by default.
        verify(mockURLFactory).createURL("X", "Y", "edit", "template=&parent=Main.WebHome&title=Y", null, "xwiki",
            context);
    }


    public void notExistingDocumentFromUIButSpaceTooLong() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(10);
        context.setDoc(document);

        when(mockRequest.getParameter("spaceReference")).thenReturn("1.3.5.7.9.11");
        when(mockRequest.getParameter("name")).thenReturn("Foo");

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify that the create template is rendered, so the UI is displayed for the user to see the error.
        assertEquals("create", result);

        // Check that the exception is properly set in the context for the UI to display.
        XWikiException exception = (XWikiException) this.oldcore.getScriptContext().getAttribute("createException");
        assertNotNull(exception);
        assertEquals(XWikiException.ERROR_XWIKI_APP_DOCUMENT_PATH_TOO_LONG, exception.getCode());

        // We should not get this far so no redirect should be done, just the template will be rendered.
        verify(mockURLFactory, never()).createURL(any(), any(), any(), any(), any(),
            any(), any(XWikiContext.class));
    }


    public void existingDocumentFromUITemplateProviderSpecifiedNonTerminalOverridenFromUIToTerminal() throws Exception
    {
        // current document = xwiki:Main.WebHome
        DocumentReference documentReference = new DocumentReference("xwiki", Arrays.asList("Main"), "WebHome");
        XWikiDocument document = mock(XWikiDocument.class);
        when(document.getDocumentReference()).thenReturn(documentReference);
        when(document.isNew()).thenReturn(false);
        when(document.getLocalReferenceMaxLength()).thenReturn(255);
        context.setDoc(document);

        // Submit from the UI spaceReference=X&name=Y&templateProvider=XWiki.MyTemplateProvider
        String templateProviderFullName = "XWiki.MyTemplateProvider";
        String spaceReferenceString = "X";
        when(mockRequest.getParameter("spaceReference")).thenReturn(spaceReferenceString);
        when(mockRequest.getParameter("name")).thenReturn("Y");
        when(mockRequest.getParameter("templateprovider")).thenReturn(templateProviderFullName);
        when(mockRequest.getParameter("tocreate")).thenReturn("terminal");

        // Mock 1 existing template provider that creates terminal documents.
        mockExistingTemplateProviders(templateProviderFullName,
            new DocumentReference("xwiki", Arrays.asList("XWiki"), "MyTemplateProvider"), Collections.EMPTY_LIST,
            false);

        // Run the action
        String result = action.render(context);

        // The tests are below this line!

        // Verify null is returned (this means the response has been returned)
        assertNull(result);

        // Note: We are creating the document X.Y as terminal, even if the template provider says otherwise.
        // Also using a template, as specified in the template provider.
        verify(mockURLFactory).createURL("X", "Y", "edit", "template=XWiki.MyTemplate&parent=Main.WebHome&title=Y",
            null, "xwiki", context);
    }


    public InternetAddress requestResetPassword(UserReference user) throws ResetPasswordException
    {
        if (this.authorizationManager.hasAccess(Right.PROGRAM)) {
            ResetPasswordRequestResponse resetPasswordRequestResponse =
                this.resetPasswordManager.requestResetPassword(user);
            this.resetPasswordManager.sendResetPasswordEmailRequest(resetPasswordRequestResponse);
            return resetPasswordRequestResponse.getUserEmail();
        } else {
            return null;
        }
    }


    private Template getClassloaderTemplate(String suffixPath, String templateName)
    {
        return getClassloaderTemplate(Thread.currentThread().getContextClassLoader(), suffixPath, templateName);
    }


    private static void writeBaloFile(HttpURLConnection conn, Path baloPath, String fullModuleName,
            long resContentLength) {
        try (InputStream inputStream = conn.getInputStream();
                FileOutputStream outputStream = new FileOutputStream(baloPath.toString())) {
            writeAndHandleProgress(inputStream, outputStream, resContentLength / 1024, fullModuleName);
        } catch (IOException e) {
            createError("error occurred copying the balo file: " + e.getMessage());
        }
    }


    private static void handleResponse(HttpURLConnection conn, int statusCode, String terminalWidth) {
        try {
            // 200 - modules found
            // Other - Error occurred, json returned with the error message
            MapValue payload;
            if (statusCode == HttpURLConnection.HTTP_OK) {
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(conn.getInputStream(), Charset.defaultCharset()))) {
                    StringBuilder result = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        result.append(line);
                    }
                    payload = (MapValue) JSONParser.parse(result.toString());
                } catch (IOException e) {
                    throw ErrorUtil.createCommandException(e.getMessage());
                }

                if (payload.getIntValue("count") > 0) {
                    ArrayValue modules = payload.getArrayValue("modules");
                    printModules(modules, terminalWidth);
                } else {
                    outStream.println("no modules found");
                }
            } else {
                StringBuilder result = new StringBuilder();
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(conn.getErrorStream(), Charset.defaultCharset()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        result.append(line);
                    }
                } catch (IOException e) {
                    throw ErrorUtil.createCommandException(e.getMessage());
                }

                payload = (MapValue) JSONParser.parse(result.toString());
                throw ErrorUtil.createCommandException(payload.getStringValue("message"));
            }
        } finally {
            conn.disconnect();
        }
    }


    public static void initializeSsl() {
        try {
            SSLContext sc = SSLContext.getInstance(SSL);
            sc.init(null, trustAllCerts, new java.security.SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            throw ErrorUtil.createCommandException("initializing SSL failed: " + e.getMessage());
        }
    }


    public void translate(BlockPickRequestPacket packet, GeyserSession session) {
        Vector3i vector = packet.getBlockPosition();
        int blockToPick = session.getConnector().getWorldManager().getBlockAt(session, vector.getX(), vector.getY(), vector.getZ());

        // Block is air - chunk caching is probably off
        if (blockToPick == BlockStateValues.JAVA_AIR_ID) {
            // Check for an item frame since the client thinks that's a block when it's an entity in Java
            ItemFrameEntity entity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
            if (entity != null) {
                // Check to see if the item frame has an item in it first
                if (entity.getHeldItem() != null && entity.getHeldItem().getId() != 0) {
                    // Grab the item in the frame
                    InventoryUtils.findOrCreateItem(session, entity.getHeldItem());
                } else {
                    // Grab the frame as the item
                    InventoryUtils.findOrCreateItem(session, entity.getEntityType() == EntityType.GLOW_ITEM_FRAME ? "minecraft:glow_item_frame" : "minecraft:item_frame");
                }
            }
            return;
        }

        InventoryUtils.findOrCreateItem(session, BlockRegistries.JAVA_BLOCKS.get(blockToPick).getPickItem());
    }


    public void translate(ItemFrameDropItemPacket packet, GeyserSession session) {
        Entity entity = ItemFrameEntity.getItemFrameEntity(session, packet.getBlockPosition());
        if (entity != null) {
            ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
                    InteractAction.ATTACK, Hand.MAIN_HAND, session.isSneaking());
            session.sendDownstreamPacket(interactPacket);
        }
    }


    public void translate(ItemStackRequestPacket packet, GeyserSession session) {
        Inventory inventory = session.getOpenInventory();
        if (inventory == null)
            return;

        InventoryTranslator translator = session.getInventoryTranslator();
        translator.translateRequests(session, inventory, packet.getRequests());
    }


    public void translate(MoveEntityAbsolutePacket packet, GeyserSession session) {
        session.setLastVehicleMoveTimestamp(System.currentTimeMillis());

        float y = packet.getPosition().getY();
        if (session.getRidingVehicleEntity() instanceof BoatEntity) {
            // Remove the offset to prevents boats from looking like they're floating in water
            y -= EntityType.BOAT.getOffset();
        }
        ClientVehicleMovePacket clientVehicleMovePacket = new ClientVehicleMovePacket(
                packet.getPosition().getX(), y, packet.getPosition().getZ(),
                packet.getRotation().getY() - 90, packet.getRotation().getX()
        );
        session.sendDownstreamPacket(clientVehicleMovePacket);
    }


    public void translate(ServerAdvancementsPacket packet, GeyserSession session) {
        AdvancementsCache advancementsCache = session.getAdvancementsCache();
        if (packet.isReset()) {
            advancementsCache.getStoredAdvancements().clear();
            advancementsCache.getStoredAdvancementProgress().clear();
        }

        // Removes removed advancements from player's stored advancements
        for (String removedAdvancement : packet.getRemovedAdvancements()) {
            advancementsCache.getStoredAdvancements().remove(removedAdvancement);
        }

        advancementsCache.getStoredAdvancementProgress().putAll(packet.getProgress());

        sendToolbarAdvancementUpdates(session, packet);

        // Adds advancements to the player's stored advancements when advancements are sent
        for (Advancement advancement : packet.getAdvancements()) {
            if (advancement.getDisplayData() != null && !advancement.getDisplayData().isHidden()) {
                GeyserAdvancement geyserAdvancement = GeyserAdvancement.from(advancement);
                advancementsCache.getStoredAdvancements().put(advancement.getId(), geyserAdvancement);
            } else {
                advancementsCache.getStoredAdvancements().remove(advancement.getId());
            }
        }
    }


    public void translate(ServerDeclareTagsPacket packet, GeyserSession session) {
        session.getTagCache().loadPacket(packet);
    }


    public void translate(ServerEntityMetadataPacket packet, GeyserSession session) {
        Entity entity;
        if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
            entity = session.getPlayerEntity();
        } else {
            entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
        }
        if (entity == null) return;

        for (EntityMetadata metadata : packet.getMetadata()) {
            try {
                entity.updateBedrockMetadata(metadata, session);
            } catch (ClassCastException e) {
                // Class cast exceptions are really the only ones we're going to get in normal gameplay
                // Because some entity rewriters forget about some values
                // Any other errors are actual bugs
                session.getConnector().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.network.translator.metadata.failed", metadata, entity.getEntityType()));
                session.getConnector().getLogger().debug("Entity Java ID: " + entity.getEntityId() + ", Geyser ID: " + entity.getGeyserId());
                if (session.getConnector().getConfig().isDebugMode()) {
                    e.printStackTrace();
                }
            }
        }

        entity.updateBedrockMetadata(session);

        // Update the interactive tag, if necessary
        if (session.getMouseoverEntity() != null && session.getMouseoverEntity().getEntityId() == entity.getEntityId()) {
            InteractiveTagManager.updateTag(session, entity);
        }
    }


    public void translate(ServerEntityPositionPacket packet, GeyserSession session) {
        Entity entity = session.getEntityCache().getEntityByJavaId(packet.getEntityId());
        if (packet.getEntityId() == session.getPlayerEntity().getEntityId()) {
            entity = session.getPlayerEntity();
        }
        if (entity == null) return;

        entity.moveRelative(session, packet.getMoveX(), packet.getMoveY(), packet.getMoveZ(), entity.getRotation(), packet.isOnGround());
    }


    public void translate(ServerSpawnLivingEntityPacket packet, GeyserSession session) {
        Vector3f position = Vector3f.from(packet.getX(), packet.getY(), packet.getZ());
        Vector3f motion = Vector3f.from(packet.getMotionX(), packet.getMotionY(), packet.getMotionZ());
        Vector3f rotation = Vector3f.from(packet.getYaw(), packet.getPitch(), packet.getHeadYaw());

        EntityType type = EntityUtils.toBedrockEntity(packet.getType());
        if (type == null) {
            session.getConnector().getLogger().warning(LanguageUtils.getLocaleStringLog("geyser.entity.type_null", packet.getType()));
            return;
        }

        Class<? extends Entity> entityClass = type.getEntityClass();
        try {
            Constructor<? extends Entity> entityConstructor = entityClass.getConstructor(long.class, long.class, EntityType.class,
                    Vector3f.class, Vector3f.class, Vector3f.class);

            Entity entity = entityConstructor.newInstance(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
                    type, position, motion, rotation
            );
            session.getEntityCache().spawnEntity(entity);
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {
            ex.printStackTrace();
        }
    }


    public void translate(ServerClearTitlesPacket packet, GeyserSession session) {
        SetTitlePacket titlePacket = new SetTitlePacket();
        // TODO handle packet.isResetTimes()
        titlePacket.setType(SetTitlePacket.Type.CLEAR);
        titlePacket.setText("");
        titlePacket.setXuid("");
        titlePacket.setPlatformOnlineId("");
        session.sendUpstreamPacket(titlePacket);
    }


    public void translate(ServerCloseWindowPacket packet, GeyserSession session) {
        // Sometimes the server can request a window close of ID 0... when the window isn't even open
        // Don't confirm in this instance
        InventoryUtils.closeInventory(session, packet.getWindowId(), (session.getOpenInventory() != null && session.getOpenInventory().getId() == packet.getWindowId()));
    }


	public PersistentTask createTask(String name, Serializable task) {
		PersistentTask ptask = new PersistentTask();
		Date currentDate = new Date();
		ptask.setCreationDate(currentDate);
		ptask.setLastModified(currentDate);
		ptask.setName(name);
		ptask.setStatus(TaskStatus.newTask);
		ptask.setTask(xstream.toXML(task));
		dbInstance.getCurrentEntityManager().persist(ptask);
		return ptask;
	}


	public boolean updateConfiguration(String mapperId, Serializable mapper, int expirationTime) {
		String configuration = XStreamHelper.createXStreamInstance().toXML(mapper);
		Date currentDate = new Date();
		Date expirationDate = null;
		if(expirationTime > 0) {
			Calendar cal = Calendar.getInstance();
			cal.setTime(currentDate);
			cal.add(Calendar.SECOND, expirationTime);
			expirationDate = cal.getTime();
		}
		int row = dbInstance.getCurrentEntityManager().createNamedQuery("updateMapperByMapperId")
			.setParameter("now", currentDate)
			.setParameter("expirationDate", expirationDate)
			.setParameter("config", configuration)
			.setParameter("mapperId", mapperId)
			.executeUpdate();

		dbInstance.commit();
		return row > 0;
	}


	public Map<String, Map<String, Object>> getCustomConfig(CourseEnvironment courseEnvironment){
		Map<String, Map<String, Object>> defaultConf = new HashMap<>();
		VFSContainer base = (VFSContainer) courseEnvironment.getCourseBaseContainer().resolve(CourseLayoutHelper.LAYOUT_COURSE_SUBFOLDER);
		if (base == null) {
			return defaultConf;
		}
		VFSContainer themeBase = (VFSContainer) base.resolve("/" + CourseLayoutHelper.CONFIG_KEY_CUSTOM);
		if (themeBase == null) {
			return defaultConf;
		}
		VFSLeaf configTarget = (VFSLeaf) themeBase.resolve(CUSTOM_CONFIG_XML);
		if (configTarget == null) {
			return defaultConf;
		}
		XStream xStream = XStreamHelper.createXStreamInstance();
		try(InputStream in=configTarget.getInputStream()) {
			return (Map<String, Map<String, Object>>) xStream.fromXML(in);
		} catch(IOException e) {
			log.error("", e);
			return defaultConf;
		}
	}


	public static final void toStream(Binder binder, ZipOutputStream zout)
	throws IOException {
		try(OutputStream out=new ShieldOutputStream(zout)) {
			myStream.toXML(binder, out);
		} catch (Exception e) {
			log.error("Cannot export this map: " + binder, e);
		}
	}


	public static List<String> checkLockedFileBeforeUnzipNonStrict(VFSLeaf zipLeaf, VFSContainer targetDir, Identity identity) {
		List<String> lockedFiles = new ArrayList<>();
		VFSLockManager vfsLockManager = CoreSpringFactory.getImpl(VFSLockManager.class);

		try(InputStream in = zipLeaf.getInputStream();
				net.sf.jazzlib.ZipInputStream oZip = new net.sf.jazzlib.ZipInputStream(in);) {
			// unzip files
			net.sf.jazzlib.ZipEntry oEntr = oZip.getNextEntry();
			while (oEntr != null) {
				if (oEntr.getName() != null && !oEntr.getName().startsWith(DIR_NAME__MACOSX)) {
					if (oEntr.isDirectory()) {
						// skip MacOSX specific metadata directory
						// directories aren't locked
						oZip.closeEntry();
						oEntr = oZip.getNextEntry();
						continue;
					} else {
						// search file
						VFSContainer createIn = targetDir;
						String name = oEntr.getName();
						// check if entry has directories which did not show up as
						// directories above
						int dirSepIndex = name.lastIndexOf('/');
						if (dirSepIndex == -1) {
							// try it windows style, backslash is also valid format
							dirSepIndex = name.lastIndexOf('\\');
						}
						if (dirSepIndex > 0) {
							// get subdirs
							createIn = getAllSubdirs(targetDir, name.substring(0, dirSepIndex), identity, false);
							if (createIn == null) {
								//sub directories don't exist, and aren't locked
								oZip.closeEntry();
								oEntr = oZip.getNextEntry();
								continue;
							}
							name = name.substring(dirSepIndex + 1);
						}

						VFSLeaf newEntry = (VFSLeaf)createIn.resolve(name);
						if(vfsLockManager.isLockedForMe(newEntry, identity, VFSLockApplicationType.vfs, null)) {
							lockedFiles.add(name);
						}
					}
				}
				oZip.closeEntry();
				oEntr = oZip.getNextEntry();
			}
		} catch (IOException e) {
			return null;
		}

		return lockedFiles;
	}


	public FileSelection(UserRequest ureq, String currentContainerRelPath) {
		if (currentContainerRelPath.equals("/")) currentContainerRelPath = "";
		this.currentContainerRelPath = currentContainerRelPath;
		parse(ureq);
	}


		public void configure(ICourse course, CourseNode newNode, ModuleConfiguration moduleConfig) {
			newNode.setDisplayOption(CourseNode.DISPLAY_OPTS_TITLE_DESCRIPTION_CONTENT);
			VFSContainer rootContainer = course.getCourseFolderContainer();
			VFSLeaf singleFile = (VFSLeaf) rootContainer.resolve("/" + filename);
			if (singleFile == null) {
				singleFile = rootContainer.createChildLeaf("/" + filename);
			}

			if(in != null) {
				moduleConfig.set(SPEditController.CONFIG_KEY_FILE, "/" + filename);

				OutputStream out = singleFile.getOutputStream(false);
				FileUtils.copy(in, out);
				FileUtils.closeSafely(out);
				FileUtils.closeSafely(in);
			} else {
				if(StringHelper.containsNonWhitespace(path)) {
					if(!path.startsWith("/")) {
						path = "/" + path;
					}
					if(!path.endsWith("/")) {
						path += "/";
					}
				} else {
					path = "/";
				}
				moduleConfig.set(SPEditController.CONFIG_KEY_FILE, path + filename);
			}
			//saved node configuration
		}


    public String toString() {
        return toNiceString(this.getClass(), "clientId", clientId, "secret", "[protected]",
            "discoveryURI", discoveryURI, "scope", scope, "customParams", customParams,
            "clientAuthenticationMethod", clientAuthenticationMethod, "useNonce", useNonce,
            "preferredJwsAlgorithm", preferredJwsAlgorithm, "maxAge", maxAge, "maxClockSkew", maxClockSkew,
            "connectTimeout", connectTimeout, "readTimeout", readTimeout, "resourceRetriever", resourceRetriever,
            "responseType", responseType, "responseMode", responseMode, "logoutUrl", logoutUrl,
            "withState", withState, "stateGenerator", stateGenerator, "logoutHandler", logoutHandler,
            "tokenValidator", tokenValidator, "mappedClaims", mappedClaims);
    }


    public static DocumentBuilderFactory getNsAwareDocumentBuilderFactory() throws ParserConfigurationException {
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        setFeature(dbf, "http://apache.org/xml/features/disallow-doctype-decl");
        return dbf;
    }


	public RoundedContainer(Dimension2D dim, double titleHeight, double attributeHeight, HColor borderColor,
			HColor backColor, HColor imgBackcolor, UStroke stroke, double rounded, double shadowing) {
		if (dim.getWidth() == 0) {
			throw new IllegalArgumentException();
		}
		this.rounded = rounded;
		this.dim = dim;
		this.imgBackcolor = imgBackcolor;
		this.titleHeight = titleHeight;
		this.borderColor = borderColor;
		this.backColor = backColor;
		this.attributeHeight = attributeHeight;
		this.stroke = stroke;
		this.shadowing = shadowing;
	}


	public void svgImage(UImageSvg image, double x, double y) {
		// https://developer.mozilla.org/fr/docs/Web/SVG/Element/image
		if (hidden == false) {
			final Element elt = (Element) document.createElement("image");
			elt.setAttribute("width", format(image.getWidth()));
			elt.setAttribute("height", format(image.getHeight()));
			elt.setAttribute("x", format(x));
			elt.setAttribute("y", format(y));

			String svg = manageScale(image);

			final String svgHeader = "<svg height=\"" + (int) (image.getHeight() * scale) + "\" width=\""
					+ (int) (image.getWidth() * scale) + "\" xmlns=\"http://www.w3.org/2000/svg\" >";

			svg = svgHeader + svg.substring(5);

			final String s = toBase64(svg);
			elt.setAttribute("xlink:href", "data:image/svg+xml;base64," + s);

			getG().appendChild(elt);
		}
		ensureVisible(x, y);
		ensureVisible(x + image.getData("width"), y + image.getData("height"));
	}


	protected SymbolContext getContextLegacy() {
		return new SymbolContext(HColorUtils.COL_D7E0F2, HColorUtils.COL_038048).withStroke(new UStroke(1.5));
	}


	public CommandExecutionResult createBinary(String code, String full, boolean compact) {
		final Player player = new PlayerBinary(full, getSkinParam(), ruler, compactByDefault);
		players.put(code, player);
		return CommandExecutionResult.ok();
	}


	public static UChange changeBack(UGraphic ug) {
		final HColor color = ug.getParam().getColor();
		if (color == null) {
			return new HColorNone().bg();
		}
		return color.bg();
	}


        function findLateSubscriptionSortedByPriority() {
            if (late_subscriptions.length === 0) {
                return null;
            }
            late_subscriptions.sort(compare_subscriptions);

            // istanbul ignore next
            if (doDebug) {
                debugLog(
                    late_subscriptions
                        .map(
                            (s: Subscription) =>
                                "[ id = " +
                                s.id +
                                " prio=" +
                                s.priority +
                                " t=" +
                                s.timeToExpiration +
                                " ka=" +
                                s.timeToKeepAlive +
                                " m?=" +
                                s.hasUncollectedMonitoredItemNotifications +
                                " " +
                                SubscriptionState[s.state] +
                                " " + s.messageSent +
                                "]"
                        )
                        .join(" \n")
                );
            }
            return late_subscriptions[late_subscriptions.length - 1];
        }


    public void validate() {
        final String filter = format(ldapConfiguration.getUserSearchFilter(), "test");
        ldapConnectionTemplate.searchFirst(ldapConfiguration.getSearchBases().get(0), filter, SearchScope.SUBTREE, entry -> entry);
    }


    private File doDownload(String driverFileUrl, String filePath) {
        Path path = Path.of(filePath);

        // create parent directory
        if (Files.notExists(path)) {
            path.getParent().toFile().mkdirs();
            try {
                Files.createFile(path);
            } catch (IOException e) {
                log.error("create file error " + filePath, e);
                throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage());
            }
        }

        // download
        try {
            return restTemplate.execute(driverFileUrl, HttpMethod.GET, null, response -> {
                if (response.getStatusCode().is2xxSuccessful()) {
                    File file = path.toFile();
                    FileOutputStream out = new FileOutputStream(file);
                    StreamUtils.copy(response.getBody(), out);
                    IOUtils.closeQuietly(out, ex -> log.error("close file error", ex));
                    log.info("{} download success ", filePath);
                    return file;
                } else {
                    log.error("{} download error from {}: {} ", filePath, driverFileUrl, response);
                    throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception("驱动下载失败："
                            + response.getStatusCode()
                            + ", "
                            + response.getStatusText());
                }
            });
        } catch (IllegalArgumentException e) {
            log.error(filePath + " download driver error", e);
            throw DomainErrors.DOWNLOAD_DRIVER_ERROR.exception(e.getMessage());
        }
    }


    private void onopen() {
        logger.fine("open");

        this.cleanup();

        this.readyState = ReadyState.OPEN;
        this.emit(EVENT_OPEN);

        final io.socket.engineio.client.Socket socket = this.engine;
        this.subs.add(On.on(socket, Engine.EVENT_DATA, new Listener() {
            @Override
            public void call(Object... objects) {
                Object data = objects[0];
                if (data instanceof String) {
                    Manager.this.ondata((String)data);
                } else if (data instanceof byte[]) {
                    Manager.this.ondata((byte[])data);
                }
            }
        }));
        this.subs.add(On.on(socket, Engine.EVENT_ERROR, new Listener() {
            @Override
            public void call(Object... objects) {
                Manager.this.onerror((Exception)objects[0]);
            }
        }));
        this.subs.add(On.on(socket, Engine.EVENT_CLOSE, new Listener() {
            @Override
            public void call(Object... objects) {
                Manager.this.onclose((String)objects[0]);
            }
        }));
        this.decoder.onDecoded(new Parser.Decoder.Callback() {
            @Override
            public void call (Packet packet) {
                Manager.this.ondecoded(packet);
            }
        });
    }


    private void ondata(String data) {
        try {
            this.decoder.add(data);
        } catch (DecodingException e) {
            this.onerror(e);
        }
    }


    public AjaxResult updateAvatar(@RequestParam("avatarfile") MultipartFile file)
    {
        SysUser currentUser = getSysUser();
        try
        {
            if (!file.isEmpty())
            {
                String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file);
                currentUser.setAvatar(avatar);
                if (userService.updateUserInfo(currentUser) > 0)
                {
                    setSysUser(userService.selectUserById(currentUser.getUserId()));
                    return success();
                }
            }
            return error();
        }
        catch (Exception e)
        {
            log.error("修改头像失败！", e);
            return error(e.getMessage());
        }
    }


armv6pmu_handle_irq(int irq_num,
		    void *dev)
{
	unsigned long pmcr = armv6_pmcr_read();
	struct perf_sample_data data;
	struct cpu_hw_events *cpuc;
	struct pt_regs *regs;
	int idx;

	if (!armv6_pmcr_has_overflowed(pmcr))
		return IRQ_NONE;

	regs = get_irq_regs();

	/*
	 * The interrupts are cleared by writing the overflow flags back to
	 * the control register. All of the other bits don't have any effect
	 * if they are rewritten, so write the whole value back.
	 */
	armv6_pmcr_write(pmcr);

	perf_sample_data_init(&data, 0);

	cpuc = &__get_cpu_var(cpu_hw_events);
	for (idx = 0; idx <= armpmu->num_events; ++idx) {
		struct perf_event *event = cpuc->events[idx];
		struct hw_perf_event *hwc;

		if (!test_bit(idx, cpuc->active_mask))
			continue;

		/*
		 * We have a single interrupt for all counters. Check that
		 * each counter has overflowed before we process it.
		 */
		if (!armv6_pmcr_counter_has_overflowed(pmcr, idx))
			continue;

		hwc = &event->hw;
		armpmu_event_update(event, hwc, idx, 1);
		data.period = event->hw.last_period;
		if (!armpmu_event_set_period(event, hwc, idx))
			continue;

		if (perf_event_overflow(event, 0, &data, regs))
			armpmu->disable(hwc, idx);
	}

	/*
	 * Handle the pending perf events.
	 *
	 * Note: this call *must* be run with interrupts disabled. For
	 * platforms that can have the PMU interrupts raised as an NMI, this
	 * will not work.
	 */
	irq_work_run();

	return IRQ_HANDLED;
}


compat_mpt_command(struct file *filp, unsigned int cmd,
			unsigned long arg)
{
	struct mpt_ioctl_command32 karg32;
	struct mpt_ioctl_command32 __user *uarg = (struct mpt_ioctl_command32 __user *) arg;
	struct mpt_ioctl_command karg;
	MPT_ADAPTER *iocp = NULL;
	int iocnum, iocnumX;
	int nonblock = (filp->f_flags & O_NONBLOCK);
	int ret;

	if (copy_from_user(&karg32, (char __user *)arg, sizeof(karg32)))
		return -EFAULT;

	/* Verify intended MPT adapter */
	iocnumX = karg32.hdr.iocnum & 0xFF;
	if (((iocnum = mpt_verify_adapter(iocnumX, &iocp)) < 0) ||
	    (iocp == NULL)) {
		printk(KERN_DEBUG MYNAM "::compat_mpt_command @%d - ioc%d not found!\n",
			__LINE__, iocnumX);
		return -ENODEV;
	}

	if ((ret = mptctl_syscall_down(iocp, nonblock)) != 0)
		return ret;

	dctlprintk(iocp, printk(MYIOC_s_DEBUG_FMT "compat_mpt_command() called\n",
	    iocp->name));
	/* Copy data to karg */
	karg.hdr.iocnum = karg32.hdr.iocnum;
	karg.hdr.port = karg32.hdr.port;
	karg.timeout = karg32.timeout;
	karg.maxReplyBytes = karg32.maxReplyBytes;

	karg.dataInSize = karg32.dataInSize;
	karg.dataOutSize = karg32.dataOutSize;
	karg.maxSenseBytes = karg32.maxSenseBytes;
	karg.dataSgeOffset = karg32.dataSgeOffset;

	karg.replyFrameBufPtr = (char __user *)(unsigned long)karg32.replyFrameBufPtr;
	karg.dataInBufPtr = (char __user *)(unsigned long)karg32.dataInBufPtr;
	karg.dataOutBufPtr = (char __user *)(unsigned long)karg32.dataOutBufPtr;
	karg.senseDataPtr = (char __user *)(unsigned long)karg32.senseDataPtr;

	/* Pass new structure to do_mpt_command
	 */
	ret = mptctl_do_mpt_command (karg, &uarg->MF);

	mutex_unlock(&iocp->ioctl_cmds.mutex);

	return ret;
}


YCPBoolean IniAgent::Write(const YCPPath &path, const YCPValue& value, const YCPValue& arg)
{
    if (!parser.isStarted())
    {
	y2warning("Can't execute Write before being mounted.");
	return YCPBoolean (false);
    }
    // no need to update if modified, we are changing value

    bool ok = false; // is the _path_ ok?
    // return value
    YCPBoolean b (true);

    if (0 == path->length ())
    {
	if (value->isString() && value->asString()->value() == "force")
	    parser.inifile.setDirty();
	else if (value->isString () && value->asString()->value() == "clean")
	    parser.inifile.clean ();
	if (0 != parser.write ())
	    b = false;
	ok = true;
    }
    else
    {
	if (( parser.repeatNames () && value->isList ()) ||
	    (!parser.repeatNames () &&  (value->isString () || value->isInteger())) ||
	    path->component_str(0) == "all"
	    )
	    {
		ok = true;
		if (parser.inifile.Write (path, value, parser.HaveRewrites ()))
		    b = false;
	    }
        else if (value->isVoid ())
	    {
		int wb  = -1;
		string del_sec = "";
		ok = true;
		if (2 == path->length ())
		{
		    string pc = path->component_str(0);
		    if ("s" == pc || "section" == pc)
		    {	// request to delete section. Find the file name
			del_sec = path->component_str (1);
			wb = parser.inifile.getSubSectionRewriteBy (del_sec.c_str());
		    }
		}
		if (parser.inifile.Delete (path))
		    b = false;
		else if (del_sec != "")
		{
		    parser.deleted_sections.insert (parser.getFileName (del_sec, wb));
		}
	    }
	else
	{
	    ycp2error ("Wrong value for path %s: %s", path->toString ().c_str (), value->toString ().c_str ());
	    b = false;
	}
    }
    if (!ok)
    {
    	ycp2error ( "Wrong path '%s' in Write().", path->toString().c_str () );
    }

    return b;
}


    IniSection (const IniSection &s) :
	IniBase (s),
	  ip (s.ip),
	  end_comment (s.end_comment), rewrite_by (s.rewrite_by),
	  container (s.container)
	{ reindex (); }


static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies,
                                                size_t mincodes, size_t numcodes, unsigned maxbitlen)
{
  unsigned error = 0;
  while(!frequencies[numcodes - 1] && numcodes > mincodes) numcodes--; /*trim zeroes*/
  tree->maxbitlen = maxbitlen;
  tree->numcodes = (unsigned)numcodes; /*number of symbols*/
  tree->lengths = (unsigned*)realloc(tree->lengths, numcodes * sizeof(unsigned));
  if(!tree->lengths) return 83; /*alloc fail*/
  /*initialize all lengths to 0*/
  memset(tree->lengths, 0, numcodes * sizeof(unsigned));

  error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen);
  if(!error) error = HuffmanTree_makeFromLengths2(tree);
  return error;
}


static BOOL ntlm_av_pair_check(NTLM_AV_PAIR* pAvPair, size_t cbAvPair)
{
	if (!pAvPair || cbAvPair < sizeof(NTLM_AV_PAIR))
		return FALSE;
	return cbAvPair >= ntlm_av_pair_get_next_offset(pAvPair);
}


void AllocateDataSet(cmsIT8* it8)
{
    TABLE* t = GetTable(it8);

    if (t -> Data) return;    // Already allocated

    t-> nSamples   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
    t-> nPatches   = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));

    t-> Data = (char**)AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * ((cmsUInt32Number) t->nPatches + 1) *sizeof (char*));
    if (t->Data == NULL) {

        SynError(it8, "AllocateDataSet: Unable to allocate data array");
    }

}


BGD_DECLARE(void *) gdImageWBMPPtr(gdImagePtr im, int *size, int fg)
{
	void *rv;
	gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
	if (out == NULL) return NULL;
	gdImageWBMPCtx(im, fg, out);
	rv = gdDPExtractData(out, size);
	out->gd_free(out);
	return rv;
}


void PCRECache::dump(const std::string& filename) {
  std::ofstream out(filename.c_str());
  switch (m_kind) {
    case CacheKind::Static:
      for (auto& it : *m_staticCache) {
        out << it.first->data() << "\n";
      }
      break;
    case CacheKind::Lru:
    case CacheKind::Scalable:
      {
        std::vector<LRUCacheKey> keys;
        if (m_kind == CacheKind::Lru) {
          m_lruCache->snapshotKeys(keys);
        } else {
          m_scalableCache->snapshotKeys(keys);
        }
        for (auto& key: keys) {
          out << key.c_str() << "\n";
        }
      }
      break;
  }
  out.close();
}


String StringUtil::Crypt(const String& input, const char *salt /* = "" */) {
  if (salt && salt[0] == '\0') {
    raise_notice("crypt(): No salt parameter was specified."
      " You must use a randomly generated salt and a strong"
      " hash function to produce a secure hash.");
  }
  return String(string_crypt(input.c_str(), salt), AttachString);
}


inline int StringData::size() const { return m_len; }


  const String& setSize(int len) {
    assertx(m_str);
    m_str->setSize(len);
    return *this;
  }


Variant HHVM_FUNCTION(mcrypt_get_block_size, const String& cipher,
                                    const Variant& module /* = null_string */) {
  MCRYPT td = mcrypt_module_open((char*)cipher.data(),
                                 (char*)MCG(algorithms_dir).data(),
                                 (char*)module.asCStrRef().data(),
                                 (char*)MCG(modes_dir).data());
  if (td == MCRYPT_FAILED) {
    MCRYPT_OPEN_MODULE_FAILED("mcrypt_get_block_size");
    return false;
  }

  int64_t ret = mcrypt_enc_get_block_size(td);
  mcrypt_module_close(td);
  return ret;
}


xmlNodePtr SimpleXMLElement_exportNode(const Object& sxe) {
  assert(sxe->instanceof(SimpleXMLElement_classof()));
  auto data = Native::data<SimpleXMLElement>(sxe.get());
  return php_sxe_get_first_node(data, data->nodep());
}


static Variant HHVM_FUNCTION(bcsqrt, const String& operand,
                             int64_t scale /* = -1 */) {
  if (scale < 0) scale = BCG(bc_precision);
  bc_num result;
  bc_init_num(&result);
  SCOPE_EXIT {
    bc_free_num(&result);
  };
  php_str2num(&result, (char*)operand.data());
  Variant ret;
  if (bc_sqrt(&result, scale) != 0) {
    if (result->n_scale > scale) {
      result->n_scale = scale;
    }
    ret = String(bc_num2str(result), AttachString);
  } else {
    raise_warning("Square root of negative number");
  }
  return ret;
}


int CServer::SendMsg(CMsgPacker *pMsg, int Flags, int ClientID)
{
	CNetChunk Packet;
	if(!pMsg)
		return -1;

	// drop packet to dummy client
	if(0 <= ClientID && ClientID < MAX_CLIENTS && GameServer()->IsClientBot(ClientID))
		return 0;

	mem_zero(&Packet, sizeof(CNetChunk));
	Packet.m_ClientID = ClientID;
	Packet.m_pData = pMsg->Data();
	Packet.m_DataSize = pMsg->Size();

	if(Flags&MSGFLAG_VITAL)
		Packet.m_Flags |= NETSENDFLAG_VITAL;
	if(Flags&MSGFLAG_FLUSH)
		Packet.m_Flags |= NETSENDFLAG_FLUSH;

	// write message to demo recorder
	if(!(Flags&MSGFLAG_NORECORD))
		m_DemoRecorder.RecordMessage(pMsg->Data(), pMsg->Size());

	if(!(Flags&MSGFLAG_NOSEND))
	{
		if(ClientID == -1)
		{
			// broadcast
			int i;
			for(i = 0; i < MAX_CLIENTS; i++)
				if(m_aClients[i].m_State == CClient::STATE_INGAME && !m_aClients[i].m_Quitting)
				{
					Packet.m_ClientID = i;
					m_NetServer.Send(&Packet);
				}
		}
		else
			m_NetServer.Send(&Packet);
	}
	return 0;
}


tTcpIpPacketParsingResult ParaNdis_CheckSumVerifyFlat(
                                                PVOID pBuffer,
                                                ULONG ulDataLength,
                                                ULONG flags,
                                                LPCSTR caller)
{
    tCompletePhysicalAddress SGBuffer;
    SGBuffer.Virtual = pBuffer;
    SGBuffer.size = ulDataLength;
    return ParaNdis_CheckSumVerify(&SGBuffer, ulDataLength, 0, flags, caller);
}


static inline ulong encode_twos_comp(long n, int prec)
{
	ulong result;
	assert(prec >= 2);
	jas_eprintf("warning: support for signed data is untested\n");
	// NOTE: Is this correct?
	if (n < 0) {
		result = -n;
		result = (result ^ 0xffffffffUL) + 1;
		result &= (1 << prec) - 1;
	} else {
		result = n;
	}
	return result;
}


jas_seq2d_t *jas_seq2d_copy(jas_seq2d_t *x)
{
	jas_matrix_t *y;
	int i;
	int j;
	y = jas_seq2d_create(jas_seq2d_xstart(x), jas_seq2d_ystart(x),
	  jas_seq2d_xend(x), jas_seq2d_yend(x));
	assert(y);
	for (i = 0; i < x->numrows_; ++i) {
		for (j = 0; j < x->numcols_; ++j) {
			*jas_matrix_getref(y, i, j) = jas_matrix_get(x, i, j);
		}
	}
	return y;
}


int jas_matrix_cmp(jas_matrix_t *mat0, jas_matrix_t *mat1)
{
	int i;
	int j;

	if (mat0->numrows_ != mat1->numrows_ || mat0->numcols_ !=
	  mat1->numcols_) {
		return 1;
	}
	for (i = 0; i < mat0->numrows_; i++) {
		for (j = 0; j < mat0->numcols_; j++) {
			if (jas_matrix_get(mat0, i, j) != jas_matrix_get(mat1, i, j)) {
				return 1;
			}
		}
	}
	return 0;
}


void jas_matrix_clip(jas_matrix_t *matrix, jas_seqent_t minval,
  jas_seqent_t maxval)
{
	int i;
	int j;
	jas_seqent_t v;
	jas_seqent_t *rowstart;
	jas_seqent_t *data;
	int rowstep;

	if (jas_matrix_numrows(matrix) > 0 && jas_matrix_numcols(matrix) > 0) {
		assert(matrix->rows_);
		rowstep = jas_matrix_rowstep(matrix);
		for (i = matrix->numrows_, rowstart = matrix->rows_[0]; i > 0; --i,
		  rowstart += rowstep) {
			data = rowstart;
			for (j = matrix->numcols_, data = rowstart; j > 0; --j,
			  ++data) {
				v = *data;
				if (v < minval) {
					*data = minval;
				} else if (v > maxval) {
					*data = maxval;
				}
			}
		}
	}
}


void UTFstring::UpdateFromUTF8()
{
  delete [] _Data;
  // find the size of the final UCS-2 string
  size_t i;
  for (_Length=0, i=0; i<UTF8string.length(); _Length++) {
    uint8 lead = static_cast<uint8>(UTF8string[i]);
    if (lead < 0x80)
      i++;
    else if ((lead >> 5) == 0x6)
      i += 2;
    else if ((lead >> 4) == 0xe)
      i += 3;
    else if ((lead >> 3) == 0x1e)
      i += 4;
    else
      // Invalid size?
      break;
  }
  _Data = new wchar_t[_Length+1];
  size_t j;
  for (j=0, i=0; i<UTF8string.length(); j++) {
    uint8 lead = static_cast<uint8>(UTF8string[i]);
    if (lead < 0x80) {
      _Data[j] = lead;
      i++;
    } else if ((lead >> 5) == 0x6) {
      _Data[j] = ((lead & 0x1F) << 6) + (UTF8string[i+1] & 0x3F);
      i += 2;
    } else if ((lead >> 4) == 0xe) {
      _Data[j] = ((lead & 0x0F) << 12) + ((UTF8string[i+1] & 0x3F) << 6) + (UTF8string[i+2] & 0x3F);
      i += 3;
    } else if ((lead >> 3) == 0x1e) {
      _Data[j] = ((lead & 0x07) << 18) + ((UTF8string[i+1] & 0x3F) << 12) + ((UTF8string[i+2] & 0x3F) << 6) + (UTF8string[i+3] & 0x3F);
      i += 4;
    } else
      // Invalid char?
      break;
  }
  _Data[j] = 0;
}


CAMLprim value caml_bitvect_test(value bv, value n)
{
  int pos = Int_val(n);
  return Val_int(Byte_u(bv, pos >> 3) & (1 << (pos & 7)));
}


void Polygon::Insert( sal_uInt16 nPos, const Point& rPt )
{
    ImplMakeUnique();

    if( nPos >= mpImplPolygon->mnPoints )
        nPos = mpImplPolygon->mnPoints;

    mpImplPolygon->ImplSplit( nPos, 1 );
    mpImplPolygon->mpPointAry[ nPos ] = rPt;
}


std::wstring CreateUniqueTempDirectory()
{
    // We need to put downloaded updates into a directory of their own, because
    // if we put it in $TMP, some DLLs could be there and interfere with the
    // installer.
    //
    // This code creates a new randomized directory name and tries to create it;
    // this process is repeated if the directory already exists.
    wchar_t tmpdir[MAX_PATH+1];
    if ( GetTempPath(MAX_PATH+1, tmpdir) == 0 )
        throw Win32Exception("Cannot create temporary directory");

    for ( ;; )
    {
        std::wstring dir(tmpdir);
        dir += L"Update-";

        UUID uuid;
        UuidCreate(&uuid);
        RPC_WSTR uuidStr;
        RPC_STATUS status = UuidToString(&uuid, &uuidStr);
        dir += reinterpret_cast<wchar_t*>(uuidStr);
        RpcStringFree(&uuidStr);

        if ( CreateDirectory(dir.c_str(), NULL) )
            return dir;
        else if ( GetLastError() != ERROR_ALREADY_EXISTS )
            throw Win32Exception("Cannot create temporary directory");
    }
}


      DSA_Verification_Operation(const DSA_PublicKey& dsa,
                                 const std::string& emsa) :
         PK_Ops::Verification_with_EMSA(emsa),
         m_group(dsa.get_group()),
         m_y(dsa.get_y()),
         m_mod_q(dsa.group_q())
         {}


AP4_HdlrAtom::AP4_HdlrAtom(AP4_UI32        size,
                           AP4_UI08        version,
                           AP4_UI32        flags,
                           AP4_ByteStream& stream) :
    AP4_Atom(AP4_ATOM_TYPE_HDLR, size, version, flags)
{
    AP4_UI32 predefined;
    stream.ReadUI32(predefined);
    stream.ReadUI32(m_HandlerType);
    stream.ReadUI32(m_Reserved[0]);
    stream.ReadUI32(m_Reserved[1]);
    stream.ReadUI32(m_Reserved[2]);

    // read the name unless it is empty
    int name_size = size-(AP4_FULL_ATOM_HEADER_SIZE+20);
    if (name_size == 0) return;
    char* name = new char[name_size+1];
    stream.Read(name, name_size);
    name[name_size] = '\0'; // force a null termination
    // handle a special case: the Quicktime files have a pascal
    // string here, but ISO MP4 files have a C string.
    // we try to detect a pascal encoding and correct it.
    if (name[0] == name_size-1) {
        m_HandlerName = name+1;
    } else {
        m_HandlerName = name;
    }
    delete[] name;
}


std::string queueloader::get_filename(const std::string& str) {
	std::string fn = ctrl->get_dlpath();

	if (fn[fn.length()-1] != NEWSBEUTER_PATH_SEP[0])
		fn.append(NEWSBEUTER_PATH_SEP);
	char buf[1024];
	snprintf(buf, sizeof(buf), "%s", str.c_str());
	char * base = basename(buf);
	if (!base || strlen(base) == 0) {
		char lbuf[128];
		time_t t = time(nullptr);
		strftime(lbuf, sizeof(lbuf), "%Y-%b-%d-%H%M%S.unknown", localtime(&t));
		fn.append(lbuf);
	} else {
		fn.append(base);
	}
	return fn;
}


untrusted_launcher_response_callback (GtkDialog                 *dialog,
                                      int                        response_id,
                                      ActivateParametersDesktop *parameters)
{
    GdkScreen *screen;
    char *uri;
    GFile *file;

    switch (response_id)
    {
        case RESPONSE_RUN:
        {
            screen = gtk_widget_get_screen (GTK_WIDGET (parameters->parent_window));
            uri = nautilus_file_get_uri (parameters->file);
            DEBUG ("Launching untrusted launcher %s", uri);
            nautilus_launch_desktop_file (screen, uri, NULL,
                                          parameters->parent_window);
            g_free (uri);
        }
        break;

        case RESPONSE_MARK_TRUSTED:
        {
            file = nautilus_file_get_location (parameters->file);
            nautilus_file_mark_desktop_file_trusted (file,
                                                     parameters->parent_window,
                                                     TRUE,
                                                     NULL, NULL);
            g_object_unref (file);
        }
        break;

        default:
        {
            /* Just destroy dialog */
        }
        break;
    }

    gtk_widget_destroy (GTK_WIDGET (dialog));
    activate_parameters_desktop_free (parameters);
}


bool IsOpened() {return hFile!=FILE_BAD_HANDLE;};


bool WideToCharMap(const wchar *Src,char *Dest,size_t DestSize,bool &Success)
{
  // String with inconvertible characters mapped to private use Unicode area
  // must have the mark code somewhere.
  if (wcschr(Src,(wchar)MappedStringMark)==NULL)
    return false;

  Success=true;
  uint SrcPos=0,DestPos=0;
  while (Src[SrcPos]!=0 && DestPos<DestSize-MB_CUR_MAX)
  {
    if (uint(Src[SrcPos])==MappedStringMark)
    {
      SrcPos++;
      continue;
    }
    // For security reasons do not restore low ASCII codes, so mapping cannot
    // be used to hide control codes like path separators.
    if (uint(Src[SrcPos])>=MapAreaStart+0x80 && uint(Src[SrcPos])<MapAreaStart+0x100)
      Dest[DestPos++]=char(uint(Src[SrcPos++])-MapAreaStart);
    else
    {
      mbstate_t ps;
      memset(&ps,0,sizeof(ps));
      if (wcrtomb(Dest+DestPos,Src[SrcPos],&ps)==-1)
      {
        Dest[DestPos]='_';
        Success=false;
      }
      SrcPos++;
      memset(&ps,0,sizeof(ps));
      int Length=mbrlen(Dest+DestPos,MB_CUR_MAX,&ps);
      DestPos+=Max(Length,1);
    }
  }
  Dest[Min(DestPos,DestSize-1)]=0;
  return true;
}


unsigned char *base64decode(const char *buf, size_t *size)
{
	if (!buf || !size) return NULL;
	size_t len = (*size > 0) ? *size : strlen(buf);
	if (len <= 0) return NULL;
	unsigned char *outbuf = (unsigned char*)malloc((len/4)*3+3);
	const char *ptr = buf;
	int p = 0;
	size_t l = 0;

	do {
		ptr += strspn(ptr, "\r\n\t ");
		if (*ptr == '\0' || ptr >= buf+len) {
			break;
		}
		l = strcspn(ptr, "\r\n\t ");
		if (l > 3 && ptr+l <= buf+len) {
			p+=base64decode_block(outbuf+p, ptr, l);
			ptr += l;
		} else {
			break;
		}
	} while (1);

	outbuf[p] = 0;
	*size = p;
	return outbuf;
}


R_API ut64 r_bin_java_element_pair_calc_size(RBinJavaElementValuePair *evp) {
	ut64 sz = 0;
	if (evp == NULL) {
		return sz;
	}
	// evp->element_name_idx = r_bin_java_read_short(bin, bin->b->cur);
	sz += 2;
	// evp->value = r_bin_java_element_value_new (bin, offset+2);
	if (evp->value) {
		sz += r_bin_java_element_value_calc_size (evp->value);
	}
	return sz;
}


R_API RBinJavaAttrInfo *r_bin_java_rtv_annotations_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	ut32 i = 0;
	ut64 offset = 0;
	if (buf_offset + 8 > sz) {
		return NULL;
	}
	RBinJavaAttrInfo *attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	offset += 6;
	if (attr) {
		attr->type = R_BIN_JAVA_ATTR_TYPE_RUNTIME_VISIBLE_ANNOTATION_ATTR;
		attr->info.annotation_array.num_annotations = R_BIN_JAVA_USHORT (buffer, offset);
		offset += 2;
		attr->info.annotation_array.annotations = r_list_newf (r_bin_java_annotation_free);
		for (i = 0; i < attr->info.annotation_array.num_annotations; i++) {
			if (offset >= sz) {
				break;
			}
			RBinJavaAnnotation *annotation = r_bin_java_annotation_new (buffer + offset, sz - offset, buf_offset + offset);
			if (annotation) {
				offset += annotation->size;
				r_list_append (attr->info.annotation_array.annotations, (void *) annotation);
			}
		}
		attr->size = offset;
	}
	return attr;
}


Http::Response AbstractWebApplication::processRequest(const Http::Request &request, const Http::Environment &env)
{
    session_ = 0;
    request_ = request;
    env_ = env;

    clear(); // clear response

    sessionInitialize();
    if (!sessionActive() && !isAuthNeeded())
        sessionStart();

    if (isBanned()) {
        status(403, "Forbidden");
        print(QObject::tr("Your IP address has been banned after too many failed authentication attempts."), Http::CONTENT_TYPE_TXT);
    }
    else {
        processRequest();
    }

    return response();
}


static int lookup1_values(int entries, int dim)
{
   int r = (int) floor(exp((float) log((float) entries) / dim));
   if ((int) floor(pow((float) r+1, dim)) <= entries)   // (int) cast for MinGW warning;
      ++r;                                              // floor() to avoid _ftol() when non-CRT
   assert(pow((float) r+1, dim) > entries);
   assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above
   return r;
}


static float *get_window(vorb *f, int len)
{
   len <<= 1;
   if (len == f->blocksize_0) return f->window[0];
   if (len == f->blocksize_1) return f->window[1];
   assert(0);
   return NULL;
}


static __forceinline void draw_line(float *output, int x0, int y0, int x1, int y1, int n)
{
   int dy = y1 - y0;
   int adx = x1 - x0;
   int ady = abs(dy);
   int base;
   int x=x0,y=y0;
   int err = 0;
   int sy;

#ifdef STB_VORBIS_DIVIDE_TABLE
   if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) {
      if (dy < 0) {
         base = -integer_divide_table[ady][adx];
         sy = base-1;
      } else {
         base =  integer_divide_table[ady][adx];
         sy = base+1;
      }
   } else {
      base = dy / adx;
      if (dy < 0)
         sy = base - 1;
      else
         sy = base+1;
   }
#else
   base = dy / adx;
   if (dy < 0)
      sy = base - 1;
   else
      sy = base+1;
#endif
   ady -= abs(base) * adx;
   if (x1 > n) x1 = n;
   if (x < x1) {
      LINE_OP(output[x], inverse_db_table[y]);
      for (++x; x < x1; ++x) {
         err += ady;
         if (err >= adx) {
            err -= adx;
            y += sy;
         } else
            y += base;
         LINE_OP(output[x], inverse_db_table[y]);
      }
   }
}


QStringList JlCompress::extractDir(QuaZip &zip, const QString &dir)
{
    if(!zip.open(QuaZip::mdUnzip)) {
        return QStringList();
    }

    QDir directory(dir);
    QStringList extracted;
    if (!zip.goToFirstFile()) {
        return QStringList();
    }
    do {
        QString name = zip.getCurrentFileName();
        QString absFilePath = directory.absoluteFilePath(name);
        if (!extractFile(&zip, "", absFilePath)) {
            removeFile(extracted);
            return QStringList();
        }
        extracted.append(absFilePath);
    } while (zip.goToNextFile());

    // Chiudo il file zip
    zip.close();
    if(zip.getZipError()!=0) {
        removeFile(extracted);
        return QStringList();
    }

    return extracted;
}


RemoteDevicePropertiesWidget::RemoteDevicePropertiesWidget(QWidget *parent)
    : QWidget(parent)
    , modified(false)
    , saveable(false)
{
    setupUi(this);
    if (qobject_cast<QTabWidget *>(parent)) {
        verticalLayout->setMargin(4);
    }
    type->addItem(tr("Samba Share"), (int)Type_Samba);
    type->addItem(tr("Samba Share (Auto-discover host and port)"), (int)Type_SambaAvahi);
    type->addItem(tr("Secure Shell (sshfs)"), (int)Type_SshFs);
    type->addItem(tr("Locally Mounted Folder"), (int)Type_File);
}


RemoteFsDevice::RemoteFsDevice(MusicLibraryModel *m, const DeviceOptions &options, const Details &d)
    : FsDevice(m, d.name, createUdi(d.name))
    , mountToken(0)
    , currentMountStatus(false)
    , details(d)
    , proc(0)
    , mounterIface(0)
    , messageSent(false)
{
    opts=options;
//    details.path=Utils::fixPath(details.path);
    load();
    mount();
    icn=MonoIcon::icon(details.isLocalFile()
                       ? FontAwesome::foldero
                       : constSshfsProtocol==details.url.scheme()
                         ? FontAwesome::linux_os
                         : FontAwesome::windows, Utils::monoIconColor());
}


void RemoteFsDevice::serviceAdded(const QString &name)
{
    if (name==details.serviceName && constSambaAvahiProtocol==details.url.scheme()) {
        sub=tr("Available");
        updateStatus();
    }
}


static inline bool isMountable(const RemoteFsDevice::Details &d)
{
    return RemoteFsDevice::constSshfsProtocol==d.url.scheme() ||
           RemoteFsDevice::constSambaProtocol==d.url.scheme() || RemoteFsDevice::constSambaAvahiProtocol==d.url.scheme();
}


void RemoteFsDevice::load()
{
    if (RemoteFsDevice::constSambaAvahiProtocol==details.url.scheme()) {
        // Start Avahi listener...
        Avahi::self();
        QUrlQuery q(details.url);
        if (q.hasQueryItem(constServiceNameQuery)) {
            details.serviceName=q.queryItemValue(constServiceNameQuery);
        }

        if (!details.serviceName.isEmpty()) {
            AvahiService *srv=Avahi::self()->getService(details.serviceName);
            if (!srv || srv->getHost().isEmpty()) {
                sub=tr("Not Available");
            } else {
                sub=tr("Available");
            }
        }
        connect(Avahi::self(), SIGNAL(serviceAdded(QString)), SLOT(serviceAdded(QString)));
        connect(Avahi::self(), SIGNAL(serviceRemoved(QString)), SLOT(serviceRemoved(QString)));
    }

    if (isConnected()) {
        setAudioFolder();
        readOpts(settingsFileName(), opts, true);
        rescan(false); // Read from cache if we have it!
    }
}


void ResourceHandle::FromProto(const ResourceHandleProto& proto) {
  set_device(proto.device());
  set_container(proto.container());
  set_name(proto.name());
  set_hash_code(proto.hash_code());
  set_maybe_type_name(proto.maybe_type_name());
  std::vector<DtypeAndPartialTensorShape> dtypes_and_shapes;
  for (const auto& dtype_and_shape : proto.dtypes_and_shapes()) {
    DataType dtype = dtype_and_shape.dtype();
    PartialTensorShape shape(dtype_and_shape.shape());
    dtypes_and_shapes.push_back(DtypeAndPartialTensorShape{dtype, shape});
  }
  dtypes_and_shapes_ = std::move(dtypes_and_shapes);
}


StatusOr<FullTypeDef> SpecializeType(const AttrSlice& attrs,
                                     const OpDef& op_def) {
  FullTypeDef ft;
  ft.set_type_id(TFT_PRODUCT);

  for (int i = 0; i < op_def.output_arg_size(); i++) {
    auto* t = ft.add_args();

    *t = op_def.output_arg(i).experimental_full_type();

    // Resolve dependent types. The convention for op registrations is to use
    // attributes as type variables.
    // See https://www.tensorflow.org/guide/create_op#type_polymorphism.
    // Once the op signature can be defined entirely in FullType, this
    // convention can be deprecated.
    //
    // Note: While this code performs some basic verifications, it generally
    // assumes consistent op defs and attributes. If more complete
    // verifications are needed, they should be done by separately, and in a
    // way that can be reused for type inference.
    for (int j = 0; j < t->args_size(); j++) {
      auto* arg = t->mutable_args(i);
      if (arg->type_id() == TFT_VAR) {
        const auto* attr = attrs.Find(arg->s());
        DCHECK(attr != nullptr);
        if (attr->value_case() == AttrValue::kList) {
          const auto& attr_list = attr->list();
          arg->set_type_id(TFT_PRODUCT);
          for (int i = 0; i < attr_list.type_size(); i++) {
            map_dtype_to_tensor(attr_list.type(i), arg->add_args());
          }

        } else if (attr->value_case() == AttrValue::kType) {
          map_dtype_to_tensor(attr->type(), arg);

        } else {
          return Status(error::UNIMPLEMENTED,
                        absl::StrCat("unknown attribute type",
                                     attrs.DebugString(), " key=", arg->s()));
        }

        arg->clear_s();
      }
    }
  }

  return ft;
}


Status OpLevelCostEstimator::PredictFusedBatchNorm(
    const OpContext& op_context, NodeCosts* node_costs) const {
  bool found_unknown_shapes = false;
  const auto& op_info = op_context.op_info;
  // x: op_info.inputs(0)
  // scale: op_info.inputs(1)
  // offset: op_info.inputs(2)
  // mean: op_info.inputs(3)  --> only for inference
  // variance: op_info.inputs(4) --> only for inference
  ConvolutionDimensions dims = OpDimensionsFromInputs(
      op_info.inputs(0).shape(), op_info, &found_unknown_shapes);
  const bool is_training = IsTraining(op_info);

  int64_t ops = 0;
  const auto rsqrt_cost = Eigen::internal::functor_traits<
      Eigen::internal::scalar_rsqrt_op<float>>::Cost;
  if (is_training) {
    ops = dims.iz * (dims.batch * dims.ix * dims.iy * 4 + 6 + rsqrt_cost);
  } else {
    ops = dims.batch * dims.ix * dims.iy * dims.iz * 2;
  }
  node_costs->num_compute_ops = ops;

  const int64_t size_nhwc =
      CalculateTensorSize(op_info.inputs(0), &found_unknown_shapes);
  const int64_t size_c =
      CalculateTensorSize(op_info.inputs(1), &found_unknown_shapes);
  if (is_training) {
    node_costs->num_input_bytes_accessed = {size_nhwc, size_c, size_c};
    node_costs->num_output_bytes_accessed = {size_nhwc, size_c, size_c, size_c,
                                             size_c};
    // FusedBatchNorm in training mode internally re-reads the input tensor:
    // one for mean/variance, and the 2nd internal read forthe actual scaling.
    // Assume small intermediate data such as mean / variance (size_c) can be
    // cached on-chip.
    node_costs->internal_read_bytes = size_nhwc;
  } else {
    node_costs->num_input_bytes_accessed = {size_nhwc, size_c, size_c, size_c,
                                            size_c};
    node_costs->num_output_bytes_accessed = {size_nhwc};
  }
  node_costs->max_memory = node_costs->num_total_output_bytes();

  if (found_unknown_shapes) {
    node_costs->inaccurate = true;
    node_costs->num_nodes_with_unknown_shapes = 1;
  }
  return Status::OK();
}


TEST_F(QuantizedConv2DTest, OddPadding) {
  const int stride = 2;
  TF_ASSERT_OK(NodeDefBuilder("quantized_conv_op", "QuantizedConv2D")
                   .Input(FakeInput(DT_QUINT8))
                   .Input(FakeInput(DT_QUINT8))
                   .Input(FakeInput(DT_FLOAT))
                   .Input(FakeInput(DT_FLOAT))
                   .Input(FakeInput(DT_FLOAT))
                   .Input(FakeInput(DT_FLOAT))
                   .Attr("out_type", DataTypeToEnum<qint32>::v())
                   .Attr("strides", {1, stride, stride, 1})
                   .Attr("padding", "SAME")
                   .Finalize(node_def()));
  TF_ASSERT_OK(InitOp());

  const int depth = 1;
  const int image_width = 4;
  const int image_height = 4;
  const int image_batch_count = 1;
  AddInputFromArray<quint8>(
      TensorShape({image_batch_count, image_height, image_width, depth}),
      {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
  const int filter_size = 3;
  const int filter_count = 1;
  AddInputFromArray<quint8>(
      TensorShape({filter_size, filter_size, depth, filter_count}),
      {1, 2, 3, 4, 5, 6, 7, 8, 9});
  AddInputFromArray<float>(TensorShape({1}), {0});
  AddInputFromArray<float>(TensorShape({1}), {255.0f});
  AddInputFromArray<float>(TensorShape({1}), {0});
  AddInputFromArray<float>(TensorShape({1}), {255.0f});

  TF_ASSERT_OK(RunOpKernel());
  const int expected_width = image_width / stride;
  const int expected_height = (image_height * filter_count) / stride;
  Tensor expected(DT_QINT32, TensorShape({image_batch_count, expected_height,
                                          expected_width, filter_count}));
  test::FillValues<qint32>(&expected, {348, 252, 274, 175});
  test::ExpectTensorEqual<qint32>(expected, *GetOutput(0));
}


bool IsReshapeOpSupported(const TfLiteRegistration* registration,
                          const TfLiteNode* node, TfLiteContext* context,
                          int coreml_version) {
  if (coreml_version >= 3) {
    return false;
  }
  if (node->inputs->size == 1) {
    const auto* params =
        reinterpret_cast<TfLiteReshapeParams*>(node->builtin_data);
    return params->num_dimensions == 3 || params->num_dimensions == 4;
  }

  const int kShapeTensor = 1;
  const auto* shape = GetInput(context, node, kShapeTensor);
  if (shape->allocation_type != kTfLiteMmapRo) {
    TF_LITE_KERNEL_LOG(context, "Reshape has non-const shape.");
    return false;
  }
  const bool is_shape_tensor =
      shape->dims->size == 1 && shape->type == kTfLiteInt32;
  return is_shape_tensor &&
         (shape->dims->data[0] == 3 || shape->dims->data[0] == 4);
}


TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  const CTCBeamSearchDecoderParams* option =
      reinterpret_cast<CTCBeamSearchDecoderParams*>(node->user_data);
  const int top_paths = option->top_paths;
  TF_LITE_ENSURE(context, option->beam_width >= top_paths);
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
  // The outputs should be top_paths * 3 + 1.
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 3 * top_paths + 1);

  const TfLiteTensor* inputs = GetInput(context, node, kInputsTensor);
  TF_LITE_ENSURE_EQ(context, NumDimensions(inputs), 3);
  // TensorFlow only supports float.
  TF_LITE_ENSURE_EQ(context, inputs->type, kTfLiteFloat32);
  const int batch_size = SizeOfDimension(inputs, 1);

  const TfLiteTensor* sequence_length =
      GetInput(context, node, kSequenceLengthTensor);
  TF_LITE_ENSURE_EQ(context, NumDimensions(sequence_length), 1);
  TF_LITE_ENSURE_EQ(context, NumElements(sequence_length), batch_size);
  // TensorFlow only supports int32.
  TF_LITE_ENSURE_EQ(context, sequence_length->type, kTfLiteInt32);

  // Resize decoded outputs.
  // Do not resize indices & values cause we don't know the values yet.
  for (int i = 0; i < top_paths; ++i) {
    TfLiteTensor* indices = GetOutput(context, node, i);
    SetTensorToDynamic(indices);
    TfLiteTensor* values = GetOutput(context, node, i + top_paths);
    SetTensorToDynamic(values);
    TfLiteTensor* output_shape = GetOutput(context, node, i + 2 * top_paths);
    SetTensorToDynamic(output_shape);
  }

  // Resize log probability outputs.
  TfLiteTensor* log_probability_output =
      GetOutput(context, node, top_paths * 3);
  TfLiteIntArray* log_probability_output_shape_array = TfLiteIntArrayCreate(2);
  log_probability_output_shape_array->data[0] = batch_size;
  log_probability_output_shape_array->data[1] = top_paths;
  return context->ResizeTensor(context, log_probability_output,
                               log_probability_output_shape_array);
}


TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  auto* data =
      reinterpret_cast<TfLiteAudioMicrofrontendParams*>(node->user_data);
  FrontendReset(data->state);

  const TfLiteTensor* input = GetInput(context, node, kInputTensor);
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);

  if (data->out_float) {
    GenerateFeatures<float>(data, input, output);
  } else {
    GenerateFeatures<int32>(data, input, output);
  }

  return kTfLiteOk;
}


TfLiteRegistration GetPassthroughOpRegistration() {
  TfLiteRegistration reg = {nullptr, nullptr, nullptr, nullptr};
  reg.init = [](TfLiteContext* context, const char*, size_t) -> void* {
    auto* first_new_tensor = new int;
    context->AddTensors(context, 2, first_new_tensor);
    return first_new_tensor;
  };
  reg.free = [](TfLiteContext* context, void* buffer) {
    delete static_cast<int*>(buffer);
  };
  reg.prepare = [](TfLiteContext* context, TfLiteNode* node) {
    auto* first_new_tensor = static_cast<int*>(node->user_data);

    const TfLiteTensor* tensor0 = GetInput(context, node, 0);
    TfLiteTensor* tensor1 = GetOutput(context, node, 0);

    TfLiteIntArray* newSize = TfLiteIntArrayCopy(tensor0->dims);
    TF_LITE_ENSURE_STATUS(context->ResizeTensor(context, tensor1, newSize));

    TfLiteIntArrayFree(node->temporaries);
    node->temporaries = TfLiteIntArrayCreate(2);
    for (int i = 0; i < 2; ++i) {
      node->temporaries->data[i] = *(first_new_tensor) + i;
    }

    auto setup_temporary = [&](int id) {
      TfLiteTensor* tmp = &context->tensors[id];
      tmp->type = kTfLiteFloat32;
      tmp->allocation_type = kTfLiteArenaRw;
      return context->ResizeTensor(context, tmp,
                                   TfLiteIntArrayCopy(tensor0->dims));
    };
    TF_LITE_ENSURE_STATUS(setup_temporary(node->temporaries->data[0]));
    TF_LITE_ENSURE_STATUS(setup_temporary(node->temporaries->data[1]));

    return kTfLiteOk;
  };
  reg.invoke = [](TfLiteContext* context, TfLiteNode* node) {
    const TfLiteTensor* a0 = GetInput(context, node, 0);

    auto populate = [&](int id) {
      TfLiteTensor* t = &context->tensors[id];
      int num = a0->dims->data[0];
      for (int i = 0; i < num; i++) {
        t->data.f[i] = a0->data.f[i];
      }
    };

    populate(node->outputs->data[0]);
    populate(node->temporaries->data[0]);
    populate(node->temporaries->data[1]);
    return kTfLiteOk;
  };

  return reg;
}


TfLiteStatus EvalHashtableFind(TfLiteContext* context, TfLiteNode* node) {
  const TfLiteTensor* input_resource_id_tensor =
      GetInput(context, node, kInputResourceIdTensor);
  int resource_id = input_resource_id_tensor->data.i32[0];

  const TfLiteTensor* key_tensor = GetInput(context, node, kKeyTensor);
  const TfLiteTensor* default_value_tensor =
      GetInput(context, node, kDefaultValueTensor);
  TfLiteTensor* output_tensor = GetOutput(context, node, 0);

  Subgraph* subgraph = reinterpret_cast<Subgraph*>(context->impl_);
  auto& resources = subgraph->resources();
  auto* lookup = resource::GetHashtableResource(&resources, resource_id);
  TF_LITE_ENSURE(context, lookup != nullptr);
  TF_LITE_ENSURE_STATUS(
      lookup->CheckKeyAndValueTypes(context, key_tensor, output_tensor));
  auto result =
      lookup->Lookup(context, key_tensor, output_tensor, default_value_tensor);
  return result;
}


TfLiteStatus SimpleOpEval(TfLiteContext* context, TfLiteNode* node) {
  const TfLiteTensor* input1 = tflite::GetInput(context, node, /*index=*/0);
  const TfLiteTensor* input2 = tflite::GetInput(context, node, /*index=*/1);

  TfLiteTensor* output = GetOutput(context, node, /*index=*/0);

  int32_t* output_data = output->data.i32;
  *output_data = *(input1->data.i32) + *(input2->data.i32);
  return kTfLiteOk;
}


TfLiteStatus SimpleStatefulOp::Invoke(TfLiteContext* context,
                                      TfLiteNode* node) {
  OpData* data = reinterpret_cast<OpData*>(node->user_data);
  *data->invoke_count += 1;

  const TfLiteTensor* input = GetInput(context, node, kInputTensor);
  const uint8_t* input_data = GetTensorData<uint8_t>(input);
  int size = NumElements(input->dims);

  uint8_t* sorting_buffer = reinterpret_cast<uint8_t*>(
      context->GetScratchBuffer(context, data->sorting_buffer));
  // Copy inputs data to the sorting buffer. We don't want to mutate the input
  // tensor as it might be used by a another node.
  for (int i = 0; i < size; i++) {
    sorting_buffer[i] = input_data[i];
  }

  // In place insertion sort on `sorting_buffer`.
  for (int i = 1; i < size; i++) {
    for (int j = i; j > 0 && sorting_buffer[j] < sorting_buffer[j - 1]; j--) {
      std::swap(sorting_buffer[j], sorting_buffer[j - 1]);
    }
  }

  TfLiteTensor* median = GetOutput(context, node, kMedianTensor);
  uint8_t* median_data = GetTensorData<uint8_t>(median);
  TfLiteTensor* invoke_count = GetOutput(context, node, kInvokeCount);
  int32_t* invoke_count_data = GetTensorData<int32_t>(invoke_count);

  median_data[0] = sorting_buffer[size / 2];
  invoke_count_data[0] = *data->invoke_count;
  return kTfLiteOk;
}


TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  const TfLiteTensor* input = GetInput(context, node, kInputTensor);
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
  TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteFloat32);
  output->type = input->type;
  TfLiteIntArray* output_size = TfLiteIntArrayCopy(input->dims);
  return context->ResizeTensor(context, output, output_size);
}


TfLiteStatus ComparisonPrepareCommon(TfLiteContext* context, TfLiteNode* node,
                                     bool is_string_allowed) {
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);

  const TfLiteTensor* input1 = GetInput(context, node, kInputTensor1);
  const TfLiteTensor* input2 = GetInput(context, node, kInputTensor2);
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);

  // Don't support string.
  if (!is_string_allowed) {
    TF_LITE_ENSURE(context, input1->type != kTfLiteString);
  }
  // Currently only support tensors have the same type.
  TF_LITE_ENSURE_TYPES_EQ(context, input1->type, input2->type);
  output->type = kTfLiteBool;

  bool requires_broadcast = !HaveSameShapes(input1, input2);

  TfLiteIntArray* output_size = nullptr;
  if (requires_broadcast) {
    TF_LITE_ENSURE_OK(context, CalculateShapeForBroadcast(
                                   context, input1, input2, &output_size));
  } else {
    output_size = TfLiteIntArrayCopy(input1->dims);
  }

  return context->ResizeTensor(context, output, output_size);
}


TfLiteStatus NonMaxSuppressionMultiClass(TfLiteContext* context,
                                         TfLiteNode* node, OpData* op_data) {
  // Get the input tensors
  const TfLiteTensor* input_box_encodings =
      GetInput(context, node, kInputTensorBoxEncodings);
  const TfLiteTensor* input_class_predictions =
      GetInput(context, node, kInputTensorClassPredictions);
  const int num_boxes = input_box_encodings->dims->data[1];
  const int num_classes = op_data->num_classes;
  TF_LITE_ENSURE_EQ(context, input_class_predictions->dims->data[0],
                    kBatchSize);
  TF_LITE_ENSURE_EQ(context, input_class_predictions->dims->data[1], num_boxes);
  const int num_classes_with_background =
      input_class_predictions->dims->data[2];

  TF_LITE_ENSURE(context, (num_classes_with_background - num_classes <= 1));
  TF_LITE_ENSURE(context, (num_classes_with_background >= num_classes));

  const TfLiteTensor* scores;
  switch (input_class_predictions->type) {
    case kTfLiteUInt8: {
      TfLiteTensor* temporary_scores = &context->tensors[op_data->scores_index];
      DequantizeClassPredictions(input_class_predictions, num_boxes,
                                 num_classes_with_background, temporary_scores);
      scores = temporary_scores;
    } break;
    case kTfLiteFloat32:
      scores = input_class_predictions;
      break;
    default:
      // Unsupported type.
      return kTfLiteError;
  }
  if (op_data->use_regular_non_max_suppression)
    TF_LITE_ENSURE_STATUS(NonMaxSuppressionMultiClassRegularHelper(
        context, node, op_data, GetTensorData<float>(scores)));
  else
    TF_LITE_ENSURE_STATUS(NonMaxSuppressionMultiClassFastHelper(
        context, node, op_data, GetTensorData<float>(scores)));

  return kTfLiteOk;
}


inline TfLiteStatus EvalImpl(TfLiteContext* context, TfLiteNode* node,
                             std::function<T(T)> func,
                             TfLiteType expected_type) {
  const TfLiteTensor* input = GetInput(context, node, 0);
  TfLiteTensor* output = GetOutput(context, node, 0);
  TF_LITE_ENSURE_TYPES_EQ(context, input->type, expected_type);
  const int64_t num_elements = NumElements(input);
  const T* in_data = GetTensorData<T>(input);
  T* out_data = GetTensorData<T>(output);
  for (int64_t i = 0; i < num_elements; ++i) {
    out_data[i] = func(in_data[i]);
  }
  return kTfLiteOk;
}


TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node) {
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
  const TfLiteTensor* input = GetInput(context, node, 0);
  TfLiteTensor* output = GetOutput(context, node, 0);
  TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);
  if (!is_supported_type(input->type)) {
    TF_LITE_UNSUPPORTED_TYPE(context, input->type, op_name);
  }
  return context->ResizeTensor(context, output,
                               TfLiteIntArrayCopy(input->dims));
}


TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 5);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);

  const TfLiteTensor* ids = GetInput(context, node, 0);
  TF_LITE_ENSURE_EQ(context, NumDimensions(ids), 1);
  TF_LITE_ENSURE_EQ(context, ids->type, kTfLiteInt32);

  const TfLiteTensor* indices = GetInput(context, node, 1);
  TF_LITE_ENSURE_EQ(context, NumDimensions(indices), 2);
  TF_LITE_ENSURE_EQ(context, indices->type, kTfLiteInt32);

  const TfLiteTensor* shape = GetInput(context, node, 2);
  TF_LITE_ENSURE_EQ(context, NumDimensions(shape), 1);
  TF_LITE_ENSURE_EQ(context, shape->type, kTfLiteInt32);

  const TfLiteTensor* weights = GetInput(context, node, 3);
  TF_LITE_ENSURE_EQ(context, NumDimensions(weights), 1);
  TF_LITE_ENSURE_EQ(context, weights->type, kTfLiteFloat32);

  TF_LITE_ENSURE_EQ(context, SizeOfDimension(indices, 0),
                    SizeOfDimension(ids, 0));
  TF_LITE_ENSURE_EQ(context, SizeOfDimension(indices, 0),
                    SizeOfDimension(weights, 0));

  const TfLiteTensor* value = GetInput(context, node, 4);
  TF_LITE_ENSURE(context, NumDimensions(value) >= 2);

  // Mark the output as a dynamic tensor.
  TfLiteTensor* output = GetOutput(context, node, 0);
  TF_LITE_ENSURE_TYPES_EQ(context, output->type, kTfLiteFloat32);
  output->allocation_type = kTfLiteDynamic;

  return kTfLiteOk;
}


TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  const TfLiteTensor* value = GetInput(context, node, kValueTensor);

  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);

  if (IsDynamicTensor(output)) {
    const TfLiteTensor* dims = GetInput(context, node, kDimsTensor);
    TF_LITE_ENSURE_OK(context, ResizeOutput(context, dims, output));
  }
#define TF_LITE_FILL(data_type)                                               \
  reference_ops::Fill(GetTensorShape(value), GetTensorData<data_type>(value), \
                      GetTensorShape(output),                                 \
                      GetTensorData<data_type>(output))
  switch (output->type) {
    case kTfLiteInt32:
      TF_LITE_FILL(int32_t);
      break;
    case kTfLiteInt64:
      TF_LITE_FILL(int64_t);
      break;
    case kTfLiteFloat32:
      TF_LITE_FILL(float);
      break;
    case kTfLiteBool:
      TF_LITE_FILL(bool);
      break;
    case kTfLiteString:
      FillString(value, output);
      break;
    default:
      context->ReportError(
          context,
          "Fill only currently supports int32, int64, float32, bool, string "
          "for input 1, got %d.",
          value->type);
      return kTfLiteError;
  }
#undef TF_LITE_FILL
  return kTfLiteOk;
}


TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  OpData* data = reinterpret_cast<OpData*>(node->user_data);

  const TfLiteTensor* input1 = GetInput(context, node, kInputTensor1);
  const TfLiteTensor* input2 = GetInput(context, node, kInputTensor2);
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);

  switch (input1->type) {
    case kTfLiteInt32: {
      return EvalImpl<int32_t>(context, data->requires_broadcast, input1,
                               input2, output);
    }
    case kTfLiteFloat32: {
      return EvalImpl<float>(context, data->requires_broadcast, input1, input2,
                             output);
    }
    default: {
      context->ReportError(context, "Type '%s' is not supported by floor_div.",
                           TfLiteTypeGetName(input1->type));
      return kTfLiteError;
    }
  }
}


TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 3);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 2);

  const TfLiteTensor* lookup = GetInput(context, node, 0);
  TF_LITE_ENSURE_EQ(context, NumDimensions(lookup), 1);
  TF_LITE_ENSURE_EQ(context, lookup->type, kTfLiteInt32);

  const TfLiteTensor* key = GetInput(context, node, 1);
  TF_LITE_ENSURE_EQ(context, NumDimensions(key), 1);
  TF_LITE_ENSURE_EQ(context, key->type, kTfLiteInt32);

  const TfLiteTensor* value = GetInput(context, node, 2);
  TF_LITE_ENSURE(context, NumDimensions(value) >= 1);
  TF_LITE_ENSURE_EQ(context, SizeOfDimension(key, 0),
                    SizeOfDimension(value, 0));
  if (value->type == kTfLiteString) {
    TF_LITE_ENSURE_EQ(context, NumDimensions(value), 1);
  }

  TfLiteTensor* hits = GetOutput(context, node, 1);
  TF_LITE_ENSURE_EQ(context, hits->type, kTfLiteUInt8);
  TfLiteIntArray* hitSize = TfLiteIntArrayCreate(1);
  hitSize->data[0] = SizeOfDimension(lookup, 0);

  TfLiteTensor* output = GetOutput(context, node, 0);
  TF_LITE_ENSURE_EQ(context, value->type, output->type);

  TfLiteStatus status = kTfLiteOk;
  if (output->type != kTfLiteString) {
    TfLiteIntArray* outputSize = TfLiteIntArrayCreate(NumDimensions(value));
    outputSize->data[0] = SizeOfDimension(lookup, 0);
    for (int i = 1; i < NumDimensions(value); i++) {
      outputSize->data[i] = SizeOfDimension(value, i);
    }
    status = context->ResizeTensor(context, output, outputSize);
  }
  if (context->ResizeTensor(context, hits, hitSize) != kTfLiteOk) {
    status = kTfLiteError;
  }
  return status;
}


TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  TfLiteTensor* output = GetOutput(context, node, 0);
  TfLiteTensor* hits = GetOutput(context, node, 1);
  const TfLiteTensor* lookup = GetInput(context, node, 0);
  const TfLiteTensor* key = GetInput(context, node, 1);
  const TfLiteTensor* value = GetInput(context, node, 2);

  const int num_rows = SizeOfDimension(value, 0);
  const int row_bytes = value->bytes / num_rows;
  void* pointer = nullptr;
  DynamicBuffer buf;

  for (int i = 0; i < SizeOfDimension(lookup, 0); i++) {
    int idx = -1;
    pointer = bsearch(&(lookup->data.i32[i]), key->data.i32, num_rows,
                      sizeof(int32_t), greater);
    if (pointer != nullptr) {
      idx = (reinterpret_cast<char*>(pointer) - (key->data.raw)) /
            sizeof(int32_t);
    }

    if (idx >= num_rows || idx < 0) {
      if (output->type == kTfLiteString) {
        buf.AddString(nullptr, 0);
      } else {
        memset(output->data.raw + i * row_bytes, 0, row_bytes);
      }
      hits->data.uint8[i] = 0;
    } else {
      if (output->type == kTfLiteString) {
        buf.AddString(GetString(value, idx));
      } else {
        memcpy(output->data.raw + i * row_bytes,
               value->data.raw + idx * row_bytes, row_bytes);
      }
      hits->data.uint8[i] = 1;
    }
  }
  if (output->type == kTfLiteString) {
    buf.WriteToTensorAsVector(output);
  }

  return kTfLiteOk;
}


TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node) {
  auto* params = reinterpret_cast<TfLitePoolParams*>(node->builtin_data);
  OpData* data = reinterpret_cast<OpData*>(node->user_data);

  TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
  TfLiteTensor* output = GetOutput(context, node, 0);
  const TfLiteTensor* input = GetInput(context, node, 0);
  TF_LITE_ENSURE_EQ(context, NumDimensions(input), 4);
  TF_LITE_ENSURE_TYPES_EQ(context, input->type, output->type);

  int batches = input->dims->data[0];
  int height = input->dims->data[1];
  int width = input->dims->data[2];
  int channels_out = input->dims->data[3];

  // Matching GetWindowedOutputSize in TensorFlow.
  auto padding = params->padding;
  int out_width, out_height;

  data->padding = ComputePaddingHeightWidth(
      params->stride_height, params->stride_width, 1, 1, height, width,
      params->filter_height, params->filter_width, padding, &out_height,
      &out_width);

  if (input->type == kTfLiteUInt8 || input->type == kTfLiteInt8) {
    if (pool_type == kAverage || pool_type == kMax) {
      TFLITE_DCHECK_LE(std::abs(input->params.scale - output->params.scale),
                       1.0e-6);
      TFLITE_DCHECK_EQ(input->params.zero_point, output->params.zero_point);
    }
    if (pool_type == kL2) {
      // We currently don't have a quantized implementation of L2Pool
      TF_LITE_ENSURE_TYPES_EQ(context, input->type, kTfLiteFloat32);
    }
  }

  TfLiteIntArray* output_size = TfLiteIntArrayCreate(4);
  output_size->data[0] = batches;
  output_size->data[1] = out_height;
  output_size->data[2] = out_width;
  output_size->data[3] = channels_out;
  return context->ResizeTensor(context, output, output_size);
}


TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);

  OpData* data = reinterpret_cast<OpData*>(node->user_data);

  const TfLiteTensor* input1 = GetInput(context, node, kInputTensor1);
  const TfLiteTensor* input2 = GetInput(context, node, kInputTensor2);
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);

  TF_LITE_ENSURE_TYPES_EQ(context, input1->type, input2->type);

  const TfLiteType type = input1->type;
  if (type != kTfLiteInt32 && type != kTfLiteFloat32) {
    TF_LITE_KERNEL_LOG(context, "Unsupported data type %s.",
                       TfLiteTypeGetName(type));
    return kTfLiteError;
  }
  output->type = type;

  data->requires_broadcast = !HaveSameShapes(input1, input2);

  TfLiteIntArray* output_size = nullptr;
  if (data->requires_broadcast) {
    TF_LITE_ENSURE_OK(context, CalculateShapeForBroadcast(
                                   context, input1, input2, &output_size));
  } else {
    output_size = TfLiteIntArrayCopy(input1->dims);
  }

  return context->ResizeTensor(context, output, output_size);
}


TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  OpData* data = static_cast<OpData*>(node->user_data);
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);

  const TfLiteTensor* input = GetInput(context, node, 0);
  TfLiteTensor* output = GetOutput(context, node, 0);

  // TODO(b/128934713): Add support for fixed-point per-channel quantization.
  // Currently this only support affine per-layer quantization.
  TF_LITE_ENSURE_EQ(context, output->quantization.type,
                    kTfLiteAffineQuantization);
  const auto* affine_quantization =
      static_cast<TfLiteAffineQuantization*>(output->quantization.params);
  TF_LITE_ENSURE(context, affine_quantization);
  TF_LITE_ENSURE(context, affine_quantization->scale);
  TF_LITE_ENSURE(context, affine_quantization->scale->size == 1);

  if (input->type == kTfLiteFloat32) {
    // Quantize use case.
    TF_LITE_ENSURE(context, output->type == kTfLiteUInt8 ||
                                output->type == kTfLiteInt8 ||
                                output->type == kTfLiteInt16);
  } else {
    // Requantize use case.
    if (input->type == kTfLiteInt16) {
      TF_LITE_ENSURE(
          context, output->type == kTfLiteInt8 || output->type == kTfLiteInt16);
    } else {
      TF_LITE_ENSURE(context,
                     input->type == kTfLiteInt8 || input->type == kTfLiteUInt8);
      TF_LITE_ENSURE(
          context, output->type == kTfLiteUInt8 || output->type == kTfLiteInt8);
    }
    const double effective_output_scale =
        static_cast<double>(input->params.scale) /
        static_cast<double>(output->params.scale);
    QuantizeMultiplier(effective_output_scale, &data->output_multiplier,
                       &data->output_shift);
  }

  return context->ResizeTensor(context, output,
                               TfLiteIntArrayCopy(input->dims));
}


TfLiteStatus PrepareMeanOrSum(TfLiteContext* context, TfLiteNode* node) {
  TF_LITE_ENSURE_OK(context, PrepareSimple(context, node));
  OpData* data = reinterpret_cast<OpData*>(node->user_data);

  // reduce_mean requires a buffer to store intermediate sum result.
  OpContext op_context(context, node);
  if (op_context.input->type == kTfLiteInt8 ||
      op_context.input->type == kTfLiteUInt8 ||
      op_context.input->type == kTfLiteInt16) {
    const double real_multiplier =
        static_cast<double>(op_context.input->params.scale) /
        static_cast<double>(op_context.output->params.scale);
    int exponent;
    QuantizeMultiplier(real_multiplier, &data->multiplier, &exponent);
    data->shift = exponent;
  }
  TfLiteTensor* temp_sum = GetTemporary(context, node, /*index=*/2);
  if (!IsConstantTensor(op_context.axis)) {
    SetTensorToDynamic(temp_sum);
    return kTfLiteOk;
  }
  temp_sum->allocation_type = kTfLiteArenaRw;
  return ResizeTempSum(context, &op_context, temp_sum);
}


TfLiteStatus PrepareSimple(TfLiteContext* context, TfLiteNode* node) {
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);

  OpContext op_context(context, node);
  TF_LITE_ENSURE_TYPES_EQ(context, op_context.axis->type, kTfLiteInt32);
  TF_LITE_ENSURE_OK(context, InitializeTemporaries(context, node, &op_context));

  TfLiteTensor* resolved_axis = GetTemporary(context, node, /*index=*/1);
  // Leaves work to Eval if axis is not constant; else resizes output.
  if (!IsConstantTensor(op_context.axis)) {
    SetTensorToDynamic(op_context.output);
    SetTensorToDynamic(resolved_axis);
    return kTfLiteOk;
  }
  resolved_axis->allocation_type = kTfLiteArenaRw;
  TF_LITE_ENSURE_OK(context,
                    ResizeTempAxis(context, &op_context, resolved_axis));
  TF_LITE_ENSURE_OK(context, ResizeOutputTensor(context, &op_context));
  return kTfLiteOk;
}


TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  TF_LITE_ENSURE(context, NumInputs(node) == 1 || NumInputs(node) == 2);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);

  // Always postpone sizing string tensors, even if we could in principle
  // calculate their shapes now. String tensors don't benefit from having their
  // shapes precalculated because the actual memory can only be allocated after
  // we know all the content.
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);
  if (output->type != kTfLiteString) {
    if (NumInputs(node) == 1 ||
        IsConstantTensor(GetInput(context, node, kShapeTensor))) {
      TF_LITE_ENSURE_OK(context, ResizeOutput(context, node));
    } else {
      SetTensorToDynamic(output);
    }
  }
  return kTfLiteOk;
}


TfLiteStatus Prepare(TfLiteContext* context, TfLiteNode* node) {
  TF_LITE_ENSURE_EQ(context, NumInputs(node), 2);
  TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);

  const TfLiteTensor* input = GetInput(context, node, kInputTensor);
  const TfLiteTensor* size = GetInput(context, node, kSizeTensor);
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);

  // TODO(ahentz): Our current implementations rely on the input being 4D,
  // and the size being 1D tensor with exactly 2 elements.
  TF_LITE_ENSURE_EQ(context, NumDimensions(input), 4);
  TF_LITE_ENSURE_EQ(context, NumDimensions(size), 1);
  TF_LITE_ENSURE_TYPES_EQ(context, size->type, kTfLiteInt32);
  TF_LITE_ENSURE_EQ(context, size->dims->data[0], 2);

  output->type = input->type;

  if (!IsConstantTensor(size)) {
    SetTensorToDynamic(output);
    return kTfLiteOk;
  }
  return ResizeOutputTensor(context, input, size, output);
}


TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);

  switch (output->type) {
    case kTfLiteFloat32: {
      return ReverseSequenceHelper<float>(context, node);
    }
    case kTfLiteUInt8: {
      return ReverseSequenceHelper<uint8_t>(context, node);
    }
    case kTfLiteInt16: {
      return ReverseSequenceHelper<int16_t>(context, node);
    }
    case kTfLiteInt32: {
      return ReverseSequenceHelper<int32_t>(context, node);
    }
    case kTfLiteInt64: {
      return ReverseSequenceHelper<int64_t>(context, node);
    }
    default: {
      context->ReportError(context,
                           "Type '%s' is not supported by reverse_sequence.",
                           TfLiteTypeGetName(output->type));
      return kTfLiteError;
    }
  }
  return kTfLiteOk;
}  // namespace


TfLiteStatus ReverseSequenceHelper(TfLiteContext* context, TfLiteNode* node) {
  const TfLiteTensor* seq_lengths_tensor =
      GetInput(context, node, kSeqLengthsTensor);
  switch (seq_lengths_tensor->type) {
    case kTfLiteInt32: {
      return ReverseSequenceImpl<T, int32_t>(context, node);
    }
    case kTfLiteInt64: {
      return ReverseSequenceImpl<T, int64_t>(context, node);
    }
    default: {
      context->ReportError(
          context,
          "Seq_lengths type '%s' is not supported by reverse_sequence.",
          TfLiteTypeGetName(seq_lengths_tensor->type));
      return kTfLiteError;
    }
  }
  return kTfLiteOk;
}


TfLiteStatus Eval(TfLiteContext* context, TfLiteNode* node) {
  const TfLiteTensor* input = GetInput(context, node, kInputTensor);
  const TfLiteTensor* fft_length = GetInput(context, node, kFftLengthTensor);
  const int32_t* fft_length_data = GetTensorData<int32_t>(fft_length);
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);

  if (output->type != kTfLiteComplex64) {
    context->ReportError(context,
                         "Type '%s' for output is not supported by rfft2d.",
                         TfLiteTypeGetName(output->type));
    return kTfLiteError;
  }

  // Resize the output tensor if the fft_length tensor is not constant.
  // Otherwise, check if the output shape is correct.
  if (!IsConstantTensor(fft_length)) {
    TF_LITE_ENSURE_STATUS(ResizeOutputandTemporaryTensors(context, node));
  } else {
    int num_dims_output = NumDimensions(output);
    const RuntimeShape output_shape = GetTensorShape(output);
    TF_LITE_ENSURE_EQ(context, num_dims_output, NumDimensions(input));
    TF_LITE_ENSURE(context, num_dims_output >= 2);
    TF_LITE_ENSURE_EQ(context, output_shape.Dims(num_dims_output - 2),
                      fft_length_data[0]);
    TF_LITE_ENSURE_EQ(context, output_shape.Dims(num_dims_output - 1),
                      fft_length_data[1] / 2 + 1);
  }

  return Rfft2dHelper(context, node);
}


TfLiteStatus ResizeOutput(TfLiteContext* context, TfLiteNode* node) {
  const TfLiteTensor* input = GetInput(context, node, kInputTensor);
  TfLiteTensor* output = GetOutput(context, node, kOutputTensor);
  const TfLiteTensor* multipliers = GetInput(context, node, kInputMultipliers);

  const int num_dimensions = NumDimensions(input);
  const int num_multipliers = NumElements(multipliers);
  TF_LITE_ENSURE_EQ(context, num_dimensions, num_multipliers);
  switch (multipliers->type) {
    case kTfLiteInt32:
      return context->ResizeTensor(
          context, output,
          MultiplyShapeDims<int32_t>(*input->dims, multipliers,
                                     num_dimensions));
    case kTfLiteInt64:
      return context->ResizeTensor(
          context, output,
          MultiplyShapeDims<int64_t>(*input->dims, multipliers,
                                     num_dimensions));
    default:
      context->ReportError(
          context, "Multipliers of type '%s' are not supported by tile.",
          TfLiteTypeGetName(multipliers->type));
      return kTfLiteError;
  }
}