Package jptests :: Package formalisms :: Package music_halfspan :: Module semantics
[hide private]
[frames] | no frames]

Source Code for Module jptests.formalisms.music_halfspan.semantics

  1  """Unit tests for jazzparser.formalisms.music_halfspan.semantics. 
  2   
  3  """ 
  4  """ 
  5  ============================== License ======================================== 
  6   Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding 
  7    
  8   This file is part of The Jazz Parser. 
  9    
 10   The Jazz Parser is free software: you can redistribute it and/or modify 
 11   it under the terms of the GNU General Public License as published by 
 12   the Free Software Foundation, either version 3 of the License, or 
 13   (at your option) any later version. 
 14    
 15   The Jazz Parser is distributed in the hope that it will be useful, 
 16   but WITHOUT ANY WARRANTY; without even the implied warranty of 
 17   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 18   GNU General Public License for more details. 
 19    
 20   You should have received a copy of the GNU General Public License 
 21   along with The Jazz Parser.  If not, see <http://www.gnu.org/licenses/>. 
 22   
 23  ============================ End license ====================================== 
 24   
 25  """ 
 26  __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"  
 27   
 28  import unittest, os 
 29  from jazzparser import settings 
 30   
 31  from jazzparser.formalisms.music_halfspan.semantics import Semantics, \ 
 32              EnharmonicCoordinate, List, semantics_from_string, ListCat, \ 
 33              Variable, FunctionApplication, Leftonto, Rightonto, \ 
 34              LambdaAbstraction, Coordination, apply, compose, \ 
 35              list_lf_to_coordinates 
 36   
37 -class TestStringBuilder(unittest.TestCase):
38 """ 39 Tests for building LFs using C{semantics_from_string}. 40 41 """
42 - def test_coordinate(self):
43 sem = semantics_from_string("<3,4>") 44 # Check this returned the right type 45 self.assertIsInstance(sem.lf, EnharmonicCoordinate) 46 # Check the coordinate was correctly interpreted 47 self.assertEqual(sem.lf.harmonic_coord, (3,4))
48
49 - def test_list(self):
50 sem = semantics_from_string("[<1,2>]") 51 # Check this returned the right type 52 self.assertIsInstance(sem.lf, List) 53 # Check the list was correctly interpreted 54 self.assertEqual(len(sem.lf), 1) 55 # And the coordinate in it 56 self.assertIsInstance(sem.lf[0], EnharmonicCoordinate) 57 self.assertEqual((sem.lf[0].x,sem.lf[0].y), (1,2)) 58 59 # Multi-element list 60 sem = semantics_from_string("[<1,2>,<3,4>]") 61 # Check this returned the right type 62 self.assertIsInstance(sem.lf, List)
63
64 - def test_cat(self):
65 sem = semantics_from_string("[<1,2>]+[<3,4>]") 66 # Check this returned the right type 67 self.assertIsInstance(sem.lf, ListCat) 68 # Check the cat was correctly interpreted 69 self.assertEqual(len(sem.lf), 2)
70
71 - def test_variable(self):
72 sem = semantics_from_string("$var13") 73 # Check this returned the right type 74 self.assertIsInstance(sem.lf, Variable) 75 # Check the variable was correctly interpreted 76 self.assertEqual(sem.lf.name, "var") 77 self.assertEqual(sem.lf.index, 13) 78 79 # Try one without a number 80 sem = semantics_from_string("$var") 81 # Check the variable was correctly interpreted 82 self.assertEqual(sem.lf.name, "var") 83 self.assertEqual(sem.lf.index, 0) 84 85 # And a single-character 86 sem = semantics_from_string("$X") 87 # Check the variable was correctly interpreted 88 self.assertEqual(sem.lf.name, "X") 89 self.assertEqual(sem.lf.index, 0)
90
91 - def test_predicates(self):
92 sem = semantics_from_string("leftonto($X)") 93 # Check this returned the right type 94 self.assertIsInstance(sem.lf, FunctionApplication) 95 self.assertIsInstance(sem.lf.functor, Leftonto) 96 97 sem = semantics_from_string("rightonto($X)") 98 # Check this returned the right type 99 self.assertIsInstance(sem.lf, FunctionApplication) 100 self.assertIsInstance(sem.lf.functor, Rightonto)
101
102 - def test_lambda_abstraction(self):
103 sem = semantics_from_string("\\$X.$X") 104 # Check this returned the right type 105 self.assertIsInstance(sem.lf, LambdaAbstraction) 106 # Check the variable was correctly interpreted 107 self.assertIsInstance(sem.lf.variable, Variable) 108 # Check the expression was correctly interpreted 109 self.assertIsInstance(sem.lf.expression, Variable) 110 111 # Something different 112 sem = semantics_from_string("\\$X.<1,2>") 113 self.assertIsInstance(sem.lf.expression, EnharmonicCoordinate) 114 115 # Try a multi-variable abstraction 116 sem = semantics_from_string("\\$X,$Y.$X") 117 # Check this returned the right type 118 self.assertIsInstance(sem.lf, LambdaAbstraction) 119 # Check the first variable was correctly interpreted 120 self.assertIsInstance(sem.lf.variable, Variable) 121 # Check the second abstraction is right too 122 self.assertIsInstance(sem.lf.expression, LambdaAbstraction) 123 self.assertIsInstance(sem.lf.expression.variable, Variable)
124
126 sem = semantics_from_string("($X $X)") 127 # Check this returned the right type 128 self.assertIsInstance(sem.lf, FunctionApplication) 129 # Check the variable was correctly interpreted 130 self.assertIsInstance(sem.lf.functor, Variable) 131 # Check the expression was correctly interpreted 132 self.assertIsInstance(sem.lf.argument, Variable) 133 134 sem = semantics_from_string("($X <1,2>)") 135 # Check this returned the right type 136 self.assertIsInstance(sem.lf, FunctionApplication) 137 # Check the variable was correctly interpreted 138 self.assertIsInstance(sem.lf.functor, Variable) 139 # Check the expression was correctly interpreted 140 self.assertIsInstance(sem.lf.argument, EnharmonicCoordinate) 141 142 sem = semantics_from_string("(($X <1,2>) <3,4>)") 143 # Check this returned the right type 144 self.assertIsInstance(sem.lf, FunctionApplication) 145 # Check the variable was correctly interpreted 146 self.assertIsInstance(sem.lf.functor, FunctionApplication) 147 # Check the expression was correctly interpreted 148 self.assertIsInstance(sem.lf.argument, EnharmonicCoordinate)
149
150 - def test_coordination(self):
151 # Create a simple coordination 152 sem = semantics_from_string(r"(\$x.leftonto($x)) & (\$x.leftonto($x))") 153 self.assertIsInstance(sem.lf, Coordination) 154 self.assertEqual(len(sem.lf), 2) 155 156 # Creat a three-way coordination (in fact it will be nested) 157 sem = semantics_from_string(r"(\$x.leftonto($x)) & (\$x.rightonto($x)) & (\$x.leftonto($x))") 158 self.assertIsInstance(sem.lf, Coordination) 159 # This is actually length 2, because it's nested 160 # It would beta-reduce to length 3 161 self.assertEqual(len(sem.lf), 2) 162 self.assertIsInstance(sem.lf[1], Coordination) 163 self.assertEqual(len(sem.lf[1]), 2)
164
165 - def test_general(self):
166 # Try doing some mixtures of things 167 # Just make sure the parsing of these doesn't generate any errors 168 sem0 = semantics_from_string("[<1,2>,<3,4>]+[<1,2>]") 169 sem1 = semantics_from_string("\\$x. ($x <1,2>)") 170 sem2 = semantics_from_string("\\$x. leftonto($x)") 171 sem3 = semantics_from_string("\\$x. leftonto(($x <1,2>))") 172 sem4 = semantics_from_string("[\\$x. leftonto(($x [<1,2>]))]")
173
174 - def test_now(self):
175 # Build a now predicate and check there are no errors 176 sem0 = semantics_from_string(r"now@1($x)") 177 sem0.beta_reduce() 178 179 sem1 = semantics_from_string(r"\$x.now@1(leftonto($x))") 180 sem1.beta_reduce() 181 182 sem2 = semantics_from_string(r"now@10((\$x.leftonto($x)) & (\$x.leftonto(leftonto($x))))") 183 sem2.beta_reduce()
184
185 -class TestPredicates(unittest.TestCase):
186 """ 187 Tests for creating and reducing the predicates. 188 189 """
190 - def test_leftonto(self):
191 # Check the leftonto gets carried to the first item of the list 192 sem = semantics_from_string("leftonto([<1,2>])") 193 sem.beta_reduce() 194 self.assertIsInstance(sem.lf, List) 195 self.assertIsInstance(sem.lf[0], FunctionApplication) 196 self.assertIsInstance(sem.lf[0].functor, Leftonto) 197 198 # Check the same thing with a multi-element list 199 sem = semantics_from_string("leftonto([<1,2>,<3,4>])") 200 sem.beta_reduce() 201 self.assertIsInstance(sem.lf, List) 202 self.assertIsInstance(sem.lf[0], FunctionApplication) 203 self.assertIsInstance(sem.lf[0].functor, Leftonto) 204 205 # Now with multiple predicates 206 sem = semantics_from_string("leftonto(leftonto([<1,2>]))") 207 sem.beta_reduce() 208 self.assertIsInstance(sem.lf, List) 209 self.assertIsInstance(sem.lf[0], FunctionApplication) 210 self.assertIsInstance(sem.lf[0].functor, Leftonto) 211 self.assertIsInstance(sem.lf[0].argument, FunctionApplication) 212 self.assertIsInstance(sem.lf[0].argument.functor, Leftonto) 213 214 # Check rightonto works as well 215 sem = semantics_from_string("rightonto([<1,2>,<3,4>])") 216 sem.beta_reduce() 217 self.assertIsInstance(sem.lf, List) 218 self.assertIsInstance(sem.lf[0], FunctionApplication) 219 self.assertIsInstance(sem.lf[0].functor, Rightonto)
220
222 # Check that \x.x works as it should: this should reduce to just a coord 223 sem0 = semantics_from_string(r"(\$x.$x <1,2>)") 224 sem0.beta_reduce() 225 sem1 = semantics_from_string("<1,2>") 226 self.assertEqual(sem0, sem1) 227 228 # Try a trivial abstraction that throws away its argument 229 sem0 = semantics_from_string(r"(\$x.<1,2> $y)") 230 sem0.beta_reduce() 231 sem1 = semantics_from_string("<1,2>") 232 self.assertEqual(sem0, sem1) 233 234 # Try a multiple abstraction applied to all its args 235 sem0 = semantics_from_string(r"((\$x,$y.$x <1,2>) <3,4>)") 236 sem0.beta_reduce() 237 sem1 = semantics_from_string("<1,2>") 238 self.assertEqual(sem0, sem1)
239
240 -class TestCoordination(unittest.TestCase):
241 """ 242 Tests for use of semantic coordination. 243 244 """
245 - def test_flatten(self):
246 """ 247 Check the flattening of coordinations during beta-reduction is working 248 249 """ 250 # A 3-way coordination 251 sem0 = semantics_from_string(r"(\$x.leftonto($x)) & (\$x.rightonto($x)) & (\$x.leftonto($x))") 252 # We've already checked that this comes out nested as expected 253 # Now check it flattens during beta-reduction 254 sem0.beta_reduce() 255 self.assertIsInstance(sem0.lf, Coordination) 256 self.assertEqual(len(sem0.lf), 3) 257 258 # Try the exact same thing, but with brackets to make it combine the 259 # other way initially 260 sem1 = semantics_from_string(r"((\$x.leftonto($x)) & (\$x.rightonto($x))) & (\$x.leftonto($x))") 261 sem1.beta_reduce() 262 self.assertIsInstance(sem1.lf, Coordination) 263 self.assertEqual(len(sem1.lf), 3) 264 # This should produce the same thing as before 265 self.assertEqual(sem0, sem1) 266 267 # This doesn't make sense, but Coordination shouldn't complain 268 sem0 = semantics_from_string(r"<1,2> & ((\$x.rightonto($x)) & (\$x.leftonto($x))) & <3,4> & <5,6>") 269 sem1 = semantics_from_string(r"<1,2> & (\$x.rightonto($x)) & (\$x.leftonto($x)) & <3,4> & <5,6>") 270 sem0.beta_reduce() 271 sem1.beta_reduce() 272 self.assertEqual(sem0, sem1)
273
274 -class TestSemantics(unittest.TestCase):
275 """ 276 General tests for the semantics module. 277 278 """
279 - def test_paper_example(self):
280 """ 281 Generates the example that we used in the paper (from Alice in 282 Wonderland) as if it's coming from the combinators. Checks that 283 the right overall LF comes out. 284 285 This is pretty insane, but a great test that the semantics is 286 behaving correctly in the contexts in which we'll be using it. 287 288 """ 289 sem = semantics_from_string 290 291 # Lexical 292 sem_0_1 = sem(r"[<0,0>]") 293 sem_1_2 = sem(r"\$x.$x") 294 # Bapply 295 sem_0_2 = apply(sem_1_2, sem_0_1) 296 self.assertTrue(sem_0_2.alpha_equivalent(sem(r"[<0,0>]"))) 297 298 # Lexical 299 sem_2_3 = sem(r"\$x.leftonto($x)") 300 sem_3_4 = sem(r"\$x.leftonto($x)") 301 # Fcomp 302 sem_2_4 = compose(sem_2_3, sem_3_4) 303 self.assertTrue(sem_2_4.alpha_equivalent( 304 sem(r"\$x.leftonto(leftonto($x))"))) 305 306 # Lexical 307 sem_4_5 = sem(r"\$x.leftonto($x)") 308 # Fcomp 309 sem_2_5 = compose(sem_2_4, sem_4_5) 310 self.assertTrue(sem_2_5.alpha_equivalent( 311 sem(r"\$x.leftonto(leftonto(leftonto($x)))"))) 312 313 # Lexical 314 sem_5_6 = sem(r"\$x.leftonto($x)") 315 # Fcomp 316 sem_2_6 = compose(sem_2_5, sem_5_6) 317 self.assertTrue(sem_2_6.alpha_equivalent( 318 sem(r"\$x.leftonto(leftonto(leftonto(leftonto($x))))"))) 319 320 # Lexical 321 sem_6_7 = sem(r"\$x.leftonto($x)") 322 # Fcomp 323 sem_2_7 = compose(sem_2_6, sem_6_7) 324 self.assertTrue(sem_2_7.alpha_equivalent( 325 sem(r"\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))"))) 326 327 # Lexical 328 sem_7_8 = sem(r"\$x.leftonto($x)") 329 sem_8_9 = sem(r"\$x.$x") 330 # Fcomp 331 sem_7_9 = compose(sem_7_8, sem_8_9) 332 self.assertTrue(sem_7_9.alpha_equivalent( 333 sem(r"\$x.leftonto($x)"))) 334 335 # Lexical 336 sem_9_10 = sem(r"\$x.leftonto($x)") 337 # Fcomp 338 sem_7_10 = compose(sem_7_9, sem_9_10) 339 self.assertTrue(sem_7_10.alpha_equivalent( 340 sem(r"\$x.leftonto(leftonto($x))"))) 341 342 # Coord 343 sem_2_10 = Semantics(Coordination([sem_2_7.lf, sem_7_10.lf])) 344 sem_2_10.beta_reduce() 345 semtest = sem(r"(\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\ 346 "& (\$x.leftonto(leftonto($x)))") 347 semtest.beta_reduce() 348 self.assertTrue(sem_2_10.alpha_equivalent(semtest)) 349 350 # Lexical 351 sem_10_11 = sem(r"\$x.leftonto($x)") 352 # Fcomp 353 sem_2_11 = compose(sem_2_10, sem_10_11) 354 semtest = sem(r"\$y.("\ 355 "((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\ 356 "& (\$x.leftonto(leftonto($x)))) leftonto($y))") 357 semtest.beta_reduce() 358 self.assertTrue(sem_2_11.alpha_equivalent(semtest)) 359 360 # Skip a couple of lexical LFs 361 sem_11_13 = sem(r"\$x.leftonto(leftonto($x))") 362 # Coord 363 sem_2_13 = Semantics(Coordination([sem_2_11.lf, sem_11_13.lf])) 364 sem_2_13.beta_reduce() 365 semtest = sem(r"(\$y.("\ 366 "((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\ 367 "& (\$x.leftonto(leftonto($x)))) leftonto($y))) "\ 368 "& (\$x.leftonto(leftonto($x)))") 369 semtest.beta_reduce() 370 self.assertTrue(sem_2_13.alpha_equivalent(semtest)) 371 372 # Lexical 373 sem_13_14 = sem(r"[<0,0>]") 374 # Fapply 375 sem_2_14 = apply(sem_2_13, sem_13_14) 376 semtest = sem(r"((\$y.("\ 377 "((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\ 378 "& (\$x.leftonto(leftonto($x)))) leftonto($y))) "\ 379 "& (\$x.leftonto(leftonto($x))) [<0,0>])") 380 semtest.beta_reduce() 381 self.assertTrue(sem_2_14.alpha_equivalent(semtest)) 382 383 # Finally, development 384 sem_0_14 = Semantics(ListCat([sem_0_2.lf, sem_2_14.lf])) 385 sem_0_14.beta_reduce() 386 semtest = sem(r"[<0,0>, "\ 387 "((\$y.("\ 388 "((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) "\ 389 "& (\$x.leftonto(leftonto($x)))) leftonto($y))) "\ 390 "& (\$x.leftonto(leftonto($x))) <0,0>)]") 391 semtest.beta_reduce() 392 self.assertTrue(sem_0_14.alpha_equivalent(semtest))
393
394 -class TestLfToCoordinates(unittest.TestCase):
395 """ 396 Tests for producing a tonal space path from a logical form. 397 398 """
399 - def setUp(self):
400 """ 401 Builds a load of logical forms and gives their expected tonal space 402 paths. 403 404 """ 405 self.longMessage = True 406 sem = semantics_from_string 407 408 # Some very simple tests on cadences 409 self.basic = [ 410 (sem(r"[leftonto(leftonto(<0,0>))]"), 411 [(2,0),(1,0),(0,0)]), 412 (sem(r"[leftonto(leftonto(leftonto(<0,0>)))]"), 413 [(3,0),(2,0),(1,0),(0,0)]), 414 (sem(r"[rightonto(rightonto(<0,0>))]"), 415 [(2,2),(3,2),(4,2)]), 416 (sem(r"[<0,0>,<0,1>]"), 417 [(0,0),(0,1)]), 418 (sem(r"[<4,-1>,<4,0>]"), 419 [(0,0),(0,1)]), 420 ] 421 # Some tests of cadences being combined by development 422 self.develop = [ 423 (sem(r"[<0,0>,leftonto(leftonto(<0,0>))]"), 424 [(0,0),(2,0),(1,0),(0,0)]), 425 (sem(r"[<0,0>,leftonto(leftonto(leftonto(<0,0>)))]"), 426 [(0,0),(-1,1),(-2,1),(-3,1),(-4,1)]), 427 (sem(r"[<0,0>,leftonto(leftonto(leftonto(<0,0>)))]"), 428 [(0,0),(-1,1),(-2,1),(-3,1),(-4,1)]), 429 (sem(r"[<-4,-2>,leftonto(leftonto(leftonto(<0,0>)))]"), 430 [(0,0),(-1,1),(-2,1),(-3,1),(-4,1)]), 431 (sem(r"[<0,0>, leftonto(leftonto(leftonto(<0,0>))), "\ 432 "leftonto(leftonto(leftonto(<0,0>)))]"), 433 [(0,0),(-1,1),(-2,1),(-3,1),(-4,1),(-5,2),(-6,2),(-7,2),(-8,2)]), 434 (sem(r"[<1,2>,leftonto(leftonto(leftonto(<0,0>)))]"), 435 [(1, 2), (-1, 1), (-2, 1), (-3, 1), (-4, 1)]), 436 ] 437 # Some test on coordinated cadences 438 self.coordination = [ 439 (sem(r"[((\$x.leftonto($x)) & (\$x.leftonto($x)) <0,0>)]"), 440 [(1,0),(1,0),(0,0)]), 441 (sem(r"[((\$x.rightonto($x)) & (\$x.leftonto($x)) <0,0>)]"), 442 [(3,2),(5,2),(4,2)]), 443 (sem(r"[((\$x.leftonto($x)) & (\$x.leftonto(leftonto(leftonto($x)))) <0,0>)]"), 444 [(1,0),(3,0),(2,0),(1,0),(0,0)]), 445 (sem(r"[((\$x.leftonto($x)) & (\$x.leftonto(leftonto(leftonto(leftonto($x))))) <0,0>)]"), 446 [(1,0),(4,0),(3,0),(2,0),(1,0),(0,0)]), 447 ] 448 # And some with nesting 449 self.nested_coordination = [ 450 (sem(r"[((\$x.leftonto($x)) & (\$x.leftonto((((\$y.leftonto($y)) & (\$y.leftonto(leftonto($y)))) $x))) <0,0>)]"), 451 [(1,0),(2,0),(1,0),(2,0),(1,0),(0,0)]), 452 # Call Me Irresponsible 453 (sem(r"[<0,0>, (((\$y.(((\$x.leftonto(leftonto(leftonto(leftonto(leftonto($x)))))) & (\$x.leftonto(leftonto($x)))) leftonto($y))) & (\$x.leftonto(leftonto($x)))) <0,0>)]"), 454 [(0,0),(-2,-1),(-3,-1),(-4,-1),(-5,-1),(-6,-1),(-5,-1),(-6,-1),(-7,-1),(-6,-1),(-7,-1),(-8,-1)]), 455 ]
456
457 - def assertExpected(self, output, correct, sems):
458 self.assertEqual(output, correct, msg="Expected output: %s. Got: %s. LF was %s" \ 459 % (correct, output, sems))
460
461 - def test_basic(self):
462 for semantics,correct in self.basic: 463 output,times = zip(*list_lf_to_coordinates(semantics.lf)) 464 self.assertExpected(list(output), correct, semantics)
465
466 - def test_develop(self):
467 for semantics,correct in self.develop: 468 output,times = zip(*list_lf_to_coordinates(semantics.lf)) 469 self.assertExpected(list(output), correct, semantics)
470
471 - def test_coordination(self):
472 for semantics,correct in self.coordination: 473 output,times = zip(*list_lf_to_coordinates(semantics.lf)) 474 self.assertExpected(list(output), correct, semantics)
475
476 - def test_nested_coordination(self):
477 for semantics,correct in self.nested_coordination: 478 output,times = zip(*list_lf_to_coordinates(semantics.lf)) 479 self.assertExpected(list(output), correct, semantics)
480
481 -class TestEnharmonicCoordinate(unittest.TestCase):
482 """ 483 Tests for certain bits of 484 L{jazzparser.formalisms.music_halfspan.semantics.EnharmonicCoordinate}. 485 486 """
487 - def test_nearest(self):
488 """ 489 This is a particularly difficult bit of EnharmonicCoordinate's 490 behaviour to get right, so it's worth testing a good few examples to 491 make sure it's behaving right. 492 493 """ 494 self.longMessage = False 495 496 # Define some test pairs and the expected result 497 TESTS = [ 498 # Tuples: base coord, coord to be shifted, expected result 499 # Some that shouldn't be shifted 500 ((0,0), (0,0), (0,0)), 501 ((0,0), (2,0), (2,0)), 502 ((0,0), (-2,0), (-2,0)), 503 ((0,0), (1,1), (1,1)), 504 # Some that should 505 ((0,0), (2,2), (-2,0)), 506 ((0,0), (-2,-2), (2,0)), 507 ] 508 509 for base,candidate,correct in TESTS: 510 # Build enharmonic coords 511 base_crd = EnharmonicCoordinate.from_harmonic_coord(base) 512 candidate_crd = EnharmonicCoordinate.from_harmonic_coord(candidate) 513 # Try running nearest on these 514 result_crd = base_crd.nearest(candidate_crd) 515 result = result_crd.harmonic_coord 516 # Check it came out right 517 self.assertEqual(result, correct, msg="nearest instance of %s "\ 518 "[%s] to %s should have been %s, got %s" % \ 519 (candidate, 520 candidate_crd.zero_coord, 521 base, 522 correct, 523 result))
524