From ebb7620ad04088bda5b88b3ce263e3527410f094 Mon Sep 17 00:00:00 2001 From: Markus Scherer Date: Tue, 12 Apr 2016 22:10:50 +0000 Subject: [PATCH] ICU-12450 move unused com.ibm.icu.dev.tool.cldr.CheckSystemFonts to Unicode Tools org.unicode.unused see changeset 1022 there X-SVN-Rev: 38614 --- .../icu/dev/tool/cldr/CheckSystemFonts.java | 654 ------------------ 1 file changed, 654 deletions(-) delete mode 100644 icu4j/tools/misc/src/com/ibm/icu/dev/tool/cldr/CheckSystemFonts.java diff --git a/icu4j/tools/misc/src/com/ibm/icu/dev/tool/cldr/CheckSystemFonts.java b/icu4j/tools/misc/src/com/ibm/icu/dev/tool/cldr/CheckSystemFonts.java deleted file mode 100644 index 92de7f61196..00000000000 --- a/icu4j/tools/misc/src/com/ibm/icu/dev/tool/cldr/CheckSystemFonts.java +++ /dev/null @@ -1,654 +0,0 @@ -/* - ********************************************************************** - * Copyright (c) 2009-2012, Google, International Business Machines - * Corporation and others. All Rights Reserved. - ********************************************************************** - * Author: Mark Davis - ********************************************************************** - */ -package com.ibm.icu.dev.tool.cldr; -import java.awt.Font; -import java.awt.GraphicsEnvironment; -import java.awt.Shape; -import java.awt.font.FontRenderContext; -import java.awt.font.GlyphVector; -import java.awt.geom.AffineTransform; -import java.awt.geom.PathIterator; -import java.awt.geom.Rectangle2D; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import com.ibm.icu.dev.util.BagFormatter; -import com.ibm.icu.dev.util.Tabber.HTMLTabber; -import com.ibm.icu.dev.util.TransliteratorUtilities; -import com.ibm.icu.dev.util.UnicodeMap; -import com.ibm.icu.dev.util.UnicodeMap.Composer; -import com.ibm.icu.dev.util.UnicodeMapIterator; -import com.ibm.icu.dev.util.XEquivalenceClass.SetMaker; -import com.ibm.icu.impl.Row; -import com.ibm.icu.impl.Row.R2; -import com.ibm.icu.impl.Utility; -import com.ibm.icu.lang.UCharacter; -import com.ibm.icu.lang.UScript; -import com.ibm.icu.text.Collator; -import com.ibm.icu.text.Normalizer; -import com.ibm.icu.text.UTF16; -import com.ibm.icu.text.UnicodeSet; -import com.ibm.icu.text.UnicodeSetIterator; - - -public class CheckSystemFonts { - - static String outputDirectoryName; - static Set SKIP_SHAPES = new HashSet(); - - public static void main(String[] args) throws IOException { - System.out.println("Arguments:\t" + Arrays.asList(args)); - if (args.length < 2) { - throw new IllegalArgumentException("Need command-line args:" + - "\n\t\tfont-name-regex" + - "\n\t\toutput-directory" - ); - } - Matcher nameMatcher = Pattern.compile(args[0], Pattern.CASE_INSENSITIVE).matcher(""); - outputDirectoryName = args[1].trim(); - File outputDirectory = new File(outputDirectoryName); - if (!outputDirectory.isDirectory()) { - throw new IllegalArgumentException("2nd arg must be valid directory"); - } - loadSkipShapes(); - - Map> data = new TreeMap>(); - Map fontMap = new TreeMap(); - getFontData(nameMatcher, data, fontMap); - - showInvisibles(); - showSameGlyphs(); - - UnicodeMap> map = showEquivalentCoverage(data); - - showRawCoverage(data); - - Map, String> toShortName = showRawCoverage(map); - - showFullCoverage(map, toShortName); - } - - private static void loadSkipShapes() { - try { - BufferedReader in = BagFormatter.openUTF8Reader(outputDirectoryName, "skip_fonts.txt"); - while (true) { - String line = in.readLine(); - if (line == null) break; - String[] fonts = line.trim().split("\\s+"); - for (String font : fonts) { - SKIP_SHAPES.add(font); - } - } - in.close(); - } catch (IOException e) { - System.err.println("Couldn't open:\t" + outputDirectoryName + "/" + "skip_fonts.txt"); - } - } - - - private static final Collator English = Collator.getInstance(); - - static { - English.setStrength(Collator.SECONDARY); - } - - public static final UnicodeSet DONT_CARE = new UnicodeSet("[[:cn:][:co:][:cs:]]").freeze(); - public static final UnicodeSet COVERAGE = new UnicodeSet(DONT_CARE).complement().freeze(); - - private static final Comparator SHORTER_FIRST = new Comparator() { - public int compare(String n1, String n2) { - int result = n1.length() - n2.length(); - if (result != 0) return result; - return n1.compareTo(n2); - } - }; - - private static final Comparator LONGER_SET_FIRST = new Comparator() { - public int compare(UnicodeSet n1, UnicodeSet n2) { - int result = n1.size() - n2.size(); - if (result != 0) return -result; - return n1.compareTo(n2); - } - }; - - private static final Comparator SHORTER_COLLECTION_FIRST = new Comparator() { - public int compare(Collection n1, Collection n2) { - int result = n1.size() - n2.size(); - if (result != 0) return result; - return UnicodeSet.compare(n1, n2); - } - }; - - private static final HashSet SKIP_TERMS = new HashSet(Arrays.asList("black", "blackitalic", "bold", "boldit", "bolditalic", "bolditalicmt", "boldmt", - "boldob", "boldoblique", "boldslanted", "book", "bookitalic", "condensed", "condensedblack", "condensedbold", "condensedextrabold", - "condensedlight", "condensedmedium", "extracondensed", "extralight", "heavy", "italic", "italicmt", "light", "lightit", "lightitalic", "medium", - "mediumitalic", "oblique", "regular", "roman", "semibold", "semibolditalic", "shadow", "slanted", "ultrabold", "ultralight", "ultralightitalic" - )); - - private static Composer> composer = new Composer>() { - Map, Set>,Set> cache = new HashMap, Set>,Set>(); - public Set compose(int codePoint, String string, Set a, Set b) { - return a == null ? b - : b == null ? null - : intern(a,b); - } - private Set intern(Set a, Set b) { - R2, Set> row = Row.of(a, b); - Set result = cache.get(row); - if (result == null) { - result = new TreeSet(English); - result.addAll(a); - result.addAll(b); - cache.put(row, result); - } - return result; - } - }; - - - private static void showFullCoverage(UnicodeMap> map, Map, String> toShortName) throws IOException { - System.out.println("\n***COVERAGE:\t" + map.keySet().size() + "\n"); - PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "coverage.txt"); - - for (UnicodeMapIterator it = new UnicodeMapIterator(map); it.nextRange();) { - String codes = "U+" + Utility.hex(it.codepoint); - String names = UCharacter.getExtendedName(it.codepoint); - if (it.codepointEnd != it.codepoint) { - codes += "..U+" + Utility.hex(it.codepointEnd); - names += ".." + UCharacter.getExtendedName(it.codepointEnd); - } - out.println(codes + "\t" + toShortName.get(map.get(it.codepoint)) + "\t" + names); - } - - UnicodeSet missing = new UnicodeSet(COVERAGE).removeAll(map.keySet()); - out.println("\nMISSING:\t" + missing.size() + "\n"); - - UnicodeMap missingMap = new UnicodeMap(); - for (UnicodeSetIterator it = new UnicodeSetIterator(missing); it.next();) { - missingMap.put(it.codepoint, UScript.getName(UScript.getScript(it.codepoint)) + "-" + getShortAge(it.codepoint)); - } - - Set sorted = new TreeSet(English); - sorted.addAll(missingMap.values()); - for (String value : sorted) { - UnicodeSet items = missingMap.getSet(value); - for (UnicodeSetIterator it = new UnicodeSetIterator(items); it.nextRange();) { - String codes = "U+" + Utility.hex(it.codepoint); - String names = UCharacter.getExtendedName(it.codepoint); - if (it.codepointEnd != it.codepoint) { - codes += "..U+" + Utility.hex(it.codepointEnd); - names += ".." + UCharacter.getExtendedName(it.codepointEnd); - } - out.println(codes + "\t" + value + "\t" + names); - } - out.println(); - } - out.close(); - } - - private static Map, String> showRawCoverage(UnicodeMap> map) throws IOException { - System.out.println("\n***COMBO NAMES\n"); - PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "combo_names.txt"); - - int count = 0; - Map, String> toShortName = new HashMap, String>(); - TreeSet> sortedValues = new TreeSet>(SHORTER_COLLECTION_FIRST); - sortedValues.addAll(map.values()); - for (Set value : sortedValues) { - String shortName = "combo" + count++; - Set contained = getLargestContained(value, toShortName.keySet()); - String valueName; - if (contained != null) { - Set remainder = new TreeSet(); - remainder.addAll(value); - remainder.removeAll(contained); - valueName = toShortName.get(contained) + " + " + remainder; - } else { - valueName = value.toString(); - } - toShortName.put(value, shortName); - out.println(shortName + "\t" + valueName); - } - out.close(); - return toShortName; - } - - private static void showRawCoverage(Map> data) throws IOException { - System.out.println("\n***RAW COVERAGE (bridging unassigned)\n"); - PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "raw_coverage.txt"); - - for (Entry> entry : data.entrySet()) { - UnicodeSet s = entry.getKey(); - Set nameSet = entry.getValue(); - String name = nameSet.iterator().next(); - UnicodeSet bridged = new UnicodeSet(s).addBridges(DONT_CARE); - out.println(name + "\t" + s.size() + "\t" + bridged); - } - out.close(); - } - - private static UnicodeMap> showEquivalentCoverage(Map> data) throws IOException { - System.out.println("\n***EQUIVALENT COVERAGE\n"); - PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "equiv_coverage.txt"); - - UnicodeMap> map = new UnicodeMap>(); - - Map> nameToSingleton = new HashMap>(); - - for (Entry> entry : data.entrySet()) { - UnicodeSet s = entry.getKey(); - Set nameSet = entry.getValue(); - String name = nameSet.iterator().next(); - //System.out.println(s); - Set temp2 = nameToSingleton.get(name); - if (temp2 == null) { - temp2 = new TreeSet(English); - temp2.add(name); - } - map.composeWith(s, temp2, composer); - if (nameSet.size() > 1) { - TreeSet temp = new TreeSet(English); - temp.addAll(nameSet); - temp.remove(name); - out.println(name + "\t" + temp); - } - } - out.close(); - return map; - } - - private static void showSameGlyphs() throws IOException { - System.out.println("\n***Visual Equivalences"); - PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "same_glyphs.txt"); - PrintWriter out2 = BagFormatter.openUTF8Writer(outputDirectoryName, "same_glyphs.html"); - out2.println(""); - out2.println(""); - out2.println(""); - out2.println(""); - HTMLTabber tabber = new HTMLTabber(); - - out2.println(tabber.process("Code1\tCode2\tNFC1\tNFC1\tCh1\tCh1\tCh1/F\tCh2/F\tName1\tName2\tFonts")); - tabber.setParameters(0, "class='c'"); - tabber.setParameters(1, "class='c'"); - tabber.setParameters(2, "class='nf'"); - tabber.setParameters(3, "class='nf'"); - tabber.setParameters(4, "class='p'"); - tabber.setParameters(5, "class='p'"); - //tabber.setParameters(6, "class='q'"); - //tabber.setParameters(7, "class='q'"); - tabber.setParameters(8, "class='n'"); - tabber.setParameters(9, "class='n'"); - tabber.setParameters(10, "class='f'"); - - for (R2 sample : equivalences.keySet()) { - final Set reasonSet = equivalences.get(sample); - String reasons = reasonSet.toString(); - if (reasons.length() > 100) reasons = reasons.substring(0,100) + "..."; - final Integer codepoint1 = sample.get0(); - final Integer codepoint2 = sample.get1(); - - out.println("U+" + Utility.hex(codepoint1) + "\t" + "U+" + Utility.hex(codepoint2) - + "\t" + showNfc(codepoint1) + "\t" + showNfc(codepoint2) - + "\t" + showChar(codepoint1, false) + "\t" + showChar(codepoint2, false) - + "\t" + UCharacter.getExtendedName(codepoint1) + "\t" + UCharacter.getExtendedName(codepoint2) - + "\t" + reasons); - String line = "U+" + Utility.hex(codepoint1) + "\t" + "U+" + Utility.hex(codepoint2) - + "\t" + showNfc(codepoint1) + "\t" + showNfc(codepoint2) - + "\t" + showChar(codepoint1, false) + "\t" + showChar(codepoint2, true) - + "\t" + showChar(codepoint1, false) + "\t" + showChar(codepoint2, true) - + "\t" + UCharacter.getExtendedName(codepoint1) + "\t" + UCharacter.getExtendedName(codepoint2) - + "\t" + reasons; - - String fonts = "class='q' style='font-family:"; - int maxCount = 5; - for (String font : reasonSet) { - if (maxCount != 5) { - fonts += ","; - } - fonts += font; - --maxCount; - if (maxCount <= 0) break; - } - fonts += "'"; - tabber.setParameters(6, fonts); - tabber.setParameters(7, fonts); - out2.println(tabber.process(line)); - } - out2.println("
"); - out2.close(); - out.close(); - } - - private static void showInvisibles() throws IOException { - System.out.println("\n***Invisibles Equivalences"); - PrintWriter out = BagFormatter.openUTF8Writer(outputDirectoryName, "invisibles.txt"); - for (String sample : invisibles) { - String reasons = invisibles.get(sample).toString(); - if (reasons.length() > 100) reasons = reasons.substring(0,100) + "..."; - int codepoint = sample.codePointAt(0); - out.println("U+" + Utility.hex(sample) - + "\t" + showChar(codepoint, false) - + "\t" + showNfc(codepoint) - + "\t" + UCharacter.getExtendedName(codepoint) - + "\t" + reasons); - - } - out.close(); - } - - private static void getFontData(Matcher nameMatcher, Map> data, Map fontMap) { - GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - Font[] fonts = env.getAllFonts(); - for (Font font : fonts) { - if (!font.isPlain()) { - continue; - } - String name = font.getName(); - int lastDash = name.lastIndexOf('-'); - String term = lastDash < 0 ? "" : name.substring(lastDash+1).toLowerCase(); - if (SKIP_TERMS.contains(term)) { - continue; - } - if (nameMatcher != null && !nameMatcher.reset(name).find()) { - continue; - } - fontMap.put(name,font); - } - for (Entry entry : fontMap.entrySet()) { - String name = entry.getKey(); - Font font = entry.getValue(); - System.out.println(name); - UnicodeSet coverage = getCoverage(font); - Set sameFonts = data.get(coverage); - if (sameFonts == null) { - data.put(coverage, sameFonts = new TreeSet(SHORTER_FIRST)); - } else { - System.out.println("\tNote: same coverage as " + sameFonts.iterator().next()); - } - sameFonts.add(name); - } - } - - static Comparator NFCLower = new Comparator() { - public int compare(Integer o1, Integer o2) { - boolean n1 = Normalizer.isNormalized(o1, Normalizer.NFC, 0); - boolean n2 = Normalizer.isNormalized(o2, Normalizer.NFC, 0); - if (n1 != n2) return n1 ? -1 : 1; - n1 = Normalizer.isNormalized(o1, Normalizer.NFKC, 0); - n2 = Normalizer.isNormalized(o2, Normalizer.NFKC, 0); - if (n1 != n2) return n1 ? -1 : 1; - return o1.compareTo(o2); - } - }; - - static Comparator> NFCLowerR2 = new Comparator>() { - public int compare(R2 o1, R2 o2) { - int diff = NFCLower.compare(o1.get0(), o2.get0()); - if (diff != 0) return diff; - return NFCLower.compare(o1.get1(), o2.get1()); - } - }; - - private static String showNfc(int codepoint) { - return Normalizer.isNormalized(codepoint, Normalizer.NFC, 0) ? "" - : Normalizer.isNormalized(codepoint, Normalizer.NFKC, 0) ? "!C" : "!K"; - } - - private static String showChar(Integer item, boolean html) { - return rtlProtect(UTF16.valueOf(item), html); - } - static UnicodeSet RTL = new UnicodeSet("[[:bc=R:][:bc=AL:][:bc=AN:]]").freeze(); - static UnicodeSet CONTROLS = new UnicodeSet("[[:cc:][:Zl:][:Zp:]]").freeze(); - static UnicodeSet INVISIBLES = new UnicodeSet("[:di:]").freeze(); - static final char LRM = '\u200E'; - - private static String rtlProtect(String source, boolean html) { - if (CONTROLS.containsSome(source)) { - source = ""; - } else if (INVISIBLES.containsSome(source)) { - source = ""; - } else if (RTL.containsSome(source) || source.startsWith("\"")) { - source = LRM + source + LRM; - } - return html ? TransliteratorUtilities.toHTML.transform(source) : source; - } - - - private static Set getLargestContained(Set value, Collection> collection) { - Set best = null; - for (Set set : collection) { - if (best != null && best.size() > set.size()) { - continue; - } - if (value.containsAll(set)) { - best = set; - } - } - return best; - } - - private static String getShortAge(int i) { - String age = UCharacter.getAge(i).toString(); - return age.substring(0,age.indexOf('.',age.indexOf('.') + 1)); - } - - static SetMaker setMaker = new SetMaker() { - public Set make() { - return new TreeSet(); - } - }; - - static UnicodeMap> invisibles = new UnicodeMap(); - static Map, Set> equivalences = new TreeMap, Set>(NFCLowerR2); -// static Set SKIP_SHAPES = new HashSet(Arrays.asList( -// "MT-Extra", -// "JCsmPC", -// "DFKaiShu-SB-Estd-BF", -// "LiGothicMed", -// "LiHeiPro", -// "LiSongPro", -// "LiSungLight", -// "PMingLiU", -// "SIL-Hei-Med-Jian", -// "SIL-Kai-Reg-Jian", -// "CharcoalCY", -// "GenevaCY", -// "HelveticaCYBoldOblique", -// "HelveticaCYOblique", -// "HelveticaCYPlain", -// "HoeflerText-Ornaments", -// "Apple-Chancery", -// "MSReferenceSpecialty", -// "Stencil", -// "Hooge0555", -// "Hooge0556", -// "Desdemona", -// "EccentricStd", -// "EngraversMT", -// "MesquiteStd", -// "RosewoodStd-Fill", -// "Stencil", -// "StencilStd", -// "Osaka", -// "Osaka-Mono", -// "Kroeger0455", -// "Kroeger0456", -// "Uni0563", -// "Uni0564", -// "Code2001", -// "AppleSymbols", -// "AppleGothic", -// "AppleMyungjo", -// "JCkg", -// "MalithiWeb", -// "JCfg" -// )); - - // bug on Mac: http://forums.sun.com/thread.jspa?threadID=5209611 - private static UnicodeSet getCoverage(Font font) { - String name = font.getFontName(); - boolean skipShapes = SKIP_SHAPES.contains(name); - UnicodeSet result = new UnicodeSet(); - final FontRenderContext fontRenderContext = new FontRenderContext(null, false, false); - char[] array = new char[1]; - char[] array2 = new char[2]; - Map> boundsToData = new TreeMap>(ShapeComparator); - for (UnicodeSetIterator it = new UnicodeSetIterator(COVERAGE); it.next();) { - if (font.canDisplay(it.codepoint)) { - char[] temp; - if (it.codepoint <= 0xFFFF) { - array[0] = (char) it.codepoint; - temp = array; - } else { - Character.toChars(it.codepoint, array2, 0); - temp = array2; - } - - GlyphVector glyphVector = font.createGlyphVector(fontRenderContext, temp); - int glyphCode = glyphVector.getGlyphCode(0); - boolean validchar = (glyphCode > 0); - if (!validchar) continue; - - result.add(it.codepoint); - - if (skipShapes) continue; - Shape shape = glyphVector.getOutline(); - if (isInvisible(shape)) { - Set set = invisibles.get(it.codepoint); - if (set == null) { - invisibles.put(it.codepoint, set = new TreeSet()); - } - set.add(name); - } else { - Rectangle2D bounds = glyphVector.getVisualBounds(); - Map map = boundsToData.get(bounds); - if (map == null) { - boundsToData.put(bounds, map = new TreeMap(ShapeComparator)); - } - UnicodeSet set = map.get(shape); - if (set == null) { - map.put(shape, set = new UnicodeSet()); - } - if (false && set.size() != 0) { - System.out.println("Adding " + Utility.hex(it.codepoint) + "\t" + UTF16.valueOf(it.codepoint) + "\tto " + set.toPattern(false)); - } - set.add(it.codepoint); - } - } - } - //System.out.println(result.size() + "\t" + result); - for (Rectangle2D bounds : boundsToData.keySet()) { - Map map = boundsToData.get(bounds); - for (UnicodeSet set : map.values()) { - set.removeAll(CONTROLS); - if (set.size() != 1) { - //System.out.println(set.toPattern(false)); - for (UnicodeSetIterator it = new UnicodeSetIterator(set); it.next();) { - for (UnicodeSetIterator it2 = new UnicodeSetIterator(set); it2.next();) { - int cp = it.codepoint; - int cp2 = it2.codepoint; - if (cp >= cp2) continue; - R2 r = Row.of(cp, cp2); - Set reasons = equivalences.get(r); - if (reasons == null) { - equivalences.put(r, reasons = new TreeSet()); - } - reasons.add(name); - } - } - } - } - } - return result.freeze(); - } - - static Comparator RectComparator = new Comparator() { - - public int compare(Rectangle2D r1, Rectangle2D r2) { - int diff; - if (0 != (diff = compareDiff(r1.getX(),r2.getX()))) return diff; - if (0 != (diff = compareDiff(r1.getY(),r2.getY()))) return diff; - if (0 != (diff = compareDiff(r1.getWidth(),r2.getWidth()))) return diff; - if (0 != (diff = compareDiff(r1.getHeight(),r2.getHeight()))) return diff; - return 0; - } - - }; - - static final AffineTransform IDENTITY = new AffineTransform(); - - static boolean isInvisible(Shape shape) { - return shape.getPathIterator(IDENTITY).isDone(); - } - - static Comparator ShapeComparator = new Comparator() { - float[] coords1 = new float[6]; - float[] coords2 = new float[6]; - - public int compare(Shape s1, Shape s2) { - int diff; - PathIterator p1 = s1.getPathIterator(IDENTITY); - PathIterator p2 = s2.getPathIterator(IDENTITY); - while (true) { - if (p1.isDone()) { - return p2.isDone() ? 0 : -1; - } else if (p2.isDone()) { - return 1; - } - int t1 = p1.currentSegment(coords1); - int t2 = p2.currentSegment(coords2); - diff = t1 - t2; - if (diff != 0) return diff; - /* - * SEG_MOVETO and SEG_LINETO types returns one point, - * SEG_QUADTO returns two points, - * SEG_CUBICTO returns 3 points - * and SEG_CLOSE does not return any points. - */ - switch (t1) { - case PathIterator.SEG_CUBICTO: - if (0 != (diff = compareDiff(coords1[5],coords2[5]))) return diff; - if (0 != (diff = compareDiff(coords1[4],coords2[4]))) return diff; - case PathIterator.SEG_QUADTO: - if (0 != (diff = compareDiff(coords1[3],coords2[3]))) return diff; - if (0 != (diff = compareDiff(coords1[2],coords2[2]))) return diff; - case PathIterator.SEG_MOVETO: - case PathIterator.SEG_LINETO: - if (0 != (diff = compareDiff(coords1[1],coords2[1]))) return diff; - if (0 != (diff = compareDiff(coords1[0],coords2[0]))) return diff; - case PathIterator.SEG_CLOSE: break; - default: throw new IllegalArgumentException(); - } - p1.next(); - p2.next(); - } - } - }; - - private static int compareDiff(float f, float g) { - return f < g ? -1 : f > g ? 1 : 0; - } - private static int compareDiff(double f, double g) { - return f < g ? -1 : f > g ? 1 : 0; - } -} -- 2.40.0