<commit_before>/*
 * Copyright (C) 2012 Paulo Marques (pjp.marques@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/* Information about the P9813 protocol obtained from:
 * http://www.seeedstudio.com/wiki/index.php?title=Twig_-_Chainable_RGB_LED
 *
 * HSB to RGB routine adapted from:
 * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
 */

// --------------------------------------------------------------------------------------

#include "ChainableLED.h"

// Forward declaration
float hue2rgb(float p, float q, float t);

// --------------------------------------------------------------------------------------

ChainableLED::ChainableLED(byte clk_pin, byte data_pin, byte number_of_leds) :
    _clk_pin(clk_pin), _data_pin(data_pin), _num_leds(number_of_leds)
{
    pinMode(_clk_pin, OUTPUT);
    pinMode(_data_pin, OUTPUT);

    _led_state = (byte*) calloc(_num_leds*3, sizeof(byte));

    for (byte i=0; i<_num_leds; i++)
        setColorRGB(i, 0, 0, 0);
}

ChainableLED::~ChainableLED()
{
    free(_led_state);
}

// --------------------------------------------------------------------------------------

void ChainableLED::clk(void)
{
    digitalWrite(_clk_pin, LOW);
    delayMicroseconds(_CLK_PULSE_DELAY);
    digitalWrite(_clk_pin, HIGH);
    delayMicroseconds(_CLK_PULSE_DELAY);
}

void ChainableLED::sendByte(byte b)
{
    // Send one bit at a time, starting with the MSB
    for (byte i=0; i<8; i++)
    {
        // If MSB is 1, write one and clock it, else write 0 and clock
        if ((b & 0x80) != 0)
            digitalWrite(_data_pin, HIGH);
        else
            digitalWrite(_data_pin, LOW);
        clk();

        // Advance to the next bit to send
        b <<= 1;
    }
}

void ChainableLED::sendColor(byte red, byte green, byte blue)
{
    // Start by sending a byte with the format "1 1 /B7 /B6 /G7 /G6 /R7 /R6"
    byte prefix = B11000000;
    if ((blue & 0x80) == 0)     prefix|= B00100000;
    if ((blue & 0x40) == 0)     prefix|= B00010000;
    if ((green & 0x80) == 0)    prefix|= B00001000;
    if ((green & 0x40) == 0)    prefix|= B00000100;
    if ((red & 0x80) == 0)      prefix|= B00000010;
    if ((red & 0x40) == 0)      prefix|= B00000001;
    sendByte(prefix);

    // Now must send the 3 colors
    sendByte(blue);
    sendByte(green);
    sendByte(red);
}

void ChainableLED::setColorRGB(byte led, byte red, byte green, byte blue)
{
    // Send data frame prefix (32x "0")
    sendByte(0x00);
    sendByte(0x00);
    sendByte(0x00);
    sendByte(0x00);

    // Send color data for each one of the leds
    for (byte i=0; i<_num_leds; i++)
    {
        if (i == led)
        {
            _led_state[i*3 + _CL_RED] = red;
            _led_state[i*3 + _CL_GREEN] = green;
            _led_state[i*3 + _CL_BLUE] = blue;
        }

        sendColor(_led_state[i*3 + _CL_RED],
                  _led_state[i*3 + _CL_GREEN],
                  _led_state[i*3 + _CL_BLUE]);
    }

    // Terminate data frame (32x "0")
    sendByte(0x00);
    sendByte(0x00);
    sendByte(0x00);
    sendByte(0x00);
}

void ChainableLED::setColorHSB(byte led, float hue, float saturation, float brightness)
{
    float r, g, b;

    constrain(hue, 0.0, 1.0);
    constrain(saturation, 0.0, 1.0);
    constrain(brightness, 0.0, 1.0);

    if(saturation == 0.0)
    {
        r = g = b = brightness;
    }
    else
    {
        float q = brightness < 0.5 ?
            brightness * (1.0 + saturation) : brightness + saturation - brightness * saturation;
        float p = 2.0 * brightness - q;
        r = hue2rgb(p, q, hue + 1.0/3.0);
        g = hue2rgb(p, q, hue);
        b = hue2rgb(p, q, hue - 1.0/3.0);
    }

    setColorRGB(led, (byte)(255.0*r), (byte)(255.0*g), (byte)(255.0*b));
}

// --------------------------------------------------------------------------------------

float hue2rgb(float p, float q, float t)
{
    if (t < 0.0)
        t += 1.0;
    if(t > 1.0)
        t -= 1.0;
    if(t < 1.0/6.0)
        return p + (q - p) * 6.0 * t;
    if(t < 1.0/2.0)
        return q;
    if(t < 2.0/3.0)
        return p + (q - p) * (2.0/3.0 - t) * 6.0;

    return p;
}
<commit_msg>Create begin method<commit_after>/*
 * Copyright (C) 2012 Paulo Marques (pjp.marques@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/* Information about the P9813 protocol obtained from:
 * http://www.seeedstudio.com/wiki/index.php?title=Twig_-_Chainable_RGB_LED
 *
 * HSB to RGB routine adapted from:
 * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
 */

// --------------------------------------------------------------------------------------

#include "ChainableLED.h"

// Forward declaration
float hue2rgb(float p, float q, float t);

// --------------------------------------------------------------------------------------

ChainableLED::ChainableLED(byte clk_pin, byte data_pin, byte number_of_leds) :
    _clk_pin(clk_pin), _data_pin(data_pin), _num_leds(number_of_leds)
{
    _led_state = (byte*) calloc(_num_leds*3, sizeof(byte));
}

ChainableLED::~ChainableLED()
{
    free(_led_state);
}

// --------------------------------------------------------------------------------------

void ChainableLED::init()
{
    pinMode(_clk_pin, OUTPUT);
    pinMode(_data_pin, OUTPUT);

    for (byte i=0; i<_num_leds; i++)
        setColorRGB(i, 0, 0, 0);
}

void ChainableLED::clk(void)
{
    digitalWrite(_clk_pin, LOW);
    delayMicroseconds(_CLK_PULSE_DELAY);
    digitalWrite(_clk_pin, HIGH);
    delayMicroseconds(_CLK_PULSE_DELAY);
}

void ChainableLED::sendByte(byte b)
{
    // Send one bit at a time, starting with the MSB
    for (byte i=0; i<8; i++)
    {
        // If MSB is 1, write one and clock it, else write 0 and clock
        if ((b & 0x80) != 0)
            digitalWrite(_data_pin, HIGH);
        else
            digitalWrite(_data_pin, LOW);
        clk();

        // Advance to the next bit to send
        b <<= 1;
    }
}

void ChainableLED::sendColor(byte red, byte green, byte blue)
{
    // Start by sending a byte with the format "1 1 /B7 /B6 /G7 /G6 /R7 /R6"
    byte prefix = B11000000;
    if ((blue & 0x80) == 0)     prefix|= B00100000;
    if ((blue & 0x40) == 0)     prefix|= B00010000;
    if ((green & 0x80) == 0)    prefix|= B00001000;
    if ((green & 0x40) == 0)    prefix|= B00000100;
    if ((red & 0x80) == 0)      prefix|= B00000010;
    if ((red & 0x40) == 0)      prefix|= B00000001;
    sendByte(prefix);

    // Now must send the 3 colors
    sendByte(blue);
    sendByte(green);
    sendByte(red);
}

void ChainableLED::setColorRGB(byte led, byte red, byte green, byte blue)
{
    // Send data frame prefix (32x "0")
    sendByte(0x00);
    sendByte(0x00);
    sendByte(0x00);
    sendByte(0x00);

    // Send color data for each one of the leds
    for (byte i=0; i<_num_leds; i++)
    {
        if (i == led)
        {
            _led_state[i*3 + _CL_RED] = red;
            _led_state[i*3 + _CL_GREEN] = green;
            _led_state[i*3 + _CL_BLUE] = blue;
        }

        sendColor(_led_state[i*3 + _CL_RED],
                  _led_state[i*3 + _CL_GREEN],
                  _led_state[i*3 + _CL_BLUE]);
    }

    // Terminate data frame (32x "0")
    sendByte(0x00);
    sendByte(0x00);
    sendByte(0x00);
    sendByte(0x00);
}

void ChainableLED::setColorHSB(byte led, float hue, float saturation, float brightness)
{
    float r, g, b;

    constrain(hue, 0.0, 1.0);
    constrain(saturation, 0.0, 1.0);
    constrain(brightness, 0.0, 1.0);

    if(saturation == 0.0)
    {
        r = g = b = brightness;
    }
    else
    {
        float q = brightness < 0.5 ?
            brightness * (1.0 + saturation) : brightness + saturation - brightness * saturation;
        float p = 2.0 * brightness - q;
        r = hue2rgb(p, q, hue + 1.0/3.0);
        g = hue2rgb(p, q, hue);
        b = hue2rgb(p, q, hue - 1.0/3.0);
    }

    setColorRGB(led, (byte)(255.0*r), (byte)(255.0*g), (byte)(255.0*b));
}

// --------------------------------------------------------------------------------------

float hue2rgb(float p, float q, float t)
{
    if (t < 0.0)
        t += 1.0;
    if(t > 1.0)
        t -= 1.0;
    if(t < 1.0/6.0)
        return p + (q - p) * 6.0 * t;
    if(t < 1.0/2.0)
        return q;
    if(t < 2.0/3.0)
        return p + (q - p) * (2.0/3.0 - t) * 6.0;

    return p;
}
<|endoftext|>


<commit_before>// Ignore unused parameter warnings coming from cppunit headers
#include <libmesh/ignore_warnings.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>
#include <libmesh/restore_warnings.h>

#include <libmesh/mesh.h>
#include <libmesh/mesh_generation.h>
#include <libmesh/mesh_refinement.h>
#include <libmesh/remote_elem.h>
#include <libmesh/replicated_mesh.h>

#include "test_comm.h"

// THE CPPUNIT_TEST_SUITE_END macro expands to code that involves
// std::auto_ptr, which in turn produces -Wdeprecated-declarations
// warnings.  These can be ignored in GCC as long as we wrap the
// offending code in appropriate pragmas.  We can't get away with a
// single ignore_warnings.h inclusion at the beginning of this file,
// since the libmesh headers pull in a restore_warnings.h at some
// point.  We also don't bother restoring warnings at the end of this
// file since it's not a header.
#include <libmesh/ignore_warnings.h>

using namespace libMesh;

class BoundaryMeshTest : public CppUnit::TestCase {
  /**
   * The goal of this test is to ensure that a 2D mesh generates
   * boundary meshes correctly.
   */
public:
  CPPUNIT_TEST_SUITE( BoundaryMeshTest );

  CPPUNIT_TEST( testMesh );

  CPPUNIT_TEST_SUITE_END();

protected:

  Mesh * _mesh;
  Mesh * _all_boundary_mesh;
  Mesh * _left_boundary_mesh;
  Mesh * _internal_boundary_mesh;

  void build_mesh()
  {
    _mesh = new Mesh(*TestCommWorld);
    _all_boundary_mesh = new Mesh(*TestCommWorld);
    _left_boundary_mesh = new Mesh(*TestCommWorld);
    _internal_boundary_mesh = new Mesh(*TestCommWorld);

    MeshTools::Generation::build_square(*_mesh, 3, 5,
                                        0.1, 0.9, 0.1, 0.9, QUAD9);

    // We'll need to skip repartitioning with DistributedMesh for now;
    // otherwise the boundary meshes' interior parents might get
    // shuffled off to different processors.
    if (!_mesh->is_serial())
      {
        _mesh->skip_partitioning(true);
        _left_boundary_mesh->skip_partitioning(true);
        _all_boundary_mesh->skip_partitioning(true);
        _internal_boundary_mesh->skip_partitioning(true);
      }

    // Set subdomain ids for specfic elements. This allows us to later
    // build an internal sideset with respect to a given
    // subdomain. The element subdomains look like:
    // ___________________
    // |  2  |  2  |  2  |
    // |_____|_____|_____|
    // |  2  |  2  |  2  |
    // |_____|_____|_____|
    // |  2  |  2  |  2  |
    // |_____|_____|_____|
    // |  1  |  1  |  2  |
    // |_____|_____|_____|
    // |  1  |  1  |  2  |
    // |_____|_____|_____|
    //
    // and we will create an internal sideset along the border between
    // subdomains 1 and 2.
    std::vector<subdomain_id_type> sbd_id_list(15);
    sbd_id_list[0] = 1;
    sbd_id_list[1] = 1;
    sbd_id_list[2] = 2;
    sbd_id_list[3] = 1;
    sbd_id_list[4] = 1;
    sbd_id_list[5] = 2;
    sbd_id_list[6] = 2;
    sbd_id_list[7] = 2;
    sbd_id_list[8] = 2;
    sbd_id_list[9] = 2;
    sbd_id_list[10] = 2;
    sbd_id_list[11] = 2;
    sbd_id_list[12] = 2;
    sbd_id_list[13] = 2;
    sbd_id_list[14] = 2;

    for (unsigned int e=0; e<sbd_id_list.size(); ++e)
      _mesh->elem_ptr(e)->subdomain_id() = sbd_id_list[e];

    // Get the border of the square
    _mesh->get_boundary_info().sync(*_all_boundary_mesh);

    std::set<boundary_id_type> left_id, right_id;
    left_id.insert(3);
    right_id.insert(1);

    // Add the right side of the square to the square; this should
    // make it a mixed dimension mesh
    _mesh->get_boundary_info().add_elements(right_id, *_mesh);
    _mesh->prepare_for_use();

    // Add the left side of the square to its own boundary mesh.
    _mesh->get_boundary_info().sync(left_id, *_left_boundary_mesh);

    // Add an internal sideset to the _mesh.  We pick an ID that does
    // not conflict with sidesets 0-3 that get created by
    // build_square().
    BoundaryInfo & bi = _mesh->get_boundary_info();
    boundary_id_type bid = 5;

    bi.add_side(_mesh->elem_ptr(1), /*side=*/1, bid);
    bi.add_side(_mesh->elem_ptr(4), /*side=*/1, bid);
    bi.add_side(_mesh->elem_ptr(4), /*side=*/2, bid);
    bi.add_side(_mesh->elem_ptr(3), /*side=*/2, bid);

    // To test the "relative to" feature, add the same sides to the
    // same sideset, but from elements in subdomain 2 instead. These
    // should not show up in the BoundaryMesh, i.e. there should not
    // be overlapped elems in the BoundaryMesh.
    bi.add_side(_mesh->elem_ptr(2), /*side=*/3, bid);
    bi.add_side(_mesh->elem_ptr(5), /*side=*/3, bid);
    bi.add_side(_mesh->elem_ptr(7), /*side=*/0, bid);
    bi.add_side(_mesh->elem_ptr(6), /*side=*/0, bid);

    // Create a BoundaryMesh from the internal sideset relative to subdomain 1.
    {
      std::set<boundary_id_type> requested_boundary_ids;
      requested_boundary_ids.insert(bid);
      std::set<subdomain_id_type> subdomains_relative_to;
      subdomains_relative_to.insert(1);
      _mesh->get_boundary_info().sync(requested_boundary_ids,
                                      *_internal_boundary_mesh,
                                      subdomains_relative_to);
    }
  }

public:
  void setUp()
  {
    this->build_mesh();
  }

  void tearDown()
  {
    delete _internal_boundary_mesh;
    delete _all_boundary_mesh;
    delete _left_boundary_mesh;
    delete _mesh;
  }

  void testMesh()
  {
    // There'd better be 3*5 + 5 elements in the interior plus right
    // boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(20),
                         _mesh->n_elem());

    // There'd better be only 7*11 nodes in the interior
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(77),
                         _mesh->n_nodes());

    // There'd better be only 2*(3+5) elements on the full boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(16),
                         _all_boundary_mesh->n_elem());

    // There'd better be only 2*2*(3+5) nodes on the full boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(32),
                         _all_boundary_mesh->n_nodes());

    // There'd better be only 5 elements on the left boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(5),
                         _left_boundary_mesh->n_elem());

    // There'd better be only 2*5+1 nodes on the left boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(11),
                         _left_boundary_mesh->n_nodes());

    // There are only four elements in the internal sideset mesh.
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(4),
                         _internal_boundary_mesh->n_elem());

    // There are 2*n_elem + 1 nodes in the internal sideset mesh.
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(9),
                         _internal_boundary_mesh->n_nodes());

    this->sanityCheck();
  }

  void sanityCheck()
  {
    // Sanity check all the elements
    MeshBase::const_element_iterator elem_it =
      _mesh->active_elements_begin();
    const MeshBase::const_element_iterator elem_end =
      _mesh->active_elements_end();
    for (; elem_it != elem_end; ++elem_it)
      {
        const Elem * elem = *elem_it;

        const Elem * pip = elem->interior_parent();

        // On a DistributedMesh we might not be able to see the
        // interior_parent of a non-local element
        if (pip == remote_elem)
          {
            CPPUNIT_ASSERT(elem->processor_id() != TestCommWorld->rank());
            continue;
          }

        // All the edges should have interior parents; none of the
        // quads should.
        if (pip)
          {
            CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);
            CPPUNIT_ASSERT_EQUAL(pip->type(), QUAD9);
            CPPUNIT_ASSERT_EQUAL(pip->level(), elem->level());

            // We only added right edges
            CPPUNIT_ASSERT_DOUBLES_EQUAL(elem->centroid()(0), 0.9,
                                         TOLERANCE*TOLERANCE);
          }
        else
          {
            CPPUNIT_ASSERT_EQUAL(elem->type(), QUAD9);
          }
      }

    MeshBase::const_element_iterator left_bdy_elem_it =
      _left_boundary_mesh->active_elements_begin();
    const MeshBase::const_element_iterator left_bdy_elem_end =
      _left_boundary_mesh->active_elements_end();
    for (; left_bdy_elem_it != left_bdy_elem_end; ++left_bdy_elem_it)
      {
        const Elem * elem = *left_bdy_elem_it;

        CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);

        const Elem * pip = elem->interior_parent();

        // On a DistributedMesh we might not be able to see the
        // interior_parent of a non-local element
        if (pip == remote_elem)
          {
            CPPUNIT_ASSERT(elem->processor_id() != TestCommWorld->rank());
            continue;
          }

        // All the edges should have interior parents
        CPPUNIT_ASSERT(pip);
        CPPUNIT_ASSERT_EQUAL(pip->type(), QUAD9);
        CPPUNIT_ASSERT_EQUAL(pip->level(), elem->level());

        // We only added left edges
        CPPUNIT_ASSERT_DOUBLES_EQUAL(elem->centroid()(0), 0.1,
                                     TOLERANCE*TOLERANCE);
      }

    MeshBase::const_element_iterator all_bdy_elem_it =
      _left_boundary_mesh->active_elements_begin();
    const MeshBase::const_element_iterator all_bdy_elem_end =
      _left_boundary_mesh->active_elements_end();
    for (; all_bdy_elem_it != all_bdy_elem_end; ++all_bdy_elem_it)
      {
        const Elem * elem = *all_bdy_elem_it;

        CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);

        const Elem * pip = elem->interior_parent();

        // On a DistributedMesh we might not be able to see the
        // interior_parent of a non-local element
        if (pip == remote_elem)
          {
            CPPUNIT_ASSERT(elem->processor_id() != TestCommWorld->rank());
            continue;
          }

        // All the edges should have interior parents
        CPPUNIT_ASSERT(pip);
        CPPUNIT_ASSERT_EQUAL(pip->type(), QUAD9);
        CPPUNIT_ASSERT_EQUAL(pip->level(), elem->level());
      }

    // Sanity check for the internal sideset mesh.
    MeshBase::const_element_iterator
      internal_elem_it = _internal_boundary_mesh->active_elements_begin(),
      internal_elem_end = _internal_boundary_mesh->active_elements_end();

    for (; internal_elem_it != internal_elem_end; ++internal_elem_it)
      {
        const Elem * elem = *internal_elem_it;

        CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);

        // All of the elements in the internal sideset mesh should
        // have the same subdomain id as the parent Elems (i.e. 1)
        // they came from.
        CPPUNIT_ASSERT_EQUAL(static_cast<subdomain_id_type>(1),
                             elem->subdomain_id());
      }
  }

};

CPPUNIT_TEST_SUITE_REGISTRATION( BoundaryMeshTest );

#ifdef LIBMESH_ENABLE_AMR

class BoundaryRefinedMeshTest : public BoundaryMeshTest {
  /**
   * The goal of this test is the same as the previous, but now we do a
   * uniform refinement and make sure the result mesh is consistent. i.e.
   * the new node shared between the 1D elements is the same as the node
   * shared on the underlying quads, and so on.
   */
public:
  CPPUNIT_TEST_SUITE( BoundaryRefinedMeshTest );

  CPPUNIT_TEST( testMesh );

  CPPUNIT_TEST_SUITE_END();

  // Yes, this is necessary. Somewhere in those macros is a protected/private
public:

  void setUp()
  {
    this->build_mesh();

    // Need to refine interior mesh before separate boundary meshes,
    // if we want to get interior_parent links right.
    MeshRefinement(*_mesh).uniformly_refine(1);
    MeshRefinement(*_left_boundary_mesh).uniformly_refine(1);
    MeshRefinement(*_all_boundary_mesh).uniformly_refine(1);
  }

  void testMesh()
  {
    // There'd better be 3*5*4 + 5*2 active elements in the interior
    // plus right boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(70),
                         _mesh->n_active_elem());

    // Plus the original 20 now-inactive elements
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(90),
                         _mesh->n_elem());

    // There'd better be only 13*21 nodes in the interior
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(273),
                         _mesh->n_nodes());

    // There'd better be only 2*2*(3+5) active elements on the full boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(32),
                         _all_boundary_mesh->n_active_elem());

    // Plus the original 16 now-inactive elements
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(48),
                         _all_boundary_mesh->n_elem());

    // There'd better be only 2*2*2*(3+5) nodes on the full boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(64),
                         _all_boundary_mesh->n_nodes());

    // There'd better be only 2*5 active elements on the left boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(10),
                         _left_boundary_mesh->n_active_elem());

    // Plus the original 5 now-inactive elements
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(15),
                         _left_boundary_mesh->n_elem());

    // There'd better be only 2*2*5+1 nodes on the left boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(21),
                         _left_boundary_mesh->n_nodes());

    this->sanityCheck();
  }

};

CPPUNIT_TEST_SUITE_REGISTRATION( BoundaryRefinedMeshTest );

#endif
<commit_msg>Unit test: Use smart pointers to manage memory.<commit_after>// Ignore unused parameter warnings coming from cppunit headers
#include <libmesh/ignore_warnings.h>
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/TestCase.h>
#include <libmesh/restore_warnings.h>

#include <libmesh/mesh.h>
#include <libmesh/mesh_generation.h>
#include <libmesh/mesh_refinement.h>
#include <libmesh/remote_elem.h>
#include <libmesh/replicated_mesh.h>

#include "test_comm.h"

// THE CPPUNIT_TEST_SUITE_END macro expands to code that involves
// std::auto_ptr, which in turn produces -Wdeprecated-declarations
// warnings.  These can be ignored in GCC as long as we wrap the
// offending code in appropriate pragmas.  We can't get away with a
// single ignore_warnings.h inclusion at the beginning of this file,
// since the libmesh headers pull in a restore_warnings.h at some
// point.  We also don't bother restoring warnings at the end of this
// file since it's not a header.
#include <libmesh/ignore_warnings.h>

using namespace libMesh;

class BoundaryMeshTest : public CppUnit::TestCase {
  /**
   * The goal of this test is to ensure that a 2D mesh generates
   * boundary meshes correctly.
   */
public:
  CPPUNIT_TEST_SUITE( BoundaryMeshTest );

  CPPUNIT_TEST( testMesh );

  CPPUNIT_TEST_SUITE_END();

protected:

  UniquePtr<Mesh> _mesh;
  UniquePtr<Mesh> _all_boundary_mesh;
  UniquePtr<Mesh> _left_boundary_mesh;
  UniquePtr<Mesh> _internal_boundary_mesh;

  void build_mesh()
  {
    _mesh.reset(new Mesh(*TestCommWorld));
    _all_boundary_mesh.reset(new Mesh(*TestCommWorld));
    _left_boundary_mesh.reset(new Mesh(*TestCommWorld));
    _internal_boundary_mesh.reset(new Mesh(*TestCommWorld));

    MeshTools::Generation::build_square(*_mesh, 3, 5,
                                        0.1, 0.9, 0.1, 0.9, QUAD9);

    // We'll need to skip repartitioning with DistributedMesh for now;
    // otherwise the boundary meshes' interior parents might get
    // shuffled off to different processors.
    if (!_mesh->is_serial())
      {
        _mesh->skip_partitioning(true);
        _left_boundary_mesh->skip_partitioning(true);
        _all_boundary_mesh->skip_partitioning(true);
        _internal_boundary_mesh->skip_partitioning(true);
      }

    // Set subdomain ids for specfic elements. This allows us to later
    // build an internal sideset with respect to a given
    // subdomain. The element subdomains look like:
    // ___________________
    // |  2  |  2  |  2  |
    // |_____|_____|_____|
    // |  2  |  2  |  2  |
    // |_____|_____|_____|
    // |  2  |  2  |  2  |
    // |_____|_____|_____|
    // |  1  |  1  |  2  |
    // |_____|_____|_____|
    // |  1  |  1  |  2  |
    // |_____|_____|_____|
    //
    // and we will create an internal sideset along the border between
    // subdomains 1 and 2.
    std::vector<subdomain_id_type> sbd_id_list(15);
    sbd_id_list[0] = 1;
    sbd_id_list[1] = 1;
    sbd_id_list[2] = 2;
    sbd_id_list[3] = 1;
    sbd_id_list[4] = 1;
    sbd_id_list[5] = 2;
    sbd_id_list[6] = 2;
    sbd_id_list[7] = 2;
    sbd_id_list[8] = 2;
    sbd_id_list[9] = 2;
    sbd_id_list[10] = 2;
    sbd_id_list[11] = 2;
    sbd_id_list[12] = 2;
    sbd_id_list[13] = 2;
    sbd_id_list[14] = 2;

    for (unsigned int e=0; e<sbd_id_list.size(); ++e)
      _mesh->elem_ptr(e)->subdomain_id() = sbd_id_list[e];

    // Get the border of the square
    _mesh->get_boundary_info().sync(*_all_boundary_mesh);

    std::set<boundary_id_type> left_id, right_id;
    left_id.insert(3);
    right_id.insert(1);

    // Add the right side of the square to the square; this should
    // make it a mixed dimension mesh
    _mesh->get_boundary_info().add_elements(right_id, *_mesh);
    _mesh->prepare_for_use();

    // Add the left side of the square to its own boundary mesh.
    _mesh->get_boundary_info().sync(left_id, *_left_boundary_mesh);

    // Add an internal sideset to the _mesh.  We pick an ID that does
    // not conflict with sidesets 0-3 that get created by
    // build_square().
    BoundaryInfo & bi = _mesh->get_boundary_info();
    boundary_id_type bid = 5;

    bi.add_side(_mesh->elem_ptr(1), /*side=*/1, bid);
    bi.add_side(_mesh->elem_ptr(4), /*side=*/1, bid);
    bi.add_side(_mesh->elem_ptr(4), /*side=*/2, bid);
    bi.add_side(_mesh->elem_ptr(3), /*side=*/2, bid);

    // To test the "relative to" feature, add the same sides to the
    // same sideset, but from elements in subdomain 2 instead. These
    // should not show up in the BoundaryMesh, i.e. there should not
    // be overlapped elems in the BoundaryMesh.
    bi.add_side(_mesh->elem_ptr(2), /*side=*/3, bid);
    bi.add_side(_mesh->elem_ptr(5), /*side=*/3, bid);
    bi.add_side(_mesh->elem_ptr(7), /*side=*/0, bid);
    bi.add_side(_mesh->elem_ptr(6), /*side=*/0, bid);

    // Create a BoundaryMesh from the internal sideset relative to subdomain 1.
    {
      std::set<boundary_id_type> requested_boundary_ids;
      requested_boundary_ids.insert(bid);
      std::set<subdomain_id_type> subdomains_relative_to;
      subdomains_relative_to.insert(1);
      _mesh->get_boundary_info().sync(requested_boundary_ids,
                                      *_internal_boundary_mesh,
                                      subdomains_relative_to);
    }
  }

public:
  void setUp()
  {
    this->build_mesh();
  }

  void testMesh()
  {
    // There'd better be 3*5 + 5 elements in the interior plus right
    // boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(20),
                         _mesh->n_elem());

    // There'd better be only 7*11 nodes in the interior
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(77),
                         _mesh->n_nodes());

    // There'd better be only 2*(3+5) elements on the full boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(16),
                         _all_boundary_mesh->n_elem());

    // There'd better be only 2*2*(3+5) nodes on the full boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(32),
                         _all_boundary_mesh->n_nodes());

    // There'd better be only 5 elements on the left boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(5),
                         _left_boundary_mesh->n_elem());

    // There'd better be only 2*5+1 nodes on the left boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(11),
                         _left_boundary_mesh->n_nodes());

    // There are only four elements in the internal sideset mesh.
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(4),
                         _internal_boundary_mesh->n_elem());

    // There are 2*n_elem + 1 nodes in the internal sideset mesh.
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(9),
                         _internal_boundary_mesh->n_nodes());

    this->sanityCheck();
  }

  void sanityCheck()
  {
    // Sanity check all the elements
    MeshBase::const_element_iterator elem_it =
      _mesh->active_elements_begin();
    const MeshBase::const_element_iterator elem_end =
      _mesh->active_elements_end();
    for (; elem_it != elem_end; ++elem_it)
      {
        const Elem * elem = *elem_it;

        const Elem * pip = elem->interior_parent();

        // On a DistributedMesh we might not be able to see the
        // interior_parent of a non-local element
        if (pip == remote_elem)
          {
            CPPUNIT_ASSERT(elem->processor_id() != TestCommWorld->rank());
            continue;
          }

        // All the edges should have interior parents; none of the
        // quads should.
        if (pip)
          {
            CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);
            CPPUNIT_ASSERT_EQUAL(pip->type(), QUAD9);
            CPPUNIT_ASSERT_EQUAL(pip->level(), elem->level());

            // We only added right edges
            CPPUNIT_ASSERT_DOUBLES_EQUAL(elem->centroid()(0), 0.9,
                                         TOLERANCE*TOLERANCE);
          }
        else
          {
            CPPUNIT_ASSERT_EQUAL(elem->type(), QUAD9);
          }
      }

    MeshBase::const_element_iterator left_bdy_elem_it =
      _left_boundary_mesh->active_elements_begin();
    const MeshBase::const_element_iterator left_bdy_elem_end =
      _left_boundary_mesh->active_elements_end();
    for (; left_bdy_elem_it != left_bdy_elem_end; ++left_bdy_elem_it)
      {
        const Elem * elem = *left_bdy_elem_it;

        CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);

        const Elem * pip = elem->interior_parent();

        // On a DistributedMesh we might not be able to see the
        // interior_parent of a non-local element
        if (pip == remote_elem)
          {
            CPPUNIT_ASSERT(elem->processor_id() != TestCommWorld->rank());
            continue;
          }

        // All the edges should have interior parents
        CPPUNIT_ASSERT(pip);
        CPPUNIT_ASSERT_EQUAL(pip->type(), QUAD9);
        CPPUNIT_ASSERT_EQUAL(pip->level(), elem->level());

        // We only added left edges
        CPPUNIT_ASSERT_DOUBLES_EQUAL(elem->centroid()(0), 0.1,
                                     TOLERANCE*TOLERANCE);
      }

    MeshBase::const_element_iterator all_bdy_elem_it =
      _left_boundary_mesh->active_elements_begin();
    const MeshBase::const_element_iterator all_bdy_elem_end =
      _left_boundary_mesh->active_elements_end();
    for (; all_bdy_elem_it != all_bdy_elem_end; ++all_bdy_elem_it)
      {
        const Elem * elem = *all_bdy_elem_it;

        CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);

        const Elem * pip = elem->interior_parent();

        // On a DistributedMesh we might not be able to see the
        // interior_parent of a non-local element
        if (pip == remote_elem)
          {
            CPPUNIT_ASSERT(elem->processor_id() != TestCommWorld->rank());
            continue;
          }

        // All the edges should have interior parents
        CPPUNIT_ASSERT(pip);
        CPPUNIT_ASSERT_EQUAL(pip->type(), QUAD9);
        CPPUNIT_ASSERT_EQUAL(pip->level(), elem->level());
      }

    // Sanity check for the internal sideset mesh.
    MeshBase::const_element_iterator
      internal_elem_it = _internal_boundary_mesh->active_elements_begin(),
      internal_elem_end = _internal_boundary_mesh->active_elements_end();

    for (; internal_elem_it != internal_elem_end; ++internal_elem_it)
      {
        const Elem * elem = *internal_elem_it;

        CPPUNIT_ASSERT_EQUAL(elem->type(), EDGE3);

        // All of the elements in the internal sideset mesh should
        // have the same subdomain id as the parent Elems (i.e. 1)
        // they came from.
        CPPUNIT_ASSERT_EQUAL(static_cast<subdomain_id_type>(1),
                             elem->subdomain_id());
      }
  }

};

CPPUNIT_TEST_SUITE_REGISTRATION( BoundaryMeshTest );

#ifdef LIBMESH_ENABLE_AMR

class BoundaryRefinedMeshTest : public BoundaryMeshTest {
  /**
   * The goal of this test is the same as the previous, but now we do a
   * uniform refinement and make sure the result mesh is consistent. i.e.
   * the new node shared between the 1D elements is the same as the node
   * shared on the underlying quads, and so on.
   */
public:
  CPPUNIT_TEST_SUITE( BoundaryRefinedMeshTest );

  CPPUNIT_TEST( testMesh );

  CPPUNIT_TEST_SUITE_END();

  // Yes, this is necessary. Somewhere in those macros is a protected/private
public:

  void setUp()
  {
    this->build_mesh();

    // Need to refine interior mesh before separate boundary meshes,
    // if we want to get interior_parent links right.
    MeshRefinement(*_mesh).uniformly_refine(1);
    MeshRefinement(*_left_boundary_mesh).uniformly_refine(1);
    MeshRefinement(*_all_boundary_mesh).uniformly_refine(1);
  }

  void testMesh()
  {
    // There'd better be 3*5*4 + 5*2 active elements in the interior
    // plus right boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(70),
                         _mesh->n_active_elem());

    // Plus the original 20 now-inactive elements
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(90),
                         _mesh->n_elem());

    // There'd better be only 13*21 nodes in the interior
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(273),
                         _mesh->n_nodes());

    // There'd better be only 2*2*(3+5) active elements on the full boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(32),
                         _all_boundary_mesh->n_active_elem());

    // Plus the original 16 now-inactive elements
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(48),
                         _all_boundary_mesh->n_elem());

    // There'd better be only 2*2*2*(3+5) nodes on the full boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(64),
                         _all_boundary_mesh->n_nodes());

    // There'd better be only 2*5 active elements on the left boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(10),
                         _left_boundary_mesh->n_active_elem());

    // Plus the original 5 now-inactive elements
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(15),
                         _left_boundary_mesh->n_elem());

    // There'd better be only 2*2*5+1 nodes on the left boundary
    CPPUNIT_ASSERT_EQUAL(static_cast<dof_id_type>(21),
                         _left_boundary_mesh->n_nodes());

    this->sanityCheck();
  }

};

CPPUNIT_TEST_SUITE_REGISTRATION( BoundaryRefinedMeshTest );

#endif
<|endoftext|>


<commit_before>/**
 * @file Cosa/OWI/Driver.cpp
 * @version 1.0
 *
 * @section License
 * Copyright (C) 2012, Mikael Patel
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 *
 * @section Description
 * 1-Wire device driver support class.
 *
 * This file is part of the Arduino Che Cosa project.
 */

#include "Cosa/OWI.hh"

OWI::Driver::Driver(OWI* pin, const uint8_t* rom) :
  m_pin(pin)
{
  if (rom != 0) {
    for (uint8_t i = 0; i < ROM_MAX; i++)
      m_rom[i] = pgm_read_byte(rom++);
  }
}

int8_t
OWI::Driver::search(int8_t last)
{
  uint8_t pos = 0;
  int8_t next = LAST;
  for (uint8_t i = 0; i < 8; i++) {
    uint8_t data = 0;
    for (uint8_t j = 0; j < 8; j++) {
      data >>= 1;
      switch (m_pin->read(2)) {
      case 0b00: // Discrepency between device roms
	if (pos == last) {
	  m_pin->write(1, 1);
	  data |= 0x80;
	  last = FIRST;
	}
	else if (pos > last) {
	  m_pin->write(0, 1);
	  next = pos;
	} else if (m_rom[i] & (1 << j)) {
	  m_pin->write(1, 1);
	  data |= 0x80;
	}
	else {
	  m_pin->write(0, 1);
	}
	break;
      case 0b01: // Only one's at this position
	m_pin->write(1, 1);
	data |= 0x80;
	break;
      case 0b10: // Only zero's at this position
	m_pin->write(0, 1);
	break;
      case 0b11: // No device detected
	return (ERROR);
      }
      pos += 1;
    }
    m_rom[i] = data;
  }
  return (next);
}

int8_t
OWI::Driver::search_rom(int8_t last)
{
  if (!m_pin->reset()) return (ERROR);
  m_pin->write(OWI::SEARCH_ROM);
  return (search(last));
}

bool
OWI::Driver::read_rom()
{
  if (!m_pin->reset()) return (0);
  m_pin->write(OWI::READ_ROM);
  m_pin->begin();
  for (uint8_t i = 0; i < ROM_MAX; i++) {
    m_rom[i] = m_pin->read();
  }
  return (m_pin->end() == 0);
}

bool
OWI::Driver::match_rom()
{
  if (!m_pin->reset()) return (0);
  m_pin->write(OWI::MATCH_ROM);
  for (uint8_t i = 0; i < ROM_MAX; i++) {
    m_pin->write(m_rom[i]);
  }
  return (1);
}

bool
OWI::Driver::skip_rom()
{
  if (!m_pin->reset()) return (0);
  m_pin->write(OWI::SKIP_ROM);
  return (1);
}

int8_t
OWI::Driver::alarm_search(int8_t last)
{
  if (!m_pin->reset()) return (ERROR);
  m_pin->write(OWI::ALARM_SEARCH);
  return (search(last));
}

void
OWI::Driver::print_rom(IOStream& stream)
{
  uint8_t i;
  stream.printf_P(PSTR("OWI::rom(family = %hd, id = "), m_rom[0]);
  for (i = 1; i < ROM_MAX - 1; i++)
    stream.printf_P(PSTR("%hd, "), m_rom[i]);
  stream.printf_P(PSTR("crc = %hd)\n"), m_rom[i]);
}

bool
OWI::Driver::connect(uint8_t family, uint8_t index)
{
  int8_t last = FIRST;
  do {
    last = search_rom(last);
    if (last == ERROR) return (0);
    if (m_rom[0] == family) {
      if (index == 0) return (1);
      index -= 1;
    }
  } while (last != LAST);
  for (uint8_t i = 1; i < ROM_MAX; i++) m_rom[i] = 0;
  return (0);
}
<commit_msg>Minor optimization of program memory constant copy.<commit_after>/**
 * @file Cosa/OWI/Driver.cpp
 * @version 1.0
 *
 * @section License
 * Copyright (C) 2012, Mikael Patel
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 *
 * @section Description
 * 1-Wire device driver support class.
 *
 * This file is part of the Arduino Che Cosa project.
 */

#include "Cosa/OWI.hh"

OWI::Driver::Driver(OWI* pin, const uint8_t* rom) :
  m_pin(pin)
{
  if (rom != 0) memcpy_P(m_rom, rom, ROM_MAX);
}

int8_t
OWI::Driver::search(int8_t last)
{
  uint8_t pos = 0;
  int8_t next = LAST;
  for (uint8_t i = 0; i < 8; i++) {
    uint8_t data = 0;
    for (uint8_t j = 0; j < 8; j++) {
      data >>= 1;
      switch (m_pin->read(2)) {
      case 0b00: // Discrepency between device roms
	if (pos == last) {
	  m_pin->write(1, 1);
	  data |= 0x80;
	  last = FIRST;
	}
	else if (pos > last) {
	  m_pin->write(0, 1);
	  next = pos;
	} else if (m_rom[i] & (1 << j)) {
	  m_pin->write(1, 1);
	  data |= 0x80;
	}
	else {
	  m_pin->write(0, 1);
	}
	break;
      case 0b01: // Only one's at this position
	m_pin->write(1, 1);
	data |= 0x80;
	break;
      case 0b10: // Only zero's at this position
	m_pin->write(0, 1);
	break;
      case 0b11: // No device detected
	return (ERROR);
      }
      pos += 1;
    }
    m_rom[i] = data;
  }
  return (next);
}

int8_t
OWI::Driver::search_rom(int8_t last)
{
  if (!m_pin->reset()) return (ERROR);
  m_pin->write(OWI::SEARCH_ROM);
  return (search(last));
}

bool
OWI::Driver::read_rom()
{
  if (!m_pin->reset()) return (0);
  m_pin->write(OWI::READ_ROM);
  m_pin->begin();
  for (uint8_t i = 0; i < ROM_MAX; i++) {
    m_rom[i] = m_pin->read();
  }
  return (m_pin->end() == 0);
}

bool
OWI::Driver::match_rom()
{
  if (!m_pin->reset()) return (0);
  m_pin->write(OWI::MATCH_ROM);
  for (uint8_t i = 0; i < ROM_MAX; i++) {
    m_pin->write(m_rom[i]);
  }
  return (1);
}

bool
OWI::Driver::skip_rom()
{
  if (!m_pin->reset()) return (0);
  m_pin->write(OWI::SKIP_ROM);
  return (1);
}

int8_t
OWI::Driver::alarm_search(int8_t last)
{
  if (!m_pin->reset()) return (ERROR);
  m_pin->write(OWI::ALARM_SEARCH);
  return (search(last));
}

void
OWI::Driver::print_rom(IOStream& stream)
{
  uint8_t i;
  stream.printf_P(PSTR("OWI::rom(family = %hd, id = "), m_rom[0]);
  for (i = 1; i < ROM_MAX - 1; i++)
    stream.printf_P(PSTR("%hd, "), m_rom[i]);
  stream.printf_P(PSTR("crc = %hd)\n"), m_rom[i]);
}

bool
OWI::Driver::connect(uint8_t family, uint8_t index)
{
  int8_t last = FIRST;
  do {
    last = search_rom(last);
    if (last == ERROR) return (0);
    if (m_rom[0] == family) {
      if (index == 0) return (1);
      index -= 1;
    }
  } while (last != LAST);
  memset(m_rom, 0, ROM_MAX);
  return (0);
}
<|endoftext|>


<commit_before>#pragma once

#include "../guia1/multicapa.cpp"

using namespace std;
using namespace arma;

namespace ic {

enum class tipoInicializacion {
	conjuntosAleatorios,
	valoresAlAzar
};

vec asignarPatrones(const mat& patrones,
                    vector<rowvec> centroides)
{
	const int nPatrones = patrones.n_rows;
	const int nConjuntos = centroides.size();
	vec tablaPatronConjunto;
	tablaPatronConjunto.set_size(nPatrones);

	for (int i = 0; i < nPatrones; ++i) {
		vec distancias;
		distancias.set_size(nConjuntos);

		for (int j = 0; j < nConjuntos; ++j) {
			distancias(j) = norm(patrones.row(i) - centroides[j]);
		}

		const int conjuntoGanador = distancias.index_min();
		tablaPatronConjunto(i) = conjuntoGanador;
	}

	return tablaPatronConjunto;
}

// patrones es una matriz que tiene un patrón en cada fila.
// Cada columna representa una dimensión.

// La función devuelve un pair.
// pair.first es un vector de STL que contiene los centroides de cada conjunto.
// pair.second es un vec de Armadillo que contiene el sigma de cada conjunto.
pair<vector<rowvec>, vec>
entrenarRadialPorLotes(const mat& patrones,
                       int nConjuntos,
                       tipoInicializacion tipo = tipoInicializacion::conjuntosAleatorios)
{
	const int nPatrones = patrones.n_rows;

	vec tablaPatronConjunto;
	tablaPatronConjunto.set_size(nPatrones);
	vector<rowvec> centroides;
	centroides.resize(nConjuntos);

	// 1. Inicialización
	switch (tipo) {
	case tipoInicializacion::conjuntosAleatorios: {
		// Se forman los k conjuntos con patrones elegidos aleatoriamente.
		// Se asigna la misma cantidad de patrones a cada conjunto.
		const uvec indicesMezclados = shuffle(linspace<uvec>(0, nPatrones - 1, nPatrones));
		int conjunto = 0;

		for (int i = 0; i < nPatrones; ++i) {
			// Los patrones no asignados se agregan a cada conjunto,
			// en round-robin.
			tablaPatronConjunto(indicesMezclados(i)) = conjunto;

			if (conjunto + 1 < nConjuntos)
				++conjunto;
			else
				conjunto = 0;
		}

		break;
	}

	case tipoInicializacion::valoresAlAzar: {
		// Se le asignan valores aleatorios a los centroides.
		// Los valores van a estar en el rango [-0.5; 0.5]
		for (rowvec& centroide : centroides) {
			centroide = randu<rowvec>(patrones.n_cols) - 0.5;
		}

		// Asignar los patrones al conjunto que tiene el centroide mas cercano
		tablaPatronConjunto = asignarPatrones(patrones, centroides);

		break;
	}
	}

	// Bucle del k-medias
	while (true) {
		// 2. Calcular los centroides de cada conjunto
		for (int i = 0; i < nConjuntos; ++i) {
			// Se extraen los indices de los patrones correspondientes al conjunto i
			const uvec indicesConjunto = find(tablaPatronConjunto == i);

			if (!indicesConjunto.empty()) {
				// Se calcula el centroide a lo largo de los patrones del conjunto.
				const rowvec centroide = mean(patrones.rows(indicesConjunto));
				centroides[i] = centroide;
			}
		}

		// 3. Asignar los patrones al conjunto que tiene el centroide mas cercano
		const vec nuevaTabla = asignarPatrones(patrones, centroides);

		// Detectar si hubo reasignaciones de patrones a conjuntos.
		// Si no hubo reasignaciones, terminamos.
		if (any(tablaPatronConjunto != nuevaTabla))
			tablaPatronConjunto = nuevaTabla;
		else
			break;
	}

	// Terminó el bucle de los k-medias.
	// Ahora hay que calcular el sigma dentro de cada conjunto.
	vec sigmas;
	sigmas.set_size(nConjuntos);

	for (int i = 0; i < nConjuntos; ++i) {
		// Se extraen los indices de los patrones correspondientes al conjunto i
		const uvec indicesConjunto = find(tablaPatronConjunto == i);

		// Se calcula el desvio a lo largo de los patrones del conjunto,
		// a lo largo de las diferentes dimensiones, y luego se saca
		// el promedio de estos desvíos.
		if (!indicesConjunto.empty()) {
			const rowvec aux = stddev(patrones.rows(indicesConjunto));
			const double sigma = mean(aux);
			sigmas[i] = sigma;
		}
	}

	// Solo vamos a devolver los centroides y sigmas para los que el conjunto
	// correspondiente no está vacío.
	vector<rowvec> centroidesFinales;
	vec sigmasFinales;

	for (int i = 0; i < nConjuntos; ++i) {
		const uvec indicesConjunto = find(tablaPatronConjunto == i);

		if (!indicesConjunto.empty()) {
			centroidesFinales.push_back(centroides[i]);
			sigmasFinales.insert_rows(sigmasFinales.n_elem, vec{sigmas(i)});
		}
	}

	return {centroidesFinales, sigmasFinales};
}

struct ParametrosRBF {
	EstructuraCapasRed estructuraRed;
	int nEpocas;
	double tasaAprendizaje;
	double inercia;
	double toleranciaError;
};

double gaussiana(const rowvec& patron,
                 rowvec centroide,
                 double sigma)
{
	// TODO: Corroborar si hay que elevar la distancia al cuadrado
	return exp(-pow(norm(patron - centroide), 2) / (2 * sigma * sigma));
}

rowvec salidaRadial(const rowvec& patron,
                    vector<rowvec> centroides,
                    vec sigmas)
{
	rowvec salida;
	salida.set_size(centroides.size());

	for (unsigned int i = 0; i < centroides.size(); ++i) {
		salida(i) = gaussiana(patron, centroides[i], sigmas(i));
	}

	return salida;
}

} // namespace ic

istream& operator>>(istream& is, ic::ParametrosRBF& parametros)
{
	// Formato:
	// estructura: [3 2 1]
	// n_epocas: 200
	// tasa_entrenamiento: 0.1
	// inercia: 0.5
	// parametro_sigmoidea: 1
	// tolerancia_error: 5
	string str;

	// No chequeamos si la etiqueta de cada línea está bien o no. No nos importa
	is >> str >> parametros.estructuraRed
	    >> str >> parametros.nEpocas
	    >> str >> parametros.tasaAprendizaje
	    >> str >> parametros.inercia
	    >> str >> parametros.toleranciaError;

	// Control básico de valores de parámetros
	if (parametros.estructuraRed.size() != 2
	    || parametros.nEpocas <= 0
	    || parametros.tasaAprendizaje <= 0
	    || parametros.toleranciaError <= 0
	    || parametros.toleranciaError >= 100)
		is.clear(ios::failbit);

	return is;
}
<commit_msg>Guia 1 - Radial por lotes: Agregar FIXME<commit_after>#pragma once

#include "../guia1/multicapa.cpp"

using namespace std;
using namespace arma;

namespace ic {

enum class tipoInicializacion {
	conjuntosAleatorios,
    valoresAlAzar //FIXME: Esto es una cagada. Usar patrones al azar
};

vec asignarPatrones(const mat& patrones,
                    vector<rowvec> centroides)
{
	const int nPatrones = patrones.n_rows;
	const int nConjuntos = centroides.size();
	vec tablaPatronConjunto;
	tablaPatronConjunto.set_size(nPatrones);

	for (int i = 0; i < nPatrones; ++i) {
		vec distancias;
		distancias.set_size(nConjuntos);

		for (int j = 0; j < nConjuntos; ++j) {
			distancias(j) = norm(patrones.row(i) - centroides[j]);
		}

		const int conjuntoGanador = distancias.index_min();
		tablaPatronConjunto(i) = conjuntoGanador;
	}

	return tablaPatronConjunto;
}

// patrones es una matriz que tiene un patrón en cada fila.
// Cada columna representa una dimensión.

// La función devuelve un pair.
// pair.first es un vector de STL que contiene los centroides de cada conjunto.
// pair.second es un vec de Armadillo que contiene el sigma de cada conjunto.
pair<vector<rowvec>, vec>
entrenarRadialPorLotes(const mat& patrones,
                       int nConjuntos,
                       tipoInicializacion tipo = tipoInicializacion::conjuntosAleatorios)
{
	const int nPatrones = patrones.n_rows;

	vec tablaPatronConjunto;
	tablaPatronConjunto.set_size(nPatrones);
	vector<rowvec> centroides;
	centroides.resize(nConjuntos);

	// 1. Inicialización
	switch (tipo) {
	case tipoInicializacion::conjuntosAleatorios: {
		// Se forman los k conjuntos con patrones elegidos aleatoriamente.
		// Se asigna la misma cantidad de patrones a cada conjunto.
		const uvec indicesMezclados = shuffle(linspace<uvec>(0, nPatrones - 1, nPatrones));
		int conjunto = 0;

		for (int i = 0; i < nPatrones; ++i) {
			// Los patrones no asignados se agregan a cada conjunto,
			// en round-robin.
			tablaPatronConjunto(indicesMezclados(i)) = conjunto;

			if (conjunto + 1 < nConjuntos)
				++conjunto;
			else
				conjunto = 0;
		}

		break;
	}

	case tipoInicializacion::valoresAlAzar: {
		// Se le asignan valores aleatorios a los centroides.
		// Los valores van a estar en el rango [-0.5; 0.5]
		for (rowvec& centroide : centroides) {
			centroide = randu<rowvec>(patrones.n_cols) - 0.5;
		}

		// Asignar los patrones al conjunto que tiene el centroide mas cercano
		tablaPatronConjunto = asignarPatrones(patrones, centroides);

		break;
	}
	}

	// Bucle del k-medias
	while (true) {
		// 2. Calcular los centroides de cada conjunto
		for (int i = 0; i < nConjuntos; ++i) {
			// Se extraen los indices de los patrones correspondientes al conjunto i
			const uvec indicesConjunto = find(tablaPatronConjunto == i);

			if (!indicesConjunto.empty()) {
				// Se calcula el centroide a lo largo de los patrones del conjunto.
				const rowvec centroide = mean(patrones.rows(indicesConjunto));
				centroides[i] = centroide;
			}
		}

		// 3. Asignar los patrones al conjunto que tiene el centroide mas cercano
		const vec nuevaTabla = asignarPatrones(patrones, centroides);

		// Detectar si hubo reasignaciones de patrones a conjuntos.
		// Si no hubo reasignaciones, terminamos.
		if (any(tablaPatronConjunto != nuevaTabla))
			tablaPatronConjunto = nuevaTabla;
		else
			break;
	}

	// Terminó el bucle de los k-medias.
	// Ahora hay que calcular el sigma dentro de cada conjunto.
	vec sigmas;
	sigmas.set_size(nConjuntos);

	for (int i = 0; i < nConjuntos; ++i) {
		// Se extraen los indices de los patrones correspondientes al conjunto i
		const uvec indicesConjunto = find(tablaPatronConjunto == i);

		// Se calcula el desvio a lo largo de los patrones del conjunto,
		// a lo largo de las diferentes dimensiones, y luego se saca
		// el promedio de estos desvíos.
		if (!indicesConjunto.empty()) {
			const rowvec aux = stddev(patrones.rows(indicesConjunto));
			const double sigma = mean(aux);
			sigmas[i] = sigma;
		}
	}

	// Solo vamos a devolver los centroides y sigmas para los que el conjunto
	// correspondiente no está vacío.
	vector<rowvec> centroidesFinales;
	vec sigmasFinales;

	for (int i = 0; i < nConjuntos; ++i) {
		const uvec indicesConjunto = find(tablaPatronConjunto == i);

		if (!indicesConjunto.empty()) {
			centroidesFinales.push_back(centroides[i]);
			sigmasFinales.insert_rows(sigmasFinales.n_elem, vec{sigmas(i)});
		}
	}

	return {centroidesFinales, sigmasFinales};
}

struct ParametrosRBF {
	EstructuraCapasRed estructuraRed;
	int nEpocas;
	double tasaAprendizaje;
	double inercia;
	double toleranciaError;
};

double gaussiana(const rowvec& patron,
                 rowvec centroide,
                 double sigma)
{
	// TODO: Corroborar si hay que elevar la distancia al cuadrado
	return exp(-pow(norm(patron - centroide), 2) / (2 * sigma * sigma));
}

rowvec salidaRadial(const rowvec& patron,
                    vector<rowvec> centroides,
                    vec sigmas)
{
	rowvec salida;
	salida.set_size(centroides.size());

	for (unsigned int i = 0; i < centroides.size(); ++i) {
		salida(i) = gaussiana(patron, centroides[i], sigmas(i));
	}

	return salida;
}

} // namespace ic

istream& operator>>(istream& is, ic::ParametrosRBF& parametros)
{
	// Formato:
	// estructura: [3 2 1]
	// n_epocas: 200
	// tasa_entrenamiento: 0.1
	// inercia: 0.5
	// parametro_sigmoidea: 1
	// tolerancia_error: 5
	string str;

	// No chequeamos si la etiqueta de cada línea está bien o no. No nos importa
	is >> str >> parametros.estructuraRed
	    >> str >> parametros.nEpocas
	    >> str >> parametros.tasaAprendizaje
	    >> str >> parametros.inercia
	    >> str >> parametros.toleranciaError;

	// Control básico de valores de parámetros
	if (parametros.estructuraRed.size() != 2
	    || parametros.nEpocas <= 0
	    || parametros.tasaAprendizaje <= 0
	    || parametros.toleranciaError <= 0
	    || parametros.toleranciaError >= 100)
		is.clear(ios::failbit);

	return is;
}
<|endoftext|>


<commit_before>/*
 * KDots
 * Copyright(c) 2011-2012 Minh Ngo <nlminhtl@gmail.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef KDOTS_POINT_HPP
#define KDOTS_POINT_HPP
#include <QMetaType>
#include "constants.hpp"

namespace KDots
{
  class Point
  {
    int m_x, m_y;
  public:
    Point()
      : m_x(-1)
      , m_y(-1)
    {}

    bool isInitialized() const
    {
      return m_x >= 0 && m_y >= 0;
    }

    Point(int x, int y)
      : m_x(x)
      , m_y(y)
    {
    }

    int x() const
    {
      return m_x;
    }

    int y() const
    {
      return m_y;
    }

    void setX(int x)
    {
      m_x = x;
    }

    void setY(int y)
    {
      m_y = y;
    }

    Point operator*(int val) const
    {
      return Point(m_x * val, m_y * val);
    }

    Point operator+(int val) const
    {
      return Point(m_x + val, m_y + val);
    }

    Point operator-(const Point& point) const
    {
      return Point(m_x + point.m_x, m_y + point.m_y);
    }

    Point operator+(const Point& point) const
    {
      return Point(m_x - point.m_x, m_y - point.m_y);
    }

    bool operator<(const Point& other) const
    {
      return m_x < other.m_x && m_y < other.m_y;
    }

    bool operator<=(const Point& other) const
    {
      return m_x <= other.m_x && m_y <= other.m_y;
    }

    bool operator>(const Point& other) const
    {
      return m_y > other.m_y && m_x > other.m_x;
    }

    bool operator>=(const Point& other) const
    {
      return m_y >= other.m_y && m_x >= other.m_x;
    }

    bool operator==(const Point& a) const
    {
      return m_x == a.m_x && m_y == a.m_y;
    }

    bool operator!=(const Point& a) const
    {
      return !(*this == a);
    }

    bool empty() const
    {
      return m_x == -1 || m_y == -1;
    }

    static int sqrLength(const Point& first, const Point& second)
    {
      const int dx = second.x() - first.x();
      const int dy = second.y() - first.y();
      return dx * dx + dy * dy;
    }

    friend QDataStream& operator<<(QDataStream& out, const Point& obj);
    friend QDataStream& operator>>(QDataStream& in, Point& obj);
  };

  QDataStream& operator<<(QDataStream& out, const Point& obj);

  QDataStream& operator>>(QDataStream& in, Point& obj);
}

Q_DECLARE_METATYPE(KDots::Point);

#endif<commit_msg>Fixing a bug with points addition/substraction<commit_after>/*
 * KDots
 * Copyright(c) 2011-2012 Minh Ngo <nlminhtl@gmail.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef KDOTS_POINT_HPP
#define KDOTS_POINT_HPP
#include <QMetaType>
#include "constants.hpp"

namespace KDots
{
  class Point
  {
    int m_x, m_y;
  public:
    Point()
      : m_x(-1)
      , m_y(-1)
    {}

    bool isInitialized() const
    {
      return m_x >= 0 && m_y >= 0;
    }

    Point(int x, int y)
      : m_x(x)
      , m_y(y)
    {
    }

    int x() const
    {
      return m_x;
    }

    int y() const
    {
      return m_y;
    }

    void setX(int x)
    {
      m_x = x;
    }

    void setY(int y)
    {
      m_y = y;
    }

    Point operator*(int val) const
    {
      return Point(m_x * val, m_y * val);
    }

    Point operator+(int val) const
    {
      return Point(m_x + val, m_y + val);
    }

    Point operator-(const Point& point) const
    {
      return Point(m_x - point.m_x, m_y - point.m_y);
    }

    Point operator+(const Point& point) const
    {
      return Point(m_x + point.m_x, m_y + point.m_y);
    }

    bool operator<(const Point& other) const
    {
      return m_x < other.m_x && m_y < other.m_y;
    }

    bool operator<=(const Point& other) const
    {
      return m_x <= other.m_x && m_y <= other.m_y;
    }

    bool operator>(const Point& other) const
    {
      return m_y > other.m_y && m_x > other.m_x;
    }

    bool operator>=(const Point& other) const
    {
      return m_y >= other.m_y && m_x >= other.m_x;
    }

    bool operator==(const Point& a) const
    {
      return m_x == a.m_x && m_y == a.m_y;
    }

    bool operator!=(const Point& a) const
    {
      return !(*this == a);
    }

    bool empty() const
    {
      return m_x == -1 || m_y == -1;
    }

    static int sqrLength(const Point& first, const Point& second)
    {
      const int dx = second.x() - first.x();
      const int dy = second.y() - first.y();
      return dx * dx + dy * dy;
    }

    friend QDataStream& operator<<(QDataStream& out, const Point& obj);
    friend QDataStream& operator>>(QDataStream& in, Point& obj);
  };

  QDataStream& operator<<(QDataStream& out, const Point& obj);

  QDataStream& operator>>(QDataStream& in, Point& obj);
}

Q_DECLARE_METATYPE(KDots::Point);

#endif<|endoftext|>


<commit_before>/*
 *  This program is a dummy C++ program to ensure Mbed TLS library header files
 *  can be included and built with a C++ compiler.
 *
 *  Copyright The Mbed TLS Contributors
 *  SPDX-License-Identifier: Apache-2.0
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may
 *  not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif

#include "mbedtls/aes.h"
#include "mbedtls/aesni.h"
#include "mbedtls/arc4.h"
#include "mbedtls/aria.h"
#include "mbedtls/asn1.h"
#include "mbedtls/asn1write.h"
#include "mbedtls/base64.h"
#include "mbedtls/bignum.h"
#include "mbedtls/blowfish.h"
#include "mbedtls/bn_mul.h"
#include "mbedtls/camellia.h"
#include "mbedtls/ccm.h"
#include "mbedtls/certs.h"
#include "mbedtls/chacha20.h"
#include "mbedtls/chachapoly.h"
#include "mbedtls/check_config.h"
#include "mbedtls/cipher.h"
#include "mbedtls/cipher_internal.h"
#include "mbedtls/cmac.h"
#include "mbedtls/compat-1.3.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/des.h"
#include "mbedtls/dhm.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/ecjpake.h"
#include "mbedtls/ecp.h"
#include "mbedtls/ecp_internal.h"
#include "mbedtls/entropy.h"
#include "mbedtls/entropy_poll.h"
#include "mbedtls/error.h"
#include "mbedtls/gcm.h"
#include "mbedtls/havege.h"
#include "mbedtls/hkdf.h"
#include "mbedtls/hmac_drbg.h"
#include "mbedtls/md.h"
#include "mbedtls/md2.h"
#include "mbedtls/md4.h"
#include "mbedtls/md5.h"
#include "mbedtls/md_internal.h"
#include "mbedtls/net.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/nist_kw.h"
#include "mbedtls/oid.h"
#include "mbedtls/padlock.h"
#include "mbedtls/pem.h"
#include "mbedtls/pk.h"
#include "mbedtls/pk_internal.h"
#include "mbedtls/pkcs11.h"
#include "mbedtls/pkcs12.h"
#include "mbedtls/pkcs5.h"
#include "mbedtls/platform_time.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/poly1305.h"
#include "mbedtls/psa_util.h"
#include "mbedtls/ripemd160.h"
#include "mbedtls/rsa.h"
#include "mbedtls/rsa_internal.h"
#include "mbedtls/sha1.h"
#include "mbedtls/sha256.h"
#include "mbedtls/sha512.h"
#include "mbedtls/ssl.h"
#include "mbedtls/ssl_cache.h"
#include "mbedtls/ssl_ciphersuites.h"
#include "mbedtls/ssl_cookie.h"
#include "mbedtls/ssl_internal.h"
#include "mbedtls/ssl_ticket.h"
#include "mbedtls/threading.h"
#include "mbedtls/timing.h"
#include "mbedtls/version.h"
#include "mbedtls/x509.h"
#include "mbedtls/x509_crl.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/x509_csr.h"
#include "mbedtls/xtea.h"

#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#endif

#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
#include "mbedtls/memory_buffer_alloc.h"
#endif

#include "psa/crypto.h"
#include "psa/crypto_se_driver.h"
#include "../library/psa_crypto_its.h"

int main()
{
    mbedtls_platform_context *ctx = NULL;
    mbedtls_platform_setup(ctx);
    mbedtls_printf("CPP Build test\n");
    mbedtls_platform_teardown(ctx);
}
<commit_msg>Add missing header to cpp_dummy_build.cpp test<commit_after>/*
 *  This program is a dummy C++ program to ensure Mbed TLS library header files
 *  can be included and built with a C++ compiler.
 *
 *  Copyright The Mbed TLS Contributors
 *  SPDX-License-Identifier: Apache-2.0
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may
 *  not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif

#include "mbedtls/aes.h"
#include "mbedtls/aesni.h"
#include "mbedtls/arc4.h"
#include "mbedtls/aria.h"
#include "mbedtls/asn1.h"
#include "mbedtls/asn1write.h"
#include "mbedtls/base64.h"
#include "mbedtls/bignum.h"
#include "mbedtls/blowfish.h"
#include "mbedtls/bn_mul.h"
#include "mbedtls/camellia.h"
#include "mbedtls/ccm.h"
#include "mbedtls/certs.h"
#include "mbedtls/chacha20.h"
#include "mbedtls/chachapoly.h"
#include "mbedtls/check_config.h"
#include "mbedtls/cipher.h"
#include "mbedtls/cipher_internal.h"
#include "mbedtls/cmac.h"
#include "mbedtls/compat-1.3.h"
#include "mbedtls/config_psa.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/debug.h"
#include "mbedtls/des.h"
#include "mbedtls/dhm.h"
#include "mbedtls/ecdh.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/ecjpake.h"
#include "mbedtls/ecp.h"
#include "mbedtls/ecp_internal.h"
#include "mbedtls/entropy.h"
#include "mbedtls/entropy_poll.h"
#include "mbedtls/error.h"
#include "mbedtls/gcm.h"
#include "mbedtls/havege.h"
#include "mbedtls/hkdf.h"
#include "mbedtls/hmac_drbg.h"
#include "mbedtls/md.h"
#include "mbedtls/md2.h"
#include "mbedtls/md4.h"
#include "mbedtls/md5.h"
#include "mbedtls/md_internal.h"
#include "mbedtls/net.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/nist_kw.h"
#include "mbedtls/oid.h"
#include "mbedtls/padlock.h"
#include "mbedtls/pem.h"
#include "mbedtls/pk.h"
#include "mbedtls/pk_internal.h"
#include "mbedtls/pkcs11.h"
#include "mbedtls/pkcs12.h"
#include "mbedtls/pkcs5.h"
#include "mbedtls/platform_time.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/poly1305.h"
#include "mbedtls/psa_util.h"
#include "mbedtls/ripemd160.h"
#include "mbedtls/rsa.h"
#include "mbedtls/rsa_internal.h"
#include "mbedtls/sha1.h"
#include "mbedtls/sha256.h"
#include "mbedtls/sha512.h"
#include "mbedtls/ssl.h"
#include "mbedtls/ssl_cache.h"
#include "mbedtls/ssl_ciphersuites.h"
#include "mbedtls/ssl_cookie.h"
#include "mbedtls/ssl_internal.h"
#include "mbedtls/ssl_ticket.h"
#include "mbedtls/threading.h"
#include "mbedtls/timing.h"
#include "mbedtls/version.h"
#include "mbedtls/x509.h"
#include "mbedtls/x509_crl.h"
#include "mbedtls/x509_crt.h"
#include "mbedtls/x509_csr.h"
#include "mbedtls/xtea.h"

#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#endif

#if defined(MBEDTLS_MEMORY_BUFFER_ALLOC_C)
#include "mbedtls/memory_buffer_alloc.h"
#endif

#include "psa/crypto.h"
#include "psa/crypto_se_driver.h"
#include "../library/psa_crypto_its.h"

int main()
{
    mbedtls_platform_context *ctx = NULL;
    mbedtls_platform_setup(ctx);
    mbedtls_printf("CPP Build test\n");
    mbedtls_platform_teardown(ctx);
}
<|endoftext|>


<commit_before>/******************************************************************************
 Copyright (c) 2016,17 Leandro T. C. Melo (ltcmelo@gmail.com)

 This library is free software; you can redistribute it and/or modify it under
 the terms of the GNU Lesser General Public License as published by the Free
 Software Foundation; either version 2.1 of the License, or (at your option)
 any later version.

 This library is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 for more details.

 You should have received a copy of the GNU Lesser General Public License along
 with this library; if not, write to the Free Software Foundation, Inc., 51
 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 *****************************************************************************/

#include "TypeOfExpression.h"
#include "AST.h"
#include "Assert.h"
#include "CoreTypes.h"
#include "Control.h"
#include "Debug.h"
#include "Literals.h"
#include "Lookup.h"
#include "Scope.h"
#include "Symbol.h"
#include "Symbols.h"
#include "TranslationUnit.h"
#include "Utils.h"

using namespace CPlusPlus;
using namespace psyche;

TypeOfExpression::TypeOfExpression(TranslationUnit *unit)
    : ASTVisitor(unit)
    , singleLookup_(0)
{}

FullySpecifiedType TypeOfExpression::resolve(ExpressionAST *ast, Scope *scope)
{
    scope_ = scope;
    accept(ast);
    return fullType_;
}

FullySpecifiedType TypeOfExpression::commonRealType(const FullySpecifiedType& lhsTy,
                                              const FullySpecifiedType& rhsTy) const
{
    PSYCHE_ASSERT(lhsTy && Type::isArithmetic(lhsTy.type())
                        && rhsTy && Type::isArithmetic(rhsTy.type()),
                  return FullySpecifiedType(),
                  "expected arithmetic type");

    if (lhsTy == rhsTy)
        return lhsTy;

    auto check = [] (const FullySpecifiedType& ty,
                     bool (Type::*is) () const,
                     auto (Type::*as) () const,
                     int kind) {
        if (((*ty).*is)() && ((*ty).*as)()->kind() ==  kind)
            return true;
        return false;
    };

    auto ty =
        check(lhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::LongDouble) ?
            lhsTy :
            check(rhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::LongDouble) ?
                rhsTy :
        check(lhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::Double) ?
            lhsTy :
            check(rhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::Double) ?
                rhsTy :
        check(lhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::Float) ?
            lhsTy :
            check(rhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::Float) ?
                rhsTy : FullySpecifiedType();

    if (ty)
        return ty;

    // Both operands are integers.

    auto lhsPromoTy = promotedType(lhsTy);
    auto rhsPromoTy = promotedType(rhsTy);

    if (lhsPromoTy == rhsPromoTy)
        return lhsPromoTy;

    if (lhsPromoTy->asIntegerType()->rank() > rhsPromoTy->asIntegerType()->rank())
        return lhsPromoTy;
    return rhsPromoTy;
}

FullySpecifiedType TypeOfExpression::promotedType(const FullySpecifiedType& baseTy) const
{
    if (!baseTy || !baseTy->isIntegerType())
        return baseTy;

    switch (baseTy->asIntegerType()->kind()) {
    case IntegerType::Bool:
    case IntegerType::Char:
    case IntegerType::Short: {
        FullySpecifiedType promoTy;
        promoTy.setUnsigned(baseTy.isUnsigned());
        promoTy.setType(control()->integerType(IntegerType::Int));
        return promoTy;
    }

    default:
        return baseTy;
    }
}

bool TypeOfExpression::visit(ArrayAccessAST *ast)
{
    accept(ast->base_expression);

    if (fullType_->isArrayType())
        fullType_ = fullType_->asArrayType()->elementType();
    else if (fullType_->isPointerType())
        fullType_ = fullType_->asPointerType()->elementType();
    else
        fullType_ = FullySpecifiedType();

    return false;
}

bool TypeOfExpression::visit(BinaryExpressionAST *ast)
{
    BinaryExpressionAST* expr = ast->asBinaryExpression();

    auto plusMinus = [this, expr] (bool isMinus) {
        accept(expr->left_expression);
        const FullySpecifiedType lhsTy = fullType_;
        accept(expr->right_expression);
        const FullySpecifiedType rhsTy = fullType_;

        if (!lhsTy || !rhsTy)
            return FullySpecifiedType();

        if (Type::isArithmetic(lhsTy.type()) && Type::isArithmetic(rhsTy.type()))
            return commonRealType(lhsTy, rhsTy);

        if (lhsTy->isPointerType() && !rhsTy->isPointerType())
            return lhsTy;

        if (rhsTy->isPointerType() && !lhsTy->isPointerType())
            return rhsTy;

        if (isMinus && rhsTy->isPointerType() && lhsTy->isPointerType()) {
            FullySpecifiedType ty(control()->integerType(IntegerType::Long));
            return ty;
        }

        return FullySpecifiedType();
    };

    switch (tokenKind(expr->binary_op_token)) {
    case T_PLUS:
        fullType_ = plusMinus(false);
        return false;

    case T_MINUS:
        fullType_ = plusMinus(true);
        return false;

    case T_STAR:
    case T_SLASH:
    case T_PERCENT:
    case T_AMPER:
    case T_PIPE:
    case T_CARET:
    case T_LESS_LESS:
    case T_GREATER_GREATER: {
        accept(expr->left_expression);
        const FullySpecifiedType lhsTy = fullType_;
        accept(expr->right_expression);
        const FullySpecifiedType rhsTy = fullType_;

        if (!lhsTy) {
            fullType_ = rhsTy;
        } else {
            if (rhsTy && Type::isArithmetic(rhsTy.type()) && Type::isArithmetic(lhsTy.type()))
                fullType_ = commonRealType(lhsTy, rhsTy);
            else
                fullType_ = lhsTy;
        }
        return false;
    }

    // Comparison and logical
    case T_LESS:
    case T_LESS_EQUAL:
    case T_GREATER:
    case T_GREATER_EQUAL:
    case T_EQUAL_EQUAL:
    case T_EXCLAIM_EQUAL:
    case T_AMPER_AMPER:
    case T_PIPE_PIPE:
        fullType_ = control()->integerType(IntegerType::Int);
        return false;

    // Assignment
    case T_EQUAL:
    case T_PLUS_EQUAL:
    case T_MINUS_EQUAL:
    case T_STAR_EQUAL:
    case T_SLASH_EQUAL:
    case T_PERCENT_EQUAL:
    case T_AMPER_EQUAL:
    case T_PIPE_EQUAL:
    case T_CARET_EQUAL:
    case T_LESS_LESS_EQUAL:
    case T_GREATER_GREATER_EQUAL:
        accept(expr->left_expression);
        return false;

    case T_COMMA:
        accept(expr->right_expression);
        return false;

    default:
        PSYCHE_ASSERT(false, return false, "unrecognized operator");
        return false;
    }
}

bool TypeOfExpression::visit(CallAST *ast)
{
    accept(ast->base_expression);
    return false;
}

bool TypeOfExpression::visit(CastExpressionAST *ast)
{
    fullType_ = ast->expression_type;
    return false;
}

bool TypeOfExpression::visit(ConditionalExpressionAST *ast)
{
    accept(ast->right_expression);
    return false;
}

void TypeOfExpression::process(const Identifier *id)
{
    const Symbol* valSym;
    if (singleLookup_)
        valSym = scope_->find(id);
    else
        valSym = lookupValueSymbol(id, scope_);

    if (!valSym || !valSym->type()) {
        fullType_ = FullySpecifiedType();
        return;
    }

    FullySpecifiedType ty = valSym->type();
    fullType_ = ty;

    while (ty.type()->isPointerType())
        ty = ty.type()->asPointerType()->elementType();

    if (ty.type()->isNamedType()) {
        id = ty.type()->asNamedType()->name()->identifier();
    } else if (ty.type()->isClassType()
               && (ty.type()->asClassType()->name()->asNameId()
                   || ty.type()->asClassType()->name()->asElaboratedNameId())) {
        id = ty.type()->asClassType()->name()->identifier();
    } else {
        return;
    }

    PSYCHE_ASSERT(id, return, "expected valid id");
    Symbol* tySym = lookupTypeSymbol(id, scope_);
    if (tySym && tySym->type()->asClassType())
        scope_ = tySym->type()->asClassType();
}

bool TypeOfExpression::visit(IdExpressionAST *ast)
{
    PSYCHE_ASSERT(ast->asIdExpression()->name->name->asNameId(),
                  return false,
                  "expected trivial identifier name");
    process(ast->name->name->identifier());
    return false;
}

bool TypeOfExpression::visit(MemberAccessAST *ast)
{
    PSYCHE_ASSERT(ast->member_name->name->asNameId(),
                  return false,
                  "expected trivial member name");

    accept(ast->base_expression);
    ++singleLookup_;
    process(ast->member_name->name->identifier());
    PSYCHE_ASSERT(singleLookup_ > 0, return false, "expected member check");
    --singleLookup_;

    return false;
}

bool TypeOfExpression::visit(NumericLiteralAST *ast)
{
    const Token& tk = tokenAt(ast->literal_token);
    if (tk.is(T_CHAR_LITERAL)) {
        // TODO: char/int
        fullType_ = control()->integerType(IntegerType::Char);
    } else {
        const NumericLiteral *numLit = numericLiteral(ast->literal_token);
        PSYCHE_ASSERT(numLit, return false, "numeric literal must exist");
        if (numLit->isDouble()) {
            fullType_ = control()->floatType(FloatType::Double);
        } else if (numLit->isLongDouble()) {
            fullType_ = control()->floatType(FloatType::LongDouble);
        } else if (numLit->isFloat()) {
            fullType_ = control()->floatType(FloatType::Float);
        } else {
            fullType_ = control()->integerType(IntegerType::Int);
        }
    }

    return false;
}

bool TypeOfExpression::visit(BoolLiteralAST*)
{
    fullType_ = control()->integerType(IntegerType::Int);
    return false;
}

bool TypeOfExpression::visit(StringLiteralAST*)
{
    FullySpecifiedType baseType = control()->integerType(IntegerType::Char);
    fullType_ = control()->pointerType(baseType);
    return false;
}

bool TypeOfExpression::visit(UnaryExpressionAST *ast)
{
    accept(ast->expression);

    switch (tokenKind(ast->unary_op_token)) {
    case T_AMPER:
        fullType_ = control()->pointerType(fullType_);
        return false;

    case T_STAR: {
        if (fullType_->isArrayType())
            fullType_ = fullType_->asArrayType()->elementType();
        else if (fullType_->isPointerType())
            fullType_ = fullType_->asPointerType()->elementType();
        else
            fullType_ = FullySpecifiedType();
        return false;
    }

    default:
        return false;
    }
}

bool TypeOfExpression::visit(SizeofExpressionAST*)
{
    fullType_ = control()->integerType(IntegerType::Int);
    return false;
}

bool TypeOfExpression::visit(PointerLiteralAST*)
{
    fullType_ = control()->pointerType(FullySpecifiedType());
    return false;
}

bool psyche::TypeOfExpression::visit(CPlusPlus::TypeIdAST *ast)
{
    accept(ast->type_specifier_list);
    accept(ast->declarator);
    return false;
}

bool TypeOfExpression::visit(SimpleSpecifierAST *ast)
{
    switch (tokenKind(ast->specifier_token)) {
    case T_CHAR:
        // TODO: char/int
        fullType_ = control()->integerType(IntegerType::Char);
        return false;
    case T_SHORT:
        fullType_ = control()->integerType(IntegerType::Short);
        return false;
    case T_INT:
        fullType_ = control()->integerType(IntegerType::Int);
        return false;
    case T_LONG:
        if (fullType_.type()
                && fullType_->asIntegerType()
                && fullType_->asIntegerType()->kind() == IntegerType::Long) {
            fullType_ = control()->integerType(IntegerType::LongLong);
        } else {
            fullType_ = control()->integerType(IntegerType::Long);
        }
        return false;
    case T_FLOAT:
        fullType_ = control()->floatType(FloatType::Float);
        return false;
    case T_DOUBLE:
        if (fullType_.type()
                && fullType_->asIntegerType()
                && fullType_->asIntegerType()->kind() == IntegerType::Long) {
            fullType_ = control()->floatType(FloatType::LongDouble);
        } else {
            fullType_ = control()->floatType(FloatType::Double);
        }
        return false;
    case T_UNSIGNED:
        fullType_.setUnsigned(true);
        return false;
    case T_SIGNED:
        fullType_.setSigned(true);
        return false;
    case T_VOID:
        return false;

    default:
        PSYCHE_ASSERT(false, return false, "unknown type specifier");
        return false;
    }
}

namespace {

FullySpecifiedType lookupTypeName(NameAST *name, const Scope* scope)
{
    Symbol* tySym = lookupTypeSymbol(name->name, scope);
    if (tySym)
        return tySym->type();
    return FullySpecifiedType();
}

} // anonymous

bool TypeOfExpression::visit(NamedTypeSpecifierAST *ast)
{
    fullType_ = lookupTypeName(ast->name, scope_);
    return false;
}

bool TypeOfExpression::visit(ElaboratedTypeSpecifierAST *ast)
{
    fullType_ = lookupTypeName(ast->name, scope_);
    return false;
}

bool TypeOfExpression::visit(DeclaratorAST *ast)
{
    accept(ast->ptr_operator_list);
    return false;
}

bool TypeOfExpression::visit(PointerAST*)
{
    fullType_ = control()->pointerType(fullType_);
    return false;
}
<commit_msg>Set const/volatile in type of expression<commit_after>/******************************************************************************
 Copyright (c) 2016,17 Leandro T. C. Melo (ltcmelo@gmail.com)

 This library is free software; you can redistribute it and/or modify it under
 the terms of the GNU Lesser General Public License as published by the Free
 Software Foundation; either version 2.1 of the License, or (at your option)
 any later version.

 This library is distributed in the hope that it will be useful, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 for more details.

 You should have received a copy of the GNU Lesser General Public License along
 with this library; if not, write to the Free Software Foundation, Inc., 51
 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 *****************************************************************************/

#include "TypeOfExpression.h"
#include "AST.h"
#include "Assert.h"
#include "CoreTypes.h"
#include "Control.h"
#include "Debug.h"
#include "Literals.h"
#include "Lookup.h"
#include "Scope.h"
#include "Symbol.h"
#include "Symbols.h"
#include "TranslationUnit.h"
#include "Utils.h"

using namespace CPlusPlus;
using namespace psyche;

TypeOfExpression::TypeOfExpression(TranslationUnit *unit)
    : ASTVisitor(unit)
    , singleLookup_(0)
{}

FullySpecifiedType TypeOfExpression::resolve(ExpressionAST *ast, Scope *scope)
{
    scope_ = scope;
    accept(ast);
    return fullType_;
}

FullySpecifiedType TypeOfExpression::commonRealType(const FullySpecifiedType& lhsTy,
                                              const FullySpecifiedType& rhsTy) const
{
    PSYCHE_ASSERT(lhsTy && Type::isArithmetic(lhsTy.type())
                        && rhsTy && Type::isArithmetic(rhsTy.type()),
                  return FullySpecifiedType(),
                  "expected arithmetic type");

    if (lhsTy == rhsTy)
        return lhsTy;

    auto check = [] (const FullySpecifiedType& ty,
                     bool (Type::*is) () const,
                     auto (Type::*as) () const,
                     int kind) {
        if (((*ty).*is)() && ((*ty).*as)()->kind() ==  kind)
            return true;
        return false;
    };

    auto ty =
        check(lhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::LongDouble) ?
            lhsTy :
            check(rhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::LongDouble) ?
                rhsTy :
        check(lhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::Double) ?
            lhsTy :
            check(rhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::Double) ?
                rhsTy :
        check(lhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::Float) ?
            lhsTy :
            check(rhsTy, &Type::isFloatType, &Type::asFloatType, FloatType::Float) ?
                rhsTy : FullySpecifiedType();

    if (ty)
        return ty;

    // Both operands are integers.

    auto lhsPromoTy = promotedType(lhsTy);
    auto rhsPromoTy = promotedType(rhsTy);

    if (lhsPromoTy == rhsPromoTy)
        return lhsPromoTy;

    if (lhsPromoTy->asIntegerType()->rank() > rhsPromoTy->asIntegerType()->rank())
        return lhsPromoTy;
    return rhsPromoTy;
}

FullySpecifiedType TypeOfExpression::promotedType(const FullySpecifiedType& baseTy) const
{
    if (!baseTy || !baseTy->isIntegerType())
        return baseTy;

    switch (baseTy->asIntegerType()->kind()) {
    case IntegerType::Bool:
    case IntegerType::Char:
    case IntegerType::Short: {
        FullySpecifiedType promoTy;
        promoTy.setUnsigned(baseTy.isUnsigned());
        promoTy.setType(control()->integerType(IntegerType::Int));
        return promoTy;
    }

    default:
        return baseTy;
    }
}

bool TypeOfExpression::visit(ArrayAccessAST *ast)
{
    accept(ast->base_expression);

    if (fullType_->isArrayType())
        fullType_ = fullType_->asArrayType()->elementType();
    else if (fullType_->isPointerType())
        fullType_ = fullType_->asPointerType()->elementType();
    else
        fullType_ = FullySpecifiedType();

    return false;
}

bool TypeOfExpression::visit(BinaryExpressionAST *ast)
{
    BinaryExpressionAST* expr = ast->asBinaryExpression();

    auto plusMinus = [this, expr] (bool isMinus) {
        accept(expr->left_expression);
        const FullySpecifiedType lhsTy = fullType_;
        accept(expr->right_expression);
        const FullySpecifiedType rhsTy = fullType_;

        if (!lhsTy || !rhsTy)
            return FullySpecifiedType();

        if (Type::isArithmetic(lhsTy.type()) && Type::isArithmetic(rhsTy.type()))
            return commonRealType(lhsTy, rhsTy);

        if (lhsTy->isPointerType() && !rhsTy->isPointerType())
            return lhsTy;

        if (rhsTy->isPointerType() && !lhsTy->isPointerType())
            return rhsTy;

        if (isMinus && rhsTy->isPointerType() && lhsTy->isPointerType()) {
            FullySpecifiedType ty(control()->integerType(IntegerType::Long));
            return ty;
        }

        return FullySpecifiedType();
    };

    switch (tokenKind(expr->binary_op_token)) {
    case T_PLUS:
        fullType_ = plusMinus(false);
        return false;

    case T_MINUS:
        fullType_ = plusMinus(true);
        return false;

    case T_STAR:
    case T_SLASH:
    case T_PERCENT:
    case T_AMPER:
    case T_PIPE:
    case T_CARET:
    case T_LESS_LESS:
    case T_GREATER_GREATER: {
        accept(expr->left_expression);
        const FullySpecifiedType lhsTy = fullType_;
        accept(expr->right_expression);
        const FullySpecifiedType rhsTy = fullType_;

        if (!lhsTy) {
            fullType_ = rhsTy;
        } else {
            if (rhsTy && Type::isArithmetic(rhsTy.type()) && Type::isArithmetic(lhsTy.type()))
                fullType_ = commonRealType(lhsTy, rhsTy);
            else
                fullType_ = lhsTy;
        }
        return false;
    }

    // Comparison and logical
    case T_LESS:
    case T_LESS_EQUAL:
    case T_GREATER:
    case T_GREATER_EQUAL:
    case T_EQUAL_EQUAL:
    case T_EXCLAIM_EQUAL:
    case T_AMPER_AMPER:
    case T_PIPE_PIPE:
        fullType_ = control()->integerType(IntegerType::Int);
        return false;

    // Assignment
    case T_EQUAL:
    case T_PLUS_EQUAL:
    case T_MINUS_EQUAL:
    case T_STAR_EQUAL:
    case T_SLASH_EQUAL:
    case T_PERCENT_EQUAL:
    case T_AMPER_EQUAL:
    case T_PIPE_EQUAL:
    case T_CARET_EQUAL:
    case T_LESS_LESS_EQUAL:
    case T_GREATER_GREATER_EQUAL:
        accept(expr->left_expression);
        return false;

    case T_COMMA:
        accept(expr->right_expression);
        return false;

    default:
        PSYCHE_ASSERT(false, return false, "unrecognized operator");
        return false;
    }
}

bool TypeOfExpression::visit(CallAST *ast)
{
    accept(ast->base_expression);
    return false;
}

bool TypeOfExpression::visit(CastExpressionAST *ast)
{
    fullType_ = ast->expression_type;
    return false;
}

bool TypeOfExpression::visit(ConditionalExpressionAST *ast)
{
    accept(ast->right_expression);
    return false;
}

void TypeOfExpression::process(const Identifier *id)
{
    const Symbol* valSym;
    if (singleLookup_)
        valSym = scope_->find(id);
    else
        valSym = lookupValueSymbol(id, scope_);

    if (!valSym || !valSym->type()) {
        fullType_ = FullySpecifiedType();
        return;
    }

    FullySpecifiedType ty = valSym->type();
    fullType_ = ty;

    while (ty.type()->isPointerType())
        ty = ty.type()->asPointerType()->elementType();

    if (ty.type()->isNamedType()) {
        id = ty.type()->asNamedType()->name()->identifier();
    } else if (ty.type()->isClassType()
               && (ty.type()->asClassType()->name()->asNameId()
                   || ty.type()->asClassType()->name()->asElaboratedNameId())) {
        id = ty.type()->asClassType()->name()->identifier();
    } else {
        return;
    }

    PSYCHE_ASSERT(id, return, "expected valid id");
    Symbol* tySym = lookupTypeSymbol(id, scope_);
    if (tySym && tySym->type()->asClassType())
        scope_ = tySym->type()->asClassType();
}

bool TypeOfExpression::visit(IdExpressionAST *ast)
{
    PSYCHE_ASSERT(ast->asIdExpression()->name->name->asNameId(),
                  return false,
                  "expected trivial identifier name");
    process(ast->name->name->identifier());
    return false;
}

bool TypeOfExpression::visit(MemberAccessAST *ast)
{
    PSYCHE_ASSERT(ast->member_name->name->asNameId(),
                  return false,
                  "expected trivial member name");

    accept(ast->base_expression);
    ++singleLookup_;
    process(ast->member_name->name->identifier());
    PSYCHE_ASSERT(singleLookup_ > 0, return false, "expected member check");
    --singleLookup_;

    return false;
}

bool TypeOfExpression::visit(NumericLiteralAST *ast)
{
    const Token& tk = tokenAt(ast->literal_token);
    if (tk.is(T_CHAR_LITERAL)) {
        // TODO: char/int
        fullType_ = control()->integerType(IntegerType::Char);
    } else {
        const NumericLiteral *numLit = numericLiteral(ast->literal_token);
        PSYCHE_ASSERT(numLit, return false, "numeric literal must exist");
        if (numLit->isDouble()) {
            fullType_ = control()->floatType(FloatType::Double);
        } else if (numLit->isLongDouble()) {
            fullType_ = control()->floatType(FloatType::LongDouble);
        } else if (numLit->isFloat()) {
            fullType_ = control()->floatType(FloatType::Float);
        } else {
            fullType_ = control()->integerType(IntegerType::Int);
        }
    }

    return false;
}

bool TypeOfExpression::visit(BoolLiteralAST*)
{
    fullType_ = control()->integerType(IntegerType::Int);
    return false;
}

bool TypeOfExpression::visit(StringLiteralAST*)
{
    FullySpecifiedType baseType = control()->integerType(IntegerType::Char);
    fullType_ = control()->pointerType(baseType);
    return false;
}

bool TypeOfExpression::visit(UnaryExpressionAST *ast)
{
    accept(ast->expression);

    switch (tokenKind(ast->unary_op_token)) {
    case T_AMPER:
        fullType_ = control()->pointerType(fullType_);
        return false;

    case T_STAR: {
        if (fullType_->isArrayType())
            fullType_ = fullType_->asArrayType()->elementType();
        else if (fullType_->isPointerType())
            fullType_ = fullType_->asPointerType()->elementType();
        else
            fullType_ = FullySpecifiedType();
        return false;
    }

    default:
        return false;
    }
}

bool TypeOfExpression::visit(SizeofExpressionAST*)
{
    fullType_ = control()->integerType(IntegerType::Int);
    return false;
}

bool TypeOfExpression::visit(PointerLiteralAST*)
{
    fullType_ = control()->pointerType(FullySpecifiedType());
    return false;
}

bool psyche::TypeOfExpression::visit(CPlusPlus::TypeIdAST *ast)
{
    accept(ast->type_specifier_list);
    accept(ast->declarator);
    return false;
}

bool TypeOfExpression::visit(SimpleSpecifierAST *ast)
{
    switch (tokenKind(ast->specifier_token)) {
    case T_CHAR:
        // TODO: char/int
        fullType_ = control()->integerType(IntegerType::Char);
        return false;
    case T_SHORT:
        fullType_ = control()->integerType(IntegerType::Short);
        return false;
    case T_INT:
        fullType_ = control()->integerType(IntegerType::Int);
        return false;
    case T_LONG:
        if (fullType_.type()
                && fullType_->asIntegerType()
                && fullType_->asIntegerType()->kind() == IntegerType::Long) {
            fullType_ = control()->integerType(IntegerType::LongLong);
        } else {
            fullType_ = control()->integerType(IntegerType::Long);
        }
        return false;
    case T_FLOAT:
        fullType_ = control()->floatType(FloatType::Float);
        return false;
    case T_DOUBLE:
        if (fullType_.type()
                && fullType_->asIntegerType()
                && fullType_->asIntegerType()->kind() == IntegerType::Long) {
            fullType_ = control()->floatType(FloatType::LongDouble);
        } else {
            fullType_ = control()->floatType(FloatType::Double);
        }
        return false;
    case T_VOID:
        return false;

    case T_UNSIGNED:
        fullType_.setUnsigned(true);
        return false;
    case T_SIGNED:
        fullType_.setSigned(true);
        return false;

    case T_CONST:
        fullType_.setConst(true);
        return false;
    case T_VOLATILE:
        fullType_.setVolatile(true);
        return false;

    default:
        PSYCHE_ASSERT(false, return false, "unknown type specifier");
        return false;
    }
}

namespace {

FullySpecifiedType lookupTypeName(NameAST *name, const Scope* scope)
{
    Symbol* tySym = lookupTypeSymbol(name->name, scope);
    if (tySym)
        return tySym->type();
    return FullySpecifiedType();
}

} // anonymous

bool TypeOfExpression::visit(NamedTypeSpecifierAST *ast)
{
    fullType_ = lookupTypeName(ast->name, scope_);
    return false;
}

bool TypeOfExpression::visit(ElaboratedTypeSpecifierAST *ast)
{
    fullType_ = lookupTypeName(ast->name, scope_);
    return false;
}

bool TypeOfExpression::visit(DeclaratorAST *ast)
{
    accept(ast->ptr_operator_list);
    return false;
}

bool TypeOfExpression::visit(PointerAST*)
{
    fullType_ = control()->pointerType(fullType_);
    return false;
}
<|endoftext|>