text:
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/accessibility/magnifier/docked_magnifier_controller.h"

#include <algorithm>
#include <utility>

#include "ash/accessibility/accessibility_controller_impl.h"
#include "ash/accessibility/magnifier/magnifier_utils.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/display/cursor_window_controller.h"
#include "ash/host/ash_window_tree_host.h"
#include "ash/keyboard/ui/keyboard_ui_controller.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/wm/overview/overview_controller.h"
#include "ash/wm/splitview/split_view_controller.h"
#include "ash/wm/work_area_insets.h"
#include "base/bind.h"
#include "base/cxx17_backports.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/aura/client/drag_drop_client.h"
#include "ui/aura/window_tree_host.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/display/screen.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/coordinate_conversion.h"

namespace ash {

namespace {

// Default, minimum, and maximum magnifier scale values (magnification levels).
constexpr float kDefaultMagnifierScale = 4.0f;
constexpr float kMinMagnifierScale = 1.0f;
constexpr float kMaxMagnifierScale = 20.0f;

// Minimum and maximum screen height divisors. These correspond to the tallest
// and shortest allowed docked magnifier viewport heights, as
// viewport_height = root_bounds.height() / screen_height_divisor.
constexpr float kMinScreenHeightDivisor = 5.0f / 4.0f;
constexpr float kMaxScreenHeightDivisor = 8.0f;

// The factor by which the offset of scroll events are scaled.
constexpr float kScrollScaleFactor = 0.0125f;

constexpr char kDockedMagnifierViewportWindowName[] =
    "DockedMagnifierViewportWindow";

// Returns the current cursor location in screen coordinates.
inline gfx::Point GetCursorScreenPoint() {
  return display::Screen::GetScreen()->GetCursorScreenPoint();
}

// Updates the workarea of the display associated with |window| such that the
// given magnifier viewport |height| is allocated at the top of the screen.
void SetViewportHeightInWorkArea(aura::Window* window, int height) {
  DCHECK(window);
  WorkAreaInsets::ForWindow(window->GetRootWindow())
      ->SetDockedMagnifierHeight(height);
}

// Returns the separator layer bounds from the given |viewport_bounds|. The
// separator layer is to be placed right below the viewport.
inline gfx::Rect SeparatorBoundsFromViewportBounds(
    const gfx::Rect& viewport_bounds) {
  return gfx::Rect(viewport_bounds.x(), viewport_bounds.bottom(),
                   viewport_bounds.width(),
                   DockedMagnifierController::kSeparatorHeight);
}

// Returns the child container in |root| that should be used as the parent of
// viewport widget.
aura::Window* GetViewportParentContainerForRoot(aura::Window* root) {
  return root->GetChildById(kShellWindowId_DockedMagnifierContainer);
}

// Returns the child container in |root| that should be used as the parent of
// the separator layer.
aura::Window* GetViewportParentContainerForDivider(aura::Window* root) {
  return root->GetChildById(kShellWindowId_OverlayContainer);
}

}  // namespace

// static
DockedMagnifierController::DockedMagnifierController() {
  Shell::Get()->session_controller()->AddObserver(this);
}

DockedMagnifierController::~DockedMagnifierController() {
  Shell* shell = Shell::Get();
  shell->session_controller()->RemoveObserver(this);

  if (GetEnabled()) {
    shell->window_tree_host_manager()->RemoveObserver(this);
    shell->RemovePreTargetHandler(this);
  }
  CHECK(!views::WidgetObserver::IsInObserverList());
}

// static
void DockedMagnifierController::RegisterProfilePrefs(
    PrefRegistrySimple* registry) {
  registry->RegisterBooleanPref(prefs::kDockedMagnifierEnabled, false);
  registry->RegisterDoublePref(prefs::kDockedMagnifierScale,
                               kDefaultMagnifierScale);
  registry->RegisterDoublePref(prefs::kDockedMagnifierScreenHeightDivisor,
                               kDefaultScreenHeightDivisor);
}

bool DockedMagnifierController::GetEnabled() const {
  return active_user_pref_service_ &&
         active_user_pref_service_->GetBoolean(prefs::kDockedMagnifierEnabled);
}

float DockedMagnifierController::GetScale() const {
  if (active_user_pref_service_)
    return active_user_pref_service_->GetDouble(prefs::kDockedMagnifierScale);

  return kDefaultMagnifierScale;
}

float DockedMagnifierController::GetScreenHeightDivisor() const {
  if (active_user_pref_service_) {
    return active_user_pref_service_->GetDouble(
        prefs::kDockedMagnifierScreenHeightDivisor);
  }

  return kDefaultScreenHeightDivisor;
}

void DockedMagnifierController::SetEnabled(bool enabled) {
  if (active_user_pref_service_) {
    active_user_pref_service_->SetBoolean(prefs::kDockedMagnifierEnabled,
                                          enabled);
  }
}

void DockedMagnifierController::SetScale(float scale) {
  if (active_user_pref_service_) {
    active_user_pref_service_->SetDouble(
        prefs::kDockedMagnifierScale,
        base::clamp(scale, kMinMagnifierScale, kMaxMagnifierScale));
  }
}

void DockedMagnifierController::SetScreenHeightDivisor(
    float screen_height_divisor) {
  if (active_user_pref_service_) {
    active_user_pref_service_->SetDouble(
        prefs::kDockedMagnifierScreenHeightDivisor,
        base::clamp(screen_height_divisor, kMinScreenHeightDivisor,
                    kMaxScreenHeightDivisor));
  }
}

void DockedMagnifierController::StepToNextScaleValue(int delta_index) {
  SetScale(magnifier_utils::GetNextMagnifierScaleValue(
      delta_index, GetScale(), kMinMagnifierScale, kMaxMagnifierScale));
}

void DockedMagnifierController::MoveMagnifierToRect(
    const gfx::Rect& rect_in_screen) {
  DCHECK(GetEnabled());
  gfx::Point point_in_screen = rect_in_screen.CenterPoint();

  // If rect is too wide to fit in viewport, include as much as we can, starting
  // with the left edge.
  const int scaled_viewport_width =
      current_source_root_window_->bounds().width() / GetScale();
  if (rect_in_screen.width() > scaled_viewport_width) {
    point_in_screen.set_x(std::max(rect_in_screen.x() +
                                       scaled_viewport_width / 2 -
                                       magnifier_utils::kLeftEdgeContextPadding,
                                   0));
  }

  CenterOnPoint(point_in_screen);
}

void DockedMagnifierController::CenterOnPoint(
    const gfx::Point& point_in_screen) {
  if (!GetEnabled())
    return;

  auto* screen = display::Screen::GetScreen();
  auto* window = screen->GetWindowAtScreenPoint(point_in_screen);
  if (!window) {
    // In tests and sometimes initially on signin screen, |point_in_screen|
    // maybe invalid and doesn't belong to any existing root window. However, we
    // are here because the Docked Magnifier is enabled. We need to create the
    // viewport widget somewhere, so we'll use the primary root window until we
    // get a valid cursor event.
    window = Shell::GetPrimaryRootWindow();
  }

  auto* root_window = window->GetRootWindow();
  DCHECK(root_window);
  SwitchCurrentSourceRootWindowIfNeeded(root_window,
                                        true /* update_old_root_workarea */);

  auto* host = root_window->GetHost();
  DCHECK(host);

  MaybeCachePointOfInterestMinimumHeight(host);

  gfx::Point point_of_interest(point_in_screen);
  ::wm::ConvertPointFromScreen(root_window, &point_of_interest);

  // The point of interest in pixels.
  gfx::PointF point_in_pixels(point_of_interest);

  // Before transforming to pixels, make sure its y-coordinate doesn't go below
  // the minimum height. Do it here for this PointF since the
  // |minimum_point_of_interest_height_| is a float, in order to avoid rounding
  // in the transform to be able to reliably verify it in tests.
  if (point_in_pixels.y() < minimum_point_of_interest_height_)
    point_in_pixels.set_y(minimum_point_of_interest_height_);

  // The pixel space is the magnified space.
  const float scale = GetScale();
  point_in_pixels.Scale(scale);

  // Transform steps: (Note that the transform is applied in the opposite
  // order)
  // 1- Scale the layer by |scale|.
  // 2- Translate the point of interest to the center point of the viewport
  //    widget.
  const gfx::Point viewport_center_point =
      magnifier_utils::GetViewportWidgetBoundsInRoot(
          current_source_root_window_, GetScreenHeightDivisor())
          .CenterPoint();
  gfx::Transform transform;
  transform.Translate(viewport_center_point.x() - point_in_pixels.x(),
                      viewport_center_point.y() - point_in_pixels.y());
  transform.Scale(scale, scale);

  // When updating the transform, we don't want any animation, otherwise the
  // movement of the mouse won't be very smooth. We want the magnifier layer to
  // update immediately with the movement of the mouse (or the change in the
  // point of interest due to input caret bounds changes ... etc.).
  ui::ScopedLayerAnimationSettings settings(
      viewport_magnifier_layer_->GetAnimator());
  settings.SetTransitionDuration(base::Milliseconds(0));
  settings.SetTweenType(gfx::Tween::ZERO);
  settings.SetPreemptionStrategy(ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET);
  viewport_magnifier_layer_->SetTransform(transform);
}

int DockedMagnifierController::GetMagnifierHeightForTesting() const {
  return GetTotalMagnifierHeight();
}

void DockedMagnifierController::OnActiveUserPrefServiceChanged(
    PrefService* pref_service) {
  active_user_pref_service_ = pref_service;
  InitFromUserPrefs();
}

void DockedMagnifierController::OnSigninScreenPrefServiceInitialized(
    PrefService* prefs) {
  OnActiveUserPrefServiceChanged(prefs);
}

void DockedMagnifierController::OnMouseEvent(ui::MouseEvent* event) {
  DCHECK(GetEnabled());
  if (::features::IsDockedMagnifierResizingEnabled())
    MaybePerformViewportResizing(event);

  CenterOnPoint(GetCursorScreenPoint());
}

void DockedMagnifierController::OnScrollEvent(ui::ScrollEvent* event) {
  DCHECK(GetEnabled());
  if (!event->IsAltDown() || !event->IsControlDown())
    return;

  if (event->type() == ui::ET_SCROLL_FLING_START ||
      event->type() == ui::ET_SCROLL_FLING_CANCEL) {
    event->StopPropagation();
    return;
  }

  if (event->type() == ui::ET_SCROLL) {
    // Notes: - Clamping of the new scale value happens inside SetScale().
    //        - Refreshing the viewport happens in the handler of the scale pref
    //          changes.
    SetScale(magnifier_utils::GetScaleFromScroll(
        event->y_offset() * kScrollScaleFactor, GetScale(), kMaxMagnifierScale,
        kMinMagnifierScale));
    event->StopPropagation();
  }
}

void DockedMagnifierController::OnTouchEvent(ui::TouchEvent* event) {
  DCHECK(GetEnabled());

  aura::Window* target = static_cast<aura::Window*>(event->target());
  aura::Window* event_root = target->GetRootWindow();
  gfx::Point event_screen_point = event->root_location();
  ::wm::ConvertPointToScreen(event_root, &event_screen_point);

  // Ignore touch events on virtual Keyboard, to stabilize docked magnifier.
  if (keyboard::KeyboardUIController::Get()->IsEnabled() &&
      keyboard::KeyboardUIController::Get()
          ->GetKeyboardWindow()
          ->GetBoundsInScreen()
          .Contains(event_screen_point))
    return;

  CenterOnPoint(event_screen_point);
}

void DockedMagnifierController::OnWidgetDestroying(views::Widget* widget) {
  DCHECK_EQ(widget, viewport_widget_);

  SwitchCurrentSourceRootWindowIfNeeded(nullptr,
                                        false /* update_old_root_workarea */);
}

void DockedMagnifierController::OnDisplayConfigurationChanged() {
  DCHECK(GetEnabled());

  // The viewport might have been on a display that just got removed, and hence
  // the viewport widget and its associated layers are already destroyed. In
  // that case we also cleared the |current_source_root_window_|.
  if (current_source_root_window_) {
    // Resolution may have changed. Update all bounds.
    const auto viewport_bounds = magnifier_utils::GetViewportWidgetBoundsInRoot(
        current_source_root_window_, GetScreenHeightDivisor());
    viewport_widget_->SetBounds(viewport_bounds);
    viewport_background_layer_->SetBounds(viewport_bounds);
    separator_layer_->SetBounds(
        SeparatorBoundsFromViewportBounds(viewport_bounds));
    SetViewportHeightInWorkArea(current_source_root_window_,
                                GetTotalMagnifierHeight());

    // Resolution changes, screen rotation, etc. can reset the host to confine
    // the mouse cursor inside the root window. We want to make sure the cursor
    // is confined properly outside the viewport. But don't confine mouse if
    // resizing.
    if (!is_resizing_)
      ConfineMouseCursorOutsideViewport();
  }

  // A change in display configuration, such as resolution, rotation, ... etc.
  // invalidates the currently cached minimum height of the point of interest.
  is_minimum_point_of_interest_height_valid_ = false;

  // Update the viewport magnifier layer transform.
  CenterOnPoint(GetCursorScreenPoint());
}

bool DockedMagnifierController::GetFullscreenMagnifierEnabled() const {
  return active_user_pref_service_ &&
         active_user_pref_service_->GetBoolean(
             prefs::kAccessibilityScreenMagnifierEnabled);
}

void DockedMagnifierController::SetFullscreenMagnifierEnabled(bool enabled) {
  if (active_user_pref_service_) {
    active_user_pref_service_->SetBoolean(
        prefs::kAccessibilityScreenMagnifierEnabled, enabled);
  }
}

int DockedMagnifierController::GetTotalMagnifierHeight() const {
  if (separator_layer_)
    return separator_layer_->bounds().bottom();

  return 0;
}

gfx::Rect DockedMagnifierController::GetTotalMagnifierBoundsForRoot(
    aura::Window* root) const {
  DCHECK(root);
  DCHECK(root->IsRootWindow());

  if (viewport_widget_ && current_source_root_window_ == root) {
    gfx::Rect bounds =
        viewport_widget_->GetNativeWindow()->GetActualBoundsInRootWindow();
    DCHECK(separator_layer_);
    bounds.set_height(separator_layer_->bounds().bottom());
    return bounds;
  }

  return gfx::Rect();
}

const views::Widget* DockedMagnifierController::GetViewportWidgetForTesting()
    const {
  return viewport_widget_;
}

const ui::Layer*
DockedMagnifierController::GetViewportMagnifierLayerForTesting() const {
  return viewport_magnifier_layer_.get();
}

float DockedMagnifierController::GetMinimumPointOfInterestHeightForTesting()
    const {
  return minimum_point_of_interest_height_;
}

void DockedMagnifierController::MaybeSetCursorSize(ui::CursorSize cursor_size) {
  if (Shell::Get()->accessibility_controller()->large_cursor().enabled())
    return;
  Shell::Get()->cursor_manager()->SetCursorSize(cursor_size);
}

void DockedMagnifierController::MaybePerformViewportResizing(
    ui::MouseEvent* event) {
  DCHECK(current_source_root_window_);
  gfx::Rect root_bounds = current_source_root_window_->GetBoundsInRootWindow();
  float magnifier_height = root_bounds.height() / GetScreenHeightDivisor();
  float root_y = event->root_location_f().y();
  const int separator_top = separator_layer_->bounds().y();
  const int separator_bottom = separator_layer_->bounds().bottom();
  bool cursor_is_over_resizer =
      root_y >= separator_top - 1 && root_y <= separator_bottom;
  ::wm::CursorManager* cursor_manager = Shell::Get()->cursor_manager();
  CursorWindowController* cursor_window_controller =
      Shell::Get()->window_tree_host_manager()->cursor_window_controller();

  // If cursor is over separator, change to north/south resize, move on top.
  // Reset once the cursor is not over separator, and user isn't resizing.
  if (cursor_is_over_resizer && !is_cursor_locked_) {
    MaybeSetCursorSize(ui::CursorSize::kLarge);
    cursor_manager->SetCursor(ui::mojom::CursorType::kNorthSouthResize);
    cursor_manager->LockCursor();
    cursor_window_controller->OnDockedMagnifierResizingStateChanged(true);
    is_cursor_locked_ = true;
  } else if (!cursor_is_over_resizer && is_cursor_locked_ && !is_resizing_) {
    MaybeSetCursorSize(ui::CursorSize::kNormal);
    cursor_manager->UnlockCursor();
    cursor_window_controller->OnDockedMagnifierResizingStateChanged(false);
    is_cursor_locked_ = false;
  }

  // If user releases left mouse button, or any other mouse button is pressed,
  // ignore and stop resizing.
  if (!event->IsOnlyLeftMouseButton() ||
      event->type() == ui::ET_MOUSE_RELEASED) {
    if (is_resizing_) {
      is_resizing_ = false;
      ConfineMouseCursorOutsideViewport();
    }
    return;
  }
  float new_screen_height_divisor =
      root_bounds.height() / std::max(1.0f, root_y + resize_offset_);

  switch (event->type()) {
    case ui::ET_MOUSE_PRESSED:
      // User clicks within separator to start resizing Docked Magnifier.
      // Subtracting one is needed to capture when mouse is at the very top.
      if (!is_resizing_ && cursor_is_over_resizer) {
        resize_offset_ = magnifier_height - root_y;
        is_resizing_ = true;
        RootWindowController::ForWindow(current_source_root_window_)
            ->ash_host()
            ->ConfineCursorToRootWindow();
      }
      break;
    case ui::ET_MOUSE_DRAGGED:
      // User continues holding and drags separator to resize Docked Magnifier.
      if (is_resizing_) {
        SetScreenHeightDivisor(base::clamp(new_screen_height_divisor,
                                           kMinScreenHeightDivisor,
                                           kMaxScreenHeightDivisor));
        OnDisplayConfigurationChanged();
      }
      break;
    default:
      break;
  }
}

void DockedMagnifierController::SwitchCurrentSourceRootWindowIfNeeded(
    aura::Window* new_root_window,
    bool update_old_root_workarea) {
  if (current_source_root_window_ == new_root_window)
    return;

  aura::Window* old_root_window = current_source_root_window_;
  current_source_root_window_ = new_root_window;

  // Current window changes means the minimum height of the point of interest is
  // no longer valid.
  is_minimum_point_of_interest_height_valid_ = false;

  if (old_root_window) {
    if (update_old_root_workarea)
      SetViewportHeightInWorkArea(old_root_window, 0);

    // Reset mouse cursor confinement to default.
    RootWindowController::ForWindow(old_root_window)
        ->ash_host()
        ->ConfineCursorToRootWindow();
  }

  separator_layer_ = nullptr;

  if (viewport_widget_) {
    viewport_widget_->RemoveObserver(this);
    viewport_widget_->Close();
    viewport_widget_ = nullptr;
  }

  viewport_background_layer_ = nullptr;
  viewport_magnifier_layer_ = nullptr;

  if (!current_source_root_window_) {
    // No need to create a new magnifier viewport.
    return;
  }

  CreateMagnifierViewport();

  auto* magnified_container = current_source_root_window_->GetChildById(
      kShellWindowId_MagnifiedContainer);
  viewport_magnifier_layer_->SetShowReflectedLayerSubtree(
      magnified_container->layer());
}

void DockedMagnifierController::InitFromUserPrefs() {
  DCHECK(active_user_pref_service_);

  pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
  pref_change_registrar_->Init(active_user_pref_service_);
  pref_change_registrar_->Add(
      prefs::kDockedMagnifierEnabled,
      base::BindRepeating(&DockedMagnifierController::OnEnabledPrefChanged,
                          base::Unretained(this)));
  pref_change_registrar_->Add(
      prefs::kDockedMagnifierScale,
      base::BindRepeating(&DockedMagnifierController::OnScalePrefChanged,
                          base::Unretained(this)));
  pref_change_registrar_->Add(
      prefs::kAccessibilityScreenMagnifierEnabled,
      base::BindRepeating(
          &DockedMagnifierController::OnFullscreenMagnifierEnabledPrefChanged,
          base::Unretained(this)));

  OnEnabledPrefChanged();
}

void DockedMagnifierController::OnEnabledPrefChanged() {
  // When switching from the signin screen to a newly created profile while the
  // Docked Magnifier is enabled, the prefs will be copied from the signin
  // profile to the user profile, and the Docked Magnifier will remain enabled.
  // We don't want to redo the below operations if the status doesn't change,
  // for example readding the same observer to the WindowTreeHostManager will
  // cause a crash on DCHECK on debug builds.
  const bool current_enabled = !!current_source_root_window_;
  const bool new_enabled = GetEnabled();
  if (current_enabled == new_enabled)
    return;

  // Toggling the status of the docked magnifier, changes the display's work
  // area. However, display's work area changes are not allowed while overview
  // mode is active (See https://crbug.com/834400). For this reason, we exit
  // overview mode, before we actually update the state of docked magnifier
  // below. https://crbug.com/894256.
  Shell* shell = Shell::Get();
  auto* overview_controller = shell->overview_controller();
  if (overview_controller->InOverviewSession()) {
    // |OverviewController::EndOverview| fails (returning false) in certain
    // cases involving tablet split view mode. We can guarantee success by
    // ensuring that tablet split view mode is not in session.
    auto* split_view_controller =
        SplitViewController::Get(Shell::GetPrimaryRootWindow());
    if (split_view_controller->InTabletSplitViewMode()) {
      split_view_controller->EndSplitView(
          SplitViewController::EndReason::kNormal);
    }
    overview_controller->EndOverview(
        OverviewEndAction::kEnabledDockedMagnifier);
  }

  if (new_enabled) {
    // Enabling the Docked Magnifier disables the Fullscreen Magnifier.
    SetFullscreenMagnifierEnabled(false);
    // Calling refresh will result in the creation of the magnifier viewport and
    // its associated layers.
    Refresh();
    // Make sure we are in front of the fullscreen magnifier which also handles
    // scroll events.
    shell->AddAccessibilityEventHandler(
        this, AccessibilityEventHandlerManager::HandlerType::kDockedMagnifier);
    shell->window_tree_host_manager()->AddObserver(this);
  } else {
    shell->window_tree_host_manager()->RemoveObserver(this);
    shell->RemoveAccessibilityEventHandler(this);

    // Setting the current root window to |nullptr| will remove the viewport and
    // all its associated layers.
    SwitchCurrentSourceRootWindowIfNeeded(nullptr,
                                          true /* update_old_root_workarea */);
  }

  // Update the green checkmark status in the accessibility menu in the system
  // tray.
  shell->accessibility_controller()->NotifyAccessibilityStatusChanged();

  // We use software composited mouse cursor so that it can be mirrored into the
  // magnifier viewport.
  shell->UpdateCursorCompositingEnabled();
}

void DockedMagnifierController::OnScalePrefChanged() {
  if (GetEnabled()) {
    // Invalidate the cached minimum height of the point of interest since the
    // change in scale changes that height.
    is_minimum_point_of_interest_height_valid_ = false;
    Refresh();
  }
}

void DockedMagnifierController::OnFullscreenMagnifierEnabledPrefChanged() {
  // Enabling the Fullscreen Magnifier disables the Docked Magnifier.
  if (GetFullscreenMagnifierEnabled())
    SetEnabled(false);
}

void DockedMagnifierController::Refresh() {
  DCHECK(GetEnabled());
  CenterOnPoint(GetCursorScreenPoint());
}

void DockedMagnifierController::CreateMagnifierViewport() {
  DCHECK(GetEnabled());
  DCHECK(current_source_root_window_);

  const auto viewport_bounds = magnifier_utils::GetViewportWidgetBoundsInRoot(
      current_source_root_window_, GetScreenHeightDivisor());

  // 1- Create the viewport widget.
  viewport_widget_ = new views::Widget;
  views::Widget::InitParams params(
      views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
  params.activatable = views::Widget::InitParams::Activatable::kNo;
  params.accept_events = false;
  params.bounds = viewport_bounds;
  params.opacity = views::Widget::InitParams::WindowOpacity::kOpaque;
  aura::Window* const viewport_parent =
      GetViewportParentContainerForRoot(current_source_root_window_);
  params.parent = viewport_parent;
  params.name = kDockedMagnifierViewportWindowName;
  viewport_widget_->Init(std::move(params));

  // 2- Create the separator layer right below the viwport widget, parented to
  //    the layer of the root window.
  separator_layer_ = std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
  separator_layer_->SetColor(SK_ColorBLACK);
  separator_layer_->SetBounds(
      SeparatorBoundsFromViewportBounds(viewport_bounds));
  aura::Window* const separator_parent =
      ::features::IsDockedMagnifierResizingEnabled()
          ? GetViewportParentContainerForDivider(current_source_root_window_)
          : viewport_parent;
  separator_parent->layer()->Add(separator_layer_.get());

  // 3- Create a background layer that will show a dark gray color behind the
  //    magnifier layer. It has the same bounds as the viewport.
  viewport_background_layer_ =
      std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
  viewport_background_layer_->SetColor(SK_ColorDKGRAY);
  viewport_background_layer_->SetBounds(viewport_bounds);
  aura::Window* viewport_window = viewport_widget_->GetNativeView();
  ui::Layer* viewport_layer = viewport_window->layer();
  viewport_layer->Add(viewport_background_layer_.get());

  // 4- Create the layer in which the contents of the screen will be mirrored
  //    and magnified.
  viewport_magnifier_layer_ =
      std::make_unique<ui::Layer>(ui::LAYER_SOLID_COLOR);
  // There are situations that the content rect for the magnified container gets
  // larger than its bounds (e.g. shelf stretches beyond the screen to allow it
  // being dragged up, or contents of mouse pointer might go beyond screen when
  // the pointer is at the edges of the screen). To avoid this extra content
  // becoming visible in the magnifier, magnifier layer should clip its contents
  // to its bounds.
  viewport_magnifier_layer_->SetMasksToBounds(true);
  viewport_layer->Add(viewport_magnifier_layer_.get());
  viewport_layer->SetMasksToBounds(true);

  // 5- Update the workarea of the current screen such that an area enough to
  //    contain the viewport and the separator is allocated at the top of the
  //    screen.
  SetViewportHeightInWorkArea(current_source_root_window_,
                              GetTotalMagnifierHeight());

  // 6- Confine the mouse cursor within the remaining part of the display.
  ConfineMouseCursorOutsideViewport();

  // 7- Show the widget, which can trigger events to request movement of the
  // viewport now that all internal state has been created.
  viewport_widget_->AddObserver(this);
  viewport_widget_->Show();
}

void DockedMagnifierController::MaybeCachePointOfInterestMinimumHeight(
    aura::WindowTreeHost* host) {
  DCHECK(GetEnabled());
  DCHECK(current_source_root_window_);
  DCHECK(host);

  if (is_minimum_point_of_interest_height_valid_)
    return;

  // Adjust the point of interest so that we don't end up magnifying the
  // magnifier. This means we don't allow the point of interest to go beyond a
  // minimum y-coordinate value. Here's how we find that minimum value:
  //
  // +-----------------+     +-----------------------------------+
  // |     Viewport    |     |                                   |
  // +====separator====+     |        Magnified Viewport         |
  // |                (+) b  |                                   |
  // |                 |     +==============separator===========(+) A
  // |                 |     |                  Distance (D) --> |
  // |                 |     |                                  (+) B
  // +-----------------+     |                                   |
  //    Screen in Non        |                                   |
  //   Magnified Space       |                                   |
  //                         |                                   |
  //                         |                                   |
  //                         +-----------------------------------+
  //                               Screen in Magnified Space
  //                     (the contents of |viewport_magnifier_layer_|)
  //
  // Problem: Find the height of the point of interest (b) in the non-magnified
  //          coordinates space, which corresponds to the height of point (B) in
  //          the magnified coordinates space, such that when point (A) is
  //          translated from the magnified coordinates space to the non-
  //          magnified coordinates space, its y coordinate is 0 (i.e. aligns
  //          with the top of the magnifier viewport).
  //
  // 1- The height of Point (A) in the magnified space is the bottom of the
  //    entire magnifier (which is actually the bottom of the separator) in the
  //    magnified coordinates space.
  //    Note that the magnified space is in pixels. This point should be
  //    translated such that its y-coordiante is not greater than 0 (in the non-
  //    magnified coordinates space), otherwise the magnifier will magnify and
  //    mirror itself.
  // 2- Point (B) is the scaled point of interest in the magnified space. The
  //    point of interest is always translated to the center point of the
  //    viewport. Hence, if point (A) goes to y = 0, and point (B) goes to a
  //    height equals to the height of the center point of the viewport,
  //    therefore means distance (D) = viewport_center_point.y().
  // 3- Now that we found the height of point (B) in the magnified space,
  //    find the the height of point (b) which is the corresponding height in
  //    the non-magnified space. This height is the minimum height below which
  //    the point of interest may not go.

  const gfx::Rect viewport_bounds =
      magnifier_utils::GetViewportWidgetBoundsInRoot(
          current_source_root_window_, GetScreenHeightDivisor());

  // 1- Point (A)'s height.
  // Note we use a Vector3dF to actually represent a 2D point. The reason is
  // Vector3dF provides an API to get the Length() of the vector without
  // converting the object to another temporary object. We need to get the
  // Length() rather than y() because screen rotation transform can make the
  // height we are interested in either x() or y() depending on the rotation
  // angle, so we just simply use Length().
  // Note: Why transform the point to the magnified scale and back? The reason
  // is that we need to go through the root window transform to go to the pixel
  // space. This will account for device scale factors, screen rotations, and
  // any other transforms that we cannot anticipate ourselves.
  gfx::Vector3dF scaled_magnifier_bottom_in_pixels(
      0.0f, viewport_bounds.bottom() + kSeparatorHeight, 0.0f);
  const float scale = GetScale();
  scaled_magnifier_bottom_in_pixels.Scale(scale);

  // 2- Point (B)'s height.
  const gfx::PointF viewport_center_point(viewport_bounds.CenterPoint());
  gfx::Vector3dF minimum_height_vector(
      0.0f,
      viewport_center_point.y() + scaled_magnifier_bottom_in_pixels.Length(),
      0.0f);

  // 3- Back to non-magnified space to get point (b)'s height.
  minimum_height_vector.Scale(1 / scale);
  minimum_point_of_interest_height_ = minimum_height_vector.Length();
  is_minimum_point_of_interest_height_valid_ = true;
}

void DockedMagnifierController::ConfineMouseCursorOutsideViewport() {
  DCHECK(current_source_root_window_);

  gfx::Rect confine_bounds =
      current_source_root_window_->GetBoundsInRootWindow();
  const auto viewport_bounds = magnifier_utils::GetViewportWidgetBoundsInRoot(
      current_source_root_window_, GetScreenHeightDivisor());
  const int docked_height = ::features::IsDockedMagnifierResizingEnabled()
                                ? viewport_bounds.height()
                                : separator_layer_->bounds().bottom();
  confine_bounds.Offset(0, docked_height);
  confine_bounds.set_height(confine_bounds.height() - docked_height);
  RootWindowController::ForWindow(current_source_root_window_)
      ->ash_host()
      ->ConfineCursorToBoundsInRoot(confine_bounds);
}

}  // namespace ash


text:
namespace offline_pages {

class PrefetchDispatcher;

// Interface to import the downloaded archive such that it can be rendered as
// offline page.
class PrefetchImporter {
 public:
  explicit PrefetchImporter(PrefetchDispatcher* dispatcher);

  PrefetchImporter(const PrefetchImporter&) = delete;
  PrefetchImporter& operator=(const PrefetchImporter&) = delete;

  virtual ~PrefetchImporter() = default;

  // Imports the downloaded archive by moving the file into archive directory
  // and creating an entry in the offline metadata database. When the import
  // is done, successfully or not, PrefetchDispatcher::ArchiveImported will be
  // called. MarkImportCompleted should then be called to remove the import from
  // the outstanding list once dispatch finishes processing it.
  virtual void ImportArchive(const PrefetchArchiveInfo& info) = 0;

  // Marks an import with |offline_id| as completed.
  // Note that this should be done inside a task, or reconciliation could be
  // affected by races.
  virtual void MarkImportCompleted(int64_t offline_id) = 0;

  // Returns a list of offline ids of those imports that are still not
  // completely done. An import will be removed from this list only after
  // the consumer reacts to the archive imported event and calls
  // MarkImportCompleted.
  virtual std::set<int64_t> GetOutstandingImports() const = 0;

 protected:
  void NotifyArchiveImported(int64_t offline_id, bool success);

 private:
  raw_ptr<PrefetchDispatcher> dispatcher_;
};

}  // namespace offline_pages

#endif  // COMPONENTS_OFFLINE_PAGES_CORE_PREFETCH_PREFETCH_IMPORTER_H_

redpajama_set_name: RedPajamaGithub


text:
/// <reference path='../tsd.d.ts' />
var app;
(function (app) {
    var AppController = (function () {
        function AppController($scope, scenarioService, $location) {
            this.scope = $scope;
            this.tabs = [];
            this.scenarioService = scenarioService;
            this.loadInitialData();
        }
        AppController.prototype.loadInitialData = function () {
            var me = this;
            this.scenarioService.loadInitialData().then(function (data) {
                me.scenarios = data.scenarios;
                me.scenarioReferenceData = data.scenarioReferenceData;
            });
        };
        AppController.prototype.removeTab = function (index) {
            this.tabs.splice(index, 1);
        };
        AppController.prototype.scenarioDetail = function (scenario) {
            var value = {
                title: scenario.name,
                scenario: scenario,
                active: true
            };
            this.tabs.push(value);
        };
        AppController.prototype.newScenario = function () {
            var value = {
                title: "New Scenario",
                scenario: new app.Scenario(),
                active: true
            };
            value.scenario.name = "New Scenario";
            this.tabs.push(value);
        };
        AppController.prototype.addTestScenario = function () {
            var refData = this.scenarioReferenceData;
            var testScenario = new app.Scenario();
            testScenario.id = this.scenarios.length + 1;
            testScenario.name = "Test Scenario " + testScenario.id;
            testScenario.description = "Test scenario " + testScenario.id + " description.";
            testScenario.dateUpdated = new Date();
            testScenario.probability = refData.probabilities[this.getRandomInt(0, refData.probabilities.length - 1)];
            testScenario.impactCost = this.getRandomInt(100, 1000);
            testScenario.impactLength = this.getRandomInt(5, 20);
            testScenario.planEffectiveness = refData.effectivenessRatings[this.getRandomInt(0, refData.effectivenessRatings.length - 1)];
            testScenario.totalImpact = this.getRandomInt(500, 10000);
            var item = new app.ScenarioItem();
            item.id = this.createUUID();
            item.itemDescription = "Scenario Item " + item.id;
            item.cost = this.getRandomInt(100, 500);
            item.timeToRecover = this.getRandomInt(5, 20);
            item.impactSeverity = refData.revenueImpacts[this.getRandomInt(0, refData.revenueImpacts.length - 1)];
            item.affectedItem = refData.affectedItems[this.getRandomInt(0, refData.affectedItems.length - 1)];
            testScenario.scenarioItems.push(item);
            this.scenarios.push(testScenario);
            this.scenarioService.saveScenarios(this.scenarios);
        };
        AppController.prototype.getRandomInt = function (min, max) {
            return Math.floor(Math.random() * (max - min + 1)) + min;
        };
        AppController.prototype.createUUID = function () {
            // http://www.ietf.org/rfc/rfc4122.txt
            var s = [];
            var hexDigits = "0123456789abcdef";
            for (var i = 0; i < 36; i++) {
                s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
            }
            s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
            s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
            s[8] = s[13] = s[18] = s[23] = "-";
            var uuid = s.join("");
            return uuid;
        };
        AppController.$inject = [
            '$scope',
            'scenarioService',
            '$location'
        ];
        return AppController;
    })();
    app.AppController = AppController;
})(app || (app = {}));

/// <reference path='../tsd.d.ts' />
var app;
(function (app) {
    var ScenarioController = (function () {
        function ScenarioController($scope, scenarioService) {
            this.$scope = $scope;
            this.scope = $scope;
            this.scenarioService = scenarioService;
            this.scenarioReferenceData = scenarioService.scenarioReferenceData;
        }
        ScenarioController.prototype.saveScenario = function () {
            this.isEdit = false;
        };
        ScenarioController.prototype.editScenario = function () {
            this.isEdit = true;
        };
        ScenarioController.$inject = [
            '$scope',
            'scenarioService'
        ];
        return ScenarioController;
    })();
    app.ScenarioController = ScenarioController;
})(app || (app = {}));

var app;
(function (app) {
    var ScenarioService = (function () {
        function ScenarioService($httpService, $q) {
            this.STORAGE_ID = 'phoenix-angularjs-typescript';
            this.httpService = $httpService;
            this.promiseService = $q;
        }
        ScenarioService.prototype.loadScenarios = function () {
            var deferred = this.promiseService.defer();
            deferred.resolve(JSON.parse(localStorage.getItem(this.STORAGE_ID) || '[]'));
            return deferred.promise;
        };
        ScenarioService.prototype.saveScenarios = function (scenarios) {
            var deferred = this.promiseService.defer();
            localStorage.setItem(this.STORAGE_ID, JSON.stringify(scenarios));
            deferred.resolve();
            return deferred.promise;
        };
        ScenarioService.prototype.loadProbabilities = function () {
            var deferred = this.promiseService.defer();
            var _this = this;
            this.httpService.get("data/probabilities.json").then(function (result) {
                deferred.resolve(result.data.data);
            });
            return deferred.promise;
        };
        ScenarioService.prototype.loadRevenueImpacts = function () {
            var deferred = this.promiseService.defer();
            var _this = this;
            this.httpService.get("data/revenueImpacts.json").then(function (result) {
                deferred.resolve(result.data.data);
            });
            return deferred.promise;
        };
        ScenarioService.prototype.loadAffectedItems = function () {
            var deferred = this.promiseService.defer();
            var _this = this;
            this.httpService.get("data/affectedItems.json").then(function (result) {
                deferred.resolve(result.data.data);
            });
            return deferred.promise;
        };
        ScenarioService.prototype.loadEffectivenessRatings = function () {
            var deferred = this.promiseService.defer();
            var _this = this;
            this.httpService.get("data/effectivenessRatings.json").then(function (result) {
                deferred.resolve(result.data.data);
            });
            return deferred.promise;
        };
        ScenarioService.prototype.loadInitialData = function () {
            var deferred = this.promiseService.defer();
            var me = this;
            var finalResult = new app.InitialDataMap();
            me.scenarioReferenceData = new app.ScenarioReferenceData();
            this.promiseService.all([
                this.loadProbabilities(),
                this.loadRevenueImpacts(),
                this.loadAffectedItems(),
                this.loadEffectivenessRatings(),
                this.loadScenarios()
            ]).then(function (result) {
                me.scenarioReferenceData.probabilities = result[0];
                me.scenarioReferenceData.revenueImpacts = result[1];
                me.scenarioReferenceData.affectedItems = result[2];
                me.scenarioReferenceData.effectivenessRatings = result[3];
                finalResult.scenarios = result[4];
                finalResult.scenarioReferenceData = me.scenarioReferenceData;
                deferred.resolve(finalResult);
            });
            return deferred.promise;
        };
        ScenarioService.$inject = [
            '$http',
            '$q'
        ];
        return ScenarioService;
    })();
    app.ScenarioService = ScenarioService;
})(app || (app = {}));

/// <reference path='../tsd.d.ts' />
/// <reference path="../controllers/AppController.ts" />
/// <reference path="../controllers/ScenarioController.ts" />
/// <reference path="../services/ScenarioService.ts" />
var app;
(function (app) {
    'use strict';
    angular.module("angularPhoenix", [
        'ui.bootstrap',
        'ngRoute',
        'dcbClearInput',
        'myApp.views.view1',
        'myApp.views.view2',
        'myApp.version'
    ]).config(['$routeProvider', function ($routeProvider) {
        $routeProvider.otherwise({ redirectTo: '/views/view1' });
    }]).controller('AppController', app.AppController).controller('ScenarioController', app.ScenarioController).service('scenarioService', app.ScenarioService);
    ;
})(app || (app = {}));

/// <reference path='../tsd.d.ts' />

/// <reference path='../tsd.d.ts' />

/// <reference path='../tsd.d.ts' />

var app;
(function (app) {
    var AffectedItem = (function () {
        function AffectedItem() {
        }
        return AffectedItem;
    })();
    app.AffectedItem = AffectedItem;
})(app || (app = {}));

var app;
(function (app) {
    var EffectivenessRating = (function () {
        function EffectivenessRating() {
        }
        return EffectivenessRating;
    })();
    app.EffectivenessRating = EffectivenessRating;
})(app || (app = {}));

var app;
(function (app) {
    var InitialDataMap = (function () {
        function InitialDataMap() {
        }
        return InitialDataMap;
    })();
    app.InitialDataMap = InitialDataMap;
})(app || (app = {}));

var app;
(function (app) {
    var Probability = (function () {
        function Probability() {
        }
        return Probability;
    })();
    app.Probability = Probability;
})(app || (app = {}));

var app;
(function (app) {
    var RevenueImpact = (function () {
        function RevenueImpact() {
        }
        return RevenueImpact;
    })();
    app.RevenueImpact = RevenueImpact;
})(app || (app = {}));

var app;
(function (app) {
    var Scenario = (function () {
        function Scenario() {
            this.scenarioItems = [];
        }
        return Scenario;
    })();
    app.Scenario = Scenario;
})(app || (app = {}));

var app;
(function (app) {
    var ScenarioItem = (function () {
        function ScenarioItem() {
        }
        return ScenarioItem;
    })();
    app.ScenarioItem = ScenarioItem;
})(app || (app = {}));

var app;
(function (app) {
    var ScenarioReferenceData = (function () {
        function ScenarioReferenceData() {
        }
        return ScenarioReferenceData;
    })();
    app.ScenarioReferenceData = ScenarioReferenceData;
})(app || (app = {}));

/// <reference path='../controllers/MainController.ts' />

/// <reference path='../../../tsd.d.ts' />
/// <reference path='../interfaces/IMainScope' />
var main;
(function (main) {
    'use strict';
    /**
     * The main controller for the app. The controller:
     * - retrieves and persists the model via the todoStorage service
     * - exposes the model to the template and provides event handlers
     */
    var MainController = (function () {
        // dependencies are injected via AngularJS $injector
        // controller's name is registered in Application.ts and specified from ng-controller attribute in index.html
        function MainController($scope, $location) {
            this.$scope = $scope;
            this.$location = $location;
        }
        // $inject annotation.
        // It provides $injector with information about dependencies to be injected into constructor
        // it is better to have it close to the constructor, because the parameters must match in count and type.
        // See http://docs.angularjs.org/guide/di
        MainController.$inject = [
            '$scope',
            '$location'
        ];
        return MainController;
    })();
    main.MainController = MainController;
})(main || (main = {}));

//# sourceMappingURL=angular-phoenix-all.js.map


text:
"use strict";

var uriTemplates = require('./uri-templates');
var assert = require('proclaim');

describe("Basic tests", function () {

	it("Basic substitution", function () {
		var template = uriTemplates("/prefix/{var}/suffix");
		var uri = template.fillFromObject({var: "test"});

		assert.strictEqual(uri, "/prefix/test/suffix");
	});
});

function createTests(title, examplesDoc) {
	describe(title + "(substitution)", function () {
		for (var sectionTitle in examplesDoc) {
			var exampleSet = examplesDoc[sectionTitle];
			describe(sectionTitle, function () {
				var variables = exampleSet.variables;
				var variableFunction = function (varName) {
					return variables[varName];
				};

				for (var i = 0; i < exampleSet.testcases.length; i++) {
					var pair = exampleSet.testcases[i];

					(function (templateString, expected) {
						it(templateString, function () {
							var template = uriTemplates(templateString);
							var actualUri = template.fillFromObject(variables);
							if (typeof expected == "string") {
								assert.strictEqual(actualUri, expected);
							} else {
								assert.includes(expected, actualUri);
							}
						});
					})(pair[0], pair[1]);
				}
			});
		}
	});

	var unguessable = {};

	describe(title + " (de-substitution)", function () {
		for (var sectionTitle in examplesDoc) {
			var exampleSet = examplesDoc[sectionTitle];
			describe(sectionTitle, function () {
				for (var i = 0; i < exampleSet.testcases.length; i++) {
					var pair = exampleSet.testcases[i];

					(function (templateString, expected, exampleSet) {
						if (unguessable[templateString]) {
							return;
						}

						it(templateString, function () {
							var original = (typeof expected == 'string') ? expected : expected[0];
							var template = uriTemplates(templateString);

							var guessedVariables = template.fromUri(original);
							assert.isObject(guessedVariables);

							var reconstructed = template.fillFromObject(guessedVariables);

							if (typeof expected == "string") {
								assert.strictEqual(reconstructed, expected);
							} else {
								assert.includes(expected, reconstructed);
							}
						});
					})(pair[0], pair[1], exampleSet);
				}
			});
		}
	});
}

createTests("Spec examples by section", require('./uritemplate-test/spec-examples-by-section.json'));
createTests("Extended examples", require('./uritemplate-test/extended-tests.json'));

createTests("Custom examples 1", require('./test/custom-tests.json'));
createTests("Custom examples 2", require('./test/custom-tests-2.json'));

require('./test/custom-tests.js');


text:
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you 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.

#include <time.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/common.h>

#include "olap/field.h"
#include "olap/column_predicate.h"
#include "olap/comparison_predicate.h"
#include "runtime/mem_pool.h"
#include "runtime/string_value.hpp"
#include "runtime/vectorized_row_batch.h"
#include "util/logging.h"

namespace doris {

namespace datetime {

static uint24_t to_date_timestamp(const char* date_string) {
    tm time_tm;
    strptime(date_string, "%Y-%m-%d", &time_tm);

    int value = (time_tm.tm_year + 1900) * 16 * 32
        + (time_tm.tm_mon + 1) * 32
        + time_tm.tm_mday;
    return uint24_t(value);
}

static uint64_t to_datetime_timestamp(const std::string& value_string) {
    tm time_tm;
    strptime(value_string.c_str(), "%Y-%m-%d %H:%M:%S", &time_tm);

    uint64_t value = ((time_tm.tm_year + 1900) * 10000L
            + (time_tm.tm_mon + 1) * 100L
            + time_tm.tm_mday) * 1000000L
        + time_tm.tm_hour * 10000L
        + time_tm.tm_min * 100L
        + time_tm.tm_sec;

    return value;
}

static std::string to_date_string(uint24_t& date_value) {
    tm time_tm;
    int value = date_value;
    memset(&time_tm, 0, sizeof(time_tm));
    time_tm.tm_mday = static_cast<int>(value & 31);
    time_tm.tm_mon = static_cast<int>(value >> 5 & 15) - 1;
    time_tm.tm_year = static_cast<int>(value >> 9) - 1900;
    char buf[20] = {'\0'};
    strftime(buf, sizeof(buf), "%Y-%m-%d", &time_tm);
    return std::string(buf);
}

static std::string to_datetime_string(uint64_t& datetime_value) {
    tm time_tm;
    int64_t part1 = (datetime_value / 1000000L);
    int64_t part2 = (datetime_value - part1 * 1000000L);

    time_tm.tm_year = static_cast<int>((part1 / 10000L) % 10000) - 1900;
    time_tm.tm_mon = static_cast<int>((part1 / 100) % 100) - 1;
    time_tm.tm_mday = static_cast<int>(part1 % 100);

    time_tm.tm_hour = static_cast<int>((part2 / 10000L) % 10000);
    time_tm.tm_min = static_cast<int>((part2 / 100) % 100);
    time_tm.tm_sec = static_cast<int>(part2 % 100);

    char buf[20] = {'\0'};
    strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &time_tm);
    return std::string(buf);
}

};

#define TEST_PREDICATE_DEFINITION(CLASS_NAME) \
class CLASS_NAME : public testing::Test { \
public: \
    CLASS_NAME() : _vectorized_batch(NULL) { \
        _mem_tracker.reset(new MemTracker(-1)); \
        _mem_pool.reset(new MemPool(_mem_tracker.get())); \
    } \
    ~CLASS_NAME() {\
        if (_vectorized_batch != NULL) { \
            delete _vectorized_batch; \
        } \
    } \
    void SetTabletSchema(std::string name, \
            const std::string& type, const std::string& aggregation, \
            uint32_t length, bool is_allow_null, bool is_key, TabletSchema* tablet_schema) { \
        TabletSchemaPB tablet_schema_pb; \
        static int id = 0; \
        ColumnPB* column = tablet_schema_pb.add_column(); \
        column->set_unique_id(++id); \
        column->set_name(name); \
        column->set_type(type); \
        column->set_is_key(is_key); \
        column->set_is_nullable(is_allow_null); \
        column->set_length(length); \
        column->set_aggregation(aggregation); \
        column->set_precision(1000); \
        column->set_frac(1000); \
        column->set_is_bf_column(false); \
        tablet_schema->init_from_pb(tablet_schema_pb); \
    } \
    void InitVectorizedBatch(const TabletSchema* tablet_schema, \
                             const std::vector<uint32_t>&ids, \
                             int size) { \
        _vectorized_batch = new VectorizedRowBatch(tablet_schema, ids, size); \
        _vectorized_batch->set_size(size); \
    } \
    std::unique_ptr<MemTracker> _mem_tracker; \
    std::unique_ptr<MemPool> _mem_pool; \
    VectorizedRowBatch* _vectorized_batch; \
}; \

TEST_PREDICATE_DEFINITION(TestEqualPredicate)
TEST_PREDICATE_DEFINITION(TestLessPredicate)

#define TEST_EQUAL_PREDICATE(TYPE, TYPE_NAME, FIELD_TYPE) \
TEST_F(TestEqualPredicate, TYPE_NAME##_COLUMN) { \
    TabletSchema tablet_schema; \
    SetTabletSchema(std::string("TYPE_NAME##_COLUMN"), FIELD_TYPE, \
                 "REPLACE", 1, false, true, &tablet_schema); \
    int size = 10; \
    std::vector<uint32_t> return_columns; \
    for (int i = 0; i < tablet_schema.num_columns(); ++i) { \
        return_columns.push_back(i); \
    } \
    InitVectorizedBatch(&tablet_schema, return_columns, size); \
    ColumnVector* col_vector = _vectorized_batch->column(0); \
    \
    /* for no nulls */ \
    col_vector->set_no_nulls(true); \
    TYPE* col_data = reinterpret_cast<TYPE*>(_mem_pool->allocate(size * sizeof(TYPE))); \
    col_vector->set_col_data(col_data); \
    for (int i = 0; i < size; ++i) { \
        *(col_data + i) = i; \
    } \
    TYPE value = 5; \
    ColumnPredicate* pred = new EqualPredicate<TYPE>(0, value); \
    pred->evaluate(_vectorized_batch); \
    ASSERT_EQ(_vectorized_batch->size(), 1); \
    uint16_t* sel = _vectorized_batch->selected(); \
    ASSERT_EQ(*(col_data + sel[0]), 5); \
    \
    /* for has nulls */ \
    col_vector->set_no_nulls(false); \
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size)); \
    memset(is_null, 0, size); \
    col_vector->set_is_null(is_null); \
    for (int i = 0; i < size; ++i) { \
        if (i % 2 == 0) { \
            is_null[i] = true; \
        } else { \
            *(col_data + i) = i; \
        } \
    } \
    _vectorized_batch->set_size(size); \
    _vectorized_batch->set_selected_in_use(false); \
    pred->evaluate(_vectorized_batch); \
    ASSERT_EQ(_vectorized_batch->size(), 1); \
    sel = _vectorized_batch->selected(); \
    ASSERT_EQ(*(col_data + sel[0]), 5); \
} \

TEST_EQUAL_PREDICATE(int8_t, TINYINT, "TINYINT")
TEST_EQUAL_PREDICATE(int16_t, SMALLINT, "SMALLINT")
TEST_EQUAL_PREDICATE(int32_t, INT, "INT")
TEST_EQUAL_PREDICATE(int64_t, BIGINT, "BIGINT")
TEST_EQUAL_PREDICATE(int128_t, LARGEINT, "LARGEINT")

TEST_F(TestEqualPredicate, FLOAT_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("FLOAT_COLUMN"), "FLOAT",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 10;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    float* col_data = reinterpret_cast<float*>(_mem_pool->allocate(size * sizeof(float)));
    col_vector->set_col_data(col_data);
    for (int i = 0; i < size; ++i) {
        *(col_data + i) = i;
    }
    float value = 5.0;
    ColumnPredicate* pred = new EqualPredicate<float>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    uint16_t* sel = _vectorized_batch->selected();
    ASSERT_FLOAT_EQ(*(col_data + sel[0]), 5.0);

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            *(col_data + i) = i;
        }
    }
    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    sel = _vectorized_batch->selected();
    ASSERT_FLOAT_EQ(*(col_data + sel[0]), 5.0);
}

TEST_F(TestEqualPredicate, DOUBLE_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("DOUBLE_COLUMN"), "DOUBLE",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 10;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    double* col_data = reinterpret_cast<double*>(_mem_pool->allocate(size * sizeof(double)));
    col_vector->set_col_data(col_data);
    for (int i = 0; i < size; ++i) {
        *(col_data + i) = i;
    }
    double value = 5.0;
    ColumnPredicate* pred = new EqualPredicate<double>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    uint16_t* sel = _vectorized_batch->selected();
    ASSERT_DOUBLE_EQ(*(col_data + sel[0]), 5.0);

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            *(col_data + i) = i;
        }
    }
    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    sel = _vectorized_batch->selected();
    ASSERT_DOUBLE_EQ(*(col_data + sel[0]), 5.0);
}

TEST_F(TestEqualPredicate, DECIMAL_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("DECIMAL_COLUMN"), "DECIMAL",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 10;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    decimal12_t* col_data = reinterpret_cast<decimal12_t*>(_mem_pool->allocate(size * sizeof(decimal12_t)));
    col_vector->set_col_data(col_data);
    for (int i = 0; i < size; ++i) {
        (*(col_data + i)).integer = i;
        (*(col_data + i)).fraction = i;
    }
    decimal12_t value(5, 5);
    ColumnPredicate* pred = new EqualPredicate<decimal12_t>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    uint16_t* sel = _vectorized_batch->selected();
    ASSERT_EQ(*(col_data + sel[0]), value);

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            (*(col_data + i)).integer = i;
            (*(col_data + i)).fraction = i;
        }
    }

    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    sel = _vectorized_batch->selected();
    ASSERT_EQ(*(col_data + sel[0]), value);
}

TEST_F(TestEqualPredicate, STRING_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("STRING_COLUMN"), "VARCHAR",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 10;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    StringValue* col_data = reinterpret_cast<StringValue*>(_mem_pool->allocate(size * sizeof(StringValue)));
    col_vector->set_col_data(col_data);

    char* string_buffer = reinterpret_cast<char*>(_mem_pool->allocate(55));
    for (int i = 0; i < size; ++i) {
        for (int j = 0; j <= i; ++j) {
            string_buffer[j] = 'a' + i;
        }
        (*(col_data + i)).len = i + 1;
        (*(col_data + i)).ptr = string_buffer;
        string_buffer += i + 1;
    }

    StringValue value;
    const char* value_buffer = "dddd";
    value.len = 4;
    value.ptr = const_cast<char*>(value_buffer);

    ColumnPredicate* pred = new EqualPredicate<StringValue>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    uint16_t* sel = _vectorized_batch->selected();
    ASSERT_EQ(sel[0], 3);
    ASSERT_EQ(*(col_data + sel[0]), value);

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    string_buffer = reinterpret_cast<char*>(_mem_pool->allocate(55));
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            for (int j = 0; j <= i; ++j) {
                string_buffer[j] = 'a' + i;
            }
            (*(col_data + i)).len = i + 1;
            (*(col_data + i)).ptr = string_buffer;
        }
        string_buffer += i + 1;
    }

    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    sel = _vectorized_batch->selected();
    ASSERT_EQ(*(col_data + sel[0]), value);
}

TEST_F(TestEqualPredicate, DATE_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("DATE_COLUMN"), "DATA",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 6;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    uint24_t* col_data = reinterpret_cast<uint24_t*>(_mem_pool->allocate(size * sizeof(uint24_t)));
    col_vector->set_col_data(col_data);

    std::vector<std::string> date_array;
    date_array.push_back("2017-09-07");
    date_array.push_back("2017-09-08");
    date_array.push_back("2017-09-09");
    date_array.push_back("2017-09-10");
    date_array.push_back("2017-09-11");
    date_array.push_back("2017-09-12");
    for (int i = 0; i < size; ++i) {
        uint24_t timestamp = datetime::to_date_timestamp(date_array[i].c_str());
        *(col_data + i) = timestamp;
    }

    uint24_t value = datetime::to_date_timestamp("2017-09-10");
    ColumnPredicate* pred = new EqualPredicate<uint24_t>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    uint16_t* sel = _vectorized_batch->selected();
    ASSERT_EQ(sel[0], 3);
    ASSERT_EQ(*(col_data + sel[0]), value);
    ASSERT_EQ(datetime::to_date_string(*(col_data + sel[0])), "2017-09-10");

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            uint24_t timestamp = datetime::to_date_timestamp(date_array[i].c_str());
            *(col_data + i) = timestamp;
        }
    }

    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    sel = _vectorized_batch->selected();
    ASSERT_EQ(*(col_data + sel[0]), value);
    ASSERT_EQ(datetime::to_date_string(*(col_data + sel[0])), "2017-09-10");
}

TEST_F(TestEqualPredicate, DATETIME_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("DATETIME_COLUMN"), "DATETIME",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 6;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    uint64_t* col_data = reinterpret_cast<uint64_t*>(_mem_pool->allocate(size * sizeof(uint64_t)));
    col_vector->set_col_data(col_data);

    std::vector<std::string> date_array;
    date_array.push_back("2017-09-07 00:00:00");
    date_array.push_back("2017-09-08 00:01:00");
    date_array.push_back("2017-09-09 00:00:01");
    date_array.push_back("2017-09-10 01:00:00");
    date_array.push_back("2017-09-11 01:01:00");
    date_array.push_back("2017-09-12 01:01:01");
    for (int i = 0; i < size; ++i) {
        uint64_t timestamp = datetime::to_datetime_timestamp(date_array[i].c_str());
        *(col_data + i) = timestamp;
    }

    uint64_t value = datetime::to_datetime_timestamp("2017-09-10 01:00:00");
    ColumnPredicate* pred = new EqualPredicate<uint64_t>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    uint16_t* sel = _vectorized_batch->selected();
    ASSERT_EQ(sel[0], 3);
    ASSERT_EQ(*(col_data + sel[0]), value);
    ASSERT_EQ(datetime::to_datetime_string(*(col_data + sel[0])), "2017-09-10 01:00:00");

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            uint64_t timestamp = datetime::to_datetime_timestamp(date_array[i].c_str());
            *(col_data + i) = timestamp;
        }
    }

    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    sel = _vectorized_batch->selected();
    ASSERT_EQ(*(col_data + sel[0]), value);
    ASSERT_EQ(datetime::to_datetime_string(*(col_data + sel[0])), "2017-09-10 01:00:00");
}

#define TEST_LESS_PREDICATE(TYPE, TYPE_NAME, FIELD_TYPE) \
TEST_F(TestLessPredicate, TYPE_NAME##_COLUMN) { \
    TabletSchema tablet_schema; \
    SetTabletSchema(std::string("TYPE_NAME_COLUMN"), FIELD_TYPE, \
                 "REPLACE", 1, false, true, &tablet_schema); \
    int size = 10; \
    std::vector<uint32_t> return_columns; \
    for (int i = 0; i < tablet_schema.num_columns(); ++i) { \
        return_columns.push_back(i); \
    } \
    InitVectorizedBatch(&tablet_schema, return_columns, size); \
    ColumnVector* col_vector = _vectorized_batch->column(0); \
    \
    /* for no nulls */ \
    col_vector->set_no_nulls(true); \
    TYPE* col_data = reinterpret_cast<TYPE*>(_mem_pool->allocate(size * sizeof(TYPE))); \
    col_vector->set_col_data(col_data); \
    for (int i = 0; i < size; ++i) { \
        *(col_data + i) = i; \
    } \
    TYPE value = 5; \
    ColumnPredicate* pred = new LessPredicate<TYPE>(0, value); \
    pred->evaluate(_vectorized_batch); \
    ASSERT_EQ(_vectorized_batch->size(), 5); \
    uint16_t* sel = _vectorized_batch->selected(); \
    TYPE sum = 0; \
    for (int i = 0; i < _vectorized_batch->size(); ++i) { \
        sum += *(col_data + sel[i]); \
    } \
    ASSERT_EQ(sum, 10); \
    \
    /* for has nulls */ \
    col_vector->set_no_nulls(false); \
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size)); \
    memset(is_null, 0, size); \
    col_vector->set_is_null(is_null); \
    for (int i = 0;i < size; ++i) {\
        if (i % 2 == 0) { \
            is_null[i] = true; \
        } else { \
            *(col_data + i) = i; \
        } \
    } \
    _vectorized_batch->set_size(size); \
    _vectorized_batch->set_selected_in_use(false); \
    pred->evaluate(_vectorized_batch); \
    ASSERT_EQ(_vectorized_batch->size(), 2); \
    sel = _vectorized_batch->selected(); \
    sum = 0; \
    for (int i = 0; i < _vectorized_batch->size(); ++i) { \
        sum += *(col_data + sel[i]); \
    } \
    ASSERT_EQ(sum, 4); \
} \

TEST_LESS_PREDICATE(int8_t, TINYINT, "TINYINT")
TEST_LESS_PREDICATE(int16_t, SMALLINT, "SMALLINT")
TEST_LESS_PREDICATE(int32_t, INT, "INT")
TEST_LESS_PREDICATE(int64_t, BIGINT, "BIGINT")
TEST_LESS_PREDICATE(int128_t, LARGEINT, "LARGEINT")

TEST_F(TestLessPredicate, FLOAT_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("FLOAT_COLUMN"), "FLOAT",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 10;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    float* col_data = reinterpret_cast<float*>(_mem_pool->allocate(size * sizeof(float)));
    col_vector->set_col_data(col_data);
    for (int i = 0; i < size; ++i) {
        *(col_data + i) = i;
    }
    float value = 5.0;
    ColumnPredicate* pred = new LessPredicate<float>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 5);
    uint16_t* sel = _vectorized_batch->selected();
    float sum = 0;
    for (int i = 0; i < _vectorized_batch->size(); ++i) {
        sum += *(col_data + sel[i]);
    }
    ASSERT_FLOAT_EQ(sum, 10.0);

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            *(col_data + i) = i;
        }
    }

    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 2);
    sel = _vectorized_batch->selected(); \
    sum = 0;
    for (int i = 0; i < _vectorized_batch->size(); ++i) {
        sum += *(col_data + sel[i]);
    }
    ASSERT_FLOAT_EQ(sum, 4.0);
}

TEST_F(TestLessPredicate, DOUBLE_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("DOUBLE_COLUMN"), "DOUBLE",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 10;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    double* col_data = reinterpret_cast<double*>(_mem_pool->allocate(size * sizeof(double)));
    col_vector->set_col_data(col_data);
    for (int i = 0; i < size; ++i) {
        *(col_data + i) = i;
    }
    double value = 5.0;
    ColumnPredicate* pred = new LessPredicate<double>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 5);
    uint16_t* sel = _vectorized_batch->selected();
    double sum = 0;
    for (int i = 0; i < _vectorized_batch->size(); ++i) {
        sum += *(col_data + sel[i]);
    }
    ASSERT_DOUBLE_EQ(sum, 10.0);

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            *(col_data + i) = i;
        }
    }

    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 2);
    sel = _vectorized_batch->selected(); \
    sum = 0;
    for (int i = 0; i < _vectorized_batch->size(); ++i) {
        sum += *(col_data + sel[i]);
    }
    ASSERT_DOUBLE_EQ(sum, 4.0);
}

TEST_F(TestLessPredicate, DECIMAL_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("DECIMAL_COLUMN"), "DECIMAL",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 10;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    decimal12_t* col_data = reinterpret_cast<decimal12_t*>(_mem_pool->allocate(size * sizeof(decimal12_t)));
    col_vector->set_col_data(col_data);
    for (int i = 0; i < size; ++i) {
        (*(col_data + i)).integer = i;
        (*(col_data + i)).fraction = i;
    }
    decimal12_t value(5, 5);
    ColumnPredicate* pred = new LessPredicate<decimal12_t>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 5);
    uint16_t* sel = _vectorized_batch->selected();
    decimal12_t sum(0, 0);
    for (int i = 0; i < _vectorized_batch->size(); ++i) {
        sum += *(col_data + sel[i]);
    }
    ASSERT_EQ(sum.integer, 10);
    ASSERT_EQ(sum.fraction, 10);

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            (*(col_data + i)).integer = i;
            (*(col_data + i)).fraction = i;
        }
    }

    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 2);
    sum.integer = 0;
    sum.fraction = 0;
    for (int i = 0; i < _vectorized_batch->size(); ++i) {
        sum += *(col_data + sel[i]);
    }
    ASSERT_EQ(sum.integer, 4);
    ASSERT_EQ(sum.fraction, 4);
}

TEST_F(TestLessPredicate, STRING_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("STRING_COLUMN"), "VARCHAR",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 10;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    StringValue* col_data = reinterpret_cast<StringValue*>(_mem_pool->allocate(size * sizeof(StringValue)));
    col_vector->set_col_data(col_data);

    char* string_buffer = reinterpret_cast<char*>(_mem_pool->allocate(55));
    for (int i = 0; i < size; ++i) {
        for (int j = 0; j <= i; ++j) {
            string_buffer[j] = 'a' + i;
        }
        (*(col_data + i)).len = i + 1;
        (*(col_data + i)).ptr = string_buffer;
        string_buffer += i + 1;
    }

    StringValue value;
    const char* value_buffer = "dddd";
    value.len = 4;
    value.ptr = const_cast<char*>(value_buffer);

    ColumnPredicate* pred = new LessPredicate<StringValue>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 3);
    uint16_t* sel = _vectorized_batch->selected();
    ASSERT_TRUE(strncmp((*(col_data + sel[0])).ptr, "a", 1) == 0);

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    string_buffer = reinterpret_cast<char*>(_mem_pool->allocate(55));
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            for (int j = 0; j <= i; ++j) {
                string_buffer[j] = 'a' + i;
            }
            (*(col_data + i)).len = i + 1;
            (*(col_data + i)).ptr = string_buffer;
        }
        string_buffer += i + 1;
    }
    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    sel = _vectorized_batch->selected();
    ASSERT_TRUE(strncmp((*(col_data + sel[0])).ptr, "bb", 2) == 0);
}

TEST_F(TestLessPredicate, DATE_COLUMN) {
    TabletSchema tablet_schema;
    SetTabletSchema(std::string("DATE_COLUMN"), "DATE",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 6;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    uint24_t* col_data = reinterpret_cast<uint24_t*>(_mem_pool->allocate(size * sizeof(uint24_t)));
    col_vector->set_col_data(col_data);

    std::vector<std::string> date_array;
    date_array.push_back("2017-09-07");
    date_array.push_back("2017-09-08");
    date_array.push_back("2017-09-09");
    date_array.push_back("2017-09-10");
    date_array.push_back("2017-09-11");
    date_array.push_back("2017-09-12");
    for (int i = 0; i < size; ++i) {
        uint24_t timestamp = datetime::to_date_timestamp(date_array[i].c_str());
        *(col_data + i) = timestamp;
    }

    uint24_t value = datetime::to_date_timestamp("2017-09-10");
    ColumnPredicate* pred = new LessPredicate<uint24_t>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 3);
    uint16_t* sel = _vectorized_batch->selected();
    ASSERT_EQ(datetime::to_date_string(*(col_data + sel[0])), "2017-09-07");

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            uint24_t timestamp = datetime::to_date_timestamp(date_array[i].c_str());
            *(col_data + i) = timestamp;
        }
    }

    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    sel = _vectorized_batch->selected();
    ASSERT_EQ(datetime::to_date_string(*(col_data + sel[0])), "2017-09-08");
}

TEST_F(TestLessPredicate, DATETIME_COLUMN) {
    TabletSchema tablet_schema;
    TabletColumn tablet_column;
    SetTabletSchema(std::string("DATETIME_COLUMN"), "DATETIME",
                 "REPLACE", 1, false, true, &tablet_schema);
    int size = 6;
    std::vector<uint32_t> return_columns;
    for (int i = 0; i < tablet_schema.num_columns(); ++i) {
        return_columns.push_back(i);
    }
    InitVectorizedBatch(&tablet_schema, return_columns, size);
    ColumnVector* col_vector = _vectorized_batch->column(0);

    // for no nulls
    col_vector->set_no_nulls(true);
    uint64_t* col_data = reinterpret_cast<uint64_t*>(_mem_pool->allocate(size * sizeof(uint64_t)));
    col_vector->set_col_data(col_data);

    std::vector<std::string> date_array;
    date_array.push_back("2017-09-07 00:00:00");
    date_array.push_back("2017-09-08 00:01:00");
    date_array.push_back("2017-09-09 00:00:01");
    date_array.push_back("2017-09-10 01:00:00");
    date_array.push_back("2017-09-11 01:01:00");
    date_array.push_back("2017-09-12 01:01:01");
    for (int i = 0; i < size; ++i) {
        uint64_t timestamp = datetime::to_datetime_timestamp(date_array[i].c_str());
        *(col_data + i) = timestamp;
    }

    uint64_t value = datetime::to_datetime_timestamp("2017-09-10 01:00:00");
    ColumnPredicate* pred = new LessPredicate<uint64_t>(0, value);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 3);
    uint16_t* sel = _vectorized_batch->selected();
    ASSERT_EQ(datetime::to_datetime_string(*(col_data + sel[0])), "2017-09-07 00:00:00");

    // for has nulls
    col_vector->set_no_nulls(false);
    bool* is_null = reinterpret_cast<bool*>(_mem_pool->allocate(size));
    memset(is_null, 0, size);
    col_vector->set_is_null(is_null);
    for (int i = 0; i < size; ++i) {
        if (i % 2 == 0) {
            is_null[i] = true;
        } else {
            uint64_t timestamp = datetime::to_datetime_timestamp(date_array[i].c_str());
            *(col_data + i) = timestamp;
        }
    }

    _vectorized_batch->set_size(size);
    _vectorized_batch->set_selected_in_use(false);
    pred->evaluate(_vectorized_batch);
    ASSERT_EQ(_vectorized_batch->size(), 1);
    sel = _vectorized_batch->selected();
    ASSERT_EQ(datetime::to_datetime_string(*(col_data + sel[0])), "2017-09-08 00:01:00");
}

} // namespace doris

int main(int argc, char** argv) {
    std::string conffile = std::string(getenv("DORIS_HOME")) + "/conf/be.conf";
    if (!doris::config::init(conffile.c_str(), false)) {
        fprintf(stderr, "error read config file. \n");
        return -1;
    }
    doris::init_glog("be-test");
    int ret = doris::OLAP_SUCCESS;
    testing::InitGoogleTest(&argc, argv);
    doris::CpuInfo::init();
    ret = RUN_ALL_TESTS();
    google::protobuf::ShutdownProtobufLibrary();
    return ret;
}


text:
$(document).ready(function () {

    var isCtrl;
    var countmass = 0;
    var mass = [];
    var isShift;
    var isBreak = false;
    var isAlt = false;
    $(document).keydown(function (e) {
        if (e.which == '17') {
            isCtrl = true;

        }
        if (e.which == '16') {
            isShift = true;
        }
        if (e.which == '18') {
            isAlt = true;
        }
        if (event.keyCode == 13) {
            $('#edit').blur();
        }
    });
    $(document).keyup(function (e) {
        if (e.which == '17') {
            isCtrl = false;
        }
        if (e.which == '16') {
            isShift = false;
        }
        if (e.which == '18') {
            isAlt = false;
        }
        unionTable(mass);
        FlagColumn = false;
        countmass = 0;
        mass.length = countmass;
    });

    $("#TableListOfStudentPrak thead tr:nth-child(odd) th").each(function () {
        $(this).attr("class", "no-target");
    });

    $("#TableListOfStudentPrak tbody tr:nth-child(odd)").each(function () {
        $(this).find("td:first").attr("class", "no-target");
    });

    //вызов модального окна с пометками

    $("#o-listOfMark").on('click', function () {

        $("#modalListOfStudentPrak").modal("hide");
        $("#listOfMark").modal("show");

    });
    // закрытия модального окна с пометками
    $("#c-modalMark").on('click', function () {
        $("#listOfMark").modal("hide");
        $("#modalListOfStudentPrak").modal("show");
    });

    //<*------*>
    // блок отвечает за объединение пар на модальном окне листа практики

    var FlagColumn = false;

    $("#TableListOfStudentPrak ").on("click", 'td', function () {

        if (isCtrl == true) {
            if ($(this).hasClass("no-target")) return;
            if ($(this).hasClass("halfNo-target")) return;
            var colIndex = $(this).parent().children().index($(this));

            var colspan = $("#modalListOfStudentPrak tbody tr td:nth-child(" + (colIndex + 1) + ")").attr("colspan");
            if (colspan != undefined) {
                FlagColumn = true;
                return;
            }
            if (mass.length != 0) {

                for (var i = 0; i < mass.length; i++) {

                    if (colIndex == mass[i]) {
                        return;
                    }

                    if (mass[i] + 1 == colIndex || mass[i] - 1 == colIndex) {
                        countmass++;
                        mass.length = countmass;
                        mass[countmass - 1] = colIndex;
                    }

                }

            } else {

                countmass++;
                mass.length = countmass;
                mass[countmass - 1] = colIndex;
            }
        }
    });

    function unionTable(mass) {
        if (mass.length <= 1 || FlagColumn == true) return;
        else {

            var min = mass[0]
            for (var i = 0; i < mass.length; i++) {
                if (mass[i] < min)
                    min = mass[i];
            }
            min += 1;
            var colspan;
            if ($("#modalListOfStudentPrak tbody tr td:nth-child(" + min + ")").attr("colspan") == undefined) {
                colspan = 0;
            }
            else { colspan = Number($("#modalListOfStudentPrak tbody tr td:nth-child(" + min + ")").attr("colspan")) - 1; }

            for (var i = 0; i < mass.length - 1; i++) {                 // удаляет лишние ячейки получающиеся при объединение нескольких ячеек
                $('#modalListOfStudentPrak tr:nth-child(odd) td:nth-child(' + (min) + ")").remove();
            }
            for (var i = 0; i < mass.length - 1; i++) {                 // удаляет лишние ячейки в head-строке таблицы, получающиеся при объединение нескольких ячеек
                $('#modalListOfStudentPrak tr:nth-child(even) th:nth-child(' + (min) + ")").remove();
            }

            colspan = colspan + mass.length;

            $("#modalListOfStudentPrak tbody tr:nth-child(odd) td:nth-child(" + min + ")").each(function () { // объединяет ячейки в таблице
                $(this).attr("colspan", colspan);
            })
            $("#modalListOfStudentPrak thead tr:nth-child(even) th:nth-child(" + min + ")").each(function () { // объединяет ячейки в head-строке таблицы
                $(this).attr("colspan", colspan);
            })

            RewriteClassOnCollumn();
        }

    }

    ////////////////блок отвечающий за разбитие ячеек//////////////////

    $("#TableListOfStudentPrak ").on("click", 'td', function () {
        if (isShift == true) {
            var colIndex = $(this).parent().children().index($(this));

            var colspan = $("#modalListOfStudentPrak tbody tr td:nth-child(" + (colIndex + 1) + ")").attr("colspan");

            if (colspan == undefined) {

                return;
            }
            else {
                BreakColumn(colIndex);
            }
        }
    });

    function BreakColumn(index) {
        var countCol;
        var numberCol;
        $("#modalListOfStudentPrak tbody tr:nth-child(odd) td:nth-child(" + (index + 1) + ")").each(function () {
            countCol = $(this).attr("colspan");
            numberCol = $(this).index();
            $(this).removeAttr("colspan");
        })

        for (var j = 0; j < countCol - 1; j++) {
            for (var i = 0; i < $("#modalListOfStudentPrak").length; i++) {
                $('#modalListOfStudentPrak tbody tr:nth-child(odd)').find('td:nth-child(' + numberCol + ')').after('	');
            }
        }

        // делает анологично предыдущему, только для head-строки в таблице практики

        $("#modalListOfStudentPrak thead tr:nth-child(even) th:nth-child(" + (index + 1) + ")").each(function () {
            countCol = $(this).attr("colspan");
            numberCol = $(this).index();
            $(this).removeAttr("colspan");
        })

        for (var j = 0; j < countCol - 1; j++) {
            for (var i = 0; i < $("#modalListOfStudentPrak").length; i++) {
                $('#modalListOfStudentPrak thead tr:nth-child(even)').find('th:nth-child(' + numberCol + ')').after('	');
            }
        }
        RewriteClassOnCollumn();
    }

    $('#TableListOfStudentPrak tbody tr:nth-child(even) td').addClass('halfNo-target');

    // Вставляет колонку с модулем

    InsertCollumn = function (e) {
        if (isAlt == false || $(this).hasClass('halfNo-target') || $(this).hasClass('no-target')) return;

        var numberOfColumn = $(this).index();

        var numberOfRow = $(this).parent('tr').index();
        var colspan = 0;
        $("#TableListOfStudentPrak tbody tr:nth-child(" + (numberOfRow + 1) + ") td").each(function () {
            if ($(this).index() > numberOfColumn) return;
            if ($(this).hasAttr('colspan')) {
                colspan += Number($(this).attr('colspan') - 1);
            }
        });

        $("#TableListOfStudentPrak tbody tr:nth-child(odd) td.col" + (numberOfColumn - 1)).after("	");
        $("#TableListOfStudentPrak tbody tr:nth-child(even) td.col" + (numberOfColumn - 1 + colspan)).after("	");

        $("#TableListOfStudentPrak thead tr:nth-child(even) th.col" + (numberOfColumn - 1)).after("	");
        $("#TableListOfStudentPrak thead tr:nth-child(odd) th.col" + (numberOfColumn - 1 + colspan)).after("	");

        ModulEvaluation();
        RewriteClassOnCollumn();
    }

    // подсчитывает баллы и пропуски, а результат заносится в колонку модуля
    var ModulEvaluation = function () {
        if ($("#TableListOfStudentPrak tbody td").is(".modul") == false) return;

        var countDoubleN = 0;
        var countHalfN = 0;
        var currentIndex = 1;
        $("#TableListOfStudentPrak tbody tr:nth-child(even) td").each(function () {
            if ($(this).hasClass("no-target")) return;
            if ($(this).parent('tr').index() > currentIndex) {
                currentIndex += 2;
                countDoubleN = 0;
                countHalfN = 0;
            }
            if ($(this).hasClass("modul")) {

                $(this).text(countDoubleN * 2 + countHalfN);
                countDoubleN = 0;
                countHalfN = 0;
                return;
            }
            if ($(this).text() == "") return;

            var index = $(this).parent('tr').index();
            $(this).attr("class", "leaving");
            if (currentIndex == index) {
                if ($(this).html() == 'Н/2') {
                    countHalfN++;
                } else countDoubleN++;
            } else { countDoubleN = 0; countHalfN = 0;}

        });

        count = 0;
        currentIndex = 0;
        countLesson = 0;

        $("#TableListOfStudentPrak tbody tr:nth-child(odd) td").each(function () {
            if ($(this).hasClass("no-target")) { return; }

            if ($(this).parent('tr').index() > currentIndex) {
                currentIndex += 2;
                count = 0;
                countLesson = 0;
            }
            if ($(this).hasClass("modul")) {
                if ($('#isRussianBudget').hasClass('ActiveRadioButton')) {
                    $(this).text((count * 6) / countLesson);
                } else{
                    $(this).text((count * 3)/countLesson);
                }
                count = 0;
                countLesson = 0;
                return;
            }

            count += Number($(this).text());
            countLesson++;

        });

    }

    // Переписывает классы колонок после изменения таблицы
    RewriteClassOnCollumn = function () {
        $('#TableListOfStudentPrak tbody td').each(function () {

            $(this).removeClassWild("col*");

            if ($(this).parent('tr').index() % 2 == 0) {
                $(this).addClass("col" + ($(this).index() - 1));
            } else $(this).addClass("col" + $(this).index());
        });
        RewriteClassOnHead();
    }

    // то же что и предыдущий блок, только для ячеек в head строке
    RewriteClassOnHead = function () {
        $('#TableListOfStudentPrak thead th').each(function () {

            $(this).removeClassWild("col*");
            $(this).addClass("col" + ($(this).index() - 1));

        });

    }

    // Вставка значений из выплывающего списка

    $('#panel').click(function (e) {

        if ($(this).hasClass('visible')) {

            var elm = e.target || event.srcElement;
            if (elm.id == "panel")
                $(this).removeClass('visible');
        }
        else {
            $(this).addClass('visible');
        }
    });

    var mark = 0;
    $('#markerList li').click(function (e) {

        $("#markerList li").removeClass('activePointList');
        $(this).addClass('activePointList');
        if ($(this).hasClass("remove")) mark = "delete";
        else mark = $(this).html();
    });

    InsertInput = function (e) {
        if (isCtrl == true || isShift == true || isAlt == true || $(this).hasClass("no-target") || $(this).hasClass("col-leave") || $(this).hasClass("modul")) { return; }
        if ($('#panel').hasClass('visible')) {

            if (mark == "delete") {
                $(this).html('');
                if ($(this).hasClass('leaving')) {
                    $(this).removeClass('leaving');
                }
                ModulEvaluation();
                return;
            }

            if ($(this).parent('tr').index() % 2 == 0) {
                if (mark != 'Н' && mark != 'Н/2') {
                    $(this).html(mark);
                }
            } else if (mark == 'Н' || mark == 'Н/2') {
                $(this).html(mark);
                $(this).addClass('leaving');
            }
            ModulEvaluation();
        } else return;
    };

    $('#isRussianBudget').click(function () {
        if ($(this).hasClass('ActiveRadioButton')) {
            $(this).removeClass('ActiveRadioButton').addClass('InactiveRadioButton');
            ModulEvaluation();
        } else { $(this).removeClass('InactiveRadioButton').addClass('ActiveRadioButton'); ModulEvaluation(); }
    });

    RewriteClassOnHead();
    $('.table_Students').on('click', 'td', InsertInput);
    $('.table_Students').on('click', 'th', InsertInput);
    $("#TableListOfStudentPrak ").on("click", 'td', InsertCollumn);
});


text:
package com.logicmaster63.tdgalaxy.tools;

import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.Viewport;

public class TDStage extends Stage {

    public TDStage() {
    }

    public TDStage(Viewport viewport) {
        super(viewport);
    }

    public TDStage(Viewport viewport, Batch batch) {
        super(viewport, batch);
    }

}


text:
<?php

namespace backend\modules\inventory;

/**
 * inventory module definition class
 */
class Inventory extends \yii\base\Module
{
    /**
     * @inheritdoc
     */
    public $controllerNamespace = 'backend\modules\inventory\controllers';

    /**
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        $this->layout = "@backend/modules/inventory/views/layouts/main";
    }
}


text:
<!DOCTYPE html>

<link rel="stylesheet" href="../style.css"/>

Processing.js

Mandelbrot

by Daniel Shiffman.

Simple rendering of the Mandelbrot set.

Original Processing.org Example: Mandelbrot

<canvas width="200" height="200"></canvas>

// All Examples Written by Casey Reas and Ben Fry
// unless otherwise stated.
// Establish a range of values on the complex plane
// A different range will allow us to "zoom" in or out on the fractal
// float xmin = -1.5; float ymin = -.1; float wh = 0.15;
float xmin = -2.5;
float ymin = -2;
float wh = 4;

void setup() {
  size(200, 200);
  noLoop();
}

void draw() {

  loadPixels();

  // Maximum number of iterations for each point on the complex plane
  int maxiterations = 200;

  // x goes from xmin to xmax
  float xmax = xmin + wh;
  // y goes from ymin to ymax
  float ymax = ymin + wh;

  // Calculate amount we increment x,y for each pixel
  float dx = (xmax - xmin) / (width);
  float dy = (ymax - ymin) / (height);

  // Start y
  float y = ymin;
  for(int j = 0; j < height; j++) {
    // Start x
    float x = xmin;
    for(int i = 0;  i < width; i++) {

      // Now we test, as we iterate z = z^2 + cm does z tend towards infinity?
      float a = x;
      float b = y;
      int n = 0;
      while (n < maxiterations) {
        float aa = a * a;
        float bb = b * b;
        float twoab = 2.0 * a * b;
        a = aa - bb + x;
        b = twoab + y;
        // Infinty in our finite world is simple, let's just consider it 16
        if(aa + bb > 16.0f) {
          break;  // Bail
        }
        n++;
      }

      // We color each pixel based on how long it takes to get to infinity
      // If we never got there, let's pick the color black
      if (n == maxiterations) pixels[i+j*width] = 0;
      else pixels[i+j*width] = color(n*16 % 255);  // Gosh, we could make fancy colors here if we wanted
      x += dx;
    }
    y += dy;
  }
  updatePixels();
}


text:
angular.module('thingsHappened').controller('thingsCtrl', [ '$scope', 'thingsDao', function($scope, thingsDao) {
  $scope.add = thingsDao.add
} ]);


text:
<?php
namespace Craft;

/**
 * Stores entry locale data
 */
class EntryLocaleRecord extends BaseRecord
{
	/**
	 * @return string
	 */
	public function getTableName()
	{
		return 'entries_i18n';
	}

	/**
	 * @access protected
	 * @return array
	 */
	protected function defineAttributes()
	{
		return array(
			'locale' => array(AttributeType::Locale, 'required' => true),
			'slug'   => array(AttributeType::String, 'required' => true),
		);
	}

	/**
	 * @return array
	 */
	public function defineRelations()
	{
		return array(
			'entry'   => array(static::BELONGS_TO, 'EntryRecord', 'required' => true, 'onDelete' => static::CASCADE),
			'section' => array(static::BELONGS_TO, 'SectionRecord', 'required' => true, 'onDelete' => static::CASCADE),
			'locale'  => array(static::BELONGS_TO, 'LocaleRecord', 'locale', 'required' => true, 'onDelete' => static::CASCADE, 'onUpdate' => static::CASCADE),
		);
	}

	/**
	 * @return array
	 */
	public function defineIndexes()
	{
		return array(
			array('columns' => array('entryId', 'locale'), 'unique' => true),
			array('columns' => array('slug','sectionId','locale'), 'unique' => true),
		);
	}
}