{"id":1051,"date":"2025-10-06T10:50:19","date_gmt":"2025-10-06T02:50:19","guid":{"rendered":"https:\/\/vm1.go2see.me\/?p=1051"},"modified":"2025-10-06T10:58:32","modified_gmt":"2025-10-06T02:58:32","slug":"lab-4-abstract-algebra-specifications","status":"publish","type":"post","link":"https:\/\/vm1.go2see.me\/?p=1051","title":{"rendered":"Lab 4: Abstract Algebra + Specifications"},"content":{"rendered":"<h1>Lab 4: Abstract Algebra + Specifications<\/h1>\n<p>If you are given a set of elements and a binary operation defined on pairs of elements in the set, can you determine some properties of this set and operation? Can you determine if the set of elements and associated operation satisfies the conditions for being an algebraic group? <\/p>\n<h3>Learning Goals<\/h3>\n<ol>\n<li>Develop proficiency in implementing user-defined, i.e., custom, types in Java<\/li>\n<li>Develop proficiency in interpreting client specifications for methods and programming Java implementations satisfying these specifications <\/li>\n<li>Learn how to use generics to implement classes that are compatible with multiple datatypes without having to specify the types in advance <\/li>\n<li>Learn how to enforce an immutability invariant for a class in Java<\/li>\n<li>Develop proficiency in reasoning about the method specifications <\/li>\n<\/ol>\n<h3>Background: Groups &amp; Related Properties<\/h3>\n<p>A group is an algebraic entity with a set G and a binary operation (an operation that involves two elements from the set), often denoted *, that satisfies the following conditions: <\/p>\n<ol>\n<li>There exists an e in the set G, such that a <em> e = a and e <\/em> a = a, for all elements a in G. Such an element e is called the identity of the group. <\/li>\n<li>For all a in G, there exists some a&#8217; in G, such that a <em> a&#8217; = e and a&#8217; <\/em> a = e. In this case, a&#8217; is the inverse of a and a is the  inverse of a&#8217;. <\/li>\n<li>For all a and b and c in G, a <em> (b <\/em> c) = (a <em> b) <\/em> c. This means * is associative. <\/li>\n<li>For all elements a and b in G, a <em> b is in G. This means G is  closed under the operation <\/em>. <\/li>\n<\/ol>\n<p>Additionally we will say a group G is commutative if for all elements a and  b in G, a <em> b = b <\/em> a and a * b is also an element in G. <\/p>\n<p>We define the n-th power of some element a in G to be a^n where n is a positive integer and a^(n+1) is defined recursively as follows: <\/p>\n<p>a^(n+1) = a * a^n <\/p>\n<p>a^1 = a <\/p>\n<p>We also define the order of an element a in G to be the smallest possible positive integer m such that a^m = e, where e is the identity of the group. Note that order is only well defined in a group. <\/p>\n<p>Formally, a non-empty subset S of a group G is a subgroup of G if:<\/p>\n<ol>\n<li>For every element s in S, s&#8217; (the inverse of s) is also in S; <\/li>\n<li>For every two elements s and t in S, s * t is also in S. <\/li>\n<\/ol>\n<h3>Background: Binary Operations Using Operation Tables<\/h3>\n<p>In this lab, the elements in the sets are all Strings. These strings are merely symbols, which are either equal or distinct: they have no deeper meaning or significance in terms of their contents. <\/p>\n<p>A binary operation on these String elements is a mapping that takes in an ordered pair of  String elements and assigns it to another String element, which may be equal or distinct from either String operand. <\/p>\n<p>Let G = { &quot;x1&quot;, &quot;x2&quot;, &quot;x3&quot; } be a set of Strings, and take * to be defined by the operation table below. <\/p>\n<p>* x1 x2 x3 <\/p>\n<p>x1 x2 x3 x1 <\/p>\n<p>x2 x3 x1 x2<\/p>\n<p>x3 x1 x2 x3<\/p>\n<p>The table above looks like a multiplication table, except that the x values need not be integers and the operation need not be multiplication. <\/p>\n<p>This operation can be equivalently written using the pair of operands (inputs to the operation) and the output as follows: <\/p>\n<table>\n<thead>\n<tr>\n<th>xi, xj<\/th>\n<th>xi * xj<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>operand pair<\/td>\n<td>output<\/td>\n<\/tr>\n<tr>\n<td>x1, x1<\/td>\n<td>x2<\/td>\n<\/tr>\n<tr>\n<td>x1, x2<\/td>\n<td>x3<\/td>\n<\/tr>\n<tr>\n<td>x1, x3<\/td>\n<td>x1<\/td>\n<\/tr>\n<tr>\n<td>x2, x1<\/td>\n<td>x3<\/td>\n<\/tr>\n<tr>\n<td>x2, x2<\/td>\n<td>x1<\/td>\n<\/tr>\n<tr>\n<td>x2, x3<\/td>\n<td>x2<\/td>\n<\/tr>\n<tr>\n<td>x3, x1<\/td>\n<td>x1<\/td>\n<\/tr>\n<tr>\n<td>x3, x2<\/td>\n<td>x2<\/td>\n<\/tr>\n<tr>\n<td>x3, x3<\/td>\n<td>x3<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>It is essential to recognize that this structure of mapping an ordered pair of input elements defines the operation  * for  G .  <\/p>\n<blockquote>\n<p>Consider for a moment the binary operation addition  + defined on the integers. Since you know how to add, you have a procedure in your mind for how to determine the output of any addition operation. However, you could also view the addition operation for the integers as being defined by an (infinitely) large operation table of the form we have presented here, where each row corresponds to a mapping of two inputs to their respective output. When we can&#8217;t easily reduce a binary operation over a finite number of elements into an easy-to-explain procedure, we can represent it instead by an operation table that enumerates all input-output pairs defined by the operation. This representation of a binary operation using an operation table always exists whether or not we choose to use it to reason about the operation or not. For operations defined on finite sets (like those in this lab), the tabular representation is more useful. <\/p>\n<\/blockquote>\n<h3>An Example<\/h3>\n<p>Now, let&#8217;s return to the set  G = { &quot;x1&quot;, &quot;x2&quot;, &quot;x3&quot; } defined by the  operation table in the previous section. Does this set and operation pair represent a group?<br \/>\nIs the operation commutative? What is the order of  x1 ?<br \/>\nTo answer the questions posed at the start of this example, we need to check and see if  G and  * satisfy the four conditions we mention in the  beginning. <\/p>\n<ol>\n<li>A quick search allows us to see that  x3 is the identity of the group, so e = x3 as:<\/li>\n<\/ol>\n<pre><code>x1 * x3 = x3 * x1 = x1\nx2 * x3 = x3 * x2 = x2 x3 * \nx3 = x3 x3 <\/code><\/pre>\n<pre><code>2. All elements of  G have their inverse as an element of G. We know  the x3 is identity, and so for any  xi, the inverse is some xj , the inverse of  that  xi is some xj such that  xi * xj = xj * xi = x3. In our example we see<\/code><\/pre>\n<pre><code>x1 * x2 = x2 * x1 = x3 = e \nx2 * x1 = x1 * x2 = x3 = e \nx3 * x3 = x3 = e <\/code><\/pre>\n<ol start=\"3\">\n<li>The operation is associative as  xi <em> (xj <\/em> xk) = (xi <em> xj) <\/em> xk  for all  xi, xj, xk in G . Let us confirm this through the following example: <\/li>\n<\/ol>\n<pre><code>x1 * (x2 * x3) = x1 * x2 = x3\n(x1 * x2) * x3 = x3 * x3 = x3\n=&gt; x1 * (x2 * x3) = (x1 * x2) * x3 <\/code><\/pre>\n<ol start=\"4\">\n<li>For each possible pair  xi, xj in  G , we see that  xi * xj is also in  G . We know this to be true as all entries in the operation table are elements in  G .<\/li>\n<\/ol>\n<p>We see that all four conditions of a group hold, therefore  indeed a group. G and  * are indeed a Group.<\/p>\n<ul>\n<li>The operation is commutative as  xi <em> xj = xj <\/em> xi for any  xi, xj .<br \/>\nWe know this to be true as the product table is<\/li>\n<li>The order of  x1 is 3 as<\/li>\n<\/ul>\n<pre><code>x1^3 = x1 * x1^2 = x1 * (x1 * x1) = x1 * x2 = x3 = e<\/code><\/pre>\n<p>and 3 is the smallest possible positive integer such that x1^3 =e<\/p>\n<ul>\n<li>S = {x3} is a subgroup of  G<\/li>\n<\/ul>\n<h3>The ProbableGroup Type<\/h3>\n<p>The ProbableGroup type, which you will implement, is created using a set of elements and an operation on pairs of elements (this operation is described explicitly using a table that maps a pair of values to the output value). <\/p>\n<p>In Java vocabulary, the <strong>ProbableGroup<\/strong> class has a constructor  public ProbableGroup(Set elements, Map, String&gt;) opTable). <\/p>\n<p>Relating to the <strong>background<\/strong> section, elements is our set G and <strong>opTable<\/strong> is our operation table. <strong>opTable<\/strong> is initialized such that <strong>opTable.get(new Pair(&quot;x1&quot;,&quot;x2&quot;))<\/strong> returns the result of the operation x1 * x2, and x1,  x2 are included in elements.<\/p>\n<p>You need to implement the constructor and complete the following methods: <\/p>\n<ol>\n<li>String product(String a, String b): returns as a * b. <\/li>\n<li>String power(String a, int n): returns a^n. <\/li>\n<li>String getIdentity(): returns the identity element of the group. <\/li>\n<li>int getOrder(String a): returns the order of the element a. <\/li>\n<li>String getInverse(String a): returns the inverse of the element  a. <\/li>\n<li>boolean isGroup(): Does this ProbableGroup instance satisfy all the conditions of a group as defined in the background section? <\/li>\n<li>boolean isCommutative(): Is the * operation commutative? <\/li>\n<li>Make ProbableGroup an immutable type without removing any methods. <\/li>\n<li>Implement the method boolean isSubGroup(Set h) that returns true if and only if elements in h are a subgroup of this. <\/li>\n<\/ol>\n<p>More detailed specs provided in the skeleton code and we recommend that you implement methods in the provided order. <\/p>\n<p>You can assume any constraint stated under <strong>@param<\/strong> or <strong>requires<\/strong> in the specs is always satisfied. (You do not need to worry about how to handle violations of the preconditions.) <\/p>\n<pre><code>Task <\/code><\/pre>\n<pre><code>There are 2 PrairieLearn assessments associated with this lab.  Assessment LB4.1 is a zero-credit assessment for you to test your implementation for the programming exercise. Assessment LB4.2 corresponds to the conceptual questions for this lab. For the programming exercise in this lab, only work submitted via GitHub will be graded. <\/code><\/pre>\n<pre><code>Programming Exercise (via GitHub) <\/code><\/pre>\n<ol>\n<li>\n<p>Accept the &quot;Lab 4: Abstract Algebra&quot; assignment via the Github Classroom link to create a new personal repo for your solution. <\/p>\n<\/li>\n<li>\n<p>Clone your personalized <code>lab-4-*<\/code> repo onto your computer and open it as an IntelliJ project.<\/p>\n<\/li>\n<li>\n<p>Implement the ProbableGroup class in<br \/>\n&#8230;\/src\/main\/java\/groups\/ProbableGroup.java <\/p>\n<ul>\n<li>You should not edit Pair.java <\/li>\n<li>See the &quot;External Resource(s)&quot; section of this guide for some more information on generics<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>Test your implementation using the provided smoke tests and any other tests you write to build confidence in the robustness of your implementation. <\/p>\n<\/li>\n<li>\n<p>(Optional). Copy and paste the contents of your  ProbableGroup.java file into the code editor for question LB4.1 on PrairieLearn and submit your work.<\/p>\n<ul>\n<li>Some tests run by the PrairieLearn auto-grader are different from the smoke tests provided to you. While we might use different tests for grading your Github submissions, a good faith implementation that passes all PrairieLearn tests will likely pass all grading tests too.<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>Push your work to GitHub. <\/p>\n<\/li>\n<li>\n<p>Check GitHub to ensure your latest commit is visible.<\/p>\n<\/li>\n<\/ol>\n<h3>\ud83e\udde0 Class Design Strategy<\/h3>\n<ul>\n<li><strong>Fields<\/strong>:\n<ul>\n<li><code>Set&lt;String&gt; elements<\/code>: the group set GG<\/li>\n<li><code>Map&lt;Pair&lt;String, String&gt;, String&gt; opTable<\/code>: the binary operation \u2217*<\/li>\n<\/ul>\n<\/li>\n<li><strong>Immutability<\/strong>:\n<ul>\n<li>Use <code>Collections.unmodifiableSet<\/code> and <code>Collections.unmodifiableMap<\/code> in the constructor.<\/li>\n<li>Avoid exposing internal mutable structures.<\/li>\n<li>No setters; all fields are <code>final<\/code>.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3>\ud83d\udd27 Method Implementation Guide<\/h3>\n<h4>1. <code>product(String a, String b)<\/code><\/h4>\n<ul>\n<li>Return <code>opTable.get(new Pair&lt;&gt;(a, b))<\/code><\/li>\n<\/ul>\n<h4>2. <code>power(String a, int n)<\/code><\/h4>\n<ul>\n<li>Use repeated application of <code>product<\/code>, starting from identity (or <code>a<\/code> if n=1n = 1)<\/li>\n<li>Handle n=0n = 0 by returning identity<\/li>\n<li>Handle negative powers only if inverse exists<\/li>\n<\/ul>\n<h4>3. <code>getIdentity()<\/code><\/h4>\n<ul>\n<li>Search for <code>e<\/code> in <code>elements<\/code> such that for all <code>x<\/code>, <code>product(e, x) == x<\/code> and <code>product(x, e) == x<\/code><\/li>\n<\/ul>\n<h4>4. <code>getOrder(String a)<\/code><\/h4>\n<ul>\n<li>Compute powers of <code>a<\/code> until you reach identity<\/li>\n<li>Return the smallest nn such that an=ea^n = e<\/li>\n<\/ul>\n<h4>5. <code>getInverse(String a)<\/code><\/h4>\n<ul>\n<li>Search for <code>b<\/code> in <code>elements<\/code> such that <code>product(a, b) == e<\/code> and <code>product(b, a) == e<\/code><\/li>\n<\/ul>\n<h4>6. <code>isGroup()<\/code><\/h4>\n<ul>\n<li>Check:\n<ul>\n<li>Closure: all products in <code>opTable<\/code> are in <code>elements<\/code><\/li>\n<li>Associativity: for all a,b,ca, b, c, (a\u2217b)\u2217c=a\u2217(b\u2217c)(a<em>b)<\/em>c = a<em>(b<\/em>c)<\/li>\n<li>Identity exists and behaves correctly<\/li>\n<li>Inverses exist for all elements<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4>7. <code>isCommutative()<\/code><\/h4>\n<ul>\n<li>For all a,ba, b, check if a\u2217b=b\u2217aa<em>b = b<\/em>a<\/li>\n<\/ul>\n<h4>8. <strong>Immutability<\/strong><\/h4>\n<ul>\n<li>Ensure constructor wraps <code>elements<\/code> and <code>opTable<\/code> with unmodifiable wrappers<\/li>\n<li>Avoid any method that mutates internal state<\/li>\n<\/ul>\n<h4>9. <code>isSubGroup(Set&lt;String&gt; h)<\/code><\/h4>\n<ul>\n<li>Check:\n<ul>\n<li>h\u2286Gh \\subseteq G<\/li>\n<li>Closure: for all a,b\u2208ha, b \\in h, a\u2217b\u2208ha*b \\in h<\/li>\n<li>Identity of group is in hh<\/li>\n<li>Inverses of all elements in hh are in hh<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3>\ud83e\uddea Testing Tips<\/h3>\n<ul>\n<li>Use smoke tests to validate:\n<ul>\n<li>Identity detection<\/li>\n<li>Inverse correctness<\/li>\n<li>Associativity (tricky\u2014requires nested products)<\/li>\n<li>Subgroup checks with known examples (e.g., cyclic groups)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3>\ud83e\uddf1 Starter Skeleton: <code>ProbableGroup.java<\/code><\/h3>\n<p>Here\u2019s a clean, immutable setup with the constructor and first few methods:<\/p>\n<p>java<\/p>\n<pre><code>package groups;\n\nimport java.util.*;\n\npublic class ProbableGroup {\n    private final Set&lt;String&gt; elements;\n    private final Map&lt;Pair&lt;String, String&gt;, String&gt; opTable;\n\n    public ProbableGroup(Set&lt;String&gt; elements, Map&lt;Pair&lt;String, String&gt;, String&gt; opTable) {\n        this.elements = Collections.unmodifiableSet(new HashSet&lt;&gt;(elements));\n        this.opTable = Collections.unmodifiableMap(new HashMap&lt;&gt;(opTable));\n    }\n\n    public String product(String a, String b) {\n        return opTable.get(new Pair&lt;&gt;(a, b));\n    }\n\n    public String power(String a, int n) {\n        if (n == 0) return getIdentity();\n        String result = a;\n        int absN = Math.abs(n);\n        for (int i = 1; i &lt; absN; i++) {\n            result = product(result, a);\n        }\n        if (n &lt; 0) result = power(getInverse(a), absN);\n        return result;\n    }\n\n    public String getIdentity() {\n        for (String e : elements) {\n            boolean isIdentity = true;\n            for (String x : elements) {\n                if (!product(e, x).equals(x) || !product(x, e).equals(x)) {\n                    isIdentity = false;\n                    break;\n                }\n            }\n            if (isIdentity) return e;\n        }\n        return null;\n    }\n\n    public String getInverse(String a) {\n        String identity = getIdentity();\n        for (String b : elements) {\n            if (product(a, b).equals(identity) &amp;&amp; product(b, a).equals(identity)) {\n                return b;\n            }\n        }\n        return null;\n    }\n\n    \/\/ Add other methods here...\n}<\/code><\/pre>\n<h3>\u2705 Sample Test Case: Subgroup Validation<\/h3>\n<p>Here\u2019s a simple test you can run to check <code>isSubGroup(Set&lt;String&gt; h)<\/code> once implemented:<\/p>\n<p>java<\/p>\n<pre><code>public static void main(String[] args) {\n    Set&lt;String&gt; elements = Set.of(&quot;e&quot;, &quot;a&quot;, &quot;b&quot;);\n    Map&lt;Pair&lt;String, String&gt;, String&gt; opTable = Map.of(\n        new Pair&lt;&gt;(&quot;e&quot;, &quot;e&quot;), &quot;e&quot;,\n        new Pair&lt;&gt;(&quot;e&quot;, &quot;a&quot;), &quot;a&quot;,\n        new Pair&lt;&gt;(&quot;e&quot;, &quot;b&quot;), &quot;b&quot;,\n        new Pair&lt;&gt;(&quot;a&quot;, &quot;e&quot;), &quot;a&quot;,\n        new Pair&lt;&gt;(&quot;a&quot;, &quot;a&quot;), &quot;e&quot;,\n        new Pair&lt;&gt;(&quot;a&quot;, &quot;b&quot;), &quot;b&quot;,\n        new Pair&lt;&gt;(&quot;b&quot;, &quot;e&quot;), &quot;b&quot;,\n        new Pair&lt;&gt;(&quot;b&quot;, &quot;a&quot;), &quot;b&quot;,\n        new Pair&lt;&gt;(&quot;b&quot;, &quot;b&quot;), &quot;e&quot;\n    );\n\n    ProbableGroup group = new ProbableGroup(elements, opTable);\n    Set&lt;String&gt; h = Set.of(&quot;e&quot;, &quot;b&quot;);\n\n    System.out.println(&quot;Is subgroup: &quot; + group.isSubGroup(h)); \/\/ Expected: true\n}<\/code><\/pre>\n<p>This example assumes <code>b * b = e<\/code>, <code>b * e = b<\/code>, and <code>e<\/code> is the identity\u2014so <code class=\"kb-btn\">e, b<\/code> forms a valid subgroup.<\/p>\n<p>Let\u2019s tackle both the <code>isSubGroup<\/code> method and a test for associativity. These will help you validate group structure and deepen your understanding of abstract algebra in code.<\/p>\n<h3>\ud83e\uddee <code>isSubGroup<\/code> Implementation<\/h3>\n<p>This method checks whether a given subset <code>h<\/code> satisfies the subgroup criteria:<\/p>\n<p>java<\/p>\n<pre><code>public boolean isSubGroup(Set&lt;String&gt; h) {\n    \/\/ Must be a subset of the group\n    if (!elements.containsAll(h)) return false;\n\n    String identity = getIdentity();\n    if (identity == null || !h.contains(identity)) return false;\n\n    for (String a : h) {\n        String inverse = getInverse(a);\n        if (inverse == null || !h.contains(inverse)) return false;\n\n        for (String b : h) {\n            String prod = product(a, b);\n            if (prod == null || !h.contains(prod)) return false;\n        }\n    }\n    return true;\n}<\/code><\/pre>\n<p>This ensures:<\/p>\n<ul>\n<li>Closure under the operation<\/li>\n<li>Identity is present<\/li>\n<li>Inverses are present<\/li>\n<\/ul>\n<h3>\ud83d\udd01 Associativity Test<\/h3>\n<p>Associativity is the trickiest group axiom to verify because it requires checking all combinations of three elements:<\/p>\n<p>java<\/p>\n<pre><code>public boolean isGroup() {\n    String identity = getIdentity();\n    if (identity == null) return false;\n\n    for (String a : elements) {\n        String inverse = getInverse(a);\n        if (inverse == null) return false;\n\n        for (String b : elements) {\n            for (String c : elements) {\n                String left = product(product(a, b), c);\n                String right = product(a, product(b, c));\n                if (!left.equals(right)) return false;\n            }\n        }\n    }\n    return true;\n}<\/code><\/pre>\n<p>This method also checks:<\/p>\n<ul>\n<li>Identity exists<\/li>\n<li>Inverses exist<\/li>\n<li>Associativity holds for all triples<\/li>\n<\/ul>\n<h3>\ud83e\uddea Test Case for Associativity<\/h3>\n<p>Here\u2019s a simple test you can run in <code>main()<\/code>:<\/p>\n<p>java<\/p>\n<pre><code>Set&lt;String&gt; elements = Set.of(&quot;e&quot;, &quot;a&quot;, &quot;b&quot;);\nMap&lt;Pair&lt;String, String&gt;, String&gt; opTable = Map.of(\n    new Pair&lt;&gt;(&quot;e&quot;, &quot;e&quot;), &quot;e&quot;,\n    new Pair&lt;&gt;(&quot;e&quot;, &quot;a&quot;), &quot;a&quot;,\n    new Pair&lt;&gt;(&quot;e&quot;, &quot;b&quot;), &quot;b&quot;,\n    new Pair&lt;&gt;(&quot;a&quot;, &quot;e&quot;), &quot;a&quot;,\n    new Pair&lt;&gt;(&quot;a&quot;, &quot;a&quot;), &quot;e&quot;,\n    new Pair&lt;&gt;(&quot;a&quot;, &quot;b&quot;), &quot;b&quot;,\n    new Pair&lt;&gt;(&quot;b&quot;, &quot;e&quot;), &quot;b&quot;,\n    new Pair&lt;&gt;(&quot;b&quot;, &quot;a&quot;), &quot;b&quot;,\n    new Pair&lt;&gt;(&quot;b&quot;, &quot;b&quot;), &quot;e&quot;\n);\n\nProbableGroup group = new ProbableGroup(elements, opTable);\nSystem.out.println(&quot;Is group: &quot; + group.isGroup()); \/\/ Expected: true<\/code><\/pre>\n<p>This setup assumes associativity holds (you\u2019ll want to verify manually or with a truth table). If you want to test a non-group, just break one of the axioms\u2014like removing an inverse or altering a product.<\/p>\n<hr \/>\n<h2>Code<\/h2>\n<pre><code class=\"language-java\">package groups;\n\nimport java.util.Map;\nimport java.util.Set;\n\npublic final class ProbableGroup {\n\n    private final Set&lt;String&gt; elements;\n    private final Map&lt;Pair&lt;String&gt;, String&gt; opTable;\n\n    public ProbableGroup(Set&lt;String&gt; elements, Map&lt;Pair&lt;String&gt;, String&gt; opTable) {\n        this.elements = java.util.Collections.unmodifiableSet(\n                new java.util.LinkedHashSet&lt;&gt;(elements)\n        );\n        java.util.Map&lt;Pair&lt;String&gt;, String&gt; copy = new java.util.LinkedHashMap&lt;&gt;();\n        for (Map.Entry&lt;Pair&lt;String&gt;, String&gt; e : opTable.entrySet()) {\n            Pair&lt;String&gt; k = e.getKey();\n            copy.put(new Pair&lt;&gt;(k.getElem1(), k.getElem2()), e.getValue());\n        }\n        this.opTable = java.util.Collections.unmodifiableMap(copy);\n    }\n\n    private static Pair&lt;String&gt; key(String a, String b) { return new Pair&lt;&gt;(a, b); }\n\n    public Set&lt;String&gt; getElements() {\n        return new java.util.LinkedHashSet&lt;&gt;(elements);\n    }\n\n    public Map&lt;Pair&lt;String&gt;, String&gt; getOpTable() {\n        java.util.Map&lt;Pair&lt;String&gt;, String&gt; copy = new java.util.LinkedHashMap&lt;&gt;();\n        for (Map.Entry&lt;Pair&lt;String&gt;, String&gt; e : opTable.entrySet()) {\n            Pair&lt;String&gt; k = e.getKey();\n            copy.put(new Pair&lt;&gt;(k.getElem1(), k.getElem2()), e.getValue());\n        }\n        return copy;\n    }\n\n    public String product(String a, String b) {\n        String r = opTable.get(key(a, b));\n        if (r == null) throw new IllegalArgumentException(&quot;No entry for (&quot; + a + &quot;,&quot; + b + &quot;)&quot;);\n        return r;\n    }\n\n    public String getIdentity() {\n        for (String cand : elements) {\n            boolean left = true;\n            for (String x : elements) {\n                if (!product(cand, x).equals(x)) { left = false; break; }\n            }\n            if (!left) continue;\n            boolean right = true;\n            for (String x : elements) {\n                if (!product(x, cand).equals(x)) { right = false; break; }\n            }\n            if (right) return cand;\n        }\n        return &quot;&quot;;\n    }\n\n    public String getInverse(String a) {\n        String e = getIdentity();\n        if (e.isEmpty()) return &quot;&quot;;\n        for (String b : elements) {\n            if (product(a, b).equals(e) &amp;&amp; product(b, a).equals(e)) return b;\n        }\n        return &quot;&quot;;\n    }\n\n    public String power(String a, int n) {\n        if (n &lt;= 0) throw new IllegalArgumentException(&quot;power requires n &gt; 0, got &quot; + n);\n        String acc = a;\n        for (int i = 1; i &lt; n; i++) acc = product(acc, a);\n        return acc;\n    }\n\n    public int order(String a) {\n        String e = getIdentity();\n        if (e.isEmpty()) return Integer.MAX_VALUE;\n        String acc = a;\n        int limit = Math.max(1, elements.size());\n        for (int m = 1; m &lt;= limit; m++) {\n            if (acc.equals(e)) return m;\n            acc = product(acc, a);\n        }\n        for (int m = limit + 1; m &lt;= limit + elements.size(); m++) {\n            if (acc.equals(e)) return m;\n            acc = product(acc, a);\n        }\n        return Integer.MAX_VALUE;\n    }\n\n    public boolean isGroup() {\n        for (String a : elements) for (String b : elements) {\n            String r = opTable.get(key(a, b));\n            if (r == null || !elements.contains(r)) return false;\n        }\n        for (String a : elements)\n            for (String b : elements)\n                for (String c : elements) {\n                    String left = product(product(a, b), c);\n                    String right = product(a, product(b, c));\n                    if (!left.equals(right)) return false;\n                }\n        String e = getIdentity();\n        if (e.isEmpty()) return false;\n        for (String a : elements) if (getInverse(a).isEmpty()) return false;\n        return true;\n    }\n\n    public boolean isCommutative() {\n        for (String a : elements) for (String b : elements) {\n            if (!product(a, b).equals(product(b, a))) return false;\n        }\n        return true;\n    }\n\n    public boolean isSubgroup(Set&lt;String&gt; h) {\n        if (h == null || h.isEmpty()) return false;\n        if (!isGroup()) return false;\n        if (!elements.containsAll(h)) return false;\n\n        String e = getIdentity();\n        if (e.isEmpty() || !h.contains(e)) return false;\n\n        for (String s : h) for (String t : h) {\n            if (!h.contains(product(s, t))) return false;\n        }\n        for (String s : h) {\n            String inv = getInverse(s);\n            if (inv.isEmpty() || !h.contains(inv)) return false;\n        }\n        return true;\n    }\n}<\/code><\/pre>\n<hr \/>\n<h2>Copilot \u8aaa<\/h2>\n<p>\u9019\u6bb5 Java \u7a0b\u5f0f\u78bc\u5b9a\u7fa9\u4e86\u4e00\u500b\u4e0d\u53ef\u8b8a\u7684\u985e\u5225 <code>ProbableGroup<\/code>\uff0c\u7528\u4f86\u8868\u793a\u4e00\u500b\u53ef\u80fd\u662f\u7fa4\uff08Group\uff09\u7684\u4ee3\u6578\u7d50\u69cb\u3002\u5b83\u4f7f\u7528\u96c6\u5408\u548c\u6620\u5c04\u4f86\u63cf\u8ff0\u5143\u7d20\u8207\u5176\u4e8c\u5143\u904b\u7b97\u8868\uff0c\u4e26\u63d0\u4f9b\u4e00\u7cfb\u5217\u65b9\u6cd5\u4f86\u6aa2\u67e5\u9019\u500b\u7d50\u69cb\u662f\u5426\u6eff\u8db3\u7fa4\u7684\u6027\u8cea\u3002\u4ee5\u4e0b\u662f\u8a73\u7d30\u89e3\u91cb\uff1a<\/p>\n<h3>\ud83e\udde9 \u985e\u5225\u7d50\u69cb\u8207\u6210\u54e1<\/h3>\n<p>java<\/p>\n<p>\u8907\u88fd<\/p>\n<pre><code>private final Set&lt;String&gt; elements;\nprivate final Map&lt;Pair&lt;String&gt;, String&gt; opTable;<\/code><\/pre>\n<ul>\n<li><code>elements<\/code>\uff1a\u7fa4\u7684\u5143\u7d20\u96c6\u5408\u3002<\/li>\n<li><code>opTable<\/code>\uff1a\u4e8c\u5143\u904b\u7b97\u8868\uff0c\u5b9a\u7fa9\u4e86\u6bcf\u5c0d\u5143\u7d20\u7684\u4e58\u7a4d\u7d50\u679c\u3002<\/li>\n<\/ul>\n<h3>\ud83c\udfd7\ufe0f \u5efa\u69cb\u5b50<\/h3>\n<p>java<\/p>\n<p>\u8907\u88fd<\/p>\n<pre><code>public ProbableGroup(Set&lt;String&gt; elements, Map&lt;Pair&lt;String&gt;, String&gt; opTable)<\/code><\/pre>\n<ul>\n<li>\u5c07\u8f38\u5165\u7684\u5143\u7d20\u96c6\u5408\u8207\u904b\u7b97\u8868\u8907\u88fd\u4e26\u8a2d\u70ba\u4e0d\u53ef\u8b8a\uff08<code>unmodifiableSet<\/code> \u548c <code>unmodifiableMap<\/code>\uff09\uff0c\u78ba\u4fdd\u5916\u90e8\u7121\u6cd5\u4fee\u6539\u3002<\/li>\n<\/ul>\n<h3>\ud83d\udd27 \u6838\u5fc3\u65b9\u6cd5\u89e3\u6790<\/h3>\n<h4><code>product(String a, String b)<\/code><\/h4>\n<ul>\n<li>\u56de\u50b3\u5143\u7d20 <code>a<\/code> \u548c <code>b<\/code> \u7684\u4e58\u7a4d\u3002<\/li>\n<li>\u82e5\u67e5\u4e0d\u5230\u5c0d\u61c9\u7684\u904b\u7b97\uff0c\u5247\u4e1f\u51fa\u4f8b\u5916\u3002<\/li>\n<\/ul>\n<h4><code>getIdentity()<\/code><\/h4>\n<ul>\n<li>\u5617\u8a66\u627e\u51fa\u55ae\u4f4d\u5143\uff08identity element\uff09\uff1a\n<ul>\n<li>\u5c0d\u6240\u6709\u5143\u7d20 <code>x<\/code>\uff0c\u82e5 <code>e * x = x<\/code> \u4e14 <code>x * e = x<\/code>\uff0c\u5247 <code>e<\/code> \u70ba\u55ae\u4f4d\u5143\u3002<\/li>\n<li>\u82e5\u627e\u4e0d\u5230\uff0c\u56de\u50b3\u7a7a\u5b57\u4e32\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h4><code>getInverse(String a)<\/code><\/h4>\n<ul>\n<li>\u627e\u51fa\u5143\u7d20 <code>a<\/code> \u7684\u53cd\u5143\u7d20 <code>b<\/code>\uff0c\u4f7f\u5f97 <code>a * b = e<\/code> \u4e14 <code>b * a = e<\/code>\u3002<\/li>\n<li>\u82e5\u627e\u4e0d\u5230\uff0c\u56de\u50b3\u7a7a\u5b57\u4e32\u3002<\/li>\n<\/ul>\n<h4><code>power(String a, int n)<\/code><\/h4>\n<ul>\n<li>\u8a08\u7b97 <code>a^n<\/code>\uff0c\u5373 <code>a<\/code> \u81ea\u4e58 <code>n<\/code> \u6b21\u3002<\/li>\n<li><code>n<\/code> \u5fc5\u9808\u5927\u65bc 0\u3002<\/li>\n<\/ul>\n<h4><code>order(String a)<\/code><\/h4>\n<ul>\n<li>\u8a08\u7b97\u5143\u7d20 <code>a<\/code> \u7684\u968e\uff08order\uff09\uff0c\u5373\u6700\u5c0f\u7684\u6b63\u6574\u6578 <code>m<\/code> \u4f7f\u5f97 <code>a^m = e<\/code>\u3002<\/li>\n<li>\u82e5\u7121\u6cd5\u627e\u5230\uff0c\u56de\u50b3 <code>Integer.MAX_VALUE<\/code>\u3002<\/li>\n<\/ul>\n<h3>\u2705 \u7fa4\u6027\u8cea\u6aa2\u67e5<\/h3>\n<h4><code>isGroup()<\/code><\/h4>\n<ul>\n<li>\u6aa2\u67e5\u662f\u5426\u6eff\u8db3\u7fa4\u7684\u56db\u500b\u689d\u4ef6\uff1a\n<ol>\n<li>\u5c01\u9589\u6027\uff1a\u6240\u6709 <code>a * b<\/code> \u90fd\u5728\u5143\u7d20\u96c6\u5408\u4e2d\u3002<\/li>\n<li>\u7d50\u5408\u5f8b\uff1a<code>(a * b) * c = a * (b * c)<\/code>\u3002<\/li>\n<li>\u55ae\u4f4d\u5143\u5b58\u5728\u3002<\/li>\n<li>\u6bcf\u500b\u5143\u7d20\u90fd\u6709\u53cd\u5143\u7d20\u3002<\/li>\n<\/ol>\n<\/li>\n<\/ul>\n<h4><code>isCommutative()<\/code><\/h4>\n<ul>\n<li>\u6aa2\u67e5\u662f\u5426\u70ba\u4ea4\u63db\u7fa4\uff08Abelian group\uff09\uff1a<code>a * b = b * a<\/code>\u3002<\/li>\n<\/ul>\n<h4><code>isSubgroup(Set&lt;String&gt; h)<\/code><\/h4>\n<ul>\n<li>\u6aa2\u67e5\u5b50\u96c6\u5408 <code>h<\/code> \u662f\u5426\u70ba\u5b50\u7fa4\uff1a\n<ul>\n<li><code>h<\/code> \u4e0d\u70ba\u7a7a\u3002<\/li>\n<li><code>h<\/code> \u662f\u539f\u7fa4\u7684\u5b50\u96c6\u3002<\/li>\n<li>\u5305\u542b\u55ae\u4f4d\u5143\u3002<\/li>\n<li>\u5c0d\u4efb\u610f <code>s, t \u2208 h<\/code>\uff0c<code>s * t \u2208 h<\/code>\u3002<\/li>\n<li>\u6bcf\u500b\u5143\u7d20\u7684\u53cd\u5143\u7d20\u4e5f\u5728 <code>h<\/code> \u4e2d\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3>\ud83e\udde0 \u984d\u5916\u88dc\u5145<\/h3>\n<ul>\n<li><code>Pair&lt;String&gt;<\/code> \u662f\u81ea\u8a02\u7684\u6cdb\u578b\u985e\u5225\uff0c\u7528\u4f86\u8868\u793a\u4e8c\u5143\u904b\u7b97\u7684\u9375\u3002<\/li>\n<li>\u6240\u6709\u8cc7\u6599\u7d50\u69cb\u90fd\u4f7f\u7528 <code>LinkedHashSet<\/code> \u548c <code>LinkedHashMap<\/code>\uff0c\u4fdd\u7559\u63d2\u5165\u9806\u5e8f\u3002<\/li>\n<li>\u985e\u5225\u662f <code>final<\/code>\uff0c\u8868\u793a\u4e0d\u80fd\u88ab\u7e7c\u627f\u3002<\/li>\n<\/ul>\n<p>\u5982\u679c\u4f60\u6b63\u5728\u8a2d\u8a08\u4ee3\u6578\u7d50\u69cb\u6a21\u64ec\u5668\u6216\u60f3\u9a57\u8b49\u67d0\u500b\u96c6\u5408\u8207\u904b\u7b97\u662f\u5426\u69cb\u6210\u7fa4\uff0c\u9019\u500b\u985e\u5225\u5c31\u662f\u4f60\u7684\u6578\u5b78\u52a9\u624b\u3002\u4f60\u60f3\u8981\u6211\u5e6b\u4f60\u6e2c\u8a66\u4e00\u500b\u5177\u9ad4\u7684\u4f8b\u5b50\u55ce\uff1f\u6bd4\u5982 Z3 \u52a0\u6cd5\u6a21 3\uff1f<\/p>\n<p>\u5728\u9801\u9762\u4e2d\u7de8\u8f2f<\/p>\n<h2>\u60a8\u8aaa<\/h2>\n<p>final \u7684\u610f\u601d<\/p>\n<h2>Copilot \u8aaa<\/h2>\n<p>\u5728 Java \u4e2d\uff0c<code>final<\/code> \u662f\u4e00\u500b\u95dc\u9375\u5b57\uff0c\u7528\u4f86\u8868\u793a\u300c\u4e0d\u53ef\u8b8a\u300d\u6216\u300c\u4e0d\u53ef\u4fee\u6539\u300d\u7684\u8a9e\u610f\u3002\u5b83\u53ef\u4ee5\u7528\u5728 <strong>\u8b8a\u6578\u3001\u65b9\u6cd5\u3001\u985e\u5225<\/strong> \u4e0a\uff0c\u610f\u601d\u7565\u6709\u4e0d\u540c\uff1a<\/p>\n<h3>\ud83d\udd12 <code>final<\/code> \u8b8a\u6578<\/h3>\n<p>java<\/p>\n<p>\u8907\u88fd<\/p>\n<pre><code>final int x = 5;<\/code><\/pre>\n<ul>\n<li>\u8868\u793a <code>x<\/code> \u662f\u5e38\u6578\uff0c\u4e00\u65e6\u8ce6\u503c\u5f8c\u5c31\u4e0d\u80fd\u518d\u6539\u8b8a\u3002<\/li>\n<li>\u5c0d\u65bc\u7269\u4ef6\u53c3\u8003\uff08\u5982 <code>final List&lt;String&gt; list<\/code>\uff09\uff0c\u4e0d\u80fd\u6539\u8b8a\u53c3\u8003\uff0c\u4f46\u53ef\u4ee5\u4fee\u6539\u7269\u4ef6\u5167\u5bb9\uff08\u4f8b\u5982\u5411 list \u52a0\u5143\u7d20\uff09\u3002<\/li>\n<\/ul>\n<h3>\ud83d\udeab <code>final<\/code> \u65b9\u6cd5<\/h3>\n<p>java<\/p>\n<p>\u8907\u88fd<\/p>\n<pre><code>public final void doSomething() { ... }<\/code><\/pre>\n<ul>\n<li>\u8868\u793a\u9019\u500b\u65b9\u6cd5 <strong>\u4e0d\u80fd\u88ab\u5b50\u985e\u8986\u5beb\uff08override\uff09<\/strong>\u3002<\/li>\n<li>\u5e38\u7528\u4f86\u4fdd\u8b77\u6838\u5fc3\u908f\u8f2f\u4e0d\u88ab\u6539\u8b8a\u3002<\/li>\n<\/ul>\n<h3>\ud83e\uddf1 <code>final<\/code> \u985e\u5225<\/h3>\n<p>java<\/p>\n<p>\u8907\u88fd<\/p>\n<pre><code>public final class ProbableGroup { ... }<\/code><\/pre>\n<ul>\n<li>\u8868\u793a\u9019\u500b\u985e\u5225 <strong>\u4e0d\u80fd\u88ab\u7e7c\u627f<\/strong>\u3002<\/li>\n<li>\u7528\u4f86\u9632\u6b62\u64f4\u5145\uff0c\u78ba\u4fdd\u985e\u5225\u884c\u70ba\u4e0d\u88ab\u6539\u8b8a\u3002<\/li>\n<\/ul>\n<p>\u5728\u4f60\u63d0\u4f9b\u7684\u7a0b\u5f0f\u78bc\u4e2d\uff1a<\/p>\n<p>java<\/p>\n<p>\u8907\u88fd<\/p>\n<pre><code>public final class ProbableGroup<\/code><\/pre>\n<p>\u9019\u8868\u793a <code>ProbableGroup<\/code> \u662f\u4e00\u500b <strong>\u5c01\u9589\u7684\u985e\u5225<\/strong>\uff0c\u4e0d\u80fd\u88ab\u5176\u4ed6\u985e\u5225\u7e7c\u627f\u3002\u9019\u6a23\u505a\u901a\u5e38\u662f\u70ba\u4e86\u5b89\u5168\u6027\u3001\u8a2d\u8a08\u5b8c\u6574\u6027\uff0c\u6216\u662f\u907f\u514d\u932f\u8aa4\u64f4\u5145\u3002<\/p>\n<p>\u5982\u679c\u4f60\u60f3\u77e5\u9053\u4ec0\u9ebc\u6642\u5019\u8a72\u7528 <code>final<\/code>\uff0c\u6216\u5b83\u5728\u8a2d\u8a08\u6a21\u5f0f\u4e2d\u7684\u89d2\u8272\uff0c\u6211\u53ef\u4ee5\u5e6b\u4f60\u6df1\u5165\u63a2\u8a0e\u3002\u4f60\u6709\u5728\u7528 <code>final<\/code> \u4f86\u4fdd\u8b77 FSM \u6a21\u7d44\u55ce\uff1f<\/p>\n<p>\u5728\u9801\u9762\u4e2d\u7de8\u8f2f<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Lab 4: Abstract Algebra + Specifications If you are giv&#8230; &raquo; <a class=\"read-more-link\" href=\"https:\/\/vm1.go2see.me\/?p=1051\">\u95b1\u8b80\u5168\u6587<\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22],"tags":[],"class_list":["post-1051","post","type-post","status-publish","format-standard","hentry","category-22"],"_links":{"self":[{"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/posts\/1051","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1051"}],"version-history":[{"count":2,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/posts\/1051\/revisions"}],"predecessor-version":[{"id":1053,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=\/wp\/v2\/posts\/1051\/revisions\/1053"}],"wp:attachment":[{"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1051"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1051"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vm1.go2see.me\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1051"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}