How to check a list contains distinct values?

I want students to enter 3 values at which tan(x) is undefined, other than pi/2 which I already gave them. I currently have something like this:

<award><when>
$undefList.numValues=3 and 
<boolean>cos($undefList[1])=0 and cos($undefList[2])=0 and cos($undefList[3])=0</boolean> and 
<boolean>$undefList[1]!=$undefList[2] and $undefList[1]!=$undefList[3] and $undefList[3]!=$undefList[2]</boolean> and 
<boolean>$undefList[1]!=pi/2 and $undefList[1]!=pi/2 and $undefList[3]!=pi/2</boolean> 
</when></award></answer>

The answer checking is working as I want it to, but I have warnings that no referent is found for my list elements with [2] and [3].

I think I know how to do repeats for the conditions that cos(x)=0 and that x is not equal to pi/2, but I don’t know how to check that each element is distinct except by brute force. Is there a property or other easy way that I’m missing? (If not, should there be?)

This should work. It is kind of slow though. I’ve been slowly working on coding some set based modules (subsets, proper subsets, set equality, etc.).

Edited to add link: Checking for repeats in user inputs

<setup>
<module name="repeated_element">
  <moduleAttributes>
    <mathList name="L">1 0 1</mathList>
  </moduleAttributes>
  <setup>
    <mathList name="L_sort"><sort>$L</sort></mathList>

  <repeatForSequence name="adj_check" from="1" to="$L_sort.numComponents-1" indexName="i">
    <conditionalContent>
      <case condition="$L_sort[$i]=$L_sort[$i+1]">
        <number>1</number>
      </case>
      <else>0</else>
    </conditionalContent>
  </repeatForSequence>
    <number name="adj_sum"><math><sum>$adj_check</sum></math></number>
   </setup>
    <boolean>$adj_sum > 0</boolean>
</module>
  <boolean name="has_repeat1"><module copy="$repeated_element" L="$l1"/></boolean>
</setup>
<problem>
<mathInput name="l1">
  <label>Enter a list of numbers to see if a repeat is identified</label>
</mathInput>
</problem>
<feedback condition="$has_repeat1 and $l1.valueChanged">
  Your list has at least one repeated element
</feedback>
<feedback condition="!$has_repeat1 and $l1.valueChanged">
  Your list consistes of entirely unique elements
</feedback>

I have found the lack of “set-like” structures to be frustrating. I don’t know of a way except brute force. I have been sorting my lists, then using a repeatForSequence to check if any adjacent elements are equal to each other. If you know how long the list of values is, you can stop your index just one shy of the number of elements.

Here are two things that may work (but it’s still rather long). It doesn’t throw any warnings at me.

  • elementof can be used to check if something is (or is not) in their list
  • <convertSetToList>could be used to check if their list is shorter than 3 elements. But the syntax is very funky. I didn’t even know this was a thing and it just popped up when I was starting to type some conditionalContent yesterday.

Both of my approaches above require that you convert the user input into <numberList> first.

<setup>
  <numberList name="L2">
    <convertSetToList><math>{$user_in}</math></convertSetToList>
  </numberList>
  <booleanList name="BL">
  <repeatForSequence name="cos_vals" from="1" to="$L2.numComponents" indexName="i">
    <boolean><abs>cos($L2[$i])</abs>=0</boolean>
  </repeatForSequence>
  </booleanList>
  <!-- <number name="s"><sum>$cos_vals</sum></number> -->
</setup>
<answer>
<mathInput name="user_in">
  <label>Input your list of numbers:</label>
</mathInput>
  <award>
    <when>
      !(pi/2 elementof $L2) 
      and 
      $L2.numComponents=3
      and 
      <and>$BL</and>
    </when>
  </award>
</answer>

Checking for duplicates

Thank you for the solutions and ideas, Carly!

I haven’t made it far through this discussion yet. But, I see why these warnings are incorrectly generated. I’m assuming that undefList is created with something like

<mathInput name="undef"/>
<mathList mergeMathLists name="undefList">$undef</mathList>

In this case, since undefList starts with having only one item in it, DoenetML thinks that $undefList[2] is not defined. It doesn’t realize that the list could get more entries added to it later.

The warning is harmless, but it is a bug. I created a bug report.

This seems a lot more complicated than it should be. But, if you convert a list to a set with { }, then use <convertSetToList>, and then use <mathList> to actually make a list (with .numValues) on it, then you get a deduplicated version of your list.

<mathInput name="undef"></mathInput>
<mathList mergeMathLists name="undefList">$undef</mathList>

<mathList name="dedup"><convertSetToList><math>{$undefList}</math></convertSetToList></mathList>
<boolean name="noDuplicates">$dedup.numValues = $undefList.numValues</boolean>

(I don’t remember what I made <convertSetToList> for, but it was probably for a specific problem. Not much thought was given to sets, it seems.)

In this example, sin(0) and 0 are considered different expressions, so they wouldn’t be deduplicated. You could simplify first, and then they would be recognized as duplicates.

I like @Carly_Vollet’s use of <convertSetToList> better. I’m also not sure how it works, but apparently, you don’t have to make a math list first.

Maybe we need some people to give guidance on how sets should work in Doenet!

I didn’t have mergeMathLists, just <mathList name="undefList">$undefEnt</mathList> where undefEnt is a mathInput. What does mergeMathLists do?

I see why the warning is generated, and it’s not a bad idea. But as long as the award simply fails when those elements don’t exist and doesn’t cause any error, then I’m fine with it. I do start by checking there are 3 elements. I just don’t like seeing yellow warnings and wanted to check both that it was okay, and also that there wasn’t an easier way.

I didn’t know about sorted lists or convertSetToList, and both of those would help a lot in general, or if my list had more than 3 elements.

Related question (in regards to evaluating cosine values repeatedly)…

Originally I had an extra sum component that added up all of my absolute cosine values to check if that sum was zero (which would show that all individual cosine values were zero). I ran into trouble though when I tried 9pi/2 in combination with a few other odd multiples of pi/2. My sum would print as zero, but my boolean asking if it was zero came up false.

My guess is that it is a floating point issue with how 9*pi/2 is rounded? Or are we using power series for cosine and we are too far out of the interval of convergence?

@Duane_Nykamp here is an example of what I mean:

List of odd multiples of pi/2: <numberList name="L">3pi/2 5pi/2 7pi/2 9pi/2 11pi/2 13pi/2</numberList><br/>

Cosine at these values:
<repeatForSequence name="cosL" from="1" to="$L.numComponents" indexName="i">
  <number>cos($L[$i])</number>
</repeatForSequence><br/>

Sum of the list:
 <sum name="s">$cosL</sum>
<br/>

Checking if list sum is actually zero (or just floats to zero):
<boolean>$s=0</boolean>

The documentation says that you operate on { }, so I just added an extra numberList outside of the convertSetToList. Then you do all of your boolean checks on that new list.